From f1c2daf4a2ffa681703d1ce9dc5e3702983ea622 Mon Sep 17 00:00:00 2001 From: Francisco Solla Date: Tue, 18 Aug 2026 01:11:37 -0300 Subject: [PATCH] =?UTF-8?q?fix(e2e=20S1):=20poll=20positivo=20do=20HTML=20?= =?UTF-8?q?da=20home=20at=C3=A9=20convergir=20p=C3=B3s-revalidate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit O warm-up único ainda deixava uma janela de stale-serve sob carga (a regeneração ISR não completava entre o GET e o goto, e o DOM carregado nunca refrescava — flake no cleanup sob a suíte paralela). Polling do estado esperado no HTML do servidor (12x1s) antes do goto torna o empty-state, o full-state e o cleanup determinísticos. --- tests/e2e/frontend.e2e.spec.ts | 35 +++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/tests/e2e/frontend.e2e.spec.ts b/tests/e2e/frontend.e2e.spec.ts index a96df3ac..24a2e0f3 100644 --- a/tests/e2e/frontend.e2e.spec.ts +++ b/tests/e2e/frontend.e2e.spec.ts @@ -481,11 +481,28 @@ test.describe('Campaign home content section', () => { return doc } - // ISR serves the stale page once while it regenerates after a revalidateTag - // (slower under the parallel suite). A warm-up GET triggers that - // regeneration so the navigation that follows always lands on fresh HTML. - const warmHome = async (request: APIRequestContext) => { - await request.get(`${baseURL}/`).catch(() => undefined) + // ISR serves the stale page while it regenerates after a revalidateTag + // (slower under the parallel suite), and a navigation that lands on it never + // refreshes its DOM. Poll the server HTML positively until it converges to + // the expected section state; the navigation that follows then always lands + // on the fresh page. + const waitForHomeSectionState = async ( + request: APIRequestContext, + expected: 'present' | 'absent', + attempts = 12, + ) => { + for (let attempt = 0; attempt < attempts; attempt += 1) { + const response = await request.get(`${baseURL}/`).catch(() => undefined) + if (response?.ok()) { + const html = await response.text() + const present = html.includes('data-home-section="contents"') + if (present === (expected === 'present')) return + } + await new Promise((resolve) => setTimeout(resolve, 1_000)) + } + throw new Error( + `A home não convergiu para o estado "${expected}" da seção de conteúdos após ${attempts}s.`, + ) } test('hides the content section while no articles are visible', async ({ page, request }) => { @@ -497,7 +514,7 @@ test.describe('Campaign home content section', () => { headers: { 'x-revalidate-secret': revalidateSecret }, }) .catch(() => undefined) - await warmHome(request) + await waitForHomeSectionState(request, 'absent') await page.goto('/') await expect(page).toHaveURL(/\/$/) @@ -574,8 +591,8 @@ test.describe('Campaign home content section', () => { try { await page.setViewportSize({ width: 1280, height: 900 }) // The create hooks revalidated the `posts` tag in the server process; - // warm the home so the navigation lands on the regenerated page. - await warmHome(request) + // wait until the regenerated page actually shows the section. + await waitForHomeSectionState(request, 'present') await page.goto('/') const section = page.locator('[data-home-section="contents"]') await expect(section).toBeVisible() @@ -663,7 +680,7 @@ test.describe('Campaign home content section', () => { // Converge the persisted dev cache back to the empty state so the // next run starts deterministic (fail-closed on delete, too). - await warmHome(request) + await waitForHomeSectionState(request, 'absent') await page.setViewportSize({ width: 1280, height: 900 }) await page.goto('/') await expect(page.locator('[data-home-section="contents"]')).toHaveCount(0)