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//! ```ignore
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//! ```ignore
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//! ```ignore
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//! ```ignore
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//! ```ignore
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.
285#[cfg(feature = "screenshot-diff")]
286pub use assertions::{Animations, ScreenshotAssertionOptions, ScreenshotAssertionOptionsBuilder};
287
288// Re-export Playwright main entry point and browser API
289pub use protocol::{
290 Browser, BrowserContext, BrowserType, FrameLocator, HeaderEntry, Page, Playwright, Response,
291 Selectors,
292};
293
294// Re-export input device types
295pub use protocol::{Keyboard, Mouse, Touchscreen};
296
297// Re-export Request and related types
298pub use protocol::{Request, ResourceTiming};
299
300// Re-export Locator and element APIs
301pub use protocol::{
302 AriaRole, AriaSnapshotMode, AriaSnapshotOptions, BoundingBox, ElementHandle, FilterOptions,
303 GetByRoleOptions, JSHandle, Locator,
304};
305
306// Re-export navigation and page options
307pub use protocol::{GotoOptions, WaitUntil};
308
309// Re-export action options
310pub use protocol::{
311 CheckOptions, ClickOptions, DragToOptions, FillOptions, HoverOptions, PressOptions,
312 PressSequentiallyOptions, SelectOptions, TapOptions, WaitForOptions, WaitForState,
313};
314
315// Re-export Position (needed for DragToOptions and other options)
316pub use protocol::Position;
317
318// Re-export form and input types
319pub use protocol::{FilePayload, SelectOption};
320
321// Re-export screenshot types
322pub use protocol::{ScreenshotClip, ScreenshotOptions, ScreenshotType};
323
324// Re-export screencast types
325pub use protocol::{
326 ActionPosition, ChapterOptions, OverlayId, Screencast, ScreencastFrame, ScreencastSize,
327 ScreencastStartOptions, ShowActionsOptions, ShowOverlayOptions,
328};
329
330// Re-export new page method types
331pub use protocol::{
332 AddScriptTagOptions, ColorScheme, EmulateMediaOptions, ForcedColors, Media, PdfMargin,
333 PdfOptions, ReducedMotion,
334};
335
336// Re-export browser context options and storage state types
337pub use protocol::{
338 BrowserContextOptions, Cookie, Geolocation, LocalStorageItem, Origin, RecordHar, RecordVideo,
339 StorageState, Viewport,
340};
341
342// Re-export EventWaiter for use with expect_page() / expect_close()
343pub use protocol::EventWaiter;
344
345// Re-export EventValue for use with expect_event()
346pub use protocol::EventValue;
347
348// Re-export ConsoleMessage types
349pub use protocol::{ConsoleMessage, ConsoleMessageLocation};
350
351// Re-export device descriptor types
352pub use protocol::{DeviceDescriptor, DeviceViewport};
353
354// Re-export WebError
355pub use protocol::WebError;
356
357// Re-export WebSocketRoute
358pub use protocol::{WebSocketRoute, WebSocketRouteCloseOptions};
359
360// Re-export FileChooser
361pub use protocol::FileChooser;
362
363// Re-export Accessibility and Coverage types
364pub use protocol::{
365 Accessibility, AccessibilitySnapshotOptions, CSSCoverageEntry, Coverage, CoverageRange,
366 JSCoverageEntry, JSCoverageRange, JSFunctionCoverage, StartCSSCoverageOptions,
367 StartJSCoverageOptions,
368};
369
370// Re-export Clock types
371pub use protocol::{Clock, ClockInstallOptions};
372
373// Re-export Video
374pub use protocol::Video;
375
376// Re-export routing types
377pub use protocol::{FetchOptions, FetchResponse, FulfillOptions, Route, UnrouteBehavior};
378
379// Re-export APIRequest public API
380pub use protocol::{APIRequest, APIRequestContext, APIRequestContextOptions, APIResponse};
381
382// Re-export launch and connection options
383pub use api::{ConnectOverCdpOptions, LaunchOptions};
384
385// Re-export browser installation helpers
386pub use server::driver::{install_browsers, install_browsers_with_deps};
387
388// Re-export the `locator!` compile-time-validated selector macro from
389// the companion `playwright-rs-macros` crate. Gated on the `macros`
390// feature (default-on) so users without the proc-macro toolchain
391// available can opt out.
392#[cfg(feature = "macros")]
393pub use playwright_rs_macros::locator;