playwright_rs/lib.rs
1//! playwright: High-level Rust bindings for Microsoft Playwright
2//!
3//! This crate provides the public API for browser automation using Playwright.
4//!
5//! # Quick tour
6//!
7//! ## Object model
8//!
9//! ```text
10//! Playwright start here — Playwright::launch().await?
11//! └── BrowserType .chromium() / .firefox() / .webkit()
12//! └── Browser .launch().await? → owns the browser process
13//! └── BrowserContext isolated cookies / storage
14//! └── Page one tab
15//! └── Locator selector with auto-wait
16//! ```
17//!
18//! [`Locator`] is the workhorse. Build one with [`Page::locator`] or one
19//! of the semantic helpers (`get_by_role`, `get_by_text`, `get_by_label`,
20//! `get_by_placeholder`, `get_by_test_id`, `get_by_alt_text`,
21//! `get_by_title`), then call action / assertion methods on it — they
22//! wait for the element to be actionable automatically.
23//!
24//! ## API conventions
25//!
26//! - **`Result<T>` everywhere.** One error type, [`Error`].
27//! - **Builders for option-heavy methods.** `goto`, `click`, `screenshot`,
28//! `tracing.start`, etc. take an `Options` struct (`..Default::default()`).
29//! - **Auto-wait by default.** Locator-based actions wait until the
30//! element is actionable; [`expect`] assertions auto-retry until the
31//! condition holds or times out. You almost never need a manual `sleep`.
32//! - **Async/await on `tokio`.** Every method that does I/O is `async`.
33//! - **Selectors validated at compile time.** Prefer the [`locator!`]
34//! macro (re-exported when the default `macros` feature is on) over
35//! raw `&str`: empty / malformed selectors fail at `cargo build`, not
36//! at runtime.
37//!
38//! ## Debugging failures
39//!
40//! Fastest path to "what happened" is tracing:
41//!
42//! 1. Wrap the test body with [`BrowserContext::tracing`] —
43//! `start({ snapshots, screenshots, sources })` → run → `stop({ path })`.
44//! 2. Open the resulting `.trace.zip` with
45//! `playwright show-trace path/to/trace.zip` — the trace viewer is
46//! language-agnostic, the same UI JS / Python users see.
47//! 3. To inspect a trace programmatically (CI bots, agent feedback
48//! loops), pull in [`playwright-rs-trace`](https://docs.rs/playwright-rs-trace)
49//! as a `[dev-dependencies]`.
50//!
51//! See [`examples/trace_on_failure.rs`](https://github.com/padamson/playwright-rust/blob/main/crates/playwright/examples/trace_on_failure.rs)
52//! for the canonical Rust pattern (Rust has no async `Drop`, so cleanup
53//! is explicit).
54//!
55//! ## Companion crates
56//!
57//! - [`playwright-rs-macros`](https://docs.rs/playwright-rs-macros) —
58//! compile-time-validated [`locator!`] macro. Default-on via the
59//! `macros` feature; surfaced here as `playwright_rs::locator!`.
60//! - [`playwright-rs-trace`](https://docs.rs/playwright-rs-trace) —
61//! pure-Rust parser for `.trace.zip` files. Standalone; add to
62//! `[dev-dependencies]` for post-mortem analysis.
63//!
64//! ## For AI coding agents
65//!
66//! If you're using this crate with Claude Code or another coding agent,
67//! see [`docs/agent/`](https://github.com/padamson/playwright-rust/tree/main/docs/agent)
68//! for a copy-paste CLAUDE.md snippet and the
69//! [`playwright-rs-usage` skill](https://github.com/padamson/playwright-rust/tree/main/.claude/skills/playwright-rs-usage)
70//! you can drop into your project's `.claude/skills/`.
71//!
72//! # Examples
73//!
74//! ## Basic Navigation and Interaction
75//!
76//! ```no_run
77//! use playwright_rs::{Playwright, SelectOption};
78//!
79//! #[tokio::main]
80//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
81//! let playwright = Playwright::launch().await?;
82//! let browser = playwright.chromium().launch().await?;
83//! let page = browser.new_page().await?;
84//!
85//! // Navigate using data URL for self-contained test
86//! let _ = page.goto(
87//! "data:text/html,<html><body>\
88//! <h1 id='title'>Welcome</h1>\
89//! <button id='btn' onclick='this.textContent=\"Clicked\"'>Click me</button>\
90//! </body></html>",
91//! None
92//! ).await;
93//!
94//! // Query elements with locators
95//! let heading = page.locator("#title").await;
96//! let text = heading.text_content().await?;
97//! assert_eq!(text, Some("Welcome".to_string()));
98//!
99//! // Click button and verify result
100//! let button = page.locator("#btn").await;
101//! button.click(None).await?;
102//! let button_text = button.text_content().await?;
103//! assert_eq!(button_text, Some("Clicked".to_string()));
104//!
105//! browser.close().await?;
106//! Ok(())
107//! }
108//! ```
109//!
110//! ## Form Interaction
111//!
112//! ```no_run
113//! use playwright_rs::{Playwright, SelectOption};
114//!
115//! #[tokio::main]
116//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
117//! let playwright = Playwright::launch().await?;
118//! let browser = playwright.chromium().launch().await?;
119//! let page = browser.new_page().await?;
120//!
121//! // Create form with data URL
122//! let _ = page.goto(
123//! "data:text/html,<html><body>\
124//! <input type='text' id='name' />\
125//! <input type='checkbox' id='agree' />\
126//! <select id='country'>\
127//! <option value='us'>USA</option>\
128//! <option value='uk'>UK</option>\
129//! <option value='ca'>Canada</option>\
130//! </select>\
131//! </body></html>",
132//! None
133//! ).await;
134//!
135//! // Fill text input
136//! let name = page.locator("#name").await;
137//! name.fill("John Doe", None).await?;
138//! assert_eq!(name.input_value(None).await?, "John Doe");
139//!
140//! // Check checkbox
141//! let checkbox = page.locator("#agree").await;
142//! checkbox.set_checked(true, None).await?;
143//! assert!(checkbox.is_checked().await?);
144//!
145//! // Select option
146//! let select = page.locator("#country").await;
147//! select.select_option("uk", None).await?;
148//! assert_eq!(select.input_value(None).await?, "uk");
149//!
150//! browser.close().await?;
151//! Ok(())
152//! }
153//! ```
154//!
155//! ## Element Screenshots
156//!
157//! ```no_run
158//! use playwright_rs::Playwright;
159//!
160//! #[tokio::main]
161//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
162//! let playwright = Playwright::launch().await?;
163//! let browser = playwright.chromium().launch().await?;
164//! let page = browser.new_page().await?;
165//!
166//! // Create element to screenshot
167//! let _ = page.goto(
168//! "data:text/html,<html><body>\
169//! <div id='box' style='width:100px;height:100px;background:blue'></div>\
170//! </body></html>",
171//! None
172//! ).await;
173//!
174//! // Take screenshot of specific element
175//! let element = page.locator("#box").await;
176//! let screenshot = element.screenshot(None).await?;
177//! assert!(!screenshot.is_empty());
178//!
179//! browser.close().await?;
180//! Ok(())
181//! }
182//! ```
183//!
184//! ## Assertions (expect API)
185//!
186//! ```no_run
187//! use playwright_rs::{expect, Playwright};
188//!
189//! #[tokio::main]
190//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
191//! let playwright = Playwright::launch().await?;
192//! let browser = playwright.chromium().launch().await?;
193//! let page = browser.new_page().await?;
194//!
195//! let _ = page.goto(
196//! "data:text/html,<html><body>\
197//! <button id='enabled'>Enabled</button>\
198//! <button id='disabled' disabled>Disabled</button>\
199//! <input type='checkbox' id='checked' checked />\
200//! </body></html>",
201//! None
202//! ).await;
203//!
204//! // Assert button states with auto-retry
205//! let enabled_btn = page.locator("#enabled").await;
206//! expect(enabled_btn.clone()).to_be_enabled().await?;
207//!
208//! let disabled_btn = page.locator("#disabled").await;
209//! expect(disabled_btn).to_be_disabled().await?;
210//!
211//! // Assert checkbox state
212//! let checkbox = page.locator("#checked").await;
213//! expect(checkbox).to_be_checked().await?;
214//!
215//! browser.close().await?;
216//! Ok(())
217//! }
218//! ```
219//!
220//! ## Observability
221//!
222//! Every public async method on the user-facing types
223//! (`Browser`, `BrowserContext`, `BrowserType`, `Page`, `Frame`, `Locator`,
224//! `ElementHandle`, `Tracing`, `CDPSession`, `Debugger`, `Screencast`, plus
225//! `Request`, `Response`, `Dialog`, `Download`, `Worker`, `FileChooser`)
226//! is instrumented with the [`tracing`] crate. Wire up any
227//! `tracing_subscriber` and you get spans for free, with cardinality-bounded
228//! identifiers (`guid`, `selector`, `url`, `name`) and selected
229//! completion-time fields (`status`, `bytes_len`, `count`, `version`).
230//! Internal `tokio::spawn` sites propagate the caller's span via
231//! `Instrument::in_current_span()` so user-registered handlers and event
232//! fan-out tasks inherit the surrounding context.
233//!
234//! Levels: top-level user operations (`goto`, `click`, `fill`, `screenshot`,
235//! `pdf`, `evaluate`, `tracing.start/stop`, `browser_type.launch`) are at
236//! `info`; everything else is at `debug`. Sensitive payloads — input
237//! values, eval expressions, request/response bodies — are deliberately
238//! excluded from span fields.
239//!
240//! ```no_run
241//! use tracing_subscriber::EnvFilter;
242//!
243//! tracing_subscriber::fmt()
244//! .with_env_filter(EnvFilter::new("playwright_rs=info"))
245//! .init();
246//! ```
247
248// Internal modules (exposed for integration tests)
249#[doc(hidden)]
250pub mod server;
251
252pub mod api;
253mod assertions;
254mod error;
255pub mod protocol;
256mod tty_guard;
257
258/// Playwright server version bundled with this crate.
259///
260/// This version determines which browser builds are compatible.
261/// When installing browsers, use this version to ensure compatibility:
262///
263/// ```bash
264/// npx playwright@1.60.0 install
265/// ```
266///
267/// See: <https://playwright.dev/docs/browsers>
268pub const PLAYWRIGHT_VERSION: &str = env!("PLAYWRIGHT_DRIVER_VERSION");
269
270/// Default timeout in milliseconds for Playwright operations.
271///
272/// This matches Playwright's standard default across all language implementations (Python, Java, .NET, JS).
273/// Required in Playwright 1.56.1+ when timeout parameter is not explicitly provided.
274///
275/// See: <https://playwright.dev/docs/test-timeouts>
276pub const DEFAULT_TIMEOUT_MS: f64 = 30000.0;
277
278// Re-export error types
279pub use error::{Error, Result};
280
281// Re-export assertions API
282pub use assertions::{PageExpectation, expect, expect_page};
283
284// Screenshot-diff types are gated on the optional feature. (`Animations` is
285// always available via the protocol re-export below; it is shared with
286// ScreenshotOptions.)
287#[cfg(feature = "screenshot-diff")]
288pub use assertions::{ScreenshotAssertionOptions, ScreenshotAssertionOptionsBuilder};
289
290// Re-export Playwright main entry point and browser API
291pub use protocol::{
292 Browser, BrowserContext, BrowserType, FrameLocator, HeaderEntry, Page, Playwright, Response,
293 Selectors,
294};
295
296// Re-export input device types
297pub use protocol::{Keyboard, Mouse, Touchscreen};
298
299// Re-export Request and related types
300pub use protocol::{Request, ResourceTiming};
301
302// Re-export Locator and element APIs
303pub use protocol::{
304 AriaRole, AriaSnapshotMode, AriaSnapshotOptions, BoundingBox, ElementHandle, FilterOptions,
305 GetByRoleOptions, HighlightOptions, JSHandle, Locator,
306};
307
308// Re-export navigation and page options
309pub use protocol::{GotoOptions, WaitUntil};
310
311// Re-export action options
312pub use protocol::{
313 CheckOptions, ClickOptions, DragToOptions, DropOptions, DropOptionsBuilder, FillOptions,
314 HoverOptions, PressOptions, PressSequentiallyOptions, SelectOptions, TapOptions,
315 WaitForOptions, WaitForState,
316};
317
318// Re-export Position (needed for DragToOptions and other options)
319pub use protocol::Position;
320
321// Re-export form and input types
322pub use protocol::{FilePayload, SelectOption};
323
324// Re-export screenshot types
325pub use protocol::{Animations, Caret, Scale, ScreenshotClip, ScreenshotOptions, ScreenshotType};
326
327// Re-export screencast types
328pub use protocol::{
329 ActionPosition, ChapterOptions, OverlayId, Screencast, ScreencastFrame, ScreencastSize,
330 ScreencastStartOptions, ShowActionsOptions, ShowOverlayOptions,
331};
332
333// Re-export new page method types
334pub use protocol::{
335 AddScriptTagOptions, ColorScheme, EmulateMediaOptions, ForcedColors, Media, PdfMargin,
336 PdfOptions, ReducedMotion,
337};
338
339// Re-export browser context options and storage state types
340pub use protocol::{
341 BrowserContextOptions, Cookie, Geolocation, LocalStorageItem, Origin, RecordHar, RecordVideo,
342 StorageState, Viewport,
343};
344
345// Re-export the tracing + HAR API. These were the last consumer-facing
346// cohort still reachable only via `playwright_rs::protocol::*`; bringing
347// them to the crate root matches every other type a consumer constructs
348// (the `protocol::` paths still work).
349pub use protocol::{
350 HarContent, HarMode, StartHarOptions, Tracing, TracingStartOptions, TracingStopOptions,
351};
352
353// Re-export EventWaiter for use with expect_page() / expect_close()
354pub use protocol::EventWaiter;
355
356// Re-export EventValue for use with expect_event()
357pub use protocol::EventValue;
358
359// Re-export ConsoleMessage types
360pub use protocol::{ConsoleMessage, ConsoleMessageLocation};
361
362// Re-export device descriptor types
363pub use protocol::{DeviceDescriptor, DeviceViewport};
364
365// Re-export WebError
366pub use protocol::{WebError, WebErrorLocation};
367
368// Re-export WebSocketRoute
369pub use protocol::{WebSocketRoute, WebSocketRouteCloseOptions};
370
371// Re-export FileChooser
372pub use protocol::FileChooser;
373
374// Re-export Accessibility and Coverage types
375pub use protocol::{
376 Accessibility, AccessibilitySnapshotOptions, CSSCoverageEntry, Coverage, CoverageRange,
377 JSCoverageEntry, JSCoverageRange, JSFunctionCoverage, StartCSSCoverageOptions,
378 StartJSCoverageOptions,
379};
380
381// Re-export Clock types
382pub use protocol::{Clock, ClockInstallOptions};
383
384// Re-export Video
385pub use protocol::Video;
386
387// Re-export routing types
388pub use protocol::{FetchOptions, FetchResponse, FulfillOptions, Route, UnrouteBehavior};
389
390// Re-export APIRequest public API
391pub use protocol::{APIRequest, APIRequestContext, APIRequestContextOptions, APIResponse};
392
393// Re-export launch and connection options
394pub use api::{ConnectOverCdpOptions, LaunchOptions};
395
396// Re-export browser installation helpers
397pub use server::driver::{install_browsers, install_browsers_with_deps};
398
399// Re-export the `locator!` compile-time-validated selector macro from
400// the companion `playwright-rs-macros` crate. Gated on the `macros`
401// feature (default-on) so users without the proc-macro toolchain
402// available can opt out.
403#[cfg(feature = "macros")]
404pub use playwright_rs_macros::locator;