From 38a71e04ca72ca086fb136b89b72c48309c49f0a Mon Sep 17 00:00:00 2001 From: Codex Bot Date: Mon, 2 Nov 2026 16:32:37 +0800 Subject: [PATCH] fix(ci): accept hydrated markers hidden by responsive layouts --- apps/waterfall-wiki/tests/brand-navigation.mjs | 4 +++- apps/waterfall-wiki/tests/brand-navigation.test.mjs | 10 ++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/apps/waterfall-wiki/tests/brand-navigation.mjs b/apps/waterfall-wiki/tests/brand-navigation.mjs index c6d359e..b7bc03d 100644 --- a/apps/waterfall-wiki/tests/brand-navigation.mjs +++ b/apps/waterfall-wiki/tests/brand-navigation.mjs @@ -51,9 +51,11 @@ export async function visitBrowserDocument(page, url, reload = false) { const remaining = timeout - (Date.now() - started); if (remaining <= 0) throw new Error('Document hydration budget exhausted: ' + url); try { + // Responsive layouts may hide the toggle. Its mounted ready marker + // proves hydration; each caller still checks the controls it needs. // A cancelled script may be a preload duplicate. Hydration is the // authority; recover only when cancellation actually blocks startup. - await page.locator('button[data-theme-ready="true"]').first().waitFor({timeout: failures.length ? Math.min(5000, remaining) : remaining}); + await page.locator('button[data-theme-ready="true"]').first().waitFor({state: 'attached', timeout: failures.length ? Math.min(5000, remaining) : remaining}); } catch (error) { if (failures.length) throw new Error('Document bootstrap script request failed: ' + failures.join('; '), {cause: error}); throw error; diff --git a/apps/waterfall-wiki/tests/brand-navigation.test.mjs b/apps/waterfall-wiki/tests/brand-navigation.test.mjs index b3ebd02..2502f73 100644 --- a/apps/waterfall-wiki/tests/brand-navigation.test.mjs +++ b/apps/waterfall-wiki/tests/brand-navigation.test.mjs @@ -49,7 +49,7 @@ test('recovery cannot extend the total navigation budget', async () => { assert.equal(calls, 1); }); -function browserFixture(failBootstrap = false, failHydration = false, blockedOnce = false) { +function browserFixture(failBootstrap = false, failHydration = false, blockedOnce = false, readyVisible = true) { const listeners = new Map(), calls = []; const page = { on(event, fn) { if (!listeners.has(event)) listeners.set(event, new Set()); listeners.get(event).add(fn); }, @@ -61,7 +61,7 @@ function browserFixture(failBootstrap = false, failHydration = false, blockedOnc if (failBootstrap && calls.length === 1) for (const fn of listeners.get('requestfailed') || []) fn(request); return response(200); }, - locator() { return {first: () => ({waitFor: async () => { calls.push('hydrated'); if (failHydration || (blockedOnce && calls.length === 2)) throw Error('Hydration timeout'); }})}; }, + locator() { return {first: () => ({waitFor: async options => { calls.push('hydrated'); if (!readyVisible && options.state !== 'attached') throw Error('Ready marker is hidden by responsive CSS'); if (failHydration || (blockedOnce && calls.length === 2)) throw Error('Hydration timeout'); }})}; }, }; return {page, calls, listeners}; } @@ -87,3 +87,9 @@ test('cancelled script does not reject an already hydrated document', async () = await visitBrowserDocument(page, '/account'); assert.deepEqual(calls, ['domcontentloaded', 'hydrated']); }); + +test('hydrated pages are ready even when responsive CSS hides the marker', async () => { + const {page, calls} = browserFixture(false, false, false, false); + await visitBrowserDocument(page, '/graphic'); + assert.deepEqual(calls, ['domcontentloaded', 'hydrated']); +});