@@ -10,6 +10,7 @@ import { beforeEach, describe, expect, it, onTestFinished, vi } from 'vitest';
1010import { PowerSyncContext } from '../src/hooks/PowerSyncContext' ;
1111import { useQuery } from '../src/hooks/watched/useQuery' ;
1212import { useWatchedQuerySubscription } from '../src/hooks/watched/useWatchedQuerySubscription' ;
13+ import { QueryResult } from '../src/hooks/watched/watch-types' ;
1314
1415export const openPowerSync = ( ) => {
1516 const db = new PowerSyncDatabase ( {
@@ -147,6 +148,13 @@ describe('useQuery', () => {
147148 (uuid (), 'second')
148149 ` ) ;
149150
151+ type TestEvent = {
152+ parameters : string [ ] ;
153+ hookResults : QueryResult < any > ;
154+ } ;
155+
156+ const hookEvents : TestEvent [ ] = [ ] ;
157+
150158 const query = ( ) => {
151159 const [ parameters , setParameters ] = React . useState < string [ ] > ( [ 'first' ] ) ;
152160
@@ -155,7 +163,12 @@ describe('useQuery', () => {
155163 newParametersPromise . then ( ( params ) => setParameters ( params ) ) ;
156164 } , [ ] ) ;
157165
158- return useQuery ( 'SELECT * FROM lists WHERE name = ?' , parameters ) ;
166+ const result = useQuery ( 'SELECT * FROM lists WHERE name = ?' , parameters ) ;
167+ hookEvents . push ( {
168+ parameters,
169+ hookResults : result
170+ } ) ;
171+ return result ;
159172 } ;
160173
161174 const { result } = renderHook ( query , { wrapper : ( { children } ) => testWrapper ( { children, db } ) } ) ;
@@ -168,6 +181,12 @@ describe('useQuery', () => {
168181 { timeout : 500 , interval : 100 }
169182 ) ;
170183
184+ // Verify that the fetching status was correlated to the parameters
185+ const firstResultEvent = hookEvents . find ( ( event ) => event . hookResults . data . length == 1 ) ;
186+ expect ( firstResultEvent ) . toBeDefined ( ) ;
187+ // Fetching should be false as soon as the results were made available
188+ expect ( firstResultEvent ?. hookResults . isFetching ) . false ;
189+
171190 // Now update the parameter
172191 updateParameters ( [ 'second' ] ) ;
173192
@@ -178,6 +197,135 @@ describe('useQuery', () => {
178197 } ,
179198 { timeout : 500 , interval : 100 }
180199 ) ;
200+
201+ // finds the first result where the parameters have changed
202+ const secondFetchingEvent = hookEvents . find ( ( event ) => event . parameters [ 0 ] == 'second' ) ;
203+ expect ( secondFetchingEvent ) . toBeDefined ( ) ;
204+ // We should immediately report that we are fetching once we detect new params
205+ expect ( secondFetchingEvent ?. hookResults . isFetching ) . true ;
206+ } ) ;
207+
208+ it ( 'should react to updated queries (fast update)' , async ( ) => {
209+ const db = openPowerSync ( ) ;
210+
211+ await db . execute ( /* sql */ `
212+ INSERT INTO
213+ lists (id, name)
214+ VALUES
215+ (uuid (), 'first'),
216+ (uuid (), 'second')
217+ ` ) ;
218+
219+ type TestEvent = {
220+ parameters : string [ ] ;
221+ hookResults : QueryResult < any > ;
222+ } ;
223+
224+ const hookEvents : TestEvent [ ] = [ ] ;
225+
226+ const queryObserver = new commonSdk . BaseObserver ( ) ;
227+ const baseQuery = 'SELECT * FROM lists WHERE name = ?' ;
228+ const query = ( ) => {
229+ const [ query , setQuery ] = React . useState ( {
230+ sql : baseQuery ,
231+ params : [ '' ]
232+ } ) ;
233+
234+ useEffect ( ( ) => {
235+ // allow updating the parameters externally
236+ queryObserver . registerListener ( {
237+ queryUpdated : ( query ) => setQuery ( query )
238+ } ) ;
239+ } , [ ] ) ;
240+
241+ const result = useQuery ( query . sql , query . params ) ;
242+ hookEvents . push ( {
243+ parameters : query . params ,
244+ hookResults : result
245+ } ) ;
246+ return result ;
247+ } ;
248+
249+ const { result } = renderHook ( query , { wrapper : ( { children } ) => testWrapper ( { children, db } ) } ) ;
250+ // let the hook render once, and immediately update the query
251+ queryObserver . iterateListeners ( ( l ) =>
252+ l . queryUpdated ?.( {
253+ sql : baseQuery ,
254+ params : [ 'first' ]
255+ } )
256+ ) ;
257+
258+ // We should only receive the first list due to the WHERE clause
259+ await vi . waitFor (
260+ ( ) => {
261+ expect ( result . current . data [ 0 ] ?. name ) . toEqual ( 'first' ) ;
262+ } ,
263+ { timeout : 500 , interval : 100 }
264+ ) ;
265+
266+ // We changed the params before the initial query could execute (we changed the params immediately)
267+ // We should not see isLoading=false for the first set of params
268+ expect (
269+ hookEvents . find ( ( event ) => event . parameters [ 0 ] == '' && event . hookResults . isLoading == false )
270+ ) . toBeUndefined ( ) ;
271+ expect (
272+ hookEvents . find (
273+ ( event ) =>
274+ event . parameters [ 0 ] == 'first' &&
275+ event . hookResults . isLoading == true &&
276+ event . hookResults . isFetching == true
277+ )
278+ ) . toBeDefined ( ) ;
279+
280+ // Verify that the fetching status was correlated to the parameters
281+ const firstResultEvent = hookEvents . find ( ( event ) => event . hookResults . data . length == 1 ) ;
282+ expect ( firstResultEvent ) . toBeDefined ( ) ;
283+ // Fetching should be false as soon as the results were made available
284+ expect ( firstResultEvent ?. hookResults . isFetching ) . false ;
285+
286+ // Now update the parameter with something which will cause an error
287+ queryObserver . iterateListeners ( ( l ) =>
288+ l . queryUpdated ?.( {
289+ sql : 'select this is a broken query' ,
290+ params : [ 'first' ]
291+ } )
292+ ) ;
293+
294+ // wait for the error to have been found
295+ await vi . waitFor (
296+ ( ) => {
297+ console . log ( result . current ) ;
298+ expect ( result . current . error ) . not . equal ( null ) ;
299+ expect ( result . current . isFetching ) . false ;
300+ } ,
301+ { timeout : 500 , interval : 100 }
302+ ) ;
303+
304+ // The error should not be present before isFetching is false
305+ expect (
306+ hookEvents . find ( ( event ) => event . hookResults . error != null && event . hookResults . isFetching == true )
307+ ) . toBeUndefined ( ) ;
308+
309+ queryObserver . iterateListeners ( ( l ) =>
310+ l . queryUpdated ?.( {
311+ sql : baseQuery ,
312+ params : [ 'second' ]
313+ } )
314+ ) ;
315+
316+ // We should now only receive the second list due to the WHERE clause and updated parameter
317+ await vi . waitFor (
318+ ( ) => {
319+ expect ( result . current . data [ 0 ] ?. name ) . toEqual ( 'second' ) ;
320+ expect ( result . current . error ) . null ;
321+ } ,
322+ { timeout : 500 , interval : 100 }
323+ ) ;
324+
325+ const secondFetchingEvent = hookEvents . find ( ( event ) => event . parameters [ 0 ] == 'second' ) ;
326+ expect ( secondFetchingEvent ) . toBeDefined ( ) ;
327+ // We should immediately report that we are fetching once we detect new params
328+ expect ( secondFetchingEvent ?. hookResults . isFetching ) . true ;
181329 } ) ;
182330
183331 it ( 'should execute compatible queries' , async ( ) => {
0 commit comments