webviews: browser tests open a gallery frame for their own entry alone, and a guard keeps it so (composer batch)
Browser tests open /gallery/frame.html?entry=<id> without entryPath. The frame then starts loading every one of the 153 *.gallery.ts(x) files (src/gallery/entryStore.ts) before it finds the entry it renders. Each test also warms the dev server by crawling /gallery/frame.html, which follows the registry's lazy imports and transforms all of them again: about 3,000 modules per test file (the crawl's count, imports included). #23080 fixed one test (markdown-selection-drag); this makes the fix system-wide and guards it.
This PR adds the helper, the guard, and the composer batch: 23 composer files, quiet-access-chip, and gallery-composer-paste (new on base; the guard caught it). Two follow-ups narrow the rest.
What changed
test/support/galleryFrame.ts(new).galleryFrame(entry, params)builds the frame URL withentryPath. It finds the entry's file from itsid: "<id>"line, so tests never hard-code a path, and it throws if no file or several files define the id.warmGalleryFrame(vite, entries, { browser, params })warms by loading each entry's frame once in a browser and waiting fordata-gallery-ready. It replaces bothwarmViteServer(vite, "/gallery/frame.html")and the hand-written warm-page loops.- If Vite's dependency optimizer reloads the page, during the navigation or after it, the warm-up starts the load again within the same budget. A stage that fails to mount (
data-gallery-ready="error") fails the warm-up and names the entry. - Why a browser load and not the module crawl: measured on Huge Blue with a cold server and the composer entry, the crawl follows lazy page hosts the frame never loads (frame/pages.ts names every page, Monaco among them). It cost 25 s and 1,227 modules, where one page load cost 20 s and 685.
- Guard (
scripts/check-browser-test-lanes.ts, runs inbun run check).- Fails any browser test that opens
frame.htmlwithoutentryPath, unless the file carries// gallery-frame-every-entry: <reason>(the reason is required). EVERY_ENTRY_PENDINGnames the files not narrowed yet, each with why. The guard fails on a listed file that is already narrowed, or that no longer exists, so the list can only shrink.- Two of those files are changed by other lanes' open PRs and wait for them: suggestion-lists-float (#22795) and transcript-thought (#23084). composer-location-strip was narrowed on base by #23210, and quiet-access-chip is migrated here now that #22902 has merged.
- The opt-out comment counts only at the start of a line, and a test counts as a browser test only if it really imports
playwright. checkDevServerWarmSourceacceptswarmGalleryFrameas a warm-up.test/check-browser-test-lanes.test.tscovers the new rule.
- Fails any browser test that opens
- The composer batch: composer-history and 22 other composer-* files open their frames through
galleryFrameand warm withwarmGalleryFrame.- A multi-entry file warms the old warm page's entry with its variant, then the other entries at their default variant (variant names differ between entries).
- Two-engine files warm once, in Chromium if it launched, else WebKit.
No assertion, test name, skip or test timeout changed. The old warm loops waited with no limit; the warm-up now has a 100 s budget, the same as warmViteServer's default.
Testing
Each file ran on Huge Blue (Linux, bun 1.4.2, CMUX_BROWSER_TESTS=1) at base and with this change. The same tests pass, and every file is faster. Huge Blue was loaded, so the times are noisy.
| file | base | this PR |
|---|---|---|
| composer-history (first head) | 0/1 (base red then: layout shift 0.0002), 77.6 s | same failure, 26.5 s |
| composer-chip-press | 52 pass, 349.0 s | 52 pass, 249.3 s |
| composer-menu-dismiss | 60 pass, 417.5 s | 60 pass, 337.0 s |
| composer-caret-focus | 42 pass, 217.2 s | 42 pass, 155.9 s |
| composer-quiet-chips | 32 pass, 172.0 s | 32 pass, 127.0 s |
| composer-draft-wrap | 32 pass, 160.5 s | 32 pass, 114.2 s |
| composer-top-strip-variants | 47 pass, 166.0 s | 47 pass, 122.0 s |
| composer-type-scale | 41 pass, 142.9 s | 41 pass, 110.3 s |
| composer-row-layout | 29 pass, 126.1 s | 29 pass, 86.7 s |
| composer-claude-split | 16 pass / 2 fail (base red), 118.4 s | same, 80.9 s |
| composer-footer-plain | 19 pass, 100.3 s | 19 pass, 74.4 s |
| composer-mention | 1 pass / 6 fail (base red), 63.1 s | same, 38.3 s |
| the other 11 composer files | 1โ9 tests each, 40.9โ58.5 s | same results, 16.8โ40.8 s |
| quiet-access-chip (rebased head) | 4 pass, 76.7 s | 4 pass, 41.8 s |
| gallery-composer-paste (rebased head) | 5 pass, 57.6 s | 5 pass, 38.1 s |
| composer-history (rebased head) | 1 pass, 45.6 s | 1 pass, 27.5 s |
bun scripts/check-browser-test-lanes.tspasses, andvp checkreports no warnings, lint errors or type errors.bun run checkstops earlier, atcheck:text-focus, which is red on base (run-board/variants/timeline/timeline.css:14, not in this PR).test/check-browser-test-lanes.test.ts: 3 pass.
Observed / Not run
- Observed: the table above, the guard flagging exactly the 66 files at base, and the check above.
- Not run: the suite on CI. CI's
react-apps-checkon this PR gives the runtime change; I'll compare it with base runs (the Test webviews step summed over 4 shards was 1,539โ1,624 s on three recent runs). - Found along the way, not changed here:
pages.markdown's default variant (showcase) never setsdata-gallery-ready. A probe gave up at 131 s.
Changelog
none
Agent session: cosmic-gray
๐ค Generated with Claude Code