Skip to content

Commit d710974

Browse files
committed
refactor: simplify getting initializationState
1 parent 9250766 commit d710974

14 files changed

Lines changed: 85 additions & 62 deletions

packages/sdk/react/__tests__/client/LDReactClient.test.ts

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,37 +76,63 @@ jest.mock('@launchdarkly/js-client-sdk', () => {
7676
};
7777
});
7878

79-
it('returns getInitializationState() === "unknown" initially', () => {
79+
it('returns getInitializationState() === "initializing" initially', () => {
8080
const { mock } = makeMockBaseClient();
8181
(createBaseClient as jest.Mock).mockReturnValue(mock);
8282

8383
const client = createClient('test-id', { kind: 'user', key: 'u1' });
84-
expect(client.getInitializationState()).toBe('unknown');
84+
expect(client.getInitializationState()).toBe('initializing');
8585
});
8686

87-
it('returns "initializing" while start() is in-flight', async () => {
87+
it('sets initializedState to "complete" after start() resolves', async () => {
8888
const { mock, resolveStart } = makeMockBaseClient();
8989
(createBaseClient as jest.Mock).mockReturnValue(mock);
9090

9191
const client = createClient('test-id', { kind: 'user', key: 'u1' });
9292
const startPromise = client.start();
93-
expect(client.getInitializationState()).toBe('initializing');
94-
9593
resolveStart('complete');
9694
await startPromise;
95+
9796
expect(client.getInitializationState()).toBe('complete');
9897
});
9998

100-
it('sets initializedState to "complete" after start() resolves', async () => {
101-
const { mock, resolveStart } = makeMockBaseClient();
99+
it('start() fires onContextChange subscribers with the new context', async () => {
100+
const ctx: LDContextStrict = { kind: 'user', key: 'u1' };
101+
const { mock, resolveStart, setContext } = makeMockBaseClient();
102102
(createBaseClient as jest.Mock).mockReturnValue(mock);
103103

104104
const client = createClient('test-id', { kind: 'user', key: 'u1' });
105+
const received: LDContextStrict[] = [];
106+
client.onContextChange((c) => received.push(c));
107+
105108
const startPromise = client.start();
109+
setContext(ctx);
106110
resolveStart('complete');
107111
await startPromise;
108112

109-
expect(client.getInitializationState()).toBe('complete');
113+
expect(received).toHaveLength(1);
114+
expect(received[0]).toEqual(ctx);
115+
});
116+
117+
it('start() only notifies context subscribers once even if called multiple times', async () => {
118+
const ctx: LDContextStrict = { kind: 'user', key: 'u1' };
119+
const { mock, resolveStart, setContext } = makeMockBaseClient();
120+
(createBaseClient as jest.Mock).mockReturnValue(mock);
121+
122+
const client = createClient('test-id', { kind: 'user', key: 'u1' });
123+
const received: LDContextStrict[] = [];
124+
client.onContextChange((c) => received.push(c));
125+
126+
setContext(ctx);
127+
const startPromise1 = client.start();
128+
resolveStart('complete');
129+
await startPromise1;
130+
131+
// Second call should not notify again
132+
const startPromise2 = client.start();
133+
await startPromise2;
134+
135+
expect(received).toHaveLength(1);
110136
});
111137

112138
it('invokes onContextChange callback after identify() resolves with the new context', async () => {

packages/sdk/react/__tests__/client/deprecated-hooks/renderHelpers.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export function makeWrapper(
1010
) {
1111
const contextValue: LDReactClientContextValue = {
1212
client: mockClient,
13-
initializedState: 'unknown',
13+
initializedState: 'initializing',
1414
...contextOverrides,
1515
};
1616

@@ -31,7 +31,7 @@ export function makeStatefulWrapper(mockClient: ReturnType<typeof makeMockClient
3131
function Wrapper({ children }: { children: React.ReactNode }) {
3232
const [ctxValue, setCtx] = React.useState<LDReactClientContextValue>({
3333
client: mockClient,
34-
initializedState: 'complete',
34+
initializedState: 'initializing',
3535
});
3636
setterRef.current = setCtx;
3737
return <LDReactContext.Provider value={ctxValue}>{children}</LDReactContext.Provider>;

packages/sdk/react/__tests__/client/deprecated-hooks/useFlags.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ it('clears the variation cache when the context changes after identify', () => {
378378
setterRef.current!({
379379
client: mockClient,
380380
context: { kind: 'user', key: 'new-user' },
381-
initializedState: 'complete',
381+
initializedState: 'initializing',
382382
});
383383
});
384384

@@ -429,7 +429,7 @@ it('reads flags from a custom react context', () => {
429429

430430
const ctxValue: LDReactClientContextValue = {
431431
client: mockClient,
432-
initializedState: 'complete',
432+
initializedState: 'initializing',
433433
};
434434

435435
render(

packages/sdk/react/__tests__/client/hooks/useInitializationStatus.test.tsx

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,6 @@ function StatusConsumer({ onStatus }: { onStatus: (s: InitializationStatus) => v
2323
return null;
2424
}
2525

26-
it('returns { status: "unknown" } when initializedState is "unknown"', () => {
27-
const captured: InitializationStatus[] = [];
28-
29-
render(
30-
<LDReactContext.Provider value={makeContextValue('unknown')}>
31-
<StatusConsumer onStatus={(s) => captured.push(s)} />
32-
</LDReactContext.Provider>,
33-
);
34-
35-
expect(captured[0]).toEqual({ status: 'unknown' });
36-
});
37-
3826
it('returns { status: "initializing" } when initializedState is "initializing"', () => {
3927
const captured: InitializationStatus[] = [];
4028

packages/sdk/react/__tests__/client/hooks/useLDClient.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ it('returns the client from the nearest provider context', () => {
1919
const mockClient = makeMockClient();
2020
const contextValue: LDReactClientContextValue = {
2121
client: mockClient,
22-
initializedState: 'unknown',
22+
initializedState: 'initializing',
2323
};
2424

2525
let capturedClient: any;
@@ -43,7 +43,7 @@ it('returns the client from a custom react context', () => {
4343
const customContext = React.createContext<LDReactClientContextValue>(null as any);
4444
const contextValue: LDReactClientContextValue = {
4545
client: mockClient,
46-
initializedState: 'unknown',
46+
initializedState: 'initializing',
4747
};
4848

4949
let capturedClient: any;

packages/sdk/react/__tests__/client/hooks/useVariation.test.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ function makeWrapper(
2020
) {
2121
const contextValue: LDReactClientContextValue = {
2222
client: mockClient,
23-
initializedState: 'unknown',
23+
initializedState: 'initializing',
2424
...contextOverrides,
2525
};
2626

@@ -148,7 +148,7 @@ it('useBoolVariation calls variation again when context changes after identify',
148148
function StatefulWrapper({ children }: { children: React.ReactNode }) {
149149
const [ctxValue, setCtx] = React.useState<LDReactClientContextValue>({
150150
client: mockClient,
151-
initializedState: 'complete',
151+
initializedState: 'initializing',
152152
});
153153
setContextValue = setCtx;
154154
return <LDReactContext.Provider value={ctxValue}>{children}</LDReactContext.Provider>;
@@ -171,7 +171,7 @@ it('useBoolVariation calls variation again when context changes after identify',
171171
setContextValue({
172172
client: mockClient,
173173
context: { kind: 'user', key: 'new-user' },
174-
initializedState: 'complete',
174+
initializedState: 'initializing',
175175
});
176176
});
177177

@@ -312,7 +312,7 @@ it('useStringVariation calls variation again when context changes', () => {
312312
function StatefulWrapper({ children }: { children: React.ReactNode }) {
313313
const [ctxValue, setCtx] = React.useState<LDReactClientContextValue>({
314314
client: mockClient,
315-
initializedState: 'complete',
315+
initializedState: 'initializing',
316316
});
317317
setContextValue = setCtx;
318318
return <LDReactContext.Provider value={ctxValue}>{children}</LDReactContext.Provider>;
@@ -335,7 +335,7 @@ it('useStringVariation calls variation again when context changes', () => {
335335
setContextValue({
336336
client: mockClient,
337337
context: { kind: 'user', key: 'new-user' },
338-
initializedState: 'complete',
338+
initializedState: 'initializing',
339339
});
340340
});
341341

@@ -404,7 +404,7 @@ it('useNumberVariation calls variation again when context changes', () => {
404404
function StatefulWrapper({ children }: { children: React.ReactNode }) {
405405
const [ctxValue, setCtx] = React.useState<LDReactClientContextValue>({
406406
client: mockClient,
407-
initializedState: 'complete',
407+
initializedState: 'initializing',
408408
});
409409
setContextValue = setCtx;
410410
return <LDReactContext.Provider value={ctxValue}>{children}</LDReactContext.Provider>;
@@ -427,7 +427,7 @@ it('useNumberVariation calls variation again when context changes', () => {
427427
setContextValue({
428428
client: mockClient,
429429
context: { kind: 'user', key: 'new-user' },
430-
initializedState: 'complete',
430+
initializedState: 'initializing',
431431
});
432432
});
433433

@@ -534,7 +534,7 @@ it('useJsonVariation calls variation again when context changes', () => {
534534
function StatefulWrapper({ children }: { children: React.ReactNode }) {
535535
const [ctxValue, setCtx] = React.useState<LDReactClientContextValue>({
536536
client: mockClient,
537-
initializedState: 'complete',
537+
initializedState: 'initializing',
538538
});
539539
setContextValue = setCtx;
540540
return <LDReactContext.Provider value={ctxValue}>{children}</LDReactContext.Provider>;
@@ -557,7 +557,7 @@ it('useJsonVariation calls variation again when context changes', () => {
557557
setContextValue({
558558
client: mockClient,
559559
context: { kind: 'user', key: 'new-user' },
560-
initializedState: 'complete',
560+
initializedState: 'initializing',
561561
});
562562
});
563563

packages/sdk/react/__tests__/client/hooks/useVariationDetail.test.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { makeMockClient } from '../mockClient';
1919
function makeWrapper(mockClient: ReturnType<typeof makeMockClient>) {
2020
const contextValue: LDReactClientContextValue = {
2121
client: mockClient,
22-
initializedState: 'unknown',
22+
initializedState: 'initializing',
2323
};
2424

2525
return function Wrapper({ children }: { children: React.ReactNode }) {
@@ -142,7 +142,7 @@ it('useBoolVariationDetail calls variation detail again when context changes', (
142142
function StatefulWrapper({ children }: { children: React.ReactNode }) {
143143
const [ctxValue, setCtx] = React.useState<LDReactClientContextValue>({
144144
client: mockClient,
145-
initializedState: 'complete',
145+
initializedState: 'initializing',
146146
});
147147
setContextValue = setCtx;
148148
return <LDReactContext.Provider value={ctxValue}>{children}</LDReactContext.Provider>;
@@ -165,7 +165,7 @@ it('useBoolVariationDetail calls variation detail again when context changes', (
165165
setContextValue({
166166
client: mockClient,
167167
context: { kind: 'user', key: 'new-user' },
168-
initializedState: 'complete',
168+
initializedState: 'initializing',
169169
});
170170
});
171171

@@ -300,7 +300,7 @@ it('useStringVariationDetail calls variation detail again when context changes',
300300
function StatefulWrapper({ children }: { children: React.ReactNode }) {
301301
const [ctxValue, setCtx] = React.useState<LDReactClientContextValue>({
302302
client: mockClient,
303-
initializedState: 'complete',
303+
initializedState: 'initializing',
304304
});
305305
setContextValue = setCtx;
306306
return <LDReactContext.Provider value={ctxValue}>{children}</LDReactContext.Provider>;
@@ -323,7 +323,7 @@ it('useStringVariationDetail calls variation detail again when context changes',
323323
setContextValue({
324324
client: mockClient,
325325
context: { kind: 'user', key: 'new-user' },
326-
initializedState: 'complete',
326+
initializedState: 'initializing',
327327
});
328328
});
329329

@@ -413,7 +413,7 @@ it('useNumberVariationDetail calls variation detail again when context changes',
413413
function StatefulWrapper({ children }: { children: React.ReactNode }) {
414414
const [ctxValue, setCtx] = React.useState<LDReactClientContextValue>({
415415
client: mockClient,
416-
initializedState: 'complete',
416+
initializedState: 'initializing',
417417
});
418418
setContextValue = setCtx;
419419
return <LDReactContext.Provider value={ctxValue}>{children}</LDReactContext.Provider>;
@@ -436,7 +436,7 @@ it('useNumberVariationDetail calls variation detail again when context changes',
436436
setContextValue({
437437
client: mockClient,
438438
context: { kind: 'user', key: 'new-user' },
439-
initializedState: 'complete',
439+
initializedState: 'initializing',
440440
});
441441
});
442442

@@ -526,7 +526,7 @@ it('useJsonVariationDetail calls variation detail again when context changes', (
526526
function StatefulWrapper({ children }: { children: React.ReactNode }) {
527527
const [ctxValue, setCtx] = React.useState<LDReactClientContextValue>({
528528
client: mockClient,
529-
initializedState: 'complete',
529+
initializedState: 'initializing',
530530
});
531531
setContextValue = setCtx;
532532
return <LDReactContext.Provider value={ctxValue}>{children}</LDReactContext.Provider>;
@@ -549,7 +549,7 @@ it('useJsonVariationDetail calls variation detail again when context changes', (
549549
setContextValue({
550550
client: mockClient,
551551
context: { kind: 'user', key: 'new-user' },
552-
initializedState: 'complete',
552+
initializedState: 'initializing',
553553
});
554554
});
555555

packages/sdk/react/__tests__/client/mockClient.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export function makeMockClient(options: MockClientOptions = {}): MockClient {
3636

3737
const initStatusSubscribers = new Set<(result: LDWaitForInitializationResult) => void>();
3838
const contextChangeSubscribers = new Set<(ctx: LDContextStrict) => void>();
39-
let initState: InitializedState = initialState ?? (preFailedError ? 'failed' : 'unknown');
39+
let initState: InitializedState = initialState ?? (preFailedError ? 'failed' : 'initializing');
4040
let initError: Error | undefined = preFailedError;
4141
let currentContext: LDContextStrict | undefined;
4242
const eventHandlers = new Map<string, Set<EventHandler>>();

packages/sdk/react/__tests__/client/provider/LDReactProvider.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ it('updates initializedState and context when onInitializationStatusChange fires
8181
</Provider>,
8282
);
8383

84-
expect(contextValues[contextValues.length - 1]?.initializedState).toBe('unknown');
84+
expect(contextValues[contextValues.length - 1]?.initializedState).toBe('initializing');
8585

8686
await act(async () => {
8787
client.fireInitStatusChange('complete');

packages/sdk/react/__tests__/client/provider/multipleEnvironments.test.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,8 @@ it('initialization state is tracked independently per context', async () => {
147147
</ProviderA>,
148148
);
149149

150-
expect(screen.getByTestId('status-a').textContent).toBe('unknown');
151-
expect(screen.getByTestId('status-b').textContent).toBe('unknown');
150+
expect(screen.getByTestId('status-a').textContent).toBe('initializing');
151+
expect(screen.getByTestId('status-b').textContent).toBe('initializing');
152152

153153
await act(async () => {
154154
clientA.fireInitStatusChange('complete');
@@ -157,8 +157,8 @@ it('initialization state is tracked independently per context', async () => {
157157
await waitFor(() => {
158158
expect(screen.getByTestId('status-a').textContent).toBe('complete');
159159
});
160-
// clientB still unknown
161-
expect(screen.getByTestId('status-b').textContent).toBe('unknown');
160+
// clientB still initializing
161+
expect(screen.getByTestId('status-b').textContent).toBe('initializing');
162162

163163
await act(async () => {
164164
clientB.fireInitStatusChange('complete');

0 commit comments

Comments
 (0)