Skip to content

Config and the test runner ​

lightningpanda test is the counterpart of playwright test. It reads your config, finds each project's test files, starts web servers and global setup once, then runs the files in worker processes. Each worker is a bun test process with its own Pyro browser. This page lists every flag, every config field that is honoured, and how workers split the work.

Usage ​

sh
lightningpanda test [options] [test-filter...]

A test-filter is a regular expression. A file runs when its absolute or working-directory-relative path matches any filter. Playwright's file.spec.ts:42 line filters are not supported.

The command needs Bun on PATH. When the CLI is started by Node, it starts itself again under Bun, and fails with "needs Bun (https://bun.sh) on PATH" if Bun is missing.

Flags ​

FlagMeaning
-c, --config <file>Config file. Default: lightningpanda.config.*, else playwright.config.* in the working directory.
--project <name>Run only this project. Repeatable. Without it, every project's files run.
-j, --workers <n|n%>Worker processes. Default: the config's workers, else half the CPUs. 50% is a share of the CPUs.
-g, --grep <regex>Only tests whose title path (file, describes, title, tags) matches.
--grep-invert <regex>Skip tests whose title path matches.
--retries <n>Retries of a failing test. Overrides the config.
--timeout <ms>Test timeout. Overrides the config and every project.
-u, --update-snapshots [mode]Rewrite snapshots and screenshot baselines. changed (the default), all, missing or none. See Snapshots.
--fully-parallelRun every file's tests in parallel, as fullyParallel: true.
-xStop after the first failure (same as --max-failures 1).
--max-failures <n>Stop after n failures.
--reporter <name>list (default), line or dot.
--listList the tests without running them.
-q, --quietDo not print the tests' own output.
-h, --helpPrint the help.
--headed, --debug, --uiPrint a warning and have no effect: Pyro is headless.

Any other flag (--shard, --trace, --repeat-each, --last-failed, --forbid-only, --output, ...) is an error: the command prints the help and exits with status 2.

Config fields ​

defineConfig(config, ...overrides) merges later configs over earlier ones, with use and expect merged key by key, as Playwright does. A plain exported object works too.

Honoured ​

FieldWhereNotes
useconfig, projectThe project's use is merged over the config's. See use options.
projectsconfigEach has name, use, testDir, testMatch, testIgnore, outputDir, timeout, retries, snapshotDir, snapshotPathTemplate, expect, metadata.
testDirconfig, projectDefault: the config file's directory.
testMatchconfig, projectDefault `**/*.@(spec
testIgnoreconfig, projectSame matching as testMatch.
timeoutconfig, projectTest timeout. Default 30 000 ms.
retriesconfig, projectDefault 0.
expect.timeoutconfigDefault timeout of web-first assertions and expect.poll. Default 5 000 ms.
expect.toHaveScreenshotconfig, projectthreshold, maxDiffPixels, maxDiffPixelRatio defaults, and pathTemplate.
expect.toMatchSnapshotconfig, projectthreshold, maxDiffPixels, maxDiffPixelRatio defaults.
expect.toMatchAriaSnapshotconfig, projectchildren (contain, equal, deep-equal) and pathTemplate.
outputDirconfig, projectDefault test-results. Each test writes into a sub-directory named after its title path.
snapshotDir, snapshotPathTemplateconfig, projectWhere snapshots live. See Snapshots.
updateSnapshotsconfigDefault update mode when -u is not given. Default missing.
webServerconfigOne or a list. See below.
globalSetup, globalTeardownconfigA path or a list of paths. See below.
workersconfigA number or a percentage string.
fullyParallelconfigTop level only.
metadataconfig, projectMerged into testInfo.project.metadata.

A project's expect replaces the top-level expect instead of merging with it, as in Playwright.

Accepted and ignored ​

These typecheck but do nothing: reporter, globalTimeout, maxFailures (use --max-failures), forbidOnly, reportSlowTests, quiet, preserveOutput, captureGitInfo, tsconfig, shard, and on projects grep, grepInvert, dependencies, repeatEach, teardown and fullyParallel. In webServer, gracefulShutdown, name and ignoreHTTPSErrors are ignored.

Project dependencies are worth a note: a "setup" project that logs in and saves storageState does not run first. Run that code in globalSetup instead.

Projects ​

lightningpanda test collects the files of every project (or those named with --project). Each file runs once, under the first project whose testDir, testMatch and testIgnore include it. Playwright would run such a file once per project.

Under plain bun test, the project is $LP_PROJECT if set, else the project whose files include the test file, else the first one.

use options ​

All Playwright use options are accepted. These take effect:

OptionEffect
baseURLResolves relative URLs in goto, waitForURL, toHaveURL, routes and request.
viewportDefault { width: 1280, height: 720 }. null leaves the browser's own size.
storageStateCookies and localStorage (path or object). See Network.
extraHTTPHeadersSent with every page request and by request.
userAgentOverrides the page's user agent.
colorScheme, reducedMotionMedia emulation for @media and matchMedia.
httpCredentialsUsed by the request fixture only (Basic auth); pages do not use it.
ignoreHTTPSErrorsUsed by the request fixture only.
actionTimeout, navigationTimeoutDefault timeouts. With neither set, an action or navigation waits up to 30 s (Playwright waits until the test times out).
testIdAttributeThe attribute getByTestId matches. Default data-testid.
contextOptionsMerged under the named options.
acceptDownloadsDefault true.
lightningpandaHow tests reach the browser. See below.

These are accepted and have no effect on pages: bypassCSP, deviceScaleFactor, geolocation, hasTouch, isMobile, javaScriptEnabled, locale, offline, permissions, proxy, timezoneId, serviceWorkers, and the worker options browserName, channel, headless, launchOptions, connectOptions, screenshot, video, trace. A proxy goes on the browser instead: lightningpanda: { args: ['--proxy', 'http://host:3128'] }.

browserName still sets the browserName fixture, so test.skip(({ browserName }) => browserName === 'webkit') behaves as the config says.

use.lightningpanda ​

ts
use: {
  lightningpanda: {
    wsEndpoint: 'ws://127.0.0.1:9222/',     // connect to a running server instead of launching one
    bin: '/path/to/lightningpanda',          // the binary to launch
    args: ['--static', 'http://app.test=./dist'], // extra `lightningpanda serve` flags
    boot: [{ url: '/platform', routes: '/platform/**' }], // pages booted ahead
  },
},
FieldDefaultMeaning
wsEndpoint$LIGHTNINGPANDA_WSA running lightningpanda serve to connect to. Without it, each worker launches its own browser.
bin$LIGHTNINGPANDA_BIN, else the bundled binaryThe binary each worker launches.
argsnoneExtra lightningpanda serve flags, for example --static, --proxy, --hyphen-digit-breaks on. See serve options.
bootnonePages booted ahead of the tests. See Booting pages ahead.

lightningpanda is a worker option, so test.use({ lightningpanda: { ... } }) in a file works. Each distinct value gets its own browser in the worker.

With wsEndpoint (or $LIGHTNINGPANDA_WS), all workers share one server, one connection each. Start it yourself, for example lightningpanda serve --port 9222, or in globalSetup.

Environment variables ​

VariableMeaning
LP_CONFIGConfig file path. PLAYWRIGHT_CONFIG is read when it is unset.
LP_PROJECTProject for plain bun test runs.
LP_UPDATE_SNAPSHOTSUpdate mode (all, changed, missing, none; 1 or true mean changed). Set by -u.
LP_RETRIES, LP_TIMEOUTOverride retries and test timeout. Set by --retries and --timeout.
LP_CONCURRENT1 runs the tests of a parallel describe concurrently inside one worker (see Workers).
LIGHTNINGPANDA_WSConnect to this server instead of launching one.
LIGHTNINGPANDA_BINBinary to launch.

See Environment variables for the browser's own variables.

Workers ​

The runner starts workers processes at once. Each runs bun test with its own browser and its own module state. Files are dealt out whole: sorted by size, largest first, round robin over the workers.

  • A file's tests run in order, in one worker, unless they are parallel.
  • Parallel tests (fullyParallel, --fully-parallel, or a describe with mode: 'parallel') spread over the workers by their position in the file. A describe's beforeAll and afterAll run in each worker that runs some of its tests, and in no other.
  • A describe in serial mode, or default mode set with test.describe.configure, keeps its tests on the file's worker.
  • Inside a worker, parallel tests still run one after another, since they may share state set in hooks. Set LP_CONCURRENT=1 to run a parallel describe's tests concurrently in the worker (Bun's test.concurrent).
  • Web servers and global setup run once, in the parent process, before any worker starts.
  • A worker that gets no tests exits before it starts a browser.

--list uses one worker.

The worker count is not capped by the number of files, because parallel describes can spread one file over every worker. With few files and no parallel tests, lower -j to avoid idle processes.

Web servers ​

webServer is one object or a list. For each:

  1. If url (or port) already answers, the server is reused, unless reuseExistingServer: false, which is an error.
  2. Otherwise command runs in a shell, in cwd (relative to the config file) with env added, in its own process group.
  3. The runner polls url (any status below 404, or 405) or the TCP port every 100 ms until it answers, for up to timeout (default 60 000 ms). It fails if the command exits first.

stdout: 'pipe' shows the server's output; stderr is shown unless stderr: 'ignore'. The servers are stopped after the run, also when the run ends early.

Global setup and teardown ​

globalSetup and globalTeardown take a path or a list of paths, relative to the config file. Each module's default export (or the module itself) is called with the config. A function returned by a global setup runs at teardown, as in Playwright. Teardowns run after the last worker ends.

Reporting ​

The report shows each test as it finishes: passes, failures, skips (declared, or test.skip() at run time), flaky tests (passed on a retry) and expected failures (test.fail()). The tests' own console.log output is printed per worker, prefixed with [w0], [w1], ... After the run come the failures with their errors and a summary.

text
Running 2 files using 4 workers

  ✓   1 e2e/todo.spec.ts › todos › adds a todo (359ms)
  ✘   2 e2e/todo.spec.ts › todos › ua and role (4962ms)
  -   3 e2e/todo.spec.ts › todos › runtime skip (skipped at run time)

  1 failed
  1 skipped
  1 passed (5.9s)
  • list prints one line per test.
  • line prints the same lines as list in this version.
  • dot prints one character per test: · pass, ± flaky, ° skipped, F failed.

There is no HTML, JSON or JUnit reporter. A worker that crashes outside a test (a syntax error in a test file, for example) is reported with the last 40 lines of Bun's output.

The exit status is 1 when any test failed or a worker crashed, else 0.

Plain bun test ​

sh
LP_CONFIG=playwright.config.ts bun test ./e2e/foo.spec.ts

This runs the files in one process with one browser. The config, web servers and global setup are loaded on the first file; teardowns run after the last file. Bun's own flags apply (-t for names, --bail, -u for snapshots).

Differences from lightningpanda test: no workers or fullyParallel spreading, Bun's reporter instead of the runner's, and a run-time test.skip() is reported by Bun as a pass (it prints ↳ skipped: <title> to the log).

AGPL-3.0-only. A derivative of Lightpanda. Built by Povio.