Skip to content

Commit c8271b3

Browse files
committed
test(SearchForm): wrap tests in QueryClientProvider
All 15 SearchForm tests were logging "No QueryClient set" errors to stderr because PageIdFetcher (rendered via PageTitleInput) uses useSuspenseQuery. Added QueryClientProvider wrapping to all test renders following the established pattern from PageIdFetcher and PageTitleInput tests. Co-Authored-By: Claude
1 parent 5884041 commit c8271b3

File tree

1 file changed

+34
-16
lines changed

1 file changed

+34
-16
lines changed

src/components/SearchForm.tsx

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,12 @@ if (import.meta.vitest) {
9898
const { render, screen, act } = await import('@testing-library/react');
9999
const { userEvent } = await import('@testing-library/user-event');
100100
const MediaWikiAPIs = await import('../services/MediaWikiAPIs');
101+
const { QueryClient, QueryClientProvider } = await import('@tanstack/react-query');
102+
type QueryClientType = InstanceType<typeof QueryClient>;
101103

102104
describe('SearchForm', () => {
103105
const mockFormAction = vi.fn();
106+
let queryClient: QueryClientType;
104107
const defaultSearchState = {
105108
wikiUrl: new URL('https://en.wikipedia.org'),
106109
pageId: null,
@@ -139,6 +142,9 @@ if (import.meta.vitest) {
139142

140143
beforeEach(() => {
141144
mockFormAction.mockClear();
145+
queryClient = new QueryClient({
146+
defaultOptions: { queries: { retry: false } },
147+
});
142148
vi.spyOn(MediaWikiAPIs, 'fetchPageId').mockImplementation(
143149
mockFetchPageId
144150
);
@@ -148,8 +154,16 @@ if (import.meta.vitest) {
148154
vi.restoreAllMocks();
149155
});
150156

157+
const renderWithQuery = (component: React.ReactElement) => {
158+
return render(
159+
<QueryClientProvider client={queryClient}>
160+
{component}
161+
</QueryClientProvider>
162+
);
163+
};
164+
151165
it('renders form inputs and button', async () => {
152-
await act(async () => render(<SearchForm {...defaultProps} />));
166+
await act(async () => renderWithQuery(<SearchForm {...defaultProps} />));
153167
expect(screen.getByLabelText(/Wiki Article Title:/i)).toBeInTheDocument();
154168
expect(screen.getByLabelText(/Text to Find:/i)).toBeInTheDocument();
155169
expect(
@@ -159,7 +173,7 @@ if (import.meta.vitest) {
159173

160174
it('submits form with correct FormData', async () => {
161175
const user = userEvent.setup();
162-
await act(async () => render(<SearchForm {...defaultProps} />));
176+
await act(async () => renderWithQuery(<SearchForm {...defaultProps} />));
163177
const titleInput = screen.getByLabelText(/Wiki Article Title:/i);
164178
const textArea = screen.getByLabelText(/Text to Find:/i);
165179
const button = screen.getByRole('button', {
@@ -185,22 +199,22 @@ if (import.meta.vitest) {
185199
});
186200

187201
it('renders the startRevId input field', async () => {
188-
await act(async () => render(<SearchForm {...defaultProps} />));
202+
await act(async () => renderWithQuery(<SearchForm {...defaultProps} />));
189203
expect(
190204
screen.getByLabelText(/Search from Rev ID \(optional\):/i)
191205
).toBeInTheDocument();
192206
});
193207

194208
it('renders the endRevId input field', async () => {
195-
await act(async () => render(<SearchForm {...defaultProps} />));
209+
await act(async () => renderWithQuery(<SearchForm {...defaultProps} />));
196210
expect(
197211
screen.getByLabelText(/End Rev ID \(optional\):/i)
198212
).toBeInTheDocument();
199213
});
200214

201215
it('does not populate the endRevId input with the revisionId from searchState', async () => {
202216
await act(async () =>
203-
render(
217+
renderWithQuery(
204218
<SearchForm
205219
formAction={mockFormAction}
206220
isPending={false}
@@ -220,7 +234,7 @@ if (import.meta.vitest) {
220234

221235
it('submits form with correct FormData including endRevId', async () => {
222236
const user = userEvent.setup();
223-
await act(async () => render(<SearchForm {...defaultProps} />));
237+
await act(async () => renderWithQuery(<SearchForm {...defaultProps} />));
224238
const titleInput = screen.getByLabelText(/Wiki Article Title:/i);
225239
const textArea = screen.getByLabelText(/Text to Find:/i);
226240
const endRevIdInput = screen.getByLabelText(/End Rev ID \(optional\):/i);
@@ -252,7 +266,7 @@ if (import.meta.vitest) {
252266
it('does not submit when inputs are empty', async () => {
253267
const user = userEvent.setup();
254268
await act(async () =>
255-
render(
269+
renderWithQuery(
256270
<SearchForm
257271
{...defaultProps}
258272
searchState={{
@@ -272,7 +286,7 @@ if (import.meta.vitest) {
272286

273287
it('disables the button when isPending is true', async () => {
274288
await act(async () =>
275-
render(
289+
renderWithQuery(
276290
<SearchForm
277291
formAction={mockFormAction}
278292
isPending={true}
@@ -285,14 +299,14 @@ if (import.meta.vitest) {
285299
});
286300

287301
it('renders the WikiSelector component', async () => {
288-
await act(async () => render(<SearchForm {...defaultProps} />));
302+
await act(async () => renderWithQuery(<SearchForm {...defaultProps} />));
289303
expect(screen.getByLabelText(/Wiki Site:/i)).toBeInTheDocument();
290304
});
291305

292306
it('the selected option remains selected after form submission', async () => {
293307
const { fireEvent } = await import('@testing-library/react');
294308
const user = userEvent.setup();
295-
await act(async () => render(<SearchForm {...defaultProps} />));
309+
await act(async () => renderWithQuery(<SearchForm {...defaultProps} />));
296310
const wikiSelector =
297311
await screen.findByLabelText<HTMLSelectElement>(/Wiki Site:/i);
298312

@@ -315,7 +329,7 @@ if (import.meta.vitest) {
315329

316330
it('renders initial values from searchState', async () => {
317331
await act(async () =>
318-
render(
332+
renderWithQuery(
319333
<SearchForm
320334
formAction={mockFormAction}
321335
isPending={false}
@@ -349,7 +363,7 @@ if (import.meta.vitest) {
349363
});
350364

351365
it('renders the order radio buttons', async () => {
352-
await act(async () => render(<SearchForm {...defaultProps} />));
366+
await act(async () => renderWithQuery(<SearchForm {...defaultProps} />));
353367
expect(
354368
screen.getByLabelText(/Ascending \(Older First\)/i)
355369
).toBeInTheDocument();
@@ -364,7 +378,7 @@ if (import.meta.vitest) {
364378
order: 'asc' as const,
365379
};
366380
await act(async () =>
367-
render(<SearchForm {...defaultProps} searchState={searchStateAsc} />)
381+
renderWithQuery(<SearchForm {...defaultProps} searchState={searchStateAsc} />)
368382
);
369383
expect(
370384
screen.getByLabelText(/Descending \(Newer First\)/i)
@@ -377,7 +391,7 @@ if (import.meta.vitest) {
377391
order: 'asc' as const,
378392
};
379393
const { rerender } = await act(async () =>
380-
render(
394+
renderWithQuery(
381395
<SearchForm {...emptySearchProps} searchState={searchStateAsc} />
382396
)
383397
);
@@ -396,7 +410,11 @@ if (import.meta.vitest) {
396410
order: 'desc' as const,
397411
};
398412
await act(async () =>
399-
rerender(<SearchForm {...defaultProps} searchState={searchStateDesc} />)
413+
rerender(
414+
<QueryClientProvider client={queryClient}>
415+
<SearchForm {...defaultProps} searchState={searchStateDesc} />
416+
</QueryClientProvider>
417+
)
400418
);
401419
const radioAsc2 = screen.getByLabelText(
402420
/Ascending \(Older First\)/i
@@ -410,7 +428,7 @@ if (import.meta.vitest) {
410428

411429
it('submits form with correct FormData including order', async () => {
412430
const user = userEvent.setup();
413-
await act(async () => render(<SearchForm {...defaultProps} />));
431+
await act(async () => renderWithQuery(<SearchForm {...defaultProps} />));
414432
const titleInput = screen.getByLabelText(/Wiki Article Title:/i);
415433
const textArea = screen.getByLabelText(/Text to Find:/i);
416434
const descendingRadio = screen.getByLabelText(

0 commit comments

Comments
 (0)