From edda068f9f453401b8b029a82da54ad9319bd95b Mon Sep 17 00:00:00 2001 From: fkwp Date: Fri, 11 Sep 2026 13:26:01 +0200 Subject: [PATCH] Stop the focus border test assuming a second device row Firefox in CI reports one microphone and no outputs, so the menu has a single selectable row and hovering the second timed out. The test now drives the pointer by coordinate over the first row, which holds however many devices a browser reports. Co-Authored-By: Claude Opus 5 (1M context) --- FEATURES_SPEC/2026-09_Audio_Quick_Menu.md | 8 +++++++ playwright/audio-menu.spec.ts | 26 ++++++++++++++--------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/FEATURES_SPEC/2026-09_Audio_Quick_Menu.md b/FEATURES_SPEC/2026-09_Audio_Quick_Menu.md index 33235a625..465e666cd 100644 --- a/FEATURES_SPEC/2026-09_Audio_Quick_Menu.md +++ b/FEATURES_SPEC/2026-09_Audio_Quick_Menu.md @@ -342,6 +342,14 @@ is what holds it there. remaining manual checks are AC6, which needs Safari, and AC21, which needs a screen reader. +### 2026-09-11 — e2e tests must not assume how many devices a browser reports +- Twice now a new e2e test has failed in CI for the same reason: how many devices a browser + invents differs between them and from a developer machine. Chromium reports several + microphones and outputs; Firefox in CI reports one microphone and no outputs, so the menu + holds a single selectable row. A test that hovered the second row timed out there. +- Tests in `playwright/audio-menu.spec.ts` now assert only what holds at any device count. + Where a fixed device list is what makes the check meaningful, it belongs in a story. + ## PRs - #4254 — draft, one commit per slice — AC1–AC27 (AC6, AC21, AC24 manual by the reviewer; diff --git a/playwright/audio-menu.spec.ts b/playwright/audio-menu.spec.ts index c0c40d7a2..46be6514f 100644 --- a/playwright/audio-menu.spec.ts +++ b/playwright/audio-menu.spec.ts @@ -181,8 +181,15 @@ test("the focus border follows the keyboard and not the pointer", async ({ await SpaHelpers.createCall(page, "Focus", "Focus border", true); await page.getByTestId("videoTile").first().waitFor(); - const chevron = page.getByRole("button", { name: "Microphone" }); - const rows = page.getByRole("menu").getByRole("menuitemradio"); + await page.getByRole("button", { name: "Microphone" }).click(); + const menu = page.getByRole("menu"); + await expect(menu).toBeVisible(); + + // However many devices this browser reports, there is at least one row. + const row = menu.getByRole("menuitemradio").first(); + const box = await row.boundingBox(); + if (box === null) throw new Error("Expected the row to be laid out"); + const centre = { x: box.x + box.width / 2, y: box.y + box.height / 2 }; const borderOfFocused = async (): Promise => page.evaluate(() => { const el = document.activeElement; @@ -190,20 +197,19 @@ test("the focus border follows the keyboard and not the pointer", async ({ }); // Reached with the pointer: the hover background carries it, no border. - await chevron.click(); - await page.getByRole("menu").waitFor(); - await rows.nth(1).hover(); - expect(await borderOfFocused()).toBe("none"); + await page.mouse.move(centre.x, centre.y); + await expect.poll(borderOfFocused).toBe("none"); // Reached with the keyboard: a border marks where the keyboard is. The menu // moves focus to whatever the pointer is over, so the browser cannot tell // these two apart on its own. await page.keyboard.press("ArrowDown"); - expect(await borderOfFocused()).toBe("solid"); + await expect.poll(borderOfFocused).toBe("solid"); - // And back, on the next movement of the pointer. - await rows.nth(0).hover(); - expect(await borderOfFocused()).toBe("none"); + // And gone again on the next movement of the pointer. A point the pointer + // is not already at, so that the move is one. + await page.mouse.move(centre.x + 4, centre.y + 2); + await expect.poll(borderOfFocused).toBe("none"); }); async function firstUncheckedIndex(