Rust is emerging as a serious web development language, with frameworks like Axum and Actix gaining traction.
Playwright has become the gold standard for E2E testing, praised for its reliability, speed, and developer experience. It’s available in JavaScript, Python, Java, and .NET – but not Rust.
The Rust web ecosystem has grown rapidly. Frameworks like Axum, Actix-web, Rocket, and Loco enable developers to build high-performance web services with Rust’s legendary safety guarantees. But there’s a critical gap: end-to-end testing.
Current options are either:
- Built on outdated protocols (WebDriver)
- Chrome-only (CDP-based tools)
- Not designed for modern web testing workflows
playwright-rust provides official-quality Rust bindings for Microsoft Playwright, following the same architecture as playwright-python, playwright-java, and playwright-dotnet.
playwright-rust follows Microsoft’s proven architecture for language bindings:
┌──────────────────────────────────────────────┐│ playwright-rs (Rust API) ││ - High-level, idiomatic Rust API ││ - Async/await with tokio ││ - Type-safe bindings │└─────────────────────┬────────────────────────┘ │ JSON-RPC over stdio┌─────────────────────▼────────────────────────┐│ Playwright Server (Node.js/TypeScript) ││ - Browser automation logic ││ - Cross-browser protocol abstraction ││ - Maintained by Microsoft Playwright team │└─────────────────────┬────────────────────────┘ │ Native protocols ┌─────────────┼─────────────┐ ▼ ▼ ▼ Chromium Firefox WebKit
playwright-rust follows Playwright’s cross-language consistency:
- Match Playwright API exactly – Same method names, same semantics
- Idiomatic Rust – Use Result, async/await, builder patterns where appropriate
- Type safety – Leverage Rust’s type system for compile-time safety
- Auto-waiting – Built-in smart waits like other Playwright implementations
- Testing-first – Designed for reliable end-to-end testing
Playwright’s language bindings use JSON-RPC to communicate with a Node.js server that handles all browser automation logic. This architecture is brilliant because:
- Feature Parity: Automatic compatibility with all Playwright features
- Cross-Browser: Full Chromium, Firefox, and WebKit support out of the box
- Low Maintenance: Browser protocol complexity handled by Microsoft’s team
- Battle-Tested: Used by millions of developers in production
Add dependencies to the Cargo.toml:
[dependencies]playwright-rs = "0.7.0"tokio = { version = "1", features = ["full"] }
You can build playwright-rust from source:
# Clone repository$ git clone https://github.com/YOUR_USERNAME/playwright-rust.git$ cd playwright-rust
# Install pre-commit hooks$ pip install pre-commit # or pipx install pre-commit$ pre-commit install
# Build$ cargo build
Browsers are installed automatically after building the project!
When you run cargo build, the build script (build.rs) automatically:
- Downloads the Playwright driver (version 1.56.1) from Azure CDN
- Extracts it to the appropriate location based on your setup:
- Workspace projects : drivers/playwright-1.56.1-<platform>/ in the workspace root
- Non-workspace projects : Platform-specific cache directory (e.g., ~/.cache/playwright-rust/drivers/ on Linux/macOS)
The build script uses robust workspace detection to find the right location automatically.
After building, install browsers using the downloaded driver’s CLI:
# Build the project (downloads Playwright 1.56.1 driver)$ cargo build
# Install browsers using the driver's CLI# macOS/Linux:$ drivers/playwright-1.56.1-*/node drivers/playwright-1.56.1-*/package/cli.js install chromium firefox webkit
# Windows:$ drivers\playwright-1.56.1-win32_x64\node.exe drivers\playwright-1.56.1-win32_x64\package\cli.js install chromium firefox webkit
Platform-specific examples:
# macOS (arm64):$ drivers/playwright-1.56.1-mac-arm64/node drivers/playwright-1.56.1-mac-arm64/package/cli.js install chromium firefox webkit
# macOS (x64):$ drivers/playwright-1.56.1-mac/node drivers/playwright-1.56.1-mac/package/cli.js install chromium firefox webkit
# Linux:$ drivers/playwright-1.56.1-linux/node drivers/playwright-1.56.1-linux/package/cli.js install chromium firefox webkit
Playwright server 1.56.1 expects specific browser builds (chromium-1194, firefox-1495, webkit-2215)
Verify installation:
# Browsers are cached in:# macOS: ~/Library/Caches/ms-playwright/# Linux: ~/.cache/ms-playwright/# Windows: %USERPROFILE%\AppData\Local\ms-playwright\
$ ls ~/Library/Caches/ms-playwright/# Should show: chromium-1194, chromium_headless_shell-1194, firefox-1495, webkit-2215
Running Tests
This project uses cargo-nextest for faster test execution. Install it once globally:
$ cargo install cargo-nextest# All tests (recommended - faster)$ cargo nextest run
# All tests (standard cargo)$ cargo test
# Integration tests only (requires browsers)$ cargo nextest run --test '*'
# Specific test$ cargo nextest run test_launch_chromium
# With logging$ RUST_LOG=debug cargo nextest run
# Doc-tests (nextest doesn't run these)# Compile-only check (fast, used in pre-commit)$ cargo test --doc --no-fail-fast
# Run ignored doctests (requires browsers, what CI does)$ cargo test --doc -- --ignored
Run all benchmarks
$ cargo bench
This runs all benchmarks and compares against the previous run.
Run specific benchmark
# Run only GUID operation benchmarks$ cargo bench --bench guid_operations
# Run only page operation benchmarks$ cargo bench --bench page_operations
# Run only browser operation benchmarks$ cargo bench --bench browser_operations
View results
After running benchmarks, open the HTML report:
$ open target/criterion/report/index.html
Baseline Management
Baselines allow you to track performance over time and compare against specific commits.
Save a baseline
# Save a baseline with a descriptive name$ cargo bench -- --save-baseline before-guid-optimization
# Or use a commit hash for reference$ cargo bench -- --save-baseline baseline-c3c16f6
This saves the current benchmark results as a named baseline for future comparison.
Compare against a baseline
# Compare current performance against a saved baseline$ cargo bench -- --baseline before-guid-optimization
Criterion will show percentage changes compared to the baseline.
List saved baselines
Baselines are stored in target/criterion/*/base/. Each benchmark stores its own baseline:
# List all baselines$ find target/criterion -type d -name base
# Or check a specific benchmark$ ls target/criterion/guid_operations/*/
Tracked Benchmarks
GUID Operations
- String Clone: Time to clone a GUID string
- Arc Clone: Time to clone an Arc GUID
- HashMap Lookups: Comparison of String vs Arc in HashMaps
Target: Arc should be 5x+ faster for cloning, 2x+ faster for lookups
Page Operations
- Page navigation (goto, reload)
- Element queries (locator operations)
- JavaScript evaluation
- Screenshots
Browser Operations
- Browser launch times (Chromium, Firefox, WebKit)
- Browser context creation
- Page creation
Visualization with Gnuplot
If you have gnuplot installed, criterion will generate SVG plots in addition to HTML reports:
# macOS$ brew install gnuplot
# Linux$ sudo apt-get install gnuplot
Criterion automatically detects gnuplot and generates plots. View them in the HTML report at target/criterion/report/index.html.
Optimization Workflow
When implementing performance optimizations, use this workflow to track improvements:
1. Save baseline before changes
# Record current performance$ cargo bench -- --save-baseline before-guid-optimization
2. Implement optimization
Make your code changes.
3. Compare against baseline
# See the performance impact$ cargo bench -- --baseline before-guid-optimization
Criterion will show percentage changes for each benchmark. Look for:
- 🟢 Green = Performance improved (faster)
- 🔴 Red = Performance regressed (slower)
- ⚪ White = No significant change
4. Document results
In the implementation plan, document:
- Baseline metrics (before)
- Optimized metrics (after)
- Percentage improvement
- Any trade-offs
5. Commit changes
Once satisfied with the improvements, commit your code.
Benchmark Structure
Benchmarks are organized in crates/playwright/benches/:
<span>guid_operations.rs</span>– GUID string performance (clone, HashMap operations)<span>page_operations.rs</span>– Page navigation, locators, JavaScript evaluation<span>browser_operations.rs</span>– Browser launch, context creation
Each benchmark uses criterion groups for organization:
fn benchmark_guid_string_operations(c: &mut Criterion) { let mut group = c.benchmark_group("guid_operations");
group.bench_function("string_clone", |b| { // benchmark code });
group.finish();}
Performance Targets
Performance optimization goals are documented in the relevant implementation plan slices. When working on performance improvements:
- Check the implementation plan for specific targets
- Save a baseline before starting work
- Implement the optimization
- Compare against the baseline to measure improvement
- Document actual results in the implementation plan
Tips
- Benchmarks run multiple iterations to reduce noise
- Close other applications for consistent results
- Browser benchmarks are slow – use longer sample times
- Use
<span>--sample-size</span>to adjust iteration count:
cargo bench -- --sample-size 10
- Filter benchmarks with patterns:
cargo bench guid # Runs only benchmarks matching "guid"