@osimatic/helpers-js 1.5.3 → 1.5.4
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/details_sub_array.js +45 -41
- package/form_helper.js +283 -232
- package/google_charts.js +154 -144
- package/google_maps.js +1 -1
- package/import_from_csv.js +166 -157
- package/multi_files_input.js +42 -34
- package/multiple_action_in_table.js +115 -105
- package/package.json +1 -1
- package/paging.js +103 -84
- package/select_all.js +65 -70
- package/sortable_list.js +12 -13
- package/tests/details_sub_array.test.js +211 -239
- package/tests/form_helper.test.js +553 -673
- package/tests/google_charts.test.js +338 -339
- package/tests/google_maps.test.js +3 -15
- package/tests/import_from_csv.test.js +391 -652
- package/tests/multi_files_input.test.js +292 -722
- package/tests/multiple_action_in_table.test.js +439 -417
- package/tests/paging.test.js +344 -475
- package/tests/select_all.test.js +232 -318
- package/tests/sortable_list.test.js +176 -500
- package/tests/user.test.js +163 -54
- package/user.js +35 -38
package/tests/paging.test.js
CHANGED
|
@@ -3,644 +3,513 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
const { Pagination, Navigation } = require('../paging');
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const item = {
|
|
15
|
-
show: jest.fn().mockReturnThis(),
|
|
16
|
-
hide: jest.fn().mockReturnThis()
|
|
17
|
-
};
|
|
18
|
-
mockItems.push(item);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const itemsWithEach = Object.assign(mockItems, {
|
|
22
|
-
length: 5,
|
|
23
|
-
each: jest.fn(function(callback) {
|
|
24
|
-
this.forEach((item, idx) => callback.call(item, idx, item));
|
|
25
|
-
})
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
// Mock li elements
|
|
29
|
-
mockLi = {
|
|
30
|
-
addClass: jest.fn().mockReturnThis(),
|
|
31
|
-
removeClass: jest.fn().mockReturnThis(),
|
|
32
|
-
data: jest.fn((key) => key === 'page' ? 1 : undefined),
|
|
33
|
-
click: jest.fn().mockReturnThis()
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
// Mock ul element
|
|
37
|
-
const mockLiList = {
|
|
38
|
-
remove: jest.fn().mockReturnThis(),
|
|
39
|
-
click: jest.fn().mockReturnThis(),
|
|
40
|
-
removeClass: jest.fn().mockReturnThis(),
|
|
41
|
-
addClass: jest.fn().mockReturnThis()
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
mockUl = {
|
|
45
|
-
find: jest.fn((selector) => {
|
|
46
|
-
if (selector === 'li') {
|
|
47
|
-
return mockLiList;
|
|
48
|
-
}
|
|
49
|
-
if (selector === 'li:first-child') {
|
|
50
|
-
return mockLi;
|
|
51
|
-
}
|
|
52
|
-
return mockLi;
|
|
53
|
-
}),
|
|
54
|
-
append: jest.fn().mockReturnThis(),
|
|
55
|
-
show: jest.fn().mockReturnThis(),
|
|
56
|
-
addClass: jest.fn().mockReturnThis(),
|
|
57
|
-
removeClass: jest.fn().mockReturnThis(),
|
|
58
|
-
remove: jest.fn().mockReturnThis(),
|
|
59
|
-
each: jest.fn(function(callback) {
|
|
60
|
-
callback.call(this, 0, this);
|
|
61
|
-
}),
|
|
62
|
-
length: 1
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
// Mock select element
|
|
66
|
-
mockSelect = {
|
|
67
|
-
children: jest.fn(() => ({ length: 0 })),
|
|
68
|
-
append: jest.fn().mockReturnThis(),
|
|
69
|
-
data: jest.fn((key) => {
|
|
70
|
-
if (key === 'nb_rows_list') return '5,10,25,50';
|
|
71
|
-
if (key === 'default_nb_rows') return '10';
|
|
72
|
-
return undefined;
|
|
73
|
-
}),
|
|
74
|
-
val: jest.fn((value) => {
|
|
75
|
-
if (value === undefined) return '10';
|
|
76
|
-
return mockSelect;
|
|
77
|
-
}),
|
|
78
|
-
change: jest.fn().mockReturnThis(),
|
|
79
|
-
length: 1
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
// Mock div element
|
|
83
|
-
mockDiv = {
|
|
84
|
-
find: jest.fn((selector) => {
|
|
85
|
-
if (selector === '.pagination_item') return itemsWithEach;
|
|
86
|
-
if (selector === '.pagination_links') return { length: 1, prepend: jest.fn(), append: jest.fn() };
|
|
87
|
-
return { length: 0 };
|
|
88
|
-
}),
|
|
89
|
-
before: jest.fn().mockReturnThis(),
|
|
90
|
-
after: jest.fn().mockReturnThis(),
|
|
91
|
-
data: jest.fn((key) => key === 'max_rows' ? '10' : undefined),
|
|
92
|
-
length: 1
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
// Mock table element
|
|
96
|
-
mockTable = {
|
|
97
|
-
find: jest.fn((selector) => {
|
|
98
|
-
if (selector === 'tbody tr:not(.hide)') return itemsWithEach;
|
|
99
|
-
return { length: 0 };
|
|
100
|
-
}),
|
|
101
|
-
data: jest.fn((key) => key === 'max_rows' ? '10' : undefined),
|
|
102
|
-
before: jest.fn().mockReturnThis(),
|
|
103
|
-
after: jest.fn().mockReturnThis(),
|
|
104
|
-
length: 1
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
// Mock jQuery global
|
|
108
|
-
global.$ = jest.fn((selector) => {
|
|
109
|
-
if (selector === 'ul.pagination') return mockUl;
|
|
110
|
-
if (typeof selector === 'string' && selector.startsWith('<ul')) return mockUl;
|
|
111
|
-
if (typeof selector === 'string' && selector.startsWith('li[data-page')) {
|
|
112
|
-
return {
|
|
113
|
-
each: jest.fn(function(callback) {
|
|
114
|
-
callback.call(mockLi, 0, mockLi);
|
|
115
|
-
})
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
// Handle $(ul) calls where ul is mockUl - return mockUl itself
|
|
119
|
-
if (selector === mockUl) {
|
|
120
|
-
return mockUl;
|
|
121
|
-
}
|
|
122
|
-
// Handle $(this) calls inside .each() - return an object with all jQuery methods
|
|
123
|
-
if (typeof selector === 'object' && selector !== null) {
|
|
124
|
-
return {
|
|
125
|
-
show: jest.fn().mockReturnThis(),
|
|
126
|
-
hide: jest.fn().mockReturnThis(),
|
|
127
|
-
addClass: jest.fn().mockReturnThis(),
|
|
128
|
-
removeClass: jest.fn().mockReturnThis(),
|
|
129
|
-
data: jest.fn((key) => key === 'page' ? 1 : undefined),
|
|
130
|
-
find: jest.fn(() => ({
|
|
131
|
-
remove: jest.fn().mockReturnThis(),
|
|
132
|
-
addClass: jest.fn().mockReturnThis(),
|
|
133
|
-
removeClass: jest.fn().mockReturnThis()
|
|
134
|
-
})),
|
|
135
|
-
remove: jest.fn().mockReturnThis(),
|
|
136
|
-
append: jest.fn().mockReturnThis()
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
return mockDiv;
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
global.$.each = jest.fn((obj, callback) => {
|
|
143
|
-
if (Array.isArray(obj)) {
|
|
144
|
-
obj.forEach((item, idx) => callback(idx, item));
|
|
145
|
-
} else {
|
|
146
|
-
Object.keys(obj).forEach(key => callback(key, obj[key]));
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
// Mock global labelDisplayAll
|
|
151
|
-
global.labelDisplayAll = 'Display all';
|
|
6
|
+
const { UrlAndQueryString } = require('../network');
|
|
7
|
+
|
|
8
|
+
function makeItems(n) {
|
|
9
|
+
return Array.from({ length: n }, (_, i) => {
|
|
10
|
+
const el = document.createElement('div');
|
|
11
|
+
el.textContent = 'Item ' + i;
|
|
12
|
+
document.body.appendChild(el);
|
|
13
|
+
return el;
|
|
152
14
|
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function setupDiv(nbItems = 5, hasPaginationLinks = false) {
|
|
18
|
+
const div = document.createElement('div');
|
|
19
|
+
if (hasPaginationLinks) {
|
|
20
|
+
const pl = document.createElement('div');
|
|
21
|
+
pl.className = 'pagination_links';
|
|
22
|
+
div.appendChild(pl);
|
|
23
|
+
}
|
|
24
|
+
for (let i = 0; i < nbItems; i++) {
|
|
25
|
+
const item = document.createElement('div');
|
|
26
|
+
item.className = 'pagination_item';
|
|
27
|
+
div.appendChild(item);
|
|
28
|
+
}
|
|
29
|
+
document.body.appendChild(div);
|
|
30
|
+
return div;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function setupTable(nbRows = 5, maxRows = 10) {
|
|
34
|
+
const table = document.createElement('table');
|
|
35
|
+
table.dataset.max_rows = String(maxRows);
|
|
36
|
+
const tbody = document.createElement('tbody');
|
|
37
|
+
for (let i = 0; i < nbRows; i++) {
|
|
38
|
+
const tr = document.createElement('tr');
|
|
39
|
+
tr.innerHTML = '<td>Row ' + i + '</td>';
|
|
40
|
+
tbody.appendChild(tr);
|
|
41
|
+
}
|
|
42
|
+
table.appendChild(tbody);
|
|
43
|
+
document.body.appendChild(table);
|
|
44
|
+
return table;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function setupSelect(opts = {}) {
|
|
48
|
+
const select = document.createElement('select');
|
|
49
|
+
if (opts.nb_rows_list != null) select.dataset.nb_rows_list = opts.nb_rows_list;
|
|
50
|
+
if (opts.default_nb_rows != null) select.dataset.default_nb_rows = String(opts.default_nb_rows);
|
|
51
|
+
document.body.appendChild(select);
|
|
52
|
+
return select;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function setupNav() {
|
|
56
|
+
document.body.innerHTML = `
|
|
57
|
+
<div>
|
|
58
|
+
<ul class="nav">
|
|
59
|
+
<a class="nav-link active" href="#tab1">Tab 1</a>
|
|
60
|
+
<a class="nav-link" href="#tab2">Tab 2</a>
|
|
61
|
+
</ul>
|
|
62
|
+
<div class="tab-content">
|
|
63
|
+
<div id="tab1" class="active show">Content 1</div>
|
|
64
|
+
<div id="tab2">Content 2</div>
|
|
65
|
+
</div>
|
|
66
|
+
</div>`;
|
|
67
|
+
return {
|
|
68
|
+
link1: document.querySelector('a[href="#tab1"]'),
|
|
69
|
+
link2: document.querySelector('a[href="#tab2"]'),
|
|
70
|
+
pane1: document.getElementById('tab1'),
|
|
71
|
+
pane2: document.getElementById('tab2'),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
afterEach(() => {
|
|
76
|
+
document.body.innerHTML = '';
|
|
77
|
+
jest.clearAllMocks();
|
|
78
|
+
delete global.bootstrap;
|
|
79
|
+
});
|
|
153
80
|
|
|
154
|
-
|
|
155
|
-
jest.clearAllMocks();
|
|
156
|
-
delete global.$;
|
|
157
|
-
delete global.labelDisplayAll;
|
|
158
|
-
});
|
|
81
|
+
describe('Pagination', () => {
|
|
159
82
|
|
|
160
83
|
describe('paginateCards', () => {
|
|
161
84
|
test('should call paginate with correct parameters', () => {
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
Pagination.paginateCards(mockDiv, 10, false);
|
|
85
|
+
const div = setupDiv(5);
|
|
86
|
+
const spy = jest.spyOn(Pagination, 'paginate').mockImplementation(() => {});
|
|
165
87
|
|
|
166
|
-
|
|
167
|
-
mockDiv,
|
|
168
|
-
expect.anything(),
|
|
169
|
-
10,
|
|
170
|
-
undefined,
|
|
171
|
-
false
|
|
172
|
-
);
|
|
88
|
+
Pagination.paginateCards(div, 10);
|
|
173
89
|
|
|
174
|
-
|
|
90
|
+
expect(spy).toHaveBeenCalledWith(div, expect.anything(), 10, null);
|
|
91
|
+
spy.mockRestore();
|
|
175
92
|
});
|
|
176
93
|
|
|
177
|
-
test('should
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
Pagination.paginateCards(mockDiv, 20, true);
|
|
94
|
+
test('should pass pagination_item elements as items', () => {
|
|
95
|
+
const div = setupDiv(3);
|
|
96
|
+
const spy = jest.spyOn(Pagination, 'paginate').mockImplementation(() => {});
|
|
181
97
|
|
|
182
|
-
|
|
183
|
-
mockDiv,
|
|
184
|
-
expect.anything(),
|
|
185
|
-
20,
|
|
186
|
-
undefined,
|
|
187
|
-
true
|
|
188
|
-
);
|
|
98
|
+
Pagination.paginateCards(div, 5);
|
|
189
99
|
|
|
190
|
-
|
|
100
|
+
const items = spy.mock.calls[0][1];
|
|
101
|
+
expect(items.length).toBe(3);
|
|
102
|
+
spy.mockRestore();
|
|
191
103
|
});
|
|
192
104
|
});
|
|
193
105
|
|
|
194
106
|
describe('paginateTable', () => {
|
|
195
107
|
test('should call paginate with table rows', () => {
|
|
196
|
-
const
|
|
108
|
+
const table = setupTable(5, 10);
|
|
109
|
+
const spy = jest.spyOn(Pagination, 'paginate').mockImplementation(() => {});
|
|
197
110
|
|
|
198
|
-
Pagination.paginateTable(
|
|
111
|
+
Pagination.paginateTable(table);
|
|
199
112
|
|
|
200
|
-
expect(
|
|
201
|
-
|
|
202
|
-
|
|
113
|
+
expect(spy).toHaveBeenCalledWith(table, expect.anything(), 10, null);
|
|
114
|
+
spy.mockRestore();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('should pass visible tbody rows as items', () => {
|
|
118
|
+
const table = setupTable(4, 10);
|
|
119
|
+
const spy = jest.spyOn(Pagination, 'paginate').mockImplementation(() => {});
|
|
120
|
+
|
|
121
|
+
Pagination.paginateTable(table);
|
|
203
122
|
|
|
204
|
-
|
|
123
|
+
const items = spy.mock.calls[0][1];
|
|
124
|
+
expect(items.length).toBe(4);
|
|
125
|
+
spy.mockRestore();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('should pass select when provided', () => {
|
|
129
|
+
const table = setupTable(5, 10);
|
|
130
|
+
const select = setupSelect();
|
|
131
|
+
const spy = jest.spyOn(Pagination, 'paginate').mockImplementation(() => {});
|
|
132
|
+
|
|
133
|
+
Pagination.paginateTable(table, select);
|
|
134
|
+
|
|
135
|
+
expect(spy).toHaveBeenCalledWith(table, expect.anything(), 10, select);
|
|
136
|
+
spy.mockRestore();
|
|
205
137
|
});
|
|
206
138
|
});
|
|
207
139
|
|
|
208
140
|
describe('paginate', () => {
|
|
209
141
|
test('should return early if div is undefined', () => {
|
|
210
|
-
Pagination.paginate(undefined,
|
|
211
|
-
// Should not throw
|
|
142
|
+
expect(() => Pagination.paginate(undefined, [], 10)).not.toThrow();
|
|
212
143
|
});
|
|
213
144
|
|
|
214
|
-
test('should return early if div
|
|
215
|
-
|
|
216
|
-
Pagination.paginate(emptyDiv, mockItems, 10, undefined, false);
|
|
217
|
-
// Should not throw
|
|
145
|
+
test('should return early if div is null', () => {
|
|
146
|
+
expect(() => Pagination.paginate(null, [], 10)).not.toThrow();
|
|
218
147
|
});
|
|
219
148
|
|
|
220
149
|
test('should initialize select with options when empty', () => {
|
|
221
|
-
const
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
Pagination.paginate(mockDiv, mockItems, 10, mockSelect, false);
|
|
150
|
+
const div = setupDiv(0);
|
|
151
|
+
const select = setupSelect({ nb_rows_list: '5,10,25,50' });
|
|
225
152
|
|
|
226
|
-
|
|
227
|
-
expect(mockSelect.append).toHaveBeenCalled();
|
|
228
|
-
expect(mockSelect.data).toHaveBeenCalledWith('nb_rows_list');
|
|
153
|
+
Pagination.paginate(div, [], 10, select);
|
|
229
154
|
|
|
230
|
-
|
|
231
|
-
|
|
155
|
+
expect(select.options.length).toBe(5); // 'Afficher tout' + 4 options
|
|
156
|
+
expect(select.options[0].textContent).toBe('Afficher tout');
|
|
157
|
+
expect(select.options[0].value).toBe('0');
|
|
232
158
|
});
|
|
233
159
|
|
|
234
|
-
test('should
|
|
235
|
-
const
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
Pagination.paginate(mockDiv, mockItems, 10, mockSelect, false);
|
|
160
|
+
test('should use custom labelDisplayAll', () => {
|
|
161
|
+
const div = setupDiv(0);
|
|
162
|
+
const select = setupSelect();
|
|
239
163
|
|
|
240
|
-
|
|
164
|
+
Pagination.paginate(div, [], 10, select, 'Show all');
|
|
241
165
|
|
|
242
|
-
|
|
243
|
-
spyInitPaginationItems.mockRestore();
|
|
166
|
+
expect(select.options[0].textContent).toBe('Show all');
|
|
244
167
|
});
|
|
245
168
|
|
|
246
|
-
test('should
|
|
247
|
-
const
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
Pagination.paginate(mockDiv, mockItems, 10, undefined, true);
|
|
169
|
+
test('should set default value when data-default_nb_rows is present', () => {
|
|
170
|
+
const div = setupDiv(0);
|
|
171
|
+
const select = setupSelect({ nb_rows_list: '5,10,25,50', default_nb_rows: 10 });
|
|
251
172
|
|
|
252
|
-
|
|
253
|
-
expect(spyInitPaginationDiv).toHaveBeenCalledWith(mockDiv, mockUl, true, true); // top
|
|
254
|
-
expect(spyInitPaginationDiv).toHaveBeenCalledWith(mockDiv, mockUl, false, true); // bottom
|
|
173
|
+
Pagination.paginate(div, [], 10, select);
|
|
255
174
|
|
|
256
|
-
|
|
257
|
-
spyInitPaginationItems.mockRestore();
|
|
175
|
+
expect(select.value).toBe('10');
|
|
258
176
|
});
|
|
259
177
|
|
|
260
|
-
test('should initialize
|
|
261
|
-
const
|
|
262
|
-
const
|
|
178
|
+
test('should not re-initialize select if already has options', () => {
|
|
179
|
+
const div = setupDiv(0);
|
|
180
|
+
const select = setupSelect({ nb_rows_list: '5,10,25,50' });
|
|
263
181
|
|
|
264
|
-
Pagination.paginate(
|
|
182
|
+
Pagination.paginate(div, [], 10, select);
|
|
183
|
+
const optCount = select.options.length;
|
|
184
|
+
Pagination.paginate(div, [], 10, select);
|
|
265
185
|
|
|
266
|
-
expect(
|
|
267
|
-
expect(spyInitPaginationDiv).toHaveBeenCalledWith(mockDiv, mockUl, false, false); // bottom
|
|
268
|
-
|
|
269
|
-
spyInitPaginationDiv.mockRestore();
|
|
270
|
-
spyInitPaginationItems.mockRestore();
|
|
186
|
+
expect(select.options.length).toBe(optCount);
|
|
271
187
|
});
|
|
272
|
-
});
|
|
273
188
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
const
|
|
189
|
+
test('should always initialize both top and bottom pagination', () => {
|
|
190
|
+
const div = setupDiv(0);
|
|
191
|
+
const spy = jest.spyOn(Pagination, 'initPaginationDiv').mockImplementation(() => {});
|
|
277
192
|
|
|
278
|
-
Pagination.
|
|
193
|
+
Pagination.paginate(div, [], 10);
|
|
279
194
|
|
|
280
|
-
expect(
|
|
195
|
+
expect(spy).toHaveBeenCalledTimes(2);
|
|
196
|
+
expect(spy).toHaveBeenCalledWith(div, true);
|
|
197
|
+
expect(spy).toHaveBeenCalledWith(div, false);
|
|
198
|
+
spy.mockRestore();
|
|
281
199
|
});
|
|
282
200
|
|
|
283
|
-
test('should
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
};
|
|
201
|
+
test('should remove existing pagination ULs before re-rendering', () => {
|
|
202
|
+
const div = setupDiv(0);
|
|
203
|
+
const staleUl = document.createElement('ul');
|
|
204
|
+
staleUl.className = 'pagination';
|
|
205
|
+
document.body.appendChild(staleUl);
|
|
289
206
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
return { length: 0 };
|
|
293
|
-
});
|
|
207
|
+
const spy = jest.spyOn(Pagination, 'initPaginationDiv').mockImplementation(() => {});
|
|
208
|
+
Pagination.paginate(div, [], 10);
|
|
294
209
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
expect(paginationLinks.append).toHaveBeenCalled();
|
|
210
|
+
expect(document.querySelectorAll('ul.pagination').length).toBe(0);
|
|
211
|
+
spy.mockRestore();
|
|
298
212
|
});
|
|
213
|
+
});
|
|
299
214
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
mockDiv.find = jest.fn((selector) => {
|
|
308
|
-
if (selector === '.pagination_links') return paginationLinks;
|
|
309
|
-
return { length: 0 };
|
|
310
|
-
});
|
|
215
|
+
describe('initPaginationDiv', () => {
|
|
216
|
+
test('should create a pagination ul element', () => {
|
|
217
|
+
const div = setupDiv(0);
|
|
218
|
+
Pagination.initPaginationDiv(div, false);
|
|
219
|
+
expect(document.querySelector('ul.pagination')).not.toBeNull();
|
|
220
|
+
});
|
|
311
221
|
|
|
312
|
-
|
|
222
|
+
test('should insert ul after div when onTop is false and no pagination_links', () => {
|
|
223
|
+
const div = setupDiv(0);
|
|
224
|
+
Pagination.initPaginationDiv(div, false);
|
|
225
|
+
expect(div.nextElementSibling.tagName).toBe('UL');
|
|
226
|
+
expect(div.nextElementSibling.classList.contains('pagination')).toBe(true);
|
|
227
|
+
});
|
|
313
228
|
|
|
314
|
-
|
|
229
|
+
test('should insert ul before div when onTop is true and no pagination_links', () => {
|
|
230
|
+
const div = setupDiv(0);
|
|
231
|
+
Pagination.initPaginationDiv(div, true);
|
|
232
|
+
expect(div.previousElementSibling.tagName).toBe('UL');
|
|
233
|
+
expect(div.previousElementSibling.classList.contains('pagination')).toBe(true);
|
|
315
234
|
});
|
|
316
235
|
|
|
317
|
-
test('should
|
|
318
|
-
|
|
236
|
+
test('should append ul to pagination_links when onTop is false', () => {
|
|
237
|
+
const div = setupDiv(0, true);
|
|
238
|
+
const paginationLinks = div.querySelector('.pagination_links');
|
|
319
239
|
|
|
320
|
-
Pagination.initPaginationDiv(
|
|
240
|
+
Pagination.initPaginationDiv(div, false);
|
|
321
241
|
|
|
322
|
-
expect(
|
|
242
|
+
expect(paginationLinks.lastElementChild.tagName).toBe('UL');
|
|
323
243
|
});
|
|
324
244
|
|
|
325
|
-
test('should
|
|
326
|
-
|
|
245
|
+
test('should prepend ul to pagination_links when onTop is true', () => {
|
|
246
|
+
const div = setupDiv(0, true);
|
|
247
|
+
const paginationLinks = div.querySelector('.pagination_links');
|
|
327
248
|
|
|
328
|
-
Pagination.initPaginationDiv(
|
|
249
|
+
Pagination.initPaginationDiv(div, true);
|
|
329
250
|
|
|
330
|
-
expect(
|
|
251
|
+
expect(paginationLinks.firstElementChild.tagName).toBe('UL');
|
|
331
252
|
});
|
|
332
253
|
});
|
|
333
254
|
|
|
334
255
|
describe('initPaginationItems', () => {
|
|
335
|
-
|
|
336
|
-
const
|
|
256
|
+
function setupPaginationUls(n = 2) {
|
|
257
|
+
const uls = [];
|
|
258
|
+
for (let i = 0; i < n; i++) {
|
|
259
|
+
const ul = document.createElement('ul');
|
|
260
|
+
ul.className = 'pagination';
|
|
261
|
+
document.body.appendChild(ul);
|
|
262
|
+
uls.push(ul);
|
|
263
|
+
}
|
|
264
|
+
return uls;
|
|
265
|
+
}
|
|
337
266
|
|
|
338
|
-
|
|
267
|
+
test('should show items up to maxItems', () => {
|
|
268
|
+
setupPaginationUls();
|
|
269
|
+
const items = makeItems(5);
|
|
339
270
|
|
|
340
|
-
|
|
341
|
-
expect(mockItems.each).toHaveBeenCalled();
|
|
342
|
-
// Verify that $ was called (for $(this).show() and $(this).hide() calls)
|
|
343
|
-
expect($Spy).toHaveBeenCalled();
|
|
271
|
+
Pagination.initPaginationItems(items, 3);
|
|
344
272
|
|
|
345
|
-
|
|
273
|
+
expect(items[0].style.display).toBe('');
|
|
274
|
+
expect(items[1].style.display).toBe('');
|
|
275
|
+
expect(items[2].style.display).toBe('');
|
|
276
|
+
expect(items[3].style.display).toBe('none');
|
|
277
|
+
expect(items[4].style.display).toBe('none');
|
|
346
278
|
});
|
|
347
279
|
|
|
348
280
|
test('should show all items when maxItems is 0', () => {
|
|
349
|
-
|
|
281
|
+
setupPaginationUls();
|
|
282
|
+
const items = makeItems(5);
|
|
283
|
+
|
|
284
|
+
Pagination.initPaginationItems(items, 0);
|
|
350
285
|
|
|
351
|
-
|
|
352
|
-
expect(mockItems.each).toHaveBeenCalled();
|
|
286
|
+
items.forEach(item => expect(item.style.display).toBe(''));
|
|
353
287
|
});
|
|
354
288
|
|
|
355
289
|
test('should hide pagination when maxItems is 0', () => {
|
|
356
|
-
|
|
290
|
+
const [ul] = setupPaginationUls(1);
|
|
291
|
+
const items = makeItems(5);
|
|
292
|
+
|
|
293
|
+
Pagination.initPaginationItems(items, 0);
|
|
357
294
|
|
|
358
|
-
|
|
359
|
-
expect(mockUl.each).toHaveBeenCalled();
|
|
295
|
+
expect(ul.classList.contains('hide')).toBe(true);
|
|
360
296
|
});
|
|
361
297
|
|
|
362
298
|
test('should hide pagination when totalItems < maxItems', () => {
|
|
363
|
-
|
|
299
|
+
const [ul] = setupPaginationUls(1);
|
|
300
|
+
const items = makeItems(5);
|
|
364
301
|
|
|
365
|
-
|
|
366
|
-
|
|
302
|
+
Pagination.initPaginationItems(items, 10);
|
|
303
|
+
|
|
304
|
+
expect(ul.classList.contains('hide')).toBe(true);
|
|
367
305
|
});
|
|
368
306
|
|
|
369
|
-
test('should create
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
hide: jest.fn().mockReturnThis()
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
manyItems.each = jest.fn(function(callback) {
|
|
378
|
-
this.forEach((item, idx) => callback.call(item, idx, item));
|
|
379
|
-
});
|
|
380
|
-
manyItems.length = 25;
|
|
307
|
+
test('should create page items when totalItems >= maxItems', () => {
|
|
308
|
+
const [ul] = setupPaginationUls(1);
|
|
309
|
+
const items = makeItems(25);
|
|
310
|
+
|
|
311
|
+
Pagination.initPaginationItems(items, 10);
|
|
381
312
|
|
|
382
|
-
|
|
313
|
+
const pages = ul.querySelectorAll('li.page-item');
|
|
314
|
+
expect(pages.length).toBe(3); // ceil(25/10) = 3
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
test('should show pagination when pages are created', () => {
|
|
318
|
+
const [ul] = setupPaginationUls(1);
|
|
319
|
+
const items = makeItems(25);
|
|
383
320
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
expect(
|
|
387
|
-
// Verify pagination is not hidden
|
|
388
|
-
expect(mockUl.removeClass).toHaveBeenCalledWith('hide');
|
|
321
|
+
Pagination.initPaginationItems(items, 10);
|
|
322
|
+
|
|
323
|
+
expect(ul.classList.contains('hide')).toBe(false);
|
|
389
324
|
});
|
|
390
325
|
|
|
391
326
|
test('should set first page as active', () => {
|
|
392
|
-
const
|
|
393
|
-
|
|
394
|
-
manyItems.push({
|
|
395
|
-
show: jest.fn().mockReturnThis(),
|
|
396
|
-
hide: jest.fn().mockReturnThis()
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
manyItems.each = jest.fn(function(callback) {
|
|
400
|
-
this.forEach((item, idx) => callback.call(item, idx, item));
|
|
401
|
-
});
|
|
402
|
-
manyItems.length = 25;
|
|
327
|
+
const [ul] = setupPaginationUls(1);
|
|
328
|
+
const items = makeItems(25);
|
|
403
329
|
|
|
404
|
-
Pagination.initPaginationItems(
|
|
330
|
+
Pagination.initPaginationItems(items, 10);
|
|
405
331
|
|
|
406
|
-
|
|
407
|
-
expect(mockUl.find).toHaveBeenCalledWith('li:first-child');
|
|
408
|
-
expect(mockLi.addClass).toHaveBeenCalledWith('active');
|
|
332
|
+
expect(ul.querySelector('li:first-child').classList.contains('active')).toBe(true);
|
|
409
333
|
});
|
|
410
334
|
|
|
411
|
-
test('should
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
manyItems.push({
|
|
415
|
-
show: jest.fn().mockReturnThis(),
|
|
416
|
-
hide: jest.fn().mockReturnThis()
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
manyItems.each = jest.fn(function(callback) {
|
|
420
|
-
this.forEach((item, idx) => callback.call(item, idx, item));
|
|
421
|
-
});
|
|
422
|
-
manyItems.length = 25;
|
|
335
|
+
test('should set data-page attribute on page items', () => {
|
|
336
|
+
const [ul] = setupPaginationUls(1);
|
|
337
|
+
const items = makeItems(25);
|
|
423
338
|
|
|
424
|
-
Pagination.initPaginationItems(
|
|
339
|
+
Pagination.initPaginationItems(items, 10);
|
|
425
340
|
|
|
426
|
-
|
|
427
|
-
expect(
|
|
341
|
+
expect(ul.querySelector('li[data-page="1"]')).not.toBeNull();
|
|
342
|
+
expect(ul.querySelector('li[data-page="2"]')).not.toBeNull();
|
|
343
|
+
expect(ul.querySelector('li[data-page="3"]')).not.toBeNull();
|
|
428
344
|
});
|
|
429
|
-
});
|
|
430
|
-
});
|
|
431
345
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
removeClass: jest.fn().mockReturnThis()
|
|
440
|
-
};
|
|
441
|
-
|
|
442
|
-
// Mock tab content
|
|
443
|
-
mockTabContent = {
|
|
444
|
-
find: jest.fn((selector) => mockTabPane)
|
|
445
|
-
};
|
|
446
|
-
|
|
447
|
-
// Mock nav link
|
|
448
|
-
mockNavLink = {
|
|
449
|
-
removeClass: jest.fn().mockReturnThis(),
|
|
450
|
-
attr: jest.fn((key) => key === 'href' ? '#tab1' : undefined)
|
|
451
|
-
};
|
|
452
|
-
|
|
453
|
-
// Mock ul nav
|
|
454
|
-
mockUlNav = {
|
|
455
|
-
find: jest.fn((selector) => {
|
|
456
|
-
if (selector === 'a.nav-link') {
|
|
457
|
-
return {
|
|
458
|
-
each: jest.fn(function(callback) {
|
|
459
|
-
callback.call(mockNavLink, 0, mockNavLink);
|
|
460
|
-
})
|
|
461
|
-
};
|
|
462
|
-
}
|
|
463
|
-
return mockNavLink;
|
|
464
|
-
}),
|
|
465
|
-
parent: jest.fn(() => ({
|
|
466
|
-
find: jest.fn((selector) => mockTabContent)
|
|
467
|
-
}))
|
|
468
|
-
};
|
|
469
|
-
|
|
470
|
-
// Mock a element
|
|
471
|
-
mockA = {
|
|
472
|
-
closest: jest.fn((selector) => mockUlNav),
|
|
473
|
-
addClass: jest.fn().mockReturnThis(),
|
|
474
|
-
attr: jest.fn((key) => key === 'href' ? '#tab2' : undefined),
|
|
475
|
-
length: 1
|
|
476
|
-
};
|
|
477
|
-
|
|
478
|
-
// Mock jQuery
|
|
479
|
-
global.$ = jest.fn((selector) => {
|
|
480
|
-
if (selector === mockNavLink || (typeof selector === 'object' && selector === mockNavLink)) {
|
|
481
|
-
return mockNavLink;
|
|
482
|
-
}
|
|
483
|
-
if (typeof selector === 'object' && selector !== null) {
|
|
484
|
-
// Handle $(element) calls
|
|
485
|
-
return {
|
|
486
|
-
removeClass: jest.fn().mockReturnThis(),
|
|
487
|
-
addClass: jest.fn().mockReturnThis(),
|
|
488
|
-
attr: jest.fn((key) => key === 'href' ? '#tab1' : undefined),
|
|
489
|
-
...selector
|
|
490
|
-
};
|
|
491
|
-
}
|
|
492
|
-
return mockA;
|
|
346
|
+
test('should sync pages across multiple pagination uls', () => {
|
|
347
|
+
const uls = setupPaginationUls(2);
|
|
348
|
+
const items = makeItems(20);
|
|
349
|
+
|
|
350
|
+
Pagination.initPaginationItems(items, 10);
|
|
351
|
+
|
|
352
|
+
uls.forEach(ul => expect(ul.querySelectorAll('li').length).toBe(2));
|
|
493
353
|
});
|
|
494
354
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
}
|
|
500
|
-
global.window.history.replaceState = jest.fn();
|
|
501
|
-
global.window.history.pushState = jest.fn();
|
|
355
|
+
test('should clear existing li items before adding new ones', () => {
|
|
356
|
+
const [ul] = setupPaginationUls(1);
|
|
357
|
+
ul.innerHTML = '<li>stale</li>';
|
|
358
|
+
const items = makeItems(25);
|
|
502
359
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
360
|
+
Pagination.initPaginationItems(items, 10);
|
|
361
|
+
|
|
362
|
+
const liTexts = [...ul.querySelectorAll('li')].map(l => l.textContent.trim());
|
|
363
|
+
expect(liTexts).not.toContain('stale');
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
test('should show correct items on page click', () => {
|
|
367
|
+
setupPaginationUls(1);
|
|
368
|
+
const items = makeItems(25);
|
|
369
|
+
|
|
370
|
+
Pagination.initPaginationItems(items, 10);
|
|
371
|
+
|
|
372
|
+
// Click page 2
|
|
373
|
+
const page2 = document.querySelector('li[data-page="2"]');
|
|
374
|
+
page2.click();
|
|
375
|
+
|
|
376
|
+
// Items 11-20 (index 10-19) should be visible, others hidden
|
|
377
|
+
expect(items[9].style.display).toBe('none'); // item 10 (page 1)
|
|
378
|
+
expect(items[10].style.display).toBe(''); // item 11 (page 2)
|
|
379
|
+
expect(items[19].style.display).toBe(''); // item 20 (page 2)
|
|
380
|
+
expect(items[20].style.display).toBe('none'); // item 21 (page 3)
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
test('should mark clicked page as active and deactivate others', () => {
|
|
384
|
+
const [ul] = setupPaginationUls(1);
|
|
385
|
+
const items = makeItems(25);
|
|
386
|
+
|
|
387
|
+
Pagination.initPaginationItems(items, 10);
|
|
388
|
+
|
|
389
|
+
document.querySelector('li[data-page="2"]').click();
|
|
508
390
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
delete global.bootstrap;
|
|
513
|
-
delete global.UrlAndQueryString;
|
|
391
|
+
expect(ul.querySelector('li[data-page="1"]').classList.contains('active')).toBe(false);
|
|
392
|
+
expect(ul.querySelector('li[data-page="2"]').classList.contains('active')).toBe(true);
|
|
393
|
+
});
|
|
514
394
|
});
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
describe('Navigation', () => {
|
|
515
398
|
|
|
516
399
|
describe('activateTab', () => {
|
|
517
|
-
test('should
|
|
518
|
-
|
|
400
|
+
test('should add active class to clicked link', () => {
|
|
401
|
+
const { link2 } = setupNav();
|
|
402
|
+
|
|
403
|
+
Navigation.activateTab(link2);
|
|
519
404
|
|
|
520
|
-
expect(
|
|
521
|
-
expect(mockNavLink.removeClass).toHaveBeenCalledWith('active');
|
|
405
|
+
expect(link2.classList.contains('active')).toBe(true);
|
|
522
406
|
});
|
|
523
407
|
|
|
524
|
-
test('should remove active
|
|
525
|
-
|
|
408
|
+
test('should remove active class from other nav links', () => {
|
|
409
|
+
const { link1, link2 } = setupNav();
|
|
526
410
|
|
|
527
|
-
|
|
528
|
-
|
|
411
|
+
Navigation.activateTab(link2);
|
|
412
|
+
|
|
413
|
+
expect(link1.classList.contains('active')).toBe(false);
|
|
529
414
|
});
|
|
530
415
|
|
|
531
|
-
test('should
|
|
532
|
-
|
|
416
|
+
test('should show the corresponding tab pane', () => {
|
|
417
|
+
const { link2, pane2 } = setupNav();
|
|
418
|
+
|
|
419
|
+
Navigation.activateTab(link2);
|
|
533
420
|
|
|
534
|
-
expect(
|
|
421
|
+
expect(pane2.classList.contains('active')).toBe(true);
|
|
422
|
+
expect(pane2.classList.contains('show')).toBe(true);
|
|
535
423
|
});
|
|
536
424
|
|
|
537
|
-
test('should
|
|
538
|
-
|
|
425
|
+
test('should hide other tab panes', () => {
|
|
426
|
+
const { link2, pane1 } = setupNav();
|
|
539
427
|
|
|
540
|
-
|
|
541
|
-
|
|
428
|
+
Navigation.activateTab(link2);
|
|
429
|
+
|
|
430
|
+
expect(pane1.classList.contains('active')).toBe(false);
|
|
431
|
+
expect(pane1.classList.contains('show')).toBe(false);
|
|
542
432
|
});
|
|
543
433
|
|
|
544
434
|
test('should only process links with # href', () => {
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
435
|
+
// Add a link with external href to ensure it is skipped
|
|
436
|
+
const { link2 } = setupNav();
|
|
437
|
+
const externalLink = document.createElement('a');
|
|
438
|
+
externalLink.className = 'nav-link';
|
|
439
|
+
externalLink.href = 'http://example.com';
|
|
440
|
+
document.querySelector('.nav').appendChild(externalLink);
|
|
548
441
|
|
|
549
|
-
|
|
550
|
-
expect(mockNavLink.removeClass).toHaveBeenCalledWith('active');
|
|
442
|
+
expect(() => Navigation.activateTab(link2)).not.toThrow();
|
|
551
443
|
});
|
|
552
444
|
});
|
|
553
445
|
|
|
554
446
|
describe('showTab', () => {
|
|
555
447
|
test('should return early if bootstrap is undefined', () => {
|
|
448
|
+
const { link1 } = setupNav();
|
|
556
449
|
delete global.bootstrap;
|
|
557
450
|
|
|
558
|
-
Navigation.showTab(
|
|
559
|
-
|
|
560
|
-
// Should not throw
|
|
451
|
+
expect(() => Navigation.showTab(link1)).not.toThrow();
|
|
561
452
|
});
|
|
562
453
|
|
|
563
454
|
test('should create and show bootstrap tab when available', () => {
|
|
564
|
-
const
|
|
565
|
-
|
|
566
|
-
};
|
|
455
|
+
const { link1 } = setupNav();
|
|
456
|
+
const mockTab = { show: jest.fn() };
|
|
457
|
+
global.bootstrap = { Tab: jest.fn(() => mockTab) };
|
|
567
458
|
|
|
568
|
-
|
|
569
|
-
Tab: jest.fn(() => mockTab)
|
|
570
|
-
};
|
|
459
|
+
Navigation.showTab(link1);
|
|
571
460
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
expect(global.bootstrap.Tab).toHaveBeenCalledWith(mockA[0]);
|
|
461
|
+
expect(global.bootstrap.Tab).toHaveBeenCalledWith(link1);
|
|
575
462
|
expect(mockTab.show).toHaveBeenCalled();
|
|
576
463
|
});
|
|
577
464
|
});
|
|
578
465
|
|
|
579
466
|
describe('addTabInHistory', () => {
|
|
467
|
+
let setParamOfUrlSpy;
|
|
468
|
+
|
|
580
469
|
beforeEach(() => {
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
};
|
|
470
|
+
setParamOfUrlSpy = jest.spyOn(UrlAndQueryString, 'setParamOfUrl').mockImplementation((key, value, url) => `${url}&${key}=${value}`);
|
|
471
|
+
jest.spyOn(window.history, 'replaceState').mockImplementation(() => {});
|
|
472
|
+
jest.spyOn(window.history, 'pushState').mockImplementation(() => {});
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
afterEach(() => {
|
|
476
|
+
setParamOfUrlSpy.mockRestore();
|
|
584
477
|
});
|
|
585
478
|
|
|
586
479
|
test('should call UrlAndQueryString.setParamOfUrl with correct parameters', () => {
|
|
587
480
|
Navigation.addTabInHistory('tab1', 'tab', true);
|
|
588
481
|
|
|
589
|
-
expect(
|
|
590
|
-
'tab',
|
|
591
|
-
'tab1',
|
|
592
|
-
expect.any(String)
|
|
593
|
-
);
|
|
482
|
+
expect(setParamOfUrlSpy).toHaveBeenCalledWith('tab', 'tab1', expect.any(String));
|
|
594
483
|
});
|
|
595
484
|
|
|
596
485
|
test('should use default queryStringKey if not provided', () => {
|
|
597
486
|
Navigation.addTabInHistory('tab1');
|
|
598
487
|
|
|
599
|
-
expect(
|
|
600
|
-
'tab',
|
|
601
|
-
'tab1',
|
|
602
|
-
expect.any(String)
|
|
603
|
-
);
|
|
488
|
+
expect(setParamOfUrlSpy).toHaveBeenCalledWith('tab', 'tab1', expect.any(String));
|
|
604
489
|
});
|
|
605
490
|
|
|
606
|
-
test('should use replaceState
|
|
607
|
-
const replaceStateSpy = jest.spyOn(global.window.history, 'replaceState');
|
|
608
|
-
const pushStateSpy = jest.spyOn(global.window.history, 'pushState');
|
|
609
|
-
|
|
491
|
+
test('should use replaceState when replace is true', () => {
|
|
610
492
|
Navigation.addTabInHistory('tab1', 'tab', true);
|
|
611
493
|
|
|
612
|
-
expect(
|
|
613
|
-
expect(
|
|
614
|
-
|
|
615
|
-
replaceStateSpy.mockRestore();
|
|
616
|
-
pushStateSpy.mockRestore();
|
|
494
|
+
expect(window.history.replaceState).toHaveBeenCalled();
|
|
495
|
+
expect(window.history.pushState).not.toHaveBeenCalled();
|
|
617
496
|
});
|
|
618
497
|
|
|
619
498
|
test('should use pushState when replace is false', () => {
|
|
620
|
-
const replaceStateSpy = jest.spyOn(global.window.history, 'replaceState');
|
|
621
|
-
const pushStateSpy = jest.spyOn(global.window.history, 'pushState');
|
|
622
|
-
|
|
623
499
|
Navigation.addTabInHistory('tab1', 'tab', false);
|
|
624
500
|
|
|
625
|
-
expect(
|
|
626
|
-
expect(
|
|
627
|
-
|
|
628
|
-
replaceStateSpy.mockRestore();
|
|
629
|
-
pushStateSpy.mockRestore();
|
|
501
|
+
expect(window.history.pushState).toHaveBeenCalled();
|
|
502
|
+
expect(window.history.replaceState).not.toHaveBeenCalled();
|
|
630
503
|
});
|
|
631
504
|
|
|
632
505
|
test('should update URL with tab parameter', () => {
|
|
633
|
-
const replaceStateSpy = jest.spyOn(global.window.history, 'replaceState');
|
|
634
|
-
|
|
635
506
|
Navigation.addTabInHistory('tab2', 'activeTab', true);
|
|
636
507
|
|
|
637
|
-
expect(
|
|
508
|
+
expect(window.history.replaceState).toHaveBeenCalledWith(
|
|
638
509
|
'',
|
|
639
|
-
|
|
510
|
+
expect.any(String),
|
|
640
511
|
expect.stringContaining('activeTab=tab2')
|
|
641
512
|
);
|
|
642
|
-
|
|
643
|
-
replaceStateSpy.mockRestore();
|
|
644
513
|
});
|
|
645
514
|
});
|
|
646
515
|
});
|