From ad2c69bda9c2b4de41e37662f97046177b8262d1 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Thu, 4 Feb 2021 13:27:43 +0200 Subject: [PATCH] Migrate to chartjs-test-utils --- package-lock.json | 38 ++++++++++++++ package.json | 2 +- test/fixture.js | 81 ----------------------------- test/index.js | 33 ++++-------- test/matchers.js | 84 ------------------------------ test/utils.js | 128 ---------------------------------------------- 6 files changed, 48 insertions(+), 318 deletions(-) delete mode 100644 test/fixture.js delete mode 100644 test/matchers.js delete mode 100644 test/utils.js diff --git a/package-lock.json b/package-lock.json index 4c65e71..b7353b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4990,6 +4990,18 @@ "color-name": "^1.0.0" } }, + "chartjs-test-utils": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/chartjs-test-utils/-/chartjs-test-utils-0.1.2.tgz", + "integrity": "sha512-l56FYfquEbUfy8qV+IvPq2Z0jeG6bYmCvKvpAXMNY8c49L3e2uvJ5H3nMaDfkwBHMTwqE+Gt9Uy7TRVzqxJp8w==", + "dev": true, + "requires": { + "jasmine": "^3.6.3", + "karma": "^5.2.3", + "karma-jasmine": "^4.0.1", + "pixelmatch": "^5.2.1" + } + }, "chokidar": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.2.tgz", @@ -9663,6 +9675,32 @@ "istanbul-lib-report": "^3.0.0" } }, + "jasmine": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.6.4.tgz", + "integrity": "sha512-hIeOou6y0BgCOKYgXYveQvlY+PTHgDPajFf+vLCYbMTQ+VjAP9+EQv0nuC9+gyCAAWISRFauB1XUb9kFuOKtcQ==", + "dev": true, + "requires": { + "glob": "^7.1.6", + "jasmine-core": "~3.6.0" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, "jasmine-core": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.6.0.tgz", diff --git a/package.json b/package.json index 65991db..743acec 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "acorn": "^8.0.4", "archiver": "^5.1.0", "chart.js": "~2.9.4", + "chartjs-test-utils": "^0.1.2", "eslint": "^7.15.0", "eslint-config-chartjs": "^0.3.0", "eslint-plugin-es": "^3.0.1", @@ -54,7 +55,6 @@ "karma-jasmine-html-reporter": "^1.5.4", "karma-rollup-preprocessor": "^7.0.5", "karma-spec-reporter": "^0.0.32", - "pixelmatch": "^5.2.1", "rollup": "^2.33.1", "rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-istanbul": "^2.0.1", diff --git a/test/fixture.js b/test/fixture.js deleted file mode 100644 index a7de75d..0000000 --- a/test/fixture.js +++ /dev/null @@ -1,81 +0,0 @@ -'use strict'; - -import utils from './utils'; - -function readFile(url, callback) { - var request = new XMLHttpRequest(); - request.onreadystatechange = function() { - if (request.readyState === 4) { - return callback(request.responseText); - } - }; - - request.open('GET', url, false); - request.send(null); -} - -function loadConfig(url, callback) { - var regex = /\.(json|js)$/i; - var matches = url.match(regex); - var type = matches ? matches[1] : 'json'; - var cfg = null; - - readFile(url, function(content) { - switch (type) { - case 'js': - cfg = new Function('"use strict";' + content + ';return fixture;')(); - break; - case 'json': - cfg = JSON.parse(content); - break; - default: - } - - callback(cfg); - }); -} - -function specFromFixture(description, inputs) { - var input = inputs.js || inputs.json; - it(input, function(done) { - loadConfig(input, function(json) { - var chart = utils.acquireChart(json.config, json.options); - if (!inputs.png) { - fail('Missing PNG comparison file for ' + inputs.json); - done(); - } - - utils.readImageData(inputs.png, function(expected) { - expect(chart).toEqualImageData(expected, json); - utils.releaseChart(chart); - done(); - }); - }); - }); -} - -function specsFromFixtures(path) { - var regex = new RegExp('(^/base/test/fixtures/' + path + '.+)\\.(png|json|js)'); - var inputs = {}; - - Object.keys(__karma__.files || {}).forEach(function(file) { - var matches = file.match(regex); - var name = matches && matches[1]; - var type = matches && matches[2]; - - if (name && type) { - inputs[name] = inputs[name] || {}; - inputs[name][type] = file; - } - }); - - return function() { - Object.keys(inputs).forEach(function(key) { - specFromFixture(key, inputs[key]); - }); - }; -} - -export default { - specs: specsFromFixtures -}; diff --git a/test/index.js b/test/index.js index 898509e..58fcfa6 100644 --- a/test/index.js +++ b/test/index.js @@ -1,23 +1,12 @@ import Chart from 'chart.js'; -import fixture from './fixture'; -import matchers from './matchers'; -import utils from './utils'; - -var charts = {}; +import {acquireChart, addMatchers, releaseChart, releaseCharts, specsFromFixtures, triggerMouseEvent} from 'chartjs-test-utils'; // force ratio=1 for tests on high-res/retina devices window.devicePixelRatio = 1; jasmine.chart = { - acquire: function() { - var chart = utils.acquireChart.apply(utils, arguments); - charts[chart.id] = chart; - return chart; - }, - release: function(chart) { - utils.releaseChart.apply(utils, arguments); - delete charts[chart.id]; - }, + acquire: acquireChart, + release: releaseChart, // Since version 1.x, this plugin isn't anymore automatically registered on first // import. This helper allows to register the given plugin for all specs contained @@ -43,11 +32,13 @@ jasmine.chart = { } }; -jasmine.fixture = fixture; -jasmine.triggerMouseEvent = utils.triggerMouseEvent; +jasmine.fixture = { + specs: specsFromFixtures +}; +jasmine.triggerMouseEvent = triggerMouseEvent; beforeEach(function() { - jasmine.addMatchers(matchers); + addMatchers(); Chart.helpers.merge(Chart.defaults.global, { animation: false, @@ -83,11 +74,5 @@ beforeEach(function() { }); afterEach(function() { - // Auto releasing acquired charts - Object.keys(charts).forEach(function(id) { - var chart = charts[id]; - if (!(chart.$test || {}).persistent) { - jasmine.chart.release(chart); - } - }); + releaseCharts(); }); diff --git a/test/matchers.js b/test/matchers.js deleted file mode 100644 index 73bcfb1..0000000 --- a/test/matchers.js +++ /dev/null @@ -1,84 +0,0 @@ -'use strict'; - -import Chart from 'chart.js'; -import pixelmatch from 'pixelmatch'; -import utils from './utils'; - -function toPercent(value) { - return Math.round(value * 10000) / 100; -} - -function buildPixelMatchPreview(actual, expected, diff, threshold, tolerance, count) { - var ratio = count / (actual.width * actual.height); - var wrapper = document.createElement('div'); - - wrapper.style.cssText = 'display: flex; overflow-y: auto'; - - [ - {data: actual, label: 'Actual'}, - {data: expected, label: 'Expected'}, - {data: diff, label: - 'diff: ' + count + 'px ' + - '(' + toPercent(ratio) + '%)
' + - 'thr: ' + toPercent(threshold) + '%, ' + - 'tol: ' + toPercent(tolerance) + '%' - } - ].forEach(function(values) { - var item = document.createElement('div'); - item.style.cssText = 'text-align: center; font: 12px monospace; line-height: 1.4; margin: 8px'; - item.innerHTML = '
' + values.label + '
'; - item.appendChild(utils.canvasFromImageData(values.data)); - wrapper.appendChild(item); - }); - - // WORKAROUND: https://github.com/karma-runner/karma-jasmine/issues/139 - wrapper.indexOf = function() { - return -1; - }; - - return wrapper; -} - -function toEqualImageData() { - return { - compare: function(actual, expected, opts) { - var message = null; - var debug = opts.debug || false; - var tolerance = opts.tolerance === undefined ? 0.001 : opts.tolerance; - var threshold = opts.threshold === undefined ? 0.1 : opts.threshold; - var ctx, idata, ddata, w, h, count, ratio; - - if (actual instanceof Chart) { - ctx = actual.ctx; - } else if (actual instanceof HTMLCanvasElement) { - ctx = actual.getContext('2d'); - } else if (actual instanceof CanvasRenderingContext2D) { - ctx = actual; - } - - if (ctx) { - h = expected.height; - w = expected.width; - idata = ctx.getImageData(0, 0, w, h); - ddata = utils.createImageData(w, h); - count = pixelmatch(idata.data, expected.data, ddata.data, w, h, {threshold: threshold}); - ratio = count / (w * h); - - if ((ratio > tolerance) || debug) { - message = buildPixelMatchPreview(idata, expected, ddata, threshold, tolerance, count); - } - } else { - message = 'Input value is not a valid image source.'; - } - - return { - message: message, - pass: !message - }; - } - }; -} - -export default { - toEqualImageData: toEqualImageData -}; diff --git a/test/utils.js b/test/utils.js deleted file mode 100644 index 103609f..0000000 --- a/test/utils.js +++ /dev/null @@ -1,128 +0,0 @@ -'use strict'; - -import Chart from 'chart.js'; - -function createCanvas(w, h) { - var canvas = document.createElement('CANVAS'); - canvas.width = w; - canvas.height = h; - return canvas; -} - -function createImageData(w, h) { - var canvas = createCanvas(w, h); - var context = canvas.getContext('2d'); - return context.getImageData(0, 0, w, h); -} - -function readImageData(url, callback) { - var image = new Image(); - - image.onload = function() { - var h = image.height; - var w = image.width; - var canvas = createCanvas(w, h); - var ctx = canvas.getContext('2d'); - ctx.drawImage(image, 0, 0, w, h); - callback(ctx.getImageData(0, 0, w, h)); - }; - - image.src = url; -} - -function canvasFromImageData(data) { - var canvas = createCanvas(data.width, data.height); - var context = canvas.getContext('2d'); - context.putImageData(data, 0, 0); - return canvas; -} - -function acquireChart(config, options) { - var wrapper = document.createElement('DIV'); - var canvas = document.createElement('CANVAS'); - var chart, key; - - // By default, remove chart animation and auto resize. - config = Chart.helpers.merge({ - options: { - animation: false, - responsive: false, - defaultFontFamily: 'Arial' - } - }, config || {}); - - options = Chart.helpers.merge({ - canvas: { - height: 512, - width: 512 - }, - wrapper: { - class: 'chartjs-wrapper' - } - }, options || {}); - - for (key in options.canvas) { - if (Object.prototype.hasOwnProperty.call(options.canvas, key)) { - canvas.setAttribute(key, options.canvas[key]); - } - } - - for (key in options.wrapper) { - if (Object.prototype.hasOwnProperty.call(options.wrapper, key)) { - wrapper.setAttribute(key, options.wrapper[key]); - } - } - - wrapper.appendChild(canvas); - window.document.body.appendChild(wrapper); - - try { - chart = new Chart(canvas.getContext('2d'), config); - } catch (e) { - window.document.body.removeChild(wrapper); - throw e; - } - - 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 triggerMouseEvent(chart, type, el) { - var node = chart.canvas; - var rect = node.getBoundingClientRect(); - var x = el ? el.x !== undefined ? el.x : el._model.x : null; - var y = el ? el.y !== undefined ? el.y : el._model.y : null; - - var event = new MouseEvent(type, { - clientX: el ? rect.left + x : undefined, - clientY: el ? rect.top + y : undefined, - cancelable: true, - bubbles: true, - view: window - }); - - node.dispatchEvent(event); -} - -export default { - acquireChart: acquireChart, - releaseChart: releaseChart, - createCanvas: createCanvas, - createImageData: createImageData, - canvasFromImageData: canvasFromImageData, - readImageData: readImageData, - triggerMouseEvent: triggerMouseEvent -};