Skip to content

Commit 5a1dcce

Browse files
committed
test(base-data-service): use withService pattern in fetchInfiniteQuery tests
1 parent b802b77 commit 5a1dcce

1 file changed

Lines changed: 119 additions & 96 deletions

File tree

packages/base-data-service/src/BaseDataService.fetchInfiniteQuery.test.ts

Lines changed: 119 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -151,145 +151,168 @@ class PaginatedService extends BaseDataService<
151151
}
152152
}
153153

154-
describe('BaseDataService: fetchInfiniteQuery', () => {
155-
let service: PaginatedService;
156-
157-
const createService = (options?: {
158-
staleTime?: number;
159-
}): PaginatedService => {
160-
const messenger = new Messenger({ namespace: serviceName });
161-
service = new PaginatedService(messenger, options);
162-
return service;
163-
};
154+
/**
155+
* The options bag that `withService` takes.
156+
*/
157+
type WithServiceOptions = {
158+
staleTime?: number;
159+
};
164160

165-
afterEach(() => {
166-
service?.destroy();
167-
});
161+
type WithServiceCallback<ReturnValue> = (payload: {
162+
service: PaginatedService;
163+
messenger: PaginatedServiceMessenger;
164+
}) => Promise<ReturnValue> | ReturnValue;
168165

166+
/**
167+
* Construct a `PaginatedService`, pass it to the given function, and tear it
168+
* down afterward.
169+
*
170+
* @param args - Either a function, or an options bag + a function. The options
171+
* bag configures the service (currently just `staleTime`). The function is
172+
* called with the new service and its messenger.
173+
* @returns The same return value as the given function.
174+
*/
175+
async function withService<ReturnValue>(
176+
...args:
177+
| [WithServiceCallback<ReturnValue>]
178+
| [WithServiceOptions, WithServiceCallback<ReturnValue>]
179+
): Promise<ReturnValue> {
180+
const [{ staleTime }, testFunction] =
181+
args.length === 2 ? args : [{}, args[0]];
182+
const messenger = new Messenger({ namespace: serviceName });
183+
const service = new PaginatedService(messenger, { staleTime });
184+
try {
185+
return await testFunction({ service, messenger });
186+
} finally {
187+
service.destroy();
188+
}
189+
}
190+
191+
describe('BaseDataService: fetchInfiniteQuery', () => {
169192
describe('with page-param callbacks', () => {
170193
it('returns the first page on a cold fetch', async () => {
171-
createService();
194+
await withService(async ({ service }) => {
195+
const page = await service.withCallbacks();
172196

173-
const page = await service.withCallbacks();
174-
175-
expect(page.data).toStrictEqual(['item-0', 'item-1', 'item-2']);
176-
expect(page.pageInfo.hasPreviousPage).toBe(false);
197+
expect(page.data).toStrictEqual(['item-0', 'item-1', 'item-2']);
198+
expect(page.pageInfo.hasPreviousPage).toBe(false);
199+
});
177200
});
178201

179202
it('jumps directly to a page by cursor on a cold cache', async () => {
180-
createService();
203+
await withService(async ({ service }) => {
204+
const page = await service.withCallbacks({ after: '6' });
181205

182-
const page = await service.withCallbacks({ after: '6' });
183-
184-
expect(page.data).toStrictEqual(['item-6', 'item-7', 'item-8']);
206+
expect(page.data).toStrictEqual(['item-6', 'item-7', 'item-8']);
207+
});
185208
});
186209

187210
it('paginates forward across every page', async () => {
188-
createService();
189-
190-
const page1 = await service.withCallbacks();
191-
const page2 = await service.withCallbacks({
192-
after: page1.pageInfo.endCursor as string,
193-
});
194-
const page3 = await service.withCallbacks({
195-
after: page2.pageInfo.endCursor as string,
211+
await withService(async ({ service }) => {
212+
const page1 = await service.withCallbacks();
213+
const page2 = await service.withCallbacks({
214+
after: page1.pageInfo.endCursor as string,
215+
});
216+
const page3 = await service.withCallbacks({
217+
after: page2.pageInfo.endCursor as string,
218+
});
219+
220+
expect(page1.data).toStrictEqual(['item-0', 'item-1', 'item-2']);
221+
expect(page2.data).toStrictEqual(['item-3', 'item-4', 'item-5']);
222+
expect(page3.data).toStrictEqual(['item-6', 'item-7', 'item-8']);
196223
});
197-
198-
expect(page1.data).toStrictEqual(['item-0', 'item-1', 'item-2']);
199-
expect(page2.data).toStrictEqual(['item-3', 'item-4', 'item-5']);
200-
expect(page3.data).toStrictEqual(['item-6', 'item-7', 'item-8']);
201224
});
202225

203226
it('paginates backward to the previous page', async () => {
204-
createService();
227+
await withService(async ({ service }) => {
228+
// Start in the middle so there is a previous page to go back to.
229+
const middle = await service.withCallbacks({ after: '3' });
230+
expect(middle.data).toStrictEqual(['item-3', 'item-4', 'item-5']);
205231

206-
// Start in the middle so there is a previous page to go back to.
207-
const middle = await service.withCallbacks({ after: '3' });
208-
expect(middle.data).toStrictEqual(['item-3', 'item-4', 'item-5']);
232+
const previous = await service.withCallbacks({
233+
before: middle.pageInfo.startCursor as string,
234+
});
209235

210-
const previous = await service.withCallbacks({
211-
before: middle.pageInfo.startCursor as string,
236+
expect(previous.data).toStrictEqual(['item-0', 'item-1', 'item-2']);
212237
});
213-
214-
expect(previous.data).toStrictEqual(['item-0', 'item-1', 'item-2']);
215238
});
216239

217240
it('returns only the requested page, not the accumulated data', async () => {
218-
createService();
219-
220-
await service.withCallbacks();
221-
await service.withCallbacks({ after: '3' });
222-
const page3 = await service.withCallbacks({ after: '6' });
241+
await withService(async ({ service }) => {
242+
await service.withCallbacks();
243+
await service.withCallbacks({ after: '3' });
244+
const page3 = await service.withCallbacks({ after: '6' });
223245

224-
expect(page3.data).toHaveLength(PAGE_SIZE);
225-
expect(page3.data).toStrictEqual(['item-6', 'item-7', 'item-8']);
246+
expect(page3.data).toHaveLength(PAGE_SIZE);
247+
expect(page3.data).toStrictEqual(['item-6', 'item-7', 'item-8']);
248+
});
226249
});
227250

228251
it('does not refetch a fresh cached page', async () => {
229-
createService({ staleTime: Infinity });
230-
231-
await service.withCallbacks();
232-
await service.withCallbacks();
252+
await withService({ staleTime: Infinity }, async ({ service }) => {
253+
await service.withCallbacks();
254+
await service.withCallbacks();
233255

234-
expect(service.queryFnCalls).toHaveLength(1);
256+
expect(service.queryFnCalls).toHaveLength(1);
257+
});
235258
});
236259

237260
it('keeps navigation correct after refetching stale pages', async () => {
238-
createService({ staleTime: 0 });
239-
240-
await service.withCallbacks();
241-
await service.withCallbacks({ after: '3' });
242-
await service.withCallbacks({ after: '6' });
243-
244-
// A param-less call is stale, so query-core rebuilds all cached pages.
245-
// This exercises the full-rebuild path, which must use the consumer's
246-
// page-param callbacks and not any resolvers injected while paging.
247-
const rebuilt = await service.withCallbacks();
248-
expect(rebuilt.data).toStrictEqual(['item-0', 'item-1', 'item-2']);
249-
250-
const page2Again = await service.withCallbacks({ after: '3' });
251-
expect(page2Again.data).toStrictEqual(['item-3', 'item-4', 'item-5']);
261+
await withService({ staleTime: 0 }, async ({ service }) => {
262+
await service.withCallbacks();
263+
await service.withCallbacks({ after: '3' });
264+
await service.withCallbacks({ after: '6' });
265+
266+
// A param-less call is stale, so query-core rebuilds all cached pages.
267+
// This exercises the full-rebuild path, which must use the consumer's
268+
// page-param callbacks and not any resolvers injected while paging.
269+
const rebuilt = await service.withCallbacks();
270+
expect(rebuilt.data).toStrictEqual(['item-0', 'item-1', 'item-2']);
271+
272+
const page2Again = await service.withCallbacks({ after: '3' });
273+
expect(page2Again.data).toStrictEqual(['item-3', 'item-4', 'item-5']);
274+
});
252275
});
253276
});
254277

255278
describe('without page-param callbacks', () => {
256279
it('fetches an arbitrary page by explicit cursor (forward)', async () => {
257-
createService();
258-
259-
const page1 = await service.withoutCallbacks();
260-
expect(page1.data).toStrictEqual(['item-0', 'item-1', 'item-2']);
280+
await withService(async ({ service }) => {
281+
const page1 = await service.withoutCallbacks();
282+
expect(page1.data).toStrictEqual(['item-0', 'item-1', 'item-2']);
261283

262-
const page2 = await service.withoutCallbacks({ after: '3' });
263-
expect(page2.data).toStrictEqual(['item-3', 'item-4', 'item-5']);
284+
const page2 = await service.withoutCallbacks({ after: '3' });
285+
expect(page2.data).toStrictEqual(['item-3', 'item-4', 'item-5']);
286+
});
264287
});
265288

266289
it('fetches the correct page content for a `before` cursor', async () => {
267-
createService();
290+
await withService(async ({ service }) => {
291+
// Cold jump into the middle, then ask for the page before it.
292+
const middle = await service.withoutCallbacks({ after: '3' });
293+
expect(middle.data).toStrictEqual(['item-3', 'item-4', 'item-5']);
268294

269-
// Cold jump into the middle, then ask for the page before it.
270-
const middle = await service.withoutCallbacks({ after: '3' });
271-
expect(middle.data).toStrictEqual(['item-3', 'item-4', 'item-5']);
272-
273-
const previous = await service.withoutCallbacks({ before: '3' });
274-
expect(previous.data).toStrictEqual(['item-0', 'item-1', 'item-2']);
295+
const previous = await service.withoutCallbacks({ before: '3' });
296+
expect(previous.data).toStrictEqual(['item-0', 'item-1', 'item-2']);
297+
});
275298
});
276299

277300
it('refetches stale multi-page state without page-param callbacks', async () => {
278-
createService({ staleTime: 0 });
279-
280-
await service.withoutCallbacks();
281-
await service.withoutCallbacks({ after: '3' });
282-
await service.withoutCallbacks({ after: '6' });
283-
284-
// Stale, so query-core rebuilds every cached page by walking forward from
285-
// the first one. With no consumer `getNextPageParam`, that walk must not
286-
// throw (the base service supplies a no-op resolver).
287-
const rebuilt = await service.withoutCallbacks();
288-
expect(rebuilt.data).toStrictEqual(['item-0', 'item-1', 'item-2']);
289-
290-
// Navigation still works after the rebuild.
291-
const page2Again = await service.withoutCallbacks({ after: '3' });
292-
expect(page2Again.data).toStrictEqual(['item-3', 'item-4', 'item-5']);
301+
await withService({ staleTime: 0 }, async ({ service }) => {
302+
await service.withoutCallbacks();
303+
await service.withoutCallbacks({ after: '3' });
304+
await service.withoutCallbacks({ after: '6' });
305+
306+
// Stale, so query-core rebuilds every cached page by walking forward
307+
// from the first one. With no consumer `getNextPageParam`, that walk
308+
// must not throw (the base service supplies a no-op resolver).
309+
const rebuilt = await service.withoutCallbacks();
310+
expect(rebuilt.data).toStrictEqual(['item-0', 'item-1', 'item-2']);
311+
312+
// Navigation still works after the rebuild.
313+
const page2Again = await service.withoutCallbacks({ after: '3' });
314+
expect(page2Again.data).toStrictEqual(['item-3', 'item-4', 'item-5']);
315+
});
293316
});
294317
});
295318
});

0 commit comments

Comments
 (0)