@@ -5,6 +5,8 @@ import { BehaviorSubject, from } from 'rxjs';
55import { writable as svelteWritable } from 'svelte/store' ;
66import { afterAll , afterEach , beforeAll , describe , expect , it , vi } from 'vitest' ;
77import type {
8+ InteropWatcher ,
9+ InteropWatcherFactory ,
810 OnUseArgument ,
911 Readable ,
1012 ReadableSignal ,
@@ -24,10 +26,12 @@ import {
2426 computed ,
2527 derived ,
2628 equal ,
29+ fromWatch ,
2730 get ,
2831 readable ,
2932 symbolObservable ,
3033 untrack ,
34+ watch ,
3135 writable ,
3236} from './index' ;
3337import { rawStoreSymbol } from './internal/exposeRawStores' ;
@@ -3565,4 +3569,198 @@ describe('stores', () => {
35653569 expectCorrectlyCleanedUp ( doubleDoubleDoubleStore ) ;
35663570 } ) ;
35673571 } ) ;
3572+
3573+ describe ( 'watch' , ( ) => {
3574+ it ( 'should work' , ( ) => {
3575+ const onUseCalls : { onUnused : number } [ ] = [ ] ;
3576+ const store = writable ( 0 , {
3577+ onUse : ( ) => {
3578+ const call = { onUnused : 0 } ;
3579+ onUseCalls . push ( call ) ;
3580+ return ( ) => {
3581+ call . onUnused ++ ;
3582+ } ;
3583+ } ,
3584+ } ) ;
3585+ const notify = vi . fn ( ) ;
3586+ const watcher = watch ( store , notify ) ;
3587+ expect ( watcher . isUpToDate ( ) ) . toBe ( false ) ;
3588+ expect ( onUseCalls . length ) . toBe ( 0 ) ;
3589+ expect ( watcher . update ( ) ) . toBe ( true ) ;
3590+ expect ( onUseCalls . length ) . toBe ( 1 ) ;
3591+ expect ( onUseCalls [ 0 ] . onUnused ) . toBe ( 0 ) ;
3592+ expect ( watcher . isUpToDate ( ) ) . toBe ( true ) ;
3593+ expect ( watcher . get ( ) ) . toBe ( 0 ) ;
3594+ expect ( notify ) . not . toHaveBeenCalled ( ) ;
3595+ store . set ( 1 ) ;
3596+ expect ( notify ) . toHaveBeenCalledOnce ( ) ;
3597+ notify . mockClear ( ) ;
3598+ expect ( watcher . isUpToDate ( ) ) . toBe ( false ) ;
3599+ store . set ( 2 ) ;
3600+ expect ( notify ) . not . toHaveBeenCalled ( ) ;
3601+ expect ( watcher . update ( ) ) . toBe ( true ) ;
3602+ expect ( watcher . isUpToDate ( ) ) . toBe ( true ) ;
3603+ expect ( watcher . get ( ) ) . toBe ( 2 ) ;
3604+ expect ( notify ) . not . toHaveBeenCalled ( ) ;
3605+ store . set ( 3 ) ;
3606+ expect ( notify ) . toHaveBeenCalledOnce ( ) ;
3607+ expect ( watcher . isUpToDate ( ) ) . toBe ( false ) ;
3608+ notify . mockClear ( ) ;
3609+ store . set ( 4 ) ;
3610+ store . set ( 2 ) ;
3611+ expect ( notify ) . not . toHaveBeenCalled ( ) ;
3612+ expect ( watcher . update ( ) ) . toBe ( false ) ;
3613+ expect ( watcher . isUpToDate ( ) ) . toBe ( true ) ;
3614+ expect ( watcher . update ( ) ) . toBe ( false ) ;
3615+ expect ( watcher . get ( ) ) . toBe ( 2 ) ;
3616+ watcher . destroy ( ) ;
3617+ expect ( ( ) => watcher . get ( ) ) . toThrowError ( 'invalid watcher state' ) ;
3618+ } ) ;
3619+ } ) ;
3620+
3621+ describe ( 'fromWatch' , ( ) => {
3622+ it ( 'should work' , ( ) => {
3623+ let notify : ( ) => void ;
3624+ let isUpdated = true ;
3625+ let value = 0 ;
3626+ const watcher = {
3627+ update : vi . fn ( ( ) => isUpdated ) ,
3628+ get : vi . fn ( ( ) => value ) ,
3629+ destroy : vi . fn ( ) ,
3630+ } satisfies InteropWatcher < number > ;
3631+ const watchFn = vi . fn ( ( notifyFn : ( ) => void ) => {
3632+ notify = notifyFn ;
3633+ return watcher ;
3634+ } ) satisfies InteropWatcherFactory < number > ;
3635+ const clearMocks = ( ) => {
3636+ watchFn . mockClear ( ) ;
3637+ watcher . update . mockClear ( ) ;
3638+ watcher . get . mockClear ( ) ;
3639+ watcher . destroy . mockClear ( ) ;
3640+ } ;
3641+ const store = fromWatch ( watchFn ) ;
3642+ expect ( watchFn ) . not . toHaveBeenCalled ( ) ;
3643+ expect ( store . get ( ) ) . toBe ( 0 ) ;
3644+ expect ( watchFn ) . toHaveBeenCalledOnce ( ) ;
3645+ expect ( watcher . update ) . toHaveBeenCalledOnce ( ) ;
3646+ expect ( watcher . get ) . toHaveBeenCalledOnce ( ) ;
3647+ expect ( watcher . destroy ) . toHaveBeenCalledOnce ( ) ;
3648+ clearMocks ( ) ;
3649+ const values : number [ ] = [ ] ;
3650+ const unsubscribe = store . subscribe ( ( value ) => {
3651+ values . push ( value ) ;
3652+ } ) ;
3653+ expect ( values ) . toEqual ( [ 0 ] ) ;
3654+ expect ( watchFn ) . toHaveBeenCalledOnce ( ) ;
3655+ expect ( watcher . update ) . toHaveBeenCalledOnce ( ) ;
3656+ expect ( watcher . get ) . toHaveBeenCalledOnce ( ) ;
3657+ expect ( watcher . destroy ) . not . toHaveBeenCalled ( ) ;
3658+ clearMocks ( ) ;
3659+ isUpdated = false ;
3660+ batch ( ( ) => {
3661+ notify ! ( ) ;
3662+ } ) ;
3663+ expect ( watcher . update ) . toHaveBeenCalledOnce ( ) ;
3664+ expect ( watchFn ) . not . toHaveBeenCalled ( ) ;
3665+ expect ( watcher . get ) . not . toHaveBeenCalled ( ) ;
3666+ expect ( watcher . destroy ) . not . toHaveBeenCalled ( ) ;
3667+ clearMocks ( ) ;
3668+ expect ( values ) . toEqual ( [ 0 ] ) ;
3669+ isUpdated = true ;
3670+ value = 2 ;
3671+ batch ( ( ) => {
3672+ notify ! ( ) ;
3673+ } ) ;
3674+ expect ( watcher . update ) . toHaveBeenCalledOnce ( ) ;
3675+ expect ( watcher . get ) . toHaveBeenCalledOnce ( ) ;
3676+ expect ( watcher . destroy ) . not . toHaveBeenCalled ( ) ;
3677+ expect ( watchFn ) . not . toHaveBeenCalled ( ) ;
3678+ clearMocks ( ) ;
3679+ expect ( values ) . toEqual ( [ 0 , 2 ] ) ;
3680+ isUpdated = true ;
3681+ watcher . get . mockImplementation ( ( ) => {
3682+ throw new Error ( 'myerror' ) ;
3683+ } ) ;
3684+ expect ( ( ) => {
3685+ batch ( ( ) => {
3686+ notify ! ( ) ;
3687+ } ) ;
3688+ } ) . toThrowError ( 'myerror' ) ;
3689+ expect ( watcher . update ) . toHaveBeenCalledOnce ( ) ;
3690+ expect ( watcher . get ) . toHaveBeenCalledOnce ( ) ;
3691+ expect ( watcher . destroy ) . not . toHaveBeenCalled ( ) ;
3692+ expect ( watchFn ) . not . toHaveBeenCalled ( ) ;
3693+ clearMocks ( ) ;
3694+ expect ( ( ) => {
3695+ store . get ( ) ;
3696+ } ) . toThrowError ( 'myerror' ) ;
3697+ expect ( values ) . toEqual ( [ 0 , 2 ] ) ;
3698+ expect ( watcher . update ) . not . toHaveBeenCalled ( ) ;
3699+ expect ( watcher . get ) . not . toHaveBeenCalled ( ) ;
3700+ expect ( watcher . destroy ) . not . toHaveBeenCalled ( ) ;
3701+ expect ( watchFn ) . not . toHaveBeenCalled ( ) ;
3702+ unsubscribe ( ) ;
3703+ expect ( watcher . destroy ) . toHaveBeenCalledOnce ( ) ;
3704+ expect ( watcher . update ) . not . toHaveBeenCalled ( ) ;
3705+ expect ( watcher . get ) . not . toHaveBeenCalled ( ) ;
3706+ expect ( watchFn ) . not . toHaveBeenCalled ( ) ;
3707+ } ) ;
3708+ } ) ;
3709+
3710+ describe ( 'watch / fromWatch' , ( ) => {
3711+ it ( 'should work to convert back and forth a basic writable' , ( ) => {
3712+ const store = writable ( 0 ) ;
3713+ const otherStore = fromWatch ( ( notify ) => watch ( store , notify ) ) ;
3714+ expect ( otherStore ( ) ) . toBe ( 0 ) ;
3715+ store . set ( 1 ) ;
3716+ expect ( otherStore ( ) ) . toBe ( 1 ) ;
3717+ store . set ( 2 ) ;
3718+ expect ( otherStore ( ) ) . toBe ( 2 ) ;
3719+
3720+ const values : number [ ] = [ ] ;
3721+ const unsubscribe = otherStore . subscribe ( ( value ) => {
3722+ values . push ( value ) ;
3723+ } ) ;
3724+ expect ( values ) . toEqual ( [ 2 ] ) ;
3725+ store . set ( 3 ) ;
3726+ expect ( values ) . toEqual ( [ 2 , 3 ] ) ;
3727+ expect ( otherStore ( ) ) . toBe ( 3 ) ;
3728+ batch ( ( ) => {
3729+ store . set ( 4 ) ;
3730+ expect ( otherStore ( ) ) . toBe ( 4 ) ;
3731+ store . set ( 5 ) ;
3732+ store . set ( 3 ) ;
3733+ } ) ;
3734+ expect ( values ) . toEqual ( [ 2 , 3 ] ) ;
3735+ unsubscribe ( ) ;
3736+ } ) ;
3737+
3738+ it ( 'should work to convert back and forth a computed' , ( ) => {
3739+ const store = writable ( 0 ) ;
3740+ const doubleStore = computed ( ( ) => store ( ) * 2 ) ;
3741+ const otherStore = fromWatch ( ( notify ) => watch ( doubleStore , notify ) ) ;
3742+ expect ( otherStore ( ) ) . toBe ( 0 ) ;
3743+ store . set ( 1 ) ;
3744+ expect ( otherStore ( ) ) . toBe ( 2 ) ;
3745+ store . set ( 2 ) ;
3746+ expect ( otherStore ( ) ) . toBe ( 4 ) ;
3747+
3748+ const values : number [ ] = [ ] ;
3749+ const unsubscribe = otherStore . subscribe ( ( value ) => {
3750+ values . push ( value ) ;
3751+ } ) ;
3752+ expect ( values ) . toEqual ( [ 4 ] ) ;
3753+ store . set ( 3 ) ;
3754+ expect ( values ) . toEqual ( [ 4 , 6 ] ) ;
3755+ expect ( otherStore ( ) ) . toBe ( 6 ) ;
3756+ batch ( ( ) => {
3757+ store . set ( 4 ) ;
3758+ expect ( otherStore ( ) ) . toBe ( 8 ) ;
3759+ store . set ( 5 ) ;
3760+ store . set ( 3 ) ;
3761+ } ) ;
3762+ expect ( values ) . toEqual ( [ 4 , 6 ] ) ;
3763+ unsubscribe ( ) ;
3764+ } ) ;
3765+ } ) ;
35683766} ) ;
0 commit comments