Repository navigation
tracking(desktop): move every test out of Electron E2E that does not need it #4761
Description
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
P0. Transcript scrolling and prompt rail.Done, test(desktop): rebuild transcript and prompt rail coverage as stories #4766.Done, test(desktop): move the composer slash, paste and Skill-draft journeys off Electron #4786. Thirteen moved; two claims stayed in Electron asslash-command-menu(6) + composer trio (7).slash-command-compact.spec.ts— that a shell holding a Session offers the commands needing one, and that submitting/compactreaches the Host rather than the model. Both sit inline inapp-shell.tsxwith no seam under them, and opening one is what the architecture ratchet refuses.fixture-thread-searchmoved 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.- 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. - 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. - 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). - Delete the harness.
--workersand the four Xvfb displays inci.yml;resetPromptRailWindow,promptRailWorker, the warm fixture (~87 lines) and the deadscrollMotionargument atfixtures.ts:419ine2e/fixtures.ts; thereleaseRendererObservations()preload bridge andsessions:e2e:release-renderer-observationschannel;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.tsalready back toworkers: 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-searchoff the warm fixture in the first PR that touchese2e/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, becausenative-transcript-perfwants 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.
ChatViewalready 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 state —
composedPath(), computedoverflowY. It does not when the behaviour is the browser's own. smoke:storybookdoes not build. Runbuild-storybookfirst.- The smoke runs headless, which paints no platform scrollbar. Anything a scrollbar can occlude is inert on CI;
SMOKE_HEADED=1runs 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
waitForboundaries, 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, theproxy-authorizationheader the main process actually sent, across two reloadsfixture-thread-search— a search hit's session key surviving the bridge intactnew-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 barrierslash-command-compact— a Session-bearing shell's command list, and/compactreaching the Host rather than the modelonboarding-viewport— minimum window sizestreaming-remount(4) — judgement: it injects failures through the bridge withrejectNextSessionObservation/rejectNextTranscriptOpen, genuinely cross-process even though the assertion is a React sequencenative-transcript-perf— CDP-driven, real compositor, manual probe behindMAKA_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
[-]tracking(desktop): move geometry assertions out of Electron E2E into Storybook[/-][+]tracking(desktop): rebuild the layout tier in Storybook[/+]Adarsh-Me commentedon Sep 4, 2026
/take
Astro-Han commentedon Sep 4, 2026
Hi @Adarsh-Me, Sorry that I mislabeled this issue as help wanted, it is a long tracking issue which I'm going to implement now. Sorry for the confusion!
[-]tracking(desktop): rebuild the layout tier in Storybook[/-][+]tracking(desktop): move every test out of Electron E2E that does not need it[/+]Adarsh-Me commentedon Sep 4, 2026
It's okay 👍
test(desktop): assert transcript geometry through an upward traversal
test(desktop): rebuild prompt rail coverage as stories
test(desktop): cut the transcript stories to what they assert
test(desktop): close what adversarial review found in these stories
test(desktop): close what adversarial review found in these stories
test(desktop): rebuild transcript and prompt rail coverage as stories (…
5 remaining items
test(desktop): retire the parallel E2E display harness
test(desktop): pin the last workbar and WorkHub geometry in stories
test(desktop): drop the duplicated E2E thread search
test(desktop): pin the last workbar and WorkHub geometry in stories
test(desktop): drop the duplicated E2E thread search
test(desktop): pin the last workbar and WorkHub geometry in stories
test(desktop): drop the duplicated E2E thread search
test(desktop): hold the Electron tier to a written budget
test(desktop): drop what the ablation could not justify
test(desktop): drop two duplicate renders and a duplicate guard case
test(desktop): close the budget guard's blind spots and the coverage …
test(desktop): finish moving non-Electron coverage out of the E2E tier (
Astro-Han commentedon Sep 6, 2026
Done. Five PRs — #4766, #4786, #4803, #4825, #4877 — took the Electron tier from 110 tests to 30 in 16 files, and
apps/desktop/e2e-budget.jsonpluscheck:e2e-budgetnow hold it there: every spec records the Electron-owned mechanism it needs, and CI fails in the planning lane when the tier and the ledger disagree.Two threads outlive this tracker and are carried elsewhere:
fix(desktop): repair the e2e budget and AppShell story after #4694 (#…