viewpoint_core/
lib.rs

1//! # Viewpoint Core - Browser Automation Library
2//!
3//! Core domain types for `Viewpoint` browser automation, providing a Playwright-inspired
4//! API for controlling Chromium-based browsers via the Chrome DevTools Protocol (CDP).
5//!
6//! This crate provides the high-level API for browser automation,
7//! including [`Browser`], [`BrowserContext`], [`Page`], and navigation types.
8//!
9//! ## Features
10//!
11//! - **Browser Control**: Launch or connect to Chromium browsers
12//! - **Page Navigation**: Navigate pages and wait for load states
13//! - **Element Interaction**: Click, type, and interact with page elements via [`Locator`]
14//! - **Network Interception**: Route, modify, and mock network requests
15//! - **Device Emulation**: Emulate mobile devices, geolocation, and media features
16//! - **Input Devices**: Keyboard, mouse, and touchscreen control
17//! - **Screenshots & PDF**: Capture screenshots and generate PDFs
18//! - **Clock Mocking**: Control time in tests with [`Clock`]
19//! - **Event Handling**: Dialogs, downloads, file choosers, console messages
20//! - **Tracing**: Record traces for debugging
21//! - **Video Recording**: Record page interactions as video
22//!
23//! ## Quick Start
24//!
25//! ```no_run
26//! use viewpoint_core::{Browser, DocumentLoadState};
27//!
28//! # async fn example() -> Result<(), viewpoint_core::CoreError> {
29//! // Launch a browser and create a new context and page
30//! let browser = Browser::launch()
31//!     .headless(true)
32//!     .launch()
33//!     .await?;
34//!
35//! let context = browser.new_context().await?;
36//! let page = context.new_page().await?;
37//!
38//! // Navigate to a page
39//! page.goto("https://example.com").goto().await?;
40//!
41//! // Interact with elements
42//! page.locator("button#submit").click().await?;
43//!
44//! // Fill a form
45//! page.locator("input[name='email']").fill("user@example.com").await?;
46//!
47//! // Get text content
48//! let text = page.locator("h1").text_content().await?;
49//! println!("Page title: {:?}", text);
50//! # Ok(())
51//! # }
52//! ```
53//!
54//! ## Browser Connection Methods
55//!
56//! There are three ways to get a [`Browser`] instance:
57//!
58//! ```no_run
59//! use viewpoint_core::Browser;
60//! use std::time::Duration;
61//!
62//! # async fn example() -> Result<(), viewpoint_core::CoreError> {
63//! // 1. Launch a new browser process
64//! let browser = Browser::launch()
65//!     .headless(true)
66//!     .launch()
67//!     .await?;
68//!
69//! // 2. Connect via WebSocket URL (for pre-configured connections)
70//! let browser = Browser::connect("ws://localhost:9222/devtools/browser/...").await?;
71//!
72//! // 3. Connect via HTTP endpoint (auto-discovers WebSocket URL)
73//! let browser = Browser::connect_over_cdp("http://localhost:9222")
74//!     .timeout(Duration::from_secs(10))
75//!     .connect()
76//!     .await?;
77//! # Ok(())
78//! # }
79//! ```
80//!
81//! ## Element Locators
82//!
83//! The [`Locator`] API provides auto-waiting and retry logic for robust element interaction:
84//!
85//! ```no_run
86//! use viewpoint_core::{Browser, AriaRole};
87//!
88//! # async fn example() -> Result<(), viewpoint_core::CoreError> {
89//! # let browser = Browser::launch().headless(true).launch().await?;
90//! # let context = browser.new_context().await?;
91//! # let page = context.new_page().await?;
92//! // CSS selector
93//! page.locator("button.primary").click().await?;
94//!
95//! // Text selector
96//! page.get_by_text("Submit").click().await?;
97//!
98//! // Role selector (accessibility)
99//! page.get_by_role(AriaRole::Button)
100//!     .with_name("Submit")
101//!     .build()
102//!     .click()
103//!     .await?;
104//!
105//! // Test ID selector (recommended for stable tests)
106//! page.get_by_test_id("submit-button").click().await?;
107//!
108//! // Label selector (for form fields)
109//! page.get_by_label("Email address").fill("test@example.com").await?;
110//!
111//! // Placeholder selector
112//! page.get_by_placeholder("Enter your name").fill("John Doe").await?;
113//!
114//! // Chained locators
115//! page.locator(".form")
116//!     .locator("input")
117//!     .first()
118//!     .fill("value")
119//!     .await?;
120//! # Ok(())
121//! # }
122//! ```
123//!
124//! ## Network Interception
125//!
126//! Intercept and modify network requests using [`Route`]:
127//!
128//! ```ignore
129//! use viewpoint_core::{Browser, Route};
130//!
131//! # async fn example() -> Result<(), viewpoint_core::CoreError> {
132//! # let browser = Browser::launch().headless(true).launch().await?;
133//! # let context = browser.new_context().await?;
134//! # let page = context.new_page().await?;
135//! // Block images
136//! page.route("**/*.{png,jpg,jpeg,gif}", |route| {
137//!     async move { route.abort().await }
138//! }).await?;
139//!
140//! // Mock an API response
141//! page.route("**/api/users", |route| {
142//!     async move {
143//!         route.fulfill()
144//!             .status(200)
145//!             .content_type("application/json")
146//!             .body(r#"{"users": []}"#)
147//!             .fulfill()
148//!             .await
149//!     }
150//! }).await?;
151//!
152//! // Modify requests
153//! page.route("**/api/**", |route| {
154//!     async move {
155//!         route.continue_route()
156//!             .header("X-Custom-Header", "value")
157//!             .continue_route()
158//!             .await
159//!     }
160//! }).await?;
161//! # Ok(())
162//! # }
163//! ```
164//!
165//! ## Device Emulation
166//!
167//! Emulate mobile devices and other capabilities:
168//!
169//! ```no_run
170//! use viewpoint_core::{Browser, Permission, ViewportSize};
171//!
172//! # async fn example() -> Result<(), viewpoint_core::CoreError> {
173//! # let browser = Browser::launch().headless(true).launch().await?;
174//! // Create a context with mobile viewport and geolocation
175//! let context = browser.new_context_builder()
176//!     .viewport(390, 844)  // iPhone 14 size
177//!     .device_scale_factor(3.0)
178//!     .is_mobile(true)
179//!     .has_touch(true)
180//!     .geolocation(37.7749, -122.4194)  // San Francisco
181//!     .permissions(vec![Permission::Geolocation])
182//!     .build()
183//!     .await?;
184//!
185//! let page = context.new_page().await?;
186//! # Ok(())
187//! # }
188//! ```
189//!
190//! ## Screenshots and PDF
191//!
192//! Capture screenshots and generate PDFs:
193//!
194//! ```no_run
195//! use viewpoint_core::Browser;
196//! use viewpoint_core::page::PaperFormat;
197//!
198//! # async fn example() -> Result<(), viewpoint_core::CoreError> {
199//! # let browser = Browser::launch().headless(true).launch().await?;
200//! # let context = browser.new_context().await?;
201//! # let page = context.new_page().await?;
202//! // Screenshot the viewport
203//! page.screenshot()
204//!     .path("screenshot.png")
205//!     .capture()
206//!     .await?;
207//!
208//! // Full page screenshot
209//! page.screenshot()
210//!     .full_page(true)
211//!     .path("full-page.png")
212//!     .capture()
213//!     .await?;
214//!
215//! // Generate PDF (headless only)
216//! page.pdf()
217//!     .format(PaperFormat::A4)
218//!     .path("document.pdf")
219//!     .generate()
220//!     .await?;
221//! # Ok(())
222//! # }
223//! ```
224//!
225//! ## Event Handling
226//!
227//! Handle browser events like dialogs and downloads:
228//!
229//! ```ignore
230//! use viewpoint_core::Browser;
231//!
232//! # async fn example() -> Result<(), viewpoint_core::CoreError> {
233//! # let browser = Browser::launch().headless(true).launch().await?;
234//! # let context = browser.new_context().await?;
235//! # let page = context.new_page().await?;
236//! // Handle dialogs (alerts, confirms, prompts)
237//! page.on_dialog(|dialog| async move {
238//!     println!("Dialog message: {}", dialog.message());
239//!     dialog.accept(None).await
240//! }).await;
241//!
242//! // Handle downloads
243//! page.on_download(|download| async move {
244//!     download.save_as("downloads/file.zip").await
245//! }).await;
246//!
247//! // Handle console messages
248//! page.on_console(|msg| async move {
249//!     println!("[{}] {}", msg.message_type(), msg.text());
250//!     Ok(())
251//! }).await;
252//! # Ok(())
253//! # }
254//! ```
255//!
256//! ## Module Organization
257//!
258//! - [`browser`] - Browser launching and connection management
259//! - [`context`] - Browser context (similar to incognito window) management
260//! - [`page`] - Page navigation, content, and interaction
261//! - [`network`] - Network interception, routing, and HAR recording
262//! - [`wait`] - Wait system and load states
263//! - [`devices`] - Predefined device descriptors
264//! - [`error`] - Error types
265//! - [`api`] - API request context for HTTP requests
266
267pub mod api;
268pub mod browser;
269pub mod context;
270pub mod devices;
271pub mod error;
272pub mod network;
273pub mod page;
274pub mod wait;
275
276pub use browser::{Browser, BrowserBuilder, ConnectOverCdpBuilder, NewContextBuilder, UserDataDir};
277pub use context::{
278    BrowserContext, ClearCookiesBuilder, ColorScheme, ContextEventManager, ContextOptions,
279    ContextOptionsBuilder, Cookie, ForcedColors, Geolocation, HandlerId, HttpCredentials,
280    IndexedDbDatabase, IndexedDbEntry, IndexedDbIndex, IndexedDbObjectStore, LocalStorageEntry,
281    PageInfo, Permission, ReducedMotion, SameSite, SetGeolocationBuilder, StorageOrigin,
282    StorageState, StorageStateBuilder, StorageStateOptions, StorageStateSource, Tracing,
283    TracingOptions, ViewportSize as ContextViewportSize,
284};
285pub use error::CoreError;
286pub use network::{
287    AbortError,
288    ContinueBuilder,
289    FetchedResponse,
290    FulfillBuilder,
291    HeaderEntry,
292    NetworkEvent,
293    NetworkEventListener,
294    RemoteAddress,
295    Request,
296    RequestEvent,
297    RequestFailedEvent,
298    RequestFinishedEvent,
299    RequestSizes,
300    RequestTiming,
301    ResourceType,
302    Response,
303    ResponseEvent,
304    Route,
305    RouteHandler,
306    RouteHandlerRegistry,
307    SecurityDetails,
308    UrlMatcher,
309    UrlPattern,
310    WaitForRequestBuilder,
311    WaitForResponseBuilder,
312    // WebSocket monitoring
313    WebSocket,
314    WebSocketFrame,
315    WebSocketManager,
316};
317pub use page::{
318    // Screenshot & PDF
319    Animations,
320    AriaCheckedState,
321    AriaRole,
322    AriaSnapshot,
323    // Element handles and bounding boxes
324    BoundingBox,
325    BoxModel,
326    ClipRegion,
327    // Clock mocking
328    Clock,
329    // Console and Error events
330    ConsoleMessage,
331    ConsoleMessageLocation,
332    ConsoleMessageType,
333    // Dialog, Download, FileChooser
334    Dialog,
335    DialogType,
336    Download,
337    DownloadState,
338    // Input devices
339    DragAndDropBuilder,
340    ElementHandle,
341    // Media and Vision Emulation
342    EmulateMediaBuilder,
343    FileChooser,
344    FilePayload,
345    FilterBuilder,
346    // Frame support
347    Frame,
348    FrameElementLocator,
349    FrameLocator,
350    FrameRoleLocatorBuilder,
351    // Navigation
352    GotoBuilder,
353    JsArg,
354    // JavaScript evaluation
355    JsHandle,
356    Keyboard,
357    Locator,
358    // Locator handlers
359    LocatorHandlerHandle,
360    LocatorHandlerOptions,
361    LocatorOptions,
362    Margins,
363    MediaType,
364    Mouse,
365    MouseButton,
366    NavigationResponse,
367    Page,
368    PageErrorInfo,
369    PaperFormat,
370    PdfBuilder,
371    Polling,
372    RoleLocatorBuilder,
373    ScreenshotBuilder,
374    ScreenshotFormat,
375    // Content manipulation
376    ScriptTagBuilder,
377    ScriptType,
378    Selector,
379    SetContentBuilder,
380    StyleTagBuilder,
381    TextOptions,
382    TimeValue,
383    Touchscreen,
384    // Video recording
385    Video,
386    VideoOptions,
387    // Viewport
388    ViewportSize,
389    VisionDeficiency,
390    WaitForFunctionBuilder,
391    WebError,
392};
393pub use wait::DocumentLoadState;