@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
|
@@ -3,81 +3,71 @@
|
|
|
3
3
|
*/
|
|
4
4
|
const { ImportFromCsv } = require('../import_from_csv');
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
} else if (typeof collection === 'object') {
|
|
53
|
-
Object.keys(collection).forEach((key) => callback(key, collection[key]));
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
global.$.isEmptyObject = jest.fn((obj) => {
|
|
58
|
-
return Object.keys(obj).length === 0;
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
mockFormMatching = {
|
|
62
|
-
find: jest.fn(() => ({
|
|
63
|
-
each: jest.fn(),
|
|
64
|
-
prop: jest.fn().mockReturnThis()
|
|
65
|
-
}))
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
mockDivResult = {
|
|
69
|
-
find: jest.fn(() => ({
|
|
70
|
-
each: jest.fn(),
|
|
71
|
-
addClass: jest.fn().mockReturnThis(),
|
|
72
|
-
length: 0
|
|
73
|
-
}))
|
|
74
|
-
};
|
|
6
|
+
const originalDefaults = { ...ImportFromCsv._defaults };
|
|
7
|
+
|
|
8
|
+
function setupFormMatching() {
|
|
9
|
+
const form = document.createElement('div');
|
|
10
|
+
form.classList.add('form_matching');
|
|
11
|
+
form.innerHTML = `
|
|
12
|
+
<div class="import_matching_select_content"></div>
|
|
13
|
+
<div class="errors hide"></div>
|
|
14
|
+
<button type="submit">Import</button>`;
|
|
15
|
+
document.body.appendChild(form);
|
|
16
|
+
return form;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function setupDivResult(rows = []) {
|
|
20
|
+
const div = document.createElement('div');
|
|
21
|
+
div.classList.add('csv_result', 'hide');
|
|
22
|
+
const tbodyRows = rows.map((row, rowIndex) => {
|
|
23
|
+
const entries = Array.isArray(row) ? row.map((v, i) => [i, v]) : Object.entries(row);
|
|
24
|
+
const tds = entries.map(([key, val]) => `<td data-key="${key}">${val ?? ''}</td>`).join('');
|
|
25
|
+
return `<tr data-line="${rowIndex + 1}">
|
|
26
|
+
<td class="select_line_checkbox"><input type="checkbox" class="import_line_checkbox" checked="checked" /></td>
|
|
27
|
+
${tds}
|
|
28
|
+
<td class="edit_line_button"></td>
|
|
29
|
+
</tr>`;
|
|
30
|
+
}).join('');
|
|
31
|
+
div.innerHTML = `<table><tbody>${tbodyRows}</tbody></table>`;
|
|
32
|
+
document.body.appendChild(div);
|
|
33
|
+
return div;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function setupTableWithRow(cells = ['John', 'john@example.com'], formMatching = null) {
|
|
37
|
+
const fm = formMatching ?? setupFormMatching();
|
|
38
|
+
const table = document.createElement('table');
|
|
39
|
+
const tbody = document.createElement('tbody');
|
|
40
|
+
const tr = document.createElement('tr');
|
|
41
|
+
|
|
42
|
+
const checkboxTd = document.createElement('td');
|
|
43
|
+
checkboxTd.className = 'select_line_checkbox';
|
|
44
|
+
checkboxTd.innerHTML = '<input type="checkbox" class="import_line_checkbox" checked="checked" />';
|
|
45
|
+
tr.appendChild(checkboxTd);
|
|
46
|
+
|
|
47
|
+
cells.forEach((val, i) => {
|
|
48
|
+
const td = document.createElement('td');
|
|
49
|
+
td.dataset.key = String(i);
|
|
50
|
+
td.textContent = val ?? '';
|
|
51
|
+
tr.appendChild(td);
|
|
75
52
|
});
|
|
76
53
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
54
|
+
const editTd = document.createElement('td');
|
|
55
|
+
editTd.className = 'edit_line_button';
|
|
56
|
+
tr.appendChild(editTd);
|
|
57
|
+
|
|
58
|
+
tbody.appendChild(tr);
|
|
59
|
+
table.appendChild(tbody);
|
|
60
|
+
document.body.appendChild(table);
|
|
61
|
+
|
|
62
|
+
return { table, tr, editTd, fm };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
afterEach(() => {
|
|
66
|
+
document.body.innerHTML = '';
|
|
67
|
+
ImportFromCsv._defaults = { ...originalDefaults };
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('ImportFromCsv', () => {
|
|
81
71
|
|
|
82
72
|
describe('setDefault', () => {
|
|
83
73
|
test('should set default options', () => {
|
|
@@ -95,10 +85,7 @@ describe('ImportFromCsv', () => {
|
|
|
95
85
|
});
|
|
96
86
|
|
|
97
87
|
test('should use defaults in getErrorsHtmlOfImportData when no params passed', () => {
|
|
98
|
-
ImportFromCsv.setDefault({
|
|
99
|
-
errorMessageImportFailed: 'Default error',
|
|
100
|
-
lineLabel: 'Row {0}',
|
|
101
|
-
});
|
|
88
|
+
ImportFromCsv.setDefault({ errorMessageImportFailed: 'Default error', lineLabel: 'Row {0}' });
|
|
102
89
|
|
|
103
90
|
const html = ImportFromCsv.getErrorsHtmlOfImportData([{ line: 1, errors: ['Err'] }]);
|
|
104
91
|
|
|
@@ -108,23 +95,12 @@ describe('ImportFromCsv', () => {
|
|
|
108
95
|
|
|
109
96
|
test('should use defaults in displayFormMatching when no selectDefaultOptionLabel passed', () => {
|
|
110
97
|
ImportFromCsv.setDefault({ selectDefaultOptionLabel: 'Pick a column' });
|
|
98
|
+
const formMatching = setupFormMatching();
|
|
111
99
|
|
|
112
|
-
|
|
113
|
-
addClass: jest.fn().mockReturnThis(),
|
|
114
|
-
empty: jest.fn().mockReturnThis(),
|
|
115
|
-
append: jest.fn().mockReturnThis()
|
|
116
|
-
};
|
|
117
|
-
mockFormMatching.find = jest.fn((selector) => {
|
|
118
|
-
if (selector === '.import_matching_select_content') return mockSelectContent;
|
|
119
|
-
return { addClass: jest.fn().mockReturnThis(), removeClass: jest.fn().mockReturnThis() };
|
|
120
|
-
});
|
|
121
|
-
mockFormMatching.removeClass = jest.fn().mockReturnThis();
|
|
122
|
-
|
|
123
|
-
ImportFromCsv.displayFormMatching(mockFormMatching, { col: 'Col' }, ['Col'], true);
|
|
100
|
+
ImportFromCsv.displayFormMatching(formMatching, { col: 'Col' }, ['Col'], true);
|
|
124
101
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
expect(calls.some(s => s.includes('Pick a column'))).toBe(true);
|
|
102
|
+
const select = formMatching.querySelector('select');
|
|
103
|
+
expect(select.options[0].textContent).toBe('Pick a column');
|
|
128
104
|
});
|
|
129
105
|
});
|
|
130
106
|
|
|
@@ -151,8 +127,7 @@ describe('ImportFromCsv', () => {
|
|
|
151
127
|
});
|
|
152
128
|
|
|
153
129
|
test('should return false for empty array', () => {
|
|
154
|
-
|
|
155
|
-
expect(ImportFromCsv.isImportErrors(json)).toBe(false);
|
|
130
|
+
expect(ImportFromCsv.isImportErrors([])).toBe(false);
|
|
156
131
|
});
|
|
157
132
|
|
|
158
133
|
test('should return true when at least one item has line and errors', () => {
|
|
@@ -166,46 +141,31 @@ describe('ImportFromCsv', () => {
|
|
|
166
141
|
|
|
167
142
|
describe('getTabLink', () => {
|
|
168
143
|
test('should extract selected values from selects', () => {
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
144
|
+
const formMatching = setupFormMatching();
|
|
145
|
+
formMatching.querySelector('.import_matching_select_content').innerHTML = `
|
|
146
|
+
<select name="name"><option value="0" selected>Col0</option></select>
|
|
147
|
+
<select name="email"><option value="1" selected>Col1</option></select>
|
|
148
|
+
<select name="phone"><option value="-1" selected>--</option></select>`;
|
|
174
149
|
|
|
175
|
-
|
|
176
|
-
val: el.val,
|
|
177
|
-
prop: el.prop
|
|
178
|
-
}));
|
|
150
|
+
const result = ImportFromCsv.getTabLink(formMatching);
|
|
179
151
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
return {
|
|
183
|
-
each: jest.fn((callback) => {
|
|
184
|
-
mockSelects.forEach((select, idx) => {
|
|
185
|
-
callback(idx, select);
|
|
186
|
-
});
|
|
187
|
-
})
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
return { each: jest.fn() };
|
|
191
|
-
});
|
|
152
|
+
expect(result).toEqual({ name: '0', email: '1' });
|
|
153
|
+
});
|
|
192
154
|
|
|
193
|
-
|
|
155
|
+
test('should return empty object when no selects with valid values', () => {
|
|
156
|
+
const formMatching = setupFormMatching();
|
|
157
|
+
formMatching.querySelector('.import_matching_select_content').innerHTML = `
|
|
158
|
+
<select name="name"><option value="-1" selected>--</option></select>`;
|
|
194
159
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
});
|
|
160
|
+
const result = ImportFromCsv.getTabLink(formMatching);
|
|
161
|
+
|
|
162
|
+
expect(result).toEqual({});
|
|
199
163
|
});
|
|
200
164
|
|
|
201
|
-
test('should return empty object when no selects
|
|
202
|
-
|
|
203
|
-
each: jest.fn((callback) => {
|
|
204
|
-
// No selects or all have -1
|
|
205
|
-
})
|
|
206
|
-
}));
|
|
165
|
+
test('should return empty object when no selects', () => {
|
|
166
|
+
const formMatching = setupFormMatching();
|
|
207
167
|
|
|
208
|
-
const result = ImportFromCsv.getTabLink(
|
|
168
|
+
const result = ImportFromCsv.getTabLink(formMatching);
|
|
209
169
|
|
|
210
170
|
expect(result).toEqual({});
|
|
211
171
|
});
|
|
@@ -230,28 +190,20 @@ describe('ImportFromCsv', () => {
|
|
|
230
190
|
});
|
|
231
191
|
|
|
232
192
|
test('should mark error lines in divResult when provided', () => {
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
const mockTr = {
|
|
236
|
-
addClass: jest.fn().mockReturnThis()
|
|
237
|
-
};
|
|
193
|
+
const divResult = setupDivResult([['a'], ['b']]);
|
|
238
194
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
return mockTr;
|
|
242
|
-
}
|
|
243
|
-
return { addClass: jest.fn().mockReturnThis() };
|
|
244
|
-
});
|
|
195
|
+
const json = [{ line: 2, errors: ['Error'] }];
|
|
196
|
+
ImportFromCsv.getErrorsHtmlOfImportData(json, divResult, 'Import failed', 'Line {0}');
|
|
245
197
|
|
|
246
|
-
|
|
198
|
+
const tr2 = divResult.querySelector('table tr[data-line="2"]');
|
|
199
|
+
expect(tr2.classList.contains('danger')).toBe(true);
|
|
247
200
|
|
|
248
|
-
|
|
201
|
+
const tr1 = divResult.querySelector('table tr[data-line="1"]');
|
|
202
|
+
expect(tr1.classList.contains('danger')).toBe(false);
|
|
249
203
|
});
|
|
250
204
|
|
|
251
205
|
test('should handle empty errors array', () => {
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
const html = ImportFromCsv.getErrorsHtmlOfImportData(json, null, 'Import failed', 'Line {0}');
|
|
206
|
+
const html = ImportFromCsv.getErrorsHtmlOfImportData([], null, 'Import failed', 'Line {0}');
|
|
255
207
|
|
|
256
208
|
expect(html).toContain('Import failed');
|
|
257
209
|
expect(html).toContain('<ul>');
|
|
@@ -261,579 +213,366 @@ describe('ImportFromCsv', () => {
|
|
|
261
213
|
|
|
262
214
|
describe('getDataToImport', () => {
|
|
263
215
|
test('should extract data from checked rows', () => {
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
return { length: 1 };
|
|
273
|
-
}
|
|
274
|
-
if (selector === 'td[data-key="0"]') {
|
|
275
|
-
return { length: 1, text: jest.fn(() => 'John Doe') };
|
|
276
|
-
}
|
|
277
|
-
if (selector === 'td[data-key="1"]') {
|
|
278
|
-
return { length: 1, text: jest.fn(() => 'john@example.com') };
|
|
279
|
-
}
|
|
280
|
-
return { length: 0 };
|
|
281
|
-
})
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
const mockTr2 = {
|
|
285
|
-
find: jest.fn((selector) => {
|
|
286
|
-
if (selector === 'input.import_line_checkbox:checked') {
|
|
287
|
-
return { length: 0 }; // Not checked
|
|
288
|
-
}
|
|
289
|
-
return { length: 0 };
|
|
290
|
-
})
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
global.$ = jest.fn((el) => el);
|
|
294
|
-
global.$.each = jest.fn((collection, callback) => {
|
|
295
|
-
if (Array.isArray(collection)) {
|
|
296
|
-
collection.forEach((item, index) => callback(index, item));
|
|
297
|
-
} else {
|
|
298
|
-
Object.keys(collection).forEach((key) => callback(key, collection[key]));
|
|
299
|
-
}
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
mockDivResult.find = jest.fn((selector) => {
|
|
303
|
-
if (selector === 'table tbody tr') {
|
|
304
|
-
return [mockTr1, mockTr2];
|
|
305
|
-
}
|
|
306
|
-
return { length: 0 };
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
const result = ImportFromCsv.getDataToImport(mockDivResult, tabLink);
|
|
216
|
+
const divResult = setupDivResult([
|
|
217
|
+
['John Doe', 'john@example.com'],
|
|
218
|
+
['Jane', 'jane@example.com']
|
|
219
|
+
]);
|
|
220
|
+
// Uncheck second row
|
|
221
|
+
divResult.querySelectorAll('input.import_line_checkbox')[1].checked = false;
|
|
222
|
+
|
|
223
|
+
const result = ImportFromCsv.getDataToImport(divResult, { name: '0', email: '1' });
|
|
310
224
|
|
|
311
225
|
expect(result).toHaveLength(1);
|
|
312
|
-
expect(result[0]).toEqual({
|
|
313
|
-
line: 1,
|
|
314
|
-
name: 'John Doe',
|
|
315
|
-
email: 'john@example.com'
|
|
316
|
-
});
|
|
226
|
+
expect(result[0]).toEqual({ line: 1, name: 'John Doe', email: 'john@example.com' });
|
|
317
227
|
});
|
|
318
228
|
|
|
319
229
|
test('should skip rows without checked checkbox', () => {
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
const mockTr = {
|
|
323
|
-
find: jest.fn(() => ({ length: 0 })) // No checked checkbox
|
|
324
|
-
};
|
|
230
|
+
const divResult = setupDivResult([['John']]);
|
|
231
|
+
divResult.querySelector('input.import_line_checkbox').checked = false;
|
|
325
232
|
|
|
326
|
-
|
|
327
|
-
global.$.each = jest.fn((collection, callback) => {
|
|
328
|
-
if (Array.isArray(collection)) {
|
|
329
|
-
collection.forEach((item, index) => callback(index, item));
|
|
330
|
-
}
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
mockDivResult.find = jest.fn(() => [mockTr]);
|
|
334
|
-
|
|
335
|
-
const result = ImportFromCsv.getDataToImport(mockDivResult, tabLink);
|
|
233
|
+
const result = ImportFromCsv.getDataToImport(divResult, { name: '0' });
|
|
336
234
|
|
|
337
235
|
expect(result).toEqual([]);
|
|
338
236
|
});
|
|
339
237
|
|
|
340
238
|
test('should handle missing columns gracefully', () => {
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
};
|
|
346
|
-
|
|
347
|
-
const mockTr = {
|
|
348
|
-
find: jest.fn((selector) => {
|
|
349
|
-
if (selector === 'input.import_line_checkbox:checked') {
|
|
350
|
-
return { length: 1 };
|
|
351
|
-
}
|
|
352
|
-
if (selector === 'td[data-key="0"]') {
|
|
353
|
-
return { length: 1, text: jest.fn(() => 'John') };
|
|
354
|
-
}
|
|
355
|
-
if (selector === 'td[data-key="1"]') {
|
|
356
|
-
return { length: 1, text: jest.fn(() => 'john@test.com') };
|
|
357
|
-
}
|
|
358
|
-
if (selector === 'td[data-key="2"]') {
|
|
359
|
-
return { length: 0 }; // Missing
|
|
360
|
-
}
|
|
361
|
-
return { length: 0 };
|
|
362
|
-
})
|
|
363
|
-
};
|
|
364
|
-
|
|
365
|
-
global.$ = jest.fn((el) => el);
|
|
366
|
-
global.$.each = jest.fn((collection, callback) => {
|
|
367
|
-
if (Array.isArray(collection)) {
|
|
368
|
-
collection.forEach((item, index) => callback(index, item));
|
|
369
|
-
} else {
|
|
370
|
-
Object.keys(collection).forEach((key) => callback(key, collection[key]));
|
|
371
|
-
}
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
mockDivResult.find = jest.fn(() => [mockTr]);
|
|
375
|
-
|
|
376
|
-
const result = ImportFromCsv.getDataToImport(mockDivResult, tabLink);
|
|
239
|
+
const divResult = setupDivResult([['John', 'john@test.com']]);
|
|
240
|
+
// tabLink references key "2" which has no td
|
|
241
|
+
|
|
242
|
+
const result = ImportFromCsv.getDataToImport(divResult, { name: '0', email: '1', phone: '2' });
|
|
377
243
|
|
|
378
244
|
expect(result).toHaveLength(1);
|
|
379
|
-
expect(result[0]).toEqual({
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
245
|
+
expect(result[0]).toEqual({ line: 1, name: 'John', email: 'john@test.com' });
|
|
246
|
+
expect(result[0].phone).toBeUndefined();
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
test('should include line number in each result', () => {
|
|
250
|
+
const divResult = setupDivResult([['A'], ['B'], ['C']]);
|
|
251
|
+
|
|
252
|
+
const result = ImportFromCsv.getDataToImport(divResult, { val: '0' });
|
|
253
|
+
|
|
254
|
+
expect(result[0].line).toBe(1);
|
|
255
|
+
expect(result[1].line).toBe(2);
|
|
256
|
+
expect(result[2].line).toBe(3);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test('should return empty array for empty table', () => {
|
|
260
|
+
const divResult = setupDivResult([]);
|
|
261
|
+
|
|
262
|
+
const result = ImportFromCsv.getDataToImport(divResult, { name: '0' });
|
|
263
|
+
|
|
264
|
+
expect(result).toEqual([]);
|
|
385
265
|
});
|
|
386
266
|
});
|
|
387
267
|
|
|
388
268
|
describe('displayFormMatching', () => {
|
|
389
269
|
test('should create select elements for each import column', () => {
|
|
390
|
-
const
|
|
391
|
-
|
|
392
|
-
email: 'Email',
|
|
393
|
-
phone: 'Phone'
|
|
394
|
-
};
|
|
270
|
+
const formMatching = setupFormMatching();
|
|
271
|
+
const importColumns = { name: 'Name', email: 'Email', phone: 'Phone' };
|
|
395
272
|
const header = ['Name', 'Email', 'Phone', 'Address'];
|
|
396
|
-
const hasHeader = true;
|
|
397
|
-
|
|
398
|
-
const mockSelectContent = {
|
|
399
|
-
addClass: jest.fn().mockReturnThis(),
|
|
400
|
-
empty: jest.fn().mockReturnThis(),
|
|
401
|
-
append: jest.fn()
|
|
402
|
-
};
|
|
403
273
|
|
|
404
|
-
|
|
405
|
-
addClass: jest.fn().mockReturnThis()
|
|
406
|
-
};
|
|
274
|
+
ImportFromCsv.displayFormMatching(formMatching, importColumns, header, true);
|
|
407
275
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
}
|
|
415
|
-
return { addClass: jest.fn().mockReturnThis(), removeClass: jest.fn().mockReturnThis() };
|
|
416
|
-
});
|
|
276
|
+
const selects = formMatching.querySelectorAll('select');
|
|
277
|
+
expect(selects).toHaveLength(3);
|
|
278
|
+
expect(selects[0].name).toBe('name');
|
|
279
|
+
expect(selects[1].name).toBe('email');
|
|
280
|
+
expect(selects[2].name).toBe('phone');
|
|
281
|
+
});
|
|
417
282
|
|
|
418
|
-
|
|
283
|
+
test('should populate options from header', () => {
|
|
284
|
+
const formMatching = setupFormMatching();
|
|
285
|
+
const header = ['Col A', 'Col B'];
|
|
419
286
|
|
|
420
|
-
ImportFromCsv.displayFormMatching(
|
|
287
|
+
ImportFromCsv.displayFormMatching(formMatching, { field: 'Field' }, header, true);
|
|
421
288
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
expect(
|
|
289
|
+
const select = formMatching.querySelector('select');
|
|
290
|
+
const optionValues = [...select.options].map(o => o.value);
|
|
291
|
+
expect(optionValues).toContain('Col A');
|
|
292
|
+
expect(optionValues).toContain('Col B');
|
|
293
|
+
expect(optionValues).toContain('-1');
|
|
425
294
|
});
|
|
426
295
|
|
|
427
|
-
test('should use index as value when hasHeader is false', () => {
|
|
428
|
-
const
|
|
296
|
+
test('should use index as option value when hasHeader is false', () => {
|
|
297
|
+
const formMatching = setupFormMatching();
|
|
429
298
|
const header = ['Col1', 'Col2'];
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
299
|
+
|
|
300
|
+
ImportFromCsv.displayFormMatching(formMatching, { field: 'Field' }, header, false);
|
|
301
|
+
|
|
302
|
+
const select = formMatching.querySelector('select');
|
|
303
|
+
const optionValues = [...select.options].map(o => o.value);
|
|
304
|
+
expect(optionValues).toContain('0');
|
|
305
|
+
expect(optionValues).toContain('1');
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
test('should auto-select option matching column label', () => {
|
|
309
|
+
const formMatching = setupFormMatching();
|
|
310
|
+
const importColumns = { name: 'Name', email: 'Email' };
|
|
311
|
+
const header = ['Name', 'Email', 'Phone'];
|
|
312
|
+
|
|
313
|
+
ImportFromCsv.displayFormMatching(formMatching, importColumns, header, true);
|
|
314
|
+
|
|
315
|
+
const nameSelect = formMatching.querySelector('select[name="name"]');
|
|
316
|
+
expect(nameSelect.value).toBe('Name');
|
|
317
|
+
|
|
318
|
+
const emailSelect = formMatching.querySelector('select[name="email"]');
|
|
319
|
+
expect(emailSelect.value).toBe('Email');
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
test('should show formMatching and hide errors', () => {
|
|
323
|
+
const formMatching = setupFormMatching();
|
|
324
|
+
formMatching.classList.add('hide');
|
|
325
|
+
|
|
326
|
+
ImportFromCsv.displayFormMatching(formMatching, { col: 'Col' }, ['Col'], true);
|
|
327
|
+
|
|
328
|
+
expect(formMatching.classList.contains('hide')).toBe(false);
|
|
329
|
+
expect(formMatching.querySelector('div.errors').classList.contains('hide')).toBe(true);
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
test('should clear previous select content', () => {
|
|
333
|
+
const formMatching = setupFormMatching();
|
|
334
|
+
|
|
335
|
+
ImportFromCsv.displayFormMatching(formMatching, { a: 'A' }, ['A'], true);
|
|
336
|
+
ImportFromCsv.displayFormMatching(formMatching, { b: 'B', c: 'C' }, ['B', 'C'], true);
|
|
337
|
+
|
|
338
|
+
const selects = formMatching.querySelectorAll('select');
|
|
339
|
+
expect(selects).toHaveLength(2);
|
|
454
340
|
});
|
|
455
341
|
});
|
|
456
342
|
|
|
457
343
|
describe('displayData', () => {
|
|
458
344
|
test('should create table with header when header is provided', () => {
|
|
459
|
-
const
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
345
|
+
const formMatching = setupFormMatching();
|
|
346
|
+
const divResult = document.createElement('div');
|
|
347
|
+
divResult.classList.add('csv_result', 'hide');
|
|
348
|
+
document.body.appendChild(divResult);
|
|
349
|
+
|
|
350
|
+
const data = [['John', 'john@example.com'], ['Jane', 'jane@example.com']];
|
|
463
351
|
const header = ['Name', 'Email'];
|
|
464
352
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
length: 1
|
|
472
|
-
};
|
|
473
|
-
|
|
474
|
-
mockDivResult.find = jest.fn((selector) => {
|
|
475
|
-
if (selector === 'table') {
|
|
476
|
-
return mockTable;
|
|
477
|
-
}
|
|
478
|
-
return { length: 0 };
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
mockDivResult.removeClass = jest.fn().mockReturnThis();
|
|
482
|
-
mockDivResult.append = jest.fn().mockReturnThis();
|
|
483
|
-
|
|
484
|
-
ImportFromCsv.displayData(mockDivResult, data, header, mockFormMatching);
|
|
485
|
-
|
|
486
|
-
expect(mockTable.empty).toHaveBeenCalled();
|
|
487
|
-
expect(mockTable.html).toHaveBeenCalled();
|
|
488
|
-
const htmlContent = mockTable.html.mock.calls[0][0];
|
|
489
|
-
expect(htmlContent).toContain('<thead>');
|
|
490
|
-
expect(htmlContent).toContain('Name');
|
|
491
|
-
expect(htmlContent).toContain('Email');
|
|
492
|
-
expect(htmlContent).toContain('<tbody>');
|
|
493
|
-
expect(htmlContent).toContain('John');
|
|
494
|
-
expect(htmlContent).toContain('jane@example.com');
|
|
495
|
-
expect(mockDivResult.removeClass).toHaveBeenCalledWith('hide');
|
|
353
|
+
ImportFromCsv.displayData(divResult, data, header, formMatching);
|
|
354
|
+
|
|
355
|
+
const thead = divResult.querySelector('thead');
|
|
356
|
+
expect(thead).not.toBeNull();
|
|
357
|
+
expect(thead.textContent).toContain('Name');
|
|
358
|
+
expect(thead.textContent).toContain('Email');
|
|
496
359
|
});
|
|
497
360
|
|
|
498
361
|
test('should create table without header when header is null', () => {
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
const mockTable = {
|
|
504
|
-
empty: jest.fn().mockReturnThis(),
|
|
505
|
-
html: jest.fn().mockReturnThis(),
|
|
506
|
-
find: jest.fn(() => ({
|
|
507
|
-
each: jest.fn()
|
|
508
|
-
})),
|
|
509
|
-
length: 1
|
|
510
|
-
};
|
|
511
|
-
|
|
512
|
-
mockDivResult.find = jest.fn(() => mockTable);
|
|
513
|
-
mockDivResult.removeClass = jest.fn().mockReturnThis();
|
|
362
|
+
const formMatching = setupFormMatching();
|
|
363
|
+
const divResult = document.createElement('div');
|
|
364
|
+
document.body.appendChild(divResult);
|
|
514
365
|
|
|
515
|
-
ImportFromCsv.displayData(
|
|
366
|
+
ImportFromCsv.displayData(divResult, [['John']], null, formMatching);
|
|
516
367
|
|
|
517
|
-
|
|
518
|
-
expect(
|
|
519
|
-
expect(htmlContent).toContain('<tbody>');
|
|
368
|
+
expect(divResult.querySelector('thead')).toBeNull();
|
|
369
|
+
expect(divResult.querySelector('tbody')).not.toBeNull();
|
|
520
370
|
});
|
|
521
371
|
|
|
522
372
|
test('should add checkboxes for each row', () => {
|
|
523
|
-
const
|
|
373
|
+
const formMatching = setupFormMatching();
|
|
374
|
+
const divResult = document.createElement('div');
|
|
375
|
+
document.body.appendChild(divResult);
|
|
524
376
|
|
|
525
|
-
|
|
526
|
-
empty: jest.fn().mockReturnThis(),
|
|
527
|
-
html: jest.fn().mockReturnThis(),
|
|
528
|
-
find: jest.fn(() => ({
|
|
529
|
-
each: jest.fn()
|
|
530
|
-
})),
|
|
531
|
-
length: 1
|
|
532
|
-
};
|
|
377
|
+
ImportFromCsv.displayData(divResult, [['John'], ['Jane']], null, formMatching);
|
|
533
378
|
|
|
534
|
-
|
|
535
|
-
|
|
379
|
+
const checkboxes = divResult.querySelectorAll('input.import_line_checkbox');
|
|
380
|
+
expect(checkboxes).toHaveLength(2);
|
|
381
|
+
expect(checkboxes[0].checked).toBe(true);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
test('should set data-line attribute on rows', () => {
|
|
385
|
+
const formMatching = setupFormMatching();
|
|
386
|
+
const divResult = document.createElement('div');
|
|
387
|
+
document.body.appendChild(divResult);
|
|
536
388
|
|
|
537
|
-
ImportFromCsv.displayData(
|
|
389
|
+
ImportFromCsv.displayData(divResult, [['A'], ['B'], ['C']], null, formMatching);
|
|
538
390
|
|
|
539
|
-
|
|
540
|
-
expect(
|
|
541
|
-
expect(
|
|
542
|
-
expect(htmlContent).toContain('data-line="1"');
|
|
543
|
-
expect(htmlContent).toContain('data-line="2"');
|
|
391
|
+
expect(divResult.querySelector('tr[data-line="1"]')).not.toBeNull();
|
|
392
|
+
expect(divResult.querySelector('tr[data-line="2"]')).not.toBeNull();
|
|
393
|
+
expect(divResult.querySelector('tr[data-line="3"]')).not.toBeNull();
|
|
544
394
|
});
|
|
545
395
|
|
|
546
396
|
test('should create table if it does not exist', () => {
|
|
547
|
-
const
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
};
|
|
556
|
-
});
|
|
557
|
-
|
|
558
|
-
let appendedTable = null;
|
|
559
|
-
mockDivResult.append = jest.fn((html) => {
|
|
560
|
-
appendedTable = html;
|
|
561
|
-
// After append, table exists
|
|
562
|
-
mockDivResult.find = jest.fn((sel) => {
|
|
563
|
-
if (sel === 'table') {
|
|
564
|
-
return {
|
|
565
|
-
empty: jest.fn().mockReturnThis(),
|
|
566
|
-
html: jest.fn().mockReturnThis(),
|
|
567
|
-
find: jest.fn(() => ({ each: jest.fn() })),
|
|
568
|
-
length: 1
|
|
569
|
-
};
|
|
570
|
-
}
|
|
571
|
-
return { each: jest.fn() };
|
|
572
|
-
});
|
|
573
|
-
return mockDivResult;
|
|
574
|
-
});
|
|
575
|
-
|
|
576
|
-
mockDivResult.removeClass = jest.fn().mockReturnThis();
|
|
577
|
-
|
|
578
|
-
ImportFromCsv.displayData(mockDivResult, data, null, mockFormMatching);
|
|
579
|
-
|
|
580
|
-
expect(mockDivResult.append).toHaveBeenCalledWith('<table class="table table-sm table-bordered"></table>');
|
|
397
|
+
const formMatching = setupFormMatching();
|
|
398
|
+
const divResult = document.createElement('div');
|
|
399
|
+
document.body.appendChild(divResult);
|
|
400
|
+
|
|
401
|
+
ImportFromCsv.displayData(divResult, [['test']], null, formMatching);
|
|
402
|
+
|
|
403
|
+
expect(divResult.querySelector('table')).not.toBeNull();
|
|
404
|
+
expect(divResult.querySelector('table').classList.contains('table')).toBe(true);
|
|
581
405
|
});
|
|
582
406
|
|
|
583
407
|
test('should handle null values in data', () => {
|
|
584
|
-
const
|
|
585
|
-
|
|
586
|
-
|
|
408
|
+
const formMatching = setupFormMatching();
|
|
409
|
+
const divResult = document.createElement('div');
|
|
410
|
+
document.body.appendChild(divResult);
|
|
411
|
+
|
|
412
|
+
ImportFromCsv.displayData(divResult, [['John', null, 'test@example.com']], null, formMatching);
|
|
413
|
+
|
|
414
|
+
const tds = divResult.querySelectorAll('tbody td[data-key]');
|
|
415
|
+
expect(tds[0].textContent).toBe('John');
|
|
416
|
+
expect(tds[1].textContent).toBe('');
|
|
417
|
+
expect(tds[2].textContent).toBe('test@example.com');
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
test('should show divResult after populating data', () => {
|
|
421
|
+
const formMatching = setupFormMatching();
|
|
422
|
+
const divResult = document.createElement('div');
|
|
423
|
+
divResult.classList.add('hide');
|
|
424
|
+
document.body.appendChild(divResult);
|
|
425
|
+
|
|
426
|
+
ImportFromCsv.displayData(divResult, [['A']], null, formMatching);
|
|
427
|
+
|
|
428
|
+
expect(divResult.classList.contains('hide')).toBe(false);
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
test('should add edit links to each row', () => {
|
|
432
|
+
const formMatching = setupFormMatching();
|
|
433
|
+
const divResult = document.createElement('div');
|
|
434
|
+
document.body.appendChild(divResult);
|
|
435
|
+
|
|
436
|
+
ImportFromCsv.displayData(divResult, [['A'], ['B']], null, formMatching);
|
|
437
|
+
|
|
438
|
+
const editLinks = divResult.querySelectorAll('a.import_edit_line');
|
|
439
|
+
expect(editLinks).toHaveLength(2);
|
|
440
|
+
});
|
|
587
441
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
each: jest.fn()
|
|
593
|
-
})),
|
|
594
|
-
length: 1
|
|
595
|
-
};
|
|
442
|
+
test('should handle object-based rows (with header)', () => {
|
|
443
|
+
const formMatching = setupFormMatching();
|
|
444
|
+
const divResult = document.createElement('div');
|
|
445
|
+
document.body.appendChild(divResult);
|
|
596
446
|
|
|
597
|
-
|
|
598
|
-
|
|
447
|
+
const data = [{ name: 'John', email: 'john@example.com' }];
|
|
448
|
+
const header = ['name', 'email'];
|
|
599
449
|
|
|
600
|
-
ImportFromCsv.displayData(
|
|
450
|
+
ImportFromCsv.displayData(divResult, data, header, formMatching);
|
|
601
451
|
|
|
602
|
-
|
|
603
|
-
expect(
|
|
604
|
-
expect(htmlContent).toContain('test@example.com');
|
|
605
|
-
// Null should be replaced with empty string
|
|
606
|
-
expect(htmlContent).toMatch(/data-key="1">(<\/td>|<)/);
|
|
452
|
+
expect(divResult.querySelector('td[data-key="name"]').textContent).toBe('John');
|
|
453
|
+
expect(divResult.querySelector('td[data-key="email"]').textContent).toBe('john@example.com');
|
|
607
454
|
});
|
|
608
455
|
});
|
|
609
456
|
|
|
610
457
|
describe('initEditLink', () => {
|
|
611
|
-
test('should
|
|
612
|
-
const
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
return mockInput;
|
|
669
|
-
})
|
|
670
|
-
};
|
|
671
|
-
|
|
672
|
-
const mockEditTd = {
|
|
673
|
-
html: jest.fn().mockReturnThis(),
|
|
674
|
-
find: jest.fn().mockReturnThis(),
|
|
675
|
-
parent: jest.fn(() => ({ parent: jest.fn(() => mockTr) }))
|
|
676
|
-
};
|
|
677
|
-
|
|
678
|
-
let clickHandler = null;
|
|
679
|
-
const mockValidateLink = {
|
|
680
|
-
click: jest.fn().mockReturnThis()
|
|
681
|
-
};
|
|
682
|
-
mockEditTd.find = jest.fn((selector) => {
|
|
683
|
-
if (selector === 'a.import_edit_line') {
|
|
684
|
-
return {
|
|
685
|
-
click: jest.fn((handler) => {
|
|
686
|
-
clickHandler = handler;
|
|
687
|
-
return mockEditTd;
|
|
688
|
-
})
|
|
689
|
-
};
|
|
690
|
-
}
|
|
691
|
-
if (selector === 'a.import_validate_line') {
|
|
692
|
-
return mockValidateLink;
|
|
693
|
-
}
|
|
694
|
-
if (selector === 'td input[type="text"]') {
|
|
695
|
-
return { length: 1 };
|
|
696
|
-
}
|
|
697
|
-
return mockEditTd;
|
|
698
|
-
});
|
|
699
|
-
mockEditTd.closest = jest.fn(() => ({
|
|
700
|
-
find: jest.fn((selector) => {
|
|
701
|
-
if (selector === 'td input[type="text"]') {
|
|
702
|
-
return { length: 1 };
|
|
703
|
-
}
|
|
704
|
-
return { length: 0 };
|
|
705
|
-
})
|
|
706
|
-
}));
|
|
707
|
-
|
|
708
|
-
global.$ = jest.fn((selector) => {
|
|
709
|
-
if (typeof selector === 'string') {
|
|
710
|
-
if (selector.includes('<a')) {
|
|
711
|
-
return mockEditTd;
|
|
712
|
-
}
|
|
713
|
-
if (selector.includes('<input')) {
|
|
714
|
-
return mockInput;
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
if (selector === mockTdToEdit) return mockTdToEdit;
|
|
718
|
-
return { parent: jest.fn(() => ({ parent: jest.fn(() => mockTr) })) };
|
|
719
|
-
});
|
|
720
|
-
|
|
721
|
-
mockFormMatching.find = jest.fn(() => ({
|
|
722
|
-
prop: jest.fn().mockReturnThis()
|
|
723
|
-
}));
|
|
724
|
-
|
|
725
|
-
ImportFromCsv.initEditLink(mockFormMatching, mockEditTd);
|
|
726
|
-
|
|
727
|
-
// Trigger the click
|
|
728
|
-
clickHandler();
|
|
729
|
-
|
|
730
|
-
expect(mockTdToEdit.data).toHaveBeenCalledWith('original_value', 'John Doe');
|
|
731
|
-
expect(mockTdToEdit.html).toHaveBeenCalled();
|
|
458
|
+
test('should set up edit link in td', () => {
|
|
459
|
+
const { editTd, fm } = setupTableWithRow();
|
|
460
|
+
|
|
461
|
+
ImportFromCsv.initEditLink(fm, editTd);
|
|
462
|
+
|
|
463
|
+
expect(editTd.querySelector('a.import_edit_line')).not.toBeNull();
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
test('should convert cells to inputs on click', () => {
|
|
467
|
+
const { editTd, fm } = setupTableWithRow(['John', 'john@example.com']);
|
|
468
|
+
|
|
469
|
+
ImportFromCsv.initEditLink(fm, editTd);
|
|
470
|
+
editTd.querySelector('a.import_edit_line').click();
|
|
471
|
+
|
|
472
|
+
const inputs = document.querySelectorAll('td[data-key] input[type="text"]');
|
|
473
|
+
expect(inputs).toHaveLength(2);
|
|
474
|
+
expect(inputs[0].value).toBe('John');
|
|
475
|
+
expect(inputs[1].value).toBe('john@example.com');
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
test('should disable submit button when editing', () => {
|
|
479
|
+
const { editTd, fm } = setupTableWithRow(['John']);
|
|
480
|
+
|
|
481
|
+
ImportFromCsv.initEditLink(fm, editTd);
|
|
482
|
+
editTd.querySelector('a.import_edit_line').click();
|
|
483
|
+
|
|
484
|
+
expect(fm.querySelector('button[type="submit"]').disabled).toBe(true);
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
test('should save original cell value in dataset', () => {
|
|
488
|
+
const { editTd, fm } = setupTableWithRow(['John']);
|
|
489
|
+
|
|
490
|
+
ImportFromCsv.initEditLink(fm, editTd);
|
|
491
|
+
editTd.querySelector('a.import_edit_line').click();
|
|
492
|
+
|
|
493
|
+
const dataCell = document.querySelector('td[data-key="0"]');
|
|
494
|
+
expect(dataCell.dataset.original_value).toBe('John');
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
test('should replace edit link with validate link after click', () => {
|
|
498
|
+
const { editTd, fm } = setupTableWithRow(['John']);
|
|
499
|
+
|
|
500
|
+
ImportFromCsv.initEditLink(fm, editTd);
|
|
501
|
+
editTd.querySelector('a.import_edit_line').click();
|
|
502
|
+
|
|
503
|
+
expect(editTd.querySelector('a.import_edit_line')).toBeNull();
|
|
504
|
+
expect(editTd.querySelector('a.import_validate_line')).not.toBeNull();
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
test('should not affect select_line_checkbox or edit_line_button cells', () => {
|
|
508
|
+
const { editTd, fm } = setupTableWithRow(['John']);
|
|
509
|
+
|
|
510
|
+
ImportFromCsv.initEditLink(fm, editTd);
|
|
511
|
+
editTd.querySelector('a.import_edit_line').click();
|
|
512
|
+
|
|
513
|
+
const checkboxTd = document.querySelector('td.select_line_checkbox');
|
|
514
|
+
expect(checkboxTd.querySelector('input[type="text"]')).toBeNull();
|
|
732
515
|
});
|
|
733
516
|
});
|
|
734
517
|
|
|
735
518
|
describe('initValidateLine', () => {
|
|
736
|
-
test('should
|
|
737
|
-
const
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
});
|
|
761
|
-
|
|
762
|
-
ImportFromCsv.
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
html: jest.fn().mockReturnThis(),
|
|
794
|
-
find: jest.fn().mockReturnThis(),
|
|
795
|
-
parent: jest.fn(() => ({ parent: jest.fn(() => mockTr) })),
|
|
796
|
-
closest: jest.fn(() => ({
|
|
797
|
-
find: jest.fn(() => ({ length: 0 }))
|
|
798
|
-
}))
|
|
799
|
-
};
|
|
800
|
-
|
|
801
|
-
let clickHandler = null;
|
|
802
|
-
mockValidateTd.find = jest.fn((selector) => {
|
|
803
|
-
if (selector === 'a.import_validate_line') {
|
|
804
|
-
return {
|
|
805
|
-
click: jest.fn((handler) => {
|
|
806
|
-
clickHandler = handler;
|
|
807
|
-
return mockValidateTd;
|
|
808
|
-
})
|
|
809
|
-
};
|
|
810
|
-
}
|
|
811
|
-
if (selector === 'a.import_edit_line') {
|
|
812
|
-
return { click: jest.fn().mockReturnThis() };
|
|
813
|
-
}
|
|
814
|
-
if (selector === 'td input[type="text"]') {
|
|
815
|
-
return { length: 0 };
|
|
816
|
-
}
|
|
817
|
-
return mockValidateTd;
|
|
818
|
-
});
|
|
819
|
-
global.$ = jest.fn((selector) => {
|
|
820
|
-
if (typeof selector === 'string' && selector.includes('<a')) {
|
|
821
|
-
return mockValidateTd;
|
|
822
|
-
}
|
|
823
|
-
if (selector === mockTdWithInput) return mockTdWithInput;
|
|
824
|
-
return { parent: jest.fn(() => ({ parent: jest.fn(() => mockTr) })) };
|
|
825
|
-
});
|
|
826
|
-
|
|
827
|
-
mockFormMatching.find = jest.fn(() => ({
|
|
828
|
-
prop: jest.fn().mockReturnThis()
|
|
829
|
-
}));
|
|
830
|
-
|
|
831
|
-
ImportFromCsv.initValidateLine(mockFormMatching, mockValidateTd);
|
|
832
|
-
|
|
833
|
-
// Trigger the click
|
|
834
|
-
clickHandler();
|
|
835
|
-
|
|
836
|
-
expect(mockTdWithInput.html).toHaveBeenCalledWith('Updated Value');
|
|
519
|
+
test('should set up validate link in td', () => {
|
|
520
|
+
const { editTd, fm } = setupTableWithRow();
|
|
521
|
+
|
|
522
|
+
ImportFromCsv.initValidateLine(fm, editTd);
|
|
523
|
+
|
|
524
|
+
expect(editTd.querySelector('a.import_validate_line')).not.toBeNull();
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
test('should convert inputs back to text on click', () => {
|
|
528
|
+
const { editTd, fm } = setupTableWithRow(['John']);
|
|
529
|
+
|
|
530
|
+
// First enter edit mode
|
|
531
|
+
ImportFromCsv.initEditLink(fm, editTd);
|
|
532
|
+
editTd.querySelector('a.import_edit_line').click();
|
|
533
|
+
|
|
534
|
+
// Now validate
|
|
535
|
+
editTd.querySelector('a.import_validate_line').click();
|
|
536
|
+
|
|
537
|
+
const dataCell = document.querySelector('td[data-key="0"]');
|
|
538
|
+
expect(dataCell.querySelector('input')).toBeNull();
|
|
539
|
+
expect(dataCell.textContent).toBe('John');
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
test('should use input value when restoring cell content', () => {
|
|
543
|
+
const { editTd, fm } = setupTableWithRow(['John']);
|
|
544
|
+
|
|
545
|
+
ImportFromCsv.initEditLink(fm, editTd);
|
|
546
|
+
editTd.querySelector('a.import_edit_line').click();
|
|
547
|
+
|
|
548
|
+
// Change input value
|
|
549
|
+
document.querySelector('td[data-key="0"] input').value = 'Jane';
|
|
550
|
+
|
|
551
|
+
editTd.querySelector('a.import_validate_line').click();
|
|
552
|
+
|
|
553
|
+
expect(document.querySelector('td[data-key="0"]').textContent).toBe('Jane');
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
test('should re-enable submit button when no inputs remain', () => {
|
|
557
|
+
const { editTd, fm } = setupTableWithRow(['John']);
|
|
558
|
+
fm.querySelector('button[type="submit"]').disabled = true;
|
|
559
|
+
|
|
560
|
+
ImportFromCsv.initEditLink(fm, editTd);
|
|
561
|
+
editTd.querySelector('a.import_edit_line').click();
|
|
562
|
+
editTd.querySelector('a.import_validate_line').click();
|
|
563
|
+
|
|
564
|
+
expect(fm.querySelector('button[type="submit"]').disabled).toBe(false);
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
test('should replace validate link with edit link after click', () => {
|
|
568
|
+
const { editTd, fm } = setupTableWithRow(['John']);
|
|
569
|
+
|
|
570
|
+
ImportFromCsv.initEditLink(fm, editTd);
|
|
571
|
+
editTd.querySelector('a.import_edit_line').click();
|
|
572
|
+
editTd.querySelector('a.import_validate_line').click();
|
|
573
|
+
|
|
574
|
+
expect(editTd.querySelector('a.import_validate_line')).toBeNull();
|
|
575
|
+
expect(editTd.querySelector('a.import_edit_line')).not.toBeNull();
|
|
837
576
|
});
|
|
838
577
|
});
|
|
839
578
|
});
|