viewpoint-core
High-level browser automation API for Rust, inspired by Playwright.
This crate provides the core browser, context, and page abstractions for the Viewpoint browser automation framework.
Features
- Browser launching and management
- Page navigation with configurable wait states
- Element locators (CSS, text, role, label, test ID, placeholder)
- Element actions (click, fill, type, hover, check, select)
- Automatic element waiting
- Automatic navigation waiting - Actions that trigger navigation automatically wait for the page to load
- Browser Context Features:
- Cookie management (add, get, clear)
- Storage state persistence (including IndexedDB)
- Permission granting (geolocation, notifications, camera, etc.)
- Geolocation mocking
- HTTP credentials for authentication
- Extra HTTP headers
- Offline mode simulation
- Configurable timeouts
- Context-level init scripts
- Context-level route interception
- Network Interception:
- Request routing and modification
- Response mocking
- HAR file replay
- WebSocket monitoring
- HTTP authentication handling
- Event System:
- Page lifecycle events
- Popup handling
- Console and page error events
- Dialog handling
- Download handling
- Advanced Features:
- Exposed Rust functions callable from JavaScript
- ARIA accessibility snapshots
- Element highlighting for debugging
- Tracing for test debugging
Usage
For testing, use viewpoint-test which re-exports this crate with additional assertions and test fixtures.
use ;
// Launch browser
let browser = launch.headless.launch.await?;
let context = browser.new_context.await?;
let page = context.new_page.await?;
// Navigate
page.goto
.wait_until
.goto
.await?;
// Locate and interact with elements
let button = page.locator;
button.click.await?;
Context Features
use ;
use HashMap;
let browser = launch.headless.launch.await?;
// Create context with options
let context = browser.new_context_builder
.geolocation // San Francisco
.permissions
.has_touch
.locale
.timezone_id
.build
.await?;
let page = context.new_page.await?;
page.goto.goto.await?;
// Cookie management
context.add_cookies.await?;
let cookies = context.cookies.await?;
// Extra HTTP headers
let mut headers = new;
headers.insert;
context.set_extra_http_headers.await?;
// Offline mode
context.set_offline.await?;
// Save storage state for reuse
let state = context.storage_state.await?;
state.save.await?;
Event System
use ;
let browser = launch.headless.launch.await?;
let context = browser.new_context.await?;
// Listen for new pages (e.g., popups)
context.on_page.await;
let page = context.new_page.await?;
// Wait for a popup triggered by an action
let popup = context.wait_for_page.await?;
Network Interception
use ;
let browser = launch.headless.launch.await?;
let context = browser.new_context.await?;
let page = context.new_page.await?;
// Mock an API response
page.route.await?;
// Block certain requests
page.route.await?;
// HAR replay - serve requests from recorded HAR file
page.route_from_har.await?;
WebSocket Monitoring
use ;
let browser = launch.headless.launch.await?;
let context = browser.new_context.await?;
let page = context.new_page.await?;
page.on_websocket.await?;
Testing Dynamic WebSocket Content
When testing applications that receive real-time data via WebSocket, you can verify that data updates are correctly reflected in the DOM without polling by using Viewpoint's auto-waiting assertions:
use Browser;
use expect;
use Arc;
use Mutex;
let browser = launch.headless.launch.await?;
let context = browser.new_context.await?;
let page = context.new_page.await?;
// Capture WebSocket messages for verification
let messages: = new;
let messages_clone = messages.clone;
// Set up WebSocket monitoring BEFORE navigation
page.on_websocket.await;
// Navigate to page with WebSocket-driven live data
page.goto.goto.await?;
// Auto-waiting assertions verify DOM updates WITHOUT polling!
// These automatically wait up to 30 seconds for the condition to be true
// Verify that live data container becomes visible
expect.to_be_visible.await?;
// Verify that WebSocket data rendered specific text content
expect.to_contain_text.await?;
// Verify connection status updated
expect.to_have_text.await?;
// Verify a list populated by WebSocket messages has items
expect.to_have_count_greater_than.await?;
// Optionally verify WebSocket messages were received
let received = messages.lock.await;
assert!;
The key insight is that expect(locator).to_have_text(), to_be_visible(), and similar assertions automatically wait for the condition to become true, eliminating the need for manual polling or sleep statements when testing WebSocket-driven UI updates.
Exposed Functions
use Browser;
let browser = launch.headless.launch.await?;
let context = browser.new_context.await?;
let page = context.new_page.await?;
// Expose a Rust function to JavaScript
page.expose_function.await?;
// Now callable from JavaScript:
// const result = await window.compute(1, 2); // returns 3
ARIA Accessibility Testing
use Browser;
let browser = launch.headless.launch.await?;
let context = browser.new_context.await?;
let page = context.new_page.await?;
page.goto.goto.await?;
// Get ARIA snapshot of an element
let snapshot = page.locator.aria_snapshot.await?;
println!; // YAML-like output
// Compare with expected structure
let expected = from_yaml?;
assert!;
Accessibility Testing at Scale
For testing accessibility across multiple pages while managing performance:
use ;
let browser = launch.headless.launch.await?;
// Define pages to audit
let pages_to_audit = vec!;
for url in pages_to_audit
Performance tips: Share Browser across pages, use fresh Context per page for isolation, and use get_by_role() to query the accessibility tree directly.
Init Scripts
use Browser;
let browser = launch.headless.launch.await?;
let context = browser.new_context.await?;
// Add a script that runs before every page load
context.add_init_script.await?;
// All pages in this context will have the script
let page = context.new_page.await?;
Navigation Auto-Wait
Actions like click(), press(), fill(), select_option(), and check()/uncheck() automatically wait for any triggered navigation to complete before returning. This matches Playwright's default behavior and prevents race conditions.
use Browser;
let browser = launch.headless.launch.await?;
let context = browser.new_context.await?;
let page = context.new_page.await?;
page.goto.goto.await?;
// Click a link - automatically waits for navigation to complete
page.locator.click.await?;
// After click returns, the new page is fully loaded
// Press Enter in a search form - waits for search results page
page.locator.fill.await?;
page.locator.press.await?;
// Results page is now loaded
// Opt out of auto-waiting when needed
page.locator.click.no_wait_after.await?;
// Returns immediately without waiting for navigation
// Same for keyboard
page.keyboard.press.no_wait_after.await?;
License
MIT