Skip to main content

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;