Skip to content

Commit a5d4d5f

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

14 files changed

Lines changed: 175 additions & 395 deletions

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

Lines changed: 23 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import {
22
createClient as createBaseClient,
33
LDContextStrict,
44
LDStartOptions,
5-
LDWaitForInitializationResult,
65
} from '@launchdarkly/js-client-sdk';
76

87
import { createClient } from '../../src/client/LDReactClient';
@@ -76,37 +75,43 @@ jest.mock('@launchdarkly/js-client-sdk', () => {
7675
};
7776
});
7877

79-
it('returns getInitializationState() === "unknown" initially', () => {
80-
const { mock } = makeMockBaseClient();
78+
it('start() fires onContextChange subscribers with the new context', async () => {
79+
const ctx: LDContextStrict = { kind: 'user', key: 'u1' };
80+
const { mock, resolveStart, setContext } = 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');
85-
});
86-
87-
it('returns "initializing" while start() is in-flight', async () => {
88-
const { mock, resolveStart } = makeMockBaseClient();
89-
(createBaseClient as jest.Mock).mockReturnValue(mock);
84+
const received: LDContextStrict[] = [];
85+
client.onContextChange((c) => received.push(c));
9086

91-
const client = createClient('test-id', { kind: 'user', key: 'u1' });
9287
const startPromise = client.start();
93-
expect(client.getInitializationState()).toBe('initializing');
94-
88+
setContext(ctx);
9589
resolveStart('complete');
9690
await startPromise;
97-
expect(client.getInitializationState()).toBe('complete');
91+
92+
expect(received).toHaveLength(1);
93+
expect(received[0]).toEqual(ctx);
9894
});
9995

100-
it('sets initializedState to "complete" after start() resolves', async () => {
101-
const { mock, resolveStart } = makeMockBaseClient();
96+
it('start() only notifies context subscribers once even if called multiple times', async () => {
97+
const ctx: LDContextStrict = { kind: 'user', key: 'u1' };
98+
const { mock, resolveStart, setContext } = makeMockBaseClient();
10299
(createBaseClient as jest.Mock).mockReturnValue(mock);
103100

104101
const client = createClient('test-id', { kind: 'user', key: 'u1' });
105-
const startPromise = client.start();
102+
const received: LDContextStrict[] = [];
103+
client.onContextChange((c) => received.push(c));
104+
105+
setContext(ctx);
106+
const startPromise1 = client.start();
106107
resolveStart('complete');
107-
await startPromise;
108+
await startPromise1;
109+
110+
// Second call should not notify again
111+
const startPromise2 = client.start();
112+
await startPromise2;
108113

109-
expect(client.getInitializationState()).toBe('complete');
114+
expect(received).toHaveLength(1);
110115
});
111116

112117
it('invokes onContextChange callback after identify() resolves with the new context', async () => {
@@ -196,128 +201,3 @@ it('noop client onContextChange returns a no-op unsubscribe', () => {
196201
// @ts-ignore
197202
global.window = originalWindow;
198203
});
199-
200-
it('invokes onInitializationStatusChange callback after start() resolves', async () => {
201-
const { mock, resolveStart } = makeMockBaseClient();
202-
(createBaseClient as jest.Mock).mockReturnValue(mock);
203-
204-
const client = createClient('test-id', { kind: 'user', key: 'u1' });
205-
const received: LDWaitForInitializationResult[] = [];
206-
client.onInitializationStatusChange((result) => received.push(result));
207-
208-
const startPromise = client.start();
209-
resolveStart('complete');
210-
await startPromise;
211-
212-
expect(received).toHaveLength(1);
213-
expect(received[0].status).toBe('complete');
214-
});
215-
216-
it('notifies multiple subscribers on start()', async () => {
217-
const { mock, resolveStart } = makeMockBaseClient();
218-
(createBaseClient as jest.Mock).mockReturnValue(mock);
219-
220-
const client = createClient('test-id', { kind: 'user', key: 'u1' });
221-
const calls1: LDWaitForInitializationResult[] = [];
222-
const calls2: LDWaitForInitializationResult[] = [];
223-
client.onInitializationStatusChange((r) => calls1.push(r));
224-
client.onInitializationStatusChange((r) => calls2.push(r));
225-
226-
const startPromise = client.start();
227-
resolveStart('complete');
228-
await startPromise;
229-
230-
expect(calls1).toHaveLength(1);
231-
expect(calls2).toHaveLength(1);
232-
});
233-
234-
it('stops notifying after the unsubscribe function is called (onInitializationStatusChange)', async () => {
235-
const { mock, resolveStart } = makeMockBaseClient();
236-
(createBaseClient as jest.Mock).mockReturnValue(mock);
237-
238-
const client = createClient('test-id', { kind: 'user', key: 'u1' });
239-
const received: LDWaitForInitializationResult[] = [];
240-
const unsubscribe = client.onInitializationStatusChange((r) => received.push(r));
241-
242-
unsubscribe();
243-
244-
const startPromise = client.start();
245-
resolveStart('complete');
246-
await startPromise;
247-
248-
expect(received).toHaveLength(0);
249-
});
250-
251-
it('calls callback immediately if start() already resolved (late subscriber)', async () => {
252-
const { mock, resolveStart } = makeMockBaseClient();
253-
(createBaseClient as jest.Mock).mockReturnValue(mock);
254-
255-
const client = createClient('test-id', { kind: 'user', key: 'u1' });
256-
const startPromise = client.start();
257-
resolveStart('complete');
258-
await startPromise;
259-
260-
const received: LDWaitForInitializationResult[] = [];
261-
client.onInitializationStatusChange((r) => received.push(r));
262-
263-
expect(received).toHaveLength(1);
264-
expect(received[0].status).toBe('complete');
265-
});
266-
267-
it('getInitializationError() returns undefined before start()', () => {
268-
const { mock } = makeMockBaseClient();
269-
(createBaseClient as jest.Mock).mockReturnValue(mock);
270-
271-
const client = createClient('test-id', { kind: 'user', key: 'u1' });
272-
expect(client.getInitializationError()).toBeUndefined();
273-
});
274-
275-
it('getInitializationError() returns undefined after successful start()', async () => {
276-
const { mock, resolveStart } = makeMockBaseClient();
277-
(createBaseClient as jest.Mock).mockReturnValue(mock);
278-
279-
const client = createClient('test-id', { kind: 'user', key: 'u1' });
280-
const startPromise = client.start();
281-
resolveStart('complete');
282-
await startPromise;
283-
284-
expect(client.getInitializationError()).toBeUndefined();
285-
});
286-
287-
it('getInitializationError() returns the error after a failed start()', async () => {
288-
const { mock } = makeMockBaseClient();
289-
const failError = new Error('network failure');
290-
(mock.start as jest.Mock).mockResolvedValueOnce({ status: 'failed', error: failError });
291-
(createBaseClient as jest.Mock).mockReturnValue(mock);
292-
293-
const client = createClient('test-id', { kind: 'user', key: 'u1' });
294-
await client.start();
295-
296-
expect(client.getInitializationError()).toBe(failError);
297-
});
298-
299-
it('noop client getInitializationError() returns undefined', () => {
300-
const originalWindow = global.window;
301-
// @ts-ignore
302-
delete global.window;
303-
304-
const client = createClient('test-id', { kind: 'user', key: 'u1' });
305-
expect(client.getInitializationError()).toBeUndefined();
306-
307-
// @ts-ignore
308-
global.window = originalWindow;
309-
});
310-
311-
it('noop client onInitializationStatusChange returns a no-op unsubscribe', () => {
312-
const originalWindow = global.window;
313-
// @ts-ignore
314-
delete global.window;
315-
316-
const client = createClient('test-id', { kind: 'user', key: 'u1' });
317-
const unsubscribe = client.onInitializationStatusChange(() => {});
318-
expect(typeof unsubscribe).toBe('function');
319-
expect(() => unsubscribe()).not.toThrow();
320-
321-
// @ts-ignore
322-
global.window = originalWindow;
323-
});

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

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

@@ -31,7 +30,6 @@ export function makeStatefulWrapper(mockClient: ReturnType<typeof makeMockClient
3130
function Wrapper({ children }: { children: React.ReactNode }) {
3231
const [ctxValue, setCtx] = React.useState<LDReactClientContextValue>({
3332
client: mockClient,
34-
initializedState: 'complete',
3533
});
3634
setterRef.current = setCtx;
3735
return <LDReactContext.Provider value={ctxValue}>{children}</LDReactContext.Provider>;

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,6 @@ 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',
382381
});
383382
});
384383

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

430429
const ctxValue: LDReactClientContextValue = {
431430
client: mockClient,
432-
initializedState: 'complete',
433431
};
434432

435433
render(

0 commit comments

Comments
 (0)