@@ -26,13 +26,15 @@ class MockProvider implements Provider {
26
26
private asyncDelay ?: number ;
27
27
private enableEvents : boolean ;
28
28
status ?: ProviderStatus = undefined ;
29
+ onContextChange ?: ( ) => Promise < void > ;
29
30
30
31
constructor ( options ?: {
31
32
hasInitialize ?: boolean ;
32
33
initialStatus ?: ProviderStatus ;
33
34
asyncDelay ?: number ;
34
35
enableEvents ?: boolean ;
35
36
failOnInit ?: boolean ;
37
+ noContextChanged ?: boolean ;
36
38
failOnContextChange ?: boolean ;
37
39
name ?: string ;
38
40
} ) {
@@ -43,6 +45,9 @@ class MockProvider implements Provider {
43
45
this . enableEvents = options ?. enableEvents ?? true ;
44
46
this . failOnInit = options ?. failOnInit ?? false ;
45
47
this . failOnContextChange = options ?. failOnContextChange ?? false ;
48
+ if ( ! options ?. noContextChanged ) {
49
+ this . onContextChange = this . changeHandler ;
50
+ }
46
51
47
52
if ( this . enableEvents ) {
48
53
this . events = new OpenFeatureEventEmitter ( ) ;
@@ -62,16 +67,6 @@ class MockProvider implements Provider {
62
67
63
68
initialize : jest . Mock < Promise < void > , [ ] > | undefined ;
64
69
65
- async onContextChange ( ) : Promise < void > {
66
- return new Promise ( ( resolve , reject ) => setTimeout ( ( ) => {
67
- if ( this . failOnContextChange ) {
68
- reject ( new Error ( ERR_MESSAGE ) ) ;
69
- } else {
70
- resolve ( ) ;
71
- }
72
- } , this . asyncDelay ) ) ;
73
- }
74
-
75
70
resolveBooleanEvaluation ( ) : ResolutionDetails < boolean > {
76
71
throw new Error ( 'Not implemented' ) ;
77
72
}
@@ -87,6 +82,18 @@ class MockProvider implements Provider {
87
82
resolveStringEvaluation ( ) : ResolutionDetails < string > {
88
83
throw new Error ( 'Not implemented' ) ;
89
84
}
85
+
86
+ private changeHandler ( ) {
87
+ return new Promise < void > ( ( resolve , reject ) =>
88
+ setTimeout ( ( ) => {
89
+ if ( this . failOnContextChange ) {
90
+ reject ( new Error ( ERR_MESSAGE ) ) ;
91
+ } else {
92
+ resolve ( ) ;
93
+ }
94
+ } , this . asyncDelay ) ,
95
+ ) ;
96
+ }
90
97
}
91
98
92
99
describe ( 'Events' , ( ) => {
@@ -96,6 +103,7 @@ describe('Events', () => {
96
103
97
104
afterEach ( async ( ) => {
98
105
await OpenFeature . clearProviders ( ) ;
106
+ OpenFeature . clearHandlers ( ) ;
99
107
jest . clearAllMocks ( ) ;
100
108
clientId = uuid ( ) ;
101
109
// hacky, but it's helpful to clear the handlers between tests
@@ -610,7 +618,6 @@ describe('Events', () => {
610
618
expect ( details ?. clientName ) . toEqual ( clientId ) ;
611
619
expect ( details ?. providerName ) . toEqual ( provider . metadata . name ) ;
612
620
done ( ) ;
613
- OpenFeature . removeHandler ( ProviderEvents . ContextChanged , handler ) ;
614
621
} catch ( e ) {
615
622
done ( e ) ;
616
623
}
@@ -630,7 +637,6 @@ describe('Events', () => {
630
637
expect ( details ?. clientName ) . toEqual ( clientId ) ;
631
638
expect ( details ?. providerName ) . toEqual ( provider . metadata . name ) ;
632
639
done ( ) ;
633
- OpenFeature . removeHandler ( ProviderEvents . Error , handler ) ;
634
640
} catch ( e ) {
635
641
done ( e ) ;
636
642
}
@@ -643,15 +649,25 @@ describe('Events', () => {
643
649
describe ( 'context set for different client' , ( ) => {
644
650
it ( "If the provider's `on context changed` function terminates normally, associated `PROVIDER_CONTEXT_CHANGED` handlers MUST run." , ( done ) => {
645
651
const provider = new MockProvider ( { initialStatus : ProviderStatus . READY } ) ;
652
+ let runCount = 0 ;
646
653
647
654
OpenFeature . setProvider ( clientId , provider ) ;
648
655
656
+ // expect 2 runs, since 2 providers are impacted by this context change (global)
649
657
const handler = ( details ?: EventDetails ) => {
650
658
try {
651
- expect ( details ?. clientName ) . toBeUndefined ( ) ;
652
- expect ( details ?. providerName ) . toEqual ( provider . metadata . name ) ;
653
- OpenFeature . removeHandler ( ProviderEvents . ContextChanged , handler ) ;
654
- done ( ) ;
659
+ runCount ++ ;
660
+ // one run should be global
661
+ if ( details ?. clientName === undefined ) {
662
+ expect ( details ?. providerName ) . toEqual ( OpenFeature . getProviderMetadata ( ) . name ) ;
663
+ } else if ( details ?. clientName === clientId ) {
664
+ // one run should be for client
665
+ expect ( details ?. clientName ) . toEqual ( clientId ) ;
666
+ expect ( details ?. providerName ) . toEqual ( provider . metadata . name ) ;
667
+ }
668
+ if ( runCount == 2 ) {
669
+ done ( ) ;
670
+ }
655
671
} catch ( e ) {
656
672
done ( e ) ;
657
673
}
@@ -669,10 +685,10 @@ describe('Events', () => {
669
685
670
686
const handler = ( details ?: EventDetails ) => {
671
687
try {
672
- expect ( details ?. clientName ) . toBeUndefined ( ) ;
688
+ // expect only one error run, because only one provider throws
689
+ expect ( details ?. clientName ) . toEqual ( clientId ) ;
673
690
expect ( details ?. providerName ) . toEqual ( provider . metadata . name ) ;
674
- expect ( details ?. message ) . toEqual ( ERR_MESSAGE ) ;
675
- OpenFeature . removeHandler ( ProviderEvents . Error , handler ) ;
691
+ expect ( details ?. message ) . toBeTruthy ( ) ;
676
692
done ( ) ;
677
693
} catch ( e ) {
678
694
done ( e ) ;
@@ -696,7 +712,6 @@ describe('Events', () => {
696
712
try {
697
713
expect ( details ?. clientName ) . toEqual ( clientId ) ;
698
714
expect ( details ?. providerName ) . toEqual ( provider . metadata . name ) ;
699
- OpenFeature . removeHandler ( ProviderEvents . ContextChanged , handler ) ;
700
715
done ( ) ;
701
716
} catch ( e ) {
702
717
done ( e ) ;
@@ -717,8 +732,7 @@ describe('Events', () => {
717
732
try {
718
733
expect ( details ?. clientName ) . toEqual ( clientId ) ;
719
734
expect ( details ?. providerName ) . toEqual ( provider . metadata . name ) ;
720
- expect ( details ?. message ) . toEqual ( ERR_MESSAGE ) ;
721
- OpenFeature . removeHandler ( ProviderEvents . Error , handler ) ;
735
+ expect ( details ?. message ) . toBeTruthy ( ) ;
722
736
done ( ) ;
723
737
} catch ( e ) {
724
738
done ( e ) ;
@@ -730,5 +744,29 @@ describe('Events', () => {
730
744
731
745
} ) ;
732
746
} ) ;
747
+
748
+ describe ( 'provider' , ( ) => {
749
+ describe ( 'has no onContextChange handler' , ( ) => {
750
+ it ( 'runs API ContextChanged event handler' , ( done ) => {
751
+ const noChangeHandlerProvider = 'noChangeHandlerProvider' ;
752
+ const provider = new MockProvider ( { initialStatus : ProviderStatus . READY , noContextChanged : true } ) ;
753
+
754
+ OpenFeature . setProvider ( noChangeHandlerProvider , provider ) ;
755
+ OpenFeature . setContext ( noChangeHandlerProvider , { } ) ;
756
+
757
+ const handler = ( details ?: EventDetails ) => {
758
+ try {
759
+ expect ( details ?. clientName ) . toEqual ( noChangeHandlerProvider ) ;
760
+ expect ( details ?. providerName ) . toEqual ( provider . metadata . name ) ;
761
+ done ( ) ;
762
+ } catch ( e ) {
763
+ done ( e ) ;
764
+ }
765
+ } ;
766
+
767
+ OpenFeature . addHandler ( ProviderEvents . ContextChanged , handler ) ;
768
+ } ) ;
769
+ } ) ;
770
+ } ) ;
733
771
} ) ;
734
772
} ) ;
0 commit comments