CI
This page covers how the Playwright suite runs in CI: the shared runner script, the official Playwright Docker image, browser selection, and the validator pipeline.
The shared runner script
All CI pipelines (local Jenkins, DevOps/main Jenkins) call the same script:
ci/run-e2e.sh
The script is driven entirely by environment variables — no pipeline-specific logic lives in the pipelines themselves.
Environment variables
| Variable | Default | Purpose |
|---|---|---|
TEST_ENV | dev2 | Target environment (dev2, dev3, qa2, demo5, local) |
WORKERS | 4 | Playwright --workers parallelism |
TESTS | (blank = full suite) | Space-separated spec paths/dirs under tests/e2e/ |
GREP | (blank = no filter) | Title regex passed to Playwright -g |
WAIT_URL | (blank = skip) | URL to poll until reachable before testing (for async deploys) |
PW_CHANNEL | chromium | Browser channel — see Browser selection |
INSTALL_DEPS | true | Run pnpm install --frozen-lockfile before testing |
What the script does
- Optionally runs
pnpm install --frozen-lockfile(idempotent in CI images that cachenode_modules). - If
WAIT_URLis set, polls it withcurlup to 5 minutes, treating any non-5xx response as "up" — a redirect to login (302/401) is acceptable. - Builds the Playwright argv array and calls
pnpm exec playwright test— noeval, soTESTSandGREPcannot be shell-interpreted.
The script sets CI=true unconditionally so playwright.config.ts activates
CI-mode settings (see below).
CI-mode settings in playwright.config.ts
When process.env.CI is set, the config applies three changes:
| Setting | Value in CI | Value locally |
|---|---|---|
retries | 2 | 0 |
workers | 1 | Playwright default (CPU-based) |
forbidOnly | true | false |
forbidOnly: true causes the run to fail immediately if a test.only was
accidentally committed to the spec files.
Note: ci/run-e2e.sh passes --workers=${WORKERS} on the command line (default
4), which overrides the workers: 1 from the config. The script's intent is
to allow parallelism control at the pipeline level while retaining the safety
net (forbidOnly, retries) that CI=1 provides.
Browser selection
playwright.config.ts reads PW_CHANNEL to select the browser:
PW_CHANNEL=chrome(or omitted locally) — uses the branded Google Chrome channel. Available on macOS and Windows; not available in Linux containers.PW_CHANNEL=chromium— uses Playwright's bundled Chromium build. This is the default insideci/run-e2e.shand the correct value for any Linux/arm64 environment, including the official Playwright Docker image.
When PW_CHANNEL=chromium, the config omits the channel field from the
project definition, which tells Playwright to use the bundled build rather than
a system browser.
Official Playwright Docker image
The recommended CI runner image is:
mcr.microsoft.com/playwright:v<version>-noble
where <version> matches the @playwright/test version pinned in
package.json. This image ships with:
- Bundled Chromium (and other browsers) plus all OS-level dependencies.
- Node and
corepack, so pnpm is activated automatically from thepackageManagerfield inpackage.json.
Because browsers are baked into the image, no separate playwright install
step is needed in CI. On a bare host (without the Docker image), install once
with:
pnpm exec playwright install --with-deps chromium
Validator pipeline
In addition to the Playwright test run, CI runs the full validator suite:
pnpm run validate
This is a shorthand that expands to:
pnpm run checkTs # tsc --noEmit (infra code only; specs are smoke-run)
pnpm run lint # eslint
pnpm run format:check # prettier --check
pnpm run test:validate-data # Zod-validates tests/data/*.jsonc
pnpm run test:validate-spec # validates tests/specs/**/*.md frontmatter + structure
All five must pass before a build is considered green. checkTs is scoped to
the framework infra; Playwright spec files are transpiled on-the-fly by the
runner and are not type-gated by this check.
Credentials in CI
The runner script expects a .env file at the repo root containing
TEST_USERNAME and TEST_PASSWORD. In CI, inject these as pipeline secrets
and write the file before invoking the script — for example:
echo "TEST_USERNAME=$SECRET_USER" > .env
echo "TEST_PASSWORD=$SECRET_PASS" >> .env
bash ci/run-e2e.sh
No other environment-specific values need to be in .env: project IDs,
estimate names, and similar data live in tests/data/*.jsonc and are version-
controlled.