umy-ui-table 1.2.4 → 1.2.6
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/lib/button.js +1 -1
- package/lib/icon.js +1 -1
- package/lib/index.js +1 -1
- package/lib/locale/format.js +1 -1
- package/lib/locale/index.js +1 -72
- package/lib/mixins/locale.js +1 -1
- package/lib/theme-chalk/button.css +1 -1
- package/lib/theme-chalk/icon.css +1 -1
- package/lib/theme-chalk/iconfont.1785242763644.ttf +0 -0
- package/lib/theme-chalk/iconfont.1785242763644.woff +0 -0
- package/lib/theme-chalk/iconfont.1785242763644.woff2 +0 -0
- package/lib/theme-chalk/index.css +1 -1
- package/lib/theme-chalk/u-table.css +1 -1
- package/lib/theme-chalk/ux-grid.css +1 -1
- package/lib/u-table-column.js +1 -1
- package/lib/u-table.js +1 -1
- package/lib/umy-ui.common.js +1 -1
- package/lib/utils/util.js +1 -1
- package/lib/ux-grid.js +1 -1
- package/lib/ux-table-column.js +1 -1
- package/package.json +4 -3
- package/packages/.DS_Store +0 -0
- package/packages/input/.DS_Store +0 -0
- package/packages/pagination/.DS_Store +0 -0
- package/packages/scrollbar/.DS_Store +0 -0
- package/packages/select/.DS_Store +0 -0
- package/packages/table/.DS_Store +0 -0
- package/packages/tag/.DS_Store +0 -0
- package/packages/tools/.DS_Store +0 -0
- package/packages/tools/locale/index.js +2 -48
- package/packages/tools/utils/.DS_Store +0 -0
- package/packages/tooltip/.DS_Store +0 -0
- package/packages/u-table/.DS_Store +0 -0
- package/packages/ux-grid/.DS_Store +0 -0
- package/packages/ux-grid/mixins/index.js +133 -25
- package/packages/ux-grid/src/dialog-draft.js +75 -0
- package/packages/ux-grid/src/element-renderer.js +809 -0
- package/packages/ux-grid/src/grid-height.js +75 -0
- package/packages/ux-grid/src/grid-kernel.js +14 -0
- package/packages/ux-grid/src/setup.js +42 -0
- package/packages/ux-grid/src/ux-grid.vue +215 -57
- package/packages/ux-grid/test/browser-acceptance.cjs +192 -0
- package/packages/ux-grid/test/browser-baseline-loader.cjs +10 -0
- package/packages/ux-grid/test/browser-entry.js +242 -0
- package/packages/ux-grid/test/browser-layout-contracts.cjs +105 -0
- package/packages/ux-grid/test/browser-layout-entry.js +66 -0
- package/packages/ux-grid/test/browser-renderer-contracts.cjs +262 -0
- package/packages/ux-grid/test/browser-renderer-entry.js +129 -0
- package/packages/ux-grid/test/browser-resize-contracts.cjs +150 -0
- package/packages/ux-grid/test/compatibility.cjs +176 -0
- package/packages/ux-grid/test/contracts.cjs +281 -0
- package/packages/ux-grid/test/delivery-umd.cjs +48 -0
- package/packages/ux-grid/test/delivery.cjs +103 -0
- package/packages/ux-grid/test/integration.cjs +132 -0
- package/packages/ux-grid/test/real-kernel-contracts.cjs +335 -0
- package/packages/ux-grid/test/real-kernel-events.cjs +233 -0
- package/packages/ux-grid/test/renderer-async.cjs +140 -0
- package/packages/ux-grid/test/renderer-boundaries.cjs +196 -0
- package/packages/ux-grid/test/ui-components.cjs +103 -0
- package/tools/locale/format.js +1 -1
- package/tools/locale/index.js +1 -7
- package/tools/mixins/locale.js +1 -1
- package/tools/utils/util.js +1 -1
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// DOM semantics only: jsdom does not measure layout or scrolling performance.
|
|
2
|
+
// UX_GRID_TEST_DEPENDENCIES=/path/to/consumer node packages/ux-grid/test/integration.cjs
|
|
3
|
+
const assert = require('assert');
|
|
4
|
+
const dependencyRoot = process.env.UX_GRID_TEST_DEPENDENCIES || process.cwd();
|
|
5
|
+
const external = name => require(require.resolve(name, { paths: [dependencyRoot] }));
|
|
6
|
+
const { JSDOM } = external('jsdom');
|
|
7
|
+
const dom = new JSDOM('<html><body><script></script></body></html>', { url: 'http://localhost/' });
|
|
8
|
+
for (const key of ['window', 'document', 'Element', 'HTMLElement', 'HTMLTableElement', 'Node', 'MutationObserver', 'getComputedStyle', 'name', 'Image']) global[key] = dom.window[key];
|
|
9
|
+
Object.defineProperty(global, 'navigator', { configurable: true, value: dom.window.navigator });
|
|
10
|
+
global.requestAnimationFrame = callback => setTimeout(callback, 0);
|
|
11
|
+
global.cancelAnimationFrame = clearTimeout;
|
|
12
|
+
const Vue = require('vue');
|
|
13
|
+
// Match a consumer bundler's Vue singleton across linked workspaces.
|
|
14
|
+
const vueModule = require.cache[require.resolve('vue')];
|
|
15
|
+
for (const root of [dependencyRoot, require.resolve('vxe-table')]) {
|
|
16
|
+
require.cache[require.resolve('vue', { paths: [root] })] = vueModule;
|
|
17
|
+
}
|
|
18
|
+
Vue.config.productionTip = false;
|
|
19
|
+
Vue.config.devtools = false;
|
|
20
|
+
Vue.use(external('element-ui'));
|
|
21
|
+
const unwrap = value => value.default || value;
|
|
22
|
+
const Grid = unwrap(require('../../../lib/ux-grid.js'));
|
|
23
|
+
const Column = unwrap(require('../../../lib/ux-table-column.js'));
|
|
24
|
+
const errors = [];
|
|
25
|
+
Vue.config.errorHandler = error => errors.push(error);
|
|
26
|
+
const delay = () => new Promise(resolve => setTimeout(resolve, 80));
|
|
27
|
+
let vm;
|
|
28
|
+
let selection;
|
|
29
|
+
(async () => {
|
|
30
|
+
vm = new Vue({
|
|
31
|
+
data: () => ({ prefix: 'old', extra: false, named: 'alpha', explicitColumns: null, rows: [{ id: 1, name: 'One', number: 2 }] }),
|
|
32
|
+
render(h) {
|
|
33
|
+
const prefix = this.prefix;
|
|
34
|
+
return h(Grid, { ref: 'grid', scopedSlots: { [this.named]: () => h('span', 'named-' + this.named) }, on: { 'selection-change': value => { selection = value; } }, props: { columns: this.explicitColumns, data: this.rows, height: 300, editConfig: { trigger: 'click', mode: 'cell' } } }, [
|
|
35
|
+
h(Column, { key: 'name', props: { field: 'name', title: 'Name', sortable: true, editRender: { name: 'ElInput' } }, scopedSlots: { default: () => h('span', prefix) } }),
|
|
36
|
+
h(Column, { key: 'number', props: { field: 'number', title: 'Number', formatter: ({ cellValue }) => prefix + cellValue } }),
|
|
37
|
+
this.extra ? h(Column, { key: 'id', props: { field: 'id', title: 'ID', slots: { default: this.named } } }) : null
|
|
38
|
+
]);
|
|
39
|
+
}
|
|
40
|
+
}).$mount();
|
|
41
|
+
document.body.appendChild(vm.$el);
|
|
42
|
+
await delay();
|
|
43
|
+
const grid = vm.$refs.grid.$refs.singleTable;
|
|
44
|
+
const before = grid.getColumnByField('name');
|
|
45
|
+
before.resizeWidth = 177;
|
|
46
|
+
await grid.sort('name', 'asc');
|
|
47
|
+
await grid.updateData(); // Exercise a cache-hit render before changing formatter.
|
|
48
|
+
await delay();
|
|
49
|
+
const originalUpdateData = grid.updateData;
|
|
50
|
+
let fullDataUpdates = 0;
|
|
51
|
+
grid.updateData = function (...args) { fullDataUpdates++; return originalUpdateData.apply(this, args); };
|
|
52
|
+
vm.prefix = 'new';
|
|
53
|
+
await delay();
|
|
54
|
+
assert(grid.getColumnByField('name') === before, 'slot update must preserve actual ColumnInfo identity');
|
|
55
|
+
assert.equal(before.resizeWidth, 177);
|
|
56
|
+
assert.equal(before.order, 'asc');
|
|
57
|
+
assert(vm.$el.textContent.includes('new2'), 'formatter updated in real DOM');
|
|
58
|
+
assert(vm.$el.textContent.includes('new'), 'slot updated in real DOM');
|
|
59
|
+
await grid.setEditCell(vm.rows[0], 'name');
|
|
60
|
+
const editing = grid.getEditCell();
|
|
61
|
+
assert(editing && editing.column === before, 'real edit cell activated');
|
|
62
|
+
assert(vm.$el.querySelector('.el-input__inner'), 'real Element input mounted in edit cell');
|
|
63
|
+
vm.prefix = 'latest';
|
|
64
|
+
await delay();
|
|
65
|
+
assert(grid.getEditCell().column === before, 'callback update preserves active edit column');
|
|
66
|
+
assert(vm.$el.textContent.includes('latest2'), 'repeated formatter updates invalidate cache');
|
|
67
|
+
assert.equal(fullDataUpdates, 0, 'render-only updates do not reprocess full table data');
|
|
68
|
+
grid.updateData = originalUpdateData;
|
|
69
|
+
await grid.clearEdit();
|
|
70
|
+
const wrapper = vm.$refs.grid;
|
|
71
|
+
let filterEvent;
|
|
72
|
+
wrapper.$on('filter-change', payload => { filterEvent = payload; });
|
|
73
|
+
const filters = [{ field: 'name', values: ['One'] }];
|
|
74
|
+
grid.$emit('filter-change', { column: before, field: 'name', filters: () => filters });
|
|
75
|
+
assert.deepStrictEqual(filterEvent.filters, [], 'only actual visible filterable columns appear in compatibility payload');
|
|
76
|
+
assert.equal(filterEvent.property, 'name');
|
|
77
|
+
grid.$emit('checkbox-change', { row: vm.rows[0], records: () => [vm.rows[0]] });
|
|
78
|
+
assert.strictEqual(selection[0], vm.rows[0]);
|
|
79
|
+
vm.extra = true;
|
|
80
|
+
await delay();
|
|
81
|
+
assert(grid.getColumnByField('id'), 'structural addition loads a real column');
|
|
82
|
+
vm.explicitColumns = [{ field: 'id', slots: { default: 'alpha' } }];
|
|
83
|
+
await delay();
|
|
84
|
+
assert(vm.$el.textContent.includes('named-alpha'), 'named scoped slot forwarded');
|
|
85
|
+
vm.named = 'beta';
|
|
86
|
+
vm.explicitColumns = [{ field: 'id', slots: { default: 'beta' } }];
|
|
87
|
+
await delay();
|
|
88
|
+
assert(grid.$scopedSlots.beta && !grid.$scopedSlots.alpha, 'dynamic scoped slot names forwarded and removed');
|
|
89
|
+
assert(vm.$el.textContent.includes('named-beta'), 'replacement named slot renders');
|
|
90
|
+
await wrapper.loadData([{ id: 2, name: 'Loaded', number: 3 }]);
|
|
91
|
+
assert.equal(grid.getTableData().fullData[0].name, 'Loaded');
|
|
92
|
+
// Pure instance listeners must work without parent v-on listeners.
|
|
93
|
+
for (let round = 0; round < 3; round++) {
|
|
94
|
+
const race = new Vue({
|
|
95
|
+
data: () => ({ cols: [{ field: 'id' }], rows: [{ id: 1 }] }),
|
|
96
|
+
render(h) { return h(Grid, { ref: 'grid', props: { columns: this.cols, data: this.rows, height: 200 } }); }
|
|
97
|
+
}).$mount();
|
|
98
|
+
document.body.appendChild(race.$el);
|
|
99
|
+
await delay();
|
|
100
|
+
const raceWrapper = race.$refs.grid;
|
|
101
|
+
const raceGrid = raceWrapper.$refs.singleTable;
|
|
102
|
+
for (const [event, kernelEvent] of [['selection-change', 'checkbox-change'], ['select', 'checkbox-change'], ['select-all', 'checkbox-all']]) {
|
|
103
|
+
let count = 0;
|
|
104
|
+
let reads = 0;
|
|
105
|
+
raceWrapper.$once(event, records => { assert.strictEqual(records[0], race.rows[0]); count++; });
|
|
106
|
+
const params = { row: race.rows[0], records: () => { reads++; return race.rows; } };
|
|
107
|
+
raceGrid.$emit(kernelEvent, params);
|
|
108
|
+
raceGrid.$emit(kernelEvent, params);
|
|
109
|
+
assert.equal(count, 1);
|
|
110
|
+
assert.equal(reads, 1, 'once listener removal stops reading records');
|
|
111
|
+
}
|
|
112
|
+
let cancelledCalls = 0;
|
|
113
|
+
const originalLoad = raceGrid.loadColumn;
|
|
114
|
+
raceGrid.loadColumn = function (...args) {
|
|
115
|
+
if (this._isDestroyed || this._isBeingDestroyed) cancelledCalls++;
|
|
116
|
+
return originalLoad.apply(this, args);
|
|
117
|
+
};
|
|
118
|
+
race.cols = [{ field: 'id' }, { field: 'name' }];
|
|
119
|
+
// The columns watcher runs first and schedules another nextTick callback;
|
|
120
|
+
// destroy before that second callback dereferences the table instance.
|
|
121
|
+
await Vue.nextTick(() => race.$destroy());
|
|
122
|
+
await delay();
|
|
123
|
+
assert(cancelledCalls > 0, 'real watcher callback reached destroyed guard');
|
|
124
|
+
if (race.$el.parentNode) race.$el.parentNode.removeChild(race.$el);
|
|
125
|
+
}
|
|
126
|
+
assert.deepStrictEqual(errors, []);
|
|
127
|
+
console.log('ux-grid real Vue/VXE/Element DOM integration passed');
|
|
128
|
+
})().catch(error => { console.error(error.stack); process.exitCode = 1; }).finally(() => {
|
|
129
|
+
if (vm) vm.$destroy();
|
|
130
|
+
dom.window.close();
|
|
131
|
+
setImmediate(() => process.exit(process.exitCode || 0));
|
|
132
|
+
});
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
// Core public wrapper contracts against actual OLD and NEW kernels in isolated
|
|
2
|
+
// Vue/jsdom processes. No mocked kernel methods; no layout/performance claims.
|
|
3
|
+
const assert = require('assert');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const root = process.env.UX_GRID_TEST_DEPENDENCIES;
|
|
6
|
+
if (!root) throw Error('Set UX_GRID_TEST_DEPENDENCIES to an installed legacy consumer');
|
|
7
|
+
const mode = process.argv[2];
|
|
8
|
+
if (!mode) {
|
|
9
|
+
const results = {};
|
|
10
|
+
const traces = {};
|
|
11
|
+
for (const name of ['old', 'new']) {
|
|
12
|
+
const child = require('child_process').spawnSync(process.execPath, [__filename, name], { env: process.env, encoding: 'utf8' });
|
|
13
|
+
assert.strictEqual(child.status, 0, name + '\n' + child.stdout + child.stderr);
|
|
14
|
+
const line = child.stdout.split('\n').find(value => value.startsWith('RESULT '));
|
|
15
|
+
assert(line, name + ' completed all assertions');
|
|
16
|
+
results[name] = JSON.parse(line.slice(7));
|
|
17
|
+
const timingLine = child.stdout.split('\n').find(value => value.startsWith('TIMING '));
|
|
18
|
+
assert(timingLine, name + ' emits timing evidence');
|
|
19
|
+
traces[name] = JSON.parse(timingLine.slice(7));
|
|
20
|
+
console.log(name + ' ' + timingLine);
|
|
21
|
+
}
|
|
22
|
+
assert.deepStrictEqual(results.new, results.old, 'public observable results match legacy');
|
|
23
|
+
for (const [label, current] of Object.entries(traces.new)) {
|
|
24
|
+
const previous = traces.old[label];
|
|
25
|
+
assert(previous, label + ' legacy observation exists');
|
|
26
|
+
if (['loadColumn', 'reloadColumn'].includes(label)) {
|
|
27
|
+
assert.equal(previous.returnType, 'undefined'); assert.equal(current.returnType, 'Promise');
|
|
28
|
+
} else assert.equal(current.returnType, previous.returnType, label + ' return contract');
|
|
29
|
+
for (const trace of [current.sequence, previous.sequence]) {
|
|
30
|
+
assert(trace.indexOf('before-call') < trace.indexOf('returned'));
|
|
31
|
+
if (trace.includes('resolved')) assert(trace.indexOf('returned') < trace.indexOf('resolved'));
|
|
32
|
+
if (trace.includes('edit-actived')) {
|
|
33
|
+
assert.equal(trace.filter(item => item === 'edit-actived').length, 1);
|
|
34
|
+
assert(trace.indexOf('returned') < trace.indexOf('edit-actived'));
|
|
35
|
+
}
|
|
36
|
+
if (label === 'clearActived') {
|
|
37
|
+
assert.equal(trace.filter(item => item === 'edit-closed').length, 1);
|
|
38
|
+
assert(trace.indexOf('edit-closed') < trace.indexOf('returned'));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// Faster nextTick completion is allowed; semantic event order is not.
|
|
42
|
+
assert.deepStrictEqual(current.sequence.filter(item => item !== 'nextTick' && item !== 'resolved'), previous.sequence.filter(item => item !== 'nextTick' && item !== 'resolved'), label + ' event order');
|
|
43
|
+
}
|
|
44
|
+
console.log('real old/new kernel wrapper contracts passed:', JSON.stringify(results.new));
|
|
45
|
+
process.exit(0);
|
|
46
|
+
}
|
|
47
|
+
const external = name => require(require.resolve(name, { paths: [root] }));
|
|
48
|
+
require('babel-polyfill');
|
|
49
|
+
const { JSDOM } = external('jsdom');
|
|
50
|
+
const dom = new JSDOM('<html><body><script></script></body></html>', { url: 'http://localhost/' });
|
|
51
|
+
for (const key of ['window', 'document', 'Element', 'HTMLElement', 'HTMLTableElement', 'Node', 'MutationObserver', 'getComputedStyle', 'name', 'Image']) global[key] = dom.window[key];
|
|
52
|
+
Object.defineProperty(global, 'navigator', { configurable: true, value: dom.window.navigator });
|
|
53
|
+
global.requestAnimationFrame = cb => setTimeout(cb, 0);
|
|
54
|
+
global.cancelAnimationFrame = clearTimeout;
|
|
55
|
+
const Vue = require('vue');
|
|
56
|
+
for (const location of [root, require.resolve('vxe-table'), require.resolve('vxe-pc-ui')]) require.cache[require.resolve('vue', { paths: [location] })] = require.cache[require.resolve('vue')];
|
|
57
|
+
Vue.config.productionTip = false;
|
|
58
|
+
Vue.config.devtools = false;
|
|
59
|
+
const errors = [];
|
|
60
|
+
Vue.config.errorHandler = error => errors.push(error.stack || String(error));
|
|
61
|
+
Vue.use(external('element-ui'));
|
|
62
|
+
let Grid;
|
|
63
|
+
if (mode === 'old') {
|
|
64
|
+
const old = external('umy-table/lib/umy-table.common.js');
|
|
65
|
+
Vue.use(old.default || old);
|
|
66
|
+
Grid = Vue.options.components.UxGrid;
|
|
67
|
+
} else {
|
|
68
|
+
const current = require('../../../lib/ux-grid.js');
|
|
69
|
+
Grid = current.default || current;
|
|
70
|
+
}
|
|
71
|
+
const settle = () => new Promise(resolve => setTimeout(resolve, 60));
|
|
72
|
+
function promiseResult(value, label) {
|
|
73
|
+
assert(value && typeof value.then === 'function', label + ' returns Promise');
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
const observations = {};
|
|
77
|
+
let summaryCalls = 0;
|
|
78
|
+
let summaryParams;
|
|
79
|
+
let summaryLabel = 'Initial summary';
|
|
80
|
+
let statusValidations = 0;
|
|
81
|
+
let spanParams;
|
|
82
|
+
const spanResult = { rowspan: 1, colspan: 1 };
|
|
83
|
+
let vm;
|
|
84
|
+
(async () => {
|
|
85
|
+
vm = new Vue({ render(h) { return h(Grid, { ref: 'grid', props: {
|
|
86
|
+
height: 300, data: [], keepSource: true, rowId: 'id',
|
|
87
|
+
showSummary: true,
|
|
88
|
+
spanMethod(params) { spanParams = params; return spanResult; },
|
|
89
|
+
summaryMethod(params) {
|
|
90
|
+
summaryCalls++;
|
|
91
|
+
summaryParams = params;
|
|
92
|
+
return [[summaryLabel, '', '', '']];
|
|
93
|
+
},
|
|
94
|
+
treeConfig: { children: 'children' }, editConfig: { mode: 'cell', trigger: 'click' },
|
|
95
|
+
mouseConfig: { selected: true },
|
|
96
|
+
rules: { name: [{ required: true, message: 'Name required' }, { trigger: 'change', validator() { statusValidations++; return Promise.resolve(); } }] },
|
|
97
|
+
columns: [{ type: 'checkbox' }, { type: 'expand' }, { field: 'id', sortable: true }, { field: 'name', treeNode: true, editRender: { name: 'ElInput' } }]
|
|
98
|
+
} }); } }).$mount();
|
|
99
|
+
document.body.appendChild(vm.$el);
|
|
100
|
+
await settle();
|
|
101
|
+
const wrapper = vm.$refs.grid;
|
|
102
|
+
const kernel = wrapper.tableExample();
|
|
103
|
+
const rows = [{ id: 1, name: 'One', children: [{ id: 11, name: 'Child' }] }, { id: 2, name: 'Two' }];
|
|
104
|
+
for (const method of ['loadData', 'reloadData']) {
|
|
105
|
+
const result = wrapper[method](rows);
|
|
106
|
+
assert(result && typeof result.then === 'function', method + ' returns Promise');
|
|
107
|
+
await result;
|
|
108
|
+
assert.strictEqual(wrapper.getTableData().fullData[0], rows[0]);
|
|
109
|
+
}
|
|
110
|
+
observations.data = wrapper.getTableData().fullData.map(row => row.id);
|
|
111
|
+
assert.strictEqual(wrapper.getRowById(1), rows[0]);
|
|
112
|
+
assert(!wrapper.getRowById('missing-row'));
|
|
113
|
+
assert(!wrapper.getColumnByField('missing-field'));
|
|
114
|
+
assert(spanParams && rows.includes(spanParams.row), 'real body renderer invokes spanMethod with original row');
|
|
115
|
+
assert.strictEqual(wrapper.getTableColumn().fullColumn[spanParams.columnIndex], spanParams.column);
|
|
116
|
+
assert.strictEqual(wrapper.arraySpanMethod(spanParams), spanResult, 'wrapper preserves callback result identity');
|
|
117
|
+
for (const method of ['loadColumn', 'reloadColumn']) {
|
|
118
|
+
const result = wrapper[method]([{ type: 'checkbox' }, { type: 'expand' }, { field: 'id', sortable: true }, { field: 'name', filters: [], treeNode: true, editRender: { name: 'ElInput' } }]);
|
|
119
|
+
console.log(method + ' return: ' + (result && typeof result.then === 'function' ? 'Promise' : String(result)));
|
|
120
|
+
if (mode === 'new') assert(result && typeof result.then === 'function');
|
|
121
|
+
else assert.strictEqual(result, undefined);
|
|
122
|
+
await result;
|
|
123
|
+
await settle();
|
|
124
|
+
assert(wrapper.getColumnByField('name'));
|
|
125
|
+
}
|
|
126
|
+
observations.columns = wrapper.getTableColumn().fullColumn.length;
|
|
127
|
+
const previousSummaryCalls = summaryCalls;
|
|
128
|
+
summaryLabel = 'Updated summary';
|
|
129
|
+
await promiseResult(wrapper.updateFooter(), 'updateFooter');
|
|
130
|
+
await Vue.nextTick();
|
|
131
|
+
assert(summaryCalls > previousSummaryCalls, 'updateFooter invokes actual summaryMethod');
|
|
132
|
+
assert.strictEqual(summaryParams.data[0], rows[0]);
|
|
133
|
+
assert.strictEqual(summaryParams.columns[3], wrapper.getColumnByField('name'));
|
|
134
|
+
assert(vm.$el.querySelector('tfoot').textContent.includes('Updated summary'), 'updated summary renders in real footer DOM');
|
|
135
|
+
observations.summary = true;
|
|
136
|
+
assert.strictEqual(wrapper.setCurrentRow(rows[1]), undefined);
|
|
137
|
+
await settle();
|
|
138
|
+
assert.strictEqual(kernel.getCurrentRecord(), rows[1]);
|
|
139
|
+
observations.current = kernel.getCurrentRecord().id;
|
|
140
|
+
assert.strictEqual(wrapper.toggleRowSelection([{ row: rows[0], selected: true }]), undefined);
|
|
141
|
+
await settle();
|
|
142
|
+
assert.strictEqual(wrapper.getCheckboxRecords()[0], rows[0]);
|
|
143
|
+
assert.equal(wrapper.isCheckedByCheckboxRow(rows[0]), true);
|
|
144
|
+
assert.strictEqual(wrapper.toggleAllSelection(true), undefined);
|
|
145
|
+
await settle();
|
|
146
|
+
observations.checked = wrapper.getCheckboxRecords().map(row => row.id).sort();
|
|
147
|
+
assert.strictEqual(wrapper.clearSelection(), undefined);
|
|
148
|
+
await settle();
|
|
149
|
+
assert.equal(wrapper.getCheckboxRecords().length, 0);
|
|
150
|
+
assert.equal(wrapper.isCheckedByCheckboxRow(rows[0]), false);
|
|
151
|
+
await promiseResult(wrapper.setRowExpand(rows[1], true), 'setRowExpand');
|
|
152
|
+
assert(kernel.isExpandByRow(rows[1]));
|
|
153
|
+
await promiseResult(wrapper.clearRowExpand(), 'clearRowExpand');
|
|
154
|
+
assert(!kernel.isExpandByRow(rows[1]));
|
|
155
|
+
await promiseResult(wrapper.setTreeExpand(rows[0], true), 'setTreeExpand');
|
|
156
|
+
assert(wrapper.isTreeExpandByRow(rows[0]));
|
|
157
|
+
assert.strictEqual(wrapper.getTreeExpandRecords()[0], rows[0]);
|
|
158
|
+
await promiseResult(wrapper.clearTreeExpand(), 'clearTreeExpand');
|
|
159
|
+
assert(!wrapper.isTreeExpandByRow(rows[0]));
|
|
160
|
+
observations.expand = true;
|
|
161
|
+
assert.strictEqual(wrapper.sort('id', 'desc'), undefined);
|
|
162
|
+
await settle();
|
|
163
|
+
assert.equal(wrapper.getColumnByField('id').order, 'desc');
|
|
164
|
+
assert.equal(wrapper.getTableData().visibleData[0].id, 2, 'sort changes actual row order');
|
|
165
|
+
assert.strictEqual(wrapper.clearSort(), undefined);
|
|
166
|
+
await settle();
|
|
167
|
+
assert(!wrapper.getColumnByField('id').order);
|
|
168
|
+
await promiseResult(wrapper.setFilter(wrapper.getColumnByField('name'), [{ label: 'One', value: 'One', checked: true }]), 'setFilter');
|
|
169
|
+
assert.equal(wrapper.getColumnByField('name').filters[0].value, 'One');
|
|
170
|
+
assert.strictEqual(wrapper.clearFilter('name'), undefined);
|
|
171
|
+
await settle();
|
|
172
|
+
assert(!wrapper.getColumnByField('name').filters[0].checked);
|
|
173
|
+
observations.sortFilter = true;
|
|
174
|
+
rows[0].name = 'Changed';
|
|
175
|
+
assert(wrapper.isUpdateByRow(rows[0], 'name'));
|
|
176
|
+
assert(wrapper.getUpdateRecords().includes(rows[0]));
|
|
177
|
+
await promiseResult(wrapper.revertData(rows[0]), 'revertData');
|
|
178
|
+
assert.equal(rows[0].name, 'One');
|
|
179
|
+
assert(!wrapper.isUpdateByRow(rows[0]));
|
|
180
|
+
observations.revert = rows[0].name;
|
|
181
|
+
const replacement = { id: 2, name: 'Reloaded two' };
|
|
182
|
+
const reloadedRow = wrapper.reloadRow(rows[1], replacement);
|
|
183
|
+
assert(reloadedRow && typeof reloadedRow.then === 'function');
|
|
184
|
+
await reloadedRow;
|
|
185
|
+
assert.equal(rows[1].name, 'Reloaded two');
|
|
186
|
+
assert(!wrapper.isUpdateByRow(rows[1]), 'reloadRow resets source baseline');
|
|
187
|
+
const updated = wrapper.updateData();
|
|
188
|
+
assert(updated && typeof updated.then === 'function');
|
|
189
|
+
await updated;
|
|
190
|
+
assert.equal(wrapper.getTableData().fullData[1].name, 'Reloaded two');
|
|
191
|
+
// Flat data avoids kernel-specific tree insertion configuration.
|
|
192
|
+
await wrapper.reloadData([{ id: 3, name: 'Three' }]);
|
|
193
|
+
const insertion = wrapper.insertRow({ id: 4, name: 'Four' }, -1);
|
|
194
|
+
assert(insertion && typeof insertion.then === 'function');
|
|
195
|
+
const inserted = await insertion;
|
|
196
|
+
assert.equal(inserted.row.id, 4);
|
|
197
|
+
assert(Array.isArray(wrapper.getRecordset()), 'legacy wrapper exposes only insertRecords');
|
|
198
|
+
assert.strictEqual(wrapper.getRecordset()[0], inserted.row);
|
|
199
|
+
assert.strictEqual(wrapper.remove(inserted.row), undefined);
|
|
200
|
+
await settle();
|
|
201
|
+
assert.equal(wrapper.getInsertRecords().length, 0);
|
|
202
|
+
observations.insertRemove = true;
|
|
203
|
+
const validRows = [{ id: 5, name: 'Valid' }];
|
|
204
|
+
await wrapper.reloadData(validRows);
|
|
205
|
+
for (const method of ['validate', 'fullValidate']) {
|
|
206
|
+
for (const args of [[], [[]], [validRows]]) {
|
|
207
|
+
const pending = wrapper[method](...args);
|
|
208
|
+
assert(pending && typeof pending.then === 'function');
|
|
209
|
+
const result = await pending;
|
|
210
|
+
assert(!result || !Object.keys(result).length, method + ' valid returns no errors');
|
|
211
|
+
}
|
|
212
|
+
validRows[0].name = '';
|
|
213
|
+
for (const [label, args] of [['default', []], ['empty', [[]]], ['explicit', [validRows]]]) {
|
|
214
|
+
const result = await wrapper[method](...args);
|
|
215
|
+
const hasErrors = !!(result && result.name && result.name.length);
|
|
216
|
+
observations[method + ':' + label] = hasErrors;
|
|
217
|
+
if (label !== 'empty') assert(hasErrors, method + ' resolves validation errors, does not reject');
|
|
218
|
+
else assert.strictEqual(hasErrors, false, method + ' empty row array excludes all rows');
|
|
219
|
+
if (hasErrors) {
|
|
220
|
+
assert.strictEqual(result.name[0].row, validRows[0]);
|
|
221
|
+
assert.equal(result.name[0].rule.message, 'Name required');
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
validRows[0].name = 'Valid';
|
|
225
|
+
}
|
|
226
|
+
observations.validation = true;
|
|
227
|
+
const trace = [];
|
|
228
|
+
const phases = {};
|
|
229
|
+
for (const name of ['selection-change', 'select', 'select-all', 'edit-actived', 'edit-closed', 'current-change']) {
|
|
230
|
+
wrapper.$on(name, () => trace.push(name));
|
|
231
|
+
}
|
|
232
|
+
async function measure(label, method, args, expectedReturn, check) {
|
|
233
|
+
trace.length = 0; trace.push('before-call');
|
|
234
|
+
const result = wrapper[method](...args);
|
|
235
|
+
const returnType = result && typeof result.then === 'function' ? 'Promise' : typeof result;
|
|
236
|
+
assert.equal(returnType, expectedReturn, label + ' return');
|
|
237
|
+
trace.push('returned');
|
|
238
|
+
const completion = returnType === 'Promise' ? result.then(() => {
|
|
239
|
+
trace.push('resolved');
|
|
240
|
+
if (check) check();
|
|
241
|
+
}) : null;
|
|
242
|
+
await Vue.nextTick(); trace.push('nextTick');
|
|
243
|
+
if (completion) await completion;
|
|
244
|
+
await settle(); trace.push('settled');
|
|
245
|
+
if (check && !completion) check();
|
|
246
|
+
phases[label] = { returnType, sequence: trace.slice() };
|
|
247
|
+
}
|
|
248
|
+
const activeRow = validRows[0];
|
|
249
|
+
await wrapper.clearActived();
|
|
250
|
+
await measure('setActiveCell', 'setActiveCell', [activeRow, 'name'], 'undefined', () => assert(wrapper.isActiveByRow(activeRow)));
|
|
251
|
+
assert(trace.includes('edit-actived'));
|
|
252
|
+
const validationsBefore = statusValidations;
|
|
253
|
+
await measure('updateStatus', 'updateStatus', [{ row: activeRow, column: wrapper.getColumnByField('name') }], 'undefined', () => assert(statusValidations > validationsBefore, 'updateStatus invokes configured change validator'));
|
|
254
|
+
await measure('clearActived', 'clearActived', [], 'Promise', () => assert(!wrapper.isActiveByRow(activeRow)));
|
|
255
|
+
assert(trace.includes('edit-closed'));
|
|
256
|
+
await measure('setActiveRow', 'setActiveRow', [activeRow], 'undefined', () => assert(wrapper.isActiveByRow(activeRow)));
|
|
257
|
+
await wrapper.clearActived();
|
|
258
|
+
activeRow.name = '';
|
|
259
|
+
await wrapper.fullValidate([activeRow]); await settle();
|
|
260
|
+
assert(vm.$el.querySelector('td.col--valid-error'), 'real validation marks invalid cell');
|
|
261
|
+
await measure('clearValidate', 'clearValidate', [], 'Promise', () => assert(!vm.$el.querySelector('td.col--valid-error'), 'clearValidate clears actual error DOM at resolution'));
|
|
262
|
+
activeRow.name = 'Valid'; await wrapper.clearActived();
|
|
263
|
+
for (const [method, args] of [
|
|
264
|
+
['toggleRowSelection', [[{ row: activeRow, selected: true }]]],
|
|
265
|
+
['newtoggleRowSelections', [[{ row: activeRow }]]],
|
|
266
|
+
['toggleAllSelection', []], ['clearSelection', []]
|
|
267
|
+
]) {
|
|
268
|
+
await measure(method, method, args, 'undefined');
|
|
269
|
+
assert.equal(trace.filter(name => name === 'selection-change').length, 1);
|
|
270
|
+
assert(trace.indexOf('selection-change') < trace.indexOf('returned'));
|
|
271
|
+
assert(!trace.includes('select') && !trace.includes('select-all'));
|
|
272
|
+
}
|
|
273
|
+
await measure('setCurrentRow', 'setCurrentRow', [activeRow], 'undefined', () => assert.strictEqual(kernel.getCurrentRecord(), activeRow));
|
|
274
|
+
assert(!trace.includes('current-change'));
|
|
275
|
+
await measure('setCurrentRow:clear', 'setCurrentRow', [], 'undefined', () => assert(!kernel.getCurrentRecord()));
|
|
276
|
+
assert(!trace.includes('current-change'));
|
|
277
|
+
await measure('reloadData', 'reloadData', [rows], 'Promise', () => assert.strictEqual(wrapper.getTableData().fullData[0], rows[0]));
|
|
278
|
+
for (const [method, args, expanded] of [
|
|
279
|
+
['toggleRowExpand', [rows[1]], true], ['toggleRowExpand', [rows[1]], false],
|
|
280
|
+
['setAllRowExpand', [true], true], ['setAllRowExpand', [false], false]
|
|
281
|
+
]) await measure(method + ':' + expanded, method, args, 'Promise', () => assert.equal(kernel.isExpandByRow(rows[1]), expanded));
|
|
282
|
+
for (const [method, args, expanded] of [
|
|
283
|
+
['toggleTreeExpand', [rows[0]], true], ['toggleTreeExpand', [rows[0]], false],
|
|
284
|
+
['setAllTreeExpand', [true], true], ['setAllTreeExpand', [false], false]
|
|
285
|
+
]) await measure(method + ':' + expanded, method, args, 'Promise', () => assert.equal(wrapper.isTreeExpandByRow(rows[0]), expanded));
|
|
286
|
+
for (const method of ['doLayout', 'refreshColumn', 'refreshScroll', 'pagingScrollTopLeft']) {
|
|
287
|
+
await measure(method, method, [], 'undefined');
|
|
288
|
+
}
|
|
289
|
+
await measure('scrollToRow', 'scrollToRow', [rows[1]], 'undefined');
|
|
290
|
+
assert.equal(wrapper.getColumnIndex(wrapper.getColumnByField('name')), 3);
|
|
291
|
+
const selectedEvents = [];
|
|
292
|
+
wrapper.$on('edit-currently-active', payload => selectedEvents.push(payload));
|
|
293
|
+
await kernel.setSelectCell(rows[0], 'id'); await settle();
|
|
294
|
+
for (const method of ['doLayout', 'refreshColumn']) {
|
|
295
|
+
selectedEvents.length = 0;
|
|
296
|
+
assert.strictEqual(wrapper[method](), undefined);
|
|
297
|
+
await settle();
|
|
298
|
+
assert(selectedEvents.length, method + ' refreshes selected-cell event');
|
|
299
|
+
if (mode === 'new') assert.equal(selectedEvents.length, 1, 'no duplicate selected refresh');
|
|
300
|
+
selectedEvents.forEach(payload => {
|
|
301
|
+
assert.strictEqual(payload.row, rows[0]);
|
|
302
|
+
assert.strictEqual(payload.column, wrapper.getColumnByField('id'));
|
|
303
|
+
assert(payload.cell instanceof HTMLElement);
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
if (mode === 'new') {
|
|
307
|
+
selectedEvents.length = 0;
|
|
308
|
+
let rendered = 0;
|
|
309
|
+
wrapper.$once('data-rendered', () => {
|
|
310
|
+
rendered++;
|
|
311
|
+
kernel.setSelectCell(rows[0], 'id');
|
|
312
|
+
});
|
|
313
|
+
await wrapper.loadData(rows); await settle();
|
|
314
|
+
assert.equal(rendered, 1, 'real loadData emits data-rendered');
|
|
315
|
+
assert.equal(selectedEvents.length, 1, 'real selected event suppresses pending rendered refresh duplicate');
|
|
316
|
+
assert.strictEqual(selectedEvents[0].row, rows[0]);
|
|
317
|
+
assert.strictEqual(selectedEvents[0].column, wrapper.getColumnByField('id'));
|
|
318
|
+
assert(selectedEvents[0].cell instanceof HTMLElement);
|
|
319
|
+
}
|
|
320
|
+
for (const method of ['loadColumn', 'reloadColumn']) {
|
|
321
|
+
const field = method + 'Unique';
|
|
322
|
+
await measure(method, method, [[{ field }]], mode === 'new' ? 'Promise' : 'undefined', () => {
|
|
323
|
+
assert.equal(wrapper.getTableColumn().fullColumn.length, 1);
|
|
324
|
+
assert(wrapper.getColumnByField(field));
|
|
325
|
+
assert(!wrapper.getColumnByField('name'));
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
console.log('TIMING ' + JSON.stringify(phases));
|
|
329
|
+
assert.deepStrictEqual(errors, []);
|
|
330
|
+
console.log('RESULT ' + JSON.stringify(observations));
|
|
331
|
+
})().catch(error => { console.error(error.stack); process.exitCode = 1; }).finally(() => {
|
|
332
|
+
if (vm) vm.$destroy();
|
|
333
|
+
dom.window.close();
|
|
334
|
+
setImmediate(() => process.exit(process.exitCode || 0));
|
|
335
|
+
});
|