Skip to content

tracking(desktop): move every test out of Electron E2E that does not need it #4761

Description

@Astro-Han

tracking(desktop): move every test out of Electron E2E that does not need it

#4741 removed 22 layout-tier tests — transcript-scroll.spec.ts (774 lines), prompt-rail.spec.ts (622), one from streaming-remount.spec.ts — because they blocked main. This tracker puts that coverage back at the right tier and deletes the harness that made it fragile.

P0 is landed (#4766, dbf5afd735): 18 of the 22 rebuilt as Storybook play stories plus one new, 18 stories, no production code changed. Smoke 277 stories / 303 renders, 0.3–2.2s each against E2E originals that were all test.slow(). Follows #4727.

PR 2 is landed (#4786, 7f7843e1e5). Tier one is now 78 tests in 35 files.

Root cause

E2E holds a large majority of tests that never needed Electron — 110 tests, ~6 minutes, on the required check. #4523 bought 40% with four Xvfb workers and a warm, reused WebContents, which invalidated the premises those tests were written against: a fresh renderer per test, exclusive OS focus, unthrottled frames. Six flake reports in 43 hours followed.

#4707's trace shows the mechanism: the warm fixture's between-test readiness gate waitForSelector('[data-turn-id]') completes on the previous session's turn-fixture-1, and the assertion then observes turn-prompt-rail-111. Patching that gate treats a symptom — #4714 did exactly that and was closed unmerged. Shrinking tier one makes it unnecessary.

Target: 83 tests in 34 files → 4–8 that genuinely need Electron, plus a manual perf probe. Counts below are a first pass; each PR confirms its own before moving.

Plan — six PRs

  1. P0. Transcript scrolling and prompt rail. Done, test(desktop): rebuild transcript and prompt rail coverage as stories #4766.
  2. slash-command-menu (6) + composer trio (7). Done, test(desktop): move the composer slash, paste and Skill-draft journeys off Electron #4786. Thirteen moved; two claims stayed in Electron as slash-command-compact.spec.ts — that a shell holding a Session offers the commands needing one, and that submitting /compact reaches the Host rather than the model. Both sit inline in app-shell.tsx with no seam under them, and opening one is what the architecture ratchet refuses. fixture-thread-search moved off the warm fixture here, retiring the flaky(e2e): pinned tail can load earlier history after its settle check #4707 flake class early as planned.
  3. P1 remainder (20). ← next. workhub-reconstruction (3), skill-draft-lifecycle (2), goal-dialog-budget (1), new-task-draft-target (2), send-message (1), session-rail-render-contract (1), about-page (1), module-hub (1), parent-session-deletion (1), accessibility-coverage (7). The last is a judgement call: no geometry, but it traverses focus across the whole shell — decide whether that is a shell contract or a component one before moving.
  4. P2 whole files (25). agent-graph-layout (1), link-color-contract (1), sidebar-geometry (1), transcript-measure (1), partial-history-notice (1), request-header-row-contract (1), settings-row-focus-ring (4), sidebar-titlebar-actions (2), sidebar-project-row (4), quote-selection (2), composer-plus-menu-stability (7). Mechanical — no extraction.
  5. P2 geometry cases, files stay (~17). session-workbar (18 geometry references), settings (10), workhub-layout (2), onboarding-viewport (its minimum-window case is genuinely Electron and stays).
  6. Delete the harness. --workers and the four Xvfb displays in ci.yml; resetPromptRailWindow, promptRailWorker, the warm fixture (~87 lines) and the dead scrollMotion argument at fixtures.ts:419 in e2e/fixtures.ts; the releaseRendererObservations() preload bridge and sessions:e2e:release-renderer-observations channel; RuntimeHostSessionObservationRegistry.releaseTarget() (added by ci: cut single-job validation time by up to 40% #4523 for this alone — the registry itself is production infrastructure and stays); playwright.config.ts already back to workers: 1.

Rules the plan follows:

  • P1 needs extraction, P2 mostly does not. That is why 2 and 3 cannot be one PR and 4 and 5 could have been.
  • The E2E test is deleted in the same PR as the story or unit test that replaces it.
  • 6 goes last on a measured cost. The e2e step is 5m03s at four workers; one worker is about +3.5 minutes on the required check until the count comes down. ci: cut single-job validation time by up to 40% #4523's 40% is not given back — what is saved is the ~68 tests that no longer start Electron at all.
  • 6 triggers on the number, not on the checklist. 26 + 42 leaves 17, not 4–8. Re-count tier one after each batch.
  • Take fixture-thread-search off the warm fixture in the first PR that touches e2e/fixtures.ts. It is the only CI test still running on a reused window, so moving it retires the flaky(e2e): pinned tail can load earlier history after its settle check #4707 flake class early; the machinery itself stays until 6, because native-transcript-perf wants warm reuse by design.

Still uncovered

Three of the 22 are not in a story. Only one is a tier decision.

Deleted test Belongs in Blocked on
switching Sessions restores a Turn anchor Storybook #4582
switching sessions reconstructs only the Host active range Storybook #4582
a gesture a nested scroller consumed does not release the tail E2E — needs Chromium's own scroll chaining

The observer test landed in #4786 as a component test, where it can install its probe before the rail mounts; the rootMargin literal it matched is now a named constant. The remaining two are uncovered regressions today.

Moving a test at this tier

From #4766:

  • Check the props before planning an extraction. ChatView already took the transcript, the history seam, the growth signal and the rail's landmark index, so no fake backend was needed and the 120-prompt seeded session turned out to be unnecessary.
  • A dispatched wheel takes the production branch whenever the guard reads DOM statecomposedPath(), computed overflowY. It does not when the behaviour is the browser's own.
  • smoke:storybook does not build. Run build-storybook first.
  • The smoke runs headless, which paints no platform scrollbar. Anything a scrollbar can occlude is inert on CI; SMOKE_HEADED=1 runs it headed.
  • Mutation-test each moved assertion against the defect it names. Twelve of test(desktop): rebuild transcript and prompt rail coverage as stories #4766's were; two more turned out unable to fail at all.
  • The flake at this tier is waitFor boundaries, not the readings — a condition that goes true while layout is still resolving, with the real assertion read outside the retry. ~1% at 4x CPU throttle, never locally.

Scenario data lives in apps/desktop/src/main/e2e-fixture (1197 lines). Share it with the story side rather than writing a second copy; settled per PR.

What stays in Electron

  • proxy-password-editing — real HTTP server, the proxy-authorization header the main process actually sent, across two reloads
  • fixture-thread-search — a search hit's session key surviving the bridge intact
  • new-task-reload — reload persistence; its send is fenced behind the fix(desktop): land prompt-rail jumps at the top and fence the e2e sends #4577 readiness barrier
  • slash-command-compact — a Session-bearing shell's command list, and /compact reaching the Host rather than the model
  • onboarding-viewport — minimum window size
  • streaming-remount (4) — judgement: it injects failures through the bridge with rejectNextSessionObservation / rejectNextTranscriptOpen, genuinely cross-process even though the assertion is a React sequence
  • native-transcript-perf — CDP-driven, real compositor, manual probe behind MAKA_TRANSCRIPT_PERF

Non-goals

Not restoring the deleted specs to E2E. Not changing retries: 0. Not patching the readiness gate — PR 6 removes it. Not moving assertions that genuinely need Electron: native input, window-boundary drags, real folders, reload persistence, cross-process contracts.

Related

#4727 — this is the composition half of that discussion, without depending on a relevance selector's outcome. #4664 — out of scope; a product race, stays open as a product bug. #4714 — superseded, PR 6 deletes the gate it targets. #4259 — the reason P0's new story exists. #4582 — blocks the two session-switch tests above. #3944 — complementary: it widens which surfaces have stories, this puts assertions where they can run.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions