@@ -3,14 +3,18 @@ import {
3
3
inject ,
4
4
InjectionToken ,
5
5
isSignal ,
6
+ linkedSignal ,
6
7
signal ,
7
8
} from '@angular/core' ;
8
9
import { TestBed } from '@angular/core/testing' ;
9
10
import {
11
+ getState ,
10
12
patchState ,
11
13
signalStore ,
14
+ signalStoreFeature ,
12
15
withComputed ,
13
16
withHooks ,
17
+ withLinkedState ,
14
18
withMethods ,
15
19
withProps ,
16
20
withState ,
@@ -500,7 +504,7 @@ describe('signalStore', () => {
500
504
} )
501
505
) ;
502
506
TestBed . inject ( Store ) ;
503
- TestBed . resetTestEnvironment ( ) ;
507
+ TestBed . resetTestingModule ( ) ;
504
508
505
509
expect ( messages ) . toEqual ( [ 'ending...' ] ) ;
506
510
} ) ;
@@ -565,4 +569,229 @@ describe('signalStore', () => {
565
569
expect ( secretStore [ SECRET ] ) . toBe ( 'not your business' ) ;
566
570
} ) ;
567
571
} ) ;
572
+
573
+ describe ( 'withLinkedState' , ( ) => {
574
+ describe ( 'updates automatically if the source changes' , ( ) =>
575
+ [
576
+ {
577
+ name : 'automatic' ,
578
+ linkedStateFeature : signalStoreFeature (
579
+ withState ( { userId : 1 } ) ,
580
+
581
+ withLinkedState ( ( { userId } ) => ( {
582
+ books : ( ) => {
583
+ userId ( ) ;
584
+
585
+ return [ ] as string [ ] ;
586
+ } ,
587
+ } ) )
588
+ ) ,
589
+ } ,
590
+ {
591
+ name : 'manual' ,
592
+ linkedStateFeature : signalStoreFeature (
593
+ withState ( { userId : 1 } ) ,
594
+ withLinkedState ( ( { userId } ) => ( {
595
+ books : linkedSignal ( {
596
+ source : userId ,
597
+ computation : ( ) => [ ] as string [ ] ,
598
+ } ) ,
599
+ } ) )
600
+ ) ,
601
+ } ,
602
+ ] . forEach ( ( { name, linkedStateFeature } ) => {
603
+ it ( name , ( ) => {
604
+ const BookStore = signalStore (
605
+ { providedIn : 'root' , protectedState : false } ,
606
+ linkedStateFeature ,
607
+ withState ( { version : 1 } ) ,
608
+ withMethods ( ( store ) => ( {
609
+ updateUser ( ) {
610
+ patchState ( store , ( value ) => value ) ;
611
+ patchState ( store , ( { userId } ) => ( { userId : userId + 1 } ) ) ;
612
+ } ,
613
+ addBook ( title : string ) {
614
+ patchState ( store , ( { books } ) => ( {
615
+ books : [ ...books , title ] ,
616
+ } ) ) ;
617
+ } ,
618
+ increaseVersion ( ) {
619
+ patchState ( store , ( { version } ) => ( { version : version + 1 } ) ) ;
620
+ } ,
621
+ } ) )
622
+ ) ;
623
+
624
+ const bookStore = TestBed . inject ( BookStore ) ;
625
+ bookStore . addBook ( 'The Neverending Story' ) ;
626
+ bookStore . increaseVersion ( ) ;
627
+ expect ( bookStore . books ( ) ) . toEqual ( [ 'The Neverending Story' ] ) ;
628
+ expect ( bookStore . version ( ) ) . toEqual ( 2 ) ;
629
+
630
+ patchState ( bookStore , { userId : 2 } ) ;
631
+ expect ( bookStore . books ( ) ) . toEqual ( [ ] ) ;
632
+ expect ( bookStore . version ( ) ) . toEqual ( 2 ) ;
633
+ } ) ;
634
+ } ) ) ;
635
+
636
+ describe ( 'updates also a spread linkedSignal' , ( ) => {
637
+ [
638
+ {
639
+ name : 'automatic' ,
640
+ linkedStateFeature : signalStoreFeature (
641
+ withState ( { userId : 1 } ) ,
642
+ withLinkedState ( ( { userId } ) => ( {
643
+ user : ( ) => {
644
+ userId ( ) ;
645
+ return { name : 'John Doe' } ;
646
+ } ,
647
+ location : ( ) => {
648
+ userId ( ) ;
649
+ return { city : 'Berlin' , country : 'Germany' } ;
650
+ } ,
651
+ } ) )
652
+ ) ,
653
+ } ,
654
+ {
655
+ name : 'manual' ,
656
+ linkedStateFeature : signalStoreFeature (
657
+ withState ( { userId : 1 } ) ,
658
+ withLinkedState ( ( { userId } ) => ( {
659
+ user : linkedSignal ( {
660
+ source : userId ,
661
+ computation : ( ) => ( { name : 'John Doe' } ) ,
662
+ } ) ,
663
+ location : linkedSignal ( {
664
+ source : userId ,
665
+ computation : ( ) => ( { city : 'Berlin' , country : 'Germany' } ) ,
666
+ } ) ,
667
+ } ) )
668
+ ) ,
669
+ } ,
670
+ ] . forEach ( ( { name, linkedStateFeature } ) => {
671
+ it ( name , ( ) => {
672
+ const UserStore = signalStore (
673
+ { providedIn : 'root' , protectedState : false } ,
674
+ linkedStateFeature ,
675
+ withMethods ( ( store ) => ( {
676
+ updateUser ( name : string ) {
677
+ patchState ( store , ( ) => ( {
678
+ user : { name } ,
679
+ } ) ) ;
680
+ } ,
681
+ updateLocation ( city : string , country : string ) {
682
+ patchState ( store , ( ) => ( {
683
+ location : { city, country } ,
684
+ } ) ) ;
685
+ } ,
686
+ } ) )
687
+ ) ;
688
+
689
+ const userStore = TestBed . inject ( UserStore ) ;
690
+
691
+ userStore . updateUser ( 'Jane Doe' ) ;
692
+ userStore . updateLocation ( 'London' , 'UK' ) ;
693
+
694
+ expect ( getState ( userStore ) ) . toEqual ( {
695
+ userId : 1 ,
696
+ user : { name : 'Jane Doe' } ,
697
+ location : { city : 'London' , country : 'UK' } ,
698
+ } ) ;
699
+
700
+ patchState ( userStore , { userId : 2 } ) ;
701
+ expect ( getState ( userStore ) ) . toEqual ( {
702
+ userId : 2 ,
703
+ user : { name : 'John Doe' } ,
704
+ location : { city : 'Berlin' , country : 'Germany' } ,
705
+ } ) ;
706
+ } ) ;
707
+ } ) ;
708
+ } ) ;
709
+
710
+ describe ( 'can depend on a Signal from another SignalStore' , ( ) => {
711
+ const UserStore = signalStore (
712
+ { providedIn : 'root' , protectedState : false } ,
713
+ withState ( { userId : 1 } )
714
+ ) ;
715
+
716
+ [
717
+ {
718
+ name : 'automatic' ,
719
+ linkedStateFeature : signalStoreFeature (
720
+ withLinkedState ( ( ) => ( {
721
+ books : ( ) => {
722
+ TestBed . inject ( UserStore ) . userId ( ) ;
723
+
724
+ return [ ] as string [ ] ;
725
+ } ,
726
+ } ) )
727
+ ) ,
728
+ } ,
729
+ {
730
+ name : 'manual' ,
731
+ linkedStateFeature : signalStoreFeature (
732
+ withLinkedState ( ( ) => {
733
+ const userStore = TestBed . inject ( UserStore ) ;
734
+
735
+ return {
736
+ books : linkedSignal ( {
737
+ source : userStore . userId ,
738
+ computation : ( ) => [ ] as string [ ] ,
739
+ } ) ,
740
+ } ;
741
+ } )
742
+ ) ,
743
+ } ,
744
+ ] . forEach ( ( { name, linkedStateFeature } ) => {
745
+ it ( name , ( ) => {
746
+ const BookStore = signalStore (
747
+ { providedIn : 'root' } ,
748
+ linkedStateFeature ,
749
+ withMethods ( ( store ) => ( {
750
+ addBook ( title : string ) {
751
+ patchState ( store , ( { books } ) => ( {
752
+ books : [ ...books , title ] ,
753
+ } ) ) ;
754
+ } ,
755
+ } ) )
756
+ ) ;
757
+
758
+ const userStore = TestBed . inject ( UserStore ) ;
759
+ const bookStore = TestBed . inject ( BookStore ) ;
760
+
761
+ bookStore . addBook ( 'The Neverending Story' ) ;
762
+ expect ( bookStore . books ( ) ) . toEqual ( [ 'The Neverending Story' ] ) ;
763
+
764
+ patchState ( userStore , { userId : 2 } ) ;
765
+
766
+ expect ( bookStore . books ( ) ) . toEqual ( [ ] ) ;
767
+ } ) ;
768
+ } ) ;
769
+ } ) ;
770
+
771
+ describe ( 'InnerSignalStore access' , ( ) => {
772
+ it ( 'can access the state signals' , ( ) => {
773
+ const UserStore = signalStore (
774
+ { providedIn : 'root' } ,
775
+ withState ( { userId : 1 } ) ,
776
+ withLinkedState ( ( { userId } ) => ( { value : userId } ) )
777
+ ) ;
778
+
779
+ const userStore = TestBed . inject ( UserStore ) ;
780
+
781
+ expect ( userStore . value ( ) ) . toBe ( 1 ) ;
782
+ } ) ;
783
+
784
+ it ( 'can access the props' , ( ) => {
785
+ const UserStore = signalStore (
786
+ { providedIn : 'root' } ,
787
+ withProps ( ( ) => ( { userId : 1 } ) ) ,
788
+ withLinkedState ( ( { userId } ) => ( { value : ( ) => userId } ) )
789
+ ) ;
790
+
791
+ const userStore = TestBed . inject ( UserStore ) ;
792
+
793
+ expect ( userStore . value ( ) ) . toBe ( 1 ) ;
794
+ } ) ;
795
+ } ) ;
796
+ } ) ;
568
797
} ) ;
0 commit comments