Repository navigation
test(webui): wait for the page's state in the isolated Chromium suite - #235
Merged
Merged
Conversation
added 2 commits
October 23, 2026 11:32
The suite slept 750ms after every navigation, every frame and every repaint. The case that walks a page's history spent five of those sleeps — 3750ms of its 4412ms — asleep, so it blew through the 5000ms budget on a runner a little slower than the one it was measured on. Nothing in the product had changed: the case failed on mainline on its own arithmetic. Every sleep is now a wait for the state the case asserts: the title the page shows, the generation the provider holds for it, the page's place in the target list, the frame that has arrived. A wait that never comes true fails the case at its own deadline with the state it waited for named, so the cases fail faster and say more than they did — the sleeps only ever guessed, and several of them guessed about something the provider already reports exactly. The streaming case opens each page's stream while that page is the one on screen, because Chrome renders the front page and throttles the ones behind it. Before, the second stream took the front and the first page produced no picture at all: the case passed on CI by luck and failed here every time. The four cases that drive a real browser and measured over 700ms are given a 30s budget, the number the repository's other real-browser suites use (browser-panel-live-navigation and workspace-browser-tab-acceptance; webui-dependency-check uses 60s for its own gate). The eight that measured under 250ms keep the default budget, so a case that suddenly takes seconds still says something. The teeth are in the waits: a broken navigation fails at the 3s wait deadline with the state it waited for named, not by running a budget out. Checked against a real Chromium: the case that failed on mainline went from 4412ms (4503ms here) to 1075ms, and the file from 39910ms to about 5200ms. Breaking the provider's back/forward on purpose fails the case in 3767ms with "timed out waiting for the back navigation to land"; breaking its generation counter fails two more, at 3851ms and 4146ms. The suite's Chrome discovery now knows about macOS, the way the provider's own candidate list does, so the suite runs — and can be checked — on a developer machine at all.
The macOS CI run failed two cases in this file, and in neither of them was the product what changed: one case ran into the 5000ms budget, and one read a field that still held the previous case's text. The budget was the first problem. A case waits for the state it asserts, and each of those waits ends at WAIT_MS — three seconds — so a case that waits seven times can spend 21s in its waits alone, and the vitest budget was reached before the last wait could report why it failed. The failure was then "Test timed out in 5000ms" instead of the diagnostic the wait carries. Every case in this file drives a real browser, so the budget is not what judges a case — the wait deadline is. The budget is now 60s for every case: the largest sum of wait deadlines in this file is 21s, the macOS run measured the slowest case at 5015ms, and 21s of waits plus that work is about 26s, so 60s leaves the round trips to a real browser about eight times the time the CI run measured them. 60s is the number the repository already gives a gate that drives a real process. The second problem was the read-back. A wait for "the page changed" is not a wait for "this text arrived": the browser hands the keystrokes to the page on its own schedule, so a read taken right after the send can still see the field as it was, and a keystroke that is still on its way lands in the next case's text — which is how a field came to hold "alphastill works". Every read-back now waits for the value itself: the field holds the text that was typed, the title is the one the page shows, and the page's own answer to the input is waited for before another write is allowed to land on top of it. Checked against a real Chromium, the whole webui group at once, three times over: the twelve cases pass every time, the slowest at 1453ms, and the same file running beside the whole group passes with the slowest at 1700ms. The teeth are still there: breaking the provider's back/forward fails the case with "timed out waiting for the back navigation to land", breaking its generation counter fails two more with "timed out waiting for the navigated page to advance its generation", and breaking the input itself fails five with "timed out waiting for the field to hold ..." — every one of them a diagnostic at the wait deadline, never a budget timeout.
modacker
force-pushed
the
fix/isolated-chromium-timeout-budget
branch
from
October 23, 2026 03:45
a40ed6b to
f3d7eab
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What broke
Mainline run 39879196689 (SHA
2bd9a2a1) went red on a test timeout, not on an assertion:Test Files 1 failed | 212 passed (213),Tests 1 failed | 3296 passed (3297),Verification failed at: test:webui.Re-running the same SHA passed. Nothing in the product had changed — the case failed on its own arithmetic.
The first version of this PR fixed that by turning the sleeps into waits, and the macOS CI run then failed two cases in the same file:
Both are fixed here. Neither was the product changing.
Why the first version still failed
The budget was too small for a case that waits several times. A case waits for the state it asserts, and every one of those waits ends at
WAIT_MS(3s). The case that waits most often waits seven times, so it can spend 21s in its waits alone — and the vitest budget was reached before the last wait could report why it failed. The failure was thenTest timed out in 5000msinstead of the diagnostic the wait carries. LoweringWAIT_MSto 3s fixed the single-wait case and left the sum untouched.A read-back waited for "the page changed", not for "this text arrived". The browser hands the keystrokes to the page on its own schedule, so a read taken right after the send can still see the field as it was — and a keystroke that is still on its way lands in the next case's text. The launch page is shared by the cases in that suite, so the case before it typed
alpha, the next case typedstill works, and the field came to holdalphastill works. The old code's 750ms sleep happened to absorb the late keystroke; a condition wait does not, because "the page changed" and "this text arrived" are different facts.What this changes
Every sleep in the file became a wait for the state the case actually asserts:
waitFornever passes a condition that did not hold. A condition that throws is the page's own state (a loading page answers a read with an error), so it is asked again, and the last error is reported if the wait ends.Every read-back now waits for the value itself:
typeInto/personTypeswait for the field to hold the text that was typed, so a keystroke still on its way cannot land in the next case's text.openPagewaits for the title the new page shows, not only for the page to be listed.Two things came out of the same file:
The budgets, and the arithmetic
Every case in this file drives a real browser, so the budget is not what judges a case — the wait deadline is. The budget is room for a runner that is several times slower than the machine the numbers were measured on, so that a slow machine cannot report a red that says nothing about the product.
The budget therefore has to cover a case's waits and the work around them:
WAIT_MSwebui-dependency-check.test.ts)60s leaves the round trips to a real browser about eight times the time the CI run measured them. 30s would have left 4s between a slow machine and a red that says nothing.
WAIT_MSis 20x below the budget, so a failure is always the condition's and never the budget's — including for a case that waits seven times, which is exactly what the macOS run got wrong.How it was checked
The local runs that matter are the whole group, because CI runs the 213 files at once and a single file on an idle machine does not reproduce that.
node scripts/run-vitest-suite.mjs webui, same file list and config as CI), with this file's per-case timings:12/12 pass in every run. The group reported
1 failed | 208 passed | 4 skipped (213)each time; the one failure ispackages/webui/test/integration/agent-transcript.test.ts(the upstream turn-queue area), and it fails the same way on the pristine base97e33fd8with this branch's changes absent — it is not this PR.pnpm --filter @mavis/webui typecheck:testpasses.timed out waiting for the back navigation to land— 1 case fails, 11 passtimed out waiting for the navigated page to advance its generation— 2 cases fail, 10 passtimed out waiting for the field to hold "alpha"/"still works"/"one"/"person typed"/"both delivered"— 5 cases fail, 7 passEvery one is a diagnostic at the wait deadline (3.1–3.6s), never vitest's
Test timed out in 5000msand neverTest timed out in 60000ms.Not addressed here
packages/webui/test/integration/isolated-chromium-screencast.test.tshas no explicit budget and its "keeps delivering frames" case sleeps 3000+500ms — 3500ms of 5000ms, the closest match to this one — andpackages/webui/test/integration/browser-use-real-input.test.tssleeps 750ms per navigation with no budget (a lower risk). Both also discover Chrome through the Linux-only path list, so they skip on a developer machine.test/vitest-suites.jsonandrelease/public-source.json.CI status of this branch
f3d7eab7)97e33fd8)verify (macos-latest)✓ isolated-chromium-two-pages.test.ts (12 tests) 36703ms, group209 passed | 4 skipped (213)agent-transcript.test.ts(the upstream turn-queue case); this file is skipped there, because the macOS Chrome discovery is only in this branchverify (windows-latest)verify (ubuntu-latest)plugin-catalogue-layout.spec.mjs×4,rail-pin.spec.mjs×2,visual-baselines.spec.mjs×8 (toHaveScreenshotmismatches). This file's 12 tests pass in the same job:✓ isolated-chromium-two-pages.test.ts (12 tests) 15394msThe ubuntu red is the mainline red: the same specs fail the same way on
97e33fd8with this branch's changes absent. Nothing in it touches the isolated Chromium suite.