|
1 | 1 | # Event-Based Waits in Playwright Suite — Design |
2 | 2 |
|
3 | 3 | **Date:** 2026-04-27 |
4 | | -**Status:** draft |
| 4 | +**Status:** landed — `test-hooks` cargo feature + WASM API, push dispatcher, `data-state` lifecycle, `Peer` wrapper, ESLint rule, ratchet script, flake harness all shipped across PRs 1–4. Two CI gates from §CI gate (symbol-leak check + flake harness) are wired to local `just check-all` only; CI workflow wiring is a follow-up tracked in the *Realised state* note below. |
| 5 | +**Implementation plan:** [`docs/plans/2026-04-27-event-based-waits-pr1-test-hooks-foundation.md`](../plans/2026-04-27-event-based-waits-pr1-test-hooks-foundation.md), [`docs/plans/2026-04-28-event-based-waits-pr1-errata.md`](../plans/2026-04-28-event-based-waits-pr1-errata.md), [`docs/plans/2026-04-29-event-based-waits-pr2-peer-wrapper.md`](../plans/2026-04-29-event-based-waits-pr2-peer-wrapper.md), [`docs/plans/2026-04-30-event-based-waits-pr3-data-state-lifecycle.md`](../plans/2026-04-30-event-based-waits-pr3-data-state-lifecycle.md), [`docs/plans/2026-04-30-event-based-waits-pr4-ratchet-flake-harness.md`](../plans/2026-04-30-event-based-waits-pr4-ratchet-flake-harness.md) |
5 | 6 | **Branch:** `claude/event-based-waits-RNFZ9` |
6 | 7 |
|
7 | 8 | > **2026-04-28 erratum.** Investigation during PR-1 execution found that |
|
13 | 14 | > sections below have been updated; the old shape is preserved in |
14 | 15 | > `docs/plans/2026-04-28-event-based-waits-pr1-errata.md`. |
15 | 16 |
|
| 17 | +> **Realised state (post-2026-05 audit).** The four PRs landed and the |
| 18 | +> system works as designed for the in-scope flows, but the body below |
| 19 | +> drifts from the realised implementation in several places. Diffs: |
| 20 | +> |
| 21 | +> - **`tab_bar.rs` was excluded** from the `data-state` lifecycle because |
| 22 | +> neither of its candidate elements has a CSS transition; recorded in |
| 23 | +> commit 0f79399 ("docs(web): record tab_bar lifecycle decision"). The |
| 24 | +> lifecycle now applies to four components + the action-sheet overlay, |
| 25 | +> not five. §Scope, §`data-state` attribute pattern Components- |
| 26 | +> receiving-the-lifecycle, and the driving-property list are stale on |
| 27 | +> this point. |
| 28 | +> - **Driving-property table fix.** `bottom_sheet.rs` drives on |
| 29 | +> `transform` (its listener accepts both `transform` and `opacity` |
| 30 | +> with `opacity` as the reduced-motion fallback); `grove_drawer.rs` |
| 31 | +> similarly accepts both; the action-sheet markup in `message.rs` |
| 32 | +> drives on `transform`. The body's list is incomplete on these points. |
| 33 | +> - **`std::mem::forget` instead of `StoredValue`** for the push |
| 34 | +> dispatcher in `app.rs`. `wasm32`'s process *is* the app, so leaking |
| 35 | +> the dispatcher handle is the simpler correct shape. The `StoredValue` |
| 36 | +> lifecycle paragraph in §Push dispatcher is superseded. |
| 37 | +> - **`EventReceiver::subscribe_now`** is used instead of |
| 38 | +> `ClientHandle::subscribe_events()` — `subscribe_now` is the race-free |
| 39 | +> path that avoids the boot-window where async-subscribe would drop |
| 40 | +> boot-time events. Comment at `app.rs:167-169` documents the rationale; |
| 41 | +> the spec's §Push dispatcher phrasing should defer to that. |
| 42 | +> - **`clock.ts` exists** as a fourth helper module at |
| 43 | +> `e2e/helpers/clock.ts` (wraps `page.clock.install`/`runFor`). The |
| 44 | +> §Helpers redesign directory listing omits it. |
| 45 | +> - **`longPress` did NOT migrate to `page.clock`** — the original |
| 46 | +> `longPress` still uses `waitForTimeout`; an opt-in |
| 47 | +> `longPressWithClock` exists but has no callers. §`page.clock for |
| 48 | +> real durations` step 1 is stale; the migration is deferred. |
| 49 | +> - **`waitUntilHeadsEqual` default timeout is 90s, not 30s** (per |
| 50 | +> `e2e/test-hooks.ts:193`). Justification recorded in code: iroh-gossip |
| 51 | +> cold-start cost — the relay log shows ~30s of dial timeouts before |
| 52 | +> the first peer-pair handshake completes. §Wait-until-heads-equal |
| 53 | +> sketch and §Three-patterns-purged item 3 need this reality. |
| 54 | +> - **`crates/web/src/test_hooks/` is a directory**, not a file. Four |
| 55 | +> submodules: `mod.rs`, `dispatcher.rs`, `snapshot.rs`, `wire.rs`. The |
| 56 | +> spec's references to `crates/web/src/test_hooks.rs` should be a |
| 57 | +> directory path. |
| 58 | +> - **Comprehensive WASM self-tests live in `crates/web/tests/test_hooks_browser.rs`**, |
| 59 | +> not `crates/web/tests/browser.rs` (which only carries the |
| 60 | +> `data-state` lifecycle tests + a single mount sanity test). §PR 1 |
| 61 | +> references the wrong file for the bulk of the test set. |
| 62 | +> - **`dev-quick` did not get the `FEATURES=""` parameter** that `dev` |
| 63 | +> carries. Spec §`just dev` paragraph implies symmetric |
| 64 | +> parameterisation; not realised. Minor follow-up. |
| 65 | +> - **CI gaps.** `scripts/check-no-test-hooks-in-prod.sh` exists and is |
| 66 | +> called from `just check-all`, but no GitHub Actions workflow runs |
| 67 | +> `check-all` or that script directly. Similarly, no path-filtered |
| 68 | +> workflow runs `just test-e2e-flake N=10` on PRs that modify `e2e/`. |
| 69 | +> Both gates exist as local-only today. §CI gate and the PR-4 |
| 70 | +> `Migrated specs must pass N=10 in CI` claim describe intent that |
| 71 | +> has not been wired to CI yet. Tracked as follow-up. |
| 72 | +> |
| 73 | +> The body below is preserved as the original design (corrected only by |
| 74 | +> the earlier 2026-04-28 erratum). The *Realised state* list above is |
| 75 | +> authoritative for current implementation shape; do not edit the body |
| 76 | +> in place to match it — that would lose the design rationale that drove |
| 77 | +> the four PRs. |
| 78 | +
|
16 | 79 | ## Problem |
17 | 80 |
|
18 | 81 | The Playwright suite leans on time-based waits as flake compensation. Audit of `e2e/` (8 spec files, 1814 LOC): |
|
0 commit comments