import { fireEvent, render, screen } from '@testing-library/react'; import HomePage from '@/app/(frontend)/page'; import SchoolPage from '@/app/(frontend)/school/[slug]/page'; import ErrorPage from '@/app/(frontend)/error'; import { APIFetchError, fetchSchools, fetchFilters, fetchSchoolDetails } from '@/lib/api'; import { fetchPlace, fetchPlaces } from '@/lib/places'; jest.mock('@/lib/api', () => ({ ...jest.requireActual('@/lib/api'), fetchSchools: jest.fn(), fetchSchoolDetails: jest.fn(), fetchFilters: jest.fn(async () => ({})), fetchDataInfo: jest.fn(async () => null), fetchNationalAverages: jest.fn(async () => null), })); jest.mock('@/lib/flags', () => ({ getFlags: jest.fn(async () => ({})) })); jest.mock('next/navigation', () => ({ notFound: () => { throw new Error('NEXT_NOT_FOUND'); }, redirect: jest.fn(), })); const realFetch = global.fetch; afterEach(() => { global.fetch = realFetch; jest.clearAllMocks(); }); test('school outages propagate; only a real 404 becomes not found', async () => { const request = { params: Promise.resolve({ slug: '100001-school' }) }; const outage = new APIFetchError('unavailable', 503); jest.mocked(fetchSchoolDetails).mockRejectedValueOnce(outage); await expect(SchoolPage(request)).rejects.toBe(outage); jest.mocked(fetchSchoolDetails).mockRejectedValueOnce(new APIFetchError('missing', 404)); await expect(SchoolPage(request)).rejects.toThrow('NEXT_NOT_FOUND'); }); test('homepage search failure is not returned as an empty successful page', async () => { jest.mocked(fetchSchools).mockRejectedValueOnce(new APIFetchError('unavailable', 503)); await expect(HomePage({ searchParams: Promise.resolve({ search: 'school' }) })).rejects.toThrow('unavailable'); }); test('a place is absent only on 404; other failures propagate', async () => { global.fetch = jest.fn().mockResolvedValue({ ok: false, status: 404 }); await expect(fetchPlace('town', 'example')).resolves.toBeNull(); jest.mocked(global.fetch).mockResolvedValue({ ok: false, status: 503 } as Response); await expect(fetchPlace('town', 'example')).rejects.toMatchObject({ status: 503 }); await expect(fetchPlaces()).rejects.toMatchObject({ status: 503 }); }); test('the error boundary offers a retry without showing an empty search', () => { const reset = jest.fn(); render(); fireEvent.click(screen.getByRole('button', { name: 'Try again' })); expect(reset).toHaveBeenCalledTimes(1); });