1717 *
1818 */
1919
20+ import assert from 'node:assert' ;
2021import { ReactElement } from 'react' ;
2122
2223import { render } from '@testing-library/react' ;
2324
2425import { usePrimaryModalState } from 'Components/Modals/PrimaryModal' ;
25- import { createDeterministicWallClock } from 'src/script/clock/deterministicWallClock' ;
26+ import { createDeterministicWallClock , DeterministicWallClock } from 'src/script/clock/deterministicWallClock' ;
2627import { MainViewModel } from 'src/script/view_model/MainViewModel' ;
2728import { t } from 'Util/LocalizerUtil' ;
29+ import { TIME_IN_MILLIS } from 'Util/TimeUtil' ;
2830
2931import { RootProvider } from '../../RootProvider' ;
3032import { ForceReloadModal } from './ForceReloadModal' ;
3133
3234interface ForceReloadModalTestContextValue {
3335 readonly doesApplicationNeedForceReload : boolean ;
3436 readonly reloadApplication : ( ) => void ;
37+ readonly wallClock ?: DeterministicWallClock ;
3538}
3639
3740function isFeatureFlagDisabledForTest ( ) : boolean {
@@ -65,22 +68,28 @@ function resetPrimaryModalState(): void {
6568function createForceReloadModalTestElement (
6669 contextValue : ForceReloadModalTestContextValue ,
6770) : ReactElement {
68- const { doesApplicationNeedForceReload, reloadApplication} = contextValue ;
71+ const {
72+ doesApplicationNeedForceReload,
73+ reloadApplication,
74+ wallClock = createDeterministicWallClock ( { initialCurrentTimestampInMilliseconds : 1_111 } ) ,
75+ } = contextValue ;
6976
7077 return (
7178 < RootProvider
7279 value = { {
7380 doesApplicationNeedForceReload,
7481 isFeatureFlagEnabled : isFeatureFlagDisabledForTest ,
7582 mainViewModel : createMainViewModelForTest ( ) ,
76- wallClock : createDeterministicWallClock ( { initialCurrentTimestampInMilliseconds : 1_111 } ) ,
83+ wallClock,
7784 } }
7885 >
7986 < ForceReloadModal reloadApplication = { reloadApplication } />
8087 </ RootProvider >
8188 ) ;
8289}
8390
91+ const forceReloadDelayInMilliseconds = TIME_IN_MILLIS . SECOND * 60 ;
92+
8493describe ( 'ForceReloadModal' , ( ) => {
8594 beforeEach ( ( ) => {
8695 resetPrimaryModalState ( ) ;
@@ -94,32 +103,89 @@ describe('ForceReloadModal', () => {
94103 } ) ;
95104
96105 it ( 'opens the modal when force reload becomes required' , ( ) => {
106+ const deterministicWallClock = createDeterministicWallClock ( { initialCurrentTimestampInMilliseconds : 0 } ) ;
97107 const reloadApplication = jest . fn ( ) ;
98- const { rerender} = render ( createForceReloadModalTestElement ( { doesApplicationNeedForceReload : false , reloadApplication} ) ) ;
108+ const { rerender} = render (
109+ createForceReloadModalTestElement ( {
110+ doesApplicationNeedForceReload : false ,
111+ reloadApplication,
112+ wallClock : deterministicWallClock ,
113+ } ) ,
114+ ) ;
99115
100- rerender ( createForceReloadModalTestElement ( { doesApplicationNeedForceReload : true , reloadApplication} ) ) ;
116+ rerender (
117+ createForceReloadModalTestElement ( {
118+ doesApplicationNeedForceReload : true ,
119+ reloadApplication,
120+ wallClock : deterministicWallClock ,
121+ } ) ,
122+ ) ;
101123
102124 expect ( usePrimaryModalState . getState ( ) . currentModalId ) . not . toBeNull ( ) ;
103125 expect ( usePrimaryModalState . getState ( ) . queue ) . toHaveLength ( 0 ) ;
104126 } ) ;
105127
106128 it ( 'does not open the modal repeatedly while force reload remains required' , ( ) => {
129+ const deterministicWallClock = createDeterministicWallClock ( { initialCurrentTimestampInMilliseconds : 0 } ) ;
107130 const reloadApplication = jest . fn ( ) ;
108- const { rerender} = render ( createForceReloadModalTestElement ( { doesApplicationNeedForceReload : true , reloadApplication} ) ) ;
131+ const { rerender} = render (
132+ createForceReloadModalTestElement ( {
133+ doesApplicationNeedForceReload : true ,
134+ reloadApplication,
135+ wallClock : deterministicWallClock ,
136+ } ) ,
137+ ) ;
109138
110- rerender ( createForceReloadModalTestElement ( { doesApplicationNeedForceReload : true , reloadApplication} ) ) ;
111- rerender ( createForceReloadModalTestElement ( { doesApplicationNeedForceReload : true , reloadApplication} ) ) ;
139+ rerender (
140+ createForceReloadModalTestElement ( {
141+ doesApplicationNeedForceReload : true ,
142+ reloadApplication,
143+ wallClock : deterministicWallClock ,
144+ } ) ,
145+ ) ;
146+ rerender (
147+ createForceReloadModalTestElement ( {
148+ doesApplicationNeedForceReload : true ,
149+ reloadApplication,
150+ wallClock : deterministicWallClock ,
151+ } ) ,
152+ ) ;
112153
113154 expect ( usePrimaryModalState . getState ( ) . queue ) . toHaveLength ( 0 ) ;
114155 } ) ;
115156
116157 it ( 'opens the modal again after force reload status returns to false and then true' , ( ) => {
158+ const deterministicWallClock = createDeterministicWallClock ( { initialCurrentTimestampInMilliseconds : 0 } ) ;
117159 const reloadApplication = jest . fn ( ) ;
118- const { rerender} = render ( createForceReloadModalTestElement ( { doesApplicationNeedForceReload : false , reloadApplication} ) ) ;
160+ const { rerender} = render (
161+ createForceReloadModalTestElement ( {
162+ doesApplicationNeedForceReload : false ,
163+ reloadApplication,
164+ wallClock : deterministicWallClock ,
165+ } ) ,
166+ ) ;
119167
120- rerender ( createForceReloadModalTestElement ( { doesApplicationNeedForceReload : true , reloadApplication} ) ) ;
121- rerender ( createForceReloadModalTestElement ( { doesApplicationNeedForceReload : false , reloadApplication} ) ) ;
122- rerender ( createForceReloadModalTestElement ( { doesApplicationNeedForceReload : true , reloadApplication} ) ) ;
168+ rerender (
169+ createForceReloadModalTestElement ( {
170+ doesApplicationNeedForceReload : true ,
171+ reloadApplication,
172+ wallClock : deterministicWallClock ,
173+ } ) ,
174+ ) ;
175+ rerender (
176+ createForceReloadModalTestElement ( {
177+ doesApplicationNeedForceReload : false ,
178+ reloadApplication,
179+ wallClock : deterministicWallClock ,
180+ } ) ,
181+ ) ;
182+ rerender (
183+ createForceReloadModalTestElement ( {
184+ doesApplicationNeedForceReload : true ,
185+ reloadApplication,
186+ wallClock : deterministicWallClock ,
187+ } ) ,
188+ ) ;
123189
124190 expect ( usePrimaryModalState . getState ( ) . currentModalId ) . not . toBeNull ( ) ;
125191 expect ( usePrimaryModalState . getState ( ) . queue ) . toHaveLength ( 1 ) ;
@@ -141,11 +207,82 @@ describe('ForceReloadModal', () => {
141207 currentModalContent . onBgClick ( ) ;
142208 expect ( usePrimaryModalState . getState ( ) . currentModalId ) . toBe ( currentModalIdentifierBeforeBackgroundClick ) ;
143209
144- if ( ! currentModalContent . primaryAction ?. action ) {
145- throw new Error ( 'Primary reload action is missing' ) ;
146- }
210+ assert ( currentModalContent . primaryAction ?. action ) ;
211+ currentModalContent . primaryAction . action ( ) ;
212+
213+ expect ( reloadApplication ) . toHaveBeenCalledTimes ( 1 ) ;
214+ } ) ;
215+
216+ it ( 'reloads automatically after 60 seconds when no user action is performed' , ( ) => {
217+ const deterministicWallClock = createDeterministicWallClock ( { initialCurrentTimestampInMilliseconds : 0 } ) ;
218+ const reloadApplication = jest . fn ( ) ;
219+
220+ render (
221+ createForceReloadModalTestElement ( {
222+ doesApplicationNeedForceReload : true ,
223+ reloadApplication,
224+ wallClock : deterministicWallClock ,
225+ } ) ,
226+ ) ;
227+
228+ deterministicWallClock . advanceByMilliseconds ( forceReloadDelayInMilliseconds - 1 ) ;
229+ expect ( reloadApplication ) . not . toHaveBeenCalled ( ) ;
230+
231+ deterministicWallClock . advanceByMilliseconds ( 1 ) ;
232+ expect ( reloadApplication ) . toHaveBeenCalledTimes ( 1 ) ;
233+
234+ deterministicWallClock . advanceByMilliseconds ( forceReloadDelayInMilliseconds ) ;
235+ expect ( reloadApplication ) . toHaveBeenCalledTimes ( 1 ) ;
236+ } ) ;
237+
238+ it ( 'cancels automatic reload if force reload requirement is removed before timeout' , ( ) => {
239+ const deterministicWallClock = createDeterministicWallClock ( { initialCurrentTimestampInMilliseconds : 0 } ) ;
240+ const reloadApplication = jest . fn ( ) ;
241+ const { rerender} = render (
242+ createForceReloadModalTestElement ( {
243+ doesApplicationNeedForceReload : false ,
244+ reloadApplication,
245+ wallClock : deterministicWallClock ,
246+ } ) ,
247+ ) ;
248+
249+ rerender (
250+ createForceReloadModalTestElement ( {
251+ doesApplicationNeedForceReload : true ,
252+ reloadApplication,
253+ wallClock : deterministicWallClock ,
254+ } ) ,
255+ ) ;
256+ rerender (
257+ createForceReloadModalTestElement ( {
258+ doesApplicationNeedForceReload : false ,
259+ reloadApplication,
260+ wallClock : deterministicWallClock ,
261+ } ) ,
262+ ) ;
263+
264+ deterministicWallClock . advanceByMilliseconds ( forceReloadDelayInMilliseconds ) ;
265+
266+ expect ( reloadApplication ) . not . toHaveBeenCalled ( ) ;
267+ } ) ;
268+
269+ it ( 'does not trigger reload twice if the user clicks reload before timeout' , ( ) => {
270+ const deterministicWallClock = createDeterministicWallClock ( { initialCurrentTimestampInMilliseconds : 0 } ) ;
271+ const reloadApplication = jest . fn ( ) ;
272+
273+ render (
274+ createForceReloadModalTestElement ( {
275+ doesApplicationNeedForceReload : true ,
276+ reloadApplication,
277+ wallClock : deterministicWallClock ,
278+ } ) ,
279+ ) ;
280+
281+ const { currentModalContent} = usePrimaryModalState . getState ( ) ;
147282
283+ assert ( currentModalContent . primaryAction ?. action ) ;
148284 currentModalContent . primaryAction . action ( ) ;
285+ deterministicWallClock . advanceByMilliseconds ( forceReloadDelayInMilliseconds ) ;
149286
150287 expect ( reloadApplication ) . toHaveBeenCalledTimes ( 1 ) ;
151288 } ) ;
0 commit comments