1
+ import { combineReducers , configureStore } from '@reduxjs/toolkit' ;
2
+ import type { AppStateStatus } from 'react-native' ;
3
+ import * as ReactNative from 'react-native' ;
4
+ import { EventType , IdentifyEventType } from '..' ;
1
5
import { SegmentClient } from '../analytics' ;
6
+ import * as checkInstalledVersion from '../internal/checkInstalledVersion' ;
7
+ import * as flushRetry from '../internal/flushRetry' ;
8
+ import * as handleAppStateChange from '../internal/handleAppStateChange' ;
9
+ import * as trackDeepLinks from '../internal/trackDeepLinks' ;
2
10
import { Logger } from '../logger' ;
3
- import * as ReactNative from 'react-native' ;
4
11
import * as alias from '../methods/alias' ;
12
+ import * as flush from '../methods/flush' ;
5
13
import * as group from '../methods/group' ;
6
14
import * as identify from '../methods/identify' ;
7
15
import * as screen from '../methods/screen' ;
8
16
import * as track from '../methods/track' ;
9
- import * as flush from '../methods/flush' ;
10
- import * as flushRetry from '../internal/flushRetry' ;
11
- import * as checkInstalledVersion from '../internal/checkInstalledVersion' ;
12
- import * as handleAppStateChange from '../internal/handleAppStateChange' ;
13
- import * as trackDeepLinks from '../internal/trackDeepLinks' ;
14
- import type { AppStateStatus } from 'react-native' ;
17
+ import { actions , Store } from '../store' ;
18
+ import mainSlice from '../store/main' ;
19
+ import systemSlice from '../store/system' ;
20
+ import userInfo from '../store/userInfo' ;
15
21
import { getMockStore } from './__helpers__/mockStore' ;
16
22
17
23
jest . mock ( 'redux-persist' , ( ) => {
@@ -109,7 +115,8 @@ describe('SegmentClient initialise', () => {
109
115
110
116
segmentClient . setupStoreSubscribe ( ) ;
111
117
112
- expect ( clientArgs . store . subscribe ) . toHaveBeenCalledTimes ( 1 ) ;
118
+ // Each watcher generates a subscription so we just check that it has subscribed at least once
119
+ expect ( clientArgs . store . subscribe ) . toHaveBeenCalled ( ) ;
113
120
} ) ;
114
121
} ) ;
115
122
@@ -147,7 +154,8 @@ describe('SegmentClient initialise', () => {
147
154
const segmentClient = new SegmentClient ( clientArgs ) ;
148
155
// @ts -ignore actual value is irrelevant
149
156
segmentClient . interval = 'INTERVAL' ;
150
- segmentClient . unsubscribe = jest . fn ( ) ;
157
+ const unsubscribe = jest . fn ( ) ;
158
+ segmentClient . watchers = [ unsubscribe ] ;
151
159
// @ts -ignore actual value is irrelevant
152
160
segmentClient . refreshTimeout = 'TIMEOUT' ;
153
161
segmentClient . appStateSubscription = {
@@ -158,7 +166,7 @@ describe('SegmentClient initialise', () => {
158
166
expect ( segmentClient . destroyed ) . toBe ( true ) ;
159
167
expect ( clearInterval ) . toHaveBeenCalledTimes ( 1 ) ;
160
168
expect ( clearInterval ) . toHaveBeenCalledWith ( 'INTERVAL' ) ;
161
- expect ( segmentClient . unsubscribe ) . toHaveBeenCalledTimes ( 1 ) ;
169
+ expect ( unsubscribe ) . toHaveBeenCalledTimes ( 1 ) ;
162
170
expect ( clearTimeout ) . toHaveBeenCalledTimes ( 1 ) ;
163
171
expect ( clearTimeout ) . toHaveBeenCalledWith ( 'TIMEOUT' ) ;
164
172
expect ( segmentClient . appStateSubscription . remove ) . toHaveBeenCalledTimes (
@@ -349,152 +357,98 @@ describe('SegmentClient #onUpdateStore', () => {
349
357
actions : { } ,
350
358
} ;
351
359
360
+ const sampleEvent : IdentifyEventType = {
361
+ userId : 'user-123' ,
362
+ anonymousId : 'eWpqvL-EHSHLWoiwagN-T' ,
363
+ type : EventType . IdentifyEvent ,
364
+ integrations : { } ,
365
+ timestamp : '2000-01-01T00:00:00.000Z' ,
366
+ traits : {
367
+ foo : 'bar' ,
368
+ } ,
369
+ messageId : 'iDMkR2-I7c2_LCsPPlvwH' ,
370
+ } ;
371
+
372
+ const rootReducer = combineReducers ( {
373
+ main : mainSlice . reducer ,
374
+ system : systemSlice . reducer ,
375
+ userInfo : userInfo . reducer ,
376
+ } ) ;
377
+ let mockStore = configureStore ( { reducer : rootReducer } ) as Store ;
378
+
352
379
beforeEach ( ( ) => {
353
380
jest . useFakeTimers ( ) ;
381
+ // Reset the Redux store to a clean state
382
+ mockStore = configureStore ( { reducer : rootReducer } ) as Store ;
354
383
} ) ;
355
384
356
385
afterEach ( ( ) => {
357
386
jest . clearAllTimers ( ) ;
358
387
jest . clearAllMocks ( ) ;
359
388
} ) ;
360
389
361
- it ( 'calls flush when there are unsent events' , ( ) => {
390
+ /**
391
+ * Creates a client wired up with store subscriptions and flush mocks for testing automatic flushes
392
+ */
393
+ const setupClient = ( flushAt : number ) : SegmentClient => {
362
394
const args = {
363
395
...clientArgs ,
364
396
config : {
365
397
...clientArgs . config ,
366
- flushAt : 1 ,
367
- } ,
368
- store : {
369
- ...clientArgs . store ,
370
- getState : jest . fn ( ) . mockReturnValue ( {
371
- main : {
372
- events : [ { messageId : '1' } ] ,
373
- eventsToRetry : [ ] ,
374
- } ,
375
- system : {
376
- settings : { } ,
377
- } ,
378
- } ) ,
398
+ flushAt,
379
399
} ,
400
+ store : mockStore ,
401
+ actions : actions ,
380
402
} ;
381
403
const client = new SegmentClient ( args ) ;
404
+ // It is important to setup the flush spy before setting up the subscriptions so that it tracks the calls in the closure
382
405
jest . spyOn ( client , 'flush' ) . mockResolvedValueOnce ( ) ;
383
- client . onUpdateStore ( ) ;
406
+ jest . spyOn ( client , 'flushRetry' ) . mockResolvedValueOnce ( ) ;
407
+ client . setupStoreSubscribe ( ) ;
408
+ return client ;
409
+ } ;
384
410
411
+ it ( 'calls flush when there are unsent events' , ( ) => {
412
+ const client = setupClient ( 1 ) ;
413
+ mockStore . dispatch ( mainSlice . actions . addEvent ( { event : sampleEvent } ) ) ;
385
414
expect ( client . flush ) . toHaveBeenCalledTimes ( 1 ) ;
386
415
} ) ;
387
416
388
417
it ( 'does not flush when number of events does not exceed the flush threshold' , ( ) => {
389
- const args = {
390
- ...clientArgs ,
391
- config : {
392
- ...clientArgs . config ,
393
- flushAt : 2 ,
394
- } ,
395
- store : {
396
- ...clientArgs . store ,
397
- getState : jest . fn ( ) . mockReturnValue ( {
398
- main : {
399
- events : [ { messageId : '1' } ] ,
400
- eventsToRetry : [ ] ,
401
- } ,
402
- system : {
403
- settings : { } ,
404
- } ,
405
- } ) ,
406
- } ,
407
- } ;
408
- const client = new SegmentClient ( args ) ;
409
- jest . spyOn ( client , 'flush' ) . mockResolvedValueOnce ( ) ;
410
- client . onUpdateStore ( ) ;
411
-
418
+ const client = setupClient ( 2 ) ;
419
+ mockStore . dispatch ( mainSlice . actions . addEvent ( { event : sampleEvent } ) ) ;
412
420
expect ( client . flush ) . not . toHaveBeenCalled ( ) ;
413
421
} ) ;
414
422
415
423
it ( 'does not call flush when there are no events to send' , ( ) => {
416
- const args = {
417
- ...clientArgs ,
418
- config : {
419
- ...clientArgs . config ,
420
- flushAt : 1 ,
421
- } ,
422
- store : {
423
- ...clientArgs . store ,
424
- getState : jest . fn ( ) . mockReturnValue ( {
425
- main : {
426
- events : [ ] ,
427
- eventsToRetry : [ ] ,
428
- } ,
429
- system : {
430
- settings : { } ,
431
- } ,
432
- } ) ,
433
- } ,
434
- } ;
435
- const client = new SegmentClient ( args ) ;
436
- jest . spyOn ( client , 'flush' ) . mockResolvedValueOnce ( ) ;
437
- jest . spyOn ( client , 'flushRetry' ) . mockResolvedValueOnce ( ) ;
438
- client . onUpdateStore ( ) ;
439
-
424
+ const client = setupClient ( 1 ) ;
440
425
expect ( client . flush ) . not . toHaveBeenCalled ( ) ;
441
426
expect ( client . flushRetry ) . not . toHaveBeenCalled ( ) ;
442
427
} ) ;
443
428
444
429
it ( 'flushes retry queue when it is non-empty' , ( ) => {
445
- const args = {
446
- ...clientArgs ,
447
- config : {
448
- ...clientArgs . config ,
449
- flushAt : 2 ,
450
- } ,
451
- store : {
452
- ...clientArgs . store ,
453
- getState : jest . fn ( ) . mockReturnValue ( {
454
- main : {
455
- events : [ ] ,
456
- eventsToRetry : [ { messageId : '1' } ] ,
457
- } ,
458
- system : {
459
- settings : { } ,
460
- } ,
461
- } ) ,
462
- } ,
463
- } ;
464
- const client = new SegmentClient ( args ) ;
465
- jest . spyOn ( client , 'flush' ) . mockResolvedValueOnce ( ) ;
466
- client . onUpdateStore ( ) ;
430
+ const client = setupClient ( 2 ) ;
467
431
468
- expect ( setTimeout ) . toHaveBeenLastCalledWith (
469
- expect . any ( Function ) ,
470
- args . config . retryInterval ! * 1000
432
+ mockStore . dispatch (
433
+ mainSlice . actions . addEventsToRetry ( {
434
+ events : [ sampleEvent ] ,
435
+ config : { ...clientArgs . config } ,
436
+ } )
471
437
) ;
472
- expect ( client . refreshTimeout ) . not . toBeNull ( ) ;
438
+
439
+ expect ( client . flushRetry ) . toHaveBeenCalledTimes ( 1 ) ;
473
440
} ) ;
474
441
475
442
it ( 'does not flush the retry queue when the refreshTimeout is not null' , ( ) => {
476
- const args = {
477
- ...clientArgs ,
478
- config : {
479
- ...clientArgs . config ,
480
- flushAt : 2 ,
481
- } ,
482
- store : {
483
- ...clientArgs . store ,
484
- getState : jest . fn ( ) . mockReturnValue ( {
485
- main : {
486
- events : [ ] ,
487
- eventsToRetry : [ { messageId : '1' } ] ,
488
- } ,
489
- system : {
490
- settings : { } ,
491
- } ,
492
- } ) ,
493
- } ,
494
- } ;
495
- const client = new SegmentClient ( args ) ;
443
+ const client = setupClient ( 2 ) ;
496
444
client . refreshTimeout = jest . fn ( ) as any ;
497
- client . onUpdateStore ( ) ;
445
+
446
+ mockStore . dispatch (
447
+ mainSlice . actions . addEventsToRetry ( {
448
+ events : [ sampleEvent ] ,
449
+ config : { ...clientArgs . config } ,
450
+ } )
451
+ ) ;
498
452
499
453
expect ( setTimeout ) . not . toHaveBeenCalled ( ) ;
500
454
} ) ;
@@ -579,8 +533,10 @@ describe('SegmentClient #flushRetry', () => {
579
533
it ( 'calls the screen method' , async ( ) => {
580
534
const flushRetrySpy = jest . spyOn ( flushRetry , 'default' ) . mockResolvedValue ( ) ;
581
535
const client = new SegmentClient ( clientArgs ) ;
536
+ client . setupStoreSubscribe ( ) ;
582
537
583
538
await client . flushRetry ( ) ;
539
+ jest . runAllTimers ( ) ;
584
540
585
541
expect ( flushRetrySpy ) . toHaveBeenCalledTimes ( 1 ) ;
586
542
} ) ;
0 commit comments