73 lines
2.7 KiB
TypeScript
73 lines
2.7 KiB
TypeScript
import { renderHook, act, waitFor } from '@testing-library/react';
|
|||
|
|
import { useSchoolSuggest } from '@/hooks/useSchoolSuggest';
|
||
|
|
|
||
|
|
const realFetch = global.fetch;
|
||
|
|
|
||
|
|
function mockFetch(rows: unknown[], delayMs = 0) {
|
||
|
|
global.fetch = jest.fn(async (_url: unknown, init?: { signal?: AbortSignal }) => {
|
||
|
|
if (delayMs) {
|
||
|
|
await new Promise((resolve, reject) => {
|
||
|
|
const t = setTimeout(resolve, delayMs);
|
||
|
|
init?.signal?.addEventListener('abort', () => {
|
||
|
|
clearTimeout(t);
|
||
|
|
reject(Object.assign(new Error('aborted'), { name: 'AbortError' }));
|
||
|
|
});
|
||
|
|
});
|
||
|
|
}
|
||
|
|
return { ok: true, json: async () => ({ suggestions: rows }) };
|
||
|
|
}) as unknown as typeof fetch;
|
||
|
|
}
|
||
|
|
|
||
|
|
const ROW = {
|
||
|
|
urn: 1, school_name: 'Brecknock Primary School', local_authority: 'Camden',
|
||
|
|
postcode: 'NW1 1AA', phase: 'Primary', school_type: 'Community school',
|
||
|
|
};
|
||
|
|
|
||
|
|
describe('useSchoolSuggest', () => {
|
||
|
|
beforeEach(() => { jest.useFakeTimers(); });
|
||
|
|
afterEach(() => { jest.useRealTimers(); global.fetch = realFetch; });
|
||
|
|
|
||
|
|
it('does not fetch below the minimum query length', () => {
|
||
|
|
mockFetch([ROW]);
|
||
|
|
renderHook(() => useSchoolSuggest('b', true));
|
||
|
|
act(() => { jest.advanceTimersByTime(500); });
|
||
|
|
expect(global.fetch).not.toHaveBeenCalled();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('does not fetch at all when disabled', () => {
|
||
|
|
// The flag being off must mean no request, not a hidden dropdown.
|
||
|
|
mockFetch([ROW]);
|
||
|
|
renderHook(() => useSchoolSuggest('brecknock', false));
|
||
|
|
act(() => { jest.advanceTimersByTime(500); });
|
||
|
|
expect(global.fetch).not.toHaveBeenCalled();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('debounces rather than firing per keystroke', () => {
|
||
|
|
mockFetch([ROW]);
|
||
|
|
const { rerender } = renderHook(
|
||
|
|
({ q }) => useSchoolSuggest(q, true), { initialProps: { q: 'br' } });
|
||
|
|
rerender({ q: 'bre' });
|
||
|
|
rerender({ q: 'brec' });
|
||
|
|
act(() => { jest.advanceTimersByTime(199); });
|
||
|
|
expect(global.fetch).not.toHaveBeenCalled();
|
||
|
|
act(() => { jest.advanceTimersByTime(2); });
|
||
|
|
expect(global.fetch).toHaveBeenCalledTimes(1);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('opens with results once they arrive', async () => {
|
||
|
|
mockFetch([ROW]);
|
||
|
|
const { result } = renderHook(() => useSchoolSuggest('brecknock', true));
|
||
|
|
act(() => { jest.advanceTimersByTime(200); });
|
||
|
|
await waitFor(() => expect(result.current.suggestions).toHaveLength(1));
|
||
|
|
expect(result.current.open).toBe(true);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('close() hides the list without clearing the query', async () => {
|
||
|
|
mockFetch([ROW]);
|
||
|
|
const { result } = renderHook(() => useSchoolSuggest('brecknock', true));
|
||
|
|
act(() => { jest.advanceTimersByTime(200); });
|
||
|
|
await waitFor(() => expect(result.current.open).toBe(true));
|
||
|
|
act(() => { result.current.close(); });
|
||
|
|
expect(result.current.open).toBe(false);
|
||
|
|
});
|
||
|
|
});
|