diff --git a/gulpfile.js b/gulpfile.js index 86800169631..c9d60471333 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -171,6 +171,8 @@ function validHTMLTask() { function startTest() { return [ + {pattern: './test/fixtures/**/*.json', included: false}, + {pattern: './test/fixtures/**/*.png', included: false}, './node_modules/moment/min/moment.min.js', './test/jasmine.index.js', './src/**/*.js', diff --git a/package.json b/package.json index 374f90d17a5..24a313f4b29 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "karma-jasmine": "^1.1.0", "karma-jasmine-html-reporter": "^0.2.2", "merge-stream": "^1.0.0", + "pixelmatch": "^4.0.2", "vinyl-source-stream": "^1.1.0", "watchify": "^3.7.0", "yargs": "^5.0.0" diff --git a/test/fixtures/element.line/fill-line-bottom-span.json b/test/fixtures/element.line/fill-line-bottom-span.json new file mode 100644 index 00000000000..ec6bb4b0d0a --- /dev/null +++ b/test/fixtures/element.line/fill-line-bottom-span.json @@ -0,0 +1,55 @@ +{ + "config": { + "type": "line", + "data": { + "labels": ["0", "1", "2", "3", "4", "5", "6", "7", "8"], + "datasets": [{ + "backgroundColor": "rgba(0, 0, 255, 0.25)", + "data": [null, null, 2, 3, 4, -4, -2, 1, 0] + }, { + "backgroundColor": "rgba(0, 255, 0, 0.25)", + "data": [6, 2, null, 4, 5, null, null, 2, 1] + }, { + "backgroundColor": "rgba(255, 0, 0, 0.25)", + "data": [7, 3, 4, 5, 6, 1, 4, null, null] + }, { + "backgroundColor": "rgba(0, 0, 255, 0.25)", + "data": [8, 7, 6, -6, -4, -6, 4, 5, 8] + }] + }, + "options": { + "responsive": false, + "spanGaps": true, + "legend": false, + "title": false, + "scales": { + "xAxes": [{ + "ticks": { + "display": false + } + }], + "yAxes": [{ + "ticks": { + "display": false + } + }] + }, + "elements": { + "point": { + "radius": 0 + }, + "line": { + "borderColor": "transparent", + "fill": "bottom", + "tension": 0 + } + } + } + }, + "options": { + "canvas": { + "height": 256, + "width": 512 + } + } +} diff --git a/test/fixtures/element.line/fill-line-bottom-span.png b/test/fixtures/element.line/fill-line-bottom-span.png new file mode 100644 index 00000000000..45e4992768b Binary files /dev/null and b/test/fixtures/element.line/fill-line-bottom-span.png differ diff --git a/test/fixtures/element.line/fill-line-bottom.json b/test/fixtures/element.line/fill-line-bottom.json new file mode 100644 index 00000000000..a0557fe78c5 --- /dev/null +++ b/test/fixtures/element.line/fill-line-bottom.json @@ -0,0 +1,55 @@ +{ + "config": { + "type": "line", + "data": { + "labels": ["0", "1", "2", "3", "4", "5", "6", "7", "8"], + "datasets": [{ + "backgroundColor": "rgba(0, 0, 255, 0.25)", + "data": [null, null, 2, 3, 4, -4, -2, 1, 0] + }, { + "backgroundColor": "rgba(0, 255, 0, 0.25)", + "data": [6, 2, null, 4, 5, null, null, 2, 1] + }, { + "backgroundColor": "rgba(255, 0, 0, 0.25)", + "data": [7, 3, 4, 5, 6, 1, 4, null, null] + }, { + "backgroundColor": "rgba(0, 0, 255, 0.25)", + "data": [8, 7, 6, -6, -4, -6, 4, 5, 8] + }] + }, + "options": { + "responsive": false, + "spanGaps": false, + "legend": false, + "title": false, + "scales": { + "xAxes": [{ + "ticks": { + "display": false + } + }], + "yAxes": [{ + "ticks": { + "display": false + } + }] + }, + "elements": { + "point": { + "radius": 0 + }, + "line": { + "borderColor": "transparent", + "fill": "bottom", + "tension": 0 + } + } + } + }, + "options": { + "canvas": { + "height": 256, + "width": 512 + } + } +} diff --git a/test/fixtures/element.line/fill-line-bottom.png b/test/fixtures/element.line/fill-line-bottom.png new file mode 100644 index 00000000000..1cd05e60dfc Binary files /dev/null and b/test/fixtures/element.line/fill-line-bottom.png differ diff --git a/test/fixtures/element.line/fill-line-spline-span.json b/test/fixtures/element.line/fill-line-spline-span.json new file mode 100644 index 00000000000..c87833b6e23 --- /dev/null +++ b/test/fixtures/element.line/fill-line-spline-span.json @@ -0,0 +1,55 @@ +{ + "config": { + "type": "line", + "data": { + "labels": ["0", "1", "2", "3", "4", "5", "6", "7", "8"], + "datasets": [{ + "backgroundColor": "rgba(0, 0, 192, 0.25)", + "data": [null, null, 2, 4, 2, 1, -1, 1, 2] + }, { + "backgroundColor": "rgba(0, 192, 0, 0.25)", + "data": [4, 2, null, 3, 2.5, null, -2, 1.5, 3] + }, { + "backgroundColor": "rgba(192, 0, 0, 0.25)", + "data": [3.5, 2, 1, 2.5, -2, 3, -1, null, null] + }, { + "backgroundColor": "rgba(128, 0, 128, 0.25)", + "data": [5, 6, 5, -2, -4, -3, 4, 2, 4.5] + }] + }, + "options": { + "responsive": false, + "spanGaps": true, + "legend": false, + "title": false, + "scales": { + "xAxes": [{ + "ticks": { + "display": false + } + }], + "yAxes": [{ + "ticks": { + "display": false + } + }] + }, + "elements": { + "point": { + "radius": 0 + }, + "line": { + "cubicInterpolationMode": "monotone", + "borderColor": "transparent", + "fill": "zero" + } + } + } + }, + "options": { + "canvas": { + "height": 256, + "width": 512 + } + } +} diff --git a/test/fixtures/element.line/fill-line-spline-span.png b/test/fixtures/element.line/fill-line-spline-span.png new file mode 100644 index 00000000000..01aae5a995d Binary files /dev/null and b/test/fixtures/element.line/fill-line-spline-span.png differ diff --git a/test/fixtures/element.line/fill-line-spline.json b/test/fixtures/element.line/fill-line-spline.json new file mode 100644 index 00000000000..543b8cd0600 --- /dev/null +++ b/test/fixtures/element.line/fill-line-spline.json @@ -0,0 +1,55 @@ +{ + "config": { + "type": "line", + "data": { + "labels": ["0", "1", "2", "3", "4", "5", "6", "7", "8"], + "datasets": [{ + "backgroundColor": "rgba(0, 0, 192, 0.25)", + "data": [null, null, 2, 4, 2, 1, -1, 1, 2] + }, { + "backgroundColor": "rgba(0, 192, 0, 0.25)", + "data": [4, 2, null, 3, 2.5, null, -2, 1.5, 3] + }, { + "backgroundColor": "rgba(192, 0, 0, 0.25)", + "data": [3.5, 2, 1, 2.5, -2, 3, -1, null, null] + }, { + "backgroundColor": "rgba(128, 0, 128, 0.25)", + "data": [5, 6, 5, -2, -4, -3, 4, 2, 4.5] + }] + }, + "options": { + "responsive": false, + "spanGaps": false, + "legend": false, + "title": false, + "scales": { + "xAxes": [{ + "ticks": { + "display": false + } + }], + "yAxes": [{ + "ticks": { + "display": false + } + }] + }, + "elements": { + "point": { + "radius": 0 + }, + "line": { + "cubicInterpolationMode": "monotone", + "borderColor": "transparent", + "fill": "zero" + } + } + } + }, + "options": { + "canvas": { + "height": 256, + "width": 512 + } + } +} diff --git a/test/fixtures/element.line/fill-line-spline.png b/test/fixtures/element.line/fill-line-spline.png new file mode 100644 index 00000000000..b2ec1797ff5 Binary files /dev/null and b/test/fixtures/element.line/fill-line-spline.png differ diff --git a/test/fixtures/element.line/fill-line-stepped-span.json b/test/fixtures/element.line/fill-line-stepped-span.json new file mode 100644 index 00000000000..c6f24e1c12b --- /dev/null +++ b/test/fixtures/element.line/fill-line-stepped-span.json @@ -0,0 +1,56 @@ +{ + "config": { + "type": "line", + "data": { + "labels": ["0", "1", "2", "3", "4", "5", "6", "7", "8"], + "datasets": [{ + "backgroundColor": "rgba(0, 0, 192, 0.25)", + "data": [null, null, 2, 4, 2, 1, -1, 1, 2] + }, { + "backgroundColor": "rgba(0, 192, 0, 0.25)", + "data": [4, 2, null, 3, 2.5, null, -2, 1.5, 3] + }, { + "backgroundColor": "rgba(192, 0, 0, 0.25)", + "data": [3.5, 2, 1, 2.5, -2, 3, -1, null, null] + }, { + "backgroundColor": "rgba(128, 0, 128, 0.25)", + "data": [5, 6, 5, -2, -4, -3, 4, 2, 4.5] + }] + }, + "options": { + "responsive": false, + "spanGaps": true, + "legend": false, + "title": false, + "scales": { + "xAxes": [{ + "ticks": { + "display": false + } + }], + "yAxes": [{ + "ticks": { + "display": false + } + }] + }, + "elements": { + "point": { + "radius": 0 + }, + "line": { + "cubicInterpolationMode": "monotone", + "borderColor": "transparent", + "stepped": true, + "fill": "zero" + } + } + } + }, + "options": { + "canvas": { + "height": 256, + "width": 512 + } + } +} diff --git a/test/fixtures/element.line/fill-line-stepped-span.png b/test/fixtures/element.line/fill-line-stepped-span.png new file mode 100644 index 00000000000..9f7c15b9ddb Binary files /dev/null and b/test/fixtures/element.line/fill-line-stepped-span.png differ diff --git a/test/fixtures/element.line/fill-line-stepped.json b/test/fixtures/element.line/fill-line-stepped.json new file mode 100644 index 00000000000..3fbdd7677cc --- /dev/null +++ b/test/fixtures/element.line/fill-line-stepped.json @@ -0,0 +1,56 @@ +{ + "config": { + "type": "line", + "data": { + "labels": ["0", "1", "2", "3", "4", "5", "6", "7", "8"], + "datasets": [{ + "backgroundColor": "rgba(0, 0, 192, 0.25)", + "data": [null, null, 2, 4, 2, 1, -1, 1, 2] + }, { + "backgroundColor": "rgba(0, 192, 0, 0.25)", + "data": [4, 2, null, 3, 2.5, null, -2, 1.5, 3] + }, { + "backgroundColor": "rgba(192, 0, 0, 0.25)", + "data": [3.5, 2, 1, 2.5, -2, 3, -1, null, null] + }, { + "backgroundColor": "rgba(128, 0, 128, 0.25)", + "data": [5, 6, 5, -2, -4, -3, 4, 2, 4.5] + }] + }, + "options": { + "responsive": false, + "spanGaps": false, + "legend": false, + "title": false, + "scales": { + "xAxes": [{ + "ticks": { + "display": false + } + }], + "yAxes": [{ + "ticks": { + "display": false + } + }] + }, + "elements": { + "point": { + "radius": 0 + }, + "line": { + "cubicInterpolationMode": "monotone", + "borderColor": "transparent", + "stepped": true, + "fill": "zero" + } + } + } + }, + "options": { + "canvas": { + "height": 256, + "width": 512 + } + } +} diff --git a/test/fixtures/element.line/fill-line-stepped.png b/test/fixtures/element.line/fill-line-stepped.png new file mode 100644 index 00000000000..19f5a8d9262 Binary files /dev/null and b/test/fixtures/element.line/fill-line-stepped.png differ diff --git a/test/fixtures/element.line/fill-line-top-span.json b/test/fixtures/element.line/fill-line-top-span.json new file mode 100644 index 00000000000..b74b1f5cac3 --- /dev/null +++ b/test/fixtures/element.line/fill-line-top-span.json @@ -0,0 +1,55 @@ +{ + "config": { + "type": "line", + "data": { + "labels": ["0", "1", "2", "3", "4", "5", "6", "7", "8"], + "datasets": [{ + "backgroundColor": "rgba(0, 0, 192, 0.25)", + "data": [null, null, 2, 3, 4, -4, -2, 1, 0] + }, { + "backgroundColor": "rgba(0, 192, 0, 0.25)", + "data": [5.5, 2, null, 4, 5, null, null, 2, 1] + }, { + "backgroundColor": "rgba(192, 0, 0, 0.25)", + "data": [7, 3, 4, 5, 6, 1, 4, null, null] + }, { + "backgroundColor": "rgba(0, 0, 192, 0.25)", + "data": [8, 7, 6.5, -6, -4, -6, 4, 5, 8] + }] + }, + "options": { + "responsive": false, + "spanGaps": true, + "legend": false, + "title": false, + "scales": { + "xAxes": [{ + "ticks": { + "display": false + } + }], + "yAxes": [{ + "ticks": { + "display": false + } + }] + }, + "elements": { + "point": { + "radius": 0 + }, + "line": { + "borderColor": "transparent", + "fill": "top", + "tension": 0 + } + } + } + }, + "options": { + "canvas": { + "height": 256, + "width": 512 + } + } +} diff --git a/test/fixtures/element.line/fill-line-top-span.png b/test/fixtures/element.line/fill-line-top-span.png new file mode 100644 index 00000000000..83519b05fb7 Binary files /dev/null and b/test/fixtures/element.line/fill-line-top-span.png differ diff --git a/test/fixtures/element.line/fill-line-top.json b/test/fixtures/element.line/fill-line-top.json new file mode 100644 index 00000000000..e0696ba0661 --- /dev/null +++ b/test/fixtures/element.line/fill-line-top.json @@ -0,0 +1,55 @@ +{ + "config": { + "type": "line", + "data": { + "labels": ["0", "1", "2", "3", "4", "5", "6", "7", "8"], + "datasets": [{ + "backgroundColor": "rgba(0, 0, 192, 0.25)", + "data": [null, null, 2, 3, 4, -4, -2, 1, 0] + }, { + "backgroundColor": "rgba(0, 192, 0, 0.25)", + "data": [5.5, 2, null, 4, 5, null, null, 2, 1] + }, { + "backgroundColor": "rgba(192, 0, 0, 0.25)", + "data": [7, 3, 4, 5, 6, 1, 4, null, null] + }, { + "backgroundColor": "rgba(0, 0, 192, 0.25)", + "data": [8, 7, 6.5, -6, -4, -6, 4, 5, 8] + }] + }, + "options": { + "responsive": false, + "spanGaps": false, + "legend": false, + "title": false, + "scales": { + "xAxes": [{ + "ticks": { + "display": false + } + }], + "yAxes": [{ + "ticks": { + "display": false + } + }] + }, + "elements": { + "point": { + "radius": 0 + }, + "line": { + "borderColor": "transparent", + "fill": "top", + "tension": 0 + } + } + } + }, + "options": { + "canvas": { + "height": 256, + "width": 512 + } + } +} diff --git a/test/fixtures/element.line/fill-line-top.png b/test/fixtures/element.line/fill-line-top.png new file mode 100644 index 00000000000..25c8f136996 Binary files /dev/null and b/test/fixtures/element.line/fill-line-top.png differ diff --git a/test/fixtures/element.line/fill-line-zero-span.json b/test/fixtures/element.line/fill-line-zero-span.json new file mode 100644 index 00000000000..64fbbb315c5 --- /dev/null +++ b/test/fixtures/element.line/fill-line-zero-span.json @@ -0,0 +1,55 @@ +{ + "config": { + "type": "line", + "data": { + "labels": ["0", "1", "2", "3", "4", "5", "6", "7", "8"], + "datasets": [{ + "backgroundColor": "rgba(0, 0, 192, 0.25)", + "data": [null, null, 2, 3, 4, -4, -2, 1, 0] + }, { + "backgroundColor": "rgba(0, 192, 0, 0.25)", + "data": [6, 2, null, 4, 5, null, null, 2, 1] + }, { + "backgroundColor": "rgba(192, 0, 0, 0.25)", + "data": [7, 3, 4, 5, 6, 1, 4, null, null] + }, { + "backgroundColor": "rgba(0, 64, 192, 0.25)", + "data": [8, 7, 6, -6, -4, -6, 4, 5, 8] + }] + }, + "options": { + "responsive": false, + "spanGaps": true, + "legend": false, + "title": false, + "scales": { + "xAxes": [{ + "ticks": { + "display": false + } + }], + "yAxes": [{ + "ticks": { + "display": false + } + }] + }, + "elements": { + "point": { + "radius": 0 + }, + "line": { + "borderColor": "transparent", + "fill": "zero", + "tension": 0 + } + } + } + }, + "options": { + "canvas": { + "height": 256, + "width": 512 + } + } +} diff --git a/test/fixtures/element.line/fill-line-zero-span.png b/test/fixtures/element.line/fill-line-zero-span.png new file mode 100644 index 00000000000..727cc0309b0 Binary files /dev/null and b/test/fixtures/element.line/fill-line-zero-span.png differ diff --git a/test/fixtures/element.line/fill-line-zero.json b/test/fixtures/element.line/fill-line-zero.json new file mode 100644 index 00000000000..1958fc5d0a1 --- /dev/null +++ b/test/fixtures/element.line/fill-line-zero.json @@ -0,0 +1,55 @@ +{ + "config": { + "type": "line", + "data": { + "labels": ["0", "1", "2", "3", "4", "5", "6", "7", "8"], + "datasets": [{ + "backgroundColor": "rgba(0, 0, 192, 0.25)", + "data": [null, null, 2, 3, 4, -4, -2, 1, 0] + }, { + "backgroundColor": "rgba(0, 192, 0, 0.25)", + "data": [6, 2, null, 4, 5, null, null, 2, 1] + }, { + "backgroundColor": "rgba(192, 0, 0, 0.25)", + "data": [7, 3, 4, 5, 6, 1, 4, null, null] + }, { + "backgroundColor": "rgba(0, 64, 192, 0.25)", + "data": [8, 7, 6, -6, -4, -6, 4, 5, 8] + }] + }, + "options": { + "responsive": false, + "spanGaps": false, + "legend": false, + "title": false, + "scales": { + "xAxes": [{ + "ticks": { + "display": false + } + }], + "yAxes": [{ + "ticks": { + "display": false + } + }] + }, + "elements": { + "point": { + "radius": 0 + }, + "line": { + "borderColor": "transparent", + "fill": "zero", + "tension": 0 + } + } + } + }, + "options": { + "canvas": { + "height": 256, + "width": 512 + } + } +} diff --git a/test/fixtures/element.line/fill-line-zero.png b/test/fixtures/element.line/fill-line-zero.png new file mode 100644 index 00000000000..f8f0208d4fd Binary files /dev/null and b/test/fixtures/element.line/fill-line-zero.png differ diff --git a/test/fixtures/element.line/fill-radar-spline.json b/test/fixtures/element.line/fill-radar-spline.json new file mode 100644 index 00000000000..c22d8a6517c --- /dev/null +++ b/test/fixtures/element.line/fill-radar-spline.json @@ -0,0 +1,50 @@ +{ + "config": { + "type": "radar", + "data": { + "labels": ["0", "1", "2", "3", "4", "5", "6", "7", "8"], + "datasets": [{ + "backgroundColor": "rgba(0, 0, 192, 0.25)", + "data": [null, null, 2, 4, 2, 1, -1, 1, 2] + }, { + "backgroundColor": "rgba(0, 192, 0, 0.25)", + "data": [4, 2, null, 3, 2.5, null, -2, 1.5, 3] + }, { + "backgroundColor": "rgba(192, 0, 0, 0.25)", + "data": [3.5, 2, 1, 2.5, -2, 3, -1, null, null] + }, { + "backgroundColor": "rgba(128, 0, 128, 0.25)", + "data": [5, 6, 5, -2, -4, -3, 4, 2, 4.5] + }] + }, + "options": { + "responsive": false, + "legend": false, + "title": false, + "scale": { + "pointLabels": { + "fontSize": 0 + }, + "ticks": { + "display": false + } + }, + "elements": { + "point": { + "radius": 0 + }, + "line": { + "borderColor": "transparent", + "tension": 0.5, + "fill": "zero" + } + } + } + }, + "options": { + "canvas": { + "height": 256, + "width": 256 + } + } +} diff --git a/test/fixtures/element.line/fill-radar-spline.png b/test/fixtures/element.line/fill-radar-spline.png new file mode 100644 index 00000000000..29b6c8e185e Binary files /dev/null and b/test/fixtures/element.line/fill-radar-spline.png differ diff --git a/test/fixtures/element.line/fill-radar-zero.json b/test/fixtures/element.line/fill-radar-zero.json new file mode 100644 index 00000000000..172007943a7 --- /dev/null +++ b/test/fixtures/element.line/fill-radar-zero.json @@ -0,0 +1,49 @@ +{ + "config": { + "type": "radar", + "data": { + "labels": ["0", "1", "2", "3", "4", "5", "6", "7", "8"], + "datasets": [{ + "backgroundColor": "rgba(0, 0, 192, 0.25)", + "data": [null, null, 2, 4, 2, 1, -1, 1, 2] + }, { + "backgroundColor": "rgba(0, 192, 0, 0.25)", + "data": [4, 2, null, 3, 2.5, null, -2, 1.5, 3] + }, { + "backgroundColor": "rgba(192, 0, 0, 0.25)", + "data": [3.5, 2, 1, 2.5, -2, 3, -1, null, null] + }, { + "backgroundColor": "rgba(128, 0, 128, 0.25)", + "data": [5, 6, 5, -2, -4, -3, 4, 2, 4.5] + }] + }, + "options": { + "responsive": false, + "legend": false, + "title": false, + "scale": { + "pointLabels": { + "fontSize": 0 + }, + "ticks": { + "display": false + } + }, + "elements": { + "point": { + "radius": 0 + }, + "line": { + "borderColor": "transparent", + "fill": "zero" + } + } + } + }, + "options": { + "canvas": { + "height": 256, + "width": 256 + } + } +} diff --git a/test/fixtures/element.line/fill-radar-zero.png b/test/fixtures/element.line/fill-radar-zero.png new file mode 100644 index 00000000000..5ede186f2c6 Binary files /dev/null and b/test/fixtures/element.line/fill-radar-zero.png differ diff --git a/test/jasmine.index.js b/test/jasmine.index.js index aeaee77c6c4..55c587ab0a2 100644 --- a/test/jasmine.index.js +++ b/test/jasmine.index.js @@ -37,6 +37,8 @@ var utils = require('./jasmine.utils'); 'position: absolute' + '}'); + jasmine.specsFromFixtures = utils.specsFromFixtures; + beforeEach(function() { jasmine.addMatchers(matchers); }); diff --git a/test/jasmine.matchers.js b/test/jasmine.matchers.js index abb90d7789e..8a3c9e121a3 100644 --- a/test/jasmine.matchers.js +++ b/test/jasmine.matchers.js @@ -1,5 +1,56 @@ 'use strict'; +var pixelmatch = require('pixelmatch'); +var utils = require('./jasmine.utils'); + +function toPercent(value) { + return Math.round(value * 10000) / 100; +} + +function createImageData(w, h) { + var canvas = utils.createCanvas(w, h); + var context = canvas.getContext('2d'); + return context.getImageData(0, 0, w, h); +} + +function canvasFromImageData(data) { + var canvas = utils.createCanvas(data.width, data.height); + var context = canvas.getContext('2d'); + context.putImageData(data, 0, 0); + return canvas; +} + +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(canvasFromImageData(values.data)); + wrapper.appendChild(item); + }); + + // WORKAROUND: https://github.com/karma-runner/karma-jasmine/issues/139 + wrapper.indexOf = function() { + return -1; + }; + + return wrapper; +} + function toBeCloseToPixel() { return { compare: function(actual, expected) { @@ -105,9 +156,50 @@ function toBeChartOfSize() { }; } +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 = 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 + }; + } + }; +} + module.exports = { toBeCloseToPixel: toBeCloseToPixel, toEqualOneOf: toEqualOneOf, toBeValidChart: toBeValidChart, - toBeChartOfSize: toBeChartOfSize + toBeChartOfSize: toBeChartOfSize, + toEqualImageData: toEqualImageData }; diff --git a/test/jasmine.utils.js b/test/jasmine.utils.js index 77f05c6e008..f5ead49ec09 100644 --- a/test/jasmine.utils.js +++ b/test/jasmine.utils.js @@ -1,3 +1,40 @@ +/* global __karma__ */ + +function loadJSON(url, callback) { + var request = new XMLHttpRequest(); + request.onreadystatechange = function() { + if (request.readyState === 4) { + return callback(JSON.parse(request.responseText)); + } + }; + + request.overrideMimeType('application/json'); + request.open('GET', url, true); + request.send(null); +} + +function createCanvas(w, h) { + var canvas = document.createElement('canvas'); + canvas.width = w; + canvas.height = h; + return canvas; +} + +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; +} + /** * Injects a new canvas (and div wrapper) and creates teh associated Chart instance * using the given config. Additional options allow tweaking elements generation. @@ -69,8 +106,53 @@ function injectCSS(css) { head.appendChild(style); } +function specFromFixture(description, inputs) { + it(inputs.json, function(done) { + loadJSON(inputs.json, function(json) { + var chart = acquireChart(json.config, json.options); + if (!inputs.png) { + fail('Missing PNG comparison file for ' + inputs.json); + if (!json.debug) { + releaseChart(chart); + } + done(); + } + + readImageData(inputs.png, function(expected) { + expect(chart).toEqualImageData(expected, json); + releaseChart(chart); + done(); + }); + }); + }); +} + +function specsFromFixtures(path) { + var regex = new RegExp('(^/base/test/fixtures/' + path + '.+)\\.(png|json)'); + 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]); + }); + }; +} + module.exports = { injectCSS: injectCSS, + createCanvas: createCanvas, acquireChart: acquireChart, - releaseChart: releaseChart + releaseChart: releaseChart, + specsFromFixtures: specsFromFixtures }; diff --git a/test/specs/element.line.tests.js b/test/specs/element.line.tests.js index 0a2a6eced56..52d94cd377c 100644 --- a/test/specs/element.line.tests.js +++ b/test/specs/element.line.tests.js @@ -1,5 +1,7 @@ // Tests for the line element -describe('Line element tests', function() { +describe('Chart.elements.Line', function() { + describe('auto', jasmine.specsFromFixtures('element.line')); + it('should be constructed', function() { var line = new Chart.elements.Line({ _datasetindex: 2,