From d59fb8c39c1bc743dddffddd151b7ddf8045c14c Mon Sep 17 00:00:00 2001 From: Ido Shamun <1993245+idoshamun@users.noreply.github.com> Date: Wed, 26 Aug 2026 09:22:24 +0300 Subject: [PATCH] fix(webapp): stop feed page tests flaking on a cold jest cache MainFeedLayout only reaches the feed pages through next/dynamic, so its module graph is compiled inside the first findBy*/waitFor call. That compile takes ~470ms with a warm jest transform cache but over 3 seconds with a cold one, which blows RTL's 1000ms default timeout and fails with "Unable to find an element by: [data-testid=postItem]". CI restores no jest transform cache and reorders test files between runs (--runInBand with --split-by=timings), so whichever feed page test lands first pays the compile. That is why the failure showed up on main and on unrelated PRs. Preload the module in beforeAll so the compile no longer sits inside the wait window, raise the file timeout to cover that compile, and give the feed waits an explicit budget. A cold-cache sweep over the other feed page tests found only these four affected, all of them pages whose getLayout is getMainFeedLayout. Cold-cache duration of the affected tests drops from 2992-4145ms (failing) to 494-678ms (passing). --- .../webapp/__tests__/MostDiscussedPage.tsx | 19 ++++++++++++---- packages/webapp/__tests__/MostUpvotedPage.tsx | 22 +++++++++++++++---- packages/webapp/__tests__/MyFeedPage.tsx | 20 +++++++++++++---- packages/webapp/__tests__/PopularPage.tsx | 15 ++++++++++++- 4 files changed, 63 insertions(+), 13 deletions(-) diff --git a/packages/webapp/__tests__/MostDiscussedPage.tsx b/packages/webapp/__tests__/MostDiscussedPage.tsx index e10d2adc702..2ae3b9d253f 100644 --- a/packages/webapp/__tests__/MostDiscussedPage.tsx +++ b/packages/webapp/__tests__/MostDiscussedPage.tsx @@ -3,7 +3,7 @@ import { MOST_DISCUSSED_FEED_QUERY } from '@dailydotdev/shared/src/graphql/feed' import nock from 'nock'; import React from 'react'; import type { RenderResult } from '@testing-library/react'; -import { render, screen, waitFor } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import { QueryClient } from '@tanstack/react-query'; import type { LoggedUser } from '@dailydotdev/shared/src/lib/user'; import type { NextRouter } from 'next/router'; @@ -19,6 +19,17 @@ import { COMMENT_FEED_QUERY } from '@dailydotdev/shared/src/graphql/comments'; import Discussed from '../pages/discussed'; import { defaultCommentsPage } from './ProfileRepliesPage'; +// Compiling the feed module graph takes seconds on a cold jest transform cache, +// so both the compile in beforeAll and the render need more than the defaults. +jest.setTimeout(30000); +const feedTimeout = 5000; + +beforeAll(async () => { + // MainFeedLayout only reaches the page through next/dynamic, so without this + // its compile lands inside the first findBy* wait and eats the whole budget. + await import('@dailydotdev/shared/src/components/MainFeedLayout'); +}); + beforeEach(() => { jest.restoreAllMocks(); jest.clearAllMocks(); @@ -92,10 +103,10 @@ function renderComponent( it('should request most discussed feed when logged-in', async () => { renderComponent([createCommentFeedMock()]); - await waitFor(async () => { - const elements = await screen.findAllByTestId('comment'); - expect(elements.length).toBeTruthy(); + const elements = await screen.findAllByTestId('comment', undefined, { + timeout: feedTimeout, }); + expect(elements.length).toBeTruthy(); }); it('should not request most discussed feed when not logged-in', async () => { diff --git a/packages/webapp/__tests__/MostUpvotedPage.tsx b/packages/webapp/__tests__/MostUpvotedPage.tsx index 9d5a57e8537..336356e7631 100644 --- a/packages/webapp/__tests__/MostUpvotedPage.tsx +++ b/packages/webapp/__tests__/MostUpvotedPage.tsx @@ -16,6 +16,17 @@ import { mockGraphQL } from '@dailydotdev/shared/__tests__/helpers/graphql'; import { TestBootProvider } from '@dailydotdev/shared/__tests__/helpers/boot'; import Upvoted from '../pages/upvoted'; +// Compiling the feed module graph takes seconds on a cold jest transform cache, +// so both the compile in beforeAll and the render need more than the defaults. +jest.setTimeout(30000); +const feedTimeout = 5000; + +beforeAll(async () => { + // MainFeedLayout only reaches the page through next/dynamic, so without this + // its compile lands inside the first findBy* wait and eats the whole budget. + await import('@dailydotdev/shared/src/components/MainFeedLayout'); +}); + afterEach(() => { nock.cleanAll(); }); @@ -92,7 +103,7 @@ it('should request most upvoted feed when logged-in', async () => { () => { expect(screen.getAllByTestId('postItem').length).toBeTruthy(); }, - { timeout: 3000 }, + { timeout: feedTimeout }, ); }); @@ -110,7 +121,10 @@ it('should request most upvoted feed when not', async () => { ], undefined, ); - await waitFor(() => { - expect(screen.getAllByTestId('postItem').length).toBeTruthy(); - }); + await waitFor( + () => { + expect(screen.getAllByTestId('postItem').length).toBeTruthy(); + }, + { timeout: feedTimeout }, + ); }); diff --git a/packages/webapp/__tests__/MyFeedPage.tsx b/packages/webapp/__tests__/MyFeedPage.tsx index 5debd7a2c7f..145df183da6 100644 --- a/packages/webapp/__tests__/MyFeedPage.tsx +++ b/packages/webapp/__tests__/MyFeedPage.tsx @@ -31,8 +31,16 @@ let defaultAlerts: Alerts = { filter: true }; const updateAlerts = jest.fn(); const originalScrollTo = window.scrollTo; -beforeAll(() => { +// Compiling the feed module graph takes seconds on a cold jest transform cache, +// so both the compile in beforeAll and the render need more than the defaults. +jest.setTimeout(30000); +const feedTimeout = 5000; + +beforeAll(async () => { window.scrollTo = jest.fn(); + // MainFeedLayout only reaches the page through next/dynamic, so without this + // its compile lands inside the first findBy* wait and eats the whole budget. + await import('@dailydotdev/shared/src/components/MainFeedLayout'); }); afterAll(() => { @@ -144,7 +152,9 @@ it('should request user feed', async () => { }); renderComponent([]); - const elements = await screen.findAllByTestId('postItem'); + const elements = await screen.findAllByTestId('postItem', undefined, { + timeout: feedTimeout, + }); expect(elements.length).toBeTruthy(); await waitFor(() => { expect(graphQLRequests).toHaveLength(1); @@ -174,7 +184,9 @@ it('should request anonymous my feed', async () => { ], undefined, ); - await waitForNock(); - const elements = await screen.findAllByTestId('postItem'); + const elements = await screen.findAllByTestId('postItem', undefined, { + timeout: feedTimeout, + }); expect(elements.length).toBeTruthy(); + await waitForNock(); }); diff --git a/packages/webapp/__tests__/PopularPage.tsx b/packages/webapp/__tests__/PopularPage.tsx index 3a5205e83d1..ab401dfbcce 100644 --- a/packages/webapp/__tests__/PopularPage.tsx +++ b/packages/webapp/__tests__/PopularPage.tsx @@ -19,6 +19,17 @@ import { mockGraphQL } from '@dailydotdev/shared/__tests__/helpers/graphql'; import { TestBootProvider } from '@dailydotdev/shared/__tests__/helpers/boot'; import Popular from '../pages/popular'; +// Compiling the feed module graph takes seconds on a cold jest transform cache, +// so both the compile in beforeAll and the render need more than the defaults. +jest.setTimeout(30000); +const feedTimeout = 5000; + +beforeAll(async () => { + // MainFeedLayout only reaches the page through next/dynamic, so without this + // its compile lands inside the first findBy* wait and eats the whole budget. + await import('@dailydotdev/shared/src/components/MainFeedLayout'); +}); + beforeEach(() => { jest.restoreAllMocks(); jest.clearAllMocks(); @@ -86,6 +97,8 @@ it('should request anonymous popular feed', async () => { ], undefined, ); - const elements = await screen.findAllByTestId('postItem'); + const elements = await screen.findAllByTestId('postItem', undefined, { + timeout: feedTimeout, + }); expect(elements.length).toBeTruthy(); });