What probe intervals and timeout defaults do Playwright's `expect.poll` and `toPass` use?
answer
- Back-off list, then the last value repeats
- Hundred, two-fifty, five hundred, one thousand
- One wrapper inherits the expect timeout
- Zero means no deadline, not instant failure
- Config keys under expect.toPass
basics
~20 sBoth probe on intervals of 100, 250, 500 then 1000 milliseconds, with the last value repeating. In Playwright 1.63 expect.poll uses the expect timeout, five seconds by default, while toPass defaults to timeout 0 and has none of its own.
solid answer
~40 sIn Playwright 1.63 both wrappers share the default probe schedule `[100, 250, 500, 1000]` ms, and the final interval repeats for as long as the loop runs. The timeouts differ, and that is the part interviewers probe. `expect.poll` inherits the expect timeout — 5 s unless `expect.timeout` in `playwright.config.ts` changes it — and takes a per-call `{ timeout }`. `toPass` defaults to a timeout of **0**, which means no deadline of its own: it keeps probing until the test timeout (30 s by default) kills the test, and the failure then reads as a test timeout rather than an assertion error. Override per call with `.toPass({ timeout, intervals })` or globally with `expect: { toPass: { timeout, intervals } }` in the config.
code
typescript · 9 linesimport { defineConfig } from '@playwright/test';
export default defineConfig({
timeout: 30_000,
expect: {
timeout: 5_000,
toPass: { timeout: 15_000, intervals: [500, 1_000, 2_000] },
},
});go deeper
Know the two numbers people ask for: the default probe schedule of 100, 250, 500 and 1000 milliseconds, and the five second expect timeout that expect.poll inherits.
Explain that the last interval repeats and that toPass has no timeout of its own, so an unbounded block runs until the test timeout and reports as one.
Set budgets deliberately: an explicit toPass timeout well inside the test timeout turns a stuck condition into a readable assertion failure instead of a killed test.
Decide the defaults a suite ships with in the config, and treat a rising timeout as a signal about the product rather than a knob to turn until CI is green.
## Two knobs, two clocks Every polling wrapper in Playwright is governed by two things: **how often** it re-checks (`intervals`) and **how long** it is allowed to keep trying (`timeout`). The intervals behave identically for `expect.poll` and `toPass`; the timeouts do not, and that asymmetry is the whole content of this question. ## The probe schedule Both wrappers default to `intervals: [100, 250, 500, 1000]`, in milliseconds: - The first re-check happens 100 ms after the first failed attempt, the next 250 ms later, then 500 ms, then 1000 ms. - Once the list is exhausted, the **last value repeats** — every subsequent probe is another second apart. - The shape is deliberate back-off: fast attempts catch a condition that is nearly ready without burning the run, and the tail keeps a long wait cheap. - Intervals are the wait **between** probes. The probe itself takes time too, so a callback that needs 800 ms produces a real spacing closer to 1.8 s in the tail. - Passing your own list replaces the default entirely: `intervals: [1_000]` means a flat one-second cadence from the first retry. ## expect.poll's timeout `expect.poll(fn, { timeout })` defaults to the **expect timeout** — the same 5 s budget the auto-retrying assertions use — which you set globally as `expect: { timeout: 5_000 }` in `playwright.config.ts`. A per-call `{ timeout: 10_000 }` overrides it for that assertion only. When the deadline passes, you get an assertion failure carrying the matcher diff and your `message`, which is a good failure: it names the value and the expectation. ## toPass's timeout is zero `toPass` is the odd one. Its `timeout` defaults to **0**, and 0 means *no timeout*, not *fail immediately*. It also does not follow the configured expect timeout. The consequences are practical: 1. An unbounded `toPass()` keeps probing until the **test timeout** (30 s by default) ends the test. 2. The report then says the test timed out, not that an assertion failed — much harder to read, because the assertion that was failing is buried in the trace rather than announced. 3. Any teardown work in the test body after the assertion never runs, since the test was killed rather than failed. The fix is to say what you mean: `.toPass({ timeout: 15_000 })` on the call, or a project-wide default under `expect: { toPass: { timeout, intervals } }`. ## Defaults at a glance | setting | `expect.poll` | `toPass` | |---|---|---| | `intervals` | `[100, 250, 500, 1000]` ms | `[100, 250, 500, 1000]` ms | | `timeout` | expect timeout, 5 s by default | `0`, meaning no timeout | | per-call override | second argument to `expect.poll` | argument to `.toPass()` | | global config key | `expect.timeout` | `expect.toPass.timeout` / `expect.toPass.intervals` | ## Choosing your own numbers - Keep the **default intervals** unless you have a reason. Custom fast intervals against an expensive callback mostly generate load. - Widen intervals, not the timeout, when each probe is costly — a poll against a slow computation is better at `[1_000, 2_000]` than at 100 ms. - Set an **explicit `toPass` timeout** everywhere it is used, so a stuck condition fails as an assertion well inside the test timeout. - Keep the wrapper's budget comfortably **below** the test timeout; a wrapper that can outlive the test converts every failure into the least informative kind. - Do not raise a timeout to make a run green. A budget should describe how long the behaviour genuinely takes, so a regression that doubles it still shows up.
- What actually stops a toPass block that was given no timeout?The test timeout, 30 seconds by default. The block keeps probing on its interval schedule until the runner kills the test, so the result is reported as a test timeout rather than as the assertion that kept failing.
- If you pass intervals: [1000], what schedule do you get?A flat one-second cadence. Your list replaces the default back-off entirely, and because the last entry repeats, every probe after the first is a second apart until the timeout.
saying these in an interview costs you the question
- Says toPass defaults to the five second expect timeout
- Reads timeout zero as failing on the first attempt
- Thinks the interval list stops after its last entry
- Believes intervals include the callback's own duration
- Raises timeouts as the standard fix for a flaky wait