Less test plumbing
The runner handles browser startup, JSON-lines request/response matching, deadlines, and shutdown. No Node.js, Python, or separate test framework is needed to execute the tests.
Small files. Repeatable checks.
Describe a browser flow in a few readable lines. Bruster runs the actions, waits for your assertions, and captures evidence when a test fails.
A domain-specific language, or DSL, gives you a small vocabulary for one job. In a .bru file, commands such as navigate, fill, click, and expect describe a UI check. Run the file with bruster test.
The runner handles browser startup, JSON-lines request/response matching, deadlines, and shutdown. No Node.js, Python, or separate test framework is needed to execute the tests.
Failures identify the file and line. The runner attempts a screenshot, DOM snapshot, and console capture, and records test results in JSON.
It can reduce the time spent writing and debugging repeated smoke tests: setup is reusable, assertions wait for the UI, and diagnostics are automatic. Browser execution still uses the same native webview. Each test starts a fresh browser and profile for isolation, which has a startup cost.
The DSL is available in source builds from main. Older release binaries may not include bruster test yet. See the installation and source-build instructions.
For a hands-on walkthrough, read the web automation testing tutorial, try the interactive form demo, and run the website and component example tests.
Starting without programming experience? Learn website testing in twenty small lessons, from a manual test plan to your first automated project.
From a source checkout with Bruster’s build dependencies installed, build the executable:
cargo build --release --lockedStart the included greeting page in one terminal. This example uses Python only to serve the HTML:
python3 -m http.server 8000 --directory examples/testingIn another terminal, run the example suite:
target/release/bruster test examples/testing/greeting.bruOn Windows, use target\release\bruster.exe. Linux needs WebKitGTK and a display or Xvfb. Add --visible to watch the browser.
The example suite greets a visitor and checks that the next test starts with an empty greeting:
base_url "http://127.0.0.1:8000"
timeout 5000
before_each
navigate "/"
end
test "greets a visitor"
fill "#name" "Ada"
click "button"
expect text "#result" "Hello, Ada!"
screenshot "greeting.png"
end
test "starts with an empty greeting"
expect title "Greeting demo"
expect visible "#name"
expect text "#result" ""
end\", \\, and \n work.# starts a comment outside a quoted string.test "name" … end. Test names must be unique within a file.before_each … end block runs before every test in that file.base_url resolves relative navigation and URL assertions; timeout sets the per-step deadline in milliseconds.Common actions cover clicking, filling, typing, key presses, selecting options, checking boxes, uploading a file, taking screenshots, and browser history. eval "JavaScript" executes application-specific setup once. Uploaded file paths are relative to the .bru file; screenshot paths are relative to the test’s artifact directory.
Run multiple files in order, and override their base URL for a different environment:
bruster test login.bru checkout.bru --base-url https://staging.example.com
bruster test tests/*.bru --timeout 15000 --viewport 1280x800
bruster test --helpThe shell expands *.bru. Pass individual files when your shell does not expand globs. The runner validates all input files before starting a browser.
Assertions retry observations until the step deadline, including while an element is still appearing. The default deadline is five seconds; set timeout in the file or --timeout on the command line.
expect visible "#dashboard"
expect hidden "#loading"
expect attached "#email"
expect text "h1" "Dashboard"
expect contains "#notice" "Saved"
expect title "My account"
expect url "/dashboard"
expect url_contains "/dashboard"
expect count ".result" 3
expect attr "#save" "aria-label" "Save changes"
expect js "document.querySelector('#terms').checked === true"expect text compares exact, whitespace-normalized element text. expect contains checks a substring. expect js requires the boolean true; keep its expression side-effect free because it may execute several times.
Actions execute once using Bruster’s existing actionability checks. A failure stops that test and the runner continues with the next one. Entire tests are not automatically retried.
Start your application server before the suite. Run tests with an explicit artifact parent directory:
bruster test login.bru checkout.bru --output artifacts/uiEach invocation creates a unique run directory and prints its location. Retain that directory as a CI artifact:
results.json: pass/fail totals, test names, source lines, step durations, errors, and artifact paths.test-NNN/stderr.log: browser process diagnostics.failure.png, snapshot.json, and console.json when a step fails.Each test owns a fresh browser process and profile, isolating cookies, storage, and page state. Startup, requests, shutdown, and failure capture all have deadlines.
| Exit code | Meaning |
|---|---|
0 | Every test passed. |
1 | A test or browser failed. |
2 | Invalid input, or report setup/write failure. |
The DSL fits smoke tests and straightforward flows such as forms, login, navigation, and asynchronous page updates. Bruster uses WKWebView on macOS, WebKitGTK on Linux, and WebView2 on Windows.
Input is synthetic. CSS :hover, native OS controls, and cross-origin iframe interaction are outside its current capabilities. The runner is sequential and has a small grammar; loops and general-purpose control flow are available through your own client around the JSON-lines API. Screenshots are evidence, with no built-in visual-diff assertions.
For selectable browser engines, tracing, parallel test orchestration, and a broader testing ecosystem, compare Bruster and Playwright. See the browser limitations and complete DSL command reference before choosing a test strategy.