From 70dde735b5847eeb72978b707262f1a8e25e9314 Mon Sep 17 00:00:00 2001 From: Nawid3333 <102778966+Nawid3333@users.noreply.github.com> Date: Sun, 11 Oct 2026 23:09:23 +0200 Subject: [PATCH 1/2] e2e: every test ends at the top of its window On CI's Linux runner, autoplay-next-episode's tests hung until mocha's 120 s timeout (main after #450, #449's runs; 2026-10-11). The step logs show where: each hang came in a test's first browser.url(), before its first step, and each in a test after one that ended switched into the player's frame (not every such test hung: a race in the driver). The navigation replaces the page, and the frame the driver waited in goes with it. A mocha root hook now switches to the window's top after every test, in every suite (framed-player.e2e.mjs did it on its own). A window or session already gone is expected; any other failure of the reset is logged, so a reset that stops working shows. Reviewed by deepseek, kimi and glm (the diagnosis; the logging and the wording are their findings). Co-Authored-By: Claude Opus 5.5 --- tests/e2e/setupGuard.mjs | 17 ++++++++++++++++- tests/unit/e2eHarness.test.mjs | 14 ++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/tests/e2e/setupGuard.mjs b/tests/e2e/setupGuard.mjs index 52affcc9..4f3863a6 100644 --- a/tests/e2e/setupGuard.mjs +++ b/tests/e2e/setupGuard.mjs @@ -29,7 +29,7 @@ export async function guardSetup(setup) { } // The configs' mochaOpts.rootHooks: the setup's failure first, the more useful reason, then -// bidi.mjs's check. +// bidi.mjs's check; and after every test, the window's top again. export const rootHooks = { beforeEach() { if (setupError) { @@ -38,4 +38,19 @@ export const rootHooks = { } bidiRootHooks.beforeEach(); }, + // A test that ended in a frame (a player's) could leave the next test's first browser.url() + // waiting in a frame the navigation removed, until the 120 s timeout: on CI's Linux runner, + // each hang came in a test after one that ended in the player's frame, before its first step + // (autoplay-next-episode, main and #449, 2026-10-11; not every such test hung - a race in the + // driver). framed-player.e2e.mjs did this itself. A window or a session already gone is no + // news; anything else is said, so a reset that stops working shows. + async afterEach() { + try { + await globalThis.browser?.switchFrame(null); + } catch (e) { + if (!/no such (window|frame)|invalid session|session (deleted|not created)/i.test(String(e?.message || e))) { + console.warn('e2e: back to the window\'s top failed after a test:', e); + } + } + }, }; diff --git a/tests/unit/e2eHarness.test.mjs b/tests/unit/e2eHarness.test.mjs index 5237cdf7..c18ec63a 100644 --- a/tests/unit/e2eHarness.test.mjs +++ b/tests/unit/e2eHarness.test.mjs @@ -40,6 +40,20 @@ describe('guardSetup and rootHooks', () => { globalThis.browser = {isBidi: false, requestedCapabilities: {}}; expect(() => rootHooks.beforeEach()).toThrow(/BiDi is not connected/); }); + + it('leave every test at the top of its window, also where that fails or no browser is up', async () => { + // A test that ended in a frame left the next one's first navigation waiting in it (2026-10-11). + const frames = []; + globalThis.browser = {switchFrame: async (to) => frames.push(to)}; + await rootHooks.afterEach(); + expect(frames).toEqual([null]); + globalThis.browser = {switchFrame: async () => { + throw new Error('no such window'); + }}; + await expect(rootHooks.afterEach()).resolves.toBeUndefined(); + delete globalThis.browser; + await expect(rootHooks.afterEach()).resolves.toBeUndefined(); + }); }); describe('writeFixture', () => { From 355811ed8b3b103a3a708ec6cd3019a68e5735c4 Mon Sep 17 00:00:00 2001 From: Nawid3333 <102778966+Nawid3333@users.noreply.github.com> Date: Sun, 11 Oct 2026 23:20:50 +0200 Subject: [PATCH 2/2] firefox-vpn e2e: the second test enters the player itself With the root hook, a test no longer starts in the frame the one before ended in. firefox-vpn's second test clicked the player's VPN button without switching into the player, relying on the first test to leave it there - on #451's CI it ran in the page (no button, no chrome.permissions there), and the third test failed after it (no permission given). It now enters the player first; its page is the first test's, nothing navigates. Locally 3/3; deepseek, kimi and glm: nothing real. Co-Authored-By: Claude Opus 5.5 --- tests/e2e/ext-specs/firefox-vpn.e2e.mjs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/e2e/ext-specs/firefox-vpn.e2e.mjs b/tests/e2e/ext-specs/firefox-vpn.e2e.mjs index 9b4ec6a1..750907be 100644 --- a/tests/e2e/ext-specs/firefox-vpn.e2e.mjs +++ b/tests/e2e/ext-specs/firefox-vpn.e2e.mjs @@ -195,6 +195,9 @@ describe('Firefox VPN', function() { it('plays once allowed, its requests going the page\'s way', async function() { proxied.length = 0; const videoTab = await browser.getWindowHandle(); + // Into the player of the case before (its page stays): a test no longer starts in the frame + // the one before ended in (setupGuard.mjs's root afterEach, 2026-10-11). + await enterPlayer(); // A player in a page has no permissions API: its button opens the permissions page. await (await browser.$('.mainplayer .vpn_button')).click(); let permsTab;