@hrforte/react-pivottable 2.2.2 → 3.0.0
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/dist/PivotDataWorker.js +219 -13
- package/dist/PivotDataWorker.js.map +1 -1
- package/dist/PivotTable.js +2 -1
- package/dist/PivotTable.js.map +1 -1
- package/dist/PivotTableUI.js +39 -66
- package/dist/PivotTableUI.js.map +1 -1
- package/dist/TableRenderers.js +568 -106
- package/dist/TableRenderers.js.map +1 -1
- package/dist/Utilities.js +104 -179
- package/dist/Utilities.js.map +1 -1
- package/dist/__tests__/PivotDataWorker-test.js +437 -7
- package/dist/__tests__/PivotDataWorker-test.js.map +1 -1
- package/dist/__tests__/PivotTableUI-processing-test.js +41 -2
- package/dist/__tests__/PivotTableUI-processing-test.js.map +1 -1
- package/dist/__tests__/TableRenderers-showNumericalOrder-test.js +449 -0
- package/dist/__tests__/TableRenderers-showNumericalOrder-test.js.map +1 -1
- package/dist/__tests__/pivotCompute-test.js +137 -0
- package/dist/__tests__/pivotCompute-test.js.map +1 -1
- package/dist/pivot-worker.js +509 -201
- package/dist/pivotCompute.js +237 -12
- package/dist/pivotCompute.js.map +1 -1
- package/dist/pivotWorker.js +165 -9
- package/dist/pivotWorker.js.map +1 -1
- package/dist/pivottable.css +274 -266
- package/package.json +1 -1
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
|
4
4
|
|
|
5
|
+
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
|
|
6
|
+
|
|
5
7
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
6
8
|
|
|
7
9
|
/* eslint-disable no-magic-numbers */
|
|
@@ -140,19 +142,447 @@ describe(' PivotDataWorker', function () {
|
|
|
140
142
|
});
|
|
141
143
|
});
|
|
142
144
|
|
|
143
|
-
it('
|
|
145
|
+
it('surfaces worker failures without a synchronous fallback', _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
|
|
146
|
+
var client, promise;
|
|
147
|
+
return regeneratorRuntime.wrap(function _callee$(_context) {
|
|
148
|
+
while (1) {
|
|
149
|
+
switch (_context.prev = _context.next) {
|
|
150
|
+
case 0:
|
|
151
|
+
client = loadClient();
|
|
152
|
+
|
|
153
|
+
client.setWorkerDebug(true);
|
|
154
|
+
|
|
155
|
+
promise = client.computePivotWithFallback(records, 'data-2', config);
|
|
156
|
+
|
|
157
|
+
// Simulate a worker that fails to load before producing any result.
|
|
158
|
+
|
|
159
|
+
instances[0].onerror(new Error('404'));
|
|
160
|
+
|
|
161
|
+
_context.next = 6;
|
|
162
|
+
return promise.then(function () {
|
|
163
|
+
throw new Error('Worker request unexpectedly succeeded');
|
|
164
|
+
}, function (error) {
|
|
165
|
+
expect(error.message).toBe('Pivot worker failed to load');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
case 6:
|
|
169
|
+
expect(loggedText()).toEqual(expect.arrayContaining([expect.stringContaining('worker error event'), expect.stringContaining('synchronous fallback disabled')]));
|
|
170
|
+
expect(loggedText()).not.toEqual(expect.arrayContaining([expect.stringContaining('running synchronous computation')]));
|
|
171
|
+
|
|
172
|
+
case 8:
|
|
173
|
+
case 'end':
|
|
174
|
+
return _context.stop();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}, _callee, this);
|
|
178
|
+
})));
|
|
179
|
+
|
|
180
|
+
it('stages large datasets in chunks before computing', function () {
|
|
144
181
|
var client = loadClient();
|
|
145
|
-
|
|
182
|
+
var largeRecords = new Array(1200).fill(null).map(function (_, index) {
|
|
183
|
+
return {
|
|
184
|
+
name: 'Employee ' + index
|
|
185
|
+
};
|
|
186
|
+
});
|
|
187
|
+
var promise = client.computePivotInWorker(largeRecords, 'large-data', config);
|
|
188
|
+
var worker = instances[0];
|
|
146
189
|
|
|
147
|
-
|
|
190
|
+
expect(worker.posted.map(function (message) {
|
|
191
|
+
return message.type;
|
|
192
|
+
})).toEqual(['pivot:setDataStart', 'pivot:setDataChunk']);
|
|
148
193
|
|
|
149
|
-
|
|
150
|
-
|
|
194
|
+
return new Promise(function (resolve) {
|
|
195
|
+
return setTimeout(resolve, 20);
|
|
196
|
+
}).then(function () {
|
|
197
|
+
var types = worker.posted.map(function (message) {
|
|
198
|
+
return message.type;
|
|
199
|
+
});
|
|
200
|
+
expect(types.filter(function (type) {
|
|
201
|
+
return type === 'pivot:setDataChunk';
|
|
202
|
+
})).toHaveLength(3);
|
|
203
|
+
expect(types.slice(-2)).toEqual(['pivot:setDataEnd', 'pivot:compute']);
|
|
204
|
+
worker.onmessage({
|
|
205
|
+
data: {
|
|
206
|
+
type: 'pivot:result',
|
|
207
|
+
id: 1,
|
|
208
|
+
result: { rowKeys: [], colKeys: [] }
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
return promise;
|
|
212
|
+
}).then(function (result) {
|
|
213
|
+
expect(result.rowKeys).toEqual([]);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
151
216
|
|
|
217
|
+
it('requests a viewport without sending the dataset again', function () {
|
|
218
|
+
var client = loadClient();
|
|
219
|
+
var promise = client.computePivotViewportInWorker(42, 64, 128, false, 16, 48);
|
|
220
|
+
var worker = instances[0];
|
|
221
|
+
expect(worker.posted).toEqual([{
|
|
222
|
+
type: 'pivot:viewport',
|
|
223
|
+
id: 1,
|
|
224
|
+
viewportKey: 42,
|
|
225
|
+
start: 64,
|
|
226
|
+
end: 128,
|
|
227
|
+
columnMode: false,
|
|
228
|
+
columnStart: 16,
|
|
229
|
+
columnEnd: 48
|
|
230
|
+
}]);
|
|
231
|
+
worker.onmessage({
|
|
232
|
+
data: {
|
|
233
|
+
type: 'pivot:viewportResult',
|
|
234
|
+
id: 1,
|
|
235
|
+
result: { start: 64, end: 128, rows: [{ displayKey: ['A'] }] }
|
|
236
|
+
}
|
|
237
|
+
});
|
|
152
238
|
return promise.then(function (result) {
|
|
153
|
-
expect(result.
|
|
154
|
-
|
|
239
|
+
expect(result.rows).toEqual([{ displayKey: ['A'] }]);
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it('releases a replaced viewport request immediately', _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() {
|
|
244
|
+
var client, firstPromise, secondPromise, worker, firstError;
|
|
245
|
+
return regeneratorRuntime.wrap(function _callee2$(_context2) {
|
|
246
|
+
while (1) {
|
|
247
|
+
switch (_context2.prev = _context2.next) {
|
|
248
|
+
case 0:
|
|
249
|
+
client = loadClient();
|
|
250
|
+
firstPromise = client.computePivotViewportInWorker(42, 0, 64, false).catch(function (error) {
|
|
251
|
+
return error;
|
|
252
|
+
});
|
|
253
|
+
secondPromise = client.computePivotViewportInWorker(42, 64, 128, false);
|
|
254
|
+
worker = instances[0];
|
|
255
|
+
_context2.next = 6;
|
|
256
|
+
return firstPromise;
|
|
257
|
+
|
|
258
|
+
case 6:
|
|
259
|
+
firstError = _context2.sent;
|
|
260
|
+
|
|
261
|
+
expect(firstError.code).toBe('PIVOT_VIEWPORT_CANCELLED');
|
|
262
|
+
expect(worker.posted.filter(function (message) {
|
|
263
|
+
return message.type === 'pivot:viewport';
|
|
264
|
+
})).toHaveLength(2);
|
|
265
|
+
|
|
266
|
+
worker.onmessage({
|
|
267
|
+
data: {
|
|
268
|
+
type: 'pivot:viewportResult',
|
|
269
|
+
id: 2,
|
|
270
|
+
result: { start: 64, end: 128, rows: [] }
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
_context2.next = 12;
|
|
274
|
+
return expect(secondPromise).resolves.toEqual({
|
|
275
|
+
start: 64,
|
|
276
|
+
end: 128,
|
|
277
|
+
rows: []
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
case 12:
|
|
281
|
+
case 'end':
|
|
282
|
+
return _context2.stop();
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}, _callee2, this);
|
|
286
|
+
})));
|
|
287
|
+
|
|
288
|
+
it('releases the staged dataset and stages it again for the next compute', _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee3() {
|
|
289
|
+
var client, firstPromise, worker, secondPromise;
|
|
290
|
+
return regeneratorRuntime.wrap(function _callee3$(_context3) {
|
|
291
|
+
while (1) {
|
|
292
|
+
switch (_context3.prev = _context3.next) {
|
|
293
|
+
case 0:
|
|
294
|
+
client = loadClient();
|
|
295
|
+
firstPromise = client.computePivotInWorker(records, 'reusable-data', config);
|
|
296
|
+
worker = instances[0];
|
|
297
|
+
|
|
298
|
+
worker.onmessage({
|
|
299
|
+
data: {
|
|
300
|
+
type: 'pivot:result',
|
|
301
|
+
id: 1,
|
|
302
|
+
result: { rowKeys: [], colKeys: [], attrValues: { name: { Nick: 1 } } }
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
_context3.next = 6;
|
|
306
|
+
return firstPromise;
|
|
307
|
+
|
|
308
|
+
case 6:
|
|
309
|
+
|
|
310
|
+
client.releaseDatasetInWorker('reusable-data');
|
|
311
|
+
secondPromise = client.computePivotInWorker(records, 'reusable-data', config);
|
|
312
|
+
|
|
313
|
+
expect(worker.posted.map(function (message) {
|
|
314
|
+
return message.type;
|
|
315
|
+
})).toEqual(['pivot:setData', 'pivot:compute', 'pivot:releaseData', 'pivot:setData', 'pivot:compute']);
|
|
316
|
+
worker.onmessage({
|
|
317
|
+
data: {
|
|
318
|
+
type: 'pivot:result',
|
|
319
|
+
id: 2,
|
|
320
|
+
result: { rowKeys: [], colKeys: [] }
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
_context3.next = 12;
|
|
324
|
+
return secondPromise;
|
|
325
|
+
|
|
326
|
+
case 12:
|
|
327
|
+
case 'end':
|
|
328
|
+
return _context3.stop();
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}, _callee3, this);
|
|
332
|
+
})));
|
|
333
|
+
|
|
334
|
+
it('terminates the shared worker after the last table owner unmounts', _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee4() {
|
|
335
|
+
var client, promise, worker;
|
|
336
|
+
return regeneratorRuntime.wrap(function _callee4$(_context4) {
|
|
337
|
+
while (1) {
|
|
338
|
+
switch (_context4.prev = _context4.next) {
|
|
339
|
+
case 0:
|
|
340
|
+
client = loadClient();
|
|
341
|
+
|
|
342
|
+
client.retainPivotWorker('table-a');
|
|
343
|
+
client.retainPivotWorker('table-b');
|
|
344
|
+
promise = client.computePivotInWorker(records, 'owned-data', config, 'table-a');
|
|
345
|
+
worker = instances[0];
|
|
346
|
+
|
|
347
|
+
worker.onmessage({
|
|
348
|
+
data: {
|
|
349
|
+
type: 'pivot:result',
|
|
350
|
+
id: 1,
|
|
351
|
+
result: { rowKeys: [], colKeys: [] }
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
_context4.next = 8;
|
|
355
|
+
return promise;
|
|
356
|
+
|
|
357
|
+
case 8:
|
|
358
|
+
|
|
359
|
+
client.releasePivotWorker('table-a');
|
|
360
|
+
expect(worker.terminated).not.toBe(true);
|
|
361
|
+
client.releasePivotWorker('table-b');
|
|
362
|
+
expect(worker.terminated).toBe(true);
|
|
363
|
+
expect(worker.posted.slice(-2)).toEqual([{ type: 'pivot:releaseOwner', ownerKey: 'table-a' }, { type: 'pivot:releaseOwner', ownerKey: 'table-b' }]);
|
|
364
|
+
|
|
365
|
+
case 13:
|
|
366
|
+
case 'end':
|
|
367
|
+
return _context4.stop();
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}, _callee4, this);
|
|
371
|
+
})));
|
|
372
|
+
|
|
373
|
+
it('caches a computed pivot and returns only requested rows', function () {
|
|
374
|
+
var originalAddEventListener = self.addEventListener;
|
|
375
|
+
var originalPostMessage = self.postMessage;
|
|
376
|
+
var posted = [];
|
|
377
|
+
var onMessage = void 0;
|
|
378
|
+
self.addEventListener = function (type, listener) {
|
|
379
|
+
if (type === 'message') {
|
|
380
|
+
onMessage = listener;
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
self.postMessage = function (message) {
|
|
384
|
+
return posted.push(message);
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
try {
|
|
388
|
+
jest.resetModules();
|
|
389
|
+
require('../pivotWorker');
|
|
390
|
+
onMessage({
|
|
391
|
+
data: { type: 'pivot:setData', id: 1, dataKey: 'x', data: records }
|
|
392
|
+
});
|
|
393
|
+
onMessage({ data: { type: 'pivot:compute', id: 1, dataKey: 'x', config: config } });
|
|
394
|
+
var computed = posted.find(function (message) {
|
|
395
|
+
return message.type === 'pivot:result';
|
|
396
|
+
});
|
|
397
|
+
expect(computed.result.viewportKey).toBe(1);
|
|
398
|
+
|
|
399
|
+
onMessage({
|
|
400
|
+
data: {
|
|
401
|
+
type: 'pivot:viewport',
|
|
402
|
+
id: 2,
|
|
403
|
+
viewportKey: 1,
|
|
404
|
+
start: 1,
|
|
405
|
+
end: 2,
|
|
406
|
+
columnMode: false
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
var viewport = posted.find(function (message) {
|
|
410
|
+
return message.type === 'pivot:viewportResult';
|
|
411
|
+
});
|
|
412
|
+
expect(viewport.result.rows).toHaveLength(1);
|
|
413
|
+
expect(viewport.result.rows[0].displayKey).toEqual(['Nick']);
|
|
414
|
+
expect(viewport.result.totalRows).toBe(2);
|
|
415
|
+
} finally {
|
|
416
|
+
self.addEventListener = originalAddEventListener;
|
|
417
|
+
self.postMessage = originalPostMessage;
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it('returns a compact result and viewport cell values for virtual tables', function () {
|
|
422
|
+
var originalAddEventListener = self.addEventListener;
|
|
423
|
+
var originalPostMessage = self.postMessage;
|
|
424
|
+
var posted = [];
|
|
425
|
+
var onMessage = void 0;
|
|
426
|
+
self.addEventListener = function (type, listener) {
|
|
427
|
+
if (type === 'message') {
|
|
428
|
+
onMessage = listener;
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
self.postMessage = function (message) {
|
|
432
|
+
return posted.push(message);
|
|
433
|
+
};
|
|
434
|
+
var largeRecords = new Array(400).fill(null).map(function (_, index) {
|
|
435
|
+
return {
|
|
436
|
+
name: 'Employee ' + index,
|
|
437
|
+
department: 'Department ' + index % 3
|
|
438
|
+
};
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
try {
|
|
442
|
+
jest.resetModules();
|
|
443
|
+
require('../pivotWorker');
|
|
444
|
+
onMessage({
|
|
445
|
+
data: {
|
|
446
|
+
type: 'pivot:setData',
|
|
447
|
+
id: 1,
|
|
448
|
+
dataKey: 'virtual-data',
|
|
449
|
+
data: largeRecords
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
onMessage({
|
|
453
|
+
data: {
|
|
454
|
+
type: 'pivot:compute',
|
|
455
|
+
id: 1,
|
|
456
|
+
dataKey: 'virtual-data',
|
|
457
|
+
config: Object.assign({}, config, {
|
|
458
|
+
cols: ['department'],
|
|
459
|
+
virtualizeRows: true,
|
|
460
|
+
rendererName: 'Table'
|
|
461
|
+
})
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
var computed = posted.find(function (message) {
|
|
465
|
+
return message.type === 'pivot:result';
|
|
466
|
+
});
|
|
467
|
+
expect(computed.result.virtualized).toBe(true);
|
|
468
|
+
expect(computed.result.totalRows).toBe(400);
|
|
469
|
+
expect(computed.result.rowKeys).toEqual([]);
|
|
470
|
+
expect(computed.result.tree).toEqual({});
|
|
471
|
+
|
|
472
|
+
onMessage({
|
|
473
|
+
data: { type: 'pivot:releaseData', dataKey: 'virtual-data' }
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
onMessage({
|
|
477
|
+
data: {
|
|
478
|
+
type: 'pivot:viewport',
|
|
479
|
+
id: 2,
|
|
480
|
+
viewportKey: 1,
|
|
481
|
+
start: 32,
|
|
482
|
+
end: 64,
|
|
483
|
+
columnMode: false
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
var viewport = posted.find(function (message) {
|
|
487
|
+
return message.type === 'pivot:viewportResult';
|
|
488
|
+
});
|
|
489
|
+
expect(viewport.result.rows).toHaveLength(32);
|
|
490
|
+
expect(viewport.result.rows[0].cells).toBeDefined();
|
|
491
|
+
expect(viewport.result.rows[0].rowTotal[0]).toBe(1);
|
|
492
|
+
|
|
493
|
+
onMessage({
|
|
494
|
+
data: {
|
|
495
|
+
type: 'pivot:compute',
|
|
496
|
+
id: 3,
|
|
497
|
+
dataKey: 'virtual-data',
|
|
498
|
+
config: config
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
var releasedDatasetError = posted.find(function (message) {
|
|
502
|
+
return message.type === 'pivot:error' && message.id === 3;
|
|
503
|
+
});
|
|
504
|
+
expect(releasedDatasetError.message).toContain('no dataset staged');
|
|
505
|
+
} finally {
|
|
506
|
+
self.addEventListener = originalAddEventListener;
|
|
507
|
+
self.postMessage = originalPostMessage;
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
it('uses a compact result when columns are wide but rows are short', function () {
|
|
512
|
+
var originalAddEventListener = self.addEventListener;
|
|
513
|
+
var originalPostMessage = self.postMessage;
|
|
514
|
+
var posted = [];
|
|
515
|
+
var onMessage = void 0;
|
|
516
|
+
self.addEventListener = function (type, listener) {
|
|
517
|
+
if (type === 'message') {
|
|
518
|
+
onMessage = listener;
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
self.postMessage = function (message) {
|
|
522
|
+
return posted.push(message);
|
|
523
|
+
};
|
|
524
|
+
var wideRecords = new Array(150).fill(null).map(function (_, index) {
|
|
525
|
+
return {
|
|
526
|
+
employee: 'Employee ' + String(index).padStart(3, '0'),
|
|
527
|
+
gender: index % 2 === 0 ? 'female' : 'male'
|
|
528
|
+
};
|
|
155
529
|
});
|
|
530
|
+
|
|
531
|
+
try {
|
|
532
|
+
jest.resetModules();
|
|
533
|
+
require('../pivotWorker');
|
|
534
|
+
onMessage({
|
|
535
|
+
data: {
|
|
536
|
+
type: 'pivot:setData',
|
|
537
|
+
id: 1,
|
|
538
|
+
dataKey: 'wide-data',
|
|
539
|
+
data: wideRecords
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
onMessage({
|
|
543
|
+
data: {
|
|
544
|
+
type: 'pivot:compute',
|
|
545
|
+
id: 1,
|
|
546
|
+
dataKey: 'wide-data',
|
|
547
|
+
config: Object.assign({}, config, {
|
|
548
|
+
rows: ['gender'],
|
|
549
|
+
cols: ['employee'],
|
|
550
|
+
virtualizeRows: true,
|
|
551
|
+
rendererName: 'Table'
|
|
552
|
+
})
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
var computed = posted.find(function (message) {
|
|
556
|
+
return message.type === 'pivot:result';
|
|
557
|
+
});
|
|
558
|
+
expect(computed.result.virtualized).toBe(true);
|
|
559
|
+
expect(computed.result.totalRows).toBe(2);
|
|
560
|
+
expect(computed.result.totalColumns).toBe(150);
|
|
561
|
+
expect(computed.result.tree).toEqual({});
|
|
562
|
+
expect(computed.result.colTotals).toEqual({});
|
|
563
|
+
|
|
564
|
+
onMessage({
|
|
565
|
+
data: {
|
|
566
|
+
type: 'pivot:viewport',
|
|
567
|
+
id: 2,
|
|
568
|
+
viewportKey: 1,
|
|
569
|
+
start: 0,
|
|
570
|
+
end: 2,
|
|
571
|
+
columnMode: false,
|
|
572
|
+
columnStart: 32,
|
|
573
|
+
columnEnd: 48
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
var viewport = posted.find(function (message) {
|
|
577
|
+
return message.type === 'pivot:viewportResult';
|
|
578
|
+
});
|
|
579
|
+
expect(viewport.result.rows).toHaveLength(2);
|
|
580
|
+
expect(Object.keys(viewport.result.rows[0].cells).length).toBeLessThanOrEqual(16);
|
|
581
|
+
expect(Object.keys(viewport.result.colTotals)).toHaveLength(16);
|
|
582
|
+
} finally {
|
|
583
|
+
self.addEventListener = originalAddEventListener;
|
|
584
|
+
self.postMessage = originalPostMessage;
|
|
585
|
+
}
|
|
156
586
|
});
|
|
157
587
|
|
|
158
588
|
it('stops logging when debug is switched off again', function () {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/__tests__/PivotDataWorker-test.js"],"names":["describe","records","name","gender","config","rows","cols","aggregations","key","aggregatorName","vals","instances","logSpy","FakeWorker","url","posted","onmessage","onerror","push","message","terminated","loggedText","mock","calls","map","call","filter","String","arg","startsWith","join","loadClient","jest","resetModules","require","beforeEach","global","Worker","spyOn","console","mockImplementation","afterEach","mockRestore","it","client","promise","computePivotInWorker","expect","not","toHaveBeenCalled","data","type","id","result","rowKeys","colKeys","setWorkerDebug","toEqual","arrayContaining","stringContaining","types","staged","find","m","toBe","dataKey","worker","processed","total","phase","totalRecords","then","computePivotWithFallback","Error","mockClear"],"mappings":"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","file":"PivotDataWorker-test.js","sourcesContent":["/* eslint-disable no-magic-numbers */\n/*\n * Tests the main-thread worker client, including the debug logging that the\n * `workerDebug` prop of PivotTableUI switches on.\n */\ndescribe(' PivotDataWorker', function() {\n const records = [\n {name: 'Nick', gender: 'male'},\n {name: 'Jane', gender: 'female'},\n ];\n const config = {\n rows: ['name'],\n cols: [],\n aggregations: [{key: 'count', aggregatorName: 'Count', vals: []}],\n };\n\n let instances;\n let logSpy;\n\n class FakeWorker {\n constructor(url) {\n this.url = url;\n this.posted = [];\n this.onmessage = null;\n this.onerror = null;\n instances.push(this);\n }\n\n postMessage(message) {\n this.posted.push(message);\n }\n\n terminate() {\n this.terminated = true;\n }\n }\n\n // console.log('%c[pivottable worker]', 'style', ...) -> strip the format\n // directive and its style argument to assert on the message itself.\n const loggedText = () =>\n logSpy.mock.calls.map(call =>\n call\n .filter(\n arg =>\n !String(arg).startsWith('%c') && !String(arg).startsWith('color:')\n )\n .join(' ')\n );\n\n const loadClient = () => {\n jest.resetModules();\n return require('../PivotDataWorker');\n };\n\n beforeEach(function() {\n instances = [];\n global.Worker = FakeWorker;\n logSpy = jest.spyOn(console, 'log').mockImplementation(() => {});\n });\n\n afterEach(function() {\n logSpy.mockRestore();\n });\n\n it('is silent by default', function() {\n const client = loadClient();\n const promise = client.computePivotInWorker(records, 'data-1', config);\n expect(logSpy).not.toHaveBeenCalled();\n // Resolve the request so nothing stays pending.\n instances[0].onmessage({\n data: {type: 'pivot:result', id: 1, result: {rowKeys: [], colKeys: []}},\n });\n return promise;\n });\n\n it('logs the whole worker pipeline when debug is enabled', function() {\n const client = loadClient();\n client.setWorkerDebug(true);\n\n const promise = client.computePivotInWorker(records, 'data-1', config);\n\n expect(loggedText()).toEqual(\n expect.arrayContaining([\n expect.stringContaining('debug logging enabled'),\n expect.stringContaining('worker created: pivot-worker.js'),\n expect.stringContaining('#1 dispatching to worker'),\n expect.stringContaining('staging 2 records'),\n ])\n );\n\n // Staging + compute messages are sent (staging only once per dataKey).\n const types = instances[0].posted.map(message => message.type);\n expect(types).toEqual(['pivot:debug', 'pivot:setData', 'pivot:compute']);\n const staged = instances[0].posted.find(m => m.type === 'pivot:setData');\n expect(staged.data).toBe(records);\n expect(staged.dataKey).toBe('data-1');\n\n const worker = instances[0];\n // Progress percentages were removed: legacy `pivot:progress` messages\n // from an older worker bundle must be ignored without corrupting the\n // request (the result still resolves afterwards).\n worker.onmessage({\n data: {\n type: 'pivot:progress',\n id: 1,\n processed: 2,\n total: 2,\n phase: 'materialize',\n },\n });\n expect(loggedText()).not.toEqual(\n expect.arrayContaining([expect.stringContaining('progress')])\n );\n\n // Debug logs forwarded from inside the worker thread.\n worker.onmessage({data: {type: 'pivot:log', message: 'computed in 5ms'}});\n expect(loggedText()).toEqual(\n expect.arrayContaining([\n expect.stringContaining('(in worker) computed in 5ms'),\n ])\n );\n\n worker.onmessage({\n data: {\n type: 'pivot:result',\n id: 1,\n result: {\n totalRecords: 2,\n rowKeys: [['Nick']],\n colKeys: [],\n aggregations: [],\n },\n },\n });\n return promise.then(result => {\n expect(result.totalRecords).toBe(2);\n expect(loggedText()).toEqual(\n expect.arrayContaining([\n expect.stringContaining('#1 result received in'),\n ])\n );\n });\n });\n\n it('logs the fallback to a synchronous computation', function() {\n const client = loadClient();\n client.setWorkerDebug(true);\n\n const promise = client.computePivotWithFallback(records, 'data-2', config);\n\n // Simulate a worker that fails to load before producing any result.\n instances[0].onerror(new Error('404'));\n\n return promise.then(result => {\n expect(result.totalRecords).toBe(2);\n expect(loggedText()).toEqual(\n expect.arrayContaining([\n expect.stringContaining('worker error event'),\n expect.stringContaining('falling back to the main thread'),\n expect.stringContaining('running synchronous computation'),\n expect.stringContaining('synchronous computation finished in'),\n ])\n );\n });\n });\n\n it('stops logging when debug is switched off again', function() {\n const client = loadClient();\n client.setWorkerDebug(true);\n logSpy.mockClear();\n client.setWorkerDebug(false);\n expect(loggedText()).toEqual(\n expect.arrayContaining([\n expect.stringContaining('debug logging disabled'),\n ])\n );\n logSpy.mockClear();\n client.setWorkerDebug(false);\n expect(logSpy).not.toHaveBeenCalled();\n });\n});\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/__tests__/PivotDataWorker-test.js"],"names":["describe","records","name","gender","config","rows","cols","aggregations","key","aggregatorName","vals","instances","logSpy","FakeWorker","url","posted","onmessage","onerror","push","message","terminated","loggedText","mock","calls","map","call","filter","String","arg","startsWith","join","loadClient","jest","resetModules","require","beforeEach","global","Worker","spyOn","console","mockImplementation","afterEach","mockRestore","it","client","promise","computePivotInWorker","expect","not","toHaveBeenCalled","data","type","id","result","rowKeys","colKeys","setWorkerDebug","toEqual","arrayContaining","stringContaining","types","staged","find","m","toBe","dataKey","worker","processed","total","phase","totalRecords","then","computePivotWithFallback","Error","error","largeRecords","Array","fill","_","index","Promise","setTimeout","resolve","toHaveLength","slice","computePivotViewportInWorker","viewportKey","start","end","columnMode","columnStart","columnEnd","displayKey","firstPromise","catch","secondPromise","firstError","code","resolves","attrValues","Nick","releaseDatasetInWorker","retainPivotWorker","releasePivotWorker","ownerKey","originalAddEventListener","self","addEventListener","originalPostMessage","postMessage","onMessage","listener","computed","viewport","totalRows","department","Object","assign","virtualizeRows","rendererName","virtualized","tree","cells","toBeDefined","rowTotal","releasedDatasetError","toContain","wideRecords","employee","padStart","totalColumns","colTotals","keys","length","toBeLessThanOrEqual","mockClear"],"mappings":"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","file":"PivotDataWorker-test.js","sourcesContent":["/* eslint-disable no-magic-numbers */\n/*\n * Tests the main-thread worker client, including the debug logging that the\n * `workerDebug` prop of PivotTableUI switches on.\n */\ndescribe(' PivotDataWorker', function() {\n const records = [\n {name: 'Nick', gender: 'male'},\n {name: 'Jane', gender: 'female'},\n ];\n const config = {\n rows: ['name'],\n cols: [],\n aggregations: [{key: 'count', aggregatorName: 'Count', vals: []}],\n };\n\n let instances;\n let logSpy;\n\n class FakeWorker {\n constructor(url) {\n this.url = url;\n this.posted = [];\n this.onmessage = null;\n this.onerror = null;\n instances.push(this);\n }\n\n postMessage(message) {\n this.posted.push(message);\n }\n\n terminate() {\n this.terminated = true;\n }\n }\n\n // console.log('%c[pivottable worker]', 'style', ...) -> strip the format\n // directive and its style argument to assert on the message itself.\n const loggedText = () =>\n logSpy.mock.calls.map(call =>\n call\n .filter(\n arg =>\n !String(arg).startsWith('%c') && !String(arg).startsWith('color:')\n )\n .join(' ')\n );\n\n const loadClient = () => {\n jest.resetModules();\n return require('../PivotDataWorker');\n };\n\n beforeEach(function() {\n instances = [];\n global.Worker = FakeWorker;\n logSpy = jest.spyOn(console, 'log').mockImplementation(() => {});\n });\n\n afterEach(function() {\n logSpy.mockRestore();\n });\n\n it('is silent by default', function() {\n const client = loadClient();\n const promise = client.computePivotInWorker(records, 'data-1', config);\n expect(logSpy).not.toHaveBeenCalled();\n // Resolve the request so nothing stays pending.\n instances[0].onmessage({\n data: {type: 'pivot:result', id: 1, result: {rowKeys: [], colKeys: []}},\n });\n return promise;\n });\n\n it('logs the whole worker pipeline when debug is enabled', function() {\n const client = loadClient();\n client.setWorkerDebug(true);\n\n const promise = client.computePivotInWorker(records, 'data-1', config);\n\n expect(loggedText()).toEqual(\n expect.arrayContaining([\n expect.stringContaining('debug logging enabled'),\n expect.stringContaining('worker created: pivot-worker.js'),\n expect.stringContaining('#1 dispatching to worker'),\n expect.stringContaining('staging 2 records'),\n ])\n );\n\n // Staging + compute messages are sent (staging only once per dataKey).\n const types = instances[0].posted.map(message => message.type);\n expect(types).toEqual(['pivot:debug', 'pivot:setData', 'pivot:compute']);\n const staged = instances[0].posted.find(m => m.type === 'pivot:setData');\n expect(staged.data).toBe(records);\n expect(staged.dataKey).toBe('data-1');\n\n const worker = instances[0];\n // Progress percentages were removed: legacy `pivot:progress` messages\n // from an older worker bundle must be ignored without corrupting the\n // request (the result still resolves afterwards).\n worker.onmessage({\n data: {\n type: 'pivot:progress',\n id: 1,\n processed: 2,\n total: 2,\n phase: 'materialize',\n },\n });\n expect(loggedText()).not.toEqual(\n expect.arrayContaining([expect.stringContaining('progress')])\n );\n\n // Debug logs forwarded from inside the worker thread.\n worker.onmessage({data: {type: 'pivot:log', message: 'computed in 5ms'}});\n expect(loggedText()).toEqual(\n expect.arrayContaining([\n expect.stringContaining('(in worker) computed in 5ms'),\n ])\n );\n\n worker.onmessage({\n data: {\n type: 'pivot:result',\n id: 1,\n result: {\n totalRecords: 2,\n rowKeys: [['Nick']],\n colKeys: [],\n aggregations: [],\n },\n },\n });\n return promise.then(result => {\n expect(result.totalRecords).toBe(2);\n expect(loggedText()).toEqual(\n expect.arrayContaining([\n expect.stringContaining('#1 result received in'),\n ])\n );\n });\n });\n\n it('surfaces worker failures without a synchronous fallback', async function() {\n const client = loadClient();\n client.setWorkerDebug(true);\n\n const promise = client.computePivotWithFallback(records, 'data-2', config);\n\n // Simulate a worker that fails to load before producing any result.\n instances[0].onerror(new Error('404'));\n\n await promise.then(\n () => {\n throw new Error('Worker request unexpectedly succeeded');\n },\n error => {\n expect(error.message).toBe('Pivot worker failed to load');\n }\n );\n expect(loggedText()).toEqual(\n expect.arrayContaining([\n expect.stringContaining('worker error event'),\n expect.stringContaining('synchronous fallback disabled'),\n ])\n );\n expect(loggedText()).not.toEqual(\n expect.arrayContaining([\n expect.stringContaining('running synchronous computation'),\n ])\n );\n });\n\n it('stages large datasets in chunks before computing', function() {\n const client = loadClient();\n const largeRecords = new Array(1200).fill(null).map((_, index) => ({\n name: `Employee ${index}`,\n }));\n const promise = client.computePivotInWorker(\n largeRecords,\n 'large-data',\n config\n );\n const worker = instances[0];\n\n expect(worker.posted.map(message => message.type)).toEqual([\n 'pivot:setDataStart',\n 'pivot:setDataChunk',\n ]);\n\n return new Promise(resolve => setTimeout(resolve, 20))\n .then(() => {\n const types = worker.posted.map(message => message.type);\n expect(\n types.filter(type => type === 'pivot:setDataChunk')\n ).toHaveLength(3);\n expect(types.slice(-2)).toEqual(['pivot:setDataEnd', 'pivot:compute']);\n worker.onmessage({\n data: {\n type: 'pivot:result',\n id: 1,\n result: {rowKeys: [], colKeys: []},\n },\n });\n return promise;\n })\n .then(result => {\n expect(result.rowKeys).toEqual([]);\n });\n });\n\n it('requests a viewport without sending the dataset again', function() {\n const client = loadClient();\n const promise = client.computePivotViewportInWorker(\n 42,\n 64,\n 128,\n false,\n 16,\n 48\n );\n const worker = instances[0];\n expect(worker.posted).toEqual([\n {\n type: 'pivot:viewport',\n id: 1,\n viewportKey: 42,\n start: 64,\n end: 128,\n columnMode: false,\n columnStart: 16,\n columnEnd: 48,\n },\n ]);\n worker.onmessage({\n data: {\n type: 'pivot:viewportResult',\n id: 1,\n result: {start: 64, end: 128, rows: [{displayKey: ['A']}]},\n },\n });\n return promise.then(result => {\n expect(result.rows).toEqual([{displayKey: ['A']}]);\n });\n });\n\n it('releases a replaced viewport request immediately', async function() {\n const client = loadClient();\n const firstPromise = client\n .computePivotViewportInWorker(42, 0, 64, false)\n .catch(error => error);\n const secondPromise = client.computePivotViewportInWorker(\n 42,\n 64,\n 128,\n false\n );\n const worker = instances[0];\n\n const firstError = await firstPromise;\n expect(firstError.code).toBe('PIVOT_VIEWPORT_CANCELLED');\n expect(\n worker.posted.filter(message => message.type === 'pivot:viewport')\n ).toHaveLength(2);\n\n worker.onmessage({\n data: {\n type: 'pivot:viewportResult',\n id: 2,\n result: {start: 64, end: 128, rows: []},\n },\n });\n await expect(secondPromise).resolves.toEqual({\n start: 64,\n end: 128,\n rows: [],\n });\n });\n\n it('releases the staged dataset and stages it again for the next compute', async function() {\n const client = loadClient();\n const firstPromise = client.computePivotInWorker(\n records,\n 'reusable-data',\n config\n );\n const worker = instances[0];\n worker.onmessage({\n data: {\n type: 'pivot:result',\n id: 1,\n result: {rowKeys: [], colKeys: [], attrValues: {name: {Nick: 1}}},\n },\n });\n await firstPromise;\n\n client.releaseDatasetInWorker('reusable-data');\n const secondPromise = client.computePivotInWorker(\n records,\n 'reusable-data',\n config\n );\n expect(worker.posted.map(message => message.type)).toEqual([\n 'pivot:setData',\n 'pivot:compute',\n 'pivot:releaseData',\n 'pivot:setData',\n 'pivot:compute',\n ]);\n worker.onmessage({\n data: {\n type: 'pivot:result',\n id: 2,\n result: {rowKeys: [], colKeys: []},\n },\n });\n await secondPromise;\n });\n\n it('terminates the shared worker after the last table owner unmounts', async function() {\n const client = loadClient();\n client.retainPivotWorker('table-a');\n client.retainPivotWorker('table-b');\n const promise = client.computePivotInWorker(\n records,\n 'owned-data',\n config,\n 'table-a'\n );\n const worker = instances[0];\n worker.onmessage({\n data: {\n type: 'pivot:result',\n id: 1,\n result: {rowKeys: [], colKeys: []},\n },\n });\n await promise;\n\n client.releasePivotWorker('table-a');\n expect(worker.terminated).not.toBe(true);\n client.releasePivotWorker('table-b');\n expect(worker.terminated).toBe(true);\n expect(worker.posted.slice(-2)).toEqual([\n {type: 'pivot:releaseOwner', ownerKey: 'table-a'},\n {type: 'pivot:releaseOwner', ownerKey: 'table-b'},\n ]);\n });\n\n it('caches a computed pivot and returns only requested rows', function() {\n const originalAddEventListener = self.addEventListener;\n const originalPostMessage = self.postMessage;\n const posted = [];\n let onMessage;\n self.addEventListener = (type, listener) => {\n if (type === 'message') {\n onMessage = listener;\n }\n };\n self.postMessage = message => posted.push(message);\n\n try {\n jest.resetModules();\n require('../pivotWorker');\n onMessage({\n data: {type: 'pivot:setData', id: 1, dataKey: 'x', data: records},\n });\n onMessage({data: {type: 'pivot:compute', id: 1, dataKey: 'x', config}});\n const computed = posted.find(message => message.type === 'pivot:result');\n expect(computed.result.viewportKey).toBe(1);\n\n onMessage({\n data: {\n type: 'pivot:viewport',\n id: 2,\n viewportKey: 1,\n start: 1,\n end: 2,\n columnMode: false,\n },\n });\n const viewport = posted.find(\n message => message.type === 'pivot:viewportResult'\n );\n expect(viewport.result.rows).toHaveLength(1);\n expect(viewport.result.rows[0].displayKey).toEqual(['Nick']);\n expect(viewport.result.totalRows).toBe(2);\n } finally {\n self.addEventListener = originalAddEventListener;\n self.postMessage = originalPostMessage;\n }\n });\n\n it('returns a compact result and viewport cell values for virtual tables', function() {\n const originalAddEventListener = self.addEventListener;\n const originalPostMessage = self.postMessage;\n const posted = [];\n let onMessage;\n self.addEventListener = (type, listener) => {\n if (type === 'message') {\n onMessage = listener;\n }\n };\n self.postMessage = message => posted.push(message);\n const largeRecords = new Array(400).fill(null).map((_, index) => ({\n name: `Employee ${index}`,\n department: `Department ${index % 3}`,\n }));\n\n try {\n jest.resetModules();\n require('../pivotWorker');\n onMessage({\n data: {\n type: 'pivot:setData',\n id: 1,\n dataKey: 'virtual-data',\n data: largeRecords,\n },\n });\n onMessage({\n data: {\n type: 'pivot:compute',\n id: 1,\n dataKey: 'virtual-data',\n config: Object.assign({}, config, {\n cols: ['department'],\n virtualizeRows: true,\n rendererName: 'Table',\n }),\n },\n });\n const computed = posted.find(message => message.type === 'pivot:result');\n expect(computed.result.virtualized).toBe(true);\n expect(computed.result.totalRows).toBe(400);\n expect(computed.result.rowKeys).toEqual([]);\n expect(computed.result.tree).toEqual({});\n\n onMessage({\n data: {type: 'pivot:releaseData', dataKey: 'virtual-data'},\n });\n\n onMessage({\n data: {\n type: 'pivot:viewport',\n id: 2,\n viewportKey: 1,\n start: 32,\n end: 64,\n columnMode: false,\n },\n });\n const viewport = posted.find(\n message => message.type === 'pivot:viewportResult'\n );\n expect(viewport.result.rows).toHaveLength(32);\n expect(viewport.result.rows[0].cells).toBeDefined();\n expect(viewport.result.rows[0].rowTotal[0]).toBe(1);\n\n onMessage({\n data: {\n type: 'pivot:compute',\n id: 3,\n dataKey: 'virtual-data',\n config,\n },\n });\n const releasedDatasetError = posted.find(\n message => message.type === 'pivot:error' && message.id === 3\n );\n expect(releasedDatasetError.message).toContain('no dataset staged');\n } finally {\n self.addEventListener = originalAddEventListener;\n self.postMessage = originalPostMessage;\n }\n });\n\n it('uses a compact result when columns are wide but rows are short', function() {\n const originalAddEventListener = self.addEventListener;\n const originalPostMessage = self.postMessage;\n const posted = [];\n let onMessage;\n self.addEventListener = (type, listener) => {\n if (type === 'message') {\n onMessage = listener;\n }\n };\n self.postMessage = message => posted.push(message);\n const wideRecords = new Array(150).fill(null).map((_, index) => ({\n employee: `Employee ${String(index).padStart(3, '0')}`,\n gender: index % 2 === 0 ? 'female' : 'male',\n }));\n\n try {\n jest.resetModules();\n require('../pivotWorker');\n onMessage({\n data: {\n type: 'pivot:setData',\n id: 1,\n dataKey: 'wide-data',\n data: wideRecords,\n },\n });\n onMessage({\n data: {\n type: 'pivot:compute',\n id: 1,\n dataKey: 'wide-data',\n config: Object.assign({}, config, {\n rows: ['gender'],\n cols: ['employee'],\n virtualizeRows: true,\n rendererName: 'Table',\n }),\n },\n });\n const computed = posted.find(message => message.type === 'pivot:result');\n expect(computed.result.virtualized).toBe(true);\n expect(computed.result.totalRows).toBe(2);\n expect(computed.result.totalColumns).toBe(150);\n expect(computed.result.tree).toEqual({});\n expect(computed.result.colTotals).toEqual({});\n\n onMessage({\n data: {\n type: 'pivot:viewport',\n id: 2,\n viewportKey: 1,\n start: 0,\n end: 2,\n columnMode: false,\n columnStart: 32,\n columnEnd: 48,\n },\n });\n const viewport = posted.find(\n message => message.type === 'pivot:viewportResult'\n );\n expect(viewport.result.rows).toHaveLength(2);\n expect(\n Object.keys(viewport.result.rows[0].cells).length\n ).toBeLessThanOrEqual(16);\n expect(Object.keys(viewport.result.colTotals)).toHaveLength(16);\n } finally {\n self.addEventListener = originalAddEventListener;\n self.postMessage = originalPostMessage;\n }\n });\n\n it('stops logging when debug is switched off again', function() {\n const client = loadClient();\n client.setWorkerDebug(true);\n logSpy.mockClear();\n client.setWorkerDebug(false);\n expect(loggedText()).toEqual(\n expect.arrayContaining([\n expect.stringContaining('debug logging disabled'),\n ])\n );\n logSpy.mockClear();\n client.setWorkerDebug(false);\n expect(logSpy).not.toHaveBeenCalled();\n });\n});\n"]}
|
|
@@ -79,6 +79,44 @@ describe(' PivotTableUI showProcessing', function () {
|
|
|
79
79
|
expect(node.querySelector('.pvtTable')).not.toBeNull();
|
|
80
80
|
});
|
|
81
81
|
|
|
82
|
+
it('does not pass undefined attrValues while the worker is pending', function () {
|
|
83
|
+
var originalWorker = global.Worker;
|
|
84
|
+
var errorSpy = jest.spyOn(console, 'error').mockImplementation(function () {});
|
|
85
|
+
global.Worker = function () {
|
|
86
|
+
function PendingWorker() {
|
|
87
|
+
_classCallCheck(this, PendingWorker);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
_createClass(PendingWorker, [{
|
|
91
|
+
key: 'postMessage',
|
|
92
|
+
value: function postMessage() {}
|
|
93
|
+
}, {
|
|
94
|
+
key: 'terminate',
|
|
95
|
+
value: function terminate() {}
|
|
96
|
+
}]);
|
|
97
|
+
|
|
98
|
+
return PendingWorker;
|
|
99
|
+
}();
|
|
100
|
+
try {
|
|
101
|
+
mount(Object.assign({}, baseProps, {
|
|
102
|
+
data: new Array(201).fill(null).map(function (_, index) {
|
|
103
|
+
return {
|
|
104
|
+
name: 'Employee ' + index
|
|
105
|
+
};
|
|
106
|
+
})
|
|
107
|
+
}));
|
|
108
|
+
var warnings = errorSpy.mock.calls.map(function (call) {
|
|
109
|
+
return call.join(' ');
|
|
110
|
+
}).filter(function (message) {
|
|
111
|
+
return message.includes('attrValues');
|
|
112
|
+
});
|
|
113
|
+
expect(warnings).toEqual([]);
|
|
114
|
+
} finally {
|
|
115
|
+
global.Worker = originalWorker;
|
|
116
|
+
errorSpy.mockRestore();
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
82
120
|
it('renders the overlay on top of the table when showProcessing is true', function () {
|
|
83
121
|
var _mount2 = mount(baseProps),
|
|
84
122
|
node = _mount2.node,
|
|
@@ -93,12 +131,13 @@ describe(' PivotTableUI showProcessing', function () {
|
|
|
93
131
|
|
|
94
132
|
var overlay = node.querySelector('.pvtLoadingOverlay');
|
|
95
133
|
expect(overlay).not.toBeNull();
|
|
134
|
+
expect(overlay.parentElement.classList.contains('pvtOutput')).toBe(true);
|
|
96
135
|
// The overlay is layered on top of the table: the table must survive.
|
|
97
136
|
expect(node.querySelector('.pvtTable')).not.toBeNull();
|
|
98
137
|
// Only a spinner and a label: progress percentages were removed, the
|
|
99
138
|
// overlay simply stays until the worker posts its result event.
|
|
100
139
|
expect(overlay.querySelector('.pvtLoadingSpinner')).not.toBeNull();
|
|
101
|
-
expect(overlay.querySelector('.pvtLoadingText').textContent).toBe('Processing
|
|
140
|
+
expect(overlay.querySelector('.pvtLoadingText').textContent).toBe('Processing');
|
|
102
141
|
expect(overlay.querySelector('.pvtLoadingBar')).toBeNull();
|
|
103
142
|
expect(overlay.querySelector('.pvtLoadingPercent')).toBeNull();
|
|
104
143
|
|
|
@@ -116,7 +155,7 @@ describe(' PivotTableUI showProcessing', function () {
|
|
|
116
155
|
expect(node.querySelector('.pvtLoadingSpinner')).not.toBeNull();
|
|
117
156
|
expect(node.querySelector('.pvtLoadingBarWrap')).toBeNull();
|
|
118
157
|
expect(node.querySelector('.pvtLoadingPercent')).toBeNull();
|
|
119
|
-
expect(node.querySelector('.pvtLoadingText').textContent).toBe('Processing
|
|
158
|
+
expect(node.querySelector('.pvtLoadingText').textContent).toBe('Processing');
|
|
120
159
|
});
|
|
121
160
|
|
|
122
161
|
it('hides the overlay only after the worker responds and the table renders', function () {
|