1
+ import { isAllowedValue } from "configcat-common/lib/RolloutEvaluator" ;
1
2
import { ClientCacheState , HookEvents , IConfig , IConfigCatClient , IConfigCatClientSnapshot , IEvaluationDetails , RefreshResult , SettingKeyValue , SettingTypeOf , SettingValue , User } from "../../src" ;
3
+ import { DefaultEventEmitter } from "configcat-common/lib/DefaultEventEmitter" ;
4
+ import { IEventEmitter } from "configcat-common" ;
5
+ import * as ConfigJson from "configcat-common/lib/ConfigJson" ;
6
+ import { Config } from "configcat-common/lib/ProjectConfig" ;
2
7
3
8
export class ConfigCatClientMockBase implements IConfigCatClient {
4
9
constructor (
@@ -91,4 +96,103 @@ export class ConfigCatClientSnapshotMockBase implements IConfigCatClientSnapshot
91
96
getValueDetails < T extends SettingValue > ( key : string , defaultValue : T , user ?: User | undefined ) : IEvaluationDetails < SettingTypeOf < T > > {
92
97
throw new Error ( "Method not implemented." ) ;
93
98
}
99
+ }
100
+
101
+ export class SimpleValueConfigCatClientMock extends ConfigCatClientMockBase {
102
+ private readonly eventEmitter = new DefaultEventEmitter ( ) ;
103
+ private readonly readyPromise : Promise < ClientCacheState > ;
104
+ private signalReady : ( value : NonNullable < SettingValue > ) => void ;
105
+ private cacheState = ClientCacheState . NoFlagData ;
106
+ private currentValue : NonNullable < SettingValue > ;
107
+ private currentConfig : Config | null = null ;
108
+
109
+ constructor ( private key : string ) {
110
+ super ( ) ;
111
+
112
+ this . readyPromise = new Promise < NonNullable < SettingValue > > ( resolve => this . signalReady = resolve )
113
+ . then ( initialValue => {
114
+ this . currentValue = initialValue ;
115
+ this . currentConfig = createConfigFromValue ( this . key , initialValue ) ;
116
+ this . cacheState = ClientCacheState . HasUpToDateFlagData ;
117
+ ( this . eventEmitter as IEventEmitter < HookEvents > ) . emit ( "clientReady" , this . cacheState ) ;
118
+ return this . cacheState ;
119
+ } ) ;
120
+ }
121
+
122
+ /** Call this method to simulate the `clientReady` event and to provide an initial feature flag value. */
123
+ async setReady ( initialValue : NonNullable < SettingValue > ) : Promise < void > {
124
+ this . signalReady ( initialValue ) ;
125
+
126
+ // Allow async continuations to run.
127
+ await new Promise < void > ( resolve => setTimeout ( ( ) => resolve ( ) , 0 ) ) ;
128
+ }
129
+
130
+ /** Call this method to simulate the `configChanged` event and to update the current feature flag value. */
131
+ changeValue ( newValue : NonNullable < SettingValue > ) : void {
132
+ if ( this . currentValue === newValue ) {
133
+ return ;
134
+ }
135
+
136
+ this . currentValue = newValue ;
137
+ this . currentConfig = createConfigFromValue ( this . key , newValue ) ;
138
+ ( this . eventEmitter as IEventEmitter < HookEvents > ) . emit ( "configChanged" , this . currentConfig ) ;
139
+ }
140
+
141
+ async getValueAsync < T extends SettingValue > ( key : string , defaultValue : T , user ?: User | undefined ) : Promise < SettingTypeOf < T > > {
142
+ await this . readyPromise ;
143
+
144
+ if ( key === this . key ) {
145
+ return ( isValidSettingValue ( this . currentValue , defaultValue ) ? this . currentValue : defaultValue ) as SettingTypeOf < T > ;
146
+ }
147
+
148
+ return super . getValueAsync ( key , defaultValue , user ) ;
149
+ }
150
+
151
+ snapshot ( ) {
152
+ const client = this ;
153
+
154
+ return new ( class extends ConfigCatClientSnapshotMockBase {
155
+ getValue < T extends SettingValue > ( key : string , defaultValue : T , user ?: User | undefined ) : SettingTypeOf < T > {
156
+ if ( key === client . key ) {
157
+ return ( isValidSettingValue ( client . currentValue , defaultValue ) ? client . currentValue : defaultValue ) as SettingTypeOf < T > ;
158
+ }
159
+
160
+ return super . getValue ( key , defaultValue , user ) ;
161
+ }
162
+ } ) ( this . cacheState , this . currentConfig ) ;
163
+ }
164
+
165
+ waitForReady ( ) : Promise < ClientCacheState > {
166
+ return this . readyPromise ;
167
+ }
168
+
169
+ on < TEventName extends keyof HookEvents > ( eventName : TEventName , listener : ( ...args : HookEvents [ TEventName ] ) => void ) : this {
170
+ this . eventEmitter . on ( eventName , listener as ( ...args : any [ ] ) => void ) ;
171
+ return this ;
172
+ }
173
+
174
+ off < TEventName extends keyof HookEvents > ( eventName : TEventName , listener : ( ...args : HookEvents [ TEventName ] ) => void ) : this {
175
+ this . eventEmitter . off ( eventName , listener as ( ...args : any [ ] ) => void ) ;
176
+ return this ;
177
+ }
178
+ } ;
179
+
180
+ function createConfigFromValue ( key : string , value : NonNullable < SettingValue > ) : Config {
181
+ const [ settingType , settingValue ] : [ ConfigJson . SettingType , ConfigJson . SettingValue ] =
182
+ typeof value === "boolean" ? [ ConfigJson . SettingType . Boolean , { b : value } ] :
183
+ typeof value === "string" ? [ ConfigJson . SettingType . String , { s : value } ] :
184
+ Number . isInteger ( this . changeValue ) ? [ ConfigJson . SettingType . Int , { i : value } ] :
185
+ [ ConfigJson . SettingType . Double , { d : value } ] ;
186
+
187
+ const configJson : Omit < ConfigJson . Config , "p" > = {
188
+ f : { [ key ] : { t : settingType , v : settingValue } as ConfigJson . SettingUnion }
189
+ } ;
190
+
191
+ return new Config ( configJson ) ;
192
+ }
193
+
194
+ function isValidSettingValue ( settingValue : NonNullable < SettingValue > , defaultValue : SettingValue ) {
195
+ return defaultValue == null
196
+ ? isAllowedValue ( settingValue )
197
+ : typeof settingValue === typeof defaultValue ;
94
198
}
0 commit comments