From 137f21094083a5d44d4af53ca68c6caa35947473 Mon Sep 17 00:00:00 2001 From: liugddx Date: Fri, 2 Oct 2026 11:06:05 +0800 Subject: [PATCH] test(desktop): decide the WorkHub popup close in the main process `workhub-layout.spec.ts` checked the renderer's `aria-expanded`, then called `closePopup` in a separate main-process evaluate. Linux can auto-dismiss the native menu between the two, and `closePopup` on a dead popup crashes the main process, which fails the test with "Target page ... has been closed" or "session closed". It failed this way on two unrelated PRs (#5906, #5902). Track the popup's lifetime with its own `menu-will-close` event, and check it in the same main-process task that closes it. The existing `aria-expanded` assertion afterwards still pins the renderer state. Co-Authored-By: Claude Opus 5.5 (1M context) --- apps/desktop/e2e/workhub-layout.spec.ts | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/apps/desktop/e2e/workhub-layout.spec.ts b/apps/desktop/e2e/workhub-layout.spec.ts index b8b0fd98ed..e67ec16c3f 100644 --- a/apps/desktop/e2e/workhub-layout.spec.ts +++ b/apps/desktop/e2e/workhub-layout.spec.ts @@ -114,7 +114,14 @@ test('WorkHub uses its coordination model and shared attachment composer', async await app.evaluate(({ Menu }) => { const original = Menu.prototype.popup; Menu.prototype.popup = function (options) { - (globalThis as unknown as { workbarMenu: Electron.Menu }).workbarMenu = this; + const probe = globalThis as unknown as { workbarMenu: Electron.Menu; workbarMenuOpen: boolean }; + probe.workbarMenu = this; + probe.workbarMenuOpen = true; + // Track the popup's own lifetime in the main process: Linux may + // auto-dismiss it (e.g. after a resize) at any moment. + this.once('menu-will-close', () => { + probe.workbarMenuOpen = false; + }); Menu.prototype.popup = original; return original.call(this, options); }; @@ -128,15 +135,17 @@ test('WorkHub uses its coordination model and shared attachment composer', async 'webContents' in child && (child as Electron.WebContentsView).webContents.getURL().includes('surface=workhub'))); return container?.getVisible(); })).toBe(true); - // aria-expanded tracks the popup IPC resolution, so a menu that already - // auto-dismissed (Linux closes popups after window resizes) must not be - // closed again — closePopup on a dead popup crashes the main process. - if ((await addPanel.getAttribute('aria-expanded')) === 'true') { + // A menu that already auto-dismissed (Linux closes popups after window + // resizes) must not be closed again: closePopup on a dead popup crashes the + // main process. Decide inside the main process, in the same task as the + // close, so the dismissal cannot land between the check and the call; + // checking the renderer's aria-expanded first left exactly that window. + await mainWindow.evaluate((window) => { + const probe = globalThis as unknown as { workbarMenu: Electron.Menu; workbarMenuOpen: boolean }; // Close on the same owner passed to popup(). The no-window overload takes // Electron's close-all MenuRunner path on Linux, even for this single menu. - await mainWindow.evaluate((window) => - (globalThis as unknown as { workbarMenu: Electron.Menu }).workbarMenu.closePopup(window)); - } + if (probe.workbarMenuOpen) probe.workbarMenu.closePopup(window); + }); await expect(addPanel).not.toHaveAttribute('aria-expanded', 'true'); await workhub.getByRole('button', { name: '收起任务工作栏', exact: true }).click(); await expect(page.locator('.maka-session-workbar[data-placement="right"]')).toBeHidden();