From cc258b5473932c939903d589604f83f2ca260469 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Mon, 31 Aug 2026 14:54:11 -0700 Subject: [PATCH] test(e2e): keep the placement guarantees the geometry rewrites dropped The consolidated popup test only asserted the options never cover the trigger, so opening above the trigger with room below it, the regression PR #38554 fixed, would have passed. Split it back into a below-trigger case and a cramped-viewport case. The header test accepted a single pixel of vertical intersection; require the refresh control's centre to sit within the tab row instead. --- .../autoRouterTemplateSelect.spec.ts | 58 +++++++++++++------ .../tests/modelsPage/responsiveHeader.spec.ts | 3 +- 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/tests/e2e/ui/tests/modelsPage/autoRouterTemplateSelect.spec.ts b/tests/e2e/ui/tests/modelsPage/autoRouterTemplateSelect.spec.ts index fe168267a54..7fc20104f20 100644 --- a/tests/e2e/ui/tests/modelsPage/autoRouterTemplateSelect.spec.ts +++ b/tests/e2e/ui/tests/modelsPage/autoRouterTemplateSelect.spec.ts @@ -17,32 +17,54 @@ async function openTemplateSelect(page: PlaywrightPage) { return trigger; } +async function boxes(trigger: Locator, options: Locator) { + const triggerBox = await trigger.boundingBox(); + const optionsBox = await options.boundingBox(); + return triggerBox && optionsBox ? { triggerBox, optionsBox } : null; +} + +function pollOptionsOpenBelowTrigger(trigger: Locator, options: Locator) { + return expect.poll(async () => { + const box = await boxes(trigger, options); + return box && box.optionsBox.y >= box.triggerBox.y + box.triggerBox.height; + }); +} + function pollOptionsCoverTrigger(trigger: Locator, options: Locator) { return expect.poll(async () => { - const triggerBox = await trigger.boundingBox(); - const optionsBox = await options.boundingBox(); - if (!triggerBox || !optionsBox) return null; - return optionsBox.y < triggerBox.y + triggerBox.height && optionsBox.y + optionsBox.height > triggerBox.y; + const box = await boxes(trigger, options); + return ( + box && + box.optionsBox.y < box.triggerBox.y + box.triggerBox.height && + box.optionsBox.y + box.optionsBox.height > box.triggerBox.y + ); }); } test.describe("Auto Router template select anchoring", () => { test.use({ storageState: ADMIN_STORAGE_PATH }); - for (const { room, height } of [ - { room: "with room below it", height: 900 }, - { room: "with no room below it", height: 560 }, - ]) { - test(`keeps the trigger uncovered when the options open ${room}`, async ({ page }) => { - await page.setViewportSize({ width: 1280, height }); - const trigger = await openTemplateSelect(page); - await trigger.scrollIntoViewIfNeeded(); + test("opens the options below the trigger when there is room below it", async ({ page }) => { + await page.setViewportSize({ width: 1280, height: 900 }); + const trigger = await openTemplateSelect(page); + await trigger.scrollIntoViewIfNeeded(); - await trigger.click(); - const options = page.getByRole("listbox"); - await expect(options).toBeVisible(); + await trigger.click(); + const options = page.getByRole("listbox"); + await expect(options).toBeVisible(); - await pollOptionsCoverTrigger(trigger, options).toBe(false); - }); - } + await pollOptionsOpenBelowTrigger(trigger, options).toBe(true); + }); + + test("keeps the trigger uncovered when the options open with no room below it", async ({ page }) => { + await page.setViewportSize({ width: 1280, height: 560 }); + const trigger = await openTemplateSelect(page); + await trigger.scrollIntoViewIfNeeded(); + + await trigger.click(); + const options = page.getByRole("listbox"); + await expect(options).toBeVisible(); + + await pollOptionsCoverTrigger(trigger, options).toBe(false); + }); }); diff --git a/tests/e2e/ui/tests/modelsPage/responsiveHeader.spec.ts b/tests/e2e/ui/tests/modelsPage/responsiveHeader.spec.ts index 366c371208f..aabdf18d427 100644 --- a/tests/e2e/ui/tests/modelsPage/responsiveHeader.spec.ts +++ b/tests/e2e/ui/tests/modelsPage/responsiveHeader.spec.ts @@ -24,7 +24,8 @@ test.describe("Models and Endpoints responsive header", () => { expect(tabsBox).not.toBeNull(); expect(refreshBox).not.toBeNull(); - const sharesARow = refreshBox!.y < tabsBox!.y + tabsBox!.height && refreshBox!.y + refreshBox!.height > tabsBox!.y; + const refreshCenterY = refreshBox!.y + refreshBox!.height / 2; + const sharesARow = refreshCenterY > tabsBox!.y && refreshCenterY < tabsBox!.y + tabsBox!.height; expect(sharesARow, "refresh wrapped onto its own row below the tabs").toBe(true); }); });