Parallel runs
Set worker and browser counts, and protect shared data while test files run in parallel.
Run independent test files together to reduce the time your suite takes. This page shows how to set concurrency and keep tests from changing accounts or server data that another test needs.
Set the worker count Link to Set the worker count
Each worker runs one file at a time in its own process, with that file's tests in sequence. Use --workers to control how many files run together.
npx retest run --workers 4npx retest run --workers 1The first command allows four files at once, while the second runs files sequentially. The default is half the machine's available cores, rounded down and at least one. Sign-in setups run first, one after another, before tests restore their saved state.
Every browser test gets a fresh context and page for each app it uses. Cookies and browser storage are separate, although the browser process may be shared. Module state and unfinished callbacks remain shared by the tests of one file process.
Spread work over browsers Link to Spread work over browsers
If one browser process becomes a bottleneck, use --browsers to spread a target's work over several processes. Each worker stays on one browser.
npx retest run --workers 6 --browsers 2For a target running the whole suite, this allows six file workers across two browsers. By default, Retest uses one browser per three workers that have files, rounded up. Targets running part of a matrix receive a share, capped by the files and workers that use them.
The human report says how many browsers each target used. Compare runs on your own app before raising the counts; more workers also increase load on the service those pages use.
Protect shared state Link to Protect shared state
A fresh context leaves shared backend data unchanged. If tests use the same inbox, account or counter, declare a lock and give it to every test that accesses that data.
import { chrome, defineConfig } from '@rehearsal-labs/retest'const config = defineConfig({ apps: { web: chrome({ baseUrl: 'http://127.0.0.1:4173' }) }, locks: ['saves'],})export default configdeclare module '@rehearsal-labs/retest' { interface Register { config: typeof config }}import { expect, test } from '@rehearsal-labs/retest'test('saves a task', { locks: ['saves'] }, async ({ page }) => { await page.goto('/') await page.getByTestId('task-title').fill('Release checklist') await page.getByTestId('save-task').click() await expect(page.getByTestId('saved-task')).toHaveText('Release checklist')})Tests holding saves take turns across files, workers and targets in this run. Other tests can continue alongside them. A test.describe block can pass locks to all its tests, and a test's own locks add to those inherited names.
Before launching its apps, a test takes all its locks together or waits with none. The wait uses none of its test budgets, and the report names the test that held the lock. Declare every lock name in the config.
Named locks belong to one run. Separate runs do not share them, so use separate accounts or coordinate outside Retest when several jobs access the same backend state.
Native and Electron resources Link to Native and Electron resources
Retest also reserves the desktop for a macOS app, the simulator for an iOS app and an Electron target's persistent data folder. A test acquires everything it needs before any app launches, and releases it after confirmed shutdown.
These reservations are shared across runs within one process. They do not coordinate every resource across separate processes, though the native desktop has its own machine lock. If shutdown cannot be confirmed, the resource remains held, and a waiting test may end with setup_failed.
Limit sessions across host runs Link to Limit sessions across host runs
A host calling runFiles can share a SessionBudget between runs. A session is one app session, such as a browser context and page; it is not a browser process.
import { SessionBudget } from '@rehearsal-labs/retest/runner'const budget = new SessionBudget({ perOwner: 4, host: 8 })// Add this option to each runFiles call that shares the budget.const sessions = { owner: 'worker-1', budget }Pass sessions in each run's options, using the appropriate owner name. An attempt reserves its apps together, so it never holds some sessions while waiting for the rest. The shared budget limits one owner to four and all owners together to eight in this process. Use Retest from code shows the host call.