From 0b53fc699dd649ec483c07d628f9c41197607a3c Mon Sep 17 00:00:00 2001 From: Diem Nguyen Date: Tue, 20 Oct 2026 09:53:46 -0700 Subject: [PATCH] test(desktop): read only the open effort menu in effort-picker e2e (#8476) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary `menuValues()` in `claude-effort-picker.spec.ts` read every `menuitemradio` on the page right after it opened a menu. Dropdown menus stay mounted for their 100ms exit animation (`POPOVER_RADIX_MOTION_CLASS`). That means the model menu, which `pick()` had just closed, could still be in the DOM, and its rows ended up in the effort list: ``` - Expected - 0 + Received + 6 + "Default model (Opus)", "Opus", "Claude Sonnet 5", "Haiku", "Claude Opus 4.8", "Custom model...", "Adapter default", "max", ``` The helper now waits for the trigger to report `aria-expanded="true"`, then reads rows only from the menu named in its `aria-controls`. The fix only touches the test; no product code changes. ### Related issue The flake was introduced with the spec in #8059. In CI it failed `Desktop Smoke E2E (2)` on #8473 (all three attempts, [job](https://github.com/block/buzz/actions/runs/39336482034/job/118347673219)) and failed once before passing on retry on #8474. ### Testing - **Forced repro.** I temporarily stretched the closed-menu exit animation to 1500ms using `page.addStyleTag` (not committed): - Before: 5/5 failed with the same `toEqual` diff. - After: 5/5 passed. - **Natural repeats.** I ran `--repeat-each` locally with 3–4 workers, 100 runs on each version: - Before: 3 failures from the menu-row leak. - After: 0. - Both versions also had the same local-only startup timeouts (`waiting for getByTestId('open-agents-view')`), which this change doesn't affect. - **Full spec.** `pnpm exec playwright test --project=smoke tests/e2e/claude-effort-picker.spec.ts`: 27 passed. - `biome check` is clean. Signed-off-by: Fizz <6be7be23ec5bba868f3e1db5ec86013d76bd1a293dce5b435a192158fec13641@buzz.block.builderlab.xyz> Co-authored-by: Fizz <6be7be23ec5bba868f3e1db5ec86013d76bd1a293dce5b435a192158fec13641@buzz.block.builderlab.xyz> --- desktop/tests/e2e/claude-effort-picker.spec.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/desktop/tests/e2e/claude-effort-picker.spec.ts b/desktop/tests/e2e/claude-effort-picker.spec.ts index 81d86645a5..27c086272e 100644 --- a/desktop/tests/e2e/claude-effort-picker.spec.ts +++ b/desktop/tests/e2e/claude-effort-picker.spec.ts @@ -137,7 +137,14 @@ async function payloadsFor(page: Page, command: string) { async function menuValues(page: Page, trigger: Locator) { await trigger.click(); - const rows = await page.getByRole("menuitemradio").allTextContents(); + await expect(trigger).toHaveAttribute("aria-expanded", "true"); + // A menu that just closed stays mounted during its exit animation, so read + // only the menu this trigger controls. + const menu = page.locator( + `[id="${await trigger.getAttribute("aria-controls")}"]`, + ); + await expect(menu).toBeVisible(); + const rows = await menu.getByRole("menuitemradio").allTextContents(); await page.keyboard.press("Escape"); return rows; }