From f88b758e9c2c5e2396ee32e950fc39e740117206 Mon Sep 17 00:00:00 2001 From: probe Date: Tue, 10 Nov 2026 19:41:28 +0800 Subject: [PATCH] test(webui): read the rail flyout's exit animation from the event it fires The shared surface exit runs for 120ms and the flyout is unmounted when it ends, so reading the element's computed animation-name after the exit state has appeared is a race: on a loaded runner the read lands after the animation has been cleaned up and sees an empty value, which says nothing about whether the shared exit ran. The sibling surfaces already read their exit from the animation event, which is what happened rather than what one instant of the page looks like. The expected values are unchanged: the exit state, the shared surface exit animation, and the flyout going away. --- .../webui/test/browser/rail-archive.spec.mjs | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/packages/webui/test/browser/rail-archive.spec.mjs b/packages/webui/test/browser/rail-archive.spec.mjs index db643c8ad..837b7c134 100644 --- a/packages/webui/test/browser/rail-archive.spec.mjs +++ b/packages/webui/test/browser/rail-archive.spec.mjs @@ -39,6 +39,27 @@ test("the rail context-menu flyout uses the shared surface exit lifecycle", asyn await configureFixture(page, () => undefined); await openApp(page); await page.emulateMedia({ reducedMotion: "no-preference" }); + // The exit animation is read from the event it fires, the way the other + // surfaces read theirs. The shared exit runs for 120ms and the flyout is + // unmounted when it ends, so a style read that lands after that sees an + // element with nothing left on it and says nothing about whether the + // shared exit ran at all. + await page.evaluate(() => { + window.__railSubmenuExitAnimation = ""; + document.addEventListener( + "animationstart", + (event) => { + if ( + event.target instanceof HTMLElement && + event.target.classList.contains("webui-context-menu-submenu") && + event.animationName === "webui-motion-surface-exit" + ) { + window.__railSubmenuExitAnimation = event.animationName; + } + }, + { capture: true }, + ); + }); await sessionRow(page, "B").first().click({ button: "right", position: { x: 40, y: 16 } }); const submenu = page.locator(".webui-context-menu-submenu"); @@ -49,8 +70,10 @@ test("the rail context-menu flyout uses the shared surface exit lifecycle", asyn await menuItem(page, "归档").hover(); await expect(submenu).toHaveAttribute("data-webui-motion-state", "exit"); - expect(await submenu.evaluate((node) => getComputedStyle(node).animationName)).toBe("webui-motion-surface-exit"); - await expect(submenu).toHaveCount(0, { timeout: 500 }); + await expect + .poll(() => page.evaluate(() => window.__railSubmenuExitAnimation)) + .toBe("webui-motion-surface-exit"); + await expect(submenu).toHaveCount(0); }); /** Opens settings the way a user does: the user menu, then 设置. */