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