@dhis2/app-service-offline 3.11.3 → 3.12.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cjs/__tests__/integration.test.js +51 -82
- package/build/cjs/index.js +0 -7
- package/build/cjs/lib/__tests__/cacheable-section-state.test.js +7 -14
- package/build/cjs/lib/__tests__/clear-sensitive-caches.test.js +17 -20
- package/build/cjs/lib/__tests__/network-status.test.js +135 -148
- package/build/cjs/lib/__tests__/offline-provider.test.js +12 -22
- package/build/cjs/lib/__tests__/use-cacheable-section.test.js +87 -98
- package/build/cjs/lib/__tests__/use-online-status-message.test.js +7 -14
- package/build/cjs/lib/cacheable-section-state.js +27 -38
- package/build/cjs/lib/cacheable-section.js +26 -27
- package/build/cjs/lib/clear-sensitive-caches.js +14 -24
- package/build/cjs/lib/dhis2-connection-status/dev-debug-log.js +1 -3
- package/build/cjs/lib/dhis2-connection-status/dhis2-connection-status.js +27 -58
- package/build/cjs/lib/dhis2-connection-status/dhis2-connection-status.test.js +287 -230
- package/build/cjs/lib/dhis2-connection-status/index.js +0 -1
- package/build/cjs/lib/dhis2-connection-status/is-ping-available.js +0 -6
- package/build/cjs/lib/dhis2-connection-status/is-ping-available.test.js +0 -1
- package/build/cjs/lib/dhis2-connection-status/smart-interval.js +35 -49
- package/build/cjs/lib/dhis2-connection-status/use-ping-query.js +4 -5
- package/build/cjs/lib/global-state-service.js +9 -27
- package/build/cjs/lib/network-status.js +10 -13
- package/build/cjs/lib/offline-interface.js +3 -14
- package/build/cjs/lib/offline-provider.js +1 -12
- package/build/cjs/lib/online-status-message.js +5 -17
- package/build/cjs/setupRTL.js +1 -1
- package/build/cjs/utils/__tests__/render-counter.test.js +3 -12
- package/build/cjs/utils/render-counter.js +2 -10
- package/build/cjs/utils/test-mocks.js +13 -18
- package/build/es/__tests__/integration.test.js +51 -74
- package/build/es/index.js +2 -2
- package/build/es/lib/__tests__/cacheable-section-state.test.js +2 -4
- package/build/es/lib/__tests__/clear-sensitive-caches.test.js +19 -16
- package/build/es/lib/__tests__/network-status.test.js +105 -114
- package/build/es/lib/__tests__/offline-provider.test.js +13 -15
- package/build/es/lib/__tests__/use-cacheable-section.test.js +69 -73
- package/build/es/lib/__tests__/use-online-status-message.test.js +2 -3
- package/build/es/lib/cacheable-section-state.js +25 -26
- package/build/es/lib/cacheable-section.js +23 -15
- package/build/es/lib/clear-sensitive-caches.js +13 -21
- package/build/es/lib/dhis2-connection-status/dev-debug-log.js +1 -3
- package/build/es/lib/dhis2-connection-status/dhis2-connection-status.js +26 -37
- package/build/es/lib/dhis2-connection-status/dhis2-connection-status.test.js +223 -159
- package/build/es/lib/dhis2-connection-status/is-ping-available.js +0 -5
- package/build/es/lib/dhis2-connection-status/smart-interval.js +34 -42
- package/build/es/lib/dhis2-connection-status/use-ping-query.js +6 -3
- package/build/es/lib/global-state-service.js +6 -12
- package/build/es/lib/network-status.js +10 -9
- package/build/es/lib/offline-interface.js +0 -3
- package/build/es/lib/offline-provider.js +0 -3
- package/build/es/lib/online-status-message.js +3 -2
- package/build/es/setupRTL.js +1 -1
- package/build/es/utils/__tests__/render-counter.test.js +2 -4
- package/build/es/utils/render-counter.js +1 -3
- package/build/es/utils/test-mocks.js +8 -9
- package/build/types/lib/cacheable-section.d.ts +1 -1
- package/build/types/lib/dhis2-connection-status/dhis2-connection-status.d.ts +1 -1
- package/build/types/lib/network-status.d.ts +1 -1
- package/build/types/lib/online-status-message.d.ts +1 -1
- package/build/types/types.d.ts +1 -1
- package/package.json +4 -4
|
@@ -1,35 +1,34 @@
|
|
|
1
1
|
import { ConfigProvider } from '@dhis2/app-service-config';
|
|
2
|
-
import { renderHook, act } from '@testing-library/react
|
|
2
|
+
import { renderHook, act } from '@testing-library/react';
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { mockOfflineInterface } from '../../utils/test-mocks';
|
|
5
5
|
import { OfflineProvider } from '../offline-provider';
|
|
6
6
|
import { getLastConnectedKey, useDhis2ConnectionStatus } from './dhis2-connection-status';
|
|
7
|
-
import { DEFAULT_INCREMENT_FACTOR, DEFAULT_MAX_DELAY_MS, DEFAULT_INITIAL_DELAY_MS } from './smart-interval';
|
|
7
|
+
import { DEFAULT_INCREMENT_FACTOR, DEFAULT_MAX_DELAY_MS, DEFAULT_INITIAL_DELAY_MS } from './smart-interval';
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
8
9
|
|
|
9
10
|
// important that this name starts with 'mock' to be hoisted correctly
|
|
10
11
|
const mockPing = jest.fn().mockImplementation(() => Promise.resolve());
|
|
11
12
|
jest.mock('./use-ping-query.ts', () => ({
|
|
12
13
|
usePingQuery: () => mockPing
|
|
13
14
|
}));
|
|
14
|
-
|
|
15
15
|
const failedPing = () => Promise.reject({
|
|
16
16
|
message: 'this is a network error',
|
|
17
17
|
type: 'network'
|
|
18
18
|
});
|
|
19
|
-
|
|
20
19
|
const FIRST_INTERVAL_MS = DEFAULT_INITIAL_DELAY_MS;
|
|
21
20
|
const SECOND_INTERVAL_MS = FIRST_INTERVAL_MS * DEFAULT_INCREMENT_FACTOR;
|
|
22
21
|
const THIRD_INTERVAL_MS = SECOND_INTERVAL_MS * DEFAULT_INCREMENT_FACTOR;
|
|
23
|
-
const FOURTH_INTERVAL_MS = THIRD_INTERVAL_MS * DEFAULT_INCREMENT_FACTOR;
|
|
22
|
+
const FOURTH_INTERVAL_MS = THIRD_INTERVAL_MS * DEFAULT_INCREMENT_FACTOR;
|
|
23
|
+
|
|
24
|
+
// Explanation: The length of the Nth interval is:
|
|
24
25
|
// initialDelay * incrementFactor ^ (N - 1)
|
|
25
26
|
// Using some algebra and the law of logs, the Nth interval
|
|
26
27
|
// which is longer than the max delay is:
|
|
27
28
|
// N >= (ln (maxDelay / initialDelay) / ln (incrementFactor)) + 1
|
|
28
29
|
// => then use Math.ceil to handle the 'greater than' effect
|
|
29
|
-
|
|
30
30
|
const INTERVALS_TO_REACH_MAX_DELAY = Math.ceil(Math.log(DEFAULT_MAX_DELAY_MS / DEFAULT_INITIAL_DELAY_MS) / Math.log(DEFAULT_INCREMENT_FACTOR) + 1);
|
|
31
|
-
|
|
32
|
-
const wrapper = (_ref) => {
|
|
31
|
+
const wrapper = _ref => {
|
|
33
32
|
let {
|
|
34
33
|
children
|
|
35
34
|
} = _ref;
|
|
@@ -49,6 +48,7 @@ const wrapper = (_ref) => {
|
|
|
49
48
|
offlineInterface: mockOfflineInterface
|
|
50
49
|
}, children));
|
|
51
50
|
};
|
|
51
|
+
|
|
52
52
|
/**
|
|
53
53
|
* Assert on the delay of the last time setTimeoutSpy was called with
|
|
54
54
|
* the `callbackAndRestart()` function in smartInterval.
|
|
@@ -58,11 +58,8 @@ const wrapper = (_ref) => {
|
|
|
58
58
|
* an assertion like:
|
|
59
59
|
* `expect(setTimeoutSpy).toHaveBeenLastCalledWith(..., expectedDelay)`
|
|
60
60
|
*/
|
|
61
|
-
|
|
62
|
-
|
|
63
61
|
const assertLastDelay = (setTimeoutSpy, expectedDelay) => {
|
|
64
62
|
const calls = setTimeoutSpy.mock.calls;
|
|
65
|
-
|
|
66
63
|
for (let i = calls.length - 1; i >= 0; i--) {
|
|
67
64
|
if (calls[i][0].name === 'callbackAndRestart') {
|
|
68
65
|
expect(calls[i][1]).toBe(expectedDelay);
|
|
@@ -70,7 +67,6 @@ const assertLastDelay = (setTimeoutSpy, expectedDelay) => {
|
|
|
70
67
|
}
|
|
71
68
|
}
|
|
72
69
|
};
|
|
73
|
-
|
|
74
70
|
const testCurrentDate = new Date('Fri, 03 Feb 2023 13:52:31 GMT');
|
|
75
71
|
beforeAll(() => {
|
|
76
72
|
jest.useFakeTimers();
|
|
@@ -82,8 +78,8 @@ beforeEach(() => {
|
|
|
82
78
|
jest.spyOn(document, 'hasFocus').mockReturnValue(true);
|
|
83
79
|
});
|
|
84
80
|
afterEach(() => {
|
|
85
|
-
jest.clearAllMocks();
|
|
86
|
-
|
|
81
|
+
jest.clearAllMocks();
|
|
82
|
+
// for lastConnected:
|
|
87
83
|
localStorage.clear();
|
|
88
84
|
});
|
|
89
85
|
afterAll(() => {
|
|
@@ -102,11 +98,11 @@ describe('initialization to the right values based on offline interface', () =>
|
|
|
102
98
|
expect(result.current.lastConnected).toBe(null);
|
|
103
99
|
});
|
|
104
100
|
test('when latestIsConnected is false', () => {
|
|
105
|
-
const customMockOfflineInterface = {
|
|
101
|
+
const customMockOfflineInterface = {
|
|
102
|
+
...mockOfflineInterface,
|
|
106
103
|
latestIsConnected: false
|
|
107
104
|
};
|
|
108
|
-
|
|
109
|
-
const customWrapper = (_ref2) => {
|
|
105
|
+
const customWrapper = _ref2 => {
|
|
110
106
|
let {
|
|
111
107
|
children
|
|
112
108
|
} = _ref2;
|
|
@@ -114,31 +110,31 @@ describe('initialization to the right values based on offline interface', () =>
|
|
|
114
110
|
offlineInterface: customMockOfflineInterface
|
|
115
111
|
}, children);
|
|
116
112
|
};
|
|
117
|
-
|
|
118
113
|
const {
|
|
119
114
|
result
|
|
120
115
|
} = renderHook(() => useDhis2ConnectionStatus(), {
|
|
121
116
|
wrapper: customWrapper
|
|
122
117
|
});
|
|
123
118
|
expect(result.current.isConnected).toBe(false);
|
|
124
|
-
expect(result.current.isDisconnected).toBe(true);
|
|
119
|
+
expect(result.current.isDisconnected).toBe(true);
|
|
120
|
+
// If localStorage is clear, sets 'lastConnected' to `now` as a best
|
|
125
121
|
// effort to provide useful information.
|
|
126
122
|
// There will be more detailed testing of lastConnected below
|
|
127
|
-
|
|
128
123
|
expect(result.current.lastConnected).toEqual(testCurrentDate);
|
|
129
|
-
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// This might happen in the unlikely circumstance that the provider
|
|
130
127
|
// renders before the offlineInterface has received a value for
|
|
131
128
|
// lastIsConnected. Normally, the ServerVersionProvider in the app
|
|
132
129
|
// adapter delays rendering the App Runtime provider (including the
|
|
133
130
|
// OfflineProvider) until the offline interface is ready, which should
|
|
134
131
|
// avoid this case.
|
|
135
|
-
|
|
136
132
|
test('when latestIsConnected is null', () => {
|
|
137
|
-
const customMockOfflineInterface = {
|
|
133
|
+
const customMockOfflineInterface = {
|
|
134
|
+
...mockOfflineInterface,
|
|
138
135
|
latestIsConnected: null
|
|
139
136
|
};
|
|
140
|
-
|
|
141
|
-
const customWrapper = (_ref3) => {
|
|
137
|
+
const customWrapper = _ref3 => {
|
|
142
138
|
let {
|
|
143
139
|
children
|
|
144
140
|
} = _ref3;
|
|
@@ -146,7 +142,6 @@ describe('initialization to the right values based on offline interface', () =>
|
|
|
146
142
|
offlineInterface: customMockOfflineInterface
|
|
147
143
|
}, children);
|
|
148
144
|
};
|
|
149
|
-
|
|
150
145
|
const {
|
|
151
146
|
result
|
|
152
147
|
} = renderHook(() => useDhis2ConnectionStatus(), {
|
|
@@ -167,50 +162,55 @@ describe('interval behavior', () => {
|
|
|
167
162
|
});
|
|
168
163
|
expect(result.current.isConnected).toBe(true);
|
|
169
164
|
expect(mockPing).not.toHaveBeenCalled();
|
|
170
|
-
assertLastDelay(setTimeoutSpy, FIRST_INTERVAL_MS);
|
|
165
|
+
assertLastDelay(setTimeoutSpy, FIRST_INTERVAL_MS);
|
|
171
166
|
|
|
167
|
+
// 500ms before first interval
|
|
172
168
|
jest.advanceTimersByTime(FIRST_INTERVAL_MS - 500);
|
|
173
|
-
expect(mockPing).not.toHaveBeenCalled();
|
|
174
|
-
|
|
169
|
+
expect(mockPing).not.toHaveBeenCalled();
|
|
170
|
+
// 500ms after first interval
|
|
175
171
|
jest.advanceTimersByTime(1000);
|
|
176
172
|
expect(mockPing).toHaveBeenCalledTimes(1);
|
|
177
|
-
assertLastDelay(setTimeoutSpy, SECOND_INTERVAL_MS);
|
|
173
|
+
assertLastDelay(setTimeoutSpy, SECOND_INTERVAL_MS);
|
|
178
174
|
|
|
175
|
+
// 500ms before second interval
|
|
179
176
|
jest.advanceTimersByTime(SECOND_INTERVAL_MS - 1000);
|
|
180
|
-
expect(mockPing).toHaveBeenCalledTimes(1);
|
|
181
|
-
|
|
177
|
+
expect(mockPing).toHaveBeenCalledTimes(1);
|
|
178
|
+
// 500ms after second interval
|
|
182
179
|
jest.advanceTimersByTime(1000);
|
|
183
180
|
expect(mockPing).toHaveBeenCalledTimes(2);
|
|
184
|
-
assertLastDelay(setTimeoutSpy, THIRD_INTERVAL_MS);
|
|
181
|
+
assertLastDelay(setTimeoutSpy, THIRD_INTERVAL_MS);
|
|
185
182
|
|
|
183
|
+
// 500ms before third interval
|
|
186
184
|
jest.advanceTimersByTime(THIRD_INTERVAL_MS - 1000);
|
|
187
|
-
expect(mockPing).toHaveBeenCalledTimes(2);
|
|
188
|
-
|
|
185
|
+
expect(mockPing).toHaveBeenCalledTimes(2);
|
|
186
|
+
// 500ms after third interval
|
|
189
187
|
jest.advanceTimersByTime(1000);
|
|
190
188
|
expect(mockPing).toHaveBeenCalledTimes(3);
|
|
191
|
-
assertLastDelay(setTimeoutSpy, FOURTH_INTERVAL_MS);
|
|
189
|
+
assertLastDelay(setTimeoutSpy, FOURTH_INTERVAL_MS);
|
|
190
|
+
|
|
191
|
+
// Run a number of intervals to reach the max delay -
|
|
192
192
|
// this number is calculated above to work for any default values.
|
|
193
193
|
// Since three have already elapsed, there will be some extra too
|
|
194
|
-
|
|
195
194
|
for (let i = 0; i < INTERVALS_TO_REACH_MAX_DELAY; i++) {
|
|
196
195
|
// Wrap in act to await async side effects of interval execution
|
|
197
196
|
// and pings
|
|
198
197
|
await act(async () => {
|
|
199
198
|
jest.runOnlyPendingTimers();
|
|
200
199
|
});
|
|
201
|
-
}
|
|
202
|
-
|
|
200
|
+
}
|
|
203
201
|
|
|
202
|
+
// Timeout should no longer be incrementing; max has been reached
|
|
204
203
|
expect(mockPing).toHaveBeenCalledTimes(3 + INTERVALS_TO_REACH_MAX_DELAY);
|
|
205
|
-
assertLastDelay(setTimeoutSpy, DEFAULT_MAX_DELAY_MS);
|
|
204
|
+
assertLastDelay(setTimeoutSpy, DEFAULT_MAX_DELAY_MS);
|
|
206
205
|
|
|
206
|
+
// Run a few more intervals to make sure it stays at max
|
|
207
207
|
for (let i = 0; i < 3; i++) {
|
|
208
208
|
await act(async () => {
|
|
209
209
|
jest.runOnlyPendingTimers();
|
|
210
210
|
});
|
|
211
|
-
}
|
|
212
|
-
|
|
211
|
+
}
|
|
213
212
|
|
|
213
|
+
// Expect continued use of the max delay
|
|
214
214
|
expect(mockPing).toHaveBeenCalledTimes(6 + INTERVALS_TO_REACH_MAX_DELAY);
|
|
215
215
|
assertLastDelay(setTimeoutSpy, DEFAULT_MAX_DELAY_MS);
|
|
216
216
|
});
|
|
@@ -218,55 +218,63 @@ describe('interval behavior', () => {
|
|
|
218
218
|
test('updates postpone pings', () => {
|
|
219
219
|
renderHook(() => useDhis2ConnectionStatus(), {
|
|
220
220
|
wrapper: wrapper
|
|
221
|
-
});
|
|
221
|
+
});
|
|
222
222
|
|
|
223
|
+
// get onUpdate function passed to mockOfflineInterface
|
|
223
224
|
const {
|
|
224
225
|
onUpdate
|
|
225
|
-
} = mockOfflineInterface.subscribeToDhis2ConnectionStatus.mock.calls[0][0];
|
|
226
|
+
} = mockOfflineInterface.subscribeToDhis2ConnectionStatus.mock.calls[0][0];
|
|
226
227
|
|
|
228
|
+
// invoke it at a few intervals, before pings are scheduled
|
|
227
229
|
for (let i = 0; i < 3; i++) {
|
|
228
230
|
jest.advanceTimersByTime(DEFAULT_INITIAL_DELAY_MS - 2000);
|
|
229
231
|
onUpdate({
|
|
230
232
|
isConnected: true
|
|
231
233
|
});
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
+
}
|
|
234
235
|
|
|
236
|
+
// expect ping mock not to have been called
|
|
235
237
|
expect(mockPing).not.toHaveBeenCalled;
|
|
236
238
|
});
|
|
237
239
|
test('if the status is the same, the ping delay is reset to the current', () => {
|
|
238
240
|
const setTimeoutSpy = jest.spyOn(window, 'setTimeout');
|
|
239
241
|
renderHook(() => useDhis2ConnectionStatus(), {
|
|
240
242
|
wrapper
|
|
241
|
-
});
|
|
243
|
+
});
|
|
242
244
|
|
|
245
|
+
// get onUpdate function passed to mockOfflineInterface
|
|
243
246
|
const {
|
|
244
247
|
onUpdate
|
|
245
|
-
} = mockOfflineInterface.subscribeToDhis2ConnectionStatus.mock.calls[0][0];
|
|
248
|
+
} = mockOfflineInterface.subscribeToDhis2ConnectionStatus.mock.calls[0][0];
|
|
246
249
|
|
|
250
|
+
// let two intervals pass to allow delay to increase
|
|
247
251
|
jest.advanceTimersByTime(FIRST_INTERVAL_MS + 50);
|
|
248
|
-
jest.advanceTimersByTime(SECOND_INTERVAL_MS);
|
|
252
|
+
jest.advanceTimersByTime(SECOND_INTERVAL_MS);
|
|
249
253
|
|
|
254
|
+
// ...delay should now be 'THIRD_INTERVAL_MS'
|
|
250
255
|
assertLastDelay(setTimeoutSpy, THIRD_INTERVAL_MS);
|
|
251
|
-
expect(mockPing).toHaveBeenCalledTimes(2);
|
|
252
|
-
// invoke it at a few intervals, before pings are scheduled
|
|
256
|
+
expect(mockPing).toHaveBeenCalledTimes(2);
|
|
253
257
|
|
|
258
|
+
// simulate updates from the SW/offline interface several times
|
|
259
|
+
// invoke it at a few intervals, before pings are scheduled
|
|
254
260
|
for (let i = 0; i < 3; i++) {
|
|
255
261
|
jest.advanceTimersByTime(THIRD_INTERVAL_MS - 2000);
|
|
256
262
|
onUpdate({
|
|
257
263
|
isConnected: true
|
|
258
264
|
});
|
|
259
|
-
}
|
|
265
|
+
}
|
|
260
266
|
|
|
267
|
+
// ping mock should STILL only have been called twice
|
|
268
|
+
expect(mockPing).toHaveBeenCalledTimes(2);
|
|
261
269
|
|
|
262
|
-
|
|
270
|
+
// the delay should still be THIRD_INTERVAL_MS
|
|
271
|
+
assertLastDelay(setTimeoutSpy, THIRD_INTERVAL_MS);
|
|
263
272
|
|
|
264
|
-
|
|
273
|
+
// The timer works as normal for the next tick --
|
|
265
274
|
// 500ms before the fourth interval:
|
|
266
|
-
|
|
267
275
|
jest.advanceTimersByTime(THIRD_INTERVAL_MS - 500);
|
|
268
|
-
expect(mockPing).toHaveBeenCalledTimes(2);
|
|
269
|
-
|
|
276
|
+
expect(mockPing).toHaveBeenCalledTimes(2);
|
|
277
|
+
// 500ms after the fourth interval
|
|
270
278
|
jest.advanceTimersByTime(1000);
|
|
271
279
|
expect(mockPing).toHaveBeenCalledTimes(3);
|
|
272
280
|
});
|
|
@@ -278,40 +286,45 @@ describe('interval behavior', () => {
|
|
|
278
286
|
result
|
|
279
287
|
} = renderHook(() => useDhis2ConnectionStatus(), {
|
|
280
288
|
wrapper: wrapper
|
|
281
|
-
});
|
|
282
|
-
|
|
289
|
+
});
|
|
290
|
+
// get onUpdate function passed to mockOfflineInterface
|
|
283
291
|
const {
|
|
284
292
|
onUpdate
|
|
285
293
|
} = mockOfflineInterface.subscribeToDhis2ConnectionStatus.mock.calls[0][0];
|
|
286
|
-
expect(result.current.isConnected).toBe(true);
|
|
287
|
-
// (Wrap in `act` to await async side effects of the executions)
|
|
294
|
+
expect(result.current.isConnected).toBe(true);
|
|
288
295
|
|
|
296
|
+
// Get to third interval
|
|
297
|
+
// (Wrap in `act` to await async side effects of the executions)
|
|
289
298
|
await act(async () => {
|
|
290
299
|
jest.runOnlyPendingTimers();
|
|
291
300
|
jest.runOnlyPendingTimers();
|
|
292
301
|
});
|
|
293
302
|
expect(mockPing).toHaveBeenCalledTimes(2);
|
|
294
|
-
assertLastDelay(setTimeoutSpy, THIRD_INTERVAL_MS);
|
|
303
|
+
assertLastDelay(setTimeoutSpy, THIRD_INTERVAL_MS);
|
|
295
304
|
|
|
305
|
+
// Trigger connection status change from offline interface
|
|
296
306
|
await act(async () => {
|
|
297
307
|
onUpdate({
|
|
298
308
|
isConnected: false
|
|
299
309
|
});
|
|
300
|
-
});
|
|
310
|
+
});
|
|
301
311
|
|
|
312
|
+
// Expect "first interval delay" to be set up
|
|
302
313
|
assertLastDelay(setTimeoutSpy, FIRST_INTERVAL_MS);
|
|
303
|
-
expect(result.current.isConnected).toBe(false);
|
|
314
|
+
expect(result.current.isConnected).toBe(false);
|
|
304
315
|
|
|
316
|
+
// Mock an error for the next ping to maintain `isConnected: false`
|
|
305
317
|
mockPing.mockImplementationOnce(() => Promise.reject({
|
|
306
318
|
message: 'this is a network error',
|
|
307
319
|
type: 'network'
|
|
308
|
-
}));
|
|
320
|
+
}));
|
|
321
|
+
// Advance past "first interval" -- make sure incrementing resumes
|
|
309
322
|
// while still 'isConnected: false'
|
|
310
|
-
|
|
311
323
|
await act(async () => {
|
|
312
324
|
jest.advanceTimersByTime(FIRST_INTERVAL_MS + 50);
|
|
313
|
-
});
|
|
325
|
+
});
|
|
314
326
|
|
|
327
|
+
// Expect another execution with the incremented interval
|
|
315
328
|
expect(mockPing).toHaveBeenCalledTimes(3);
|
|
316
329
|
assertLastDelay(setTimeoutSpy, SECOND_INTERVAL_MS);
|
|
317
330
|
});
|
|
@@ -322,13 +335,15 @@ describe('interval behavior', () => {
|
|
|
322
335
|
} = renderHook(() => useDhis2ConnectionStatus(), {
|
|
323
336
|
wrapper: wrapper
|
|
324
337
|
});
|
|
325
|
-
expect(result.current.isConnected).toBe(true);
|
|
338
|
+
expect(result.current.isConnected).toBe(true);
|
|
326
339
|
|
|
340
|
+
// Get to third interval
|
|
327
341
|
jest.runOnlyPendingTimers();
|
|
328
342
|
jest.runOnlyPendingTimers();
|
|
329
343
|
expect(mockPing).toHaveBeenCalledTimes(2);
|
|
330
|
-
assertLastDelay(setTimeoutSpy, THIRD_INTERVAL_MS);
|
|
344
|
+
assertLastDelay(setTimeoutSpy, THIRD_INTERVAL_MS);
|
|
331
345
|
|
|
346
|
+
// Mock a network error
|
|
332
347
|
mockPing.mockImplementationOnce(() => Promise.reject({
|
|
333
348
|
message: 'this is a network error',
|
|
334
349
|
type: 'network'
|
|
@@ -347,9 +362,10 @@ describe('pings aren\'t sent when the app is not focused; "standby behavior"', (
|
|
|
347
362
|
renderHook(() => useDhis2ConnectionStatus(), {
|
|
348
363
|
wrapper
|
|
349
364
|
});
|
|
350
|
-
window.dispatchEvent(new Event('blur'));
|
|
351
|
-
// it will enter a loop
|
|
365
|
+
window.dispatchEvent(new Event('blur'));
|
|
352
366
|
|
|
367
|
+
// This recursively executes all timers -- if it's not in standby,
|
|
368
|
+
// it will enter a loop
|
|
353
369
|
jest.runAllTimers();
|
|
354
370
|
expect(mockPing).not.toHaveBeenCalled();
|
|
355
371
|
});
|
|
@@ -357,8 +373,9 @@ describe('pings aren\'t sent when the app is not focused; "standby behavior"', (
|
|
|
357
373
|
jest.spyOn(document, 'hasFocus').mockReturnValue(false);
|
|
358
374
|
renderHook(() => useDhis2ConnectionStatus(), {
|
|
359
375
|
wrapper
|
|
360
|
-
});
|
|
376
|
+
});
|
|
361
377
|
|
|
378
|
+
// This recursively executes all timers
|
|
362
379
|
jest.runAllTimers();
|
|
363
380
|
expect(mockPing).not.toHaveBeenCalled();
|
|
364
381
|
});
|
|
@@ -366,27 +383,33 @@ describe('pings aren\'t sent when the app is not focused; "standby behavior"', (
|
|
|
366
383
|
renderHook(() => useDhis2ConnectionStatus(), {
|
|
367
384
|
wrapper
|
|
368
385
|
});
|
|
369
|
-
window.dispatchEvent(new Event('blur'));
|
|
370
|
-
|
|
386
|
+
window.dispatchEvent(new Event('blur'));
|
|
387
|
+
// wait half of the first interval
|
|
371
388
|
jest.advanceTimersByTime(FIRST_INTERVAL_MS / 2);
|
|
372
|
-
window.dispatchEvent(new Event('focus'));
|
|
389
|
+
window.dispatchEvent(new Event('focus'));
|
|
373
390
|
|
|
374
|
-
|
|
391
|
+
// wait for just over the second half of the first interval
|
|
392
|
+
jest.advanceTimersByTime(FIRST_INTERVAL_MS / 2 + 50);
|
|
375
393
|
|
|
394
|
+
// ping should execute normally
|
|
376
395
|
expect(mockPing).toHaveBeenCalledTimes(1);
|
|
377
396
|
});
|
|
378
397
|
test('if the app is defocused until after a scheduled ping, that ping is not sent until the app is refocused', () => {
|
|
379
398
|
renderHook(() => useDhis2ConnectionStatus(), {
|
|
380
399
|
wrapper
|
|
381
400
|
});
|
|
382
|
-
window.dispatchEvent(new Event('blur'));
|
|
401
|
+
window.dispatchEvent(new Event('blur'));
|
|
383
402
|
|
|
384
|
-
|
|
403
|
+
// wait for twice the first interval
|
|
404
|
+
jest.advanceTimersByTime(FIRST_INTERVAL_MS * 2);
|
|
385
405
|
|
|
386
|
-
|
|
406
|
+
// no pings should be sent since it's in standby
|
|
407
|
+
expect(mockPing).not.toHaveBeenCalled();
|
|
387
408
|
|
|
388
|
-
|
|
409
|
+
// refocus the page
|
|
410
|
+
window.dispatchEvent(new Event('focus'));
|
|
389
411
|
|
|
412
|
+
// ping should execute immediately
|
|
390
413
|
expect(mockPing).toHaveBeenCalledTimes(1);
|
|
391
414
|
});
|
|
392
415
|
});
|
|
@@ -395,8 +418,9 @@ describe('it pings when an offline event is detected', () => {
|
|
|
395
418
|
renderHook(() => useDhis2ConnectionStatus(), {
|
|
396
419
|
wrapper
|
|
397
420
|
});
|
|
398
|
-
window.dispatchEvent(new Event('offline'));
|
|
421
|
+
window.dispatchEvent(new Event('offline'));
|
|
399
422
|
|
|
423
|
+
// ping should execute immediately
|
|
400
424
|
expect(mockPing).toHaveBeenCalledTimes(1);
|
|
401
425
|
});
|
|
402
426
|
test('if the app is not focused, it does not ping immediately, but pings immediately when refocused', () => {
|
|
@@ -404,11 +428,13 @@ describe('it pings when an offline event is detected', () => {
|
|
|
404
428
|
wrapper
|
|
405
429
|
});
|
|
406
430
|
window.dispatchEvent(new Event('blur'));
|
|
407
|
-
window.dispatchEvent(new Event('offline'));
|
|
431
|
+
window.dispatchEvent(new Event('offline'));
|
|
408
432
|
|
|
409
|
-
|
|
410
|
-
|
|
433
|
+
// ping should not execute, but should be queued for refocus
|
|
434
|
+
expect(mockPing).toHaveBeenCalledTimes(0);
|
|
411
435
|
|
|
436
|
+
// upon refocus, the ping should execute immediately
|
|
437
|
+
// despite a full interval not elapsing
|
|
412
438
|
window.dispatchEvent(new Event('focus'));
|
|
413
439
|
expect(mockPing).toHaveBeenCalledTimes(1);
|
|
414
440
|
});
|
|
@@ -420,11 +446,12 @@ describe('it pings when an offline event is detected', () => {
|
|
|
420
446
|
});
|
|
421
447
|
window.dispatchEvent(new Event('blur'));
|
|
422
448
|
window.dispatchEvent(new Event('offline'));
|
|
423
|
-
window.dispatchEvent(new Event('focus'));
|
|
449
|
+
window.dispatchEvent(new Event('focus'));
|
|
450
|
+
// upon refocus, the ping should execute immediately
|
|
424
451
|
// despite a full interval not elapsing
|
|
452
|
+
expect(mockPing).toHaveBeenCalledTimes(1);
|
|
425
453
|
|
|
426
|
-
|
|
427
|
-
|
|
454
|
+
// The delay should be the initial again -- it shouldn't increment
|
|
428
455
|
assertLastDelay(setTimeoutSpy, FIRST_INTERVAL_MS);
|
|
429
456
|
});
|
|
430
457
|
test('same as previous, but interval is reset if status changes', async () => {
|
|
@@ -434,18 +461,21 @@ describe('it pings when an offline event is detected', () => {
|
|
|
434
461
|
} = renderHook(() => useDhis2ConnectionStatus(), {
|
|
435
462
|
wrapper: wrapper
|
|
436
463
|
});
|
|
437
|
-
expect(result.current.isConnected).toBe(true);
|
|
464
|
+
expect(result.current.isConnected).toBe(true);
|
|
438
465
|
|
|
466
|
+
// Get to third interval
|
|
439
467
|
jest.runOnlyPendingTimers();
|
|
440
468
|
jest.runOnlyPendingTimers();
|
|
441
469
|
expect(mockPing).toHaveBeenCalledTimes(2);
|
|
442
|
-
assertLastDelay(setTimeoutSpy, THIRD_INTERVAL_MS);
|
|
470
|
+
assertLastDelay(setTimeoutSpy, THIRD_INTERVAL_MS);
|
|
443
471
|
|
|
472
|
+
// Mock a network error
|
|
444
473
|
mockPing.mockImplementationOnce(() => Promise.reject({
|
|
445
474
|
message: 'this is a network error',
|
|
446
475
|
type: 'network'
|
|
447
|
-
}));
|
|
476
|
+
}));
|
|
448
477
|
|
|
478
|
+
// Blur, trigger 'offline' event, and refocus to trigger a ping
|
|
449
479
|
window.dispatchEvent(new Event('blur'));
|
|
450
480
|
window.dispatchEvent(new Event('offline'));
|
|
451
481
|
await act(async () => {
|
|
@@ -461,15 +491,18 @@ describe('it pings when an offline event is detected', () => {
|
|
|
461
491
|
wrapper
|
|
462
492
|
});
|
|
463
493
|
window.dispatchEvent(new Event('blur'));
|
|
464
|
-
window.dispatchEvent(new Event('offline'));
|
|
494
|
+
window.dispatchEvent(new Event('offline'));
|
|
465
495
|
|
|
496
|
+
// Elapse twice one interval - it should enter full standby
|
|
466
497
|
jest.advanceTimersByTime(FIRST_INTERVAL_MS * 2);
|
|
467
|
-
expect(mockPing).toHaveBeenCalledTimes(0);
|
|
468
|
-
// not just the offline event
|
|
498
|
+
expect(mockPing).toHaveBeenCalledTimes(0);
|
|
469
499
|
|
|
500
|
+
// Refocusing should trigger a ping from the full standby,
|
|
501
|
+
// not just the offline event
|
|
470
502
|
window.dispatchEvent(new Event('focus'));
|
|
471
|
-
expect(mockPing).toHaveBeenCalledTimes(1);
|
|
503
|
+
expect(mockPing).toHaveBeenCalledTimes(1);
|
|
472
504
|
|
|
505
|
+
// The delay should increment this time, as it would from normal standby
|
|
473
506
|
assertLastDelay(setTimeoutSpy, SECOND_INTERVAL_MS);
|
|
474
507
|
});
|
|
475
508
|
test('the same as previous, but the interval is reset if status has changed', async () => {
|
|
@@ -479,23 +512,27 @@ describe('it pings when an offline event is detected', () => {
|
|
|
479
512
|
} = renderHook(() => useDhis2ConnectionStatus(), {
|
|
480
513
|
wrapper: wrapper
|
|
481
514
|
});
|
|
482
|
-
expect(result.current.isConnected).toBe(true);
|
|
515
|
+
expect(result.current.isConnected).toBe(true);
|
|
483
516
|
|
|
517
|
+
// Get to third interval
|
|
484
518
|
jest.runOnlyPendingTimers();
|
|
485
519
|
jest.runOnlyPendingTimers();
|
|
486
520
|
expect(mockPing).toHaveBeenCalledTimes(2);
|
|
487
|
-
assertLastDelay(setTimeoutSpy, THIRD_INTERVAL_MS);
|
|
488
|
-
// it should enter full standby
|
|
521
|
+
assertLastDelay(setTimeoutSpy, THIRD_INTERVAL_MS);
|
|
489
522
|
|
|
523
|
+
// Blur and elapse twice the third interval --
|
|
524
|
+
// it should enter full standby
|
|
490
525
|
window.dispatchEvent(new Event('blur'));
|
|
491
526
|
window.dispatchEvent(new Event('offline'));
|
|
492
|
-
jest.advanceTimersByTime(THIRD_INTERVAL_MS * 2);
|
|
527
|
+
jest.advanceTimersByTime(THIRD_INTERVAL_MS * 2);
|
|
493
528
|
|
|
529
|
+
// Mock a network error for the next ping
|
|
494
530
|
mockPing.mockImplementationOnce(() => Promise.reject({
|
|
495
531
|
message: 'this is a network error',
|
|
496
532
|
type: 'network'
|
|
497
|
-
}));
|
|
533
|
+
}));
|
|
498
534
|
|
|
535
|
+
// Trigger a ping by refocusing
|
|
499
536
|
await act(async () => {
|
|
500
537
|
window.dispatchEvent(new Event('focus'));
|
|
501
538
|
});
|
|
@@ -523,10 +560,11 @@ describe('it pings when an online event is detected', () => {
|
|
|
523
560
|
window.dispatchEvent(new Event('online'));
|
|
524
561
|
window.dispatchEvent(new Event('offline'));
|
|
525
562
|
expect(mockPing).toHaveBeenCalledTimes(3);
|
|
526
|
-
window.dispatchEvent(new Event('online'));
|
|
563
|
+
window.dispatchEvent(new Event('online'));
|
|
564
|
+
// Another ping should NOT be sent immediately after this latest
|
|
527
565
|
// online event
|
|
528
|
-
|
|
529
|
-
|
|
566
|
+
expect(mockPing).toHaveBeenCalledTimes(3);
|
|
567
|
+
// (Not testing throttle time here because further pings may interfere)
|
|
530
568
|
});
|
|
531
569
|
});
|
|
532
570
|
describe('lastConnected status', () => {
|
|
@@ -536,21 +574,26 @@ describe('lastConnected status', () => {
|
|
|
536
574
|
} = renderHook(() => useDhis2ConnectionStatus(), {
|
|
537
575
|
wrapper: wrapper
|
|
538
576
|
});
|
|
539
|
-
expect(result.current.isConnected).toBe(true);
|
|
577
|
+
expect(result.current.isConnected).toBe(true);
|
|
540
578
|
|
|
541
|
-
|
|
579
|
+
// Mock a network error for the next ping
|
|
580
|
+
mockPing.mockImplementationOnce(failedPing);
|
|
542
581
|
|
|
582
|
+
// Trigger a ping (to fail and switch to disconnected)
|
|
543
583
|
await act(async () => {
|
|
544
584
|
jest.runOnlyPendingTimers();
|
|
545
585
|
});
|
|
546
|
-
expect(mockPing).toHaveBeenCalledTimes(1);
|
|
586
|
+
expect(mockPing).toHaveBeenCalledTimes(1);
|
|
547
587
|
|
|
588
|
+
// Expect 'disconnected' status now
|
|
548
589
|
expect(result.current.isConnected).toBe(false);
|
|
549
|
-
expect(result.current.isDisconnected).toBe(true);
|
|
590
|
+
expect(result.current.isDisconnected).toBe(true);
|
|
550
591
|
|
|
592
|
+
// Check localStorage for the dummy date
|
|
551
593
|
const localStorageDate = localStorage.getItem(getLastConnectedKey());
|
|
552
|
-
expect(localStorageDate).toBe(testCurrentDate.toUTCString());
|
|
594
|
+
expect(localStorageDate).toBe(testCurrentDate.toUTCString());
|
|
553
595
|
|
|
596
|
+
// Check hook return value
|
|
554
597
|
expect(result.current.lastConnected).toBeInstanceOf(Date);
|
|
555
598
|
expect(result.current.lastConnected).toEqual(testCurrentDate);
|
|
556
599
|
});
|
|
@@ -560,18 +603,22 @@ describe('lastConnected status', () => {
|
|
|
560
603
|
} = renderHook(() => useDhis2ConnectionStatus(), {
|
|
561
604
|
wrapper: wrapper
|
|
562
605
|
});
|
|
563
|
-
expect(result.current.isConnected).toBe(true);
|
|
606
|
+
expect(result.current.isConnected).toBe(true);
|
|
564
607
|
|
|
565
|
-
|
|
608
|
+
// Mock a network error for the next ping
|
|
609
|
+
mockPing.mockImplementationOnce(failedPing);
|
|
566
610
|
|
|
611
|
+
// Trigger an immediate ping (to fail and switch to disconnected)
|
|
567
612
|
await act(async () => {
|
|
568
613
|
jest.runOnlyPendingTimers();
|
|
569
614
|
});
|
|
570
|
-
expect(mockPing).toHaveBeenCalledTimes(1);
|
|
615
|
+
expect(mockPing).toHaveBeenCalledTimes(1);
|
|
571
616
|
|
|
617
|
+
// Verify hook return value
|
|
572
618
|
expect(result.current.isConnected).toBe(false);
|
|
573
|
-
expect(result.current.lastConnected).toEqual(testCurrentDate);
|
|
619
|
+
expect(result.current.lastConnected).toEqual(testCurrentDate);
|
|
574
620
|
|
|
621
|
+
// Trigger a successful ping to go back online
|
|
575
622
|
await act(async () => {
|
|
576
623
|
jest.runOnlyPendingTimers();
|
|
577
624
|
});
|
|
@@ -585,16 +632,19 @@ describe('lastConnected status', () => {
|
|
|
585
632
|
unmount
|
|
586
633
|
} = renderHook(() => useDhis2ConnectionStatus(), {
|
|
587
634
|
wrapper: wrapper
|
|
588
|
-
});
|
|
635
|
+
});
|
|
589
636
|
|
|
637
|
+
// Mock a network error for the next ping to trigger 'disconnected'
|
|
590
638
|
mockPing.mockImplementationOnce(failedPing);
|
|
591
639
|
await act(async () => {
|
|
592
640
|
jest.runOnlyPendingTimers();
|
|
593
641
|
});
|
|
594
|
-
expect(result.current.isConnected).toBe(false);
|
|
642
|
+
expect(result.current.isConnected).toBe(false);
|
|
595
643
|
|
|
596
|
-
|
|
644
|
+
// Unmount
|
|
645
|
+
unmount();
|
|
597
646
|
|
|
647
|
+
// Expect value to persist in localStorage
|
|
598
648
|
const localStorageDate = localStorage.getItem(getLastConnectedKey());
|
|
599
649
|
expect(localStorageDate).toBe(testCurrentDate.toUTCString());
|
|
600
650
|
});
|
|
@@ -605,22 +655,26 @@ describe('lastConnected status', () => {
|
|
|
605
655
|
} = renderHook(() => useDhis2ConnectionStatus(), {
|
|
606
656
|
wrapper
|
|
607
657
|
});
|
|
608
|
-
expect(result.current.isConnected).toBe(true);
|
|
658
|
+
expect(result.current.isConnected).toBe(true);
|
|
609
659
|
|
|
660
|
+
// Mock a network error for the next ping to trigger disconnected
|
|
610
661
|
mockPing.mockImplementationOnce(failedPing);
|
|
611
662
|
await act(async () => {
|
|
612
663
|
jest.runOnlyPendingTimers();
|
|
613
664
|
});
|
|
614
|
-
expect(result.current.isConnected).toBe(false);
|
|
665
|
+
expect(result.current.isConnected).toBe(false);
|
|
615
666
|
|
|
667
|
+
// Check localStorage for the dummy date
|
|
616
668
|
const localStorageDate = localStorage.getItem(getLastConnectedKey());
|
|
617
|
-
expect(localStorageDate).toBe(testCurrentDate.toUTCString());
|
|
669
|
+
expect(localStorageDate).toBe(testCurrentDate.toUTCString());
|
|
618
670
|
|
|
671
|
+
// Trigger another ping to go back to connected
|
|
619
672
|
await act(async () => {
|
|
620
673
|
jest.runOnlyPendingTimers();
|
|
621
674
|
});
|
|
622
|
-
expect(result.current.isConnected).toBe(true);
|
|
675
|
+
expect(result.current.isConnected).toBe(true);
|
|
623
676
|
|
|
677
|
+
// Unmount and expect localStorage to be clear for next session
|
|
624
678
|
unmount();
|
|
625
679
|
expect(localStorage.getItem(getLastConnectedKey())).toBe(null);
|
|
626
680
|
});
|
|
@@ -628,36 +682,38 @@ describe('lastConnected status', () => {
|
|
|
628
682
|
test('it sets lastConnected to `now` if nothing is found in localStorage', async () => {
|
|
629
683
|
// use a custom offlineInterface with `latestIsConnected: false`
|
|
630
684
|
// to initialize the `isConnected` state to false
|
|
631
|
-
const customMockOfflineInterface = {
|
|
685
|
+
const customMockOfflineInterface = {
|
|
686
|
+
...mockOfflineInterface,
|
|
632
687
|
latestIsConnected: false
|
|
633
688
|
};
|
|
634
|
-
|
|
635
|
-
const customWrapper = (_ref4) => {
|
|
689
|
+
const customWrapper = _ref4 => {
|
|
636
690
|
let {
|
|
637
691
|
children
|
|
638
692
|
} = _ref4;
|
|
639
693
|
return /*#__PURE__*/React.createElement(OfflineProvider, {
|
|
640
694
|
offlineInterface: customMockOfflineInterface
|
|
641
695
|
}, children);
|
|
642
|
-
};
|
|
643
|
-
|
|
696
|
+
};
|
|
644
697
|
|
|
698
|
+
// render hook with custom wrapper
|
|
645
699
|
renderHook(() => useDhis2ConnectionStatus(), {
|
|
646
700
|
wrapper: customWrapper
|
|
647
|
-
});
|
|
701
|
+
});
|
|
648
702
|
|
|
703
|
+
// expect correct lastConnected time (mocked Date.now())
|
|
649
704
|
expect(localStorage.getItem(getLastConnectedKey())).toBe(testCurrentDate.toUTCString());
|
|
650
705
|
});
|
|
651
706
|
test('if a value is already in localStorage, it uses that without overwriting', async () => {
|
|
652
707
|
// seed localStorage with an imaginary 'lastConnected' value from last session
|
|
653
708
|
const testPreviousDate = new Date('2023-01-01');
|
|
654
|
-
localStorage.setItem(getLastConnectedKey(), testPreviousDate.toUTCString());
|
|
709
|
+
localStorage.setItem(getLastConnectedKey(), testPreviousDate.toUTCString());
|
|
655
710
|
|
|
656
|
-
|
|
711
|
+
// render hook with custom wrapper
|
|
712
|
+
const customMockOfflineInterface = {
|
|
713
|
+
...mockOfflineInterface,
|
|
657
714
|
latestIsConnected: false
|
|
658
715
|
};
|
|
659
|
-
|
|
660
|
-
const customWrapper = (_ref5) => {
|
|
716
|
+
const customWrapper = _ref5 => {
|
|
661
717
|
let {
|
|
662
718
|
children
|
|
663
719
|
} = _ref5;
|
|
@@ -665,29 +721,30 @@ describe('lastConnected status', () => {
|
|
|
665
721
|
offlineInterface: customMockOfflineInterface
|
|
666
722
|
}, children);
|
|
667
723
|
};
|
|
668
|
-
|
|
669
724
|
const {
|
|
670
725
|
result
|
|
671
726
|
} = renderHook(() => useDhis2ConnectionStatus(), {
|
|
672
727
|
wrapper: customWrapper
|
|
673
|
-
});
|
|
728
|
+
});
|
|
674
729
|
|
|
730
|
+
// On render, the hook should retain last connected
|
|
675
731
|
expect(result.current.lastConnected).not.toBe(null);
|
|
676
|
-
expect(result.current.lastConnected).toEqual(testPreviousDate);
|
|
677
|
-
|
|
732
|
+
expect(result.current.lastConnected).toEqual(testPreviousDate);
|
|
733
|
+
// should be the same in localStorage too
|
|
678
734
|
expect(localStorage.getItem(getLastConnectedKey())).toBe(testPreviousDate.toUTCString());
|
|
679
735
|
});
|
|
680
736
|
});
|
|
681
737
|
test("it doesn't change lastConnected if already disconnected", async () => {
|
|
682
738
|
// seed localStorage with an imaginary 'lastConnected' value from last session
|
|
683
739
|
const testPreviousDate = new Date('2023-01-01');
|
|
684
|
-
localStorage.setItem(getLastConnectedKey(), testPreviousDate.toUTCString());
|
|
740
|
+
localStorage.setItem(getLastConnectedKey(), testPreviousDate.toUTCString());
|
|
685
741
|
|
|
686
|
-
|
|
742
|
+
// render hook with custom wrapper
|
|
743
|
+
const customMockOfflineInterface = {
|
|
744
|
+
...mockOfflineInterface,
|
|
687
745
|
latestIsConnected: false
|
|
688
746
|
};
|
|
689
|
-
|
|
690
|
-
const customWrapper = (_ref6) => {
|
|
747
|
+
const customWrapper = _ref6 => {
|
|
691
748
|
let {
|
|
692
749
|
children
|
|
693
750
|
} = _ref6;
|
|
@@ -706,26 +763,29 @@ describe('lastConnected status', () => {
|
|
|
706
763
|
offlineInterface: customMockOfflineInterface
|
|
707
764
|
}, children));
|
|
708
765
|
};
|
|
709
|
-
|
|
710
766
|
const {
|
|
711
767
|
result
|
|
712
768
|
} = renderHook(() => useDhis2ConnectionStatus(), {
|
|
713
769
|
wrapper: customWrapper
|
|
714
|
-
});
|
|
770
|
+
});
|
|
715
771
|
|
|
716
|
-
|
|
772
|
+
// As in previous test, the hook should retain last connected
|
|
773
|
+
expect(result.current.lastConnected).toEqual(testPreviousDate);
|
|
717
774
|
|
|
775
|
+
// Mock a network error for the next ping and trigger
|
|
718
776
|
mockPing.mockImplementationOnce(failedPing);
|
|
719
777
|
await act(async () => {
|
|
720
778
|
jest.runOnlyPendingTimers();
|
|
721
779
|
});
|
|
722
|
-
expect(mockPing).toHaveBeenCalledTimes(1);
|
|
780
|
+
expect(mockPing).toHaveBeenCalledTimes(1);
|
|
723
781
|
|
|
724
|
-
|
|
782
|
+
// Expect the same lastConnected as before
|
|
783
|
+
expect(result.current.lastConnected).toEqual(testPreviousDate);
|
|
784
|
+
// should be the same in localStorage too
|
|
785
|
+
expect(localStorage.getItem(getLastConnectedKey())).toBe(testPreviousDate.toUTCString());
|
|
725
786
|
|
|
726
|
-
|
|
787
|
+
// Verify the same with a signal from the service worker
|
|
727
788
|
// get onUpdate function passed to mockOfflineInterface
|
|
728
|
-
|
|
729
789
|
const {
|
|
730
790
|
onUpdate
|
|
731
791
|
} = mockOfflineInterface.subscribeToDhis2ConnectionStatus.mock.calls[0][0];
|
|
@@ -733,8 +793,9 @@ describe('lastConnected status', () => {
|
|
|
733
793
|
onUpdate({
|
|
734
794
|
isConnected: false
|
|
735
795
|
});
|
|
736
|
-
});
|
|
796
|
+
});
|
|
737
797
|
|
|
798
|
+
// Expect the same lastConnected as before
|
|
738
799
|
expect(result.current.lastConnected).toEqual(testPreviousDate);
|
|
739
800
|
});
|
|
740
801
|
test('lastConnected is saved specifically to an app if a name is provided', async () => {
|
|
@@ -742,13 +803,14 @@ describe('lastConnected status', () => {
|
|
|
742
803
|
const testAppName = 'test-app-name';
|
|
743
804
|
const lastConnectedKey = getLastConnectedKey(testAppName);
|
|
744
805
|
const testPreviousDate = new Date('2023-01-01');
|
|
745
|
-
localStorage.setItem(lastConnectedKey, testPreviousDate.toUTCString());
|
|
806
|
+
localStorage.setItem(lastConnectedKey, testPreviousDate.toUTCString());
|
|
746
807
|
|
|
747
|
-
|
|
808
|
+
// render hook with custom wrapper to start disconnected with app name
|
|
809
|
+
const customMockOfflineInterface = {
|
|
810
|
+
...mockOfflineInterface,
|
|
748
811
|
latestIsConnected: false
|
|
749
812
|
};
|
|
750
|
-
|
|
751
|
-
const customWrapper = (_ref7) => {
|
|
813
|
+
const customWrapper = _ref7 => {
|
|
752
814
|
let {
|
|
753
815
|
children
|
|
754
816
|
} = _ref7;
|
|
@@ -768,15 +830,16 @@ describe('lastConnected status', () => {
|
|
|
768
830
|
offlineInterface: customMockOfflineInterface
|
|
769
831
|
}, children));
|
|
770
832
|
};
|
|
771
|
-
|
|
772
833
|
const {
|
|
773
834
|
result
|
|
774
835
|
} = renderHook(() => useDhis2ConnectionStatus(), {
|
|
775
836
|
wrapper: customWrapper
|
|
776
|
-
});
|
|
837
|
+
});
|
|
777
838
|
|
|
778
|
-
|
|
839
|
+
// Expect previous value to be read correctly
|
|
840
|
+
expect(result.current.lastConnected).toEqual(testPreviousDate);
|
|
779
841
|
|
|
842
|
+
// Go to connected then disconnected again to generate a new date
|
|
780
843
|
await act(async () => {
|
|
781
844
|
jest.runOnlyPendingTimers();
|
|
782
845
|
});
|
|
@@ -787,15 +850,16 @@ describe('lastConnected status', () => {
|
|
|
787
850
|
await act(async () => {
|
|
788
851
|
jest.runOnlyPendingTimers();
|
|
789
852
|
});
|
|
790
|
-
expect(result.current.isConnected).toBe(false);
|
|
791
|
-
|
|
792
|
-
expect(result.current.lastConnected).toEqual(testCurrentDate);
|
|
853
|
+
expect(result.current.isConnected).toBe(false);
|
|
854
|
+
// Note the new date:
|
|
855
|
+
expect(result.current.lastConnected).toEqual(testCurrentDate);
|
|
793
856
|
|
|
857
|
+
// Verify localStorage
|
|
794
858
|
expect(localStorage.getItem(lastConnectedKey)).toBe(testCurrentDate.toUTCString());
|
|
795
859
|
});
|
|
796
860
|
});
|
|
797
861
|
describe("when the /api/ping endpoint isn't supported", () => {
|
|
798
|
-
const customWrapper =
|
|
862
|
+
const customWrapper = _ref8 => {
|
|
799
863
|
let {
|
|
800
864
|
children
|
|
801
865
|
} = _ref8;
|
|
@@ -815,7 +879,6 @@ describe("when the /api/ping endpoint isn't supported", () => {
|
|
|
815
879
|
offlineInterface: mockOfflineInterface
|
|
816
880
|
}, children));
|
|
817
881
|
};
|
|
818
|
-
|
|
819
882
|
test("pings aren't sent", async () => {
|
|
820
883
|
const setTimeoutSpy = jest.spyOn(window, 'setTimeout');
|
|
821
884
|
renderHook(() => useDhis2ConnectionStatus(), {
|
|
@@ -832,13 +895,14 @@ describe("when the /api/ping endpoint isn't supported", () => {
|
|
|
832
895
|
result
|
|
833
896
|
} = renderHook(() => useDhis2ConnectionStatus(), {
|
|
834
897
|
wrapper: wrapper
|
|
835
|
-
});
|
|
836
|
-
|
|
898
|
+
});
|
|
899
|
+
// get onUpdate function passed to mockOfflineInterface
|
|
837
900
|
const {
|
|
838
901
|
onUpdate
|
|
839
902
|
} = mockOfflineInterface.subscribeToDhis2ConnectionStatus.mock.calls[0][0];
|
|
840
|
-
expect(result.current.isConnected).toBe(true);
|
|
903
|
+
expect(result.current.isConnected).toBe(true);
|
|
841
904
|
|
|
905
|
+
// Trigger connection status change from offline interface
|
|
842
906
|
await act(async () => {
|
|
843
907
|
onUpdate({
|
|
844
908
|
isConnected: false
|