Skip to content

Commit bd205a8

Browse files
committed
Update ConfigCatClient mocks
1 parent ff56e13 commit bd205a8

File tree

1 file changed

+187
-173
lines changed

1 file changed

+187
-173
lines changed
Lines changed: 187 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -1,197 +1,211 @@
1-
import { Config, ConfigJson, ClientCacheState, EvaluationDetails, HookEvents, IConfigCatClient, IConfigCatClientSnapshot, Internals, prepareConfig, RefreshResult, SettingKeyValue, SettingTypeOf, SettingValue, User } from "@configcat/sdk";
1+
import { ClientCacheState, Config, ConfigJson, EvaluationDetails, HookEvents, IConfigCatClient, IConfigCatClientSnapshot, Internals, prepareConfig, RefreshErrorCode, RefreshResult, SettingKeyValue, SettingTypeOf, SettingValue, User } from "@configcat/sdk";
22
import { isAllowedValue } from "@configcat/sdk/lib/esm/ProjectConfig.js";
33

44
export class ConfigCatClientMockBase implements IConfigCatClient {
5-
constructor(
6-
public isOffline = false) {
7-
}
8-
9-
getValueAsync<T extends SettingValue>(key: string, defaultValue: T, user?: User | undefined): Promise<SettingTypeOf<T>> {
10-
throw new Error("Method not implemented.");
11-
}
12-
getValueDetailsAsync<T extends SettingValue>(key: string, defaultValue: T, user?: User | undefined): Promise<EvaluationDetails<SettingTypeOf<T>>> {
13-
throw new Error("Method not implemented.");
14-
}
15-
getAllKeysAsync(): Promise<string[]> {
16-
throw new Error("Method not implemented.");
17-
}
18-
getAllValuesAsync(user?: User | undefined): Promise<SettingKeyValue<SettingValue>[]> {
19-
throw new Error("Method not implemented.");
20-
}
21-
getAllValueDetailsAsync(user?: User | undefined): Promise<EvaluationDetails<SettingValue>[]> {
22-
throw new Error("Method not implemented.");
23-
}
24-
getKeyAndValueAsync(variationId: string): Promise<SettingKeyValue<SettingValue> | null> {
25-
throw new Error("Method not implemented.");
26-
}
27-
forceRefreshAsync(): Promise<RefreshResult> {
28-
throw new Error("Method not implemented.");
29-
}
30-
waitForReady(): Promise<ClientCacheState> {
31-
throw new Error("Method not implemented.");
32-
}
33-
snapshot(): IConfigCatClientSnapshot {
34-
throw new Error("Method not implemented.");
35-
}
36-
setDefaultUser(defaultUser: User): void {
37-
throw new Error("Method not implemented.");
38-
}
39-
clearDefaultUser(): void {
40-
throw new Error("Method not implemented.");
41-
}
42-
setOnline(): void {
43-
throw new Error("Method not implemented.");
44-
}
45-
setOffline(): void {
46-
throw new Error("Method not implemented.");
47-
}
48-
dispose(): void {
49-
throw new Error("Method not implemented.");
50-
}
51-
addListener<TEventName extends keyof HookEvents>(eventName: TEventName, listener: (...args: HookEvents[TEventName]) => void): this {
52-
throw new Error("Method not implemented.");
53-
}
54-
on<TEventName extends keyof HookEvents>(eventName: TEventName, listener: (...args: HookEvents[TEventName]) => void): this {
55-
throw new Error("Method not implemented.");
56-
}
57-
once<TEventName extends keyof HookEvents>(eventName: TEventName, listener: (...args: HookEvents[TEventName]) => void): this {
58-
throw new Error("Method not implemented.");
59-
}
60-
removeListener<TEventName extends keyof HookEvents>(eventName: TEventName, listener: (...args: HookEvents[TEventName]) => void): this {
61-
throw new Error("Method not implemented.");
62-
}
63-
off<TEventName extends keyof HookEvents>(eventName: TEventName, listener: (...args: HookEvents[TEventName]) => void): this {
64-
throw new Error("Method not implemented.");
65-
}
66-
removeAllListeners(eventName?: keyof HookEvents | undefined): this {
67-
throw new Error("Method not implemented.");
68-
}
69-
listeners(eventName: keyof HookEvents): Function[] {
70-
throw new Error("Method not implemented.");
71-
}
72-
listenerCount(eventName: keyof HookEvents): number {
73-
throw new Error("Method not implemented.");
74-
}
75-
eventNames(): (keyof HookEvents)[] {
76-
throw new Error("Method not implemented.");
77-
}
5+
constructor(
6+
public isOffline = false) {
7+
}
8+
9+
getValueAsync<T extends SettingValue>(key: string, defaultValue: T, user?: User): Promise<SettingTypeOf<T>> {
10+
throw new Error("Method not implemented.");
11+
}
12+
getValueDetailsAsync<T extends SettingValue>(key: string, defaultValue: T, user?: User): Promise<EvaluationDetails<SettingTypeOf<T>>> {
13+
throw new Error("Method not implemented.");
14+
}
15+
getAllKeysAsync(): Promise<string[]> {
16+
throw new Error("Method not implemented.");
17+
}
18+
getAllValuesAsync(user?: User): Promise<SettingKeyValue[]> {
19+
throw new Error("Method not implemented.");
20+
}
21+
getAllValueDetailsAsync(user?: User): Promise<EvaluationDetails[]> {
22+
throw new Error("Method not implemented.");
23+
}
24+
getKeyAndValueAsync(variationId: string): Promise<SettingKeyValue | null> {
25+
throw new Error("Method not implemented.");
26+
}
27+
forceRefreshAsync(): Promise<RefreshResult> {
28+
throw new Error("Method not implemented.");
29+
}
30+
waitForReady(): Promise<ClientCacheState> {
31+
throw new Error("Method not implemented.");
32+
}
33+
snapshot(): IConfigCatClientSnapshot {
34+
throw new Error("Method not implemented.");
35+
}
36+
setDefaultUser(defaultUser: User): void {
37+
throw new Error("Method not implemented.");
38+
}
39+
clearDefaultUser(): void {
40+
throw new Error("Method not implemented.");
41+
}
42+
setOnline(): void {
43+
throw new Error("Method not implemented.");
44+
}
45+
setOffline(): void {
46+
throw new Error("Method not implemented.");
47+
}
48+
dispose(): void {
49+
throw new Error("Method not implemented.");
50+
}
51+
addListener<TEventName extends keyof HookEvents>(eventName: TEventName, listener: (...args: HookEvents[TEventName]) => void): this {
52+
throw new Error("Method not implemented.");
53+
}
54+
on<TEventName extends keyof HookEvents>(eventName: TEventName, listener: (...args: HookEvents[TEventName]) => void): this {
55+
throw new Error("Method not implemented.");
56+
}
57+
once<TEventName extends keyof HookEvents>(eventName: TEventName, listener: (...args: HookEvents[TEventName]) => void): this {
58+
throw new Error("Method not implemented.");
59+
}
60+
removeListener<TEventName extends keyof HookEvents>(eventName: TEventName, listener: (...args: HookEvents[TEventName]) => void): this {
61+
throw new Error("Method not implemented.");
62+
}
63+
off<TEventName extends keyof HookEvents>(eventName: TEventName, listener: (...args: HookEvents[TEventName]) => void): this {
64+
throw new Error("Method not implemented.");
65+
}
66+
removeAllListeners(eventName?: keyof HookEvents): this {
67+
throw new Error("Method not implemented.");
68+
}
69+
listeners(eventName: keyof HookEvents): Function[] {
70+
throw new Error("Method not implemented.");
71+
}
72+
listenerCount(eventName: keyof HookEvents): number {
73+
throw new Error("Method not implemented.");
74+
}
75+
eventNames(): (keyof HookEvents)[] {
76+
throw new Error("Method not implemented.");
77+
}
7878
}
7979

8080
export class ConfigCatClientSnapshotMockBase implements IConfigCatClientSnapshot {
81-
constructor(
82-
public cacheState = ClientCacheState.NoFlagData,
83-
public fetchedConfig: Config | null = null) {
84-
}
85-
86-
getAllKeys(): readonly string[] {
87-
throw new Error("Method not implemented.");
88-
}
89-
getValue<T extends SettingValue>(key: string, defaultValue: T, user?: User | undefined): SettingTypeOf<T> {
90-
throw new Error("Method not implemented.");
91-
}
92-
getValueDetails<T extends SettingValue>(key: string, defaultValue: T, user?: User | undefined): EvaluationDetails<SettingTypeOf<T>> {
93-
throw new Error("Method not implemented.");
94-
}
95-
getKeyAndValue(variationId: string): SettingKeyValue | null {
96-
throw new Error("Method not implemented.");
97-
}
81+
constructor(
82+
public cacheState = ClientCacheState.NoFlagData,
83+
public fetchedConfig: Config | null = null) {
84+
}
85+
86+
getAllKeys(): readonly string[] {
87+
throw new Error("Method not implemented.");
88+
}
89+
getValue<T extends SettingValue>(key: string, defaultValue: T, user?: User): SettingTypeOf<T> {
90+
throw new Error("Method not implemented.");
91+
}
92+
getValueDetails<T extends SettingValue>(key: string, defaultValue: T, user?: User): EvaluationDetails<SettingTypeOf<T>> {
93+
throw new Error("Method not implemented.");
94+
}
95+
getKeyAndValue(variationId: string): SettingKeyValue | null {
96+
throw new Error("Method not implemented.");
97+
}
9898
}
9999

100100
export class SimpleValueConfigCatClientMock extends ConfigCatClientMockBase {
101-
private readonly eventEmitter = new Internals.DefaultEventEmitter();
102-
private readonly readyPromise: Promise<ClientCacheState>;
103-
private signalReady: (value: NonNullable<SettingValue>) => void;
104-
private cacheState = ClientCacheState.NoFlagData;
105-
private currentValue: NonNullable<SettingValue>;
106-
private currentConfig: Config | null = null;
107-
108-
constructor(private key: string) {
109-
super();
110-
111-
this.readyPromise = new Promise<NonNullable<SettingValue>>(resolve => this.signalReady = resolve)
112-
.then(initialValue => {
113-
this.currentValue = initialValue;
114-
this.currentConfig = createConfigFromValue(this.key, initialValue);
115-
this.cacheState = ClientCacheState.HasUpToDateFlagData;
116-
(this.eventEmitter as Internals.IEventEmitter<HookEvents>).emit("clientReady", this.cacheState);
117-
return this.cacheState;
118-
});
119-
}
120-
121-
/** Call this method to simulate the `clientReady` event and to provide an initial feature flag value. */
122-
async setReady(initialValue: NonNullable<SettingValue>): Promise<void> {
123-
this.signalReady(initialValue);
124-
125-
// Allow async continuations to run.
126-
await new Promise<void>(resolve => setTimeout(() => resolve(), 0));
127-
}
128-
129-
/** Call this method to simulate the `configChanged` event and to update the current feature flag value. */
130-
changeValue(newValue: NonNullable<SettingValue>): void {
131-
if (this.currentValue === newValue) {
132-
return;
101+
private readonly eventEmitter = new Internals.DefaultEventEmitter();
102+
private readonly readyPromise: Promise<ClientCacheState>;
103+
private signalReady!: (value: NonNullable<SettingValue>) => void;
104+
private cacheState = ClientCacheState.NoFlagData;
105+
private currentValue!: NonNullable<SettingValue>;
106+
private currentConfig: Config | null = null;
107+
108+
constructor(private readonly key: string) {
109+
super();
110+
111+
this.readyPromise = new Promise<NonNullable<SettingValue>>(resolve => this.signalReady = resolve)
112+
.then(initialValue => {
113+
this.currentValue = initialValue;
114+
this.currentConfig = createConfigFromValue(this.key, initialValue);
115+
this.cacheState = ClientCacheState.HasUpToDateFlagData;
116+
(this.eventEmitter as Internals.IEventEmitter<HookEvents>).emit("clientReady", this.cacheState);
117+
return this.cacheState;
118+
});
119+
}
120+
121+
/** Call this method to simulate the `clientReady` event and to provide an initial feature flag value. */
122+
async setReady(initialValue: NonNullable<SettingValue>): Promise<void> {
123+
this.signalReady(initialValue);
124+
125+
// Allow async continuations to run.
126+
await new Promise<void>(resolve => setTimeout(() => resolve(), 0));
127+
}
128+
129+
/** Call this method to simulate the `configChanged` event and to update the current feature flag value. */
130+
changeValue(newValue: NonNullable<SettingValue>): void {
131+
if (this.currentValue === newValue) {
132+
return;
133+
}
134+
135+
this.currentValue = newValue;
136+
this.currentConfig = createConfigFromValue(this.key, newValue);
137+
(this.eventEmitter as Internals.IEventEmitter<HookEvents>).emit("configChanged", this.currentConfig);
138+
}
139+
140+
override async getValueAsync<T extends SettingValue>(key: string, defaultValue: T, user?: User): Promise<SettingTypeOf<T>> {
141+
await this.readyPromise;
142+
143+
if (key === this.key) {
144+
return (isValidSettingValue(this.currentValue, defaultValue) ? this.currentValue : defaultValue) as SettingTypeOf<T>;
145+
}
146+
147+
return super.getValueAsync(key, defaultValue, user);
148+
}
149+
150+
override snapshot() {
151+
const client = this;
152+
153+
return new (class extends ConfigCatClientSnapshotMockBase {
154+
override getValue<T extends SettingValue>(key: string, defaultValue: T, user?: User): SettingTypeOf<T> {
155+
if (key === client.key) {
156+
return (isValidSettingValue(client.currentValue, defaultValue) ? client.currentValue : defaultValue) as SettingTypeOf<T>;
133157
}
134158

135-
this.currentValue = newValue;
136-
this.currentConfig = createConfigFromValue(this.key, newValue);
137-
(this.eventEmitter as Internals.IEventEmitter<HookEvents>).emit("configChanged", this.currentConfig);
138-
}
159+
return super.getValue(key, defaultValue, user);
160+
}
139161

140-
async getValueAsync<T extends SettingValue>(key: string, defaultValue: T, user?: User | undefined): Promise<SettingTypeOf<T>> {
141-
await this.readyPromise;
142-
143-
if (key === this.key) {
144-
return (isValidSettingValue(this.currentValue, defaultValue) ? this.currentValue : defaultValue) as SettingTypeOf<T>;
162+
override getValueDetails<T extends SettingValue>(key: string, defaultValue: T, user?: User) {
163+
if (key === client.key) {
164+
return isValidSettingValue(client.currentValue, defaultValue)
165+
? { key, value: client.currentValue, isDefaultValue: false, user } as EvaluationDetails<SettingTypeOf<T>>
166+
: { key, value: defaultValue, isDefaultValue: true, user, errorMessage: "Invalid setting type." } as EvaluationDetails<SettingTypeOf<T>>;
145167
}
146168

147-
return super.getValueAsync(key, defaultValue, user);
148-
}
169+
return super.getValueDetails(key, defaultValue, user);
170+
}
171+
})(this.cacheState, this.currentConfig);
172+
}
149173

150-
snapshot() {
151-
const client = this;
174+
override waitForReady(): Promise<ClientCacheState> {
175+
return this.readyPromise;
176+
}
152177

153-
return new (class extends ConfigCatClientSnapshotMockBase {
154-
getValue<T extends SettingValue>(key: string, defaultValue: T, user?: User | undefined): SettingTypeOf<T> {
155-
if (key === client.key) {
156-
return (isValidSettingValue(client.currentValue, defaultValue) ? client.currentValue : defaultValue) as SettingTypeOf<T>;
157-
}
178+
override on<TEventName extends keyof HookEvents>(eventName: TEventName, listener: (...args: HookEvents[TEventName]) => void): this {
179+
this.eventEmitter.on(eventName, listener as (...args: any[]) => void);
180+
return this;
181+
}
158182

159-
return super.getValue(key, defaultValue, user);
160-
}
161-
})(this.cacheState, this.currentConfig);
162-
}
163-
164-
waitForReady(): Promise<ClientCacheState> {
165-
return this.readyPromise;
166-
}
167-
168-
on<TEventName extends keyof HookEvents>(eventName: TEventName, listener: (...args: HookEvents[TEventName]) => void): this {
169-
this.eventEmitter.on(eventName, listener as (...args: any[]) => void);
170-
return this;
171-
}
183+
override off<TEventName extends keyof HookEvents>(eventName: TEventName, listener: (...args: HookEvents[TEventName]) => void): this {
184+
this.eventEmitter.off(eventName, listener as (...args: any[]) => void);
185+
return this;
186+
}
172187

173-
off<TEventName extends keyof HookEvents>(eventName: TEventName, listener: (...args: HookEvents[TEventName]) => void): this {
174-
this.eventEmitter.off(eventName, listener as (...args: any[]) => void);
175-
return this;
176-
}
177-
};
188+
override forceRefreshAsync(): Promise<RefreshResult> {
189+
return Promise.resolve({ isSuccess: true, errorCode: RefreshErrorCode.None });
190+
}
191+
}
178192

179193
function createConfigFromValue(key: string, value: NonNullable<SettingValue>): Config {
180-
const [settingType, settingValue]: [ConfigJson.SettingType, ConfigJson.SettingValue] =
181-
typeof value === "boolean" ? [ConfigJson.SettingType.Boolean, { b: value }] :
182-
typeof value === "string" ? [ConfigJson.SettingType.String, { s: value }] :
183-
Number.isInteger(this.changeValue) ? [ConfigJson.SettingType.Int, { i: value }] :
184-
[ConfigJson.SettingType.Double, { d: value }];
185-
186-
const configJson: Omit<ConfigJson.Config, "p"> = {
187-
f: { [key]: { t: settingType, v: settingValue } as ConfigJson.SettingUnion }
188-
};
194+
const [settingType, settingValue]: [ConfigJson.SettingType, ConfigJson.SettingValue] =
195+
typeof value === "boolean" ? [ConfigJson.SettingType.Boolean, { b: value }]
196+
: typeof value === "string" ? [ConfigJson.SettingType.String, { s: value }]
197+
: Number.isInteger(value) ? [ConfigJson.SettingType.Int, { i: value }]
198+
: [ConfigJson.SettingType.Double, { d: value }];
199+
200+
const configJson: Omit<ConfigJson.Config, "p"> = {
201+
f: { [key]: { t: settingType, v: settingValue } as ConfigJson.SettingUnion },
202+
};
189203

190-
return prepareConfig(configJson);
204+
return prepareConfig(configJson);
191205
}
192206

193207
function isValidSettingValue(settingValue: NonNullable<SettingValue>, defaultValue: SettingValue) {
194-
return defaultValue == null
195-
? isAllowedValue(settingValue)
196-
: typeof settingValue === typeof defaultValue;
197-
}
208+
return defaultValue == null
209+
? isAllowedValue(settingValue)
210+
: typeof settingValue === typeof defaultValue;
211+
}

0 commit comments

Comments
 (0)