@@ -3,7 +3,7 @@ import { Component, Injectable, inject } from '@angular/core';
33import { TestBed } from '@angular/core/testing' ;
44import { BehaviorSubject , from } from 'rxjs' ;
55import { writable as svelteWritable } from 'svelte/store' ;
6- import { afterAll , afterEach , beforeAll , describe , expect , it , vi } from 'vitest' ;
6+ import { afterAll , afterEach , beforeAll , describe , expect , it , test , vi } from 'vitest' ;
77import type {
88 OnUseArgument ,
99 Readable ,
@@ -30,10 +30,11 @@ import {
3030 untrack ,
3131 writable ,
3232} from './index' ;
33- import { rawStoreSymbol } from './internal/exposeRawStores' ;
34- import { RawStoreFlags } from './internal/store' ;
3533import { flushUnused } from './internal/asyncFlush' ;
34+ import { RawStoreFlags , rawStoreSymbol } from './internal/store' ;
3635import type { RawStoreWritable } from './internal/storeWritable' ;
36+ import { setActiveConsumer , type Signal , type Watcher } from './interop' ;
37+ import * as mySample from './sample' ;
3738
3839const expectCorrectlyCleanedUp = < T > ( store : StoreInput < T > ) => {
3940 const rawStore = ( store as any ) [ rawStoreSymbol ] as RawStoreWritable < T > ;
@@ -3565,4 +3566,301 @@ describe('stores', () => {
35653566 expectCorrectlyCleanedUp ( doubleDoubleDoubleStore ) ;
35663567 } ) ;
35673568 } ) ;
3569+
3570+ describe ( 'watch' , ( ) => {
3571+ it ( 'should work' , ( ) => {
3572+ const onUseCalls : { onUnused : number } [ ] = [ ] ;
3573+ const store = writable ( 0 , {
3574+ onUse : ( ) => {
3575+ const call = { onUnused : 0 } ;
3576+ onUseCalls . push ( call ) ;
3577+ return ( ) => {
3578+ call . onUnused ++ ;
3579+ } ;
3580+ } ,
3581+ } ) ;
3582+ const notify = vi . fn ( ) ;
3583+ const watcher = store . watchSignal ( notify ) ;
3584+ watcher . start ( ) ;
3585+ expect ( watcher . isUpToDate ( ) ) . toBe ( false ) ;
3586+ expect ( onUseCalls . length ) . toBe ( 0 ) ;
3587+ expect ( watcher . update ( ) ) . toBe ( true ) ;
3588+ expect ( onUseCalls . length ) . toBe ( 1 ) ;
3589+ expect ( onUseCalls [ 0 ] . onUnused ) . toBe ( 0 ) ;
3590+ expect ( watcher . isUpToDate ( ) ) . toBe ( true ) ;
3591+ expect ( watcher . get ( ) ) . toBe ( 0 ) ;
3592+ expect ( notify ) . not . toHaveBeenCalled ( ) ;
3593+ store . set ( 1 ) ;
3594+ expect ( notify ) . toHaveBeenCalledOnce ( ) ;
3595+ notify . mockClear ( ) ;
3596+ expect ( watcher . isUpToDate ( ) ) . toBe ( false ) ;
3597+ store . set ( 2 ) ;
3598+ expect ( notify ) . not . toHaveBeenCalled ( ) ;
3599+ expect ( watcher . update ( ) ) . toBe ( true ) ;
3600+ expect ( watcher . isUpToDate ( ) ) . toBe ( true ) ;
3601+ expect ( watcher . get ( ) ) . toBe ( 2 ) ;
3602+ expect ( notify ) . not . toHaveBeenCalled ( ) ;
3603+ store . set ( 3 ) ;
3604+ expect ( notify ) . toHaveBeenCalledOnce ( ) ;
3605+ expect ( watcher . isUpToDate ( ) ) . toBe ( false ) ;
3606+ notify . mockClear ( ) ;
3607+ store . set ( 4 ) ;
3608+ store . set ( 2 ) ;
3609+ expect ( notify ) . not . toHaveBeenCalled ( ) ;
3610+ expect ( watcher . update ( ) ) . toBe ( false ) ;
3611+ expect ( watcher . isUpToDate ( ) ) . toBe ( true ) ;
3612+ expect ( watcher . update ( ) ) . toBe ( false ) ;
3613+ expect ( watcher . get ( ) ) . toBe ( 2 ) ;
3614+ watcher . stop ( ) ;
3615+ expect ( ( ) => watcher . get ( ) ) . toThrowError ( 'invalid watcher state' ) ;
3616+ } ) ;
3617+ } ) ;
3618+
3619+ describe ( 'fromWatch' , ( ) => {
3620+ it ( 'should work' , ( ) => {
3621+ let notify : ( ) => void ;
3622+ let isUpdated = true ;
3623+ let value = 0 ;
3624+ const watcher = {
3625+ isUpToDate : vi . fn ( ( ) => {
3626+ throw new Error ( 'unexpected call to isUpToDate' ) ;
3627+ } ) ,
3628+ isStarted : vi . fn ( ( ) => {
3629+ throw new Error ( 'unexpected call to isStarted' ) ;
3630+ } ) ,
3631+ update : vi . fn ( ( ) => isUpdated ) ,
3632+ get : vi . fn ( ( ) => value ) ,
3633+ start : vi . fn ( ) ,
3634+ stop : vi . fn ( ) ,
3635+ } satisfies Watcher < number > ;
3636+ const watchFn = vi . fn ( ( notifyFn : ( ) => void ) => {
3637+ notify = notifyFn ;
3638+ return watcher ;
3639+ } ) satisfies ( notify : ( ) => void ) => Watcher < number > ;
3640+ const clearMocks = ( ) => {
3641+ watchFn . mockClear ( ) ;
3642+ watcher . update . mockClear ( ) ;
3643+ watcher . get . mockClear ( ) ;
3644+ watcher . stop . mockClear ( ) ;
3645+ } ;
3646+ const store = asReadable ( { watchSignal : watchFn } ) ;
3647+ expect ( watchFn ) . toHaveBeenCalledOnce ( ) ;
3648+ expect ( store . get ( ) ) . toBe ( 0 ) ;
3649+ expect ( watchFn ) . toHaveBeenCalledOnce ( ) ;
3650+ expect ( watcher . update ) . toHaveBeenCalledOnce ( ) ;
3651+ expect ( watcher . get ) . toHaveBeenCalledOnce ( ) ;
3652+ expect ( watcher . stop ) . toHaveBeenCalledOnce ( ) ;
3653+ clearMocks ( ) ;
3654+ const values : number [ ] = [ ] ;
3655+ const unsubscribe = store . subscribe ( ( value ) => {
3656+ values . push ( value ) ;
3657+ } ) ;
3658+ expect ( values ) . toEqual ( [ 0 ] ) ;
3659+ expect ( watcher . update ) . toHaveBeenCalledOnce ( ) ;
3660+ expect ( watcher . get ) . toHaveBeenCalledOnce ( ) ;
3661+ expect ( watchFn ) . not . toHaveBeenCalled ( ) ;
3662+ expect ( watcher . stop ) . not . toHaveBeenCalled ( ) ;
3663+ clearMocks ( ) ;
3664+ isUpdated = false ;
3665+ batch ( ( ) => {
3666+ notify ! ( ) ;
3667+ } ) ;
3668+ expect ( watcher . update ) . toHaveBeenCalledOnce ( ) ;
3669+ expect ( watchFn ) . not . toHaveBeenCalled ( ) ;
3670+ expect ( watcher . get ) . not . toHaveBeenCalled ( ) ;
3671+ expect ( watcher . stop ) . not . toHaveBeenCalled ( ) ;
3672+ clearMocks ( ) ;
3673+ expect ( values ) . toEqual ( [ 0 ] ) ;
3674+ isUpdated = true ;
3675+ value = 2 ;
3676+ batch ( ( ) => {
3677+ notify ! ( ) ;
3678+ } ) ;
3679+ expect ( watcher . update ) . toHaveBeenCalledOnce ( ) ;
3680+ expect ( watcher . get ) . toHaveBeenCalledOnce ( ) ;
3681+ expect ( watcher . stop ) . not . toHaveBeenCalled ( ) ;
3682+ expect ( watchFn ) . not . toHaveBeenCalled ( ) ;
3683+ clearMocks ( ) ;
3684+ expect ( values ) . toEqual ( [ 0 , 2 ] ) ;
3685+ isUpdated = true ;
3686+ watcher . get . mockImplementation ( ( ) => {
3687+ throw new Error ( 'myerror' ) ;
3688+ } ) ;
3689+ expect ( ( ) => {
3690+ batch ( ( ) => {
3691+ notify ! ( ) ;
3692+ } ) ;
3693+ } ) . toThrowError ( 'myerror' ) ;
3694+ expect ( watcher . update ) . toHaveBeenCalledOnce ( ) ;
3695+ expect ( watcher . get ) . toHaveBeenCalledOnce ( ) ;
3696+ expect ( watcher . stop ) . not . toHaveBeenCalled ( ) ;
3697+ expect ( watchFn ) . not . toHaveBeenCalled ( ) ;
3698+ clearMocks ( ) ;
3699+ expect ( ( ) => {
3700+ store . get ( ) ;
3701+ } ) . toThrowError ( 'myerror' ) ;
3702+ expect ( values ) . toEqual ( [ 0 , 2 ] ) ;
3703+ expect ( watcher . update ) . not . toHaveBeenCalled ( ) ;
3704+ expect ( watcher . get ) . not . toHaveBeenCalled ( ) ;
3705+ expect ( watcher . stop ) . not . toHaveBeenCalled ( ) ;
3706+ expect ( watchFn ) . not . toHaveBeenCalled ( ) ;
3707+ unsubscribe ( ) ;
3708+ expect ( watcher . stop ) . toHaveBeenCalledOnce ( ) ;
3709+ expect ( watcher . update ) . not . toHaveBeenCalled ( ) ;
3710+ expect ( watcher . get ) . not . toHaveBeenCalled ( ) ;
3711+ expect ( watchFn ) . not . toHaveBeenCalled ( ) ;
3712+ expect ( watcher . isUpToDate ) . not . toHaveBeenCalled ( ) ;
3713+ } ) ;
3714+ } ) ;
3715+
3716+ describe ( 'watch / fromWatch' , ( ) => {
3717+ it ( 'should work to convert back and forth a basic writable' , ( ) => {
3718+ const store = writable ( 0 ) ;
3719+ const otherStore = asReadable ( { watchSignal : ( notify ) => store . watchSignal ( notify ) } ) ;
3720+ expect ( otherStore ( ) ) . toBe ( 0 ) ;
3721+ store . set ( 1 ) ;
3722+ expect ( otherStore ( ) ) . toBe ( 1 ) ;
3723+ store . set ( 2 ) ;
3724+ expect ( otherStore ( ) ) . toBe ( 2 ) ;
3725+
3726+ const values : number [ ] = [ ] ;
3727+ const unsubscribe = otherStore . subscribe ( ( value ) => {
3728+ values . push ( value ) ;
3729+ } ) ;
3730+ expect ( values ) . toEqual ( [ 2 ] ) ;
3731+ store . set ( 3 ) ;
3732+ expect ( values ) . toEqual ( [ 2 , 3 ] ) ;
3733+ expect ( otherStore ( ) ) . toBe ( 3 ) ;
3734+ batch ( ( ) => {
3735+ store . set ( 4 ) ;
3736+ expect ( otherStore ( ) ) . toBe ( 4 ) ;
3737+ store . set ( 5 ) ;
3738+ store . set ( 3 ) ;
3739+ } ) ;
3740+ expect ( values ) . toEqual ( [ 2 , 3 ] ) ;
3741+ unsubscribe ( ) ;
3742+ } ) ;
3743+
3744+ it ( 'should work to convert back and forth a basic Store' , ( ) => {
3745+ class MyStore extends Store < number > {
3746+ increase ( ) {
3747+ this . update ( ( value ) => value + 1 ) ;
3748+ }
3749+ }
3750+ const store = new MyStore ( 0 ) ;
3751+
3752+ const otherStore = asReadable ( { watchSignal : ( notify ) => store . watchSignal ( notify ) } ) ;
3753+ expect ( otherStore ( ) ) . toBe ( 0 ) ;
3754+ store . increase ( ) ;
3755+ expect ( otherStore ( ) ) . toBe ( 1 ) ;
3756+ store . increase ( ) ;
3757+ expect ( otherStore ( ) ) . toBe ( 2 ) ;
3758+
3759+ const values : number [ ] = [ ] ;
3760+ const unsubscribe = otherStore . subscribe ( ( value ) => {
3761+ values . push ( value ) ;
3762+ } ) ;
3763+ expect ( values ) . toEqual ( [ 2 ] ) ;
3764+ store . increase ( ) ;
3765+ expect ( values ) . toEqual ( [ 2 , 3 ] ) ;
3766+ expect ( otherStore ( ) ) . toBe ( 3 ) ;
3767+ batch ( ( ) => {
3768+ store . increase ( ) ;
3769+ expect ( otherStore ( ) ) . toBe ( 4 ) ;
3770+ store . increase ( ) ;
3771+ store . increase ( ) ;
3772+ } ) ;
3773+ expect ( values ) . toEqual ( [ 2 , 3 , 6 ] ) ;
3774+ unsubscribe ( ) ;
3775+ } ) ;
3776+
3777+ it ( 'should work to convert back and forth a computed' , ( ) => {
3778+ const store = writable ( 0 ) ;
3779+ const doubleStore = computed ( ( ) => store ( ) * 2 ) ;
3780+ const otherStore = asReadable ( {
3781+ watchSignal : ( notify ) => doubleStore . watchSignal ( notify ) ,
3782+ } ) ;
3783+ expect ( otherStore ( ) ) . toBe ( 0 ) ;
3784+ store . set ( 1 ) ;
3785+ expect ( otherStore ( ) ) . toBe ( 2 ) ;
3786+ store . set ( 2 ) ;
3787+ expect ( otherStore ( ) ) . toBe ( 4 ) ;
3788+
3789+ const values : number [ ] = [ ] ;
3790+ const unsubscribe = otherStore . subscribe ( ( value ) => {
3791+ values . push ( value ) ;
3792+ } ) ;
3793+ expect ( values ) . toEqual ( [ 4 ] ) ;
3794+ store . set ( 3 ) ;
3795+ expect ( values ) . toEqual ( [ 4 , 6 ] ) ;
3796+ expect ( otherStore ( ) ) . toBe ( 6 ) ;
3797+ batch ( ( ) => {
3798+ store . set ( 4 ) ;
3799+ expect ( otherStore ( ) ) . toBe ( 8 ) ;
3800+ store . set ( 5 ) ;
3801+ store . set ( 3 ) ;
3802+ } ) ;
3803+ expect ( values ) . toEqual ( [ 4 , 6 ] ) ;
3804+ unsubscribe ( ) ;
3805+ } ) ;
3806+ } ) ;
3807+
3808+ describe ( 'interop computed' , ( ) => {
3809+ test ( 'should work with a computed from another library' , ( ) => {
3810+ const a = writable ( 0 ) ;
3811+ const notify = vi . fn ( ) ;
3812+ const watchers : Watcher < any > [ ] = [ ] ;
3813+ const consumer = {
3814+ addProducer : vi . fn ( < T > ( signal : Signal < T > ) => {
3815+ const watcher = signal . watchSignal ( notify ) ;
3816+ watchers . push ( watcher ) ;
3817+ watcher . start ( ) ;
3818+ watcher . update ( ) ;
3819+ } ) ,
3820+ } ;
3821+ const computeValue = ( ) => {
3822+ const prevConsumer = setActiveConsumer ( consumer ) ;
3823+ try {
3824+ return 2 * a ( ) ;
3825+ } finally {
3826+ setActiveConsumer ( prevConsumer ) ;
3827+ }
3828+ } ;
3829+ expect ( computeValue ( ) ) . toBe ( 0 ) ;
3830+ expect ( consumer . addProducer ) . toHaveBeenCalledOnce ( ) ;
3831+ expect ( notify ) . not . toHaveBeenCalled ( ) ;
3832+ a . set ( 1 ) ;
3833+ expect ( notify ) . toHaveBeenCalledOnce ( ) ;
3834+ expect ( watchers ) . toHaveLength ( 1 ) ;
3835+ } ) ;
3836+
3837+ test ( 'should work with a store from another library' , ( ) => {
3838+ const a = mySample . signal ( 0 ) ;
3839+ const myComputed = computed ( ( ) => a . get ( ) ) ;
3840+ console . log ( 'before myComputed()' ) ;
3841+ expect ( myComputed ( ) ) . toBe ( 0 ) ;
3842+ console . log ( 'before a.set(1)' ) ;
3843+ a . set ( 1 ) ;
3844+ console . log ( 'before myComputed()' ) ;
3845+ expect ( myComputed ( ) ) . toBe ( 1 ) ;
3846+ } ) ;
3847+
3848+ test ( 'should work with a store from another library (live)' , ( ) => {
3849+ const a = mySample . signal ( 0 ) ;
3850+ const myComputed = computed ( ( ) => a . get ( ) ) ;
3851+ const values : number [ ] = [ ] ;
3852+ console . log ( 'before subscribe()' ) ;
3853+ myComputed . subscribe ( ( value ) => {
3854+ values . push ( value ) ;
3855+ } ) ;
3856+ expect ( values ) . toEqual ( [ 0 ] ) ;
3857+ console . log ( 'before myComputed()' ) ;
3858+ expect ( myComputed ( ) ) . toBe ( 0 ) ;
3859+ console . log ( 'before a.set(1)' ) ;
3860+ a . set ( 1 ) ;
3861+ expect ( values ) . toEqual ( [ 0 , 1 ] ) ;
3862+ console . log ( 'before myComputed()' ) ;
3863+ expect ( myComputed ( ) ) . toBe ( 1 ) ;
3864+ } ) ;
3865+ } ) ;
35683866} ) ;
0 commit comments