51 lines
2.5 KiB
TypeScript
51 lines
2.5 KiB
TypeScript
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(<ErrorPage error={new Error('offline')} reset={reset} />);
|
||
|
|
fireEvent.click(screen.getByRole('button', { name: 'Try again' }));
|
||
|
|
expect(reset).toHaveBeenCalledTimes(1);
|
||
|
|
});
|