|
2 | 2 | createClient as createBaseClient, |
3 | 3 | LDContextStrict, |
4 | 4 | LDStartOptions, |
5 | | - LDWaitForInitializationResult, |
6 | 5 | } from '@launchdarkly/js-client-sdk'; |
7 | 6 |
|
8 | 7 | import { createClient } from '../../src/client/LDReactClient'; |
@@ -76,37 +75,43 @@ jest.mock('@launchdarkly/js-client-sdk', () => { |
76 | 75 | }; |
77 | 76 | }); |
78 | 77 |
|
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(); |
81 | 81 | (createBaseClient as jest.Mock).mockReturnValue(mock); |
82 | 82 |
|
83 | 83 | 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)); |
90 | 86 |
|
91 | | - const client = createClient('test-id', { kind: 'user', key: 'u1' }); |
92 | 87 | const startPromise = client.start(); |
93 | | - expect(client.getInitializationState()).toBe('initializing'); |
94 | | - |
| 88 | + setContext(ctx); |
95 | 89 | resolveStart('complete'); |
96 | 90 | await startPromise; |
97 | | - expect(client.getInitializationState()).toBe('complete'); |
| 91 | + |
| 92 | + expect(received).toHaveLength(1); |
| 93 | + expect(received[0]).toEqual(ctx); |
98 | 94 | }); |
99 | 95 |
|
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(); |
102 | 99 | (createBaseClient as jest.Mock).mockReturnValue(mock); |
103 | 100 |
|
104 | 101 | 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(); |
106 | 107 | resolveStart('complete'); |
107 | | - await startPromise; |
| 108 | + await startPromise1; |
| 109 | + |
| 110 | + // Second call should not notify again |
| 111 | + const startPromise2 = client.start(); |
| 112 | + await startPromise2; |
108 | 113 |
|
109 | | - expect(client.getInitializationState()).toBe('complete'); |
| 114 | + expect(received).toHaveLength(1); |
110 | 115 | }); |
111 | 116 |
|
112 | 117 | 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', () => { |
196 | 201 | // @ts-ignore |
197 | 202 | global.window = originalWindow; |
198 | 203 | }); |
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 | | -}); |
0 commit comments