diff --git a/gulpfile.js b/gulpfile.js index 6cd6efa5d91..86800169631 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -12,12 +12,13 @@ var uglify = require('gulp-uglify'); var util = require('gulp-util'); var zip = require('gulp-zip'); var exec = require('child_process').exec; -var karma = require('gulp-karma'); +var karma = require('karma'); var browserify = require('browserify'); var source = require('vinyl-source-stream'); var merge = require('merge-stream'); var collapse = require('bundle-collapser/plugin'); var argv = require('yargs').argv +var path = require('path'); var package = require('./package.json'); var srcDir = './src/'; @@ -34,14 +35,6 @@ var header = "/*!\n" + " * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md\n" + " */\n"; -var preTestFiles = [ - './node_modules/moment/min/moment.min.js' -]; - -var testFiles = [ - './test/*.js' -]; - gulp.task('bower', bowerTask); gulp.task('build', buildTask); gulp.task('package', packageTask); @@ -53,7 +46,6 @@ gulp.task('size', ['library-size', 'module-sizes']); gulp.task('server', serverTask); gulp.task('validHTML', validHTMLTask); gulp.task('unittest', unittestTask); -gulp.task('unittestWatch', unittestWatchTask); gulp.task('library-size', librarySizeTask); gulp.task('module-sizes', moduleSizesTask); gulp.task('_open', _openTask); @@ -157,6 +149,7 @@ function lintTask() { 'beforeEach', 'describe', 'expect', + 'fail', 'it', 'jasmine', 'moment', @@ -177,37 +170,31 @@ function validHTMLTask() { } function startTest() { - return [].concat(preTestFiles).concat([ - './src/**/*.js', - './test/mockContext.js' - ]).concat( - argv.inputs? - argv.inputs.split(';'): - testFiles); + return [ + './node_modules/moment/min/moment.min.js', + './test/jasmine.index.js', + './src/**/*.js', + ].concat( + argv.inputs? + argv.inputs.split(';'): + ['./test/specs/**/*.js'] + ); } -function unittestTask() { - return gulp.src(startTest()) - .pipe(karma({ - configFile: 'karma.conf.ci.js', - action: 'run' - })); +function unittestTask(done) { + new karma.Server({ + configFile: path.join(__dirname, 'karma.conf.js'), + singleRun: !argv.watch, + files: startTest(), + }, done).start(); } -function unittestWatchTask() { - return gulp.src(startTest()) - .pipe(karma({ - configFile: 'karma.conf.js', - action: 'watch' - })); -} - -function coverageTask() { - return gulp.src(startTest()) - .pipe(karma({ - configFile: 'karma.coverage.conf.js', - action: 'run' - })); +function coverageTask(done) { + new karma.Server({ + configFile: path.join(__dirname, 'karma.coverage.conf.js'), + files: startTest(), + singleRun: true, + }, done).start(); } function librarySizeTask() { diff --git a/karma.conf.ci.js b/karma.conf.ci.js deleted file mode 100644 index 7ed5d0fe2c9..00000000000 --- a/karma.conf.ci.js +++ /dev/null @@ -1,25 +0,0 @@ -module.exports = function(config) { - var configuration = { - browsers: ['Firefox'], - customLaunchers: { - Chrome_travis_ci: { - base: 'Chrome', - flags: ['--no-sandbox'] - } - }, - frameworks: ['browserify', 'jasmine'], - reporters: ['progress', 'html'], - preprocessors: { - 'src/**/*.js': ['browserify'] - }, - browserify: { - debug: true - } - }; - - if (process.env.TRAVIS) { - configuration.browsers.push('Chrome_travis_ci'); - } - - config.set(configuration); -}; \ No newline at end of file diff --git a/karma.conf.js b/karma.conf.js index acdff3f9cad..39e5ae61abc 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -1,14 +1,33 @@ -module.exports = function(config) { - config.set({ - browsers: ['Chrome', 'Firefox'], +/* eslint camelcase: 0 */ + +module.exports = function(karma) { + var config = { + browsers: ['Firefox'], frameworks: ['browserify', 'jasmine'], - reporters: ['progress', 'html'], + reporters: ['progress', 'kjhtml'], preprocessors: { - 'src/**/*.js': ['browserify'] + './test/jasmine.index.js': ['browserify'], + './src/**/*.js': ['browserify'] }, + browserify: { debug: true } - }); -}; \ No newline at end of file + }; + + // https://swizec.com/blog/how-to-run-javascript-tests-in-chrome-on-travis/swizec/6647 + if (process.env.TRAVIS) { + config.browsers.push('chrome_travis_ci'); + config.customLaunchers = { + chrome_travis_ci: { + base: 'Chrome', + flags: ['--no-sandbox'] + } + }; + } else { + config.browsers.push('Chrome'); + } + + karma.set(config); +}; diff --git a/karma.coverage.conf.js b/karma.coverage.conf.js index dd70f701ac6..4975f1688b2 100644 --- a/karma.coverage.conf.js +++ b/karma.coverage.conf.js @@ -1,12 +1,14 @@ module.exports = function(config) { var configuration = { browsers: ['Firefox'], - frameworks: ['browserify', 'jasmine'], + reporters: ['progress', 'coverage'], preprocessors: { - 'src/**/*.js': ['browserify'] + './test/jasmine.index.js': ['browserify'], + './src/**/*.js': ['browserify'] }, + browserify: { debug: true, transform: [['browserify-istanbul', { @@ -15,8 +17,7 @@ module.exports = function(config) { } }]] }, - - reporters: ['progress', 'coverage'], + coverageReporter: { dir: 'coverage/', reporters: [ diff --git a/package.json b/package.json index 6a3a28ea9b2..374f90d17a5 100644 --- a/package.json +++ b/package.json @@ -21,22 +21,21 @@ "gulp-file": "^0.3.0", "gulp-html-validator": "^0.0.2", "gulp-insert": "~0.5.0", - "gulp-karma": "0.0.4", "gulp-replace": "^0.5.4", "gulp-size": "~0.4.0", "gulp-streamify": "^1.0.2", "gulp-uglify": "~2.0.x", "gulp-util": "~2.2.x", "gulp-zip": "~3.2.0", - "jasmine": "^2.3.2", - "jasmine-core": "^2.3.4", - "karma": "^0.12.37", - "karma-browserify": "^5.0.1", - "karma-chrome-launcher": "^0.2.0", - "karma-coverage": "^0.5.1", - "karma-firefox-launcher": "^0.1.6", - "karma-jasmine": "^0.3.6", - "karma-jasmine-html-reporter": "^0.1.8", + "jasmine": "^2.5.0", + "jasmine-core": "^2.5.0", + "karma": "^1.5.0", + "karma-browserify": "^5.1.0", + "karma-chrome-launcher": "^2.0.0", + "karma-coverage": "^1.1.0", + "karma-firefox-launcher": "^1.0.0", + "karma-jasmine": "^1.1.0", + "karma-jasmine-html-reporter": "^0.2.2", "merge-stream": "^1.0.0", "vinyl-source-stream": "^1.1.0", "watchify": "^3.7.0", diff --git a/test/jasmine.context.js b/test/jasmine.context.js new file mode 100644 index 00000000000..814c24622e0 --- /dev/null +++ b/test/jasmine.context.js @@ -0,0 +1,125 @@ +// Code from http://stackoverflow.com/questions/4406864/html-canvas-unit-testing +var Context = function() { + this._calls = []; // names/args of recorded calls + this._initMethods(); + + this._fillStyle = null; + this._lineCap = null; + this._lineDashOffset = null; + this._lineJoin = null; + this._lineWidth = null; + this._strokeStyle = null; + + // Define properties here so that we can record each time they are set + Object.defineProperties(this, { + fillStyle: { + get: function() { + return this._fillStyle; + }, + set: function(style) { + this._fillStyle = style; + this.record('setFillStyle', [style]); + } + }, + lineCap: { + get: function() { + return this._lineCap; + }, + set: function(cap) { + this._lineCap = cap; + this.record('setLineCap', [cap]); + } + }, + lineDashOffset: { + get: function() { + return this._lineDashOffset; + }, + set: function(offset) { + this._lineDashOffset = offset; + this.record('setLineDashOffset', [offset]); + } + }, + lineJoin: { + get: function() { + return this._lineJoin; + }, + set: function(join) { + this._lineJoin = join; + this.record('setLineJoin', [join]); + } + }, + lineWidth: { + get: function() { + return this._lineWidth; + }, + set: function(width) { + this._lineWidth = width; + this.record('setLineWidth', [width]); + } + }, + strokeStyle: { + get: function() { + return this._strokeStyle; + }, + set: function(style) { + this._strokeStyle = style; + this.record('setStrokeStyle', [style]); + } + }, + }); +}; + +Context.prototype._initMethods = function() { + // define methods to test here + // no way to introspect so we have to do some extra work :( + var me = this; + var methods = { + arc: function() {}, + beginPath: function() {}, + bezierCurveTo: function() {}, + clearRect: function() {}, + closePath: function() {}, + fill: function() {}, + fillRect: function() {}, + fillText: function() {}, + lineTo: function() {}, + measureText: function(text) { + // return the number of characters * fixed size + return text ? {width: text.length * 10} : {width: 0}; + }, + moveTo: function() {}, + quadraticCurveTo: function() {}, + restore: function() {}, + rotate: function() {}, + save: function() {}, + setLineDash: function() {}, + stroke: function() {}, + strokeRect: function() {}, + setTransform: function() {}, + translate: function() {}, + }; + + Object.keys(methods).forEach(function(name) { + me[name] = function() { + me.record(name, arguments); + return methods[name].apply(me, arguments); + }; + }); +}; + +Context.prototype.record = function(methodName, args) { + this._calls.push({ + name: methodName, + args: Array.prototype.slice.call(args) + }); +}; + +Context.prototype.getCalls = function() { + return this._calls; +}; + +Context.prototype.resetCalls = function() { + this._calls = []; +}; + +module.exports = Context; diff --git a/test/jasmine.index.js b/test/jasmine.index.js new file mode 100644 index 00000000000..aeaee77c6c4 --- /dev/null +++ b/test/jasmine.index.js @@ -0,0 +1,53 @@ +var Context = require('./jasmine.context'); +var matchers = require('./jasmine.matchers'); +var utils = require('./jasmine.utils'); + +(function() { + + // Keep track of all acquired charts to automatically release them after each specs + var charts = {}; + + function acquireChart() { + var chart = utils.acquireChart.apply(utils, arguments); + charts[chart.id] = chart; + return chart; + } + + function releaseChart(chart) { + utils.releaseChart.apply(utils, arguments); + delete charts[chart.id]; + } + + function createMockContext() { + return new Context(); + } + + window.acquireChart = acquireChart; + window.releaseChart = releaseChart; + window.createMockContext = createMockContext; + + // some style initialization to limit differences between browsers across different plateforms. + utils.injectCSS( + '.chartjs-wrapper, .chartjs-wrapper canvas {' + + 'border: 0;' + + 'margin: 0;' + + 'padding: 0;' + + '}' + + '.chartjs-wrapper {' + + 'position: absolute' + + '}'); + + beforeEach(function() { + jasmine.addMatchers(matchers); + }); + + afterEach(function() { + // Auto releasing acquired charts + Object.keys(charts).forEach(function(id) { + var chart = charts[id]; + if (!(chart.$test || {}).persistent) { + releaseChart(chart); + } + }); + }); +}()); diff --git a/test/jasmine.matchers.js b/test/jasmine.matchers.js new file mode 100644 index 00000000000..abb90d7789e --- /dev/null +++ b/test/jasmine.matchers.js @@ -0,0 +1,113 @@ +'use strict'; + +function toBeCloseToPixel() { + return { + compare: function(actual, expected) { + var result = false; + + if (!isNaN(actual) && !isNaN(expected)) { + var diff = Math.abs(actual - expected); + var A = Math.abs(actual); + var B = Math.abs(expected); + var percentDiff = 0.005; // 0.5% diff + result = (diff <= (A > B ? A : B) * percentDiff) || diff < 2; // 2 pixels is fine + } + + return {pass: result}; + } + }; +} + +function toEqualOneOf() { + return { + compare: function(actual, expecteds) { + var result = false; + for (var i = 0, l = expecteds.length; i < l; i++) { + if (actual === expecteds[i]) { + result = true; + break; + } + } + return { + pass: result + }; + } + }; +} + +function toBeValidChart() { + return { + compare: function(actual) { + var message = null; + + if (!(actual instanceof Chart)) { + message = 'Expected ' + actual + ' to be an instance of Chart'; + } else if (!(actual.canvas instanceof HTMLCanvasElement)) { + message = 'Expected canvas to be an instance of HTMLCanvasElement'; + } else if (!(actual.ctx instanceof CanvasRenderingContext2D)) { + message = 'Expected context to be an instance of CanvasRenderingContext2D'; + } else if (typeof actual.height !== 'number' || !isFinite(actual.height)) { + message = 'Expected height to be a strict finite number'; + } else if (typeof actual.width !== 'number' || !isFinite(actual.width)) { + message = 'Expected width to be a strict finite number'; + } + + return { + message: message? message : 'Expected ' + actual + ' to be valid chart', + pass: !message + }; + } + }; +} + +function toBeChartOfSize() { + return { + compare: function(actual, expected) { + var res = toBeValidChart().compare(actual); + if (!res.pass) { + return res; + } + + var message = null; + var canvas = actual.ctx.canvas; + var style = getComputedStyle(canvas); + var pixelRatio = window.devicePixelRatio; + var dh = parseInt(style.height, 10); + var dw = parseInt(style.width, 10); + var rh = canvas.height; + var rw = canvas.width; + var orh = rh / pixelRatio; + var orw = rw / pixelRatio; + + // sanity checks + if (actual.height !== orh) { + message = 'Expected chart height ' + actual.height + ' to be equal to original render height ' + orh; + } else if (actual.width !== orw) { + message = 'Expected chart width ' + actual.width + ' to be equal to original render width ' + orw; + } + + // validity checks + if (dh !== expected.dh) { + message = 'Expected display height ' + dh + ' to be equal to ' + expected.dh; + } else if (dw !== expected.dw) { + message = 'Expected display width ' + dw + ' to be equal to ' + expected.dw; + } else if (rh !== expected.rh) { + message = 'Expected render height ' + rh + ' to be equal to ' + expected.rh; + } else if (rw !== expected.rw) { + message = 'Expected render width ' + rw + ' to be equal to ' + expected.rw; + } + + return { + message: message? message : 'Expected ' + actual + ' to be a chart of size ' + expected, + pass: !message + }; + } + }; +} + +module.exports = { + toBeCloseToPixel: toBeCloseToPixel, + toEqualOneOf: toEqualOneOf, + toBeValidChart: toBeValidChart, + toBeChartOfSize: toBeChartOfSize +}; diff --git a/test/jasmine.utils.js b/test/jasmine.utils.js new file mode 100644 index 00000000000..77f05c6e008 --- /dev/null +++ b/test/jasmine.utils.js @@ -0,0 +1,76 @@ +/** + * Injects a new canvas (and div wrapper) and creates teh associated Chart instance + * using the given config. Additional options allow tweaking elements generation. + * @param {object} config - Chart config. + * @param {object} options - Chart acquisition options. + * @param {object} options.canvas - Canvas attributes. + * @param {object} options.wrapper - Canvas wrapper attributes. + * @param {boolean} options.persistent - If true, the chart will not be released after the spec. + */ +function acquireChart(config, options) { + var wrapper = document.createElement('div'); + var canvas = document.createElement('canvas'); + var chart, key; + + config = config || {}; + options = options || {}; + options.canvas = options.canvas || {height: 512, width: 512}; + options.wrapper = options.wrapper || {class: 'chartjs-wrapper'}; + + for (key in options.canvas) { + if (options.canvas.hasOwnProperty(key)) { + canvas.setAttribute(key, options.canvas[key]); + } + } + + for (key in options.wrapper) { + if (options.wrapper.hasOwnProperty(key)) { + wrapper.setAttribute(key, options.wrapper[key]); + } + } + + // by default, remove chart animation and auto resize + config.options = config.options || {}; + config.options.animation = config.options.animation === undefined? false : config.options.animation; + config.options.responsive = config.options.responsive === undefined? false : config.options.responsive; + config.options.defaultFontFamily = config.options.defaultFontFamily || 'Arial'; + + wrapper.appendChild(canvas); + window.document.body.appendChild(wrapper); + + chart = new Chart(canvas.getContext('2d'), config); + chart.$test = { + persistent: options.persistent, + wrapper: wrapper + }; + + return chart; +} + +function releaseChart(chart) { + chart.destroy(); + + var wrapper = (chart.$test || {}).wrapper; + if (wrapper && wrapper.parentNode) { + wrapper.parentNode.removeChild(wrapper); + } +} + +function injectCSS(css) { + // http://stackoverflow.com/q/3922139 + var head = document.getElementsByTagName('head')[0]; + var style = document.createElement('style'); + style.setAttribute('type', 'text/css'); + if (style.styleSheet) { // IE + style.styleSheet.cssText = css; + } else { + style.appendChild(document.createTextNode(css)); + } + head.appendChild(style); +} + +module.exports = { + injectCSS: injectCSS, + acquireChart: acquireChart, + releaseChart: releaseChart +}; diff --git a/test/mockContext.js b/test/mockContext.js deleted file mode 100644 index b6d2c13f535..00000000000 --- a/test/mockContext.js +++ /dev/null @@ -1,348 +0,0 @@ -/* eslint guard-for-in: 1 */ -/* eslint camelcase: 1 */ -(function() { - // Code from http://stackoverflow.com/questions/4406864/html-canvas-unit-testing - var Context = function() { - this._calls = []; // names/args of recorded calls - this._initMethods(); - - this._fillStyle = null; - this._lineCap = null; - this._lineDashOffset = null; - this._lineJoin = null; - this._lineWidth = null; - this._strokeStyle = null; - - // Define properties here so that we can record each time they are set - Object.defineProperties(this, { - fillStyle: { - get: function() { - return this._fillStyle; - }, - set: function(style) { - this._fillStyle = style; - this.record('setFillStyle', [style]); - } - }, - lineCap: { - get: function() { - return this._lineCap; - }, - set: function(cap) { - this._lineCap = cap; - this.record('setLineCap', [cap]); - } - }, - lineDashOffset: { - get: function() { - return this._lineDashOffset; - }, - set: function(offset) { - this._lineDashOffset = offset; - this.record('setLineDashOffset', [offset]); - } - }, - lineJoin: { - get: function() { - return this._lineJoin; - }, - set: function(join) { - this._lineJoin = join; - this.record('setLineJoin', [join]); - } - }, - lineWidth: { - get: function() { - return this._lineWidth; - }, - set: function(width) { - this._lineWidth = width; - this.record('setLineWidth', [width]); - } - }, - strokeStyle: { - get: function() { - return this._strokeStyle; - }, - set: function(style) { - this._strokeStyle = style; - this.record('setStrokeStyle', [style]); - } - }, - }); - }; - - Context.prototype._initMethods = function() { - // define methods to test here - // no way to introspect so we have to do some extra work :( - var methods = { - arc: function() {}, - beginPath: function() {}, - bezierCurveTo: function() {}, - clearRect: function() {}, - closePath: function() {}, - fill: function() {}, - fillRect: function() {}, - fillText: function() {}, - lineTo: function() {}, - measureText: function(text) { - // return the number of characters * fixed size - return text ? {width: text.length * 10} : {width: 0}; - }, - moveTo: function() {}, - quadraticCurveTo: function() {}, - restore: function() {}, - rotate: function() {}, - save: function() {}, - setLineDash: function() {}, - stroke: function() {}, - strokeRect: function() {}, - setTransform: function() {}, - translate: function() {}, - }; - - // attach methods to the class itself - var me = this; - var methodName; - - var addMethod = function(name, method) { - me[methodName] = function() { - me.record(name, arguments); - return method.apply(me, arguments); - }; - }; - - for (methodName in methods) { - var method = methods[methodName]; - - addMethod(methodName, method); - } - }; - - Context.prototype.record = function(methodName, args) { - this._calls.push({ - name: methodName, - args: Array.prototype.slice.call(args) - }); - }; - - Context.prototype.getCalls = function() { - return this._calls; - }; - - Context.prototype.resetCalls = function() { - this._calls = []; - }; - - window.createMockContext = function() { - return new Context(); - }; - - // Custom matcher - function toBeCloseToPixel() { - return { - compare: function(actual, expected) { - var result = false; - - if (!isNaN(actual) && !isNaN(expected)) { - var diff = Math.abs(actual - expected); - var A = Math.abs(actual); - var B = Math.abs(expected); - var percentDiff = 0.005; // 0.5% diff - result = (diff <= (A > B ? A : B) * percentDiff) || diff < 2; // 2 pixels is fine - } - - return {pass: result}; - } - }; - } - - function toEqualOneOf() { - return { - compare: function(actual, expecteds) { - var result = false; - for (var i = 0, l = expecteds.length; i < l; i++) { - if (actual === expecteds[i]) { - result = true; - break; - } - } - return { - pass: result - }; - } - }; - } - - function toBeValidChart() { - return { - compare: function(actual) { - var message = null; - - if (!(actual instanceof Chart)) { - message = 'Expected ' + actual + ' to be an instance of Chart'; - } else if (!(actual.canvas instanceof HTMLCanvasElement)) { - message = 'Expected canvas to be an instance of HTMLCanvasElement'; - } else if (!(actual.ctx instanceof CanvasRenderingContext2D)) { - message = 'Expected context to be an instance of CanvasRenderingContext2D'; - } else if (typeof actual.height !== 'number' || !isFinite(actual.height)) { - message = 'Expected height to be a strict finite number'; - } else if (typeof actual.width !== 'number' || !isFinite(actual.width)) { - message = 'Expected width to be a strict finite number'; - } - - return { - message: message? message : 'Expected ' + actual + ' to be valid chart', - pass: !message - }; - } - }; - } - - function toBeChartOfSize() { - return { - compare: function(actual, expected) { - var res = toBeValidChart().compare(actual); - if (!res.pass) { - return res; - } - - var message = null; - var canvas = actual.ctx.canvas; - var style = getComputedStyle(canvas); - var pixelRatio = window.devicePixelRatio; - var dh = parseInt(style.height, 10); - var dw = parseInt(style.width, 10); - var rh = canvas.height; - var rw = canvas.width; - var orh = rh / pixelRatio; - var orw = rw / pixelRatio; - - // sanity checks - if (actual.height !== orh) { - message = 'Expected chart height ' + actual.height + ' to be equal to original render height ' + orh; - } else if (actual.width !== orw) { - message = 'Expected chart width ' + actual.width + ' to be equal to original render width ' + orw; - } - - // validity checks - if (dh !== expected.dh) { - message = 'Expected display height ' + dh + ' to be equal to ' + expected.dh; - } else if (dw !== expected.dw) { - message = 'Expected display width ' + dw + ' to be equal to ' + expected.dw; - } else if (rh !== expected.rh) { - message = 'Expected render height ' + rh + ' to be equal to ' + expected.rh; - } else if (rw !== expected.rw) { - message = 'Expected render width ' + rw + ' to be equal to ' + expected.rw; - } - - return { - message: message? message : 'Expected ' + actual + ' to be a chart of size ' + expected, - pass: !message - }; - } - }; - } - - beforeEach(function() { - jasmine.addMatchers({ - toBeCloseToPixel: toBeCloseToPixel, - toEqualOneOf: toEqualOneOf, - toBeValidChart: toBeValidChart, - toBeChartOfSize: toBeChartOfSize - }); - }); - - // Canvas injection helpers - var charts = {}; - - /** - * Injects a new canvas (and div wrapper) and creates teh associated Chart instance - * using the given config. Additional options allow tweaking elements generation. - * @param {object} config - Chart config. - * @param {object} options - Chart acquisition options. - * @param {object} options.canvas - Canvas attributes. - * @param {object} options.wrapper - Canvas wrapper attributes. - * @param {boolean} options.persistent - If true, the chart will not be released after the spec. - */ - function acquireChart(config, options) { - var wrapper = document.createElement('div'); - var canvas = document.createElement('canvas'); - var chart, key; - - config = config || {}; - options = options || {}; - options.canvas = options.canvas || {height: 512, width: 512}; - options.wrapper = options.wrapper || {class: 'chartjs-wrapper'}; - - for (key in options.canvas) { - if (options.canvas.hasOwnProperty(key)) { - canvas.setAttribute(key, options.canvas[key]); - } - } - - for (key in options.wrapper) { - if (options.wrapper.hasOwnProperty(key)) { - wrapper.setAttribute(key, options.wrapper[key]); - } - } - - // by default, remove chart animation and auto resize - config.options = config.options || {}; - config.options.animation = config.options.animation === undefined? false : config.options.animation; - config.options.responsive = config.options.responsive === undefined? false : config.options.responsive; - config.options.defaultFontFamily = config.options.defaultFontFamily || 'Arial'; - - wrapper.appendChild(canvas); - window.document.body.appendChild(wrapper); - - chart = new Chart(canvas.getContext('2d'), config); - chart._test_persistent = options.persistent; - chart._test_wrapper = wrapper; - charts[chart.id] = chart; - return chart; - } - - function releaseChart(chart) { - chart.destroy(); - chart._test_wrapper.remove(); - delete charts[chart.id]; - } - - afterEach(function() { - // Auto releasing acquired charts - for (var id in charts) { - var chart = charts[id]; - if (!chart._test_persistent) { - releaseChart(chart); - } - } - }); - - function injectCSS(css) { - // http://stackoverflow.com/q/3922139 - var head = document.getElementsByTagName('head')[0]; - var style = document.createElement('style'); - style.setAttribute('type', 'text/css'); - if (style.styleSheet) { // IE - style.styleSheet.cssText = css; - } else { - style.appendChild(document.createTextNode(css)); - } - head.appendChild(style); - } - - window.acquireChart = acquireChart; - window.releaseChart = releaseChart; - - // some style initialization to limit differences between browsers across different plateforms. - injectCSS( - '.chartjs-wrapper, .chartjs-wrapper canvas {' + - 'border: 0;' + - 'margin: 0;' + - 'padding: 0;' + - '}' + - '.chartjs-wrapper {' + - 'position: absolute' + - '}'); -}()); diff --git a/test/controller.bar.tests.js b/test/specs/controller.bar.tests.js similarity index 100% rename from test/controller.bar.tests.js rename to test/specs/controller.bar.tests.js diff --git a/test/controller.bubble.tests.js b/test/specs/controller.bubble.tests.js similarity index 100% rename from test/controller.bubble.tests.js rename to test/specs/controller.bubble.tests.js diff --git a/test/controller.doughnut.tests.js b/test/specs/controller.doughnut.tests.js similarity index 100% rename from test/controller.doughnut.tests.js rename to test/specs/controller.doughnut.tests.js diff --git a/test/controller.line.tests.js b/test/specs/controller.line.tests.js similarity index 100% rename from test/controller.line.tests.js rename to test/specs/controller.line.tests.js diff --git a/test/controller.polarArea.tests.js b/test/specs/controller.polarArea.tests.js similarity index 100% rename from test/controller.polarArea.tests.js rename to test/specs/controller.polarArea.tests.js diff --git a/test/controller.radar.tests.js b/test/specs/controller.radar.tests.js similarity index 100% rename from test/controller.radar.tests.js rename to test/specs/controller.radar.tests.js diff --git a/test/core.controller.tests.js b/test/specs/core.controller.tests.js similarity index 100% rename from test/core.controller.tests.js rename to test/specs/core.controller.tests.js diff --git a/test/core.datasetController.tests.js b/test/specs/core.datasetController.tests.js similarity index 100% rename from test/core.datasetController.tests.js rename to test/specs/core.datasetController.tests.js diff --git a/test/core.element.tests.js b/test/specs/core.element.tests.js similarity index 100% rename from test/core.element.tests.js rename to test/specs/core.element.tests.js diff --git a/test/core.helpers.tests.js b/test/specs/core.helpers.tests.js similarity index 100% rename from test/core.helpers.tests.js rename to test/specs/core.helpers.tests.js diff --git a/test/core.interaction.tests.js b/test/specs/core.interaction.tests.js similarity index 100% rename from test/core.interaction.tests.js rename to test/specs/core.interaction.tests.js diff --git a/test/core.layoutService.tests.js b/test/specs/core.layoutService.tests.js similarity index 100% rename from test/core.layoutService.tests.js rename to test/specs/core.layoutService.tests.js diff --git a/test/core.legend.tests.js b/test/specs/core.legend.tests.js similarity index 100% rename from test/core.legend.tests.js rename to test/specs/core.legend.tests.js diff --git a/test/core.plugin.tests.js b/test/specs/core.plugin.tests.js similarity index 100% rename from test/core.plugin.tests.js rename to test/specs/core.plugin.tests.js diff --git a/test/core.scaleService.tests.js b/test/specs/core.scaleService.tests.js similarity index 100% rename from test/core.scaleService.tests.js rename to test/specs/core.scaleService.tests.js diff --git a/test/core.title.tests.js b/test/specs/core.title.tests.js similarity index 100% rename from test/core.title.tests.js rename to test/specs/core.title.tests.js diff --git a/test/core.tooltip.tests.js b/test/specs/core.tooltip.tests.js similarity index 100% rename from test/core.tooltip.tests.js rename to test/specs/core.tooltip.tests.js diff --git a/test/element.arc.tests.js b/test/specs/element.arc.tests.js similarity index 100% rename from test/element.arc.tests.js rename to test/specs/element.arc.tests.js diff --git a/test/element.line.tests.js b/test/specs/element.line.tests.js similarity index 100% rename from test/element.line.tests.js rename to test/specs/element.line.tests.js diff --git a/test/element.point.tests.js b/test/specs/element.point.tests.js similarity index 100% rename from test/element.point.tests.js rename to test/specs/element.point.tests.js diff --git a/test/element.rectangle.tests.js b/test/specs/element.rectangle.tests.js similarity index 100% rename from test/element.rectangle.tests.js rename to test/specs/element.rectangle.tests.js diff --git a/test/defaultConfig.tests.js b/test/specs/global.defaults.tests.js similarity index 100% rename from test/defaultConfig.tests.js rename to test/specs/global.defaults.tests.js diff --git a/test/global.deprecations.tests.js b/test/specs/global.deprecations.tests.js similarity index 100% rename from test/global.deprecations.tests.js rename to test/specs/global.deprecations.tests.js diff --git a/test/platform.dom.tests.js b/test/specs/platform.dom.tests.js similarity index 100% rename from test/platform.dom.tests.js rename to test/specs/platform.dom.tests.js diff --git a/test/scale.category.tests.js b/test/specs/scale.category.tests.js similarity index 100% rename from test/scale.category.tests.js rename to test/specs/scale.category.tests.js diff --git a/test/scale.linear.tests.js b/test/specs/scale.linear.tests.js similarity index 100% rename from test/scale.linear.tests.js rename to test/specs/scale.linear.tests.js diff --git a/test/scale.logarithmic.tests.js b/test/specs/scale.logarithmic.tests.js similarity index 100% rename from test/scale.logarithmic.tests.js rename to test/specs/scale.logarithmic.tests.js diff --git a/test/scale.radialLinear.tests.js b/test/specs/scale.radialLinear.tests.js similarity index 100% rename from test/scale.radialLinear.tests.js rename to test/specs/scale.radialLinear.tests.js diff --git a/test/scale.time.tests.js b/test/specs/scale.time.tests.js similarity index 100% rename from test/scale.time.tests.js rename to test/specs/scale.time.tests.js