fix(mobile): bound reads by operation budget with last-read grace (#1099)

Closed
TO
19h ago
+620-187

What and why

  • On @e2e-dev/mobile a snapshot or screenshot that hung held the step until the test timeout (#1099). Every device read now runs under the operation's budget (InputBudget.read, packages/mobile/src/support.ts), so a hung read fails the step as OPERATION_TIMEOUT (or ACTION_MAY_HAVE_COMMITTED once an input of that operation went out).
  • Bounding reads alone broke slow polls: on iOS a snapshot takes 1-2 s, so the last read of waitFor/expect/toHaveScreenshot got cut at the deadline and failed as a hang instead of LOCATOR_NOT_FOUND.
  • Core fix: ReadWindow (packages/e2e/src/internal/time.ts). Each read of one wait gets the time left plus the slowest read that wait already finished, capped by the running phase's deadline. A read cut off within that tail ends the wait as a normal timeout; a read that hung with more time left is still the failure.
  • @e2e-dev/mobile now needs e2e >=0.21.4 (this PR's e2e patch changeset ships it): on 0.21.3 core, mobile reads would end at the deadline with no last-read grace.
  • No public type change: ReadWindow is internal; OperationContext.timeoutMs docs note the last read can get more than the deadline leaves.

Issue

Fixes #1099

Verified

Ran it locally: no device run. Unit evidence on the head merged with main (f4e8c8e1a + nit 7f1d05ed8):

  • Reverting the read bound (readWithin -> direct call) fails 6 mobile tests, e.g. "bounds a snapshot by the operation budget" and "takes no second snapshot for a capture the budget abandoned".
  • Reverting the slowest-read grace in ReadWindow.timeoutMs() fails 5 core tests (time, locator-engine, locator-wait-for), e.g. "lets a slow last resolve finish past the deadline: a late match resolves".
  • Reverting the slowest-read term in ReadWindow.cutOff() fails "ends on a read cut off within the longest read time of the deadline, and fails on one that hung with more left".
  • pnpm check passes; e2e (3171), mobile (420), web (290) unit suites pass.
mainthis branch
A hung snapshot holds the step until the test timeoutThe step fails at its own timeout
(with bounded reads only) a slow last iOS read in expect fails as a hangThe last read finishes; a missing node is LOCATOR_NOT_FOUND / ASSERTION_FAILED

Checklist

  • Changeset added (pnpm changeset) for any change to a published package, or the change is testbed, docs, or CI only.
  • Breaking change: title carries !, the changeset body names what breaks and what replaces it, and the deprecation warning landed a window earlier (stability policy in CONTRIBUTING.md).
  • Docs updated in the same PR: the docs/**/*.mdx page for the behavior, and skills/e2e/ if the skill describes it.
  • Contract change: emitted .d.ts reviewed, tests/types/sdk-types.ts updated; wire change edits the schema and both fixtures.
  • Engine contract change (e2e/engine): changesets for e2e, @e2e-dev/web, and @e2e-dev/mobile.