@@ -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