From 3b547e08c1bd9d54367ef1a6431f00bc8a3d787f Mon Sep 17 00:00:00 2001 From: Matt Toohey Date: Thu, 29 Oct 2026 15:47:55 +1100 Subject: [PATCH] test(browser): open the message-link context menu by keyboard "a message link's context menu opens its thread as a tab without leaving the channel" flaked on WebKit CI (runs 40809436911, 40568494289, 40449417643) with an empty menu after `link.click({ button: "right" })`. The first attempt met Virtua's 150 ms pointer-event hold, so the channel timeline section intercepted it. Playwright's retry scrolled the timeline 53 px, restarting the hold, and reported success because it checks the hit target only on the press; the contextmenu event reached the scroller and the link's trigger never ran. The link's ContextMenuTrigger in MessageLink.tsx opens its menu on Shift+F10 and the ContextMenu key, anchored to the link, and MessageLink.test.tsx covers both keys. This journey is about the composed menu, preview and tab owner, not hit testing, so focus the link and press Shift+F10, as the activation journey earlier in the spec already does with Enter. Other right-clicks on message links: - navigation-thread-history.spec.mjs right-clicks a link in the thread panel, which is not virtualized, so it has no hold and keeps the real pointer path for the link menu. - external-links.spec.mjs right-clicks a channel-timeline link after end() without virtuaIdle and shares the exposure. It is not known to flake and is the remaining timeline pointer path, so it is unchanged. Verification (local macOS, Playwright Chromium and WebKit; the repo defines no Firefox project): - With the hold kept active by a 1 px scrollTop nudge every frame (scratch spec, not committed), Shift+F10 opened the menu in both engines; the right-click timed out with an empty menu in both. - The fixed test with --repeat-each=40: WebKit 40/40, Chromium 40/40. - buzz-links.spec.mjs in full: WebKit 9/9, Chromium 9/9. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Matt Toohey --- tests/browser/buzz-links.spec.mjs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/browser/buzz-links.spec.mjs b/tests/browser/buzz-links.spec.mjs index d14e9301cb..f2a40976b8 100644 --- a/tests/browser/buzz-links.spec.mjs +++ b/tests/browser/buzz-links.spec.mjs @@ -225,11 +225,14 @@ test("a message link's context menu opens its thread as a tab without leaving th await expect(link).toBeVisible(); await virtuaIdle(page); const before = (await state(page)).entry; - // Target the link itself instead of a cached coordinate: under load WebKit can - // spend the first pointer move opening the preview and toolbar, shifting the - // actionable point before the contextmenu event. The test is about the link's - // composed context-menu owner, not stale coordinate hit-testing. - await link.click({ button: "right" }); + // A right-click that meets Virtua's 150ms pointer-event hold is intercepted by + // the scroller; Playwright's retry scrolls the list, restarting the hold, and + // the contextmenu event can then reach the scroller instead of the link. This + // test is about the link's composed context-menu owner, not hit testing: open + // it by keyboard, which no scroll can intercept. + await link.focus(); + await expect(link).toBeFocused(); + await link.press("Shift+F10"); const menu = page.getByRole("menu"); await expect(menu.getByRole("menuitem")).toHaveText([ "Open link",