import { act, fireEvent, render, screen } from '@testing-library/react'; import { HomeView } from '@/components/HomeView'; import { fetchSchools } from '@/lib/api'; import { primaryFixture } from '../support/schoolFixtures'; import type { SchoolsResponse, School } from '@/lib/types'; let params = new URLSearchParams('postcode=SW1A+1AA'); jest.mock('next/navigation', () => ({ useSearchParams: () => params, usePathname: () => '/', useRouter: () => ({ push: jest.fn(), replace: jest.fn() }), })); jest.mock('@/context/ComparisonContext', () => ({ useComparisonContext: () => ({ addSchool: jest.fn(), removeSchool: jest.fn(), selectedSchools: [] }), })); jest.mock('@/lib/api', () => ({ fetchSchools: jest.fn(), fetchNationalAverages: jest.fn(async () => ({})), fetchLAaverages: jest.fn(async () => ({ secondary: { attainment_8_by_la: {} } })), })); // Renders only the List/Map switch HomeView hands it, which lives in its row. jest.mock('@/components/FilterBar', () => ({ FilterBar: ({ viewSwitch }: { viewSwitch?: unknown }) => viewSwitch || null, })); jest.mock('@/components/SchoolRow', () => ({ SchoolRow: ({ school }: {school: School}) =>
{school.school_name}
})); jest.mock('@/components/SchoolMap', () => ({ SchoolMap: ({ schools }: {schools: School[]}) =>
{schools.map(s => s.school_name).join(',')}
})); const filters = { local_authorities: [], school_types: [], years: [], phases: [], genders: [], admissions_policies: [] }; function response(name: string): SchoolsResponse { return { schools: [{ ...primaryFixture.schoolInfo, school_name: name }], total: 2, page: 1, page_size: 1, total_pages: 2 }; } function deferred() { let resolve!: (value: SchoolsResponse) => void; let reject!: (error: Error) => void; const promise = new Promise((yes, no) => { resolve = yes; reject = no; }); return { promise, resolve, reject }; } beforeEach(() => { params = new URLSearchParams('postcode=SW1A+1AA'); jest.mocked(fetchSchools).mockReset(); }); test('load-more results from an old search are discarded, even after returning to it', async () => { // Name searches: a postcode search opens on the map, which has no Load more. params = new URLSearchParams('search=abbey'); const pending = deferred(); jest.mocked(fetchSchools).mockReturnValueOnce(pending.promise); const view = render(); fireEvent.click(screen.getByRole('button', { name: 'Load more schools' })); const signal = jest.mocked(fetchSchools).mock.calls[0][1]?.signal; params = new URLSearchParams('search=brecknock'); view.rerender(); expect(signal?.aborted).toBe(true); params = new URLSearchParams('search=abbey'); view.rerender(); await act(async () => pending.resolve(response('Stale append'))); expect(screen.queryByText('Stale append')).not.toBeInTheDocument(); expect(screen.getByRole('button', { name: 'Load more schools' })).toBeEnabled(); }); test('an older map response cannot overwrite the current search', async () => { const first = deferred(), second = deferred(); jest.mocked(fetchSchools).mockReturnValueOnce(first.promise).mockReturnValueOnce(second.promise); const initial = response('Initial A'); const view = render(); fireEvent.click(screen.getByRole('button', { name: 'Map' })); params = new URLSearchParams('postcode=SW2+1AA'); view.rerender(); await act(async () => second.resolve(response('Current map'))); await act(async () => first.resolve(response('Stale map'))); expect(screen.getByTestId('map')).toHaveTextContent('Current map'); expect(screen.getByTestId('map')).not.toHaveTextContent('Stale map'); }); test('failed map requests can be retried by reopening the map', async () => { const pending = deferred(); jest.mocked(fetchSchools).mockReturnValueOnce(pending.promise).mockResolvedValue(response('Retry result')); render(); fireEvent.click(screen.getByRole('button', { name: 'Map' })); await act(async () => pending.reject(new Error('offline'))); fireEvent.click(screen.getByRole('button', { name: 'List' })); await act(async () => fireEvent.click(screen.getByRole('button', { name: 'Map' }))); expect(fetchSchools).toHaveBeenCalledTimes(2); expect(screen.getByTestId('map')).toHaveTextContent('Retry result'); });