Better visual tests, test passing, more SVGs (#5076)

* improving golddens

* more tests

* test passing

* removed dist

* fixed travis

* make visual running in browser too

* more travis

* fixed travis

* fixed travis

* more coffee
This commit is contained in:
Andrea Bogazzi 2018-06-28 12:31:26 +02:00 committed by GitHub
parent 4bb1ffb9e6
commit d95db41608
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
58 changed files with 570 additions and 62 deletions

View file

@ -30,6 +30,8 @@ jobs:
fast_finish: true
allow_failures:
- env: LAUNCHER=Node CANFAIL=TRUE
- env: LAUNCHER=Firefox CANFAIL=TRUE
- env: LAUNCHER=Chrome CANFAIL=TRUE
include:
- stage: Linting and Building
env: STEP=LINT
@ -70,8 +72,15 @@ jobs:
node_js: "4"
- stage: Visual Tests
node_js: "8"
env: LAUNCHER=Node CANFAIL=TRUE
script: npm run test:visual
- stage: Visual Tests
env: LAUNCHER=Chrome CANFAIL=TRUE
install: npm install testem@1.18.4 qunit@2.4.1
script: npm run build:fast && testem ci --port 8080 -f testem-visual.json -l $LAUNCHER
- stage: Visual Tests
env: LAUNCHER=Firefox CANFAIL=TRUE
install: npm install testem@1.18.4 qunit@2.4.1
script: npm run build:fast && testem ci --port 8080 -f testem-visual.json -l $LAUNCHER
script: 'npm run build:fast && testem ci --port 8080 -f testem.json -l $LAUNCHER'

View file

@ -54,6 +54,7 @@
"export_tests_to_site": "cp test/unit/*.js ../fabricjs.com/test/unit",
"all": "npm run build && npm run test && npm run lint && npm run lint_tests && npm run export_dist_to_site && npm run export_tests_to_site",
"testem": "testem .",
"testem:visual": "testem --file testem-visual.json",
"testem:ci": "testem ci"
},
"optionalDependencies": {

View file

@ -1,28 +0,0 @@
var testrunner = require('node-qunit');
testrunner.options.log.summary = true;
testrunner.options.log.tests = false;
testrunner.options.log.assertions = false;
testrunner.options.log.coverage = false;
testrunner.options.coverage = true;
testrunner.options.maxBlockDuration = 120000;
testrunner.run({
deps: './test/fixtures/test_script.js',
code: './dist/fabric.js',
tests: [
'./test/visual/svg_import.js',
],
// tests: ['./test/unit/object_clipPath.js',],
}, function(err, report) {
if (err) {
console.log(err);
process.exit(1);
}
if(report.failed > 0){
process.on('exit', function() {
process.exit(1);
});
}
});

156
test/lib/pixelmatch.js Normal file
View file

@ -0,0 +1,156 @@
'use strict';
function pixelmatch(img1, img2, output, width, height, options) {
if (!options) options = {};
var threshold = options.threshold === undefined ? 0.1 : options.threshold;
// maximum acceptable square distance between two colors;
// 35215 is the maximum possible value for the YIQ difference metric
var maxDelta = 35215 * threshold * threshold,
diff = 0;
// compare each pixel of one image against the other one
for (var y = 0; y < height; y++) {
for (var x = 0; x < width; x++) {
var pos = (y * width + x) * 4;
// squared YUV distance between colors at this pixel position
var delta = colorDelta(img1, img2, pos, pos);
// the color difference is above the threshold
if (delta > maxDelta) {
// check it's a real rendering difference or just anti-aliasing
if (!options.includeAA && (antialiased(img1, x, y, width, height, img2) ||
antialiased(img2, x, y, width, height, img1))) {
// one of the pixels is anti-aliasing; draw as yellow and do not count as difference
if (output) drawPixel(output, pos, 255, 255, 0);
} else {
// found substantial difference not caused by anti-aliasing; draw it as red
if (output) drawPixel(output, pos, 255, 0, 0);
diff++;
}
} else if (output) {
// pixels are similar; draw background as grayscale image blended with white
var val = blend(grayPixel(img1, pos), 0.1);
drawPixel(output, pos, val, val, val);
}
}
}
// return the number of different pixels
return diff;
}
// check if a pixel is likely a part of anti-aliasing;
// based on "Anti-aliased Pixel and Intensity Slope Detector" paper by V. Vysniauskas, 2009
function antialiased(img, x1, y1, width, height, img2) {
var x0 = Math.max(x1 - 1, 0),
y0 = Math.max(y1 - 1, 0),
x2 = Math.min(x1 + 1, width - 1),
y2 = Math.min(y1 + 1, height - 1),
pos = (y1 * width + x1) * 4,
zeroes = 0,
positives = 0,
negatives = 0,
min = 0,
max = 0,
minX, minY, maxX, maxY;
// go through 8 adjacent pixels
for (var x = x0; x <= x2; x++) {
for (var y = y0; y <= y2; y++) {
if (x === x1 && y === y1) continue;
// brightness delta between the center pixel and adjacent one
var delta = colorDelta(img, img, pos, (y * width + x) * 4, true);
// count the number of equal, darker and brighter adjacent pixels
if (delta === 0) zeroes++;
else if (delta < 0) negatives++;
else if (delta > 0) positives++;
// if found more than 2 equal siblings, it's definitely not anti-aliasing
if (zeroes > 2) return false;
if (!img2) continue;
// remember the darkest pixel
if (delta < min) {
min = delta;
minX = x;
minY = y;
}
// remember the brightest pixel
if (delta > max) {
max = delta;
maxX = x;
maxY = y;
}
}
}
if (!img2) return true;
// if there are no both darker and brighter pixels among siblings, it's not anti-aliasing
if (negatives === 0 || positives === 0) return false;
// if either the darkest or the brightest pixel has more than 2 equal siblings in both images
// (definitely not anti-aliased), this pixel is anti-aliased
return (!antialiased(img, minX, minY, width, height) && !antialiased(img2, minX, minY, width, height)) ||
(!antialiased(img, maxX, maxY, width, height) && !antialiased(img2, maxX, maxY, width, height));
}
// calculate color difference according to the paper "Measuring perceived color difference
// using YIQ NTSC transmission color space in mobile applications" by Y. Kotsarenko and F. Ramos
function colorDelta(img1, img2, k, m, yOnly) {
var a1 = img1[k + 3] / 255,
a2 = img2[m + 3] / 255,
r1 = blend(img1[k + 0], a1),
g1 = blend(img1[k + 1], a1),
b1 = blend(img1[k + 2], a1),
r2 = blend(img2[m + 0], a2),
g2 = blend(img2[m + 1], a2),
b2 = blend(img2[m + 2], a2),
y = rgb2y(r1, g1, b1) - rgb2y(r2, g2, b2);
if (yOnly) return y; // brightness difference only
var i = rgb2i(r1, g1, b1) - rgb2i(r2, g2, b2),
q = rgb2q(r1, g1, b1) - rgb2q(r2, g2, b2);
return 0.5053 * y * y + 0.299 * i * i + 0.1957 * q * q;
}
function rgb2y(r, g, b) { return r * 0.29889531 + g * 0.58662247 + b * 0.11448223; }
function rgb2i(r, g, b) { return r * 0.59597799 - g * 0.27417610 - b * 0.32180189; }
function rgb2q(r, g, b) { return r * 0.21147017 - g * 0.52261711 + b * 0.31114694; }
// blend semi-transparent color with white
function blend(c, a) {
return 255 + (c - 255) * a;
}
function drawPixel(output, pos, r, g, b) {
output[pos + 0] = r;
output[pos + 1] = g;
output[pos + 2] = b;
output[pos + 3] = 255;
}
function grayPixel(img, i) {
var a = img[i + 3] / 255,
r = blend(img[i + 0], a),
g = blend(img[i + 1], a),
b = blend(img[i + 2], a);
return rgb2y(r, g, b);
}

View file

@ -0,0 +1,13 @@
<?xml version="1.0" standalone="no" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="105" height="77" xml:space="preserve">
<desc>Created with Fabric.js 1.0.12
</desc>
<linearGradient id="linear" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="0" y2="77" >
<stop offset="0" style="stop-color:#cc5232"/>
<stop offset="0.3" style="stop-color:#b0c72d"/>
<stop offset="0.6" style="stop-color:aqua"/>
<stop offset="1" style="stop-color:teal"/>
</linearGradient>
<rect x="0" y="0" rx="0" ry="0" width="105" height="77" style="stroke: none; stroke-width: 0; stroke-dasharray: ; fill: url(#linear); opacity: 1;"/>
</svg>

After

Width:  |  Height:  |  Size: 796 B

View file

@ -0,0 +1,11 @@
<?xml version="1.0" standalone="no" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="105" height="77" xml:space="preserve">
<desc>Created with Fabric.js 1.0.12
</desc>
<linearGradient id="linear" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="0" y2="77" >
<stop offset="0%" style="stop-color:#cc5232;stop-opacity:0"/>
<stop offset="100%" style="stop-color:#b0c72d;stop-opacity:1"/>
</linearGradient>
<rect x="0" y="0" rx="0" ry="0" width="105" height="77" style="stroke: none; stroke-width: 0; stroke-dasharray: ; fill: url(#linear); opacity: 1;"/>
</svg>

After

Width:  |  Height:  |  Size: 732 B

View file

@ -0,0 +1,11 @@
<?xml version="1.0" standalone="no" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="105" height="77" xml:space="preserve">
<desc>Created with Fabric.js 1.0.12
</desc>
<linearGradient id="linear" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="0" y2="77" >
<stop offset="0%" style="stop-color:blue;stop-opacity:0"/>
<stop offset="100%" style="stop-color:red;stop-opacity:1"/>
</linearGradient>
<rect x="0" y="0" rx="0" ry="0" width="105" height="77" style="stroke: none; stroke-width: 0; stroke-dasharray: ; fill: url(#linear); opacity: 1;"/>
</svg>

After

Width:  |  Height:  |  Size: 725 B

View file

@ -0,0 +1,20 @@
<?xml version="1.0" standalone="no"?>
<svg width="120" height="240" version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="Gradient1" gradientUnits="objectBoundingBox">
<stop offset="0%" stop-color="red"/>
<stop offset="50%" stop-color="black" stop-opacity="0"/>
<stop offset="100%" stop-color="blue"/>
</linearGradient>
<linearGradient id="Gradient2" x1="0" x2="0" y1="0" y2="100%">
<stop offset="0%" stop-color="red"/>
<stop offset="50%" stop-color="black" stop-opacity="0"/>
<stop offset="100%" stop-color="blue"/>
</linearGradient>
</defs>
<rect id="rect1" x="10" y="10" rx="15" ry="15" width="100" height="100" fill="url(#Gradient1)"/>
<rect x="10" y="120" rx="15" ry="15" width="100" height="100" fill="url(#Gradient2)"/>
</svg>

After

Width:  |  Height:  |  Size: 845 B

View file

@ -0,0 +1,13 @@
<?xml version="1.0" standalone="no"?>
<svg width="120" height="120" version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="Gradient2" x1="0" x2="0" y1="0" y2="100%">
<stop offset="0%" stop-color="red"/>
<stop offset="50%" stop-color="black" stop-opacity="0.5"/>
<stop offset="100%" stop-color="blue"/>
</linearGradient>
</defs>
<rect x="10" y="10" rx="15" ry="15" width="100" height="100" fill="url(#Gradient2)"/>
</svg>

After

Width:  |  Height:  |  Size: 486 B

View file

@ -0,0 +1,13 @@
<?xml version="1.0" standalone="no" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="105" height="77" xml:space="preserve">
<desc>Created with Fabric.js 1.0.12
</desc>
<linearGradient id="linear" gradientUnits="userSpaceOnUse" x1="52.5" y1="0" x2="0" y2="77" >
<stop offset="0" style="stop-color:#cc5232"/>
<stop offset="0.3" style="stop-color:#b0c72d"/>
<stop offset="0.6" style="stop-color:rgba(0,0,153,0.5)"/>
<stop offset="1" style="stop-color:#009999" stop-opacity="0.9"/>
</linearGradient>
<rect x="0" y="0" rx="0" ry="0" width="105" height="77" style="stroke: none; stroke-width: 0; stroke-dasharray: ; fill: url(#linear); opacity: 1;"/>
</svg>

After

Width:  |  Height:  |  Size: 834 B

View file

@ -0,0 +1,13 @@
<?xml version="1.0" standalone="no" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="105" height="77" xml:space="preserve">
<desc>Created with Fabric.js 1.0.12
</desc>
<linearGradient id="linear" gradientUnits="userSpaceOnUse" x1="52.5" y1="0" x2="0" y2="77" >
<stop offset="0" style="stop-color:#cc5232"/>
<stop offset="0.3" style="stop-color:#b0c72d; stop-opacity: 0.7;"/>
<stop offset="0.6" style="stop-color:rgba(0,0,153,0.5)" stop-opacity="0.5"/>
<stop offset="1" style="stop-color:#009999" stop-opacity="0.9"/>
</linearGradient>
<rect x="0" y="0" rx="0" ry="0" width="105" height="77" style="stroke: none; stroke-width: 0; stroke-dasharray: ; fill: url(#linear); opacity: 1;"/>
</svg>

After

Width:  |  Height:  |  Size: 873 B

View file

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="three_stops">
<stop offset="0%" style="stop-color: #ffcc00;"/>
<stop offset="33.3%" style="stop-color: #cc6699"/>
<stop offset="100%" style="stop-color: #66cc99;"/>
</linearGradient>
</defs>
<rect x="0" y="0" width="200" height="100"
style="fill: url(#three_stops); stroke: black;"/>
</svg>

After

Width:  |  Height:  |  Size: 427 B

View file

@ -0,0 +1,11 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" width="220" height="200" version="1.1">
<defs>
<radialGradient id="grey_blue" cx="20%" cy="40%" r="50%" fx="50%" fy="50%">
<stop offset="0%" style="stop-color:rgb(200,200,200);stop-opacity:0"/>
<stop offset="100%" style="stop-color:rgb(0,0,255);stop-opacity:1"/>
</radialGradient>
</defs>
<ellipse cx="110" cy="100" rx="110" ry="100" style="fill:url(#grey_blue)"/>
</svg>

After

Width:  |  Height:  |  Size: 552 B

View file

@ -0,0 +1,16 @@
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<radialGradient id="myRadialGradient4"
fx="5%" fy="5%" r="65%"
spreadMethod="pad">
<stop offset="0%" stop-color="#00ee00" stop-opacity="1"/>
<stop offset="100%" stop-color="#006600" stop-opacity="1" />
</radialGradient>
</defs>
<rect x="0" y="0" width="100" height="100" rx="10" ry="10"
style="fill:url(#myRadialGradient4);
stroke: #005000; stroke-width: 3;" />
</svg>

After

Width:  |  Height:  |  Size: 571 B

View file

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<radialGradient id="three_stops">
<stop offset="0%" style="stop-color: #f96;"/>
<stop offset="50%" style="stop-color: #9c9;"/>
<stop offset="100%" style="stop-color: #906;"/>
</radialGradient>
</defs>
<rect x="0" y="0" width="100" height="100"
style="fill: url(#three_stops); stroke: black;"/>
</svg>

After

Width:  |  Height:  |  Size: 417 B

View file

@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<radialGradient id="center_origin"
cx="0" cy="0" r="100%">
<stop offset="0%" style="stop-color: #f96;"/>
<stop offset="50%" style="stop-color: #9c9;"/>
<stop offset="100%" style="stop-color: #906;"/>
</radialGradient>
</defs>
<rect x="0" y="0" width="100" height="100"
style="fill: url(#center_origin); stroke: black;"/>
</svg>

After

Width:  |  Height:  |  Size: 448 B

View file

@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<radialGradient id="focal_set"
cx="0" cy="0" fx="30%" fy="30%" r="100%">
<stop offset="0%" style="stop-color: #f96;"/>
<stop offset="50%" style="stop-color: #9c9;"/>
<stop offset="100%" style="stop-color: #906;"/>
</radialGradient>
</defs>
<rect x="0" y="0" width="100" height="100"
style="fill: url(#focal_set); stroke: black;"/>
</svg>

After

Width:  |  Height:  |  Size: 458 B

View file

@ -0,0 +1,12 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" width="360" height="200" xmlns="http://www.w3.org/2000/svg">
<defs>
<radialGradient id="grey_blue" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
<stop offset="0%" style="stop-color:rgb(200,200,200); stop-opacity:0"/>
<stop offset="100%" style="stop-color:rgb(0,0,255); stop-opacity:1"/>
</radialGradient>
</defs>
<ellipse cx="180" cy="100" rx="180" ry="100" style="fill:url(#grey_blue)"/>
</svg>

After

Width:  |  Height:  |  Size: 555 B

View file

@ -0,0 +1,13 @@
<?xml version="1.0" standalone="no" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" width="220" height="300" version="1.1">
<defs>
<radialGradient id="grey_blue" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
<stop offset="0%" style="stop-color:red;stop-opacity:0.8"/>
<stop offset="30%" style="stop-color:#0000ff;"/>
<stop offset="60%" style="stop-color:rgba(0,153,153,0.5);"/>
<stop offset="100%" style="stop-color:blue;stop-opacity:1"/>
</radialGradient>
</defs>
<ellipse cx="110" cy="150" rx="110" ry="150" style="fill:url(#grey_blue)"/>
</svg>

After

Width:  |  Height:  |  Size: 648 B

View file

@ -0,0 +1,11 @@
<?xml version="1.0" standalone="no" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" width="220" height="200" version="1.1">
<defs>
<radialGradient id="grey_blue" cx="50%" cy="50%" r="50%" fx="20%" fy="40%">
<stop offset="0%" style="stop-color:gray;stop-opacity:0"/>
<stop offset="100%" style="stop-color:blue;stop-opacity:1"/>
</radialGradient>
</defs>
<ellipse cx="110" cy="100" rx="110" ry="100" style="fill:url(#grey_blue)"/>
</svg>

After

Width:  |  Height:  |  Size: 533 B

View file

@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" width="220" height="200" version="1.1">
<defs>
<radialGradient id="grey_blue" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
<stop offset="0%" style="stop-color:#808080;stop-opacity:0"></stop>
<stop offset="50%" style="stop-color:#009999;stop-opacity:0.6"></stop>
<stop offset="100%" style="stop-color:#0000FF;stop-opacity:1"></stop>
</radialGradient>
</defs>
<ellipse cx="110" cy="100" rx="110" ry="100" style="fill:url(#grey_blue)"></ellipse>
</svg>

After

Width:  |  Height:  |  Size: 495 B

View file

@ -0,0 +1,11 @@
<?xml version="1.0" standalone="no"?>
<svg width="100" height="100" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<defs>
<radialGradient id="Gradient1">
<stop offset="0%" stop-color="red"/>
<stop offset="100%" stop-color="blue"/>
</radialGradient>
</defs>
<rect x="0" y="0" rx="15" ry="15" width="100" height="100" fill="url(#Gradient1)"/>
</svg>

After

Width:  |  Height:  |  Size: 386 B

View file

@ -0,0 +1,11 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" width="220" height="200" version="1.1">
<defs>
<radialGradient id="grey_blue" cx="20%" cy="40%" r="50%" fx="50%" fy="50%">
<stop offset="0%" style="stop-color:rgb(200,200,200);stop-opacity:0"/>
<stop offset="100%" style="stop-color:rgba(0,120,120,0.2);stop-opacity:1"/>
</radialGradient>
</defs>
<ellipse cx="110" cy="100" rx="110" ry="100" style="fill:url(#grey_blue)"/>
</svg>

After

Width:  |  Height:  |  Size: 559 B

View file

@ -0,0 +1,10 @@
<?xml version="1.0" standalone="no"?>
<svg width="100" height="100" version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<radialGradient id="Gradient2" cx="25%" cy="25%" r="25%">
<stop offset="0%" stop-color="red"/>
<stop offset="100%" stop-color="blue"/>
</radialGradient>
</defs>
<rect x="0" y="0" rx="15" ry="15" width="100" height="100" fill="url(#Gradient2)"/>
</svg>

After

Width:  |  Height:  |  Size: 404 B

View file

@ -7,9 +7,11 @@
<desc>Example stroke01 - stroke-width</desc>
<g fill="none" stroke="black">
<path stroke-width="1" d="M5 5 l215 0" />
<path stroke-width="1" d="M5 9.5 l215 0" />
<path stroke-width="2" d="M5 20 l215 0" />
<path stroke-width="4" d="M5 40 l215 0" />
<path stroke-width="6" d="M5 60 l215 0" />
<path style="stroke: #000; stroke-width: 10;" d="M5 80 l215 0" />
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 541 B

After

Width:  |  Height:  |  Size: 639 B

View file

@ -7,9 +7,9 @@
<desc>Example stroke5 - stroke-dasharray</desc>
<g fill="none" stroke="black" stroke-width="3">
<path stroke-dasharray="5,5" d="M5 20 l215 0"/>
<path stroke-dasharray="10,10" d="M5 40 l215 0"/>
<path stroke-dasharray="15,10,5,5,5,10" d="M5 60 l215 0"/>
<path style="stroke-dasharray:10,10,2.5,10;" d="M5 80 l215 0"/>
<path stroke-dasharray="5,5" d="M5 20.5 l215 0"/>
<path stroke-dasharray="10,10" d="M5 40.5 l215 0"/>
<path stroke-dasharray="15,10,5,5,5,10" d="M5 60.5 l215 0"/>
<path style="stroke-dasharray:10,10,2.5,10;" d="M5 80.5 l215 0"/>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 576 B

After

Width:  |  Height:  |  Size: 585 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 595 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 703 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 754 B

After

Width:  |  Height:  |  Size: 777 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1,006 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 778 B

After

Width:  |  Height:  |  Size: 802 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 947 B

After

Width:  |  Height:  |  Size: 943 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 811 B

After

Width:  |  Height:  |  Size: 755 B

View file

@ -0,0 +1,46 @@
<html>
<body>
<input type="file" id="fileselect" name="fileselect[]" multiple="multiple" />
<div id="c" ></div>
</body>
<script>
var div = document.getElementById('c');
var fileselect = document.getElementById('fileselect');
function downloadHandler(e) {
var url = e.target.toDataURL();
var a = document.createElement('a');
a.href = url;
a.download = e.target.getAttribute('file-name').replace('.svg', '.png');
a.click();
}
function handleFile(f) {
var reader = new FileReader();
var canvas = document.createElement('canvas');
div.appendChild(canvas)
reader.onload = function(e) {
var image = new Image();
image.onload = function() {
canvas.setAttribute('file-name', f.name)
canvas.addEventListener('click', downloadHandler);
canvas.width = image.width;
canvas.height = image.height;
canvas.getContext('2d').drawImage(image, 0, 0);
}
image.src = e.target.result;
}
reader.readAsDataURL(f);
}
function FileSelectHandler(e) {
var files = e.target.files || e.dataTransfer.files;
// process all File objects
for (var i = 0, f; f = files[i]; i++) {
handleFile(f);
}
}
fileselect.addEventListener("change", FileSelectHandler, false);
</script>
</html>

View file

@ -1,10 +1,13 @@
(function(global) {
var fs = global.fs;
var pixelmatch = global.pixelmatch;
(function() {
var _pixelMatch = pixelmatch;
if (fabric.isLikelyNode) {
var fs = global.fs;
_pixelMatch = global.pixelmatch;
}
var fabricCanvas = this.canvas = new fabric.Canvas(null, {enableRetinaScaling: false, renderOnAddRemove: false});
var pixelmatchOptions = {
includeAA: true,
threshold: 0.05,
threshold: 0.1
};
function getAbsolutePath(path) {
@ -17,19 +20,25 @@
return src;
}
function getAsset(filename) {
function getAsset(filename, callback) {
var finalName = '/assets/' + filename + '.svg';
if (fabric.isLikelyNode) {
var path = (__dirname + finalName);
return fs.readFileSync(path, { encoding: 'utf8' });
} else {
return fs.readFile(path, { encoding: 'utf8' }, callback);
}
else {
var path = getAbsolutePath('test/visual' + finalName);
fabric.util.request(path, {
onComplete: function(xhr) {
callback(null, xhr.responseText);
}
});
}
}
function getGolden(filename) {
var finalName = '/golden/' + filename + '.png';
return fabric.isLikelyNode ? (__dirname + finalName) : getAbsolutePath(finalName);
return fabric.isLikelyNode ? (__dirname + finalName) : getAbsolutePath('test/visual' + finalName);
}
function getGoldenImage(filename, callback) {
@ -58,11 +67,13 @@
var goldenImageData = ctx.getImageData(0, 0, width, height).data;
ctx.clearRect(0, 0, width, height);
var outputImageData = ctx.getImageData(0, 0, width, height).data;
fabric.loadSVGFromString(getAsset(filename), function(objects) {
fabricCanvas.add.apply(fabricCanvas, objects);
fabricCanvas.renderAll();
var fabricImageData = fabricCanvas.contextContainer.getImageData(0, 0, width, height).data;
callback(fabricImageData, goldenImageData, width, height, outputImageData);
getAsset(filename, function(err, string) {
fabric.loadSVGFromString(string, function(objects) {
fabricCanvas.add.apply(fabricCanvas, objects);
fabricCanvas.renderAll();
var fabricImageData = fabricCanvas.contextContainer.getImageData(0, 0, width, height).data;
callback(fabricImageData, goldenImageData, width, height, outputImageData);
});
});
});
}
@ -75,24 +86,48 @@
});
[
'svg_stroke_1',
'svg_stroke_2',
'svg_stroke_3',
'svg_stroke_4',
'svg_stroke_5',
'svg_stroke_6',
'svg_stroke_7',
'svg_stroke_8',
].forEach(function(filename) {
['svg_stroke_1', 0],
['svg_stroke_2', 0],
['svg_stroke_3', 0],
['svg_stroke_4', 8],
['svg_stroke_5', 4],
['svg_stroke_6', 83],
['svg_stroke_7', 0],
['svg_stroke_8', 0],
['svg_linear_1', 0],
['svg_linear_2', 0],
['svg_linear_3', 0],
['svg_linear_4', 14],
['svg_linear_5', 8],
['svg_linear_6', 83],
['svg_linear_7', 0],
['svg_linear_8', 0],
['svg_radial_1', 100],
['svg_radial_2', 0],
['svg_radial_3', 0],
['svg_radial_4', 143],
['svg_radial_5', 143],
['svg_radial_6', 8],
['svg_radial_8', 0],
['svg_radial_9', 8],
['svg_radial_10', 12],
['svg_radial_11', 0],
['svg_radial_12', 8],
['svg_radial_13', 4],
].forEach(function(filenameArray) {
var filename = filenameArray[0];
var expectedPixels = filenameArray[1];
QUnit.test('Import test for file ' + filename, function(assert) {
var done = assert.async();
loadAndPrepareCanvasFor(filename, function(imageDataCanvas, imageDataGolden, width, height, output) {
var totalPixels = width * height;
var percentage = 0.01;
var differentPixels = pixelmatch(imageDataCanvas, imageDataGolden, output, width, height, pixelmatchOptions);
assert.ok(differentPixels < totalPixels * percentage, 'Image ' + filename + ' has too many different pixels ' + differentPixels + ' representing ' + differentPixels / totalPixels * 100 + '%');
var differentPixels = _pixelMatch(imageDataCanvas, imageDataGolden, output, width, height, pixelmatchOptions);
var percDiff = differentPixels / totalPixels * 100;
assert.ok(differentPixels < totalPixels * percentage, 'Image ' + filename + ' has too many different pixels ' + differentPixels + ' representing ' + percDiff + '%');
done();
console.log('Different pixels for', filename, ':', differentPixels, '/', totalPixels, 'expected:', expectedPixels, ' diff:', percDiff.toFixed(3), '%');
});
});
});
})(global);
})();

34
testem-visual.json Normal file
View file

@ -0,0 +1,34 @@
{
"framework": "qunit",
"serve_files": [
"dist/fabric.js",
"test/lib/pixelmatch.js",
"test/visual/*.js"
],
"routes": {
"/fixtures": "test/fixtures"
},
"test_page": "tests.mustache?hidepassed&hideskipped&timeout=60000",
"browser_args": {
"Chrome": [ "--headless", "--disable-gpu", "--remote-debugging-port=9222" ],
"Firefox": [ "--headless" ]
},
"launch_in_dev": [
"Chrome",
"Node",
"Firefox"
],
"launch_in_ci": [
"Chrome",
"Node",
"Firefox"
],
"launchers": {
"Node": {
"command": "npm run test:visual",
"protocol": "tap"
}
},
"timeout": 540,
"parallel": 4
}

View file

@ -1,7 +1,7 @@
<!doctype html>
<html>
<head>
<title>Test'em</title>
<title>Unit Tests</title>
<script src="/node_modules/qunit/qunit/qunit.js"></script>
<script src="/testem.js"></script>
{{#serve_files}}<script src="{{{src}}}"{{#attrs}} {{&.}}{{/attrs}}></script>{{/serve_files}}