skip to content

What does Playwright change about a run when the CI environment variable is set?

level: middleimportance: should knowfreq 44%

answer

  1. One ambient variable, one default
  2. Output shape, not run behaviour
  3. Terminal redraw versus append-only log
  4. One character per test result
  5. Starter config branches are yours

basics

~20 s

Only the default reporter: with CI set and no reporter configured, Playwright prints one character per test instead of the interactive line-per-test output. Everything else people attribute to it is written in the project's own config.

solid answer

~40 s

Playwright reads the conventional `CI` variable that providers set inside a job and uses it to pick a different default reporter: `list` locally, `dot` when `CI` is set. That is the whole framework-level switch, and it disappears the moment your config names a reporter. It exists because `list` redraws lines for a live terminal, which turns an append-only CI log for a large payroll regression suite into thousands of near-duplicate lines, while `dot` emits one character per result and keeps failure detail at the end. What it does not do: it does not choose worker counts, retries, or how a run exits. Those come from options you set — the generated starter config branches on `process.env.CI` itself, which is your code doing the switching, not the runner.

code

typescript · 6 lines
typescript
import { defineConfig } from '@playwright/test';

export default defineConfig({
  testDir: './tests/payroll',
  reporter: [['line'], ['html', { open: 'never' }]],
});

go deeper

for a junior

Know that Playwright notices the CI variable and prints compact dot output instead of the interactive list output, and that naming a reporter in the config overrides it.

for a middle

Explain why the default flips, and separate it clearly from the config ternaries on process.env.CI that projects write for themselves.

for a senior

Prefer an explicit reporter in version control over a default that changes with the environment, so a log's shape is a reviewed decision rather than an ambient one.

for a principal

Set the standard for how local and CI behaviour differ: keep the delta small, visible in one place, and justified, rather than scattered through ambient conditionals.

## What the variable actually switches Playwright reads the conventional `CI` environment variable — the one virtually every CI provider sets for you inside a job — and uses it to pick a different **default reporter**. With no reporter declared in `playwright.config.ts` or on the command line: - locally, you get `list`, which prints a line per test as it runs and rewrites it with the result; - with `CI` set, you get `dot`, which prints one character per test. That is the switch itself. It is a default, so it evaporates the moment your config names a reporter — which is what most real projects do — and it applies to output only. No test selection, no timing, no parallelism changes because the variable is present. ## Why the default flips at all Interactive output assumes a terminal that can redraw lines. A CI log is an append-only stream: every redraw becomes another line, so a `list` run of a large payroll regression suite produces a log with thousands of near-duplicate lines that is slow to load and hard to scan. `dot` writes one character per test result and a failure block at the end. The signal you want in a CI log — how many, which ones failed, and the failure text — survives; the progress animation that only makes sense on a live terminal does not. | | `list` (local default) | `dot` (CI default) | |---|---|---| | Output per test | A line, rewritten in place on completion | A single character | | Suits | A live terminal | An append-only log stream | | Failure detail | Printed at the end | Printed at the end | ## What it does *not* do This is the part interviews probe, because the mental model "setting CI makes Playwright CI-ready" is wrong and expensive: - It does **not** decide worker count, retries, or whether a stray focused test fails the run. Those are config options; if your config does not set them, `CI` changes nothing about them. - It does **not** install browsers or system dependencies. - It does **not** change the exit code contract or where artefacts are written. The reason people believe otherwise is the generated starter config, which branches on `process.env.CI` for several options. That is **your** code doing the switching, not the runner: the scaffolding writes those ternaries so the same config behaves differently in the two places. Read the config and you can see exactly which behaviours are yours and which are the framework's. ## Using it deliberately 1. Set the reporter explicitly in `playwright.config.ts` if the choice matters to you, rather than depending on a default that changes with the environment. A run whose output shape depends on an ambient variable is a run that surprises whoever reads the log next. 2. If your config branches on `process.env.CI`, keep every branch in one place so a reader sees the whole local-versus-CI delta at once. 3. Set `CI=1` yourself when you want CI behaviour on a workstation — for example reproducing a colleague's log format — and unset it in a container you are debugging interactively, where the live `list` output is more useful. 4. Do not *unset* it in a real CI job to get prettier output; you get a much larger log and no other benefit. ## Reading a dot run Each character is a result, so a wall of dots with a few marks in it tells you the run's shape at a glance, and the detail you actually need — the failing tests, their errors and the summary counts — comes after. If a team finds that unreadable, the answer is to name a reporter in the config, not to fight the environment variable. That also makes the choice reviewable: a reporter in version control is a decision someone made, while a default that flips on an ambient variable is a decision nobody made.

  • Why is dot a better default than list in a CI log?
    The list reporter rewrites a line in place as each test finishes, which needs a terminal that can redraw. An append-only log turns each redraw into another line, so a large suite produces an enormous, unreadable file. The dot reporter emits one character per result and prints the failure detail at the end, which is the part you actually read.
  • Why do people think the variable changes more than the reporter?
    Because the generated starter config branches on process.env.CI for several options, so the same file behaves differently in the two environments. That is project code, not framework behaviour. Reading the config tells you exactly which differences you own and which the runner provides.

saying these in an interview costs you the question

  • Believes CI automatically enables retries
  • Thinks the variable changes worker counts by itself
  • Assumes it installs browsers or dependencies
  • Unsets CI in a job to get prettier output
  • Cannot say which behaviours come from the config