提交 bae8e947 编写于 作者: O Ovilia

improve ui test structure

上级 b4e3a625
......@@ -168,3 +168,4 @@ map/tool
theme/thumb
/lib
todo
**/*.log
......@@ -5,7 +5,6 @@ else
# use last release
old=$(git rev-list --tags --max-count=1)
fi
echo $old
mkdir -p tmp
cp ../../dist/echarts.js "tmp/newEcharts.js"
git show $old:dist/echarts.js > 'tmp/oldEcharts.js'
......@@ -2,16 +2,29 @@
var helper = context.uiHelper = {};
// canvas comparing strategy, 'stack' or 'content'
var STRATEGY = 'stack';
// dom for failed cases
var failedDom = document.createElement('div');
failedDom.setAttribute('id', 'failed-panel');
var hasFailedDom = false;
/**
* expect canvas.toDataURL to be the same by old and new echarts
* @param {string} title title of suite and case
* @param {function} doTest test body
* @param {function} done done callback provided by jasmine
*/
helper.expectEqualCanvasContent = function(doTest, done) {
helper.expectEqualCanvasContent = function(title, doTest, done) {
var that = this;
window.require(['oldEcharts', 'newEcharts'], function (oldE, newE) {
var oldCanvas = doTest(oldE);
var newCanvas = doTest(newE);
expect(oldCanvas.toDataURL()).toEqual(newCanvas.toDataURL());
var oldImg = doTest(oldE).toDataURL();
var newImg = doTest(newE).toDataURL();
if (oldImg !== newImg) {
that.addFailedCases(title, oldImg, newImg);
}
expect(oldImg).toEqual(newImg);
done();
});
};
......@@ -19,13 +32,22 @@
/**
* expect canvas operation stack provided by canteen
* to be the same by old and new echarts
* @param {string} title title of suite and case
* @param {function} doTest test body
* @param {function} done done callback provided by jasmine
*/
helper.expectEqualCanvasStack = function(doTest, done) {
helper.expectEqualCanvasStack = function(title, doTest, done) {
var that = this;
window.require(['oldEcharts', 'newEcharts'], function (oldE, newE) {
var oldCtx = doTest(oldE).getContext('2d');
var newCtx = doTest(newE).getContext('2d');
var oldCanvas = doTest(oldE);
var newCanvas = doTest(newE);
var oldImg = oldCanvas.toDataURL();
var newImg = newCanvas.toDataURL();
if (oldImg !== newImg) {
that.addFailedCases(title, oldImg, newImg);
}
var oldCtx = oldCanvas.getContext('2d');
var newCtx = newCanvas.getContext('2d');
// hash of canvas operation stack, provided by canteen
// https://github.com/platfora/Canteen
expect(oldCtx.hash()).toEqual(newCtx.hash());
......@@ -35,15 +57,15 @@
/**
* expect canvas with strategy
* @param {string} strategy 'content' or 'stack'
* @param {string} title title of suite and case
* @param {function} doTest test body
* @param {function} done done callback provided by jasmine
*/
helper.expectEqualCanvas = function(strategy, doTest, done) {
if (strategy === 'content') {
helper.expectEqualCanvasContent(doTest, done);
} else if (strategy === 'stack') {
helper.expectEqualCanvasStack(doTest, done);
helper.expectEqualCanvas = function(title, doTest, done) {
if (STRATEGY === 'content') {
helper.expectEqualCanvasContent(title, doTest, done);
} else if (STRATEGY === 'stack') {
helper.expectEqualCanvasStack(title, doTest, done);
} else {
console.error('Invalid equal canvas strategy!');
}
......@@ -68,4 +90,85 @@
return canvas;
};
/**
* run test with only setOption
* @param {string} name name of the test
* @param {object} option echarts option
*/
helper.testOption = function(name, option) {
var that = this;
it(name, function(done) {
that.expectEqualCanvas(name, function(ec) {
var canvas = that.getRenderedCanvas(ec, function(myChart) {
myChart.setOption(option);
});
return canvas;
}, done);
});
}
/**
* run test with setOption for whole spec
* @param {string} specName spec name
* @param {object[]} suites arrary of suites
*/
helper.testOptionSpec = function(specName, suites) {
for (var sid = 0, slen = suites.length; sid < slen; ++sid) {
(function(suiteName, cases) {
describe('show', function() {
for (var cid = 0, clen = cases.length; cid < clen; ++cid) {var name = specName + ' - ' + suiteName + ': '
+ cases[cid].name;
uiHelper.testOption(name, cases[cid].option);
}
});
})(suites[sid].name, suites[sid].cases);
}
}
/**
* @param {string} name name of the test
* @param {string} oldImgSrc old canvas.toDataURL value
* @param {string} newImgSrc new canvas.toDataURL value
* add a failed case in dom
*/
helper.addFailedCases = function(name, oldImgSrc, newImgSrc) {
// group of this case
var group = document.createElement('div');
var title = document.createElement('h6');
title.innerHTML = name + '. Here are old, new, and diff images.';
group.appendChild(title);
// old image and new image
var oldImg = document.createElement('img');
oldImg.src = oldImgSrc;
oldImg.setAttribute('title', 'Old Image');
var newImg = document.createElement('img');
newImg.src = newImgSrc;
newImg.setAttribute('title', 'New Image');
group.appendChild(oldImg);
group.appendChild(newImg);
// diff image
var diff = imagediff.diff(oldImg, newImg);
console.log(diff);
var canvas = document.createElement('canvas');
canvas.width = oldImg.width;
canvas.height = oldImg.height;
var ctx = canvas.getContext('2d');
ctx.putImageData(diff, 0, 0);
var diffImg = document.createElement('img');
diffImg.src = canvas.toDataURL();
diffImg.setAttribute('title', 'Diff Image');
group.appendChild(diffImg);
failedDom.appendChild(group);
// append to dom
if (!hasFailedDom) {
var body = document.getElementsByTagName('body')[0];
body.appendChild(failedDom);
hasFailedDom = true;
}
};
})(window);
(function (name, definition) {
var root = this;
if (typeof module !== 'undefined') {
var Canvas;
try {
Canvas = require('canvas');
} catch (e) {}
module.exports = definition(root, name, Canvas);
} else if (typeof define === 'function' && typeof define.amd === 'object') {
define(definition(root, name));
} else {
root[name] = definition(root, name);
}
})('imagediff', function (root, name, Canvas) {
var
TYPE_ARRAY = /\[object Array\]/i,
TYPE_CANVAS = /\[object (Canvas|HTMLCanvasElement)\]/i,
TYPE_CONTEXT = /\[object CanvasRenderingContext2D\]/i,
TYPE_IMAGE = /\[object (Image|HTMLImageElement)\]/i,
TYPE_IMAGE_DATA = /\[object ImageData\]/i,
UNDEFINED = 'undefined',
canvas = getCanvas(),
context = canvas.getContext('2d'),
previous = root[name],
imagediff, jasmine;
// Creation
function getCanvas (width, height) {
var canvas;
if (Canvas) {
canvas = new Canvas();
} else if (root.document && root.document.createElement) {
canvas = document.createElement('canvas');
} else {
throw new Error(
e.message + '\n' +
'Please see https://github.com/HumbleSoftware/js-imagediff#cannot-find-module-canvas\n'
);
}
if (width) canvas.width = width;
if (height) canvas.height = height;
return canvas;
}
function getImageData (width, height) {
canvas.width = width;
canvas.height = height;
context.clearRect(0, 0, width, height);
return context.createImageData(width, height);
}
// Type Checking
function isImage (object) {
return isType(object, TYPE_IMAGE);
}
function isCanvas (object) {
return isType(object, TYPE_CANVAS);
}
function isContext (object) {
return isType(object, TYPE_CONTEXT);
}
function isImageData (object) {
return !!(object &&
isType(object, TYPE_IMAGE_DATA) &&
typeof(object.width) !== UNDEFINED &&
typeof(object.height) !== UNDEFINED &&
typeof(object.data) !== UNDEFINED);
}
function isImageType (object) {
return (
isImage(object) ||
isCanvas(object) ||
isContext(object) ||
isImageData(object)
);
}
function isType (object, type) {
return typeof (object) === 'object' && !!Object.prototype.toString.apply(object).match(type);
}
// Type Conversion
function copyImageData (imageData) {
var
height = imageData.height,
width = imageData.width,
data = imageData.data,
newImageData, newData, i;
canvas.width = width;
canvas.height = height;
newImageData = context.getImageData(0, 0, width, height);
newData = newImageData.data;
for (i = imageData.data.length; i--;) {
newData[i] = data[i];
}
return newImageData;
}
function toImageData (object) {
if (isImage(object)) { return toImageDataFromImage(object); }
if (isCanvas(object)) { return toImageDataFromCanvas(object); }
if (isContext(object)) { return toImageDataFromContext(object); }
if (isImageData(object)) { return object; }
}
function toImageDataFromImage (image) {
var
height = image.height,
width = image.width;
canvas.width = width;
canvas.height = height;
context.clearRect(0, 0, width, height);
context.drawImage(image, 0, 0);
return context.getImageData(0, 0, width, height);
}
function toImageDataFromCanvas (canvas) {
var
height = canvas.height,
width = canvas.width,
context = canvas.getContext('2d');
return context.getImageData(0, 0, width, height);
}
function toImageDataFromContext (context) {
var
canvas = context.canvas,
height = canvas.height,
width = canvas.width;
return context.getImageData(0, 0, width, height);
}
function toCanvas (object) {
var
data = toImageData(object),
canvas = getCanvas(data.width, data.height),
context = canvas.getContext('2d');
context.putImageData(data, 0, 0);
return canvas;
}
// ImageData Equality Operators
function equalWidth (a, b) {
return a.width === b.width;
}
function equalHeight (a, b) {
return a.height === b.height;
}
function equalDimensions (a, b) {
return equalHeight(a, b) && equalWidth(a, b);
}
function equal (a, b, tolerance) {
var
aData = a.data,
bData = b.data,
length = aData.length,
i;
tolerance = tolerance || 0;
if (!equalDimensions(a, b)) return false;
for (i = length; i--;) if (aData[i] !== bData[i] && Math.abs(aData[i] - bData[i]) > tolerance) return false;
return true;
}
// Diff
function diff (a, b, options) {
return (equalDimensions(a, b) ? diffEqual : diffUnequal)(a, b, options);
}
function diffEqual (a, b, options) {
var
height = a.height,
width = a.width,
c = getImageData(width, height), // c = a - b
aData = a.data,
bData = b.data,
cData = c.data,
length = cData.length,
row, column,
i, j, k, v;
for (i = 0; i < length; i += 4) {
cData[i] = Math.abs(aData[i] - bData[i]);
cData[i+1] = Math.abs(aData[i+1] - bData[i+1]);
cData[i+2] = Math.abs(aData[i+2] - bData[i+2]);
cData[i+3] = Math.abs(255 - Math.abs(aData[i+3] - bData[i+3]));
}
return c;
}
function diffUnequal (a, b, options) {
var
height = Math.max(a.height, b.height),
width = Math.max(a.width, b.width),
c = getImageData(width, height), // c = a - b
aData = a.data,
bData = b.data,
cData = c.data,
align = options && options.align,
rowOffset,
columnOffset,
row, column,
i, j, k, v;
for (i = cData.length - 1; i > 0; i = i - 4) {
cData[i] = 255;
}
// Add First Image
offsets(a);
for (row = a.height; row--;){
for (column = a.width; column--;) {
i = 4 * ((row + rowOffset) * width + (column + columnOffset));
j = 4 * (row * a.width + column);
cData[i+0] = aData[j+0]; // r
cData[i+1] = aData[j+1]; // g
cData[i+2] = aData[j+2]; // b
// cData[i+3] = aData[j+3]; // a
}
}
// Subtract Second Image
offsets(b);
for (row = b.height; row--;){
for (column = b.width; column--;) {
i = 4 * ((row + rowOffset) * width + (column + columnOffset));
j = 4 * (row * b.width + column);
cData[i+0] = Math.abs(cData[i+0] - bData[j+0]); // r
cData[i+1] = Math.abs(cData[i+1] - bData[j+1]); // g
cData[i+2] = Math.abs(cData[i+2] - bData[j+2]); // b
}
}
// Helpers
function offsets (imageData) {
if (align === 'top') {
rowOffset = 0;
columnOffset = 0;
} else {
rowOffset = Math.floor((height - imageData.height) / 2);
columnOffset = Math.floor((width - imageData.width) / 2);
}
}
return c;
}
// Validation
function checkType () {
var i;
for (i = 0; i < arguments.length; i++) {
if (!isImageType(arguments[i])) {
throw {
name : 'ImageTypeError',
message : 'Submitted object was not an image.'
};
}
}
}
// Jasmine Matchers
function get (element, content) {
element = document.createElement(element);
if (element && content) {
element.innerHTML = content;
}
return element;
}
jasmine = {
toBeImageData : function () {
return imagediff.isImageData(this.actual);
},
toImageDiffEqual : function (expected, tolerance) {
if (typeof (document) !== UNDEFINED) {
this.message = function () {
var
div = get('div'),
a = get('div', '<div>Actual:</div>'),
b = get('div', '<div>Expected:</div>'),
c = get('div', '<div>Diff:</div>'),
diff = imagediff.diff(this.actual, expected),
canvas = getCanvas(),
context;
canvas.height = diff.height;
canvas.width = diff.width;
div.style.overflow = 'hidden';
a.style.float = 'left';
b.style.float = 'left';
c.style.float = 'left';
context = canvas.getContext('2d');
context.putImageData(diff, 0, 0);
a.appendChild(toCanvas(this.actual));
b.appendChild(toCanvas(expected));
c.appendChild(canvas);
div.appendChild(a);
div.appendChild(b);
div.appendChild(c);
return [
div,
"Expected not to be equal."
];
};
}
return imagediff.equal(this.actual, expected, tolerance);
}
};
// Image Output
function imageDataToPNG (imageData, outputFile, callback) {
var
canvas = toCanvas(imageData),
base64Data,
decodedImage;
callback = callback || Function;
base64Data = canvas.toDataURL().replace(/^data:image\/\w+;base64,/,"");
decodedImage = new Buffer(base64Data, 'base64');
require('fs').writeFile(outputFile, decodedImage, callback);
}
// Definition
imagediff = {
createCanvas : getCanvas,
createImageData : getImageData,
isImage : isImage,
isCanvas : isCanvas,
isContext : isContext,
isImageData : isImageData,
isImageType : isImageType,
toImageData : function (object) {
checkType(object);
if (isImageData(object)) { return copyImageData(object); }
return toImageData(object);
},
equal : function (a, b, tolerance) {
checkType(a, b);
a = toImageData(a);
b = toImageData(b);
return equal(a, b, tolerance);
},
diff : function (a, b, options) {
checkType(a, b);
a = toImageData(a);
b = toImageData(b);
return diff(a, b, options);
},
jasmine : jasmine,
// Compatibility
noConflict : function () {
root[name] = previous;
return imagediff;
}
};
if (typeof module !== 'undefined') {
imagediff.imageDataToPNG = imageDataToPNG;
}
return imagediff;
});
require.config({
paths: {
'oldEcharts': 'tmp/oldEcharts',
'newEcharts': '../../dist/echarts'
'newEcharts': 'tmp/newEcharts'
}
});
......@@ -2,37 +2,28 @@ describe('title', function() {
var uiHelper = window.uiHelper;
// canvas comparing strategy, 'stack' or 'content'
// see ../../core/uiHelper.js for more detail
var STRATEGY = 'stack';
var suites = [{
name: 'show',
cases: [{
name: 'should display given title by default',
option: {
series: [],
title: {
text: 'test title'
}
}
}, {
name: 'should hide title when show is false',
option: {
series: [],
title: {
text: 'hidden title',
display: false
}
}
}]
}];
describe('title-show', function() {
it('should display given title by default', function(done) {
uiHelper.expectEqualCanvas(STRATEGY, function(echarts) {
return uiHelper.getRenderedCanvas(echarts, function(myChart) {
myChart.setOption({
series: [],
title: {
text: 'test title'
}
});
});
}, done);
});
it('should hide title when show is false', function(done) {
uiHelper.expectEqualCanvas(STRATEGY, function(echarts) {
return uiHelper.getRenderedCanvas(echarts, function(myChart) {
myChart.setOption({
series: [],
title: {
text: 'hidden title',
display: false
}
});
});
}, done);
});
});
uiHelper.testOptionSpec('title', suites);
});
......@@ -7,11 +7,23 @@
<link rel="shortcut icon" type="image/png" href="lib/jasmine-2.3.4/jasmine_favicon.png">
<link rel="stylesheet" href="lib/jasmine-2.3.4/jasmine.css">
<style type="text/css">
#failed-panel {
margin-top: 20px;
}
#failed-panel img {
width: 33.3%;
display: inline-block;
}
</style>
<script src="lib/jasmine-2.3.4/jasmine.js"></script>
<script src="lib/jasmine-2.3.4/jasmine-html.js"></script>
<script src="lib/jasmine-2.3.4/boot.js"></script>
<script src="lib/canteen.js"></script>
<script src="lib/imagediff.js"></script>
<script src="../esl.js"></script>
<script src="spec/ui/config.js"></script>
......@@ -21,6 +33,7 @@
</head>
<body>
</body>
</html>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册