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.
This commit is contained in:
Yuneng Jiang 2026-08-31 14:54:11 -07:00
parent 78e1c658b4
commit cc258b5473
No known key found for this signature in database
2 changed files with 42 additions and 19 deletions

View file

@ -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);
});
});

View file

@ -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);
});
});