From 12e5f8dac63c37552275d8745e4814422dff71f5 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 2 Oct 2026 13:49:23 -0700 Subject: [PATCH] test(ui): disambiguate session filter controls The online-people toolbar added by c1b973aec107 shares the Filter & sort name prefix and sidebar-session-sort class with the session toolbar. Playwright substring lookups therefore matched both controls, breaking six Control UI e2e suites in scheduled main CI run 37054655798. Match the complete session button name at all 16 ambiguous lookups, including the shared ownership helper. Also target that exact button in the owner-header filtered-state assertion, which reproduced the same collision through the shared class after the first selectors were fixed. Product code and existing assertions remain unchanged. Before: six suites failed, nine tests failed, 24 passed (235.19s wall). After: all 12 relevant suites and 74 tests passed with one worker. Independent autoreview found no actionable findings. Test cost: 408.17s wall for node scripts/run-vitest.mjs run --config test/vitest/vitest.ui-e2e.config.ts --configLoader runner <12 e2e files> --maxWorkers=1. --- ui/src/e2e/chat-flow.navigation-presentation.e2e.test.ts | 2 +- ui/src/e2e/chat-header-session-outcomes.e2e.test.ts | 2 +- ui/src/e2e/chat-startup-priority.e2e.test.ts | 2 +- ui/src/e2e/session-management.archive.e2e.test.ts | 6 +++--- ui/src/e2e/session-management.bulk-delete-scope.e2e.test.ts | 2 +- ui/src/e2e/session-management.filtered-errors.e2e.test.ts | 2 +- ui/src/e2e/session-management.groups.e2e.test.ts | 4 ++-- ui/src/e2e/session-owner-filter-empty-section.e2e.test.ts | 2 +- ui/src/e2e/session-owner-filter-header.e2e.test.ts | 2 +- ui/src/e2e/session-owner-filter-keyboard.e2e.test.ts | 6 +++--- ui/src/e2e/session-ownership-visuals.test-support.ts | 2 +- ui/src/e2e/session-ownership.e2e.test.ts | 2 +- 12 files changed, 17 insertions(+), 17 deletions(-) diff --git a/ui/src/e2e/chat-flow.navigation-presentation.e2e.test.ts b/ui/src/e2e/chat-flow.navigation-presentation.e2e.test.ts index 75f6004b0fb9..32f953226f9e 100644 --- a/ui/src/e2e/chat-flow.navigation-presentation.e2e.test.ts +++ b/ui/src/e2e/chat-flow.navigation-presentation.e2e.test.ts @@ -784,7 +784,7 @@ suite.define(() => { .evaluate((label) => getComputedStyle(label).fontWeight); expect(activeWeight).toBe(inactiveWeight); - const filterAndSort = page.getByRole("button", { name: "Filter & sort" }); + const filterAndSort = page.getByRole("button", { name: "Filter & sort", exact: true }); await filterAndSort.click(); await chooseSidebarMenuOption(page, "Sort by", "Last updated"); await closeSidebarMenu(page); diff --git a/ui/src/e2e/chat-header-session-outcomes.e2e.test.ts b/ui/src/e2e/chat-header-session-outcomes.e2e.test.ts index 5e10536b9780..8795563d8d91 100644 --- a/ui/src/e2e/chat-header-session-outcomes.e2e.test.ts +++ b/ui/src/e2e/chat-header-session-outcomes.e2e.test.ts @@ -150,7 +150,7 @@ suite.define(() => { if (restartable) { await page.getByRole("button", { name: "Unarchive", exact: true }).waitFor(); - await page.getByRole("button", { name: "Filter & sort" }).click(); + await page.getByRole("button", { name: "Filter & sort", exact: true }).click(); await chooseSidebarMenuOption(page, "Status", "All"); await closeSidebarMenu(page); } diff --git a/ui/src/e2e/chat-startup-priority.e2e.test.ts b/ui/src/e2e/chat-startup-priority.e2e.test.ts index 6509b08b23c3..c89e4f53b953 100644 --- a/ui/src/e2e/chat-startup-priority.e2e.test.ts +++ b/ui/src/e2e/chat-startup-priority.e2e.test.ts @@ -395,7 +395,7 @@ suite.define(() => { }, ], }); - await page.getByRole("button", { name: "Filter & sort" }).click(); + await page.getByRole("button", { name: "Filter & sort", exact: true }).click(); await chooseSidebarMenuOption(page, "Status", "Archived"); await closeSidebarMenu(page); await gateway.waitForRequest("sessions.list", { diff --git a/ui/src/e2e/session-management.archive.e2e.test.ts b/ui/src/e2e/session-management.archive.e2e.test.ts index 72a42ec6a271..ba05066c7e9c 100644 --- a/ui/src/e2e/session-management.archive.e2e.test.ts +++ b/ui/src/e2e/session-management.archive.e2e.test.ts @@ -76,11 +76,11 @@ suite.define(() => { expect(await gateway.getRequests("sessions.patch")).toEqual([]); await captureUiProof(suite, page, "agent-archive-after.png"); - await page.getByRole("button", { name: "Filter & sort" }).click(); + await page.getByRole("button", { name: "Filter & sort", exact: true }).click(); await chooseSidebarMenuOption(page, "Status", "Archived"); await closeSidebarMenu(page); await row.waitFor({ state: "visible" }); - await page.getByRole("button", { name: "Filter & sort" }).click(); + await page.getByRole("button", { name: "Filter & sort", exact: true }).click(); await chooseSidebarMenuOption(page, "Status", "Active"); await closeSidebarMenu(page); await row.waitFor({ state: "detached" }); @@ -121,7 +121,7 @@ suite.define(() => { try { await page.goto(`${suite.server.baseUrl}chat`); - await page.getByRole("button", { name: "Filter & sort" }).click(); + await page.getByRole("button", { name: "Filter & sort", exact: true }).click(); await chooseSidebarMenuOption(page, "Status", "Archived"); await closeSidebarMenu(page); diff --git a/ui/src/e2e/session-management.bulk-delete-scope.e2e.test.ts b/ui/src/e2e/session-management.bulk-delete-scope.e2e.test.ts index 107c8c141e61..3372e47e146a 100644 --- a/ui/src/e2e/session-management.bulk-delete-scope.e2e.test.ts +++ b/ui/src/e2e/session-management.bulk-delete-scope.e2e.test.ts @@ -136,7 +136,7 @@ suite.define(() => { await page.screenshot({ path: path.join(artifactDir, `${stage}.png`) }); }; const filter = async (label: "Archived" | "All") => { - await sidebar.getByRole("button", { name: "Filter & sort" }).click(); + await sidebar.getByRole("button", { name: "Filter & sort", exact: true }).click(); await chooseSidebarMenuOption(sidebar.page(), "Status", label); await closeSidebarMenu(page); }; diff --git a/ui/src/e2e/session-management.filtered-errors.e2e.test.ts b/ui/src/e2e/session-management.filtered-errors.e2e.test.ts index 8451ed55f4e2..26a85c9fe533 100644 --- a/ui/src/e2e/session-management.filtered-errors.e2e.test.ts +++ b/ui/src/e2e/session-management.filtered-errors.e2e.test.ts @@ -134,7 +134,7 @@ suite.define(() => { try { await page.goto(`${suite.server.baseUrl}chat`); const selectFilter = async (label: "Archived" | "All" | "Active") => { - await page.getByRole("button", { name: "Filter & sort" }).click(); + await page.getByRole("button", { name: "Filter & sort", exact: true }).click(); await chooseSidebarMenuOption(page, "Status", label); await closeSidebarMenu(page); }; diff --git a/ui/src/e2e/session-management.groups.e2e.test.ts b/ui/src/e2e/session-management.groups.e2e.test.ts index f841f56cadcc..ce0ede7bfc28 100644 --- a/ui/src/e2e/session-management.groups.e2e.test.ts +++ b/ui/src/e2e/session-management.groups.e2e.test.ts @@ -694,7 +694,7 @@ suite.define(() => { // Group by "None" flattens the category sections into the plain list. The // confirm left the pointer over the dialog rather than the sidebar; the // global toolbar remains available without revealing a section action. - const filterAndSortButton = page.getByRole("button", { name: "Filter & sort" }); + const filterAndSortButton = page.getByRole("button", { name: "Filter & sort", exact: true }); await filterAndSortButton.click(); await openSidebarMenu(page); const showAutomationSessions = page.getByRole("switch", { @@ -937,7 +937,7 @@ suite.define(() => { await expect.poll(() => page.locator(".sidebar-recent-session").count()).toBe(11); const patchCountBeforeFlatDrag = (await gateway.getRequests("sessions.patch")).length; - const filterAndSortButton = page.getByRole("button", { name: "Filter & sort" }); + const filterAndSortButton = page.getByRole("button", { name: "Filter & sort", exact: true }); await filterAndSortButton.click(); await chooseSidebarMenuOption(page, "Group by", "None"); await closeSidebarMenu(page); diff --git a/ui/src/e2e/session-owner-filter-empty-section.e2e.test.ts b/ui/src/e2e/session-owner-filter-empty-section.e2e.test.ts index 512add6b251e..e7015f10a30a 100644 --- a/ui/src/e2e/session-owner-filter-empty-section.e2e.test.ts +++ b/ui/src/e2e/session-owner-filter-empty-section.e2e.test.ts @@ -52,7 +52,7 @@ suite.define(() => { try { await page.goto(controlUiSessionUrl(suite.server.baseUrl, "agent:main:owner-0")); - const filter = page.getByRole("button", { name: "Filter & sort" }); + const filter = page.getByRole("button", { name: "Filter & sort", exact: true }); const menu = page.locator(".sidebar-session-sort-menu"); await filter.click(); await chooseSidebarMenuOption(menu.page(), "Group by", "Person"); diff --git a/ui/src/e2e/session-owner-filter-header.e2e.test.ts b/ui/src/e2e/session-owner-filter-header.e2e.test.ts index 09af47ecf8f6..40f67ac31a3e 100644 --- a/ui/src/e2e/session-owner-filter-header.e2e.test.ts +++ b/ui/src/e2e/session-owner-filter-header.e2e.test.ts @@ -109,7 +109,7 @@ suite.define(() => { await expectBrowser(bobSection).toHaveCount(0); await expectBrowser(adaSection.getByText("Ada research", { exact: true })).toBeVisible(); const summary = page.locator(".sidebar-session-filter-summary"); - const funnel = page.locator(".sidebar-session-sort"); + const funnel = page.getByRole("button", { name: "Filter & sort", exact: true }); await expectBrowser(summary).toBeVisible(); await expectBrowser(summary).toContainText("Ada"); await expectBrowser(summary).toHaveAccessibleName("Ada ยท Show all sessions"); diff --git a/ui/src/e2e/session-owner-filter-keyboard.e2e.test.ts b/ui/src/e2e/session-owner-filter-keyboard.e2e.test.ts index f9aff6fc28ba..436c76178078 100644 --- a/ui/src/e2e/session-owner-filter-keyboard.e2e.test.ts +++ b/ui/src/e2e/session-owner-filter-keyboard.e2e.test.ts @@ -51,7 +51,7 @@ suite.define(() => { try { await page.goto(controlUiSessionUrl(suite.server.baseUrl, "agent:main:ada")); await waitForSessionRosterHydration(page); - const trigger = page.getByRole("button", { name: "Filter & sort" }); + const trigger = page.getByRole("button", { name: "Filter & sort", exact: true }); await trigger.focus(); await page.keyboard.press("Enter"); const menu = page.locator(".sidebar-session-sort-menu"); @@ -125,7 +125,7 @@ suite.define(() => { if (name === "compact") { await page.getByRole("button", { name: "Expand sidebar" }).click(); } - await page.getByRole("button", { name: "Filter & sort" }).click(); + await page.getByRole("button", { name: "Filter & sort", exact: true }).click(); await openSidebarMenu(page); const menu = page.locator(".sidebar-session-sort-menu"); const menuBounds = await menu.locator(".sidebar-session-filter-panel").boundingBox(); @@ -180,7 +180,7 @@ suite.define(() => { await page.goto(controlUiSessionUrl(suite.server.baseUrl, "agent:main:rtl-owners")); await page.locator("html").evaluate((element) => element.setAttribute("dir", "rtl")); await page.getByRole("button", { name: "Expand sidebar" }).click(); - await page.getByRole("button", { name: "Filter & sort" }).click(); + await page.getByRole("button", { name: "Filter & sort", exact: true }).click(); await openSidebarMenu(page); const menu = page.locator(".sidebar-session-sort-menu"); const owners = menu.locator("#sidebar-sessions-owner"); diff --git a/ui/src/e2e/session-ownership-visuals.test-support.ts b/ui/src/e2e/session-ownership-visuals.test-support.ts index 76ac00da854c..04e3a0e8eb8f 100644 --- a/ui/src/e2e/session-ownership-visuals.test-support.ts +++ b/ui/src/e2e/session-ownership-visuals.test-support.ts @@ -95,7 +95,7 @@ export async function captureSessionOwnerPageProof( } export async function openSidebarSortMenu(page: Page) { - const filterAndSort = page.getByRole("button", { name: "Filter & sort" }); + const filterAndSort = page.getByRole("button", { name: "Filter & sort", exact: true }); await expect.poll(() => filterAndSort.count(), { timeout: 2_000 }).toBe(1); const menu = await openSidebarMenu(page); await waitForControlUiProofSurface(menu.locator(".sidebar-session-filter-panel"), [ diff --git a/ui/src/e2e/session-ownership.e2e.test.ts b/ui/src/e2e/session-ownership.e2e.test.ts index c035beea2a42..a60d8fbd584e 100644 --- a/ui/src/e2e/session-ownership.e2e.test.ts +++ b/ui/src/e2e/session-ownership.e2e.test.ts @@ -490,7 +490,7 @@ suite.define(() => { await currentPage.getByText("Ada research", { exact: true }).first().waitFor(); await currentPage.getByText("Bob operations", { exact: true }).first().waitFor(); - const filterAndSort = currentPage.getByRole("button", { name: "Filter & sort" }); + const filterAndSort = currentPage.getByRole("button", { name: "Filter & sort", exact: true }); await filterAndSort.focus(); await currentPage.keyboard.press("Enter");