@@ -2340,4 +2340,233 @@ describe(`QueryCollection`, () => {
23402340 expect ( collection . size ) . toBe ( items . length )
23412341 } )
23422342 } )
2343+
2344+ describe ( `Query State Utils` , ( ) => {
2345+ it ( `should expose isFetching, isRefetching, isLoading state` , async ( ) => {
2346+ const queryKey = [ `queryStateTest` ]
2347+ const items = [ { id : `1` , name : `Item 1` } ]
2348+ const queryFn = vi . fn ( ) . mockResolvedValue ( items )
2349+
2350+ const config : QueryCollectionConfig < TestItem > = {
2351+ id : `queryStateTest` ,
2352+ queryClient,
2353+ queryKey,
2354+ queryFn,
2355+ getKey,
2356+ startSync : true ,
2357+ }
2358+
2359+ const options = queryCollectionOptions ( config )
2360+ const collection = createCollection ( options )
2361+
2362+ // Initially should be loading (first fetch)
2363+ expect ( collection . utils . isLoading ( ) ) . toBe ( true )
2364+ expect ( collection . utils . isFetching ( ) ) . toBe ( true )
2365+ expect ( collection . utils . isRefetching ( ) ) . toBe ( false )
2366+
2367+ // Wait for initial fetch to complete
2368+ await vi . waitFor ( ( ) => {
2369+ expect ( collection . status ) . toBe ( `ready` )
2370+ } )
2371+
2372+ // After initial fetch, should not be loading/fetching
2373+ expect ( collection . utils . isLoading ( ) ) . toBe ( false )
2374+ expect ( collection . utils . isFetching ( ) ) . toBe ( false )
2375+ expect ( collection . utils . isRefetching ( ) ) . toBe ( false )
2376+
2377+ // Trigger a refetch
2378+ const refetchPromise = collection . utils . refetch ( )
2379+
2380+ // During refetch, should be fetching and refetching, but not loading
2381+ await vi . waitFor ( ( ) => {
2382+ expect ( collection . utils . isFetching ( ) ) . toBe ( true )
2383+ } )
2384+ expect ( collection . utils . isRefetching ( ) ) . toBe ( true )
2385+ expect ( collection . utils . isLoading ( ) ) . toBe ( false )
2386+
2387+ await refetchPromise
2388+
2389+ // After refetch completes, should not be fetching
2390+ expect ( collection . utils . isFetching ( ) ) . toBe ( false )
2391+ expect ( collection . utils . isRefetching ( ) ) . toBe ( false )
2392+ expect ( collection . utils . isLoading ( ) ) . toBe ( false )
2393+ } )
2394+
2395+ it ( `should expose dataUpdatedAt timestamp` , async ( ) => {
2396+ const queryKey = [ `dataUpdatedAtTest` ]
2397+ const items = [ { id : `1` , name : `Item 1` } ]
2398+ const queryFn = vi . fn ( ) . mockResolvedValue ( items )
2399+
2400+ const config : QueryCollectionConfig < TestItem > = {
2401+ id : `dataUpdatedAtTest` ,
2402+ queryClient,
2403+ queryKey,
2404+ queryFn,
2405+ getKey,
2406+ startSync : true ,
2407+ }
2408+
2409+ const options = queryCollectionOptions ( config )
2410+ const collection = createCollection ( options )
2411+
2412+ // Initially should be 0 (no data yet)
2413+ expect ( collection . utils . dataUpdatedAt ( ) ) . toBe ( 0 )
2414+
2415+ // Wait for initial fetch to complete
2416+ await vi . waitFor ( ( ) => {
2417+ expect ( collection . status ) . toBe ( `ready` )
2418+ } )
2419+
2420+ // After successful fetch, should have a timestamp
2421+ const firstTimestamp = collection . utils . dataUpdatedAt ( )
2422+ expect ( firstTimestamp ) . toBeGreaterThan ( 0 )
2423+
2424+ // Wait a bit to ensure timestamp difference
2425+ await new Promise ( ( resolve ) => setTimeout ( resolve , 10 ) )
2426+
2427+ // Trigger a refetch
2428+ await collection . utils . refetch ( )
2429+
2430+ // Timestamp should be updated
2431+ const secondTimestamp = collection . utils . dataUpdatedAt ( )
2432+ expect ( secondTimestamp ) . toBeGreaterThan ( firstTimestamp )
2433+ } )
2434+
2435+ it ( `should expose fetchStatus` , async ( ) => {
2436+ const queryKey = [ `fetchStatusTest` ]
2437+ const items = [ { id : `1` , name : `Item 1` } ]
2438+ const queryFn = vi . fn ( ) . mockResolvedValue ( items )
2439+
2440+ const config : QueryCollectionConfig < TestItem > = {
2441+ id : `fetchStatusTest` ,
2442+ queryClient,
2443+ queryKey,
2444+ queryFn,
2445+ getKey,
2446+ startSync : true ,
2447+ }
2448+
2449+ const options = queryCollectionOptions ( config )
2450+ const collection = createCollection ( options )
2451+
2452+ // Initially should be 'fetching'
2453+ expect ( collection . utils . fetchStatus ( ) ) . toBe ( `fetching` )
2454+
2455+ // Wait for initial fetch to complete
2456+ await vi . waitFor ( ( ) => {
2457+ expect ( collection . status ) . toBe ( `ready` )
2458+ } )
2459+
2460+ // After fetch completes, should be 'idle'
2461+ expect ( collection . utils . fetchStatus ( ) ) . toBe ( `idle` )
2462+
2463+ // Trigger a refetch
2464+ const refetchPromise = collection . utils . refetch ( )
2465+
2466+ // During refetch, should be 'fetching'
2467+ await vi . waitFor ( ( ) => {
2468+ expect ( collection . utils . fetchStatus ( ) ) . toBe ( `fetching` )
2469+ } )
2470+
2471+ await refetchPromise
2472+
2473+ // After refetch completes, should be 'idle'
2474+ expect ( collection . utils . fetchStatus ( ) ) . toBe ( `idle` )
2475+ } )
2476+
2477+ it ( `should maintain query state across multiple refetches` , async ( ) => {
2478+ const queryKey = [ `multipleRefetchStateTest` ]
2479+ let callCount = 0
2480+ const queryFn = vi . fn ( ) . mockImplementation ( ( ) => {
2481+ callCount ++
2482+ return Promise . resolve ( [ { id : `1` , name : `Item ${ callCount } ` } ] )
2483+ } )
2484+
2485+ const config : QueryCollectionConfig < TestItem > = {
2486+ id : `multipleRefetchStateTest` ,
2487+ queryClient,
2488+ queryKey,
2489+ queryFn,
2490+ getKey,
2491+ startSync : true ,
2492+ }
2493+
2494+ const options = queryCollectionOptions ( config )
2495+ const collection = createCollection ( options )
2496+
2497+ // Wait for initial load
2498+ await vi . waitFor ( ( ) => {
2499+ expect ( collection . status ) . toBe ( `ready` )
2500+ } )
2501+
2502+ const initialTimestamp = collection . utils . dataUpdatedAt ( )
2503+
2504+ // Perform multiple refetches
2505+ for ( let i = 0 ; i < 3 ; i ++ ) {
2506+ await new Promise ( ( resolve ) => setTimeout ( resolve , 10 ) )
2507+ await collection . utils . refetch ( )
2508+
2509+ // After each refetch, should have an updated timestamp
2510+ expect ( collection . utils . dataUpdatedAt ( ) ) . toBeGreaterThan (
2511+ initialTimestamp
2512+ )
2513+ expect ( collection . utils . isFetching ( ) ) . toBe ( false )
2514+ expect ( collection . utils . isRefetching ( ) ) . toBe ( false )
2515+ expect ( collection . utils . isLoading ( ) ) . toBe ( false )
2516+ expect ( collection . utils . fetchStatus ( ) ) . toBe ( `idle` )
2517+ }
2518+
2519+ expect ( queryFn ) . toHaveBeenCalledTimes ( 4 ) // 1 initial + 3 refetches
2520+ } )
2521+
2522+ it ( `should expose query state even when collection has errors` , async ( ) => {
2523+ const queryKey = [ `errorStateTest` ]
2524+ const testError = new Error ( `Test error` )
2525+ const successData = [ { id : `1` , name : `Item 1` } ]
2526+
2527+ const queryFn = vi
2528+ . fn ( )
2529+ . mockResolvedValueOnce ( successData ) // Initial success
2530+ . mockRejectedValueOnce ( testError ) // Error on refetch
2531+
2532+ const config : QueryCollectionConfig < TestItem > = {
2533+ id : `errorStateTest` ,
2534+ queryClient,
2535+ queryKey,
2536+ queryFn,
2537+ getKey,
2538+ startSync : true ,
2539+ retry : false ,
2540+ }
2541+
2542+ const options = queryCollectionOptions ( config )
2543+ const collection = createCollection ( options )
2544+
2545+ // Wait for initial success
2546+ await vi . waitFor ( ( ) => {
2547+ expect ( collection . status ) . toBe ( `ready` )
2548+ expect ( collection . utils . isError ( ) ) . toBe ( false )
2549+ } )
2550+
2551+ const successTimestamp = collection . utils . dataUpdatedAt ( )
2552+ expect ( successTimestamp ) . toBeGreaterThan ( 0 )
2553+
2554+ // Trigger a refetch that will error
2555+ await collection . utils . refetch ( )
2556+
2557+ // Wait for error
2558+ await vi . waitFor ( ( ) => {
2559+ expect ( collection . utils . isError ( ) ) . toBe ( true )
2560+ } )
2561+
2562+ // Query state should still be accessible
2563+ expect ( collection . utils . isFetching ( ) ) . toBe ( false )
2564+ expect ( collection . utils . isRefetching ( ) ) . toBe ( false )
2565+ expect ( collection . utils . isLoading ( ) ) . toBe ( false )
2566+ expect ( collection . utils . fetchStatus ( ) ) . toBe ( `idle` )
2567+
2568+ // dataUpdatedAt should remain at the last successful fetch timestamp
2569+ expect ( collection . utils . dataUpdatedAt ( ) ) . toBe ( successTimestamp )
2570+ } )
2571+ } )
23432572} )
0 commit comments