skip to content

Runner Settings

The config file as the one place a run is shaped: the projects it splits into, the setup that has to pass first, and the time each layer is allowed to take before it gives up.

on this pageshow

explore

questions

15

What does Playwright's webServer config option do before the first test runs?

level: juniorimportance: must knowfreq 71%

answer

  1. Config key that boots the app
  2. Waiting before the first test
  3. Two ways to prove readiness
  4. Different answer locally and in CI

basics

~10 s

Playwright's webServer option spawns a command before the suite and waits until the url or port it names answers, then runs the tests and stops the process it started when the run ends.

solid answer

~40 s

`webServer` in `playwright.config.ts` names a `command` Playwright spawns before any test runs, plus either a `url` it polls until the response status is 2xx, 3xx, 400, 401, 402 or 403, or a `port` it waits to accept connections. Only when that readiness check passes does the runner start tests, so the internal admin console is guaranteed to be up. `reuseExistingServer: !process.env.CI` is the usual setting: locally it attaches to a dev server you already have running, while in CI it insists on starting its own. `cwd`, `env`, `stdout`, `stderr` and `gracefulShutdown` shape the child process, and `timeout` bounds the wait -- exceeding it fails the run before any test executes. Playwright kills the process it started at the end, and since 1.43 the key also accepts an array when a suite needs several services.

code

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

export default defineConfig({
  use: { baseURL: 'http://localhost:4173' },
  webServer: {
    command: 'pnpm run start:admin-console',
    url: 'http://localhost:4173/health',
    timeout: 120_000,
    reuseExistingServer: !process.env.CI,
    stdout: 'pipe',
  },
});

go deeper

for a junior

Know that this config key starts the app for you and waits for it, and be able to point at the command plus url or port pair in a config you are shown.

for a middle

Explain the readiness check itself: which statuses count as up, how port differs from url, what the timeout bounds, and why the process is killed when the run finishes.

for a senior

Show the CI judgment: reuse locally but never in CI, pipe server logs so a startup failure is diagnosable, and know that each shard starts its own server.

for a principal

Own the choice of whether the suite starts the application at all, versus testing a deployed environment, and be clear about what each option costs in reproducibility.

## What the option actually does Playwright's `webServer` block in `playwright.config.ts` hands the lifecycle of the application under test to the test runner. You give it a `command` -- the same shell line you would type by hand to start the internal admin console -- and Playwright spawns that command as a child process before it starts executing tests. It then blocks, polling for a readiness signal, and only once that signal arrives does the first test file run. When the run ends, Playwright terminates the process it started. No test code has to know that the server was started for it; the suite simply finds the app already listening. ## Two ways to say "ready" Exactly one of `url` or `port` is required, and the choice decides how Playwright probes for readiness. | Option | Readiness check | When to reach for it | |---|---|---| | `url` | Requests that URL until the response status is 2xx, 3xx, 400, 401, 402 or 403 | An HTTP app, where a health route -- or even a redirect to sign-in -- proves the process is answering | | `port` | Waits until that port on `localhost` accepts a TCP connection | A service with no convenient HTTP route to poll | The accepted status list matters in practice. A `401` from the admin console's protected root still means "the server is answering", so you do not have to add an unauthenticated health endpoint just to satisfy the runner. A `500`, by contrast, does **not** count as ready: Playwright keeps waiting until `webServer.timeout` expires and then fails the whole run before a single test has executed. ## The rest of the options - `command` -- required; the shell command Playwright spawns. - `cwd` -- the working directory for that command; resolved relative to the config file by default. - `env` -- extra environment variables handed to the child process only. - `timeout` -- how long to wait for the readiness signal before failing the run. - `reuseExistingServer` -- when `true` and something already answers at the `url` or `port`, Playwright uses it instead of spawning a second one. - `stdout` and `stderr` -- `'pipe'` to surface the server's logs in the run output, `'ignore'` to silence them. - `ignoreHTTPSErrors` -- for a dev server presenting a self-signed certificate. - `gracefulShutdown` -- for example `{ signal: 'SIGTERM', timeout: 500 }`, to ask the process to stop before it is killed. ## Local versus CI 1. On a developer machine the admin console is usually already running under a dev server, and restarting it for every run would be slow and would throw away hot reload. 2. On CI nothing is running, and silently attaching to a stray process would make the run test something other than the commit under build. 3. `reuseExistingServer: !process.env.CI` encodes both rules in one line: attach locally, insist on a fresh process in CI, and fail loudly there if the port is already taken. ## Where it sits in the run order - Web server processes are started before `globalSetup` runs and before any project's tests, including a setup project, so a precondition step can already talk to the app. - Since Playwright 1.43 the key also accepts an **array** of server definitions, which is how a suite boots the admin console and a stub for a downstream service together. - Every invocation of `playwright test` owns its own server, so a sharded CI matrix starts one server per shard rather than sharing a single instance. ## What it is not - It is not a per-test hook. One process serves the entire run and every worker in it, so it cannot reset application state between tests. - It is not a health model for production; the readiness probe only proves the process answers, not that background migrations or caches have finished. - It is not required. Teams that deploy the console to a shared environment before the suite runs simply omit `webServer` and point `use.baseURL` at that environment instead. ## Diagnosing a start-up failure When the run dies before any test, the message names the readiness check that never passed, and the useful next steps are mechanical: 1. Run the `command` by hand in the same directory and watch what it prints. 2. Set `stdout: 'pipe'` so the server's own log reaches the run output instead of being swallowed. 3. Confirm the probe target: a `url` on the wrong path answers `404`, which is not in the accepted list. 4. Raise `timeout` only after establishing that the server really is slow to boot rather than broken.

  • The admin console's root returns 401 until you sign in. Can it still be used as the webServer url?
    Yes. Playwright treats 2xx, 3xx, 400, 401, 402 and 403 as proof the server is answering, so a protected root is a perfectly good readiness probe. Only a connection refusal or a 5xx keeps it waiting, so you do not need to add an unauthenticated health route just to satisfy the runner.
  • Why would reuseExistingServer be wrong to leave true on CI?
    On CI it would let the run silently attach to whatever already occupies the port -- a leftover process from an earlier job, or a different build. The suite would then test something other than the commit under build and pass for the wrong reason. Setting it to false makes a busy port a loud failure instead.

saying these in an interview costs you the question

  • Thinks tests start immediately and race the server's startup
  • Says reuseExistingServer should stay true in CI
  • Believes the server must be started by hand in globalSetup
  • Assumes Playwright leaves the spawned server running afterwards
  • Thinks a url answering 500 counts as ready
open as a page

In a Playwright config, what does the `projects` array do?

level: juniorimportance: must knowfreq 78%

basics

~20 s

The projects array lists named run variants, each with its own use options and file filters. Playwright runs every matching spec once per project, so one test yields one result per project, and --project=<name> runs a single variant.

open as a page

In Playwright, what is the difference between the test timeout and the expect timeout?

level: juniorimportance: must knowfreq 80%

basics

~10 s

Playwright runs two independent clocks: the test timeout caps a whole test at 30 seconds by default, including its fixtures and hooks, while the expect timeout caps a single auto-retrying assertion at 5 seconds.

open as a page

In a Playwright config, what does naming a project in another project's dependencies guarantee?

level: middleimportance: must knowfreq 62%

basics

~10 s

It guarantees ordering: every test in the named project finishes before the dependent project starts, and if any of them fails, the dependent project's tests are skipped rather than run.

open as a page

In Playwright, how does `test.use()` in a spec file interact with a project's `use` options?

level: middleimportance: must knowfreq 62%

basics

~20 s

test.use() merges over the project's use block key by key for the tests in its scope, so the file wins on the keys it names and inherits the rest. It applies in every project that runs the file.

open as a page

In Playwright, how do you give one slow test a larger budget than the configured timeout?

level: middleimportance: must knowfreq 66%

basics

~10 s

Call test.setTimeout(120_000) inside the test to replace its budget, or test.slow() to triple the configured one. Both affect only the current test, leaving the suite-wide timeout tight enough that a hang still fails fast.

open as a page

Why does a Playwright run with --project=admin-console --no-deps fail when the same command without the flag passes?

level: middleimportance: should knowfreq 34%

basics

~20 s

Because --no-deps tells Playwright to run only the named project and skip the projects it lists in dependencies, so whatever the setup project would have prepared is missing or stale and the tests break on the precondition.

open as a page

In a Playwright project, how do `testDir`, `testMatch` and `testIgnore` decide which specs run?

level: middleimportance: should knowfreq 47%

basics

~20 s

Each project resolves its own file list: testDir is the root it scans, testMatch keeps files matching a glob or regex, and testIgnore drops files. Ignore beats match, and each key overrides the top-level value for that project only.

open as a page

In Playwright, what changes when you set use.actionTimeout instead of leaving it at its default?

level: middleimportance: should knowfreq 52%

basics

~20 s

Playwright leaves actionTimeout at 0, so a stuck click waits until the test clock expires. Setting it under use gives every action its own budget, so the failure names the call and the test keeps its remaining time.

open as a page

Why do teams prefer a Playwright setup project over globalSetup for suite preconditions?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Because a setup project is made of real tests, it gets fixtures, retries, a trace and a report entry, while globalSetup is a plain function the runner calls with none of those, so its failures leave only a log line.

open as a page

One project in a Playwright matrix is intermittently red while the others are green — which project-level options contain it without editing the specs?

level: seniorimportance: should knowfreq 41%

basics

~20 s

Reproduce with --project on the failing variant, then contain it in the config: raise retries on that project only, adjust its use options, and drop the genuinely unsupported specs with that project's testIgnore. Leave the other projects untouched.

open as a page

A Playwright fixture that seeds admin-console staff accounts takes 45 seconds and every test times out. What do you change?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Fixture setup is charged to the test timeout by default, so a 45-second fixture blows a 30-second budget. Give that fixture its own timeout in its options tuple instead of raising the suite-wide timeout for every test.

open as a page

Your Playwright suite spends six minutes in setup projects before any test starts -- how do you decide what stays a suite-level precondition?

level: principalimportance: should knowfreq 27%

basics

~20 s

Judge each step by who needs it, how often it must run, and whether it needs a browser. Split one broad setup project into narrow ones so a project waits only for what it lists.

open as a page

When should a new variation in a Playwright suite become another entry in `projects` rather than a `test.use()` in the spec files?

level: principalimportance: should knowfreq 31%

basics

~20 s

Make it a project when the variation is an axis the whole suite should run under and be reported on separately. Keep it in test.use when only certain specs need it, since a project re-runs everything.

open as a page

In Playwright, what does a run-wide globalTimeout buy a CI pipeline that per-test timeouts cannot?

level: principalimportance: should knowfreq 33%

basics

~20 s

globalTimeout caps the wall-clock duration of an entire Playwright run and defaults to no limit. Per-test budgets bound one test each, so a large suite and its retries can still run for hours with no test misbehaving.

open as a page