Playwright
@rstest/playwright provides Playwright fixtures and Playwright-style assertions for Rstest tests that run in Node.js workers. Use it for E2E tests against a complete page or app, such as a local dev server, a preview server, or a deployed URL. The test runs in Node.js and uses Playwright to drive the page.
vs native Playwright
@rstest/playwright and native Playwright use different runners and configuration files:
Use @rstest/playwright when you want Playwright-driven E2E tests to run in the same Rstest workflow as the rest of your tests. Use native Playwright when you want the full Playwright Test runner workflow and its configuration model.
vs Rstest browser mode
The main difference is the testing scenario: Rstest browser mode web-bundles test modules and runs test code in a browser runtime, while @rstest/playwright controls a page that is already prepared by your app or server.
Because @rstest/playwright controls an external page instead of running the test in Rstest's browser runner, it does not use the Browser UI preview iframe. For visual debugging, use headed mode with PWDEBUG=1.
Migrate to @rstest/playwright
To migrate an existing Playwright Test project to @rstest/playwright, use the migrate-to-rstest skill with a Coding Agent that supports Skills. Agent Skills are domain-specific knowledge packs that help Coding Agents provide more accurate suggestions or perform actions for a specific scenario. The skills package installs these skills into a Coding Agent. The migrate-to-rstest skill includes Playwright-specific migration guidance for configuration, fixtures, and behavior parity.
Install the skill first:
Then copy the following prompt and send it to your Coding Agent:
Copy this prompt and send it to your Coding Agent.
Install
Install both packages:
- @rstest/playwright adds the Rstest fixtures and assertions.
- playwright provides the browser automation runtime.
Install the Chromium browser binary used by Playwright:
Basic usage
Import test and expect from @rstest/playwright instead of @rstest/core:
Regular lifecycle helpers are also available from @rstest/playwright:
If your test modules do not rely on side effects that need isolation, set isolate: false in rstest.config.ts to reuse the worker module cache across test files and avoid repeated Playwright startup cost:
Configure playwright options
Global Playwright configuration in rstest.config.ts is not supported yet. If multiple test files use the same options, define a shared module that overrides the playwright fixture with test.extend, then import test and expect from that module in every test file:
If only a few tests need different options, call test.extend again from those test files using the shared test as the base. Overriding the playwright fixture replaces its value instead of merging it, so the new value must include every shared option that the test should preserve.
The playwright fixture supports these options:
Fixtures
@rstest/playwright provides these fixtures:
The sections below show how each fixture is commonly used. page and serve link to the existing examples to avoid repeating the same code.
Using fixtures in hooks
Suite-level hooks can request fixtures provided by the tests in that suite. Specify the hook's fixture context type explicitly; a fixture used only by a hook does not need auto: true:
Hooks are scoped to their describe block, not to an extended test object. Every test in the block must provide the fixtures requested by the hook; otherwise, Rstest fails that test before invoking the hook and reports the missing fixture. The same behavior applies to afterEach and to cleanup functions returned by beforeEach. Fixture instances are shared across the hooks and test body for one test attempt, then torn down in reverse setup order.
Declare fixture dependencies through direct object destructuring in the hook parameter. Destructuring a named context inside the hook body does not request fixtures. Rest properties and default values are not supported in fixture-aware callbacks.
Using fixtures with test.for
Destructure fixtures directly from the second callback parameter when using test.for:
A named second parameter can still access built-in TestContext APIs such as task and expect, but property access or destructuring through that name does not request fixtures.
browser
Use browser when you need to create a custom browser context yourself:
context
Use context when one test needs multiple pages that share the same browser context:
page
See Basic usage for the common E2E flow of opening and asserting a page.
request
Use request when you only need Playwright's API client and do not need to launch a browser:
serve
See Local app server for serving a built app from local files.
Assertions
expect keeps normal Rstest assertions. When the actual value is a Playwright Locator or Page, it also provides retrying Playwright-style async assertions.
Locator assertions target Playwright Locator values and follow the naming of Playwright Locator assertions where possible. They are aligned with the element assertions already supported by @rstest/browser where possible:
toBeVisible(options?)toBeHidden(options?)toBeEnabled(options?)toBeDisabled(options?)toBeChecked(options?)toBeUnchecked(options?)toBeAttached(options?)toBeDetached(options?)toBeEditable(options?)toBeFocused(options?)toBeEmpty(options?)toBeInViewport(options?)toContainText(expected, options?)toHaveAttribute(name, expected?, options?)toHaveClass(expected, options?)toHaveCSS(propertyName, expected, options?)toHaveCount(expected, options?)toHaveId(expected, options?)toHaveJSProperty(name, expected, options?)toHaveText(expected, options?)toHaveValue(expected, options?)
Page assertions:
toHaveTitle(expected, options?)toHaveURL(expected, options?)
String text assertions normalize whitespace. Playwright assertions retry until they pass or the timeout option is reached. The default timeout is 5000 milliseconds.
.not and expect.soft are also supported:
Trace debugging
Set playwright.trace or RSTEST_PLAYWRIGHT_TRACE to capture Playwright's official trace.zip artifact from the context fixture. The trace covers the default page fixture and pages created with context.newPage(). Fixture configuration takes priority over the environment variable, then falls back to off.
For temporary CLI-style debugging without changing test code, set RSTEST_PLAYWRIGHT_TRACE:
Use RSTEST_PLAYWRIGHT_TRACE_OUTPUT_DIR to override the default output directory when trace is enabled by the environment variable:
trace accepts 'off', 'on', 'retain-on-failure', 'on-first-retry', 'on-all-retries', or an options object:
on-first-retry records and keeps a trace only for the first retry. on-all-retries records and keeps a trace for every retry. Neither mode starts tracing during the initial attempt, so passing tests avoid trace startup and temporary artifact work.
By default, traces are written to .rstest/playwright-traces/<test-name>-<hash>/. If the same test saves multiple traces, for example across retries, later attempts use a numeric suffix to avoid overwriting earlier traces. Every saved trace contains:
trace.zip: Playwright's official trace artifact. Open it withnpx playwright show-trace <path-to-trace.zip>.
When summary is enabled (the default), the directory also contains:
trace-summary.json: Rstest-aware test metadata, artifact paths, and error stacks for tools and AI assistants.debug.md: a human-readable debugging report.
trace.zip is not a generic Chrome/Perfetto trace. It is Playwright's trace format and is intended to be inspected with Playwright Trace Viewer.
Local app server
Use the serve fixture when a test needs to serve a built app. It starts a static server for the entry file and automatically stops the server after the test.
When PWDEBUG=1 is enabled, serve keeps the server alive by default so the opened page remains available for inspection. In non-watch runs, this may keep the Rstest process open until you stop it manually. Set keepAliveOnDebug: false if you want the server to close even in debug mode.
Headed debugging
Set PWDEBUG=1 to launch Chromium in headed mode while debugging locally:
This environment variable keeps your tests unchanged and applies these defaults:
headless: falseslowMo: 100devtools: true
You can also override the debug defaults from the test:
To stop on a page while debugging, use Playwright's page.pause() with a zero test timeout:
In debug mode, failed tests automatically call page.pause() before closing the page and context. Set pauseOnFailure: false in debug options, or RSTEST_PLAYWRIGHT_PAUSE=false, to disable this behavior.
For non-interactive debugging in CI or local runs, capture a screenshot when a test fails:
This keeps the test runner non-blocking while preserving the failed page state as an artifact. See examples/playwright for a complete Rsbuild + Playwright example.