Repository navigation
Description
Version
1.65.0
Steps to reproduce
- In an empty folder, run
npm i -D @playwright/test@1.65.0andnpx playwright install chromium. - Save this test as
tests/clock-interval.spec.ts:
import { test } from '@playwright/test';
test('fastForward with an interval that has no delay', async ({ page }) => {
test.setTimeout(10_000);
await page.clock.install({ time: 0 });
await page.clock.pauseAt(1000);
await page.evaluate(() => {
setInterval(() => {});
});
await page.clock.fastForward(1000);
});- Run
npx playwright test.
Expected behavior
page.clock.fastForward(1000) calls the interval callback and returns, as it does with setInterval(() => {}, 1).
Actual behavior
page.clock.fastForward(1000) does not return. The test fails with Test timeout of 10000ms exceeded.
The result is the same:
- with
setInterval(fn, 0), - with
page.clock.runFor(10)instead offastForward(1000), - with a running clock (
page.clock.install()withoutpauseAt()).
With a running clock, other timers stop too: a setTimeout(fn, 10) that the page sets together with the interval has not fired after page.waitForTimeout(1000).
With setInterval(fn, 1), all of these work.
I see this on 1.65.0 with Chromium, and on main (58d69dc) with Chromium, Firefox and WebKit.
Additional context
In packages/injected/src/clock.ts, _takeFirstTimer() moves an interval forward by its delay (lines 355-356):
if (timer.type === TimerType.Interval)
timer.callAt = shiftTicks(timer.callAt, timer.delay);With a delay of 0, the interval is due again at the same tick, so the loop in _runTo() always finds a due timer and does not end. With a running clock, the real-time timer runs this loop, so later timers do not fire, and fastForward() and runFor() wait for the loop to end when they pause the clock.
Browsers and Node.js give such an interval a minimum delay: in the HTML timer initialization steps, a timeout below 4 becomes 4 when the nesting level is greater than 5, and Node.js sets a delay below 1 to 1.
I found this with Toast.ProgressFill of @kobalte/core, which calls setInterval() without a delay while a toast is shown.
I would like to work on this. I have a one-line fix and a regression test. The fix makes _takeFirstTimer() move an interval forward by at least 1 ms (Math.max(timer.delay, 1)). This is the same 1 ms step that addTimer() already uses for a zero-delay timer that is set during a timer callback. The first call is not delayed, as with setTimeout(fn). The regression test times out without the fix and passes with it. With the fix, tests/library/unit/clock.spec.ts and tests/library/page-clock.spec.ts pass on Chromium, Firefox and WebKit. I will wait for the issue to be assigned before I open a pull request.
Environment
System:
OS: Windows 11 10.0.26300
CPU: (12) x64 12th Gen Intel(R) Core(TM) i5-12400F
Binaries:
Node: 26.12.1
npm: 12.0.2
npmPackages:
@playwright/test: 1.65.0 => 1.65.0
Activity
github-actions commentedon Nov 6, 2026
microsoft-playwright-automation commentedon Nov 6, 2026
Hi, I'm the Playwright bot and I reproduced this on Linux.
Reproduced on
main(58d69dcc392af3a6994eab0323dc2e883f05f396,1.66.0-next) in Chromium, Firefox and WebKit. A zero-delay interval keeps its next execution at the same tick, so the clock never finishes processing it. The proposed fix advances repeat calls by 1 ms, without delaying the first call or changing positive delays.Reproduction and checks
The existing
CK-022 replaces global setIntervaltest now checks an interval without a delay. With the clock installed at time 0 and paused at 1000, its added case is:Before the fix, the test exceeded its 10-second timeout in all three browsers while callbacks continued to run. After the fix, all 657 tests in
tests/library/page-clock.spec.tsandtests/library/unit/clock.spec.tspassed across Chromium 158.0.8127.18, Firefox 159.0 and WebKit 27.2, headless on Linux.Commands:
The build and all lint/type checks passed. The reported published version and Windows were not tested.
Draft PR: #43948
Triaged on Linux by the Playwright bot - agent run