|
/*
|
* Licensed to the Apache Software Foundation (ASF) under one
|
* or more contributor license agreements. See the NOTICE file
|
* distributed with this work for additional information
|
* regarding copyright ownership. The ASF licenses this file
|
* to you under the Apache License, Version 2.0 (the
|
* "License"); you may not use this file except in compliance
|
* with the License. You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing,
|
* software distributed under the License is distributed on an
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* KIND, either express or implied. See the License for the
|
* specific language governing permissions and limitations
|
* under the License.
|
*/
|
|
(function (context) {
|
|
var DEFAULT_DATA_TABLE_LIMIT = 8;
|
|
var objToString = Object.prototype.toString;
|
var TYPED_ARRAY = {
|
'[object Int8Array]': 1,
|
'[object Uint8Array]': 1,
|
'[object Uint8ClampedArray]': 1,
|
'[object Int16Array]': 1,
|
'[object Uint16Array]': 1,
|
'[object Int32Array]': 1,
|
'[object Uint32Array]': 1,
|
'[object Float32Array]': 1,
|
'[object Float64Array]': 1
|
};
|
|
var testHelper = {};
|
|
|
/**
|
* @param {Object} opt
|
* @param {string|Array.<string>} [opt.title] If array, each item is on a single line.
|
* Can use '**abc**', means <strong>abc</strong>.
|
* @param {Option} opt.option
|
* @param {Object} [opt.info] info object to display.
|
* @param {string} [opt.infoKey='option']
|
* @param {Object|Array} [opt.dataTable]
|
* @param {Array.<Object|Array>} [opt.dataTables] Multiple dataTables.
|
* @param {number} [opt.dataTableLimit=DEFAULT_DATA_TABLE_LIMIT]
|
* @param {number} [opt.width]
|
* @param {number} [opt.height]
|
* @param {boolean} [opt.draggable]
|
* @param {boolean} [opt.lazyUpdate]
|
* @param {boolean} [opt.notMerge]
|
* @param {boolean} [opt.autoResize=true]
|
* @param {Array.<Object>|Object} [opt.button] {text: ..., onClick: ...}, or an array of them.
|
* @param {Array.<Object>|Object} [opt.buttons] {text: ..., onClick: ...}, or an array of them.
|
* @param {boolean} [opt.recordCanvas] 'test/lib/canteen.js' is required.
|
*/
|
testHelper.create = function (echarts, domOrId, opt) {
|
var dom = getDom(domOrId);
|
|
if (!dom) {
|
return;
|
}
|
|
var title = document.createElement('div');
|
var left = document.createElement('div');
|
var chartContainer = document.createElement('div');
|
var buttonsContainer = document.createElement('div');
|
var dataTableContainer = document.createElement('div');
|
var infoContainer = document.createElement('div');
|
var recordCanvasContainer = document.createElement('div');
|
|
title.setAttribute('title', dom.getAttribute('id'));
|
|
title.className = 'test-title';
|
dom.className = 'test-chart-block';
|
left.className = 'test-chart-block-left';
|
chartContainer.className = 'test-chart';
|
buttonsContainer.className = 'test-buttons';
|
dataTableContainer.className = 'test-data-table';
|
infoContainer.className = 'test-info';
|
recordCanvasContainer.className = 'record-canvas';
|
|
if (opt.info) {
|
dom.className += ' test-chart-block-has-right';
|
infoContainer.className += ' test-chart-block-right';
|
}
|
|
left.appendChild(recordCanvasContainer);
|
left.appendChild(buttonsContainer);
|
left.appendChild(dataTableContainer);
|
left.appendChild(chartContainer);
|
dom.appendChild(infoContainer);
|
dom.appendChild(left);
|
dom.parentNode.insertBefore(title, dom);
|
|
var chart;
|
|
var optTitle = opt.title;
|
if (optTitle) {
|
if (optTitle instanceof Array) {
|
optTitle = optTitle.join('\n');
|
}
|
title.innerHTML = '<div class="test-title-inner">'
|
+ testHelper.encodeHTML(optTitle)
|
.replace(/\*\*([^*]+?)\*\*/g, '<strong>$1</strong>')
|
.replace(/\n/g, '<br>')
|
+ '</div>';
|
}
|
|
if (opt.option) {
|
chart = testHelper.createChart(echarts, chartContainer, opt.option, opt, opt.setOptionOpts);
|
}
|
|
var dataTables = opt.dataTables;
|
if (!dataTables && opt.dataTable) {
|
dataTables = [opt.dataTable];
|
}
|
if (dataTables) {
|
var tableHTML = [];
|
for (var i = 0; i < dataTables.length; i++) {
|
tableHTML.push(createDataTableHTML(dataTables[i], opt));
|
}
|
dataTableContainer.innerHTML = tableHTML.join('');
|
}
|
|
var buttons = opt.buttons || opt.button;
|
if (!(buttons instanceof Array)) {
|
buttons = buttons ? [buttons] : [];
|
}
|
if (buttons.length) {
|
for (var i = 0; i < buttons.length; i++) {
|
var btnDefine = buttons[i];
|
if (btnDefine) {
|
var btn = document.createElement('button');
|
btn.innerHTML = testHelper.encodeHTML(btnDefine.name || btnDefine.text || 'button');
|
btn.addEventListener('click', btnDefine.onClick || btnDefine.onclick);
|
buttonsContainer.appendChild(btn);
|
}
|
}
|
}
|
|
if (opt.info) {
|
updateInfo(opt.info, opt.infoKey);
|
}
|
|
function updateInfo(info, infoKey) {
|
infoContainer.innerHTML = createObjectHTML(info, infoKey || 'option');
|
}
|
|
initRecordCanvas(opt, chart, recordCanvasContainer);
|
|
chart.__testHelper = {
|
updateInfo: updateInfo
|
};
|
|
return chart;
|
};
|
|
function initRecordCanvas(opt, chart, recordCanvasContainer) {
|
if (!opt.recordCanvas) {
|
return;
|
}
|
recordCanvasContainer.innerHTML = ''
|
+ '<button>Show Canvas Record</button>'
|
+ '<button>Clear Canvas Record</button>'
|
+ '<div class="content-area"><textarea></textarea><br><button>Close</button></div>';
|
var buttons = recordCanvasContainer.getElementsByTagName('button');
|
var canvasRecordButton = buttons[0];
|
var clearButton = buttons[1];
|
var closeButton = buttons[2];
|
var recordArea = recordCanvasContainer.getElementsByTagName('textarea')[0];
|
var contentAraa = recordArea.parentNode;
|
canvasRecordButton.addEventListener('click', function () {
|
var content = [];
|
eachCtx(function (zlevel, ctx) {
|
content.push('\nLayer zlevel: ' + zlevel, '\n\n');
|
if (typeof ctx.stack !== 'function') {
|
alert('Missing: <script src="test/lib/canteen.js"></script>');
|
return;
|
}
|
var stack = ctx.stack();
|
for (var i = 0; i < stack.length; i++) {
|
var line = stack[i];
|
content.push(JSON.stringify(line), ',\n');
|
}
|
});
|
contentAraa.style.display = 'block';
|
recordArea.value = content.join('');
|
});
|
clearButton.addEventListener('click', function () {
|
eachCtx(function (zlevel, ctx) {
|
ctx.clear();
|
});
|
recordArea.value = 'Cleared.';
|
});
|
closeButton.addEventListener('click', function () {
|
contentAraa.style.display = 'none';
|
});
|
|
function eachCtx(cb) {
|
var layers = chart.getZr().painter.getLayers();
|
for (var zlevel in layers) {
|
if (layers.hasOwnProperty(zlevel)) {
|
var layer = layers[zlevel];
|
var canvas = layer.dom;
|
var ctx = canvas.getContext('2d');
|
cb(zlevel, ctx);
|
}
|
}
|
}
|
}
|
|
/**
|
* @param {ECharts} echarts
|
* @param {HTMLElement|string} domOrId
|
* @param {Object} option
|
* @param {boolean|number} opt If number, means height
|
* @param {boolean} opt.lazyUpdate
|
* @param {boolean} opt.notMerge
|
* @param {number} opt.width
|
* @param {number} opt.height
|
* @param {boolean} opt.draggable
|
*/
|
testHelper.createChart = function (echarts, domOrId, option, opt) {
|
if (typeof opt === 'number') {
|
opt = {height: opt};
|
}
|
else {
|
opt = opt || {};
|
}
|
|
var dom = getDom(domOrId);
|
|
if (dom) {
|
if (opt.width != null) {
|
dom.style.width = opt.width + 'px';
|
}
|
if (opt.height != null) {
|
dom.style.height = opt.height + 'px';
|
}
|
|
var chart = echarts.init(dom);
|
|
if (opt.draggable) {
|
if (!window.draggable) {
|
throw new Error(
|
'Pleasse add the script in HTML: \n'
|
+ '<script src="lib/draggable.js"></script>'
|
);
|
}
|
window.draggable.init(dom, chart, {throttle: 70});
|
}
|
|
option && chart.setOption(option, {
|
lazyUpdate: opt.lazyUpdate,
|
notMerge: opt.notMerge
|
});
|
|
var isAutoResize = opt.autoResize == null ? true : opt.autoResize;
|
if (isAutoResize) {
|
testHelper.resizable(chart);
|
}
|
|
return chart;
|
}
|
};
|
|
/**
|
* @usage
|
* ```js
|
* testHelper.printAssert(chart, function (assert) {
|
* // If any error thrown here, a "checked: Fail" will be printed on the chart;
|
* // Otherwise, "checked: Pass" will be printed on the chart.
|
* assert(condition1);
|
* assert(condition2);
|
* assert(condition3);
|
* });
|
* ```
|
* `testHelper.printAssert` can be called multiple times for one chart instance.
|
* For each call, one result (fail or pass) will be printed.
|
*
|
* @param chartOrDomId {EChartsInstance | string}
|
* @param checkFn {Function} param: a function `assert`.
|
*/
|
testHelper.printAssert = function (chartOrDomId, checkerFn) {
|
var hostDOMEl;
|
var chart;
|
if (typeof chartOrDomId === 'string') {
|
hostDOMEl = document.getElementById(chartOrDomId);
|
}
|
else {
|
chart = chartOrDomId;
|
hostDOMEl = chartOrDomId.getDom();
|
}
|
var failErr;
|
function assert(cond) {
|
if (!cond) {
|
throw new Error();
|
}
|
}
|
try {
|
checkerFn(assert);
|
}
|
catch (err) {
|
console.error(err);
|
failErr = err;
|
}
|
var printAssertRecord = hostDOMEl.__printAssertRecord || (hostDOMEl.__printAssertRecord = []);
|
|
var resultDom = document.createElement('div');
|
resultDom.innerHTML = failErr ? 'checked: Fail' : 'checked: Pass';
|
var fontSize = 40;
|
resultDom.style.cssText = [
|
'position: absolute;',
|
'left: 20px;',
|
'font-size: ' + fontSize + 'px;',
|
'z-index: ' + (failErr ? 99999 : 88888) + ';',
|
'color: ' + (failErr ? 'red' : 'green') + ';',
|
].join('');
|
printAssertRecord.push(resultDom);
|
hostDOMEl.appendChild(resultDom);
|
|
relayoutResult();
|
|
function relayoutResult() {
|
var chartHeight = chart ? chart.getHeight() : hostDOMEl.offsetHeight;
|
var lineHeight = Math.min(fontSize + 10, (chartHeight - 20) / printAssertRecord.length);
|
for (var i = 0; i < printAssertRecord.length; i++) {
|
var record = printAssertRecord[i];
|
record.style.top = (10 + i * lineHeight) + 'px';
|
}
|
}
|
};
|
|
|
var _dummyRequestAnimationFrameMounted = false;
|
|
/**
|
* Usage:
|
* ```js
|
* testHelper.controlFrame({pauseAt: 60});
|
* // Then load echarts.js (must after controlFrame called)
|
* ```
|
*
|
* @param {Object} [opt]
|
* @param {number} [opt.puaseAt] If specified `pauseAt`, auto pause at the frame.
|
* @param {Function} [opt.onFrame]
|
*/
|
testHelper.controlFrame = function (opt) {
|
opt = opt || {};
|
var pauseAt = opt.pauseAt;
|
pauseAt == null && (pauseAt = 0);
|
|
var _running = true;
|
var _pendingCbList = [];
|
var _frameNumber = 0;
|
var _mounted = false;
|
|
function getRunBtnText() {
|
return _running ? 'pause' : 'run';
|
}
|
|
var buttons = [{
|
text: getRunBtnText(),
|
onclick: function () {
|
buttons[0].el.innerHTML = getRunBtnText();
|
_running ? pause() : run();
|
}
|
}, {
|
text: 'next frame',
|
onclick: nextFrame
|
}];
|
|
var btnPanel = document.createElement('div');
|
btnPanel.className = 'control-frame-btn-panel'
|
var infoEl = document.createElement('div');
|
infoEl.className = 'control-frame-info';
|
btnPanel.appendChild(infoEl);
|
document.body.appendChild(btnPanel);
|
for (var i = 0; i < buttons.length; i++) {
|
var button = buttons[i];
|
var btnEl = button.el = document.createElement('button');
|
btnEl.innerHTML = button.text;
|
btnEl.addEventListener('click', button.onclick);
|
btnPanel.appendChild(btnEl);
|
}
|
|
if (_dummyRequestAnimationFrameMounted) {
|
throw new Error('Do not support `controlFrame` twice');
|
}
|
_dummyRequestAnimationFrameMounted = true;
|
var raf = window.requestAnimationFrame;
|
window.requestAnimationFrame = function (cb) {
|
_pendingCbList.push(cb);
|
if (_running && !_mounted) {
|
_mounted = true;
|
raf(nextFrame);
|
}
|
};
|
|
function run() {
|
_running = true;
|
nextFrame();
|
}
|
|
function pause() {
|
_running = false;
|
}
|
|
function nextFrame() {
|
opt.onFrame && opt.onFrame(_frameNumber);
|
|
if (pauseAt != null && _frameNumber === pauseAt) {
|
_running = false;
|
pauseAt = null;
|
}
|
infoEl.innerHTML = 'Frame: ' + _frameNumber + ' ( ' + (_running ? 'Running' : 'Paused') + ' )';
|
buttons[0].el.innerHTML = getRunBtnText();
|
|
_mounted = false;
|
var pending = _pendingCbList;
|
_pendingCbList = [];
|
for (var i = 0; i < pending.length; i++) {
|
pending[i]();
|
}
|
_frameNumber++;
|
}
|
}
|
|
testHelper.resizable = function (chart) {
|
var dom = chart.getDom();
|
var width = dom.clientWidth;
|
var height = dom.clientHeight;
|
function resize() {
|
var newWidth = dom.clientWidth;
|
var newHeight = dom.clientHeight;
|
if (width !== newWidth || height !== newHeight) {
|
chart.resize();
|
width = newWidth;
|
height = newHeight;
|
}
|
}
|
if (window.attachEvent) {
|
// Use builtin resize in IE
|
window.attachEvent('onresize', chart.resize);
|
}
|
else if (window.addEventListener) {
|
window.addEventListener('resize', resize, false);
|
}
|
};
|
|
// Clean params specified by `cleanList` and seed a param specifid by `newVal` in URL.
|
testHelper.setURLParam = function (cleanList, newVal) {
|
var params = getParamListFromURL();
|
for (var i = params.length - 1; i >= 0; i--) {
|
for (var j = 0; j < cleanList.length; j++) {
|
if (params[i] === cleanList[j]) {
|
params.splice(i, 1);
|
}
|
}
|
}
|
newVal && params.push(newVal);
|
params.sort();
|
location.search = params.join('&');
|
};
|
|
// Whether has param `val` in URL.
|
testHelper.hasURLParam = function (val) {
|
var params = getParamListFromURL();
|
for (var i = params.length - 1; i >= 0; i--) {
|
if (params[i] === val) {
|
return true;
|
}
|
}
|
return false;
|
};
|
|
// Nodejs `path.resolve`.
|
testHelper.resolve = function () {
|
var resolvedPath = '';
|
var resolvedAbsolute;
|
|
for (var i = arguments.length - 1; i >= 0 && !resolvedAbsolute; i--) {
|
var path = arguments[i];
|
if (path) {
|
resolvedPath = path + '/' + resolvedPath;
|
resolvedAbsolute = path[0] === '/';
|
}
|
}
|
|
if (!resolvedAbsolute) {
|
throw new Error('At least one absolute path should be input.');
|
}
|
|
// Normalize the path
|
resolvedPath = normalizePathArray(resolvedPath.split('/'), false).join('/');
|
|
return '/' + resolvedPath;
|
};
|
|
testHelper.encodeHTML = function (source) {
|
return String(source)
|
.replace(/&/g, '&')
|
.replace(/</g, '<')
|
.replace(/>/g, '>')
|
.replace(/"/g, '"')
|
.replace(/'/g, ''');
|
};
|
|
/**
|
* @public
|
* @return {string} Current url dir.
|
*/
|
testHelper.dir = function () {
|
return location.origin + testHelper.resolve(location.pathname, '..');
|
};
|
|
/**
|
* Not accurate.
|
* @param {*} type
|
* @return {string} 'function', 'array', 'typedArray', 'regexp',
|
* 'date', 'object', 'boolean', 'number', 'string'
|
*/
|
var getType = testHelper.getType = function (value) {
|
var type = typeof value;
|
var typeStr = objToString.call(value);
|
|
return !!TYPED_ARRAY[objToString.call(value)]
|
? 'typedArray'
|
: typeof type === 'function'
|
? 'function'
|
: typeStr === '[object Array]'
|
? 'array'
|
: typeStr === '[object Number]'
|
? 'number'
|
: typeStr === '[object Boolean]'
|
? 'boolean'
|
: typeStr === '[object String]'
|
? 'string'
|
: typeStr === '[object RegExp]'
|
? 'regexp'
|
: typeStr === '[object Date]'
|
? 'date'
|
: !!value && type === 'object'
|
? 'object'
|
: null;
|
};
|
|
/**
|
* JSON.stringify(obj, null, 2) will vertically layout array, which takes too much space.
|
* Can print like:
|
* [
|
* {name: 'xxx', value: 123},
|
* {name: 'xxx', value: 123},
|
* {name: 'xxx', value: 123}
|
* ]
|
* {
|
* arr: [33, 44, 55],
|
* str: 'xxx'
|
* }
|
*
|
* @param {*} object
|
* @param {opt|string} [opt] If string, means key.
|
* @param {string} [opt.key=''] Top level key, if given, print like: 'someKey: [asdf]'
|
* @param {string} [opt.objectLineBreak=true]
|
* @param {string} [opt.arrayLineBreak=false]
|
* @param {string} [opt.indent=4]
|
* @param {string} [opt.lineBreak='\n']
|
* @param {string} [opt.quotationMark='\'']
|
*/
|
var printObject = testHelper.printObject = function (obj, opt) {
|
opt = typeof opt === 'string'
|
? {key: opt}
|
: (opt || {});
|
|
var indent = opt.indent != null ? opt.indent : 4;
|
var lineBreak = opt.lineBreak != null ? opt.lineBreak : '\n';
|
var quotationMark = opt.quotationMark != null ? opt.quotationMark : '\'';
|
|
return doPrint(obj, opt.key, 0).str;
|
|
function doPrint(obj, key, depth) {
|
var codeIndent = (new Array(depth * indent + 1)).join(' ');
|
var subCodeIndent = (new Array((depth + 1) * indent + 1)).join(' ');
|
var hasLineBreak = false;
|
|
var preStr = key != null ? (key + ': ' ) : '';
|
var str;
|
|
var objType = getType(obj);
|
|
switch (objType) {
|
case 'function':
|
hasLineBreak = true;
|
str = preStr + quotationMark + obj + quotationMark;
|
break;
|
case 'regexp':
|
case 'date':
|
str = preStr + quotationMark + obj + quotationMark;
|
break;
|
case 'array':
|
case 'typedArray':
|
hasLineBreak = opt.arrayLineBreak != null ? opt.arrayLineBreak : false;
|
// If no break line in array, print in single line, like [12, 23, 34].
|
// else, each item takes a line.
|
var childBuilder = [];
|
for (var i = 0, len = obj.length; i < len; i++) {
|
var subResult = doPrint(obj[i], null, depth + 1);
|
childBuilder.push(subResult.str);
|
if (subResult.hasLineBreak) {
|
hasLineBreak = true;
|
}
|
}
|
var tail = hasLineBreak ? lineBreak : '';
|
var delimiter = ',' + (hasLineBreak ? (lineBreak + subCodeIndent) : ' ');
|
var subPre = hasLineBreak ? subCodeIndent : '';
|
var endPre = hasLineBreak ? codeIndent : '';
|
str = ''
|
+ preStr + '[' + tail
|
+ subPre + childBuilder.join(delimiter) + tail
|
+ endPre + ']';
|
break;
|
case 'object':
|
hasLineBreak = opt.objectLineBreak != null ? opt.objectLineBreak : true;
|
var childBuilder = [];
|
for (var i in obj) {
|
if (obj.hasOwnProperty(i)) {
|
var subResult = doPrint(obj[i], i, depth + 1);
|
childBuilder.push(subResult.str);
|
if (subResult.hasLineBreak) {
|
hasLineBreak = true;
|
}
|
}
|
}
|
str = ''
|
+ preStr + '{' + (hasLineBreak ? lineBreak : '')
|
+ (childBuilder.length
|
? (hasLineBreak ? subCodeIndent : '') + childBuilder.join(',' + (hasLineBreak ? lineBreak + subCodeIndent: ' ')) + (hasLineBreak ? lineBreak: '')
|
: ''
|
)
|
+ (hasLineBreak ? codeIndent : '') + '}';
|
break;
|
case 'boolean':
|
case 'number':
|
str = preStr + obj + '';
|
break;
|
case 'string':
|
str = JSON.stringify(obj); // escapse \n\r or others.
|
str = preStr + quotationMark + str.slice(1, str.length - 1) + quotationMark;
|
break;
|
default:
|
str = preStr + obj + '';
|
}
|
|
return {
|
str: str,
|
hasLineBreak: hasLineBreak
|
};
|
}
|
};
|
|
/**
|
* Usage:
|
* ```js
|
* // Print all elements that has `style.text`:
|
* var str = testHelper.stringifyElements(chart, {
|
* attr: ['z', 'z2', 'style.text', 'style.fill', 'style.stroke'],
|
* filter: el => el.style && el.style.text
|
* });
|
* ```
|
*
|
* @param {EChart} chart
|
* @param {Object} [opt]
|
* @param {string|Array.<string>} [opt.attr] Only print the given attrName;
|
* For example: 'z2' or ['z2', 'style.fill', 'style.stroke']
|
* @param {function} [opt.filter] print a subtree only if any satisfied node exists.
|
* param: el, return: boolean
|
*/
|
testHelper.stringifyElements = function (chart, opt) {
|
if (!chart) {
|
return;
|
}
|
opt = opt || {};
|
var attrNameList = opt.attr;
|
if (getType(attrNameList) !== 'array') {
|
attrNameList = attrNameList ? [attrNameList] : [];
|
}
|
|
var zr = chart.getZr();
|
var roots = zr.storage.getRoots();
|
var plainRoots = [];
|
|
retrieve(roots, plainRoots);
|
|
var elsStr = printObject(plainRoots, {indent: 2});
|
|
return elsStr;
|
|
// Only retrieve the value of the given attrName.
|
function retrieve(elList, plainNodes) {
|
var anySatisfied = false;
|
for (var i = 0; i < elList.length; i++) {
|
var el = elList[i];
|
|
var thisElSatisfied = !opt.filter || opt.filter(el);
|
|
var plainNode = {};
|
|
copyElment(plainNode, el);
|
|
var textContent = el.getTextContent();
|
if (textContent) {
|
plainNode.textContent = {};
|
copyElment(plainNode.textContent, textContent);
|
}
|
|
var thisSubAnySatisfied = false;
|
if (el.isGroup) {
|
plainNode.children = [];
|
thisSubAnySatisfied = retrieve(el.childrenRef(), plainNode.children);
|
}
|
|
if (thisElSatisfied || thisSubAnySatisfied) {
|
plainNodes.push(plainNode);
|
anySatisfied = true;
|
}
|
}
|
|
return anySatisfied;
|
}
|
|
function copyElment(plainNode, el) {
|
for (var i = 0; i < attrNameList.length; i++) {
|
var attrName = attrNameList[i];
|
var attrParts = attrName.split('.');
|
var partsLen = attrParts.length;
|
if (!partsLen) {
|
continue;
|
}
|
var elInner = el;
|
var plainInner = plainNode;
|
for (var j = 0; j < partsLen - 1 && elInner; j++) {
|
var attr = attrParts[j];
|
elInner = el[attr];
|
if (elInner) {
|
plainInner = plainInner[attr] || (plainInner[attr] = {});
|
}
|
}
|
var attr = attrParts[partsLen - 1];
|
if (elInner && elInner.hasOwnProperty(attr)) {
|
plainInner[attr] = elInner[attr];
|
}
|
}
|
}
|
};
|
|
/**
|
* Usage:
|
* ```js
|
* // Print all elements that has `style.text`:
|
* testHelper.printElements(chart, {
|
* attr: ['z', 'z2', 'style.text', 'style.fill', 'style.stroke'],
|
* filter: el => el.style && el.style.text
|
* });
|
* ```
|
*
|
* @see `stringifyElements`.
|
*/
|
testHelper.printElements = function (chart, opt) {
|
var elsStr = testHelper.stringifyElements(chart, opt);
|
console.log(elsStr);
|
};
|
|
/**
|
* Usage:
|
* ```js
|
* // Print all elements that has `style.text`:
|
* testHelper.retrieveElements(chart, {
|
* filter: el => el.style && el.style.text
|
* });
|
* ```
|
*
|
* @param {EChart} chart
|
* @param {Object} [opt]
|
* @param {function} [opt.filter] print a subtree only if any satisfied node exists.
|
* param: el, return: boolean
|
* @return {Array.<Element>}
|
*/
|
testHelper.retrieveElements = function (chart, opt) {
|
if (!chart) {
|
return;
|
}
|
opt = opt || {};
|
var attrNameList = opt.attr;
|
if (getType(attrNameList) !== 'array') {
|
attrNameList = attrNameList ? [attrNameList] : [];
|
}
|
|
var zr = chart.getZr();
|
var roots = zr.storage.getRoots();
|
var result = [];
|
|
retrieve(roots);
|
|
function retrieve(elList) {
|
for (var i = 0; i < elList.length; i++) {
|
var el = elList[i];
|
if (!opt.filter || opt.filter(el)) {
|
result.push(el);
|
}
|
if (el.isGroup) {
|
retrieve(el.childrenRef());
|
}
|
}
|
}
|
|
return result;
|
};
|
|
// opt: {record: JSON, width: number, height: number}
|
testHelper.reproduceCanteen = function (opt) {
|
var canvas = document.createElement('canvas');
|
canvas.style.width = opt.width + 'px';
|
canvas.style.height = opt.height + 'px';
|
var dpr = Math.max(window.devicePixelRatio || 1, 1);
|
canvas.width = opt.width * dpr;
|
canvas.height = opt.height * dpr;
|
|
var ctx = canvas.getContext('2d');
|
var record = opt.record;
|
|
for (var i = 0; i < record.length; i++) {
|
var line = record[i];
|
if (line.attr) {
|
if (!line.hasOwnProperty('val')) {
|
alertIllegal(line);
|
}
|
ctx[line.attr] = line.val;
|
}
|
else if (line.method) {
|
if (!line.hasOwnProperty('arguments')) {
|
alertIllegal(line);
|
}
|
ctx[line.method].apply(ctx, line.arguments);
|
}
|
else {
|
alertIllegal(line);
|
}
|
}
|
|
function alertIllegal(line) {
|
throw new Error('Illegal line: ' + JSON.stringify(line));
|
}
|
|
document.body.appendChild(canvas);
|
};
|
|
function createDataTableHTML(data, opt) {
|
var sourceFormat = detectSourceFormat(data);
|
var dataTableLimit = opt.dataTableLimit || DEFAULT_DATA_TABLE_LIMIT;
|
|
if (!sourceFormat) {
|
return '';
|
}
|
|
var html = ['<table><tbody>'];
|
|
if (sourceFormat === 'arrayRows') {
|
for (var i = 0; i < data.length && i <= dataTableLimit; i++) {
|
var line = data[i];
|
var htmlLine = ['<tr>'];
|
for (var j = 0; j < line.length; j++) {
|
var val = i === dataTableLimit ? '...' : line[j];
|
htmlLine.push('<td>' + testHelper.encodeHTML(val) + '</td>');
|
}
|
htmlLine.push('</tr>');
|
html.push(htmlLine.join(''));
|
}
|
}
|
else if (sourceFormat === 'objectRows') {
|
for (var i = 0; i < data.length && i <= dataTableLimit; i++) {
|
var line = data[i];
|
var htmlLine = ['<tr>'];
|
for (var key in line) {
|
if (line.hasOwnProperty(key)) {
|
var keyText = i === dataTableLimit ? '...' : key;
|
htmlLine.push('<td class="test-data-table-key">' + testHelper.encodeHTML(keyText) + '</td>');
|
var val = i === dataTableLimit ? '...' : line[key];
|
htmlLine.push('<td>' + testHelper.encodeHTML(val) + '</td>');
|
}
|
}
|
htmlLine.push('</tr>');
|
html.push(htmlLine.join(''));
|
}
|
}
|
else if (sourceFormat === 'keyedColumns') {
|
for (var key in data) {
|
var htmlLine = ['<tr>'];
|
htmlLine.push('<td class="test-data-table-key">' + testHelper.encodeHTML(key) + '</td>');
|
if (data.hasOwnProperty(key)) {
|
var col = data[key] || [];
|
for (var i = 0; i < col.length && i <= dataTableLimit; i++) {
|
var val = i === dataTableLimit ? '...' : col[i];
|
htmlLine.push('<td>' + testHelper.encodeHTML(val) + '</td>');
|
}
|
}
|
htmlLine.push('</tr>');
|
html.push(htmlLine.join(''));
|
}
|
}
|
|
html.push('</tbody></table>');
|
|
return html.join('');
|
}
|
|
function detectSourceFormat(data) {
|
if (data.length) {
|
for (var i = 0, len = data.length; i < len; i++) {
|
var item = data[i];
|
|
if (item == null) {
|
continue;
|
}
|
else if (item.length) {
|
return 'arrayRows';
|
}
|
else if (typeof data === 'object') {
|
return 'objectRows';
|
}
|
}
|
}
|
else if (typeof data === 'object') {
|
return 'keyedColumns';
|
}
|
}
|
|
function createObjectHTML(obj, key) {
|
var html = isObject(obj)
|
? testHelper.encodeHTML(printObject(obj, key))
|
: obj
|
? obj.toString()
|
: '';
|
|
return [
|
'<pre class="test-print-object">',
|
html,
|
'</pre>'
|
].join('');
|
}
|
|
var getDom = testHelper.getDom = function (domOrId) {
|
return getType(domOrId) === 'string' ? document.getElementById(domOrId) : domOrId;
|
}
|
|
|
// resolves . and .. elements in a path array with directory names there
|
// must be no slashes or device names (c:\) in the array
|
// (so also no leading and trailing slashes - it does not distinguish
|
// relative and absolute paths)
|
function normalizePathArray(parts, allowAboveRoot) {
|
var res = [];
|
for (var i = 0; i < parts.length; i++) {
|
var p = parts[i];
|
|
// ignore empty parts
|
if (!p || p === '.') {
|
continue;
|
}
|
|
if (p === '..') {
|
if (res.length && res[res.length - 1] !== '..') {
|
res.pop();
|
} else if (allowAboveRoot) {
|
res.push('..');
|
}
|
} else {
|
res.push(p);
|
}
|
}
|
|
return res;
|
}
|
|
function getParamListFromURL() {
|
var params = location.search.replace('?', '');
|
return params ? params.split('&') : [];
|
}
|
|
function isObject(value) {
|
// Avoid a V8 JIT bug in Chrome 19-20.
|
// See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
|
const type = typeof value;
|
return type === 'function' || (!!value && type === 'object');
|
}
|
|
context.testHelper = testHelper;
|
|
})(window);
|