Skip to main content
Validate extension behavior across browsers with repeatable end-to-end tests. Extension.js projects can use Playwright to test extension flows, UI rendering, and integration behavior in continuous integration (CI) and local environments.

Playwright testing capabilities

Why use it

  • Catch runtime regressions that unit tests miss.
  • Validate extension behavior on real browser engines.
  • Verify multi-browser changes before release.

Typical setup

Install Playwright test dependencies in your project:
Create a playwright.config.ts and define browser projects and reporting.
For deterministic automation, do not parse terminal text. Use the metadata files that Extension.js generates:
  • dist/extension-js/<browser>/ready.json
  • dist/extension-js/<browser>/events.ndjson (newline-delimited JSON for watch/rebuild events)
ready.json includes stable fields intended for scripts/agents:
  • status: starting | ready | error
  • command: dev | start | preview
  • browser
  • distPath
  • manifestPath
  • port
  • pid
  • runId
  • startedAt
  • compiledAt
  • errors
Use this contract as the source of truth for readiness and failures.

Running tests

Canonical Playwright flow (AI-friendly)

  1. Start Extension.js in no-browser mode.
  2. Wait until ready.json reports status: "ready".
  3. Launch Playwright with the extension output from distPath.
  4. Run tests and shut down.

Development mode vs test mode

  • dev is for watch-mode iteration (extension dev --no-browser + extension dev --wait).
  • start is for production-style checks (extension start --no-browser + extension start --wait).

Important distinction: run mode vs readiness gate

  • --no-browser is the run mode: it starts the extension pipeline without launching a browser.
  • A readiness gate (extension dev --wait --browser=<browser>) is the synchronization step that tells Playwright when the extension is ready.
--no-browser produces build output. The wait step confirms readiness before tests proceed. If your environment cannot run a second CLI process, poll ready.json directly. Use this two-process pattern:
  1. Process A: extension dev --no-browser
  2. Process B: extension dev --wait --browser=<browser> --wait-format=json
  3. Start Playwright only after the wait step exits successfully
Production-oriented variant:
  1. Process A: extension start --no-browser
  2. Process B: extension start --wait --browser=<browser> --wait-format=json
  3. Start Playwright only after the wait step exits successfully

Practical guidance for extensions

  • Keep test fixtures deterministic; extension startup can be sensitive to profile state.
  • Prefer explicit waits on extension UI conditions over fixed timeouts.
  • Run Chromium and Firefox projects in CI for cross-engine confidence.
  • Capture traces/screenshots/videos on failure for faster debugging.
  • Prefer ready.json/events.ndjson over stdout parsing for machine reliability.

Common pitfalls

  • Relying on fixed timeouts instead of state-based waits
  • Running only one browser target in CI
  • Skipping artifact upload for failed runs
  • Coupling tests to local-only profile or environment assumptions

Repository reference

Your repository includes a Playwright configuration at:
  • playwright.config.ts

Next steps