inspector/scripts/lib/browser-timeouts.mjs
cliffhall 58e53d5a92 fix: state every test-gate timeout instead of inheriting a library default
Three of the six Vitest projects ran on Vitest's own testTimeout: 5000 and
five on its hookTimeout/teardownTimeout: 10000, and all 788 Testing Library
waits in the web unit project ran on asyncUtilTimeout: 1000. None of those
numbers was chosen here, and all of them were sized for an idle machine rather
than for three or four concurrent worktree sessions each running a full
local:gate on eight cores. #2292, #2278, #2250, #1942 and #1742 were each that
class, diagnosed one site at a time.

This is the complement of #1596, not a retreat from it: every fix that issue
produced stands, and no race is hidden here. What changes is that a correct,
deterministic test is no longer cut off by a budget nobody picked.

- vitest.shared.mts exports frozen TIMEOUTS / INTEGRATION_TIMEOUTS; all six
  projects spread one of them, clients/launcher importing the shared module for
  the first time to do so.
- Both web projects configure Testing Library's asyncUtilTimeout. The storybook
  one needs its own setup file, deliberately outside .storybook/ and free of
  setProjectAnnotations so the automatic preview-annotation provisioning
  (#1898) is untouched.
- 28 per-suite restatements of the integration project's own budget are gone,
  so raising the shared value now moves every site under it. The handful that
  genuinely differ — cross-process lock contention, a full OAuth round trip —
  keep their budget as a named constant that says why.
- AppRenderer's waitFor no longer asks for exactly its enclosing budget, which
  it could never win (#2292's shape); cli's e2e child timer drops below the
  project budget so the bound carrying the diagnostic is the one that fires.
- The transition auto-settle's slack term is named RAF_SLACK_MS and raised;
  the term derived from HEADER_ANIM_MS is untouched. The two answer to
  different things and must not be scaled as one number.
- The 19 Playwright budgets scattered across three web smokes become four
  named constants in scripts/lib/browser-timeouts.mjs.

scripts/verify-test-timeouts.mjs resolves each project through Vitest itself
and asserts the budgets, the asyncUtilTimeout sites, and that retry stays
unset. It was observed red against the pre-change config: unit/tui/launcher at
testTimeout 5000, five projects at hookTimeout 10000, both asyncUtilTimeout
sites unmet.

Closes #2323

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PsmUAZ6aLUAFqenjJKSSRp
Signed-off-by: cliffhall <cliff@futurescale.com>
2026-09-12 08:32:34 -04:00

49 lines
2.2 KiB
JavaScript

/**
* Playwright wall-clock budgets for the web smokes (#2323).
*
* Before this, `smoke-web-tabs.mjs`, `smoke-web-elicitation.mjs` and
* `smoke-web-browser.mjs` carried 15 locator budgets between them across three
* independently-chosen scales, with no config anywhere to raise. They are the
* same three or four decisions repeated, so they are named once here and the
* scripts import them — the same shape `render-smoke.mjs`'s `DEFAULTS` already
* has, and for the same reason: a budget nobody can find is a budget nobody
* revisits.
*
* These are generous on purpose and cost a passing run nothing. Playwright
* budgets are ceilings on a poll, not sleeps: every one of them returns the
* instant its locator condition holds. What they have to absorb is a smoke
* running against a cold `dist/` build on a machine already carrying three
* other agent sessions' gates.
*
* ⚠️ Raising one of these does NOT make a smoke wait longer for a server that
* is never coming: the launcher-death race in `smoke-web-browser.mjs` and the
* `waitForStage` diagnostics in the other two are what report that, and they
* report it with a cause rather than a timeout.
*/
export const BROWSER_TIMEOUTS = Object.freeze({
/**
* Load the page, or act on a control the app is already showing — a click on
* a rendered tab or list row, a wait for the first meaningful frame. What it
* absorbs is the browser's own scheduling under load, not a server.
*/
ui: 30_000,
/**
* Wait for something that only appears after a round trip to an MCP server:
* an elicitation modal, a tool result panel. Longer than `ui` because a test
* server has to boot, negotiate and answer before the DOM can change.
*/
roundTrip: 45_000,
/**
* Wait on a sub-element of something already on screen — a row inside a
* populated panel, a section of an open accordion. The enclosing wait has
* already paid for the round trip, so this only covers the render.
*/
nested: 15_000,
/**
* A wait whose expiry is caught and ignored, so it must stay short: it is
* pure added latency on every passing run. `networkidle` is the only one —
* the Google Fonts request may never idle on a restricted network.
*/
bestEffort: 5_000,
});