/** * Regression: opening a compare link while a DIFFERENT basket is stored must * not blank the page. * * The basket hydrates from localStorage first, which can fire a fetch for the * OLD school set; the URL-seed effect then replaces the basket with the URL's * schools (already covered by SSR data, so no new fetch). When the stale * response for the old set finally lands, it must not clobber the fresh SSR * data — that left every section (including the trends chart) empty until a * hard refresh. */ import { act, render, screen, waitFor } from '@testing-library/react'; import { ComparisonView } from '@/components/ComparisonView'; import { ComparisonProvider } from '@/context/ComparisonProvider'; import type { ComparisonData, School } from '@/lib/types'; const fetchComparison = jest.fn(); jest.mock('@/lib/api', () => ({ fetchComparison: (...args: unknown[]) => fetchComparison(...args), })); jest.mock('@/lib/analytics', () => ({ track: jest.fn() })); function school(urn: number, name: string): School { return { urn, school_name: name, local_authority: 'Testshire', school_type: 'Community school', rwm_expected_pct: 80, phase: 'Primary', } as School; } function data(urn: number, name: string): ComparisonData { return { school_info: school(urn, name), yearly_data: [{ year: 202425, rwm_expected_pct: 80 }] as ComparisonData['yearly_data'], ofsted: null, census: null, admissions: null, admissions_history: [], deprivation: null, }; } // The visitor's previously stored basket (a different school entirely). const STORED_SCHOOL = school(900, 'Old Stored School'); // The comparison the URL (and SSR) actually asked for. const URL_DATA = { '100': data(100, 'Alpha Primary'), '200': data(200, 'Beta Primary'), }; beforeEach(() => { fetchComparison.mockReset(); localStorage.clear(); }); test('a stale fetch for the previously stored basket does not clobber the URL comparison', async () => { localStorage.setItem('selectedSchools', JSON.stringify([STORED_SCHOOL])); const pending: Array<(v: unknown) => void> = []; fetchComparison.mockImplementation(() => new Promise((resolve) => pending.push(resolve))); render( , ); // The URL's schools render from SSR data once the basket is reseeded. await waitFor(() => { expect(screen.getByRole('heading', { name: 'At a glance' })).toBeInTheDocument(); }); expect(screen.getAllByText('Alpha Primary').length).toBeGreaterThan(0); // The transient stored-basket fetch (for school 900) resolves LATE, after // the basket has moved on to the URL's schools. await act(async () => { for (const resolve of pending) { resolve({ comparison: { '900': data(900, 'Old Stored School') }, national_averages: { year: 202425, primary: {}, secondary: {}, by_year: [] }, benchmarks: undefined, }); } }); // The page must still show the URL comparison — not go blank. expect(screen.getByRole('heading', { name: 'At a glance' })).toBeInTheDocument(); expect(screen.getAllByText('Alpha Primary').length).toBeGreaterThan(0); });