Tests: Start getting QUnit tests to work on Node.js

Work in progress…
This commit is contained in:
Jürg Lehni 2016-01-27 19:57:07 +01:00
parent 0c1b4376d3
commit 4c84c3dad5
44 changed files with 322 additions and 176 deletions

View file

@ -18,4 +18,5 @@ addons:
script: script:
- npm run lint - npm run lint
- gulp minify - gulp minify
- gulp test - gulp test:browser
- gulp test:node

View file

@ -11,9 +11,68 @@
*/ */
var gulp = require('gulp'), var gulp = require('gulp'),
qunit = require('gulp-qunit'); gulp_qunit = require('gulp-qunit'),
node_qunit = require('qunit'),
gutil = require('gulp-util'),
extend = require('extend'),
minimist = require('minimist');
gulp.task('test', function() { // Support simple command line options to pass on to test:node, to display
return gulp.src('test/index.html') // errors selectively, e.g.:
.pipe(qunit({ timeout: 20, noGlobals: true })); // gulp test:node --assertions
var options = minimist(process.argv.slice(2), {
boolean: true
});
gulp.task('test', ['test:browser']);
gulp.task('test:browser', ['minify:acorn'], function() {
return gulp.src('test/index.html')
.pipe(gulp_qunit({ timeout: 20, noGlobals: true }));
});
gulp.task('test:node', ['minify:acorn'], function(callback) {
var name = 'node-qunit';
node_qunit.setup({
log: extend({ errors: true }, options)
});
// Use the correct working directory for tests:
process.chdir('./test');
node_qunit.run({
maxBlockDuration: 100 * 1000,
deps: [
// To dynamically load the tests files from the sources, we need to
// require Prepro.js first. Since we need a sub-module, we have to
// use relative addresses: require('prepro/lib/node') does not work
// because of the way node-qunit handles relative addresses.
'../node_modules/prepro/lib/node.js',
// Note that loading dist/paper-full.js also works in combination
// with `gulp load`, in which case Prepro.js is present and handles
// the loading transparently.
{ path: '../dist/paper-full.js', namespace: 'paper' }
],
// Now load the actual test files through test/load.js, using Prepro.js
// for the loading, which was requested above.
code: 'load.js'
}, function(err, stats) {
var result;
if (err) {
result = new gutil.PluginError(name, err);
} else {
// Imitate the way gulp-qunit formats results and errors.
var color = gutil.colors[stats.failed > 0 ? 'red' : 'green'];
gutil.log('Took ' + stats.runtime + ' ms to run ' +
gutil.colors.blue(stats.assertions) + ' tests. ' +
color(stats.passed + ' passed, ' + stats.failed + ' failed.'));
if (stats.failed > 0) {
err = 'QUnit assertions failed';
gutil.log(name + ': ' + gutil.colors.red('✖ ') + err);
result = new gutil.PluginError(name, err);
} else {
gutil.log(name + ': ' + gutil.colors.green('✔ ') +
'QUnit assertions all passed');
}
}
callback(result);
});
}); });

View file

@ -18,7 +18,7 @@ gulp.on('error', function(err) {
var msg = err.toString(); var msg = err.toString();
if (msg === '[object Object]') if (msg === '[object Object]')
msg = err; msg = err;
gutil.log(ERROR, err); gutil.log(ERROR, msg);
if (err.stack) if (err.stack)
gutil.log(ERROR, err.stack); gutil.log(ERROR, err.stack);
this.emit('end'); this.emit('end');

View file

@ -25,7 +25,7 @@
"README.md" "README.md"
], ],
"engines": { "engines": {
"node": ">=0.8.0 <5.0.0" "node": ">=0.8.0 <6.0.0"
}, },
"dependencies": { "dependencies": {
"jsdom": "git://github.com/lehni/jsdom.git#3d55789d0f4d55392721b1e22890837fde472375", "jsdom": "git://github.com/lehni/jsdom.git#3d55789d0f4d55392721b1e22890837fde472375",
@ -42,7 +42,7 @@
"gulp": "^3.9.0", "gulp": "^3.9.0",
"gulp-cached": "^1.1.0", "gulp-cached": "^1.1.0",
"gulp-jshint": "^2.0.0", "gulp-jshint": "^2.0.0",
"gulp-prepro": "^2.0.0", "gulp-prepro": "^2.1.0",
"gulp-qunit": "git://github.com/lehni/gulp-qunit.git#459c5603ceac460327a40dc89df6f19c786dc61b", "gulp-qunit": "git://github.com/lehni/gulp-qunit.git#459c5603ceac460327a40dc89df6f19c786dc61b",
"gulp-rename": "^1.2.2", "gulp-rename": "^1.2.2",
"gulp-rimraf": "^0.2.0", "gulp-rimraf": "^0.2.0",
@ -56,7 +56,9 @@
"jshint": "2.8.x", "jshint": "2.8.x",
"jshint-summary": "^0.4.0", "jshint-summary": "^0.4.0",
"merge-stream": "^1.0.0", "merge-stream": "^1.0.0",
"prepro": "^2.0.0", "minimist": "^1.2.0",
"prepro": "^2.1.0",
"qunit": "^0.7.7",
"qunitjs": "^1.20.0", "qunitjs": "^1.20.0",
"require-dir": "^0.3.0", "require-dir": "^0.3.0",
"resemblejs": "^2.1.0", "resemblejs": "^2.1.0",

View file

@ -45,7 +45,7 @@ if (typeof window === 'object') {
} }
} else { } else {
// Node.js based loading through Prepro.js: // Node.js based loading through Prepro.js:
var prepro = require('prepro/lib/node.js'), var prepro = require('prepro/lib/node'),
// Load the default browser-based options for further amendments. // Load the default browser-based options for further amendments.
// Step out and back into src, in case this is loaded from // Step out and back into src, in case this is loaded from
// dist/paper-node.js // dist/paper-node.js

View file

@ -10,26 +10,42 @@
* All rights reserved. * All rights reserved.
*/ */
// Until window.history.pushState() works when running locally, we need to trick var isNode = typeof global === 'object',
// qunit into thinking that the feature is not present. This appears to work... root;
// TODO: Ideally we should fix this in QUnit instead.
delete window.history;
window.history = {};
QUnit.begin(function() { if (isNode) {
if (QUnit.urlParams.hidepassed) { root = global;
document.getElementById('qunit-tests').className += ' hidepass'; // Resemble.js needs the Image constructor this global.
} global.Image = paper.window.Image;
resemble.outputSettings({ } else {
errorColor: { root = window;
red: 255, // This is only required when running in the browser:
green: 51, // Until window.history.pushState() works when running locally, we need to
blue: 0 // trick qunit into thinking that the feature is not present. This appears
}, // to work...
errorType: 'flat', // TODO: Ideally we should fix this in QUnit instead.
transparency: 1 delete window.history;
window.history = {};
QUnit.begin(function() {
if (QUnit.urlParams.hidepassed) {
document.getElementById('qunit-tests').className += ' hidepass';
}
resemble.outputSettings({
errorColor: {
red: 255,
green: 51,
blue: 0
},
errorType: 'flat',
transparency: 1
});
}); });
}); }
// The unit-tests expect the paper classes to be global.
if (!('Base' in root))
paper.install(root);
var errorHandler = console.error; var errorHandler = console.error;
console.error = function() { console.error = function() {
@ -37,8 +53,48 @@ console.error = function() {
errorHandler.apply(this, arguments); errorHandler.apply(this, arguments);
}; };
// NOTE: In order to "export" all methods into the shared Prepro.js scope when
// using node-qunit, we need to define global functions as:
// `var name = function() {}`. `function name() {}` does not work!
var currentProject;
var test = function(testName, expected) {
var parameters = expected.toString().match(/^\s*function[^\(]*\(([^\)]*)/)[1];
// If this is running on an older version of QUnit (e.g. node-qunit is stuck
// with v1.10 for now), emulate the new assert.async() syntax through
// QUnit.asyncTest() and QUnit.start();
if (!QUnit.async && parameters === 'assert') {
return QUnit.asyncTest(testName, function() {
// Since tests may be asynchronous, remove the old project before
// running the next test.
if (currentProject)
currentProject.remove();
currentProject = new Project();
// Pass a fake assert object with just the functions that we need,
// so far a async() function returning a done() function:
expected({
async: function() {
return function() {
QUnit.start();
};
}
});
});
} else {
return QUnit.test(testName, function(assert) {
// Since tests may be asynchronous, remove the old project before
// running the next test.
if (currentProject)
currentProject.remove();
currentProject = new Project();
expected(assert);
});
}
};
// Override equals to convert functions to message and execute them as tests() // Override equals to convert functions to message and execute them as tests()
function equals(actual, expected, message, options) { var equals = function(actual, expected, message, options) {
// Allow the use of functions for actual, which will get called and their // Allow the use of functions for actual, which will get called and their
// source content extracted for readable reports. // source content extracted for readable reports.
if (typeof actual === 'function') { if (typeof actual === 'function') {
@ -55,7 +111,7 @@ function equals(actual, expected, message, options) {
|| type === 'boolean' && 'Boolean' || type === 'boolean' && 'Boolean'
|| type === 'undefined' && 'Undefined' || type === 'undefined' && 'Undefined'
|| Array.isArray(expected) && 'Array' || Array.isArray(expected) && 'Array'
|| expected instanceof Element && 'Element' // handle DOM Elements || expected instanceof window.Element && 'Element' // handle DOM Elements
|| (cls = expected && expected._class) // check _class 2nd last || (cls = expected && expected._class) // check _class 2nd last
|| type === 'object' && 'Object'; // Object as catch-all || type === 'object' && 'Object'; // Object as catch-all
var comparator = type && comparators[type]; var comparator = type && comparators[type];
@ -76,7 +132,40 @@ function equals(actual, expected, message, options) {
actual, identical ? expected : 'not ' + expected, actual, identical ? expected : 'not ' + expected,
message + ': identical after cloning'); message + ': identical after cloning');
} }
} };
// A list of classes that should be identical after their owners were cloned.
var identicalAfterCloning = {
Gradient: true,
Symbol: true
};
var getFunctionMessage = function(func) {
var message = func.toString().match(
/^\s*function[^\{]*\{([\s\S]*)\}\s*$/)[1]
.replace(/ /g, '')
.replace(/^\s+|\s+$/g, '');
if (/^return /.test(message)) {
message = message
.replace(/^return /, '')
.replace(/;$/, '');
}
return message;
};
var createSVG = function(str, attrs) {
if (attrs) {
// Similar to SVGExport's createElement / setAttributes.
var node = document.createElementNS('http://www.w3.org/2000/svg', str);
for (var key in attrs)
node.setAttribute(key, attrs[key]);
return node;
} else {
return new window.DOMParser().parseFromString(
'<svg xmlns="http://www.w3.org/2000/svg">' + str + '</svg>',
'text/xml');
}
};
// Register a jsDump parser for Base. // Register a jsDump parser for Base.
QUnit.jsDump.setParser('Base', function (obj, stack) { QUnit.jsDump.setParser('Base', function (obj, stack) {
@ -95,14 +184,15 @@ QUnit.jsDump.setParser('object', function (obj, stack) {
: objectParser).call(this, obj, stack); : objectParser).call(this, obj, stack);
}); });
function compareProperties(actual, expected, properties, message, options) { var compareProperties = function(actual, expected, properties, message, options) {
for (var i = 0, l = properties.length; i < l; i++) { for (var i = 0, l = properties.length; i < l; i++) {
var key = properties[i]; var key = properties[i];
equals(actual[key], expected[key], message + '.' + key, options); equals(actual[key], expected[key], message + '.' + key, options);
} }
} };
function compareItem(actual, expected, message, options, properties) { var compareItem = function(actual, expected, message, options, properties) {
options = options || {};
function rasterize(item, group, resolution) { function rasterize(item, group, resolution) {
var raster = null; var raster = null;
@ -119,7 +209,7 @@ function compareItem(actual, expected, message, options, properties) {
+ '" src="' + raster.source + '">'; + '" src="' + raster.source + '">';
} }
if (options && options.rasterize) { if (options.rasterize) {
// In order to properly compare pixel by pixel, we need to put each item // In order to properly compare pixel by pixel, we need to put each item
// into a group with a white background of the united dimensions of the // into a group with a white background of the united dimensions of the
// bounds of both items before rasterizing. // bounds of both items before rasterizing.
@ -159,11 +249,15 @@ function compareItem(actual, expected, message, options, properties) {
.compareTo(expected.getImageData()) .compareTo(expected.getImageData())
// When working with imageData, this call is synchronous: // When working with imageData, this call is synchronous:
.onComplete(function(data) { result = data; }); .onComplete(function(data) { result = data; });
var identical = result ? 100 - result.misMatchPercentage : 0, var tolerance = (options.tolerance || 1e-4) * 100, // percentages...
ok = identical == 100, fixed = ((1 / tolerance) + '').length - 1,
text = identical.toFixed(2) + '% identical'; identical = result ? 100 - result.misMatchPercentage : 0,
QUnit.push(ok, text, '100.00% identical', message); reached = identical.toFixed(fixed),
if (!ok && result) { hundred = (100).toFixed(fixed),
ok = reached == hundred;
QUnit.push(ok, reached + '% identical', hundred + '% identical',
message);
if (!ok && result && !isNode) {
// Get the right entry for this unit test and assertion, and // Get the right entry for this unit test and assertion, and
// replace the results with images // replace the results with images
var entry = document.getElementById('qunit-test-output-' + id) var entry = document.getElementById('qunit-test-output-' + id)
@ -179,14 +273,14 @@ function compareItem(actual, expected, message, options, properties) {
} }
} }
} else { } else {
if (options && options.cloned) if (options.cloned)
QUnit.notStrictEqual(actual.id, expected.id, QUnit.notStrictEqual(actual.id, expected.id,
'not ' + message + '.id'); 'not ' + message + '.id');
QUnit.strictEqual(actual.constructor, expected.constructor, QUnit.strictEqual(actual.constructor, expected.constructor,
message + '.constructor'); message + '.constructor');
// When item is cloned and has a name, the name will be versioned: // When item is cloned and has a name, the name will be versioned:
equals(actual.name, equals(actual.name,
options && options.cloned && expected.name options.cloned && expected.name
? expected.name + ' 1' : expected.name, ? expected.name + ' 1' : expected.name,
message + '.name'); message + '.name');
compareProperties(actual, expected, ['children', 'bounds', 'position', compareProperties(actual, expected, ['children', 'bounds', 'position',
@ -201,7 +295,7 @@ function compareItem(actual, expected, message, options, properties) {
'dashOffset', 'miterLimit', 'fontSize', 'font', 'leading', 'dashOffset', 'miterLimit', 'fontSize', 'font', 'leading',
'justification'], message + '.style', options); 'justification'], message + '.style', options);
} }
} };
// A list of comparator functions, based on `expected` type. See equals() for // A list of comparator functions, based on `expected` type. See equals() for
// an explanation of how the type is determined. // an explanation of how the type is determined.
@ -359,56 +453,3 @@ var comparators = {
message, options); message, options);
} }
}; };
// A list of classes that should be identical after their owners were cloned.
var identicalAfterCloning = {
Gradient: true,
Symbol: true
};
function getFunctionMessage(func) {
var message = func.toString().match(
/^\s*function[^\{]*\{([\s\S]*)\}\s*$/)[1]
.replace(/ /g, '')
.replace(/^\s+|\s+$/g, '');
if (/^return /.test(message)) {
message = message
.replace(/^return /, '')
.replace(/;$/, '');
}
return message;
}
function test(testName, expected) {
return QUnit.test(testName, function() {
var project = new Project();
expected();
project.remove();
});
}
function asyncTest(testName, expected) {
return QUnit.asyncTest(testName, function() {
var project = new Project();
expected(function() {
project.remove();
QUnit.start();
});
});
}
// SVG
function createSVG(str, attrs) {
if (attrs) {
// Similar to SVGExport's createElement / setAttributes.
var node = document.createElementNS('http://www.w3.org/2000/svg', str);
for (var key in attrs)
node.setAttribute(key, attrs[key]);
return node;
} else {
return new window.DOMParser().parseFromString(
'<svg xmlns="http://www.w3.org/2000/svg">' + str + '</svg>',
'text/xml');
}
}

View file

@ -3,11 +3,10 @@
<head> <head>
<title>Paper.js Tests</title> <title>Paper.js Tests</title>
<link rel="stylesheet" href="../node_modules/qunitjs/qunit/qunit.css"> <link rel="stylesheet" href="../node_modules/qunitjs/qunit/qunit.css">
<script type="text/javascript" src="../node_modules/qunitjs/qunit/qunit.js"></script> <script src="../node_modules/qunitjs/qunit/qunit.js"></script>
<script type="text/javascript" src="../node_modules/resemblejs/resemble.js"></script> <script src="../dist/paper-full.js"></script>
<script type="text/javascript" src="js/helpers.js"></script> <script src="../node_modules/prepro/lib/browser.js"></script>
<script type="text/javascript" src="../src/load.js"></script> <script src="load.js"></script>
<script type="text/javascript" src="tests/load.js"></script>
</head> </head>
<body> <body>
<h1 id="qunit-header">QUnit Test Suite</h1> <h1 id="qunit-header">QUnit Test Suite</h1>

15
test/load.js Normal file
View file

@ -0,0 +1,15 @@
/*
* Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
* http://paperjs.org/
*
* Copyright (c) 2011 - 2016, Juerg Lehni & Jonathan Puckey
* http://scratchdisk.com/ & http://jonathanpuckey.com/
*
* Distributed under the MIT license. See LICENSE file for details.
*
* All rights reserved.
*/
/*#*/ include('helpers.js');
/*#*/ include('../node_modules/resemblejs/resemble.js', { namespace: 'resemble' });
/*#*/ include('tests/load.js');

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('Color'); QUnit.module('Color');
test('Set named color', function() { test('Set named color', function() {
var path = new Path(); var path = new Path();
@ -222,6 +222,3 @@ test('Color#divide', function() {
var color = new Color(1, 1, 1); var color = new Color(1, 1, 1);
equals(color.divide(4), new Color([0.25, 0.25, 0.25])); equals(color.divide(4), new Color([0.25, 0.25, 0.25]));
}); });

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('Compound Path'); QUnit.module('Compound Path');
test('moveTo / lineTo', function() { test('moveTo / lineTo', function() {
var path = new CompoundPath(); var path = new CompoundPath();

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('Curve'); QUnit.module('Curve');
test('Curve#getParameterOf()', function() { test('Curve#getParameterOf()', function() {
// For issue #708: // For issue #708:

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('CurveLocation'); QUnit.module('CurveLocation');
test('CurveLocation#offset', function() { test('CurveLocation#offset', function() {
var path = new Path(); var path = new Path();

View file

@ -10,10 +10,10 @@
* All rights reserved. * All rights reserved.
*/ */
module('Emitter'); QUnit.module('Emitter');
test('on()', function() { test('on()', function() {
var emitter = new Base(Emitter), var emitter = new Item(),
installed; installed;
// fake event type registration // fake event type registration
emitter._eventTypes = {mousemove: {install: function(){ installed = true;} } }; emitter._eventTypes = {mousemove: {install: function(){ installed = true;} } };
@ -37,7 +37,7 @@ test('on()', function() {
}); });
test('off()', function() { test('off()', function() {
var emitter = new Base(Emitter), var emitter = new Item(),
uninstalled, called = 0, uninstalled, called = 0,
handler = function () {called++}, handler = function () {called++},
handler2 = function () {}; handler2 = function () {};
@ -68,7 +68,7 @@ test('off()', function() {
}); });
test('emit()', function() { test('emit()', function() {
var emitter = new Base(Emitter), var emitter = new Item(),
called, called,
handler = function (e) {called = e}; handler = function (e) {called = e};
// fake event type registration // fake event type registration

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('Group'); QUnit.module('Group');
test('new Group()', function() { test('new Group()', function() {
var group = new Group(); var group = new Group();

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('HitResult'); QUnit.module('HitResult');
test('Hit-testing options', function() { test('Hit-testing options', function() {
var defaultOptions = { var defaultOptions = {

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('Item'); QUnit.module('Item');
test('copyTo(project)', function() { test('copyTo(project)', function() {
var project = paper.project; var project = paper.project;

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('Item Bounds'); QUnit.module('Item Bounds');
test('item.bounds caching', function() { test('item.bounds caching', function() {
var circle = new Path.Circle(new Point(100, 100), 50); var circle = new Path.Circle(new Point(100, 100), 50);
@ -94,5 +94,5 @@ test('text.bounds', function() {
var text = new PointText(new Point(50, 100)); var text = new PointText(new Point(50, 100));
text.fillColor = 'black'; text.fillColor = 'black';
text.content = 'This is a test'; text.content = 'This is a test';
equals(text.bounds, new Rectangle(50, 89.2, 67, 14.4), 'text.bounds', { tolerance: 0.5 }); equals(text.bounds, new Rectangle(50, 89.2, 67, 14.4), 'text.bounds', { tolerance: 1 });
}); });

View file

@ -10,6 +10,8 @@
* All rights reserved. * All rights reserved.
*/ */
QUnit.module('Item Cloning');
function cloneAndCompare(item) { function cloneAndCompare(item) {
var copy = item.clone(); var copy = item.clone();
equals(function() { equals(function() {

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('Getting and Matching Items'); QUnit.module('Getting and Matching Items');
test('Item#getItems()', function() { test('Item#getItems()', function() {
var group = new Group([new Path({ selected: true }), new Raster()]); var group = new Group([new Path({ selected: true }), new Raster()]);
@ -72,14 +72,14 @@ test('Project#getItems()', function() {
className: 'Group' className: 'Group'
}); });
equals(function() { equals(function() {
return matches.length == 1 && matches[0] === group return matches.length == 1 && matches[0] === group;
}, true); }, true);
var matches = paper.project.getItems({ var matches = paper.project.getItems({
type: 'group' type: 'group'
}); });
equals(function() { equals(function() {
return matches.length == 1 && matches[0] === group return matches.length == 1 && matches[0] === group;
}, true); }, true);
var raster = new Raster(); var raster = new Raster();
@ -87,7 +87,7 @@ test('Project#getItems()', function() {
class: Raster class: Raster
}); });
equals(function() { equals(function() {
return matches.length == 1 && matches[0] === raster return matches.length == 1 && matches[0] === raster;
}, true); }, true);
equals(function() { equals(function() {
@ -120,7 +120,7 @@ test('Project#getItems() with compare function', function() {
var items = paper.project.getItems({ var items = paper.project.getItems({
opacity: function(value) { opacity: function(value) {
return value < 1 return value < 1;
} }
}); });
equals(function() { equals(function() {
@ -162,23 +162,26 @@ test('Project#getItems() with color', function() {
}); });
test('Project#getItems() with regex function', function() { test('Project#getItems() with regex function', function() {
var decoyPath = new Path({ var layer = paper.project.activeLayer;
var stopPath = new Path({
name: 'stop' name: 'stop'
}); });
var decoyPath2 = new Path({ var pausePath = new Path({
name: 'pause' name: 'pause'
}); });
var path = new Path({ var startPath = new Path({
name: 'starting' name: 'starting'
}); });
var items = paper.project.getItems({ var items = paper.project.getItems({
name: /^start/g name: /^start/g
}); });
// console.log(paper.project.activeLayer);
equals(function() { equals(function() {
return items.length == 1 && items[0] == path; return items.length == 1 && items[0] == startPath;
}, true); }, true);
equals(function() { equals(function() {

View file

@ -10,6 +10,8 @@
* All rights reserved. * All rights reserved.
*/ */
QUnit.module('Item Order');
test('Item Order', function() { test('Item Order', function() {
var line = new Path(); var line = new Path();
line.add([0, 0], [100, 100]); line.add([0, 0], [100, 100]);

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('JSON'); QUnit.module('JSON');
function testExportImportJSON(project) { function testExportImportJSON(project) {
// Use higher precision than in comparissons, for bounds // Use higher precision than in comparissons, for bounds

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('Layer'); QUnit.module('Layer');
test('previousSibling / nextSibling', function() { test('previousSibling / nextSibling', function() {
var project = paper.project; var project = paper.project;

View file

@ -10,7 +10,8 @@
* All rights reserved. * All rights reserved.
*/ */
module('Matrix'); QUnit.module('Matrix');
test('Decomposition: rotate()', function() { test('Decomposition: rotate()', function() {
function testAngle(a, ea) { function testAngle(a, ea) {
var m = new Matrix().rotate(a), var m = new Matrix().rotate(a),

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('Path'); QUnit.module('Path');
test('path.join(path)', function() { test('path.join(path)', function() {
var path = new Path(); var path = new Path();

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('PathItem Contains'); QUnit.module('PathItem Contains');
function testPoint(item, point, inside, message) { function testPoint(item, point, inside, message) {
equals(item.contains(point), inside, message || ('The point ' + point equals(item.contains(point), inside, message || ('The point ' + point

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('Path Boolean Operations'); QUnit.module('Path Boolean Operations');
function createPath(str) { function createPath(str) {
var ctor = (str.match(/z/gi) || []).length > 1 ? CompoundPath : Path; var ctor = (str.match(/z/gi) || []).length > 1 ? CompoundPath : Path;
@ -30,7 +30,7 @@ function compareBoolean(actual, expected, message, options) {
strokeColor: 'black', strokeColor: 'black',
fillColor: expected.closed ? 'yellow' : null fillColor: expected.closed ? 'yellow' : null
}; };
equals(actual, expected, message, options || { rasterize: true }); equals(actual, expected, message, Base.set({ rasterize: true }, options));
} }
test('#541', function() { test('#541', function() {
@ -884,7 +884,8 @@ test('Isolated edge-cases from @iconexperience\'s boolean-test suite', function(
closed: true closed: true
}); });
compareBoolean(function() { return path1.unite(); }, compareBoolean(function() { return path1.unite(); },
'M428.65987,123.24313c0,0 18.24445,159.97772 20.21157,166.76806c-3.05664,-6.18082 -73.53131,-139.25432 -73.53131,-139.25432z M448.97323,290.23336c0,0 0,0 0,0c0.22704,0.04317 -0.06896,-0.00471 0,0c-0.02659,-0.00506 -0.06063,-0.08007 -0.1018,-0.22217c0.07286,0.14733 0.10741,0.22256 0.1018,0.22217z'); 'M428.65987,123.24313c0,0 18.24445,159.97772 20.21157,166.76806c-3.05664,-6.18082 -73.53131,-139.25432 -73.53131,-139.25432z M448.97323,290.23336c0,0 0,0 0,0c0.22704,0.04317 -0.06896,-0.00471 0,0c-0.02659,-0.00506 -0.06063,-0.08007 -0.1018,-0.22217c0.07286,0.14733 0.10741,0.22256 0.1018,0.22217z',
null, { tolerance: 1e-3 });
// #784#issuecomment-168605018 // #784#issuecomment-168605018
var path1 = new CompoundPath(); var path1 = new CompoundPath();

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('Path Bounds'); QUnit.module('Path Bounds');
test('path.bounds', function() { test('path.bounds', function() {
var path = new Path([ var path = new Path([

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('Path Curves'); QUnit.module('Path Curves');
test('path.curves synchronisation', function() { test('path.curves synchronisation', function() {
var path = new Path(); var path = new Path();

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('Path Drawing Commands'); QUnit.module('Path Drawing Commands');
test('path.lineTo(point);', function() { test('path.lineTo(point);', function() {
var path = new Path(); var path = new Path();

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('Path Intersections'); QUnit.module('Path Intersections');
function testIntersection(intersections, results) { function testIntersection(intersections, results) {
equals(intersections.length, results.length, 'intersections.length'); equals(intersections.length, results.length, 'intersections.length');

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('Path Length'); QUnit.module('Path Length');
test('path.length', function() { test('path.length', function() {
var path = new Path([ var path = new Path([

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('Predefined Path Shapes'); QUnit.module('Predefined Path Shapes');
test('new Path.Rectangle([50, 50], [100, 100])', function() { test('new Path.Rectangle([50, 50], [100, 100])', function() {
var path = new Path.Rectangle([50, 50], [100, 100]); var path = new Path.Rectangle([50, 50], [100, 100]);

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('Symbol & Placed Symbol'); QUnit.module('Symbol & Placed Symbol');
test('placedSymbol bounds', function() { test('placedSymbol bounds', function() {
var path = new Path.Circle([50, 50], 50); var path = new Path.Circle([50, 50], 50);

View file

@ -10,7 +10,8 @@
* All rights reserved. * All rights reserved.
*/ */
module('Point'); QUnit.module('Point');
test('new Point(10, 20)', function() { test('new Point(10, 20)', function() {
var point = new Point(10, 20); var point = new Point(10, 20);
equals(point.x, 10, 'point.x'); equals(point.x, 10, 'point.x');
@ -44,8 +45,6 @@ test('new Point({ angle: 45, length: 20})', function() {
equals(point, new Point(15.32089, 12.85575)); equals(point, new Point(15.32089, 12.85575));
}); });
module('Point vector operations');
test('normalize(length)', function() { test('normalize(length)', function() {
var point = new Point(0, 10).normalize(20); var point = new Point(0, 10).normalize(20);
equals(point, new Point(0, 20)); equals(point, new Point(0, 20));

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('Project'); QUnit.module('Project');
test('activate()', function() { test('activate()', function() {
var project = new Project(); var project = new Project();

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('Raster'); QUnit.module('Raster');
test('Create a raster without a source and check its size', function() { test('Create a raster without a source and check its size', function() {
var raster = new Raster(); var raster = new Raster();
@ -23,23 +23,34 @@ test('Create a raster without a source and set its size', function() {
equals(raster.size, new Size(640, 480), true); equals(raster.size, new Size(640, 480), true);
}); });
asyncTest('Create a raster from a url', function(callback) { test('Create a raster from a url', function(assert) {
var done = assert.async();
var raster = new Raster('assets/paper-js.gif'); var raster = new Raster('assets/paper-js.gif');
raster.onLoad = function() { raster.onLoad = function() {
equals(raster.size, new Size(146, 146), true); equals(raster.size, new Size(146, 146), true);
callback(); done();
};
raster.onError = function(event) {
pushFailure(event.event);
done();
}; };
}); });
asyncTest('Create a raster from a data url', function(callback) { test('Create a raster from a data url', function(assert) {
var done = assert.async();
var raster = new Raster('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABlJREFUeNpi+s/AwPCfgYmR4f9/hv8AAQYAHiAFAS8Lwy8AAAAASUVORK5CYII='); var raster = new Raster('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABlJREFUeNpi+s/AwPCfgYmR4f9/hv8AAQYAHiAFAS8Lwy8AAAAASUVORK5CYII=');
raster.onLoad = function() { raster.onLoad = function() {
equals(raster.size, new Size(2, 2), true); equals(raster.size, new Size(2, 2), true);
callback(); done();
};
raster.onError = function(event) {
pushFailure(event.event);
done();
}; };
}); });
asyncTest('Create a raster from a dom image', function(callback) { test('Create a raster from a dom image', function(assert) {
var done = assert.async();
var img = document.createElement('img'); var img = document.createElement('img');
img.src = 'assets/paper-js.gif'; img.src = 'assets/paper-js.gif';
document.body.appendChild(img); document.body.appendChild(img);
@ -48,19 +59,19 @@ asyncTest('Create a raster from a dom image', function(callback) {
var raster = new Raster(img); var raster = new Raster(img);
equals(raster.size, new Size(146, 146), true); equals(raster.size, new Size(146, 146), true);
document.body.removeChild(img); document.body.removeChild(img);
callback(); done();
} }
}); });
}); });
test('Create a raster from a canvas', function(callback) { test('Create a raster from a canvas', function() {
var canvas = CanvasProvider.getCanvas(30, 20); var canvas = paper.createCanvas(30, 20);
var raster = new Raster(canvas); var raster = new Raster(canvas);
equals(raster.size, new Size(30, 20), true); equals(raster.size, new Size(30, 20), true);
CanvasProvider.release(canvas);
}); });
asyncTest('Create a raster from a dom id', function(callback) { test('Create a raster from a dom id', function(assert) {
var done = assert.async();
var img = document.createElement('img'); var img = document.createElement('img');
img.src = 'assets/paper-js.gif'; img.src = 'assets/paper-js.gif';
img.id = 'testimage'; img.id = 'testimage';
@ -70,12 +81,13 @@ asyncTest('Create a raster from a dom id', function(callback) {
var raster = new Raster('testimage'); var raster = new Raster('testimage');
equals(raster.size, new Size(146, 146), true); equals(raster.size, new Size(146, 146), true);
document.body.removeChild(img); document.body.removeChild(img);
callback(); done();
} }
}); });
}); });
asyncTest('Raster#getPixel / setPixel', function(callback) { test('Raster#getPixel / setPixel', function(assert) {
var done = assert.async();
var raster = new Raster('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABlJREFUeNpi+s/AwPCfgYmR4f9/hv8AAQYAHiAFAS8Lwy8AAAAASUVORK5CYII='); var raster = new Raster('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABlJREFUeNpi+s/AwPCfgYmR4f9/hv8AAQYAHiAFAS8Lwy8AAAAASUVORK5CYII=');
raster.onLoad = function() { raster.onLoad = function() {
equals(raster.getPixel(0, 0), new Color(1, 0, 0, 1)); equals(raster.getPixel(0, 0), new Color(1, 0, 0, 1));
@ -86,12 +98,17 @@ asyncTest('Raster#getPixel / setPixel', function(callback) {
// Alpha // Alpha
var color = new Color(1, 1, 0, 0.50196); var color = new Color(1, 1, 0, 0.50196);
raster.setPixel([0, 0], color); raster.setPixel([0, 0], color);
equals(raster.getPixel([0, 0]), color, 'alpha'); equals(raster.getPixel([0, 0]), color, 'alpha', { tolerance: 1e-2 });
callback(); done();
};
raster.onError = function(event) {
pushFailure(event.event);
done();
}; };
}); });
asyncTest('Raster#getSubCanvas', function(callback) { test('Raster#getSubCanvas', function(assert) {
var done = assert.async();
var raster = new Raster('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABlJREFUeNpi+s/AwPCfgYmR4f9/hv8AAQYAHiAFAS8Lwy8AAAAASUVORK5CYII='); var raster = new Raster('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABlJREFUeNpi+s/AwPCfgYmR4f9/hv8AAQYAHiAFAS8Lwy8AAAAASUVORK5CYII=');
raster.onLoad = function() { raster.onLoad = function() {
var canvas = raster.getSubCanvas(new Rectangle({ var canvas = raster.getSubCanvas(new Rectangle({
@ -114,7 +131,11 @@ asyncTest('Raster#getSubCanvas', function(callback) {
equals(function() { equals(function() {
return Base.equals(Array.prototype.slice.call(ctx.getImageData(0, 0, 1, 2).data), expected); return Base.equals(Array.prototype.slice.call(ctx.getImageData(0, 0, 1, 2).data), expected);
}, true); }, true);
callback(); done();
};
raster.onError = function(event) {
pushFailure(event.event);
done();
}; };
}); });
@ -132,7 +153,7 @@ test('Raster#getAverageColor(path)', function() {
var raster = paper.project.activeLayer.rasterize(72); var raster = paper.project.activeLayer.rasterize(72);
circle.scale(0.8); circle.scale(0.8);
equals(raster.getAverageColor(circle), circle.fillColor, null, equals(raster.getAverageColor(circle), circle.fillColor, null,
{ tolerance: 10e-4 }); { tolerance: 1e-3 });
}); });
test('Raster#getAverageColor(path) with compound path', function() { test('Raster#getAverageColor(path) with compound path', function() {
@ -155,5 +176,5 @@ test('Raster#getAverageColor(path) with compound path', function() {
path.scale(0.8); path.scale(0.8);
path2.scale(1.2); path2.scale(1.2);
equals(raster.getAverageColor(compoundPath), new Color(1, 0, 0), null, equals(raster.getAverageColor(compoundPath), new Color(1, 0, 0), null,
{ tolerance: 10e-4 }); { tolerance: 1e-3 });
}); });

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('Rectangle'); QUnit.module('Rectangle');
test('new Rectangle(new Point(10, 20), new Size(30, 40));', function() { test('new Rectangle(new Point(10, 20), new Size(30, 40));', function() {
var rect = new Rectangle(new Point(10, 20), new Size(30, 40)); var rect = new Rectangle(new Point(10, 20), new Size(30, 40));

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('SVGExport'); QUnit.module('SVGExport');
test('Export SVG line', function() { test('Export SVG line', function() {
var attrs = { var attrs = {

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('SVGImport'); QUnit.module('SVGImport');
test('Import SVG line', function() { test('Import SVG line', function() {
var attrs = { var attrs = {
@ -59,7 +59,7 @@ test('Import SVG ellipse', function() {
cy: 80, cy: 80,
rx: 100, rx: 100,
ry: 50 ry: 50
} };
var imported = paper.project.importSVG(createSVG('ellipse', attrs), var imported = paper.project.importSVG(createSVG('ellipse', attrs),
{ expandShapes: true }); { expandShapes: true });
var path = new Path.Ellipse({ var path = new Path.Ellipse({
@ -74,7 +74,7 @@ test('Import SVG circle', function() {
cx: 100, cx: 100,
cy: 80, cy: 80,
r: 50 r: 50
} };
var imported = paper.project.importSVG(createSVG('circle', attrs), var imported = paper.project.importSVG(createSVG('circle', attrs),
{ expandShapes: true }); { expandShapes: true });
var path = new Path.Circle({ var path = new Path.Circle({
@ -115,7 +115,8 @@ test('Import SVG polyline', function() {
}); });
test('Import complex CompoundPath and clone', function() { test('Import complex CompoundPath and clone', function() {
var svg = createSVG('<path id="path" fill="red" d="M4,14h20v-2H4V14z M15,26h7v-2h-7V26z M15,22h9v-2h-9V22z M15,18h9v-2h-9V18z M4,26h9V16H4V26z M28,10V6H0v22c0,0,0,4,4,4 h25c0,0,3-0.062,3-4V10H28z M4,30c-2,0-2-2-2-2V8h24v20c0,0.921,0.284,1.558,0.676,2H4z"/>;'); var svg = createSVG('<path fill="red" d="M4,14h20v-2H4V14z M15,26h7v-2h-7V26z M15,22h9v-2h-9V22z M15,18h9v-2h-9V18z M4,26h9V16H4V26z M28,10V6H0v22c0,0,0,4,4,4 h25c0,0,3-0.062,3-4V10H28z M4,30c-2,0-2-2-2-2V8h24v20c0,0.921,0.284,1.558,0.676,2H4z"/>;');
var item = paper.project.importSVG(svg.getElementById('path')); var item = paper.project.importSVG(svg.firstChild);
equals(item.clone(), item, null, { cloned: true }); equals(item.clone(), item, null, { cloned: true });
return;
}); });

View file

@ -10,7 +10,8 @@
* All rights reserved. * All rights reserved.
*/ */
module('Segment'); QUnit.module('Segment');
test('new Segment(point)', function() { test('new Segment(point)', function() {
var segment = new Segment(new Point(10, 10)); var segment = new Segment(new Point(10, 10));
equals(segment.toString(), '{ point: { x: 10, y: 10 } }'); equals(segment.toString(), '{ point: { x: 10, y: 10 } }');

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('Shape'); QUnit.module('Shape');
test('shape.toPath().toShape()', function() { test('shape.toPath().toShape()', function() {
var shapes = { var shapes = {

View file

@ -10,7 +10,8 @@
* All rights reserved. * All rights reserved.
*/ */
module('Size'); QUnit.module('Size');
test('new Size(10, 20)', function() { test('new Size(10, 20)', function() {
var size = new Size(10, 20); var size = new Size(10, 20);
equals(size.toString(), '{ width: 10, height: 20 }'); equals(size.toString(), '{ width: 10, height: 20 }');

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('Style'); QUnit.module('Style');
test('style defaults', function() { test('style defaults', function() {
var path = new Path(); var path = new Path();

View file

@ -10,7 +10,7 @@
* All rights reserved. * All rights reserved.
*/ */
module('TextItem'); QUnit.module('TextItem');
test('PointText', function() { test('PointText', function() {
var text = new PointText({ var text = new PointText({