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,262 @@
|
|
|
1
|
+
// Real Chrome Element renderer popup/edit boundaries, old and new isolated pages.
|
|
2
|
+
// No mocked emissions or private resize handler calls.
|
|
3
|
+
// Run with UX_GRID_TEST_DEPENDENCIES pointing to an installed legacy consumer.
|
|
4
|
+
// No dependencies downloaded; creates a temporary bundle and isolated Chrome profile.
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const http = require('http');
|
|
9
|
+
const { spawn } = require('child_process');
|
|
10
|
+
const webpack = require('webpack');
|
|
11
|
+
const WebSocket = require('ws');
|
|
12
|
+
const VueLoaderPlugin = require('vue-loader/lib/plugin');
|
|
13
|
+
const root = path.resolve(__dirname, '../../..');
|
|
14
|
+
const consumer = process.env.UX_GRID_TEST_DEPENDENCIES;
|
|
15
|
+
if (!consumer) throw Error('Set UX_GRID_TEST_DEPENDENCIES to legacy consumer project');
|
|
16
|
+
const output = fs.mkdtempSync(path.join(os.tmpdir(), 'ux-grid-renderer-browser-'));
|
|
17
|
+
const chromePath = process.env.UX_GRID_TEST_CHROME || '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
|
|
18
|
+
let chrome, server, ws;
|
|
19
|
+
const results = {};
|
|
20
|
+
const persist = () => fs.writeFileSync(path.join(output, 'results.json'), JSON.stringify(results, null, 2));
|
|
21
|
+
const pending = new Map();
|
|
22
|
+
let id = 0;
|
|
23
|
+
function call(method, params = {}, sessionId) {
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
const requestId = ++id;
|
|
26
|
+
const timer = setTimeout(() => { pending.delete(requestId); reject(Error('CDP timeout: ' + method)); }, 60000);
|
|
27
|
+
pending.set(requestId, { resolve: result => { clearTimeout(timer); resolve(result); }, reject: error => { clearTimeout(timer); reject(error); } });
|
|
28
|
+
ws.send(JSON.stringify({ id: requestId, method, params, sessionId }));
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
(async () => {
|
|
32
|
+
const config = {
|
|
33
|
+
mode: 'development', devtool: false, entry: path.join(__dirname, 'browser-renderer-entry.js'),
|
|
34
|
+
output: { path: output, filename: 'bundle.js' },
|
|
35
|
+
resolve: { extensions: ['.js', '.vue', '.json'], alias: {
|
|
36
|
+
'vue$': require.resolve('vue/dist/vue.runtime.esm.js'),
|
|
37
|
+
'element-ui': path.join(consumer, 'node_modules/element-ui'),
|
|
38
|
+
'umy-table': path.join(consumer, 'node_modules/umy-table'),
|
|
39
|
+
'umy-ui': root
|
|
40
|
+
}, modules: [path.join(root, 'node_modules'), path.join(consumer, 'node_modules')] },
|
|
41
|
+
module: { rules: [
|
|
42
|
+
{ test: /\.vue$/, loader: 'vue-loader' },
|
|
43
|
+
{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' },
|
|
44
|
+
{ test: /\.css$/, use: ['style-loader', 'css-loader'] },
|
|
45
|
+
{ test: /\.(woff|ttf)$/, loader: 'file-loader' }
|
|
46
|
+
] }, plugins: [new VueLoaderPlugin(), new webpack.NormalModuleReplacementPlugin(/^xe-utils(?:\/.*)?$/, resource => {
|
|
47
|
+
if (resource.context.includes(path.join(consumer, 'node_modules/umy-table'))) resource.request = require.resolve(resource.request, { paths: [consumer] });
|
|
48
|
+
})], performance: { hints: false }
|
|
49
|
+
};
|
|
50
|
+
await new Promise((resolve, reject) => webpack(config, (error, stats) => error || stats.hasErrors() ? reject(error || Error(stats.toString({ all: false, errors: true }))) : resolve()));
|
|
51
|
+
server = http.createServer((request, response) => {
|
|
52
|
+
const asset = path.join(output, path.basename(request.url.split('?')[0]));
|
|
53
|
+
if (fs.existsSync(asset) && fs.statSync(asset).isFile()) { response.setHeader('Content-Type', request.url.endsWith('.js') ? 'application/javascript; charset=utf-8' : 'application/octet-stream'); fs.createReadStream(asset).pipe(response); }
|
|
54
|
+
else { response.setHeader('Content-Type', 'text/html; charset=utf-8'); response.end('<!doctype html><html><head><meta charset="utf-8"></head><body><script>window.bootErrors=[];window.onerror=function(m,u,l,c,e){bootErrors.push({message:m,url:u,line:l,stack:e&&e.stack})}</script><script src="/' + (request.url.includes('baseline=1') ? 'baseline.js' : 'bundle.js') + '"></script></body></html>'); }
|
|
55
|
+
});
|
|
56
|
+
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
|
|
57
|
+
const endpoint = await new Promise((resolve, reject) => {
|
|
58
|
+
chrome = spawn(chromePath, ['--headless=new', '--disable-gpu', '--no-first-run', '--no-default-browser-check', '--remote-debugging-port=0', '--user-data-dir=' + path.join(output, 'profile'), 'about:blank']);
|
|
59
|
+
chrome.on('error', reject);
|
|
60
|
+
chrome.on('exit', code => reject(Error('Chrome exited: ' + code)));
|
|
61
|
+
chrome.stderr.on('data', data => { const match = String(data).match(/DevTools listening on (ws:\/\/[^\s]+)/); if (match) resolve(match[1]); });
|
|
62
|
+
});
|
|
63
|
+
ws = new WebSocket(endpoint);
|
|
64
|
+
await new Promise(resolve => ws.on('open', resolve));
|
|
65
|
+
ws.on('message', message => {
|
|
66
|
+
const data = JSON.parse(message);
|
|
67
|
+
if (pending.has(data.id)) { const entry = pending.get(data.id); pending.delete(data.id); data.error ? entry.reject(Error(JSON.stringify(data.error))) : entry.resolve(data.result); }
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const assert = require('assert');
|
|
71
|
+
for (const version of ['legacy', 'current'].filter(value => !process.env.UX_GRID_TEST_VERSION || process.env.UX_GRID_TEST_VERSION === value)) {
|
|
72
|
+
const {targetId} = await call('Target.createTarget', {url:'about:blank'});
|
|
73
|
+
const {sessionId} = await call('Target.attachToTarget', {targetId,flatten:true});
|
|
74
|
+
await call('Emulation.setDeviceMetricsOverride',{width:1200,height:800,deviceScaleFactor:1,mobile:false},sessionId);
|
|
75
|
+
const evaluate = async expression => {
|
|
76
|
+
const result=await call('Runtime.evaluate',{expression,awaitPromise:true,returnByValue:true},sessionId);
|
|
77
|
+
if(result.exceptionDetails) throw Error(JSON.stringify(result.exceptionDetails));
|
|
78
|
+
return result.result.value;
|
|
79
|
+
};
|
|
80
|
+
const pause = () => evaluate('new Promise(resolve=>setTimeout(resolve,250))');
|
|
81
|
+
const click = async (selector,text,index=0,child) => {
|
|
82
|
+
const point=await evaluate('rendererTest.point('+JSON.stringify(selector)+','+(text===undefined?'undefined':JSON.stringify(text))+','+index+','+JSON.stringify(child)+')');
|
|
83
|
+
await call('Input.dispatchMouseEvent',{type:'mouseMoved',...point},sessionId);
|
|
84
|
+
await call('Input.dispatchMouseEvent',{type:'mousePressed',...point,button:'left',clickCount:1},sessionId);
|
|
85
|
+
await call('Input.dispatchMouseEvent',{type:'mouseReleased',...point,button:'left',clickCount:1},sessionId);
|
|
86
|
+
await pause();
|
|
87
|
+
};
|
|
88
|
+
const outside = async () => {
|
|
89
|
+
await call('Input.dispatchMouseEvent',{type:'mousePressed',x:1100,y:650,button:'left',clickCount:1},sessionId);
|
|
90
|
+
await call('Input.dispatchMouseEvent',{type:'mouseReleased',x:1100,y:650,button:'left',clickCount:1},sessionId);
|
|
91
|
+
await pause();
|
|
92
|
+
};
|
|
93
|
+
await call('Page.navigate',{url:'http://127.0.0.1:'+server.address().port+'/?legacy='+Number(version==='legacy')},sessionId);
|
|
94
|
+
for(let i=0;i<100;i++){if(await evaluate('Boolean(window.rendererTest)'))break;await new Promise(r=>setTimeout(r,100));}
|
|
95
|
+
results[version]={};
|
|
96
|
+
for(const kind of ['select','remote','multiple','cascader','cascaderMultiple','cascaderLazy','date','dateObject','datetime','dateRange','time','timeObject','timeAmPm','timeRange'].filter(value=>!process.env.UX_GRID_TEST_CASE||value===process.env.UX_GRID_TEST_CASE)){
|
|
97
|
+
const suite=results[version][kind]={};
|
|
98
|
+
suite.initial=await evaluate('rendererTest.mount('+JSON.stringify(kind)+','+(process.env.UX_GRID_TEST_MODERN !== '0')+')');
|
|
99
|
+
const popup=kind==='select'||kind==='remote'||kind==='multiple'?'.el-select-dropdown':kind.startsWith('cascader')?'.el-cascader__dropdown':kind.startsWith('date')?'.el-picker-panel':'.el-time-panel';
|
|
100
|
+
if(!await evaluate('rendererTest.hasVisible('+JSON.stringify(popup)+')')) await click(kind.endsWith('Range')?'tbody .el-date-editor':'tbody .el-input__inner');
|
|
101
|
+
if(kind==='remote') {
|
|
102
|
+
await call('Input.insertText',{text:'bet'},sessionId);
|
|
103
|
+
await evaluate('new Promise(resolve=>setTimeout(resolve,500))');
|
|
104
|
+
suite.queried=await evaluate('rendererTest.state()');persist();
|
|
105
|
+
}
|
|
106
|
+
await evaluate('rendererTest.resetCallbacks()');
|
|
107
|
+
if(kind==='select'||kind==='remote') await click('.el-select-dropdown__item','Beta');
|
|
108
|
+
if(kind==='multiple'){
|
|
109
|
+
await click('.el-select-dropdown__item','Zero');
|
|
110
|
+
await click('.el-select-dropdown__item','False');
|
|
111
|
+
const before=await evaluate('rendererTest.state()');
|
|
112
|
+
suite.beforeDisabled=before;
|
|
113
|
+
assert(before.focus.select,'multiple selection retains real input focus before disabled option click');
|
|
114
|
+
await click('.el-select-dropdown__item','Disabled');
|
|
115
|
+
const after=await evaluate('rendererTest.state()');
|
|
116
|
+
assert.deepStrictEqual(after.callbacks,before.callbacks,'disabled option is inert');
|
|
117
|
+
}
|
|
118
|
+
if(kind==='cascader'||kind==='cascaderLazy'){await click('.el-cascader-node__label','Parent');await click('.el-cascader-node__label','Child');}
|
|
119
|
+
if(kind==='cascaderMultiple'){
|
|
120
|
+
await click('.el-cascader-node__label','Parent');
|
|
121
|
+
await click('.el-cascader-node','Child',0,'.el-checkbox__input');
|
|
122
|
+
await click('.el-cascader-node','Other',0,'.el-checkbox__input');
|
|
123
|
+
}
|
|
124
|
+
if(kind==='date'||kind==='dateObject') await click('.el-date-table td.available:not(.prev-month):not(.next-month) span','19');
|
|
125
|
+
if(kind==='datetime'){
|
|
126
|
+
await click('.el-date-table td.available:not(.prev-month):not(.next-month) span','19');
|
|
127
|
+
await click('.el-picker-panel__footer button.is-plain');
|
|
128
|
+
}
|
|
129
|
+
if(kind==='dateRange'){
|
|
130
|
+
await click('.el-date-range-picker__content.is-left td.available:not(.prev-month):not(.next-month) span','20');
|
|
131
|
+
await click('.el-date-range-picker__content.is-left td.available:not(.prev-month):not(.next-month) span','22');
|
|
132
|
+
}
|
|
133
|
+
if(kind==='time'||kind==='timeObject'){
|
|
134
|
+
await click('.el-time-spinner__wrapper:first-child .el-time-spinner__item','14');
|
|
135
|
+
await click('.el-time-panel__btn.confirm');
|
|
136
|
+
}
|
|
137
|
+
if(kind==='timeAmPm'){
|
|
138
|
+
await click('.el-time-spinner__wrapper:first-child .el-time-spinner__item','02 PM');
|
|
139
|
+
await click('.el-time-panel__btn.confirm');
|
|
140
|
+
}
|
|
141
|
+
if(kind==='timeRange'){
|
|
142
|
+
await click('.el-time-range-picker__cell:first-child .el-time-spinner__wrapper:first-child .el-time-spinner__item','12');
|
|
143
|
+
await click('.el-time-panel__btn.confirm');
|
|
144
|
+
}
|
|
145
|
+
suite.selected=await evaluate('rendererTest.state()');persist();
|
|
146
|
+
const expected={select:'b',remote:'b',multiple:[0,false],cascader:['p','c'],cascaderLazy:['p','c'],cascaderMultiple:[2,3],date:'2026-09-19',datetime:'2026-09-19 13:24:56',dateRange:['2026-09-20','2026-09-22'],time:'14:24:56',timeAmPm:'02:24:56 PM',timeRange:['12:24:56','14:25:57']}[kind];
|
|
147
|
+
if(kind==='dateObject') {
|
|
148
|
+
assert.equal(suite.selected.valueType,'[object Date]');
|
|
149
|
+
assert.deepStrictEqual(suite.selected.dateParts,[2026,8,19,0,0,0]);
|
|
150
|
+
} else if(kind==='timeObject') {
|
|
151
|
+
assert.equal(suite.selected.valueType,'[object Date]');
|
|
152
|
+
assert.deepStrictEqual(suite.selected.dateParts.slice(3),[14,24,56]);
|
|
153
|
+
} else assert.deepStrictEqual(suite.selected.value,expected,version+' '+kind+' model');
|
|
154
|
+
assert(suite.selected.active,version+' '+kind+' popup click keeps edit active');
|
|
155
|
+
assert.equal(suite.selected.callbacks.filter(e=>e.type==='change').length,kind==='multiple'||kind==='cascaderMultiple'?2:1,'one change per chosen value');
|
|
156
|
+
assert.equal(suite.selected.validationCalls.length,kind==='multiple'||kind==='cascaderMultiple'?2:1,'one actual change validator per chosen value');
|
|
157
|
+
assert(suite.selected.validationCalls.every(item=>item.sameRow),'change validator receives original row');
|
|
158
|
+
assert(suite.selected.callbacks.filter(e=>e.sameRow!==undefined).every(e=>e.sameRow),'renderer callbacks retain row identity');
|
|
159
|
+
if(kind==='remote') {
|
|
160
|
+
assert(suite.selected.remoteQueries.some(query=>query.includes('bet')),'actual text input invokes remoteMethod');
|
|
161
|
+
await evaluate('rendererTest.clearRemoteOptions()');
|
|
162
|
+
}
|
|
163
|
+
if(kind==='cascaderLazy') assert(suite.selected.lazyCalls>0,'real cascader invokes asynchronous lazyLoad');
|
|
164
|
+
await outside();
|
|
165
|
+
suite.display=await evaluate('rendererTest.state()'); persist();
|
|
166
|
+
assert(!suite.display.active,'outside popup closes edit and renders label');
|
|
167
|
+
if(version==='current') {
|
|
168
|
+
const label={select:'Beta',remote:'Beta',multiple:'Zero, False',cascader:'Parent / Child',cascaderLazy:'Parent / Child',cascaderMultiple:'Parent / Child, Parent / Other',date:'2026-09-19',dateObject:'2026-09-19',datetime:'2026-09-19 13:24:56',dateRange:'2026-09-20 - 2026-09-22',time:'14:24:56',timeObject:'14:24:56',timeAmPm:'02:24:56 PM',timeRange:'12:24:56 - 14:25:57'}[kind];
|
|
169
|
+
assert(suite.display.text.includes(label),kind+' closed display retains correct label/time format');
|
|
170
|
+
}
|
|
171
|
+
if(kind==='cascaderLazy' && version==='legacy') {
|
|
172
|
+
assert.deepStrictEqual(suite.display.errors,[]);
|
|
173
|
+
console.log(version+' '+kind+' selection/display passed');
|
|
174
|
+
continue; // Legacy has no remount cache; current must verify its added restoration.
|
|
175
|
+
}
|
|
176
|
+
await evaluate('rendererTest.activate()');
|
|
177
|
+
if(kind==='cascaderLazy') {
|
|
178
|
+
const restored = await evaluate("document.querySelector('tbody .el-cascader input').value");
|
|
179
|
+
assert.equal(restored,'Parent / Child','lazy editor restores selected path on remount');
|
|
180
|
+
}
|
|
181
|
+
if(kind==='multiple') {
|
|
182
|
+
await evaluate('rendererTest.resetCallbacks()');
|
|
183
|
+
await click('tbody .el-tag__close');
|
|
184
|
+
suite.removed=await evaluate('rendererTest.state()');persist();
|
|
185
|
+
assert.deepStrictEqual(suite.removed.value,[false],'remove first selected tag');
|
|
186
|
+
assert.equal(suite.removed.callbacks.filter(e=>e.type==='change').length,1);
|
|
187
|
+
}
|
|
188
|
+
const input=await evaluate('rendererTest.point('+JSON.stringify(kind==='multiple'||kind==='cascaderMultiple'?'tbody .el-input__suffix':kind.endsWith('Range')?'tbody .el-date-editor':'tbody .el-input__inner')+')');
|
|
189
|
+
await call('Input.dispatchMouseEvent',{type:'mouseMoved',...input},sessionId);await pause();
|
|
190
|
+
await evaluate('rendererTest.resetCallbacks()');
|
|
191
|
+
await click('tbody .el-icon-circle-close');
|
|
192
|
+
suite.cleared=await evaluate('rendererTest.state()');persist();
|
|
193
|
+
const empty=kind==='multiple'||kind.startsWith('cascader')?[]:kind==='select'||kind==='remote'?'':null;
|
|
194
|
+
assert.deepStrictEqual(suite.cleared.value,empty,kind+' clears model');
|
|
195
|
+
assert(suite.cleared.active,kind+' clear preserves edit');
|
|
196
|
+
assert.equal(suite.cleared.callbacks.filter(e=>e.type==='change').length,1);
|
|
197
|
+
assert.equal(suite.cleared.validationCalls.length,1,'clear runs actual change validator');
|
|
198
|
+
assert.equal(suite.cleared.callbacks.filter(e=>e.type==='clear').length,kind==='select'||kind==='remote'||kind==='multiple'?1:0);
|
|
199
|
+
if(kind==='select') {
|
|
200
|
+
await evaluate('rendererTest.resetCallbacks()');
|
|
201
|
+
if(!await evaluate("rendererTest.hasVisible('.el-select-dropdown')")) await click('tbody .el-input__inner');
|
|
202
|
+
await click('.el-select-dropdown__item','Alpha');
|
|
203
|
+
suite.reselected=await evaluate('rendererTest.state()');persist();
|
|
204
|
+
assert.equal(suite.reselected.value,'a'); assert(suite.reselected.active);
|
|
205
|
+
assert.equal(suite.reselected.callbacks.filter(e=>e.type==='change').length,1);
|
|
206
|
+
assert.equal(suite.reselected.validationCalls.length,1);
|
|
207
|
+
}
|
|
208
|
+
if(kind==='cascaderLazy') {
|
|
209
|
+
await evaluate('rendererTest.resetCallbacks()');
|
|
210
|
+
if(!await evaluate("rendererTest.hasVisible('.el-cascader__dropdown')")) await click('tbody .el-input__inner');
|
|
211
|
+
await click('.el-cascader-node__label','Parent');
|
|
212
|
+
await click('.el-cascader-node__label','Child');
|
|
213
|
+
suite.reselected=await evaluate('rendererTest.state()');persist();
|
|
214
|
+
assert.deepStrictEqual(suite.reselected.value,['p','c']);
|
|
215
|
+
assert(suite.reselected.active);
|
|
216
|
+
assert.equal(suite.reselected.callbacks.filter(e=>e.type==='change').length,1);
|
|
217
|
+
assert.equal(suite.reselected.validationCalls.length,1);
|
|
218
|
+
assert.equal(suite.reselected.lazyCalls,suite.selected.lazyCalls,'remount reuses loaded children without refetch');
|
|
219
|
+
}
|
|
220
|
+
await outside();
|
|
221
|
+
suite.outside=await evaluate('rendererTest.state()');persist();
|
|
222
|
+
assert(!suite.outside.active,kind+' outside click closes edit');
|
|
223
|
+
assert.equal(suite.outside.callbacks.filter(e=>e.type==='edit-closed').length,1);
|
|
224
|
+
if(version==='current' && process.env.UX_GRID_TEST_MODERN !== '0') assert(suite.outside.modernCalls > 0,'modern business interceptor remains reachable on outside click');
|
|
225
|
+
assert.deepStrictEqual(suite.outside.errors,[]);
|
|
226
|
+
console.log(version+' '+kind+' passed');
|
|
227
|
+
}
|
|
228
|
+
if(!process.env.UX_GRID_TEST_CASE || process.env.UX_GRID_TEST_CASE==='filter') {
|
|
229
|
+
const suite=results[version].filter={};
|
|
230
|
+
await evaluate('rendererTest.mountFilter()');
|
|
231
|
+
await click('thead .elx-filter--btn,thead .vxe-filter--btn');
|
|
232
|
+
await click('.elx-table--filter-wrapper .el-input__inner,.vxe-table--filter-wrapper .el-input__inner');
|
|
233
|
+
await click('.el-select-dropdown__item','Beta');
|
|
234
|
+
suite.selected=await evaluate('rendererTest.filterState()');persist();
|
|
235
|
+
assert(suite.selected.open,'dropdown click keeps filter panel open');
|
|
236
|
+
assert.equal(suite.selected.callbacks.filter(e=>e.type==='change').length,1);
|
|
237
|
+
await click('.elx-table--filter-footer button:first-child,.vxe-table--filter-footer button:first-child');
|
|
238
|
+
suite.confirmed=await evaluate('rendererTest.filterState()');persist();
|
|
239
|
+
assert.deepStrictEqual(suite.confirmed.rows,[2]);
|
|
240
|
+
assert.deepStrictEqual(suite.confirmed.callbacks.find(e=>e.type==='filter-change').datas,['b']);
|
|
241
|
+
await click('thead .elx-filter--btn,thead .vxe-filter--btn');
|
|
242
|
+
const point=await evaluate("rendererTest.point('.elx-table--filter-wrapper .el-input__inner,.vxe-table--filter-wrapper .el-input__inner')");
|
|
243
|
+
await call('Input.dispatchMouseEvent',{type:'mouseMoved',...point},sessionId);await pause();
|
|
244
|
+
await click('.elx-table--filter-wrapper .el-icon-circle-close,.vxe-table--filter-wrapper .el-icon-circle-close');
|
|
245
|
+
suite.cleared=await evaluate('rendererTest.filterState()');persist();
|
|
246
|
+
if(version==='current') {
|
|
247
|
+
assert.equal(suite.cleared.options[0].pendingChecked,false,'clear itself cancels pending filter selection');
|
|
248
|
+
await click('.vxe-table--filter-footer button:first-child');
|
|
249
|
+
} else await click('.elx-table--filter-footer button:last-child');
|
|
250
|
+
suite.reset=await evaluate('rendererTest.filterState()');persist();
|
|
251
|
+
assert.deepStrictEqual(suite.reset.rows,[1,2]);
|
|
252
|
+
assert.equal(suite.reset.callbacks.filter(e=>e.type==='filter-change').length,2);
|
|
253
|
+
assert.deepStrictEqual(suite.reset.errors,[]);
|
|
254
|
+
console.log(version+' filter passed');
|
|
255
|
+
}
|
|
256
|
+
await call('Target.closeTarget',{targetId});
|
|
257
|
+
}
|
|
258
|
+
console.log('Result directory: '+output);persist();
|
|
259
|
+
})().catch(error=>{results.failure=error.stack||String(error);persist();console.error(error);console.error('Result directory: '+output);process.exitCode=1;}).finally(()=>{
|
|
260
|
+
if(ws)ws.close();if(chrome)chrome.kill();if(server)server.close();
|
|
261
|
+
setTimeout(()=>process.exit(process.exitCode||0),500);
|
|
262
|
+
});
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/* Actual Element popup controls exercised by browser-renderer-contracts.cjs. */
|
|
2
|
+
import 'babel-polyfill';
|
|
3
|
+
import Vue from 'vue';
|
|
4
|
+
import Element from 'element-ui';
|
|
5
|
+
import 'element-ui/lib/theme-chalk/index.css';
|
|
6
|
+
import { interceptor } from 'vxe-table/lib/ui';
|
|
7
|
+
Vue.use(Element);
|
|
8
|
+
Vue.config.productionTip = false;
|
|
9
|
+
window.testErrors = [];
|
|
10
|
+
Vue.config.errorHandler = error => window.testErrors.push(error.stack || String(error));
|
|
11
|
+
const legacy = new URLSearchParams(location.search).get('legacy') === '1';
|
|
12
|
+
let Grid;
|
|
13
|
+
if (legacy) {
|
|
14
|
+
const old = require('umy-table/lib/umy-table.common.js');
|
|
15
|
+
Vue.use(old.default || old);
|
|
16
|
+
Grid = Vue.options.components.UxGrid;
|
|
17
|
+
require('umy-table/lib/theme-chalk/index.css');
|
|
18
|
+
} else {
|
|
19
|
+
Grid = require('../src/ux-grid.vue').default;
|
|
20
|
+
require('../../../lib/theme-chalk/ux-grid.css');
|
|
21
|
+
}
|
|
22
|
+
const delay = (ms = 120) => new Promise(resolve => setTimeout(resolve, ms));
|
|
23
|
+
let vm;
|
|
24
|
+
let callbacks;
|
|
25
|
+
let modernCalls = 0;
|
|
26
|
+
let lazyCalls = 0;
|
|
27
|
+
let remoteQueries = [];
|
|
28
|
+
let validationCalls = [];
|
|
29
|
+
let currentRenderOpts;
|
|
30
|
+
const modern = () => { modernCalls++; };
|
|
31
|
+
const definitions = {
|
|
32
|
+
select: { value: 'a', name: 'ElSelect', props: { clearable: true }, options: [{ value: 'a', label: 'Alpha' }, { value: 'b', label: 'Beta' }] },
|
|
33
|
+
remote: { value: 'a', name: 'ElSelect', props: { clearable: true, filterable: true, remote: true }, options: [{ value: 'a', label: 'Alpha' }] },
|
|
34
|
+
multiple: { value: [], name: 'ElSelect', props: { multiple: true, clearable: true }, optionProps: { value: 'id', label: 'text' }, optionGroupProps: { label: 'title', options: 'items' }, optionGroups: [{ title: 'Values', items: [{ id: 0, text: 'Zero' }, { id: false, text: 'False' }, { id: 2, text: 'Disabled', disabled: true }] }] },
|
|
35
|
+
cascader: { value: [], name: 'ElCascader', props: { clearable: true, options: [{ value: 'p', label: 'Parent', children: [{ value: 'c', label: 'Child' }, { value: 'o', label: 'Other' }] }] } },
|
|
36
|
+
cascaderMultiple: { value: [], name: 'ElCascader', props: { clearable: true, props: { multiple: true, emitPath: false, value: 'id', label: 'text', children: 'nodes' }, options: [{ id: 1, text: 'Parent', nodes: [{ id: 2, text: 'Child' }, { id: 3, text: 'Other' }] }] } },
|
|
37
|
+
cascaderLazy: { value: [], name: 'ElCascader', props: { clearable: true, props: { lazy: true, lazyLoad(node, resolve) { lazyCalls++; setTimeout(() => resolve(node.level ? [{ value: 'c', label: 'Child', leaf: true }] : [{ value: 'p', label: 'Parent', leaf: false }]), 20); } }, options: [{ value: 'p', label: 'Parent', leaf: false }] } },
|
|
38
|
+
date: { value: '2026-09-17', name: 'ElDatePicker', props: { type: 'date', valueFormat: 'yyyy-MM-dd', clearable: true } },
|
|
39
|
+
dateObject: { value: new Date(2026, 8, 17), name: 'ElDatePicker', props: { type: 'date', clearable: true } },
|
|
40
|
+
dateRange: { value: ['2026-09-17', '2026-09-19'], name: 'ElDatePicker', props: { type: 'daterange', valueFormat: 'yyyy-MM-dd', clearable: true } },
|
|
41
|
+
datetime: { value: '2026-09-17 13:24:56', name: 'ElDatePicker', props: { type: 'datetime', valueFormat: 'yyyy-MM-dd HH:mm:ss', format: 'yyyy-MM-dd HH:mm:ss', clearable: true } },
|
|
42
|
+
time: { value: '13:24:56', name: 'ElTimePicker', props: { valueFormat: 'HH:mm:ss', format: 'HH:mm:ss', clearable: true } },
|
|
43
|
+
timeObject: { value: new Date(2026, 8, 17, 13, 24, 56), name: 'ElTimePicker', props: { format: 'HH:mm:ss', clearable: true } },
|
|
44
|
+
timeAmPm: { value: '01:24:56 PM', name: 'ElTimePicker', props: { valueFormat: 'hh:mm:ss A', format: 'hh:mm:ss A', clearable: true } },
|
|
45
|
+
timeRange: { value: ['13:24:56', '14:25:57'], name: 'ElTimePicker', props: { isRange: true, valueFormat: 'HH:mm:ss', format: 'HH:mm:ss', clearable: true } }
|
|
46
|
+
};
|
|
47
|
+
function visible(element) {
|
|
48
|
+
const rect = element.getBoundingClientRect();
|
|
49
|
+
return rect.width > 0 && rect.height > 0 && getComputedStyle(element).visibility !== 'hidden' && getComputedStyle(element).display !== 'none';
|
|
50
|
+
}
|
|
51
|
+
window.rendererTest = {
|
|
52
|
+
async mount(kind, withModern = false) {
|
|
53
|
+
if (vm) { vm.$destroy(); vm.$el.remove(); }
|
|
54
|
+
interceptor.delete('event.clearEdit', modern);
|
|
55
|
+
if (withModern && !legacy) interceptor.add('event.clearEdit', modern);
|
|
56
|
+
modernCalls = 0; lazyCalls = 0; remoteQueries = []; callbacks = []; validationCalls = [];
|
|
57
|
+
const definition = definitions[kind];
|
|
58
|
+
const row = { id: 1, value: Array.isArray(definition.value) ? definition.value.slice() : definition.value };
|
|
59
|
+
const renderOpts = Object.assign({}, definition, { events: {
|
|
60
|
+
input(params, value) { callbacks.push({ type: 'input', value, sameRow: params.row === row, current: row.value }); },
|
|
61
|
+
change(params, value) { callbacks.push({ type: 'change', value, sameRow: params.row === row, current: row.value }); },
|
|
62
|
+
clear(params) { callbacks.push({ type: 'clear', sameRow: params.row === row }); }
|
|
63
|
+
} });
|
|
64
|
+
delete renderOpts.value;
|
|
65
|
+
if (kind === 'remote') {
|
|
66
|
+
renderOpts.options = definition.options.slice();
|
|
67
|
+
renderOpts.props = Object.assign({}, definition.props, { remoteMethod(query) {
|
|
68
|
+
remoteQueries.push(query);
|
|
69
|
+
setTimeout(() => {
|
|
70
|
+
renderOpts.options.splice(0, renderOpts.options.length, ...(query ? [{ value: 'b', label: 'Beta' }] : []));
|
|
71
|
+
}, 20);
|
|
72
|
+
} });
|
|
73
|
+
}
|
|
74
|
+
Vue.observable(renderOpts);
|
|
75
|
+
currentRenderOpts = renderOpts;
|
|
76
|
+
vm = new Vue({ render(h) { return h(Grid, { ref: 'grid', props: {
|
|
77
|
+
height: 260, rowId: 'id', data: [row], editConfig: { mode: 'cell', trigger: 'click' },
|
|
78
|
+
rules: { value: [{ trigger: 'change', validator(params) { validationCalls.push({ value: params.cellValue, sameRow: params.row === row }); } }] },
|
|
79
|
+
columns: [{ field: 'value', title: kind, width: 380, editRender: renderOpts }, { field: 'id', title: 'ID', width: 100 }]
|
|
80
|
+
} }); } }).$mount();
|
|
81
|
+
document.body.appendChild(vm.$el); await delay(300);
|
|
82
|
+
this.wrapper = vm.$refs.grid; this.kernel = this.wrapper.tableExample(); this.row = row;
|
|
83
|
+
this.wrapper.$on('edit-closed', () => callbacks.push({ type: 'edit-closed' }));
|
|
84
|
+
this.wrapper.setActiveCell(row, 'value'); await delay(160);
|
|
85
|
+
return this.state();
|
|
86
|
+
},
|
|
87
|
+
point(selector, text, index = 0, child) {
|
|
88
|
+
const elements = Array.from(document.querySelectorAll(selector)).filter(element => visible(element) && (text === undefined || element.textContent.trim() === text));
|
|
89
|
+
const matched = elements[index];
|
|
90
|
+
const element = child && matched ? matched.querySelector(child) : matched;
|
|
91
|
+
if (!element) throw Error('No visible control: ' + selector + ' text=' + text + ' index=' + index);
|
|
92
|
+
const rect = element.getBoundingClientRect();
|
|
93
|
+
return { x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 };
|
|
94
|
+
},
|
|
95
|
+
hasVisible(selector) { return Array.from(document.querySelectorAll(selector)).some(visible); },
|
|
96
|
+
async state() {
|
|
97
|
+
await delay();
|
|
98
|
+
const value = this.row.value;
|
|
99
|
+
const dateParts = value instanceof Date ? [value.getFullYear(), value.getMonth(), value.getDate(), value.getHours(), value.getMinutes(), value.getSeconds()] : null;
|
|
100
|
+
const focused = document.activeElement;
|
|
101
|
+
return { value, dateParts, valueType: Object.prototype.toString.call(value), active: this.wrapper.isActiveByRow(this.row), callbacks: callbacks.slice(), validationCalls: validationCalls.slice(), focus: { tag: focused && focused.tagName, select: !!(focused && focused.closest && focused.closest('.el-select')) }, modernCalls, lazyCalls, remoteQueries: remoteQueries.slice(), text: vm.$el.textContent, errors: window.testErrors.slice() };
|
|
102
|
+
},
|
|
103
|
+
resetCallbacks() { callbacks.length = 0; validationCalls.length = 0; },
|
|
104
|
+
async activate() { this.wrapper.setActiveCell(this.row, 'value'); await delay(160); },
|
|
105
|
+
async clearRemoteOptions() { currentRenderOpts.options.splice(0); await delay(); },
|
|
106
|
+
async mountFilter() {
|
|
107
|
+
if (vm) { vm.$destroy(); vm.$el.remove(); }
|
|
108
|
+
callbacks = [];
|
|
109
|
+
const rows = [{ id: 1, value: 'a' }, { id: 2, value: 'b' }];
|
|
110
|
+
vm = new Vue({ render(h) { return h(Grid, { ref: 'grid', props: {
|
|
111
|
+
height: 260, data: rows,
|
|
112
|
+
columns: [{ field: 'value', title: 'Filter', width: 380, filters: [{ value: 'picked', data: null }], filterRender: {
|
|
113
|
+
name: 'ElSelect', props: { clearable: true }, options: [{ value: 'a', label: 'Alpha' }, { value: 'b', label: 'Beta' }],
|
|
114
|
+
events: { input(params, value) { callbacks.push({ type: 'input', value }); }, change(params, value) { callbacks.push({ type: 'change', value }); } }
|
|
115
|
+
} }, { field: 'id', width: 100 }]
|
|
116
|
+
} }); } }).$mount();
|
|
117
|
+
document.body.appendChild(vm.$el); await delay(300);
|
|
118
|
+
this.wrapper = vm.$refs.grid; this.kernel = this.wrapper.tableExample();
|
|
119
|
+
this.wrapper.$on('filter-change', payload => callbacks.push({ type: 'filter-change', values: payload.values, datas: payload.datas }));
|
|
120
|
+
},
|
|
121
|
+
async filterState() {
|
|
122
|
+
await delay();
|
|
123
|
+
return { rows: this.wrapper.getTableData().visibleData.map(row => row.id),
|
|
124
|
+
open: this.hasVisible('.elx-table--filter-wrapper,.vxe-table--filter-wrapper'),
|
|
125
|
+
options: this.wrapper.getColumnByField('value').filters.map(option=>({ data: option.data, checked: option.checked, pendingChecked: option._checked })),
|
|
126
|
+
callbacks: callbacks.slice(), errors: window.testErrors.slice() };
|
|
127
|
+
},
|
|
128
|
+
removeModern() { interceptor.delete('event.clearEdit', modern); }
|
|
129
|
+
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// E08 real Chrome pointer drag against old/new kernels, plain and fixed headers.
|
|
2
|
+
// No mocked emissions or private resize handler calls.
|
|
3
|
+
// Run with UX_GRID_TEST_DEPENDENCIES pointing to an installed legacy consumer.
|
|
4
|
+
// No dependencies downloaded; creates a temporary bundle and isolated Chrome profile.
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const http = require('http');
|
|
9
|
+
const { spawn } = require('child_process');
|
|
10
|
+
const webpack = require('webpack');
|
|
11
|
+
const WebSocket = require('ws');
|
|
12
|
+
const VueLoaderPlugin = require('vue-loader/lib/plugin');
|
|
13
|
+
const root = path.resolve(__dirname, '../../..');
|
|
14
|
+
const consumer = process.env.UX_GRID_TEST_DEPENDENCIES;
|
|
15
|
+
if (!consumer) throw Error('Set UX_GRID_TEST_DEPENDENCIES to legacy consumer project');
|
|
16
|
+
const output = fs.mkdtempSync(path.join(os.tmpdir(), 'ux-grid-browser-'));
|
|
17
|
+
const chromePath = process.env.UX_GRID_TEST_CHROME || '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
|
|
18
|
+
let chrome, server, ws;
|
|
19
|
+
const results = {};
|
|
20
|
+
const persist = () => fs.writeFileSync(path.join(output, 'results.json'), JSON.stringify(results, null, 2));
|
|
21
|
+
const pending = new Map();
|
|
22
|
+
let id = 0;
|
|
23
|
+
function call(method, params = {}, sessionId) {
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
const requestId = ++id;
|
|
26
|
+
const timer = setTimeout(() => { pending.delete(requestId); reject(Error('CDP timeout: ' + method)); }, 60000);
|
|
27
|
+
pending.set(requestId, { resolve: result => { clearTimeout(timer); resolve(result); }, reject: error => { clearTimeout(timer); reject(error); } });
|
|
28
|
+
ws.send(JSON.stringify({ id: requestId, method, params, sessionId }));
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
(async () => {
|
|
32
|
+
const config = {
|
|
33
|
+
mode: 'development', devtool: false, entry: path.join(__dirname, 'browser-entry.js'),
|
|
34
|
+
output: { path: output, filename: 'bundle.js' },
|
|
35
|
+
resolve: { extensions: ['.js', '.vue', '.json'], alias: {
|
|
36
|
+
'vue$': require.resolve('vue/dist/vue.runtime.esm.js'),
|
|
37
|
+
'element-ui': path.join(consumer, 'node_modules/element-ui'),
|
|
38
|
+
'umy-table': path.join(consumer, 'node_modules/umy-table'),
|
|
39
|
+
'umy-ui': root
|
|
40
|
+
}, modules: [path.join(root, 'node_modules'), path.join(consumer, 'node_modules')] },
|
|
41
|
+
module: { rules: [
|
|
42
|
+
{ test: /\.vue$/, loader: 'vue-loader' },
|
|
43
|
+
{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' },
|
|
44
|
+
{ test: /\.css$/, use: ['style-loader', 'css-loader'] },
|
|
45
|
+
{ test: /\.(woff|ttf)$/, loader: 'file-loader' }
|
|
46
|
+
] }, plugins: [new VueLoaderPlugin(), new webpack.NormalModuleReplacementPlugin(/^xe-utils(?:\/.*)?$/, resource => {
|
|
47
|
+
if (resource.context.includes(path.join(consumer, 'node_modules/umy-table'))) resource.request = require.resolve(resource.request, { paths: [consumer] });
|
|
48
|
+
})], performance: { hints: false }
|
|
49
|
+
};
|
|
50
|
+
await new Promise((resolve, reject) => webpack(config, (error, stats) => error || stats.hasErrors() ? reject(error || Error(stats.toString({ all: false, errors: true }))) : resolve()));
|
|
51
|
+
server = http.createServer((request, response) => {
|
|
52
|
+
const asset = path.join(output, path.basename(request.url.split('?')[0]));
|
|
53
|
+
if (fs.existsSync(asset) && fs.statSync(asset).isFile()) { response.setHeader('Content-Type', request.url.endsWith('.js') ? 'application/javascript; charset=utf-8' : 'application/octet-stream'); fs.createReadStream(asset).pipe(response); }
|
|
54
|
+
else { response.setHeader('Content-Type', 'text/html; charset=utf-8'); response.end('<!doctype html><html><head><meta charset="utf-8"></head><body><script>window.bootErrors=[];window.onerror=function(m,u,l,c,e){bootErrors.push({message:m,url:u,line:l,stack:e&&e.stack})}</script><script src="/' + (request.url.includes('baseline=1') ? 'baseline.js' : 'bundle.js') + '"></script></body></html>'); }
|
|
55
|
+
});
|
|
56
|
+
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
|
|
57
|
+
const endpoint = await new Promise((resolve, reject) => {
|
|
58
|
+
chrome = spawn(chromePath, ['--headless=new', '--disable-gpu', '--no-first-run', '--no-default-browser-check', '--remote-debugging-port=0', '--user-data-dir=' + path.join(output, 'profile'), 'about:blank']);
|
|
59
|
+
chrome.on('error', reject);
|
|
60
|
+
chrome.on('exit', code => reject(Error('Chrome exited: ' + code)));
|
|
61
|
+
chrome.stderr.on('data', data => { const match = String(data).match(/DevTools listening on (ws:\/\/[^\s]+)/); if (match) resolve(match[1]); });
|
|
62
|
+
});
|
|
63
|
+
ws = new WebSocket(endpoint);
|
|
64
|
+
await new Promise(resolve => ws.on('open', resolve));
|
|
65
|
+
ws.on('message', message => {
|
|
66
|
+
const data = JSON.parse(message);
|
|
67
|
+
if (pending.has(data.id)) { const entry = pending.get(data.id); pending.delete(data.id); data.error ? entry.reject(Error(JSON.stringify(data.error))) : entry.resolve(data.result); }
|
|
68
|
+
});
|
|
69
|
+
for (const version of ['legacy', 'current']) for (const fixed of [false, true]) {
|
|
70
|
+
const legacy = version === 'legacy';
|
|
71
|
+
const { targetId } = await call('Target.createTarget', { url: 'about:blank' });
|
|
72
|
+
const { sessionId } = await call('Target.attachToTarget', { targetId, flatten: true });
|
|
73
|
+
await call('Emulation.setDeviceMetricsOverride', { width: 1200, height: 800, deviceScaleFactor: 1, mobile: false }, sessionId);
|
|
74
|
+
const evaluate = async expression => {
|
|
75
|
+
const result = await call('Runtime.evaluate', { expression, awaitPromise: true, returnByValue: true }, sessionId);
|
|
76
|
+
if (result.exceptionDetails) throw Error(JSON.stringify(result.exceptionDetails));
|
|
77
|
+
return result.result.value;
|
|
78
|
+
};
|
|
79
|
+
await call('Page.navigate', { url: 'http://127.0.0.1:' + server.address().port + '/?legacy=' + Number(legacy) + '&baseline=' + Number(version === 'baseline') }, sessionId);
|
|
80
|
+
for (let attempt = 0; attempt < 100; attempt++) {
|
|
81
|
+
if (await evaluate('Boolean(window.acceptance)')) break;
|
|
82
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
83
|
+
}
|
|
84
|
+
const suite = results[version + (fixed ? ':fixed-left' : ':plain')] = {};
|
|
85
|
+
console.log('boot errors:', await evaluate('window.bootErrors'));
|
|
86
|
+
|
|
87
|
+
{
|
|
88
|
+
suite.mount = await evaluate('acceptance.mount(20)');
|
|
89
|
+
suite.prepare = await evaluate(`(async () => {
|
|
90
|
+
await wrapper.loadColumn([{field:'name', title:'Resize', width:200, resizable:true${fixed ? ", fixed:'left'" : ''}}, {field:'amount', width:${fixed ? 1600 : 160}}]);
|
|
91
|
+
await new Promise(r => setTimeout(r, 400));
|
|
92
|
+
window.resizeTrace = [];
|
|
93
|
+
window.resizePayloads = [];
|
|
94
|
+
window.resizeColumn = wrapper.getColumnByField('name');
|
|
95
|
+
document.addEventListener('mouseup', e => { window.resizeUp = e; resizeTrace.push('mouseup-capture'); }, true);
|
|
96
|
+
wrapper.$on('header-dragend', p => {
|
|
97
|
+
resizeTrace.push('header-dragend');
|
|
98
|
+
resizePayloads.push({ keys:Object.keys(p).sort(), field:p.column.field || p.column.property,
|
|
99
|
+
sameColumn:p.column === resizeColumn, sameEvent:p.$event === resizeUp,
|
|
100
|
+
eventType:p.$event && p.$event.type, columnIndex:p.columnIndex, $columnIndex:p.$columnIndex,
|
|
101
|
+
$rowIndex:p.$rowIndex, fixed:p.fixed, fixedType:typeof p.fixed, isHidden:p.isHidden, hiddenType:typeof p.isHidden, width:p.column.renderWidth });
|
|
102
|
+
});
|
|
103
|
+
const handle = document.querySelector(${JSON.stringify(fixed ? '.vxe-table--fixed-left-wrapper .vxe-cell--col-resizable, .elx-table--fixed-left-wrapper .elx-resizable' : '.vxe-cell--col-resizable, .elx-resizable')});
|
|
104
|
+
if (!handle) throw Error('Resize handle absent: ' + document.querySelector('thead').innerHTML);
|
|
105
|
+
const rect = handle.getBoundingClientRect();
|
|
106
|
+
return {x:rect.x+1, y:rect.y+rect.height/2, width:resizeColumn.renderWidth, handle:handle.className, target:document.elementFromPoint(rect.x+1,rect.y+rect.height/2).className};
|
|
107
|
+
})()`);
|
|
108
|
+
await call('Input.dispatchMouseEvent', {type:'mouseMoved', x:suite.prepare.x, y:suite.prepare.y}, sessionId);
|
|
109
|
+
await call('Input.dispatchMouseEvent', {type:'mousePressed', x:suite.prepare.x, y:suite.prepare.y, button:'left', clickCount:1}, sessionId);
|
|
110
|
+
for (const dx of [20,40,60]) await call('Input.dispatchMouseEvent', {type:'mouseMoved', x:suite.prepare.x+dx, y:suite.prepare.y, button:'left', buttons:1}, sessionId);
|
|
111
|
+
await call('Input.dispatchMouseEvent', {type:'mouseReleased', x:suite.prepare.x+60, y:suite.prepare.y, button:'left', clickCount:1}, sessionId);
|
|
112
|
+
suite.resize = await evaluate(`new Promise(resolve => setTimeout(() => resolve({trace:resizeTrace, events:resizePayloads, width:resizeColumn.renderWidth, errors:testErrors}),700))`);
|
|
113
|
+
const assert = require('assert');
|
|
114
|
+
assert.equal(suite.resize.events.length, 1);
|
|
115
|
+
const event = suite.resize.events[0];
|
|
116
|
+
assert(event.sameColumn && event.sameEvent);
|
|
117
|
+
assert.equal(event.eventType, 'mouseup');
|
|
118
|
+
if (fixed) { assert.equal(event.fixed, 'left'); assert.strictEqual(event.isHidden, false); }
|
|
119
|
+
else { assert(!event.fixed); assert(!event.isHidden); }
|
|
120
|
+
assert.equal(event.columnIndex, 0); assert.equal(event.$columnIndex, 0); assert.equal(event.$rowIndex, 0);
|
|
121
|
+
assert.deepStrictEqual(event.keys, ['$columnIndex','$event','$rowIndex','column','columnIndex','fixed','isHidden'].sort());
|
|
122
|
+
assert.equal(suite.resize.width, suite.prepare.width + 60);
|
|
123
|
+
assert.equal(event.width, suite.resize.width);
|
|
124
|
+
assert(suite.prepare.target.includes('resizable'), 'pointer hits actual resize handle');
|
|
125
|
+
assert.deepStrictEqual(suite.resize.trace, ['mouseup-capture','header-dragend']);
|
|
126
|
+
assert.deepStrictEqual(suite.resize.errors, []);
|
|
127
|
+
console.log(version + (fixed ? ':fixed-left' : ':plain') + ' resize: ' + JSON.stringify(suite));
|
|
128
|
+
persist();
|
|
129
|
+
await call('Target.closeTarget', {targetId});
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
}
|
|
134
|
+
for (const fixed of [false, true]) {
|
|
135
|
+
const suffix = fixed ? ':fixed-left' : ':plain';
|
|
136
|
+
const old = results['legacy' + suffix].resize;
|
|
137
|
+
const current = results['current' + suffix].resize;
|
|
138
|
+
const normalize = result => ({ ...result, events: result.events.map(event => ({
|
|
139
|
+
...event, fixed:event.fixed || null, isHidden:!!event.isHidden, fixedType:undefined, hiddenType:undefined
|
|
140
|
+
})) });
|
|
141
|
+
require('assert').deepStrictEqual(normalize(current), normalize(old), suffix + ' semantic event contract');
|
|
142
|
+
}
|
|
143
|
+
console.log('Result directory: ' + output);
|
|
144
|
+
persist();
|
|
145
|
+
})().catch(error => { results.failure = error.stack || String(error); persist(); console.error(error); console.error('Result directory: ' + output); process.exitCode = 1; }).finally(() => {
|
|
146
|
+
if (ws) ws.close();
|
|
147
|
+
if (chrome) chrome.kill();
|
|
148
|
+
if (server) server.close();
|
|
149
|
+
setTimeout(() => process.exit(process.exitCode || 0), 500);
|
|
150
|
+
});
|