@osimatic/helpers-js 1.4.25 → 1.4.27
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/.claude/settings.local.json +1 -1
- package/duration.js +174 -125
- package/file.js +19 -4
- package/google_charts.js +2 -1
- package/location.js +5 -1
- package/media.js +6 -6
- package/multi_files_input.js +3 -1
- package/multiple_action_in_table.js +27 -11
- package/package.json +2 -1
- package/paging.js +2 -2
- package/tests/__mocks__/socket.io-client.js +13 -0
- package/tests/count_down.test.js +580 -0
- package/tests/details_sub_array.test.js +367 -0
- package/tests/file.test.js +210 -0
- package/tests/flash_message.test.js +297 -0
- package/tests/form_date.test.js +1142 -0
- package/tests/form_helper.test.js +780 -130
- package/tests/google_charts.test.js +768 -0
- package/tests/google_maps.test.js +655 -0
- package/tests/google_recaptcha.test.js +441 -0
- package/tests/import_from_csv.test.js +797 -0
- package/tests/list_box.test.js +255 -0
- package/tests/location.test.js +86 -0
- package/tests/media.test.js +15 -0
- package/tests/multi_files_input.test.js +1015 -0
- package/tests/multiple_action_in_table.test.js +477 -0
- package/tests/paging.test.js +646 -0
- package/tests/select_all.test.js +360 -0
- package/tests/sortable_list.test.js +602 -0
- package/tests/string.test.js +16 -0
- package/tests/web_rtc.test.js +458 -0
- package/tests/web_socket.test.js +538 -0
- package/tmpclaude-00a6-cwd +0 -1
- package/tmpclaude-0526-cwd +0 -1
- package/tmpclaude-0973-cwd +0 -1
- package/tmpclaude-0b61-cwd +0 -1
- package/tmpclaude-0fa4-cwd +0 -1
- package/tmpclaude-104f-cwd +0 -1
- package/tmpclaude-1468-cwd +0 -1
- package/tmpclaude-146f-cwd +0 -1
- package/tmpclaude-223d-cwd +0 -1
- package/tmpclaude-2330-cwd +0 -1
- package/tmpclaude-282a-cwd +0 -1
- package/tmpclaude-2846-cwd +0 -1
- package/tmpclaude-28a6-cwd +0 -1
- package/tmpclaude-2b5a-cwd +0 -1
- package/tmpclaude-2def-cwd +0 -1
- package/tmpclaude-324b-cwd +0 -1
- package/tmpclaude-35d3-cwd +0 -1
- package/tmpclaude-3906-cwd +0 -1
- package/tmpclaude-3b32-cwd +0 -1
- package/tmpclaude-3da9-cwd +0 -1
- package/tmpclaude-3dc3-cwd +0 -1
- package/tmpclaude-3e3b-cwd +0 -1
- package/tmpclaude-43b6-cwd +0 -1
- package/tmpclaude-4495-cwd +0 -1
- package/tmpclaude-462f-cwd +0 -1
- package/tmpclaude-4aa8-cwd +0 -1
- package/tmpclaude-4b29-cwd +0 -1
- package/tmpclaude-4db5-cwd +0 -1
- package/tmpclaude-4e01-cwd +0 -1
- package/tmpclaude-5101-cwd +0 -1
- package/tmpclaude-524f-cwd +0 -1
- package/tmpclaude-5636-cwd +0 -1
- package/tmpclaude-5cdd-cwd +0 -1
- package/tmpclaude-5f1f-cwd +0 -1
- package/tmpclaude-6078-cwd +0 -1
- package/tmpclaude-622e-cwd +0 -1
- package/tmpclaude-6802-cwd +0 -1
- package/tmpclaude-6e36-cwd +0 -1
- package/tmpclaude-7793-cwd +0 -1
- package/tmpclaude-7f96-cwd +0 -1
- package/tmpclaude-8566-cwd +0 -1
- package/tmpclaude-8874-cwd +0 -1
- package/tmpclaude-8915-cwd +0 -1
- package/tmpclaude-8c8b-cwd +0 -1
- package/tmpclaude-94df-cwd +0 -1
- package/tmpclaude-9859-cwd +0 -1
- package/tmpclaude-9ac5-cwd +0 -1
- package/tmpclaude-9f18-cwd +0 -1
- package/tmpclaude-a202-cwd +0 -1
- package/tmpclaude-a741-cwd +0 -1
- package/tmpclaude-ab5f-cwd +0 -1
- package/tmpclaude-b008-cwd +0 -1
- package/tmpclaude-b0a1-cwd +0 -1
- package/tmpclaude-b63d-cwd +0 -1
- package/tmpclaude-b681-cwd +0 -1
- package/tmpclaude-b72d-cwd +0 -1
- package/tmpclaude-b92f-cwd +0 -1
- package/tmpclaude-bc49-cwd +0 -1
- package/tmpclaude-bc50-cwd +0 -1
- package/tmpclaude-bccf-cwd +0 -1
- package/tmpclaude-be55-cwd +0 -1
- package/tmpclaude-c228-cwd +0 -1
- package/tmpclaude-c717-cwd +0 -1
- package/tmpclaude-c7ce-cwd +0 -1
- package/tmpclaude-cf3e-cwd +0 -1
- package/tmpclaude-d142-cwd +0 -1
- package/tmpclaude-d5bc-cwd +0 -1
- package/tmpclaude-d6ae-cwd +0 -1
- package/tmpclaude-d77a-cwd +0 -1
- package/tmpclaude-d8da-cwd +0 -1
- package/tmpclaude-dbdb-cwd +0 -1
- package/tmpclaude-de61-cwd +0 -1
- package/tmpclaude-de81-cwd +0 -1
- package/tmpclaude-df9d-cwd +0 -1
- package/tmpclaude-e786-cwd +0 -1
- package/tmpclaude-f01d-cwd +0 -1
- package/tmpclaude-f2a9-cwd +0 -1
- package/tmpclaude-fc36-cwd +0 -1
- package/tmpclaude-ffef-cwd +0 -1
|
@@ -0,0 +1,580 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
const { CountDown } = require('../count_down');
|
|
5
|
+
|
|
6
|
+
describe('CountDown', () => {
|
|
7
|
+
let mockDiv;
|
|
8
|
+
let mockLink;
|
|
9
|
+
let mockCountDownCurrent;
|
|
10
|
+
let mockCountDownText;
|
|
11
|
+
let intervalCallback;
|
|
12
|
+
let clickCallback;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
// Setup fake timers
|
|
16
|
+
jest.useFakeTimers();
|
|
17
|
+
|
|
18
|
+
// Setup global labels
|
|
19
|
+
global.labelNextUpdate = 'Next Update';
|
|
20
|
+
global.labelDoUpdate = 'Refresh Now';
|
|
21
|
+
|
|
22
|
+
// Mock link element
|
|
23
|
+
mockLink = {
|
|
24
|
+
click: jest.fn(function(callback) {
|
|
25
|
+
clickCallback = callback;
|
|
26
|
+
return this;
|
|
27
|
+
}),
|
|
28
|
+
attr: jest.fn().mockReturnThis(),
|
|
29
|
+
button: jest.fn().mockReturnThis(),
|
|
30
|
+
prop: jest.fn(() => false),
|
|
31
|
+
length: 1
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Mock progress bar current
|
|
35
|
+
mockCountDownCurrent = {
|
|
36
|
+
width: jest.fn(),
|
|
37
|
+
length: 1
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Mock text element
|
|
41
|
+
mockCountDownText = {
|
|
42
|
+
html: jest.fn(),
|
|
43
|
+
length: 1
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Mock main div
|
|
47
|
+
mockDiv = {
|
|
48
|
+
length: 1,
|
|
49
|
+
append: jest.fn().mockReturnThis(),
|
|
50
|
+
find: jest.fn((selector) => {
|
|
51
|
+
if (selector === '.count_down_link a') {
|
|
52
|
+
return mockLink;
|
|
53
|
+
}
|
|
54
|
+
if (selector === '.count_down_current') {
|
|
55
|
+
return mockCountDownCurrent;
|
|
56
|
+
}
|
|
57
|
+
if (selector === '.count_down_text') {
|
|
58
|
+
return mockCountDownText;
|
|
59
|
+
}
|
|
60
|
+
return { length: 0 };
|
|
61
|
+
})
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Spy on setInterval to capture callback
|
|
65
|
+
const originalSetInterval = global.setInterval;
|
|
66
|
+
jest.spyOn(global, 'setInterval').mockImplementation((callback, delay) => {
|
|
67
|
+
intervalCallback = callback;
|
|
68
|
+
return originalSetInterval(callback, delay);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
afterEach(() => {
|
|
73
|
+
jest.clearAllTimers();
|
|
74
|
+
jest.restoreAllMocks();
|
|
75
|
+
delete global.labelNextUpdate;
|
|
76
|
+
delete global.labelDoUpdate;
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe('constructor', () => {
|
|
80
|
+
test('should return early when div has no length', () => {
|
|
81
|
+
const emptyDiv = { length: 0 };
|
|
82
|
+
const countDown = new CountDown(emptyDiv, jest.fn());
|
|
83
|
+
|
|
84
|
+
expect(countDown.div).toBeUndefined();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('should create countdown UI elements', () => {
|
|
88
|
+
const callback = jest.fn((completeCallback) => {
|
|
89
|
+
completeCallback();
|
|
90
|
+
});
|
|
91
|
+
const countDown = new CountDown(mockDiv, callback);
|
|
92
|
+
|
|
93
|
+
expect(mockDiv.append).toHaveBeenCalledTimes(4);
|
|
94
|
+
expect(mockDiv.append).toHaveBeenCalledWith('<div class="count_down_title">Next Update</div>');
|
|
95
|
+
expect(mockDiv.append).toHaveBeenCalledWith('<div class="count_down_progress"><div class="count_down_current"></div></div>');
|
|
96
|
+
expect(mockDiv.append).toHaveBeenCalledWith('<div class="count_down_text"></div>');
|
|
97
|
+
expect(mockDiv.append).toHaveBeenCalledWith('<div class="count_down_link"><a href="#" data-loading-text="<i class=\'fa fa-circle-notch fa-spin\'></i>">Refresh Now</a></div>');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('should initialize properties', () => {
|
|
101
|
+
const callback = jest.fn((completeCallback) => {
|
|
102
|
+
completeCallback();
|
|
103
|
+
});
|
|
104
|
+
const countDown = new CountDown(mockDiv, callback);
|
|
105
|
+
|
|
106
|
+
expect(countDown.div).toBe(mockDiv);
|
|
107
|
+
expect(countDown.callbackOnRefreshData).toBe(callback);
|
|
108
|
+
expect(countDown.alreadyMakingRequest).toBe(false);
|
|
109
|
+
expect(countDown.secondsBefRefresh).toBe(10);
|
|
110
|
+
expect(countDown.refreshIntervalMillis).toBe(60);
|
|
111
|
+
expect(countDown.currentMillis).toBe(0);
|
|
112
|
+
expect(countDown.currentSecond).toBe(0);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('should set up click handler on refresh link', () => {
|
|
116
|
+
const callback = jest.fn((completeCallback) => {
|
|
117
|
+
completeCallback();
|
|
118
|
+
});
|
|
119
|
+
const countDown = new CountDown(mockDiv, callback);
|
|
120
|
+
|
|
121
|
+
expect(mockLink.click).toHaveBeenCalled();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('should handle click on refresh link', () => {
|
|
125
|
+
const callback = jest.fn((completeCallback) => {
|
|
126
|
+
completeCallback();
|
|
127
|
+
});
|
|
128
|
+
const countDown = new CountDown(mockDiv, callback);
|
|
129
|
+
|
|
130
|
+
// Execute the click callback
|
|
131
|
+
const result = clickCallback.call(mockLink);
|
|
132
|
+
|
|
133
|
+
expect(result).toBe(false);
|
|
134
|
+
expect(callback).toHaveBeenCalled();
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test('should start interval timer', () => {
|
|
138
|
+
const callback = jest.fn((completeCallback) => {
|
|
139
|
+
completeCallback();
|
|
140
|
+
});
|
|
141
|
+
const countDown = new CountDown(mockDiv, callback);
|
|
142
|
+
|
|
143
|
+
expect(setInterval).toHaveBeenCalledWith(expect.any(Function), 60);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test('should call refreshData immediately after construction', () => {
|
|
147
|
+
const callback = jest.fn((completeCallback) => {
|
|
148
|
+
completeCallback();
|
|
149
|
+
});
|
|
150
|
+
const countDown = new CountDown(mockDiv, callback);
|
|
151
|
+
|
|
152
|
+
expect(callback).toHaveBeenCalled();
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('should handle missing refresh link', () => {
|
|
156
|
+
const mockEmptyLink = {
|
|
157
|
+
length: 0,
|
|
158
|
+
attr: jest.fn().mockReturnThis(),
|
|
159
|
+
button: jest.fn().mockReturnThis()
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
mockDiv.find = jest.fn((selector) => {
|
|
163
|
+
if (selector === '.count_down_link a') {
|
|
164
|
+
return mockEmptyLink;
|
|
165
|
+
}
|
|
166
|
+
if (selector === '.count_down_current') {
|
|
167
|
+
return mockCountDownCurrent;
|
|
168
|
+
}
|
|
169
|
+
if (selector === '.count_down_text') {
|
|
170
|
+
return mockCountDownText;
|
|
171
|
+
}
|
|
172
|
+
return { length: 0 };
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const callback = jest.fn((completeCallback) => {
|
|
176
|
+
completeCallback();
|
|
177
|
+
});
|
|
178
|
+
expect(() => {
|
|
179
|
+
new CountDown(mockDiv, callback);
|
|
180
|
+
}).not.toThrow();
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
describe('setCallbackOnRefreshData', () => {
|
|
185
|
+
test('should update callback', () => {
|
|
186
|
+
const callback1 = jest.fn();
|
|
187
|
+
const callback2 = jest.fn();
|
|
188
|
+
const countDown = new CountDown(mockDiv, callback1);
|
|
189
|
+
|
|
190
|
+
countDown.setCallbackOnRefreshData(callback2);
|
|
191
|
+
|
|
192
|
+
expect(countDown.callbackOnRefreshData).toBe(callback2);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
describe('refreshData', () => {
|
|
197
|
+
test('should reset currentMillis', () => {
|
|
198
|
+
const callback = jest.fn((completeCallback) => {
|
|
199
|
+
completeCallback();
|
|
200
|
+
});
|
|
201
|
+
const countDown = new CountDown(mockDiv, callback);
|
|
202
|
+
|
|
203
|
+
countDown.currentMillis = 5000;
|
|
204
|
+
countDown.refreshData();
|
|
205
|
+
|
|
206
|
+
expect(countDown.currentMillis).toBe(0);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test('should not launch new request if already making request', () => {
|
|
210
|
+
const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
211
|
+
const callback = jest.fn((completeCallback) => {
|
|
212
|
+
completeCallback();
|
|
213
|
+
});
|
|
214
|
+
const countDown = new CountDown(mockDiv, callback);
|
|
215
|
+
|
|
216
|
+
countDown.alreadyMakingRequest = true;
|
|
217
|
+
const callCountBefore = callback.mock.calls.length;
|
|
218
|
+
|
|
219
|
+
countDown.refreshData();
|
|
220
|
+
|
|
221
|
+
expect(callback.mock.calls.length).toBe(callCountBefore);
|
|
222
|
+
expect(consoleLogSpy).toHaveBeenCalledWith('Already making request, no new request lauched.');
|
|
223
|
+
|
|
224
|
+
consoleLogSpy.mockRestore();
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test('should disable button during request', () => {
|
|
228
|
+
const callback = jest.fn((completeCallback) => {
|
|
229
|
+
// Don't call completeCallback to keep request active
|
|
230
|
+
});
|
|
231
|
+
const countDown = new CountDown(mockDiv, callback);
|
|
232
|
+
|
|
233
|
+
jest.clearAllMocks();
|
|
234
|
+
countDown.refreshData();
|
|
235
|
+
|
|
236
|
+
expect(mockLink.attr).toHaveBeenCalledWith('disabled', true);
|
|
237
|
+
expect(mockLink.button).toHaveBeenCalledWith('loading');
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test('should enable button after request completes', () => {
|
|
241
|
+
const callback = jest.fn((completeCallback) => {
|
|
242
|
+
completeCallback();
|
|
243
|
+
});
|
|
244
|
+
const countDown = new CountDown(mockDiv, callback);
|
|
245
|
+
|
|
246
|
+
jest.clearAllMocks();
|
|
247
|
+
countDown.refreshData();
|
|
248
|
+
|
|
249
|
+
expect(mockLink.attr).toHaveBeenCalledWith('disabled', false);
|
|
250
|
+
expect(mockLink.button).toHaveBeenCalledWith('reset');
|
|
251
|
+
expect(countDown.alreadyMakingRequest).toBe(false);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
test('should call callback if it is a function', () => {
|
|
255
|
+
const callback = jest.fn((completeCallback) => {
|
|
256
|
+
completeCallback();
|
|
257
|
+
});
|
|
258
|
+
const countDown = new CountDown(mockDiv, callback);
|
|
259
|
+
|
|
260
|
+
jest.clearAllMocks();
|
|
261
|
+
countDown.refreshData();
|
|
262
|
+
|
|
263
|
+
expect(callback).toHaveBeenCalledWith(expect.any(Function));
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
test('should not crash if callback is not a function', () => {
|
|
267
|
+
const countDown = new CountDown(mockDiv, null);
|
|
268
|
+
|
|
269
|
+
expect(() => {
|
|
270
|
+
countDown.refreshData();
|
|
271
|
+
}).not.toThrow();
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
test('should set alreadyMakingRequest to true during request', () => {
|
|
275
|
+
let countDownInstance;
|
|
276
|
+
const callback = jest.fn((completeCallback) => {
|
|
277
|
+
// Note: Bug in original code - sets CountDown.alreadyMakingRequest (static) instead of this.alreadyMakingRequest
|
|
278
|
+
// We test the actual behavior, not the intended behavior
|
|
279
|
+
completeCallback();
|
|
280
|
+
});
|
|
281
|
+
countDownInstance = new CountDown(mockDiv, callback);
|
|
282
|
+
|
|
283
|
+
countDownInstance.alreadyMakingRequest = false;
|
|
284
|
+
jest.clearAllMocks();
|
|
285
|
+
countDownInstance.refreshData();
|
|
286
|
+
|
|
287
|
+
// After refreshData completes, alreadyMakingRequest should be false again
|
|
288
|
+
expect(countDownInstance.alreadyMakingRequest).toBe(false);
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
describe('interval timer behavior', () => {
|
|
293
|
+
test('should increment currentMillis on each interval tick', () => {
|
|
294
|
+
const callback = jest.fn((completeCallback) => {
|
|
295
|
+
completeCallback();
|
|
296
|
+
});
|
|
297
|
+
const countDown = new CountDown(mockDiv, callback);
|
|
298
|
+
|
|
299
|
+
countDown.currentMillis = 0;
|
|
300
|
+
|
|
301
|
+
// Execute interval callback
|
|
302
|
+
intervalCallback();
|
|
303
|
+
|
|
304
|
+
expect(countDown.currentMillis).toBe(60);
|
|
305
|
+
expect(countDown.currentSecond).toBe(0);
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
test('should update currentSecond correctly', () => {
|
|
309
|
+
const callback = jest.fn((completeCallback) => {
|
|
310
|
+
completeCallback();
|
|
311
|
+
});
|
|
312
|
+
const countDown = new CountDown(mockDiv, callback);
|
|
313
|
+
|
|
314
|
+
countDown.currentMillis = 0;
|
|
315
|
+
|
|
316
|
+
// Run multiple intervals to reach 1 second
|
|
317
|
+
for (let i = 0; i < 17; i++) {
|
|
318
|
+
intervalCallback();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
expect(countDown.currentSecond).toBe(1);
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
test('should reset currentMillis when button is disabled', () => {
|
|
325
|
+
mockLink.prop = jest.fn(() => true); // Button disabled
|
|
326
|
+
|
|
327
|
+
const callback = jest.fn((completeCallback) => {
|
|
328
|
+
completeCallback();
|
|
329
|
+
});
|
|
330
|
+
const countDown = new CountDown(mockDiv, callback);
|
|
331
|
+
|
|
332
|
+
countDown.currentMillis = 5000;
|
|
333
|
+
|
|
334
|
+
intervalCallback();
|
|
335
|
+
|
|
336
|
+
expect(countDown.currentMillis).toBe(0);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
test('should update progress bar width correctly', () => {
|
|
340
|
+
const callback = jest.fn((completeCallback) => {
|
|
341
|
+
completeCallback();
|
|
342
|
+
});
|
|
343
|
+
const countDown = new CountDown(mockDiv, callback);
|
|
344
|
+
|
|
345
|
+
countDown.currentMillis = 5000;
|
|
346
|
+
jest.clearAllMocks();
|
|
347
|
+
|
|
348
|
+
intervalCallback();
|
|
349
|
+
|
|
350
|
+
expect(mockCountDownCurrent.width).toHaveBeenCalled();
|
|
351
|
+
const widthArg = mockCountDownCurrent.width.mock.calls[0][0];
|
|
352
|
+
expect(widthArg).toBeGreaterThan(0);
|
|
353
|
+
expect(widthArg).toBeLessThanOrEqual(120);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
test('should update text with remaining seconds', () => {
|
|
357
|
+
const callback = jest.fn((completeCallback) => {
|
|
358
|
+
completeCallback();
|
|
359
|
+
});
|
|
360
|
+
const countDown = new CountDown(mockDiv, callback);
|
|
361
|
+
|
|
362
|
+
countDown.currentMillis = 0;
|
|
363
|
+
jest.clearAllMocks();
|
|
364
|
+
|
|
365
|
+
intervalCallback();
|
|
366
|
+
|
|
367
|
+
expect(mockCountDownText.html).toHaveBeenCalledWith('10s');
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
test('should show 0s when time reaches limit', () => {
|
|
371
|
+
const callback = jest.fn((completeCallback) => {
|
|
372
|
+
completeCallback();
|
|
373
|
+
});
|
|
374
|
+
const countDown = new CountDown(mockDiv, callback);
|
|
375
|
+
|
|
376
|
+
countDown.currentMillis = 10000;
|
|
377
|
+
countDown.currentSecond = 10;
|
|
378
|
+
jest.clearAllMocks();
|
|
379
|
+
|
|
380
|
+
intervalCallback();
|
|
381
|
+
|
|
382
|
+
expect(mockCountDownText.html).toHaveBeenCalledWith('0s');
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
test('should set progress bar to full when time reaches limit', () => {
|
|
386
|
+
const callback = jest.fn((completeCallback) => {
|
|
387
|
+
completeCallback();
|
|
388
|
+
});
|
|
389
|
+
const countDown = new CountDown(mockDiv, callback);
|
|
390
|
+
|
|
391
|
+
countDown.currentMillis = 10000;
|
|
392
|
+
countDown.currentSecond = 10;
|
|
393
|
+
jest.clearAllMocks();
|
|
394
|
+
|
|
395
|
+
intervalCallback();
|
|
396
|
+
|
|
397
|
+
expect(mockCountDownCurrent.width).toHaveBeenCalledWith(120);
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
test('should trigger refreshData after timeout when time limit reached', () => {
|
|
401
|
+
const callback = jest.fn((completeCallback) => {
|
|
402
|
+
completeCallback();
|
|
403
|
+
});
|
|
404
|
+
const countDown = new CountDown(mockDiv, callback);
|
|
405
|
+
|
|
406
|
+
countDown.currentMillis = 10000;
|
|
407
|
+
countDown.currentSecond = 10;
|
|
408
|
+
|
|
409
|
+
const callCountBefore = callback.mock.calls.length;
|
|
410
|
+
jest.clearAllMocks();
|
|
411
|
+
|
|
412
|
+
// Execute interval callback which should schedule a setTimeout
|
|
413
|
+
intervalCallback();
|
|
414
|
+
|
|
415
|
+
// Advance timers to trigger the setTimeout(callback, 100)
|
|
416
|
+
jest.advanceTimersByTime(100);
|
|
417
|
+
|
|
418
|
+
// Should have called refreshData through the timeout
|
|
419
|
+
expect(callback.mock.calls.length).toBeGreaterThan(0);
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
test('should reset currentMillis after reaching time limit', () => {
|
|
423
|
+
const callback = jest.fn((completeCallback) => {
|
|
424
|
+
completeCallback();
|
|
425
|
+
});
|
|
426
|
+
const countDown = new CountDown(mockDiv, callback);
|
|
427
|
+
|
|
428
|
+
countDown.currentMillis = 10000;
|
|
429
|
+
countDown.currentSecond = 10;
|
|
430
|
+
|
|
431
|
+
intervalCallback();
|
|
432
|
+
|
|
433
|
+
expect(countDown.currentMillis).toBe(0);
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
test('should handle missing progress bar element', () => {
|
|
437
|
+
mockDiv.find = jest.fn((selector) => {
|
|
438
|
+
if (selector === '.count_down_link a') {
|
|
439
|
+
return mockLink;
|
|
440
|
+
}
|
|
441
|
+
if (selector === '.count_down_current') {
|
|
442
|
+
return { length: 0 };
|
|
443
|
+
}
|
|
444
|
+
if (selector === '.count_down_text') {
|
|
445
|
+
return mockCountDownText;
|
|
446
|
+
}
|
|
447
|
+
return { length: 0 };
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
const callback = jest.fn((completeCallback) => {
|
|
451
|
+
completeCallback();
|
|
452
|
+
});
|
|
453
|
+
const countDown = new CountDown(mockDiv, callback);
|
|
454
|
+
|
|
455
|
+
expect(() => {
|
|
456
|
+
intervalCallback();
|
|
457
|
+
}).not.toThrow();
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
test('should handle missing text element', () => {
|
|
461
|
+
mockDiv.find = jest.fn((selector) => {
|
|
462
|
+
if (selector === '.count_down_link a') {
|
|
463
|
+
return mockLink;
|
|
464
|
+
}
|
|
465
|
+
if (selector === '.count_down_current') {
|
|
466
|
+
return mockCountDownCurrent;
|
|
467
|
+
}
|
|
468
|
+
if (selector === '.count_down_text') {
|
|
469
|
+
return { length: 0 };
|
|
470
|
+
}
|
|
471
|
+
return { length: 0 };
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
const callback = jest.fn((completeCallback) => {
|
|
475
|
+
completeCallback();
|
|
476
|
+
});
|
|
477
|
+
const countDown = new CountDown(mockDiv, callback);
|
|
478
|
+
|
|
479
|
+
expect(() => {
|
|
480
|
+
intervalCallback();
|
|
481
|
+
}).not.toThrow();
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
test('should calculate progress correctly at different time points', () => {
|
|
485
|
+
const callback = jest.fn((completeCallback) => {
|
|
486
|
+
completeCallback();
|
|
487
|
+
});
|
|
488
|
+
const countDown = new CountDown(mockDiv, callback);
|
|
489
|
+
|
|
490
|
+
// Test at 5 seconds (50%)
|
|
491
|
+
countDown.currentMillis = 5000;
|
|
492
|
+
countDown.currentSecond = 5;
|
|
493
|
+
jest.clearAllMocks();
|
|
494
|
+
|
|
495
|
+
intervalCallback();
|
|
496
|
+
|
|
497
|
+
expect(mockCountDownText.html).toHaveBeenCalledWith('5s');
|
|
498
|
+
const width1 = mockCountDownCurrent.width.mock.calls[0][0];
|
|
499
|
+
expect(width1).toBeGreaterThan(50);
|
|
500
|
+
expect(width1).toBeLessThan(70);
|
|
501
|
+
|
|
502
|
+
// Test at 9 seconds (90%)
|
|
503
|
+
countDown.currentMillis = 9000;
|
|
504
|
+
countDown.currentSecond = 9;
|
|
505
|
+
jest.clearAllMocks();
|
|
506
|
+
|
|
507
|
+
intervalCallback();
|
|
508
|
+
|
|
509
|
+
expect(mockCountDownText.html).toHaveBeenCalledWith('1s');
|
|
510
|
+
const width2 = mockCountDownCurrent.width.mock.calls[0][0];
|
|
511
|
+
expect(width2).toBeGreaterThan(100);
|
|
512
|
+
});
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
describe('integration scenarios', () => {
|
|
516
|
+
test('should complete full countdown cycle', () => {
|
|
517
|
+
const callback = jest.fn((completeCallback) => {
|
|
518
|
+
completeCallback();
|
|
519
|
+
});
|
|
520
|
+
const countDown = new CountDown(mockDiv, callback);
|
|
521
|
+
|
|
522
|
+
const initialCallCount = callback.mock.calls.length;
|
|
523
|
+
|
|
524
|
+
// Start from 0
|
|
525
|
+
countDown.currentMillis = 0;
|
|
526
|
+
|
|
527
|
+
// Simulate intervals until reaching 10 seconds
|
|
528
|
+
for (let i = 0; i < 167; i++) { // 167 * 60ms ≈ 10020ms
|
|
529
|
+
intervalCallback();
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Advance timers to trigger the setTimeout(callback, 100)
|
|
533
|
+
jest.advanceTimersByTime(100);
|
|
534
|
+
|
|
535
|
+
// Should have triggered refresh at least once more
|
|
536
|
+
expect(callback.mock.calls.length).toBeGreaterThan(initialCallCount);
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
test('should handle rapid manual refreshes', () => {
|
|
540
|
+
const callback = jest.fn((completeCallback) => {
|
|
541
|
+
// Simulate async request - don't complete immediately
|
|
542
|
+
});
|
|
543
|
+
const countDown = new CountDown(mockDiv, callback);
|
|
544
|
+
|
|
545
|
+
jest.clearAllMocks();
|
|
546
|
+
|
|
547
|
+
// First manual refresh
|
|
548
|
+
countDown.refreshData();
|
|
549
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
550
|
+
|
|
551
|
+
// Manually set alreadyMakingRequest to true to simulate in-progress request
|
|
552
|
+
countDown.alreadyMakingRequest = true;
|
|
553
|
+
|
|
554
|
+
// Try to refresh again while first is in progress
|
|
555
|
+
countDown.refreshData();
|
|
556
|
+
expect(callback).toHaveBeenCalledTimes(1); // Should not call again
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
test('should allow new refresh after previous completes', () => {
|
|
560
|
+
let completeCallback;
|
|
561
|
+
const callback = jest.fn((cb) => {
|
|
562
|
+
completeCallback = cb;
|
|
563
|
+
});
|
|
564
|
+
const countDown = new CountDown(mockDiv, callback);
|
|
565
|
+
|
|
566
|
+
jest.clearAllMocks();
|
|
567
|
+
|
|
568
|
+
// First refresh
|
|
569
|
+
countDown.refreshData();
|
|
570
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
571
|
+
|
|
572
|
+
// Complete the first refresh
|
|
573
|
+
completeCallback();
|
|
574
|
+
|
|
575
|
+
// Now should allow new refresh
|
|
576
|
+
countDown.refreshData();
|
|
577
|
+
expect(callback).toHaveBeenCalledTimes(2);
|
|
578
|
+
});
|
|
579
|
+
});
|
|
580
|
+
});
|