diff --git a/circle.yml b/circle.yml index a9a93cf0840..2615242402e 100644 --- a/circle.yml +++ b/circle.yml @@ -1,17 +1,17 @@ general: artifacts: - - "build/test_images/" # relative to the build directory - - "build/test_images_diff/" # relative to the build directory + - "build/test_images/" + - "build/test_images_diff/" machine: node: - version: 4.2.1 + version: 4.2.1 services: - docker - + dependencies: - pre: + pre: - docker pull plotly/imageserver:latest post: - npm run cibuild diff --git a/tasks/baseline.sh b/tasks/baseline.sh index 02396268504..3b60509bc27 100755 --- a/tasks/baseline.sh +++ b/tasks/baseline.sh @@ -2,23 +2,12 @@ # # TODO adapt this for Windows # -# TODO add package.json config arguments to configure: -# - container name, -# - ports and -# - imageserver version -# # =============================================================================== -CONTAINER_NAME="imagetest" -IMAGE_NAME="registry.plot.ly:5000/imageserver" -IMAGE_VERSION="1.3.0" +CONTAINER_NAME="imagetest" # same as in docker-compose.yml -# Run docker container: -# -# docker run -d --name $CONTAINER_NAME \ -# -v $PWD/plotly.js:/var/www/streambed/image_server/plotly.js \ -# -p 9010:9010 -p 2022:22 \ -# $IMAGE_NAME:[$IMAGE_VERSION] +# create/run/start docker container with: +# $ docker-compose up -d CMD=( "cd /var/www/streambed/image_server/plotly.js &&" diff --git a/tasks/test_image.sh b/tasks/test_image.sh index 9dee5295017..d4f3de554a5 100755 --- a/tasks/test_image.sh +++ b/tasks/test_image.sh @@ -2,23 +2,12 @@ # # TODO adapt this for Windows # -# TODO add package.json config arguments to configure: -# - container name, -# - ports and -# - imageserver version -# # =============================================================================== -CONTAINER_NAME="imagetest" -IMAGE_NAME="registry.plot.ly:5000/imageserver" -IMAGE_VERSION="1.3.0" +CONTAINER_NAME="imagetest" # same as in docker-compose.yml -# Run docker container: -# -# docker run -d --name $CONTAINER_NAME \ -# -v $PWD/plotly.js:/var/www/streambed/image_server/plotly.js \ -# -p 9010:9010 -p 2022:22 \ -# $IMAGE_NAME:[$IMAGE_VERSION] +# create/run/start docker container with: +# $ docker-compose up -d CMD=( "cd /var/www/streambed/image_server/plotly.js &&" diff --git a/test/image/README.md b/test/image/README.md index a8afc9861cf..02851f5fa37 100644 --- a/test/image/README.md +++ b/test/image/README.md @@ -71,6 +71,9 @@ docker images docker ps -a ``` +whereas `docker ps` lists only the started containers. + + ### Stop your testing container Inside your `plotly.js` directory, run @@ -87,4 +90,4 @@ Inside your `plotly.js` directory, run docker-compose rm -f ``` -For more comprehensive information about docker, please refer to [docker document](http://docs.docker.com/) +For more comprehensive information about docker, please refer to the [docker docs](http://docs.docker.com/). diff --git a/test/image/compare_pixels_test.js b/test/image/compare_pixels_test.js index f709cde767b..0caf0136bff 100644 --- a/test/image/compare_pixels_test.js +++ b/test/image/compare_pixels_test.js @@ -26,12 +26,21 @@ else runSingle(userFileName); function runAll () { test('testing mocks', function (t) { - console.error('### beginning pixel comparison tests ###'); var files = fs.readdirSync(constants.pathToTestImageMocks); - // -1 for font-wishlist and - // -38 for the gl2d mocks - t.plan(files.length - 39); + /* + * Some test cases exhibit run-to-run randomness; + * skip over these few test cases for now. + * + * More info: + * https://github.com/plotly/plotly.js/issues/62 + * + * 40 test cases are removed: + * - font-wishlist (1 test case) + * - all gl2d (38) + * - gl2d_bunny-hull (1) + */ + t.plan(files.length - 40); for (var i = 0; i < files.length; i ++) { testMock(files[i], t); @@ -54,6 +63,9 @@ function testMock (fileName, t) { // TODO fix race condition in gl2d image generation if(fileName.indexOf('gl2d_') !== -1) return; + // TODO fix run-to-run randomness + if(fileName === 'gl3d_bunny-hull.json') return; + var figure = require(path.join(constants.pathToTestImageMocks, fileName)); var bodyMock = { figure: figure, @@ -66,7 +78,6 @@ function testMock (fileName, t) { var diffPath = path.join(constants.pathToTestImagesDiff, 'diff-' + imageFileName); var savedImageStream = fs.createWriteStream(savedImagePath); var options = getOptions(bodyMock, 'http://localhost:9010/'); - var statusCode; function checkImage () { var options = { @@ -75,17 +86,12 @@ function testMock (fileName, t) { tolerance: 0.0 }; - if(statusCode === 485) { - console.error(imageFileName, '- skip'); - } - else { - gm.compare( - savedImagePath, - path.join(constants.pathToTestImageBaselines, imageFileName), - options, - onEqualityCheck - ); - } + gm.compare( + savedImagePath, + path.join(constants.pathToTestImageBaselines, imageFileName), + options, + onEqualityCheck + ); } function onEqualityCheck (err, isEqual) { @@ -95,16 +101,12 @@ function testMock (fileName, t) { } if (isEqual) { fs.unlinkSync(diffPath); - console.error(imageFileName + ' is pixel perfect'); } - t.ok(isEqual, savedImagePath + ' should be pixel perfect'); + t.ok(isEqual, imageFileName + ' should be pixel perfect'); } request(options) - .on('response', function(response) { - statusCode = response.statusCode; - }) .pipe(savedImageStream) .on('close', checkImage); } diff --git a/test/jasmine/assets/create_graph_div.js b/test/jasmine/assets/create_graph_div.js new file mode 100644 index 00000000000..0788b7f5dbc --- /dev/null +++ b/test/jasmine/assets/create_graph_div.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = function createGraphDiv() { + var gd = document.createElement('div'); + gd.id = 'graph'; + document.body.appendChild(gd); + return gd; +}; diff --git a/test/jasmine/assets/destroy_graph_div.js b/test/jasmine/assets/destroy_graph_div.js new file mode 100644 index 00000000000..84f3d72eaa7 --- /dev/null +++ b/test/jasmine/assets/destroy_graph_div.js @@ -0,0 +1,6 @@ +'use strict'; + +module.exports = function destroyGraphDiv() { + var gd = document.getElementById('graph'); + document.body.removeChild(gd); +}; diff --git a/test/jasmine/karma.ciconf.js b/test/jasmine/karma.ciconf.js index 5772b47ae00..3e044c77df8 100644 --- a/test/jasmine/karma.ciconf.js +++ b/test/jasmine/karma.ciconf.js @@ -7,6 +7,15 @@ function func(config) { func.defaultConfig.logLevel = config.LOG_INFO; // Continuous Integration mode + + /* + * WebGL interaction test cases fail on the CircleCI + * most likely due to a WebGL/driver issue; + * exclude them from the CircleCI test bundle. + * + */ + func.defaultConfig.exclude = ['tests/gl_plot_interact_test.js']; + // if true, Karma captures browsers, runs the tests and exits func.defaultConfig.singleRun = true; diff --git a/test/jasmine/tests/gl_plot_interact_test.js b/test/jasmine/tests/gl_plot_interact_test.js new file mode 100644 index 00000000000..abc7807a83e --- /dev/null +++ b/test/jasmine/tests/gl_plot_interact_test.js @@ -0,0 +1,46 @@ +var d3 = require('d3'); + +var Plotly = require('@src/index'); + +var createGraphDiv = require('../assets/create_graph_div'); +var destroyGraphDiv = require('../assets/destroy_graph_div'); + +/* + * WebGL interaction test cases fail on the CircleCI + * most likely due to a WebGL/driver issue + * + */ + + +describe('Test plot structure', function () { + 'use strict'; + + afterEach(destroyGraphDiv); + + describe('gl3d plots', function() { + var mock = require('@mocks/gl3d_marker-arrays.json'); + + beforeEach(function(done) { + Plotly.plot(createGraphDiv(), mock.data, mock.layout).then(done); + }); + + it('has one *canvas* node', function() { + var nodes = d3.selectAll('canvas'); + expect(nodes[0].length).toEqual(1); + }); + }); + + describe('gl2d plots', function() { + var mock = require('@mocks/gl2d_10.json'); + + beforeEach(function(done) { + Plotly.plot(createGraphDiv(), mock.data, mock.layout).then(done); + }); + + it('has one *canvas* node', function() { + var nodes = d3.selectAll('canvas'); + expect(nodes[0].length).toEqual(1); + }); + }); + +}); diff --git a/test/jasmine/tests/plot_interact_test.js b/test/jasmine/tests/plot_interact_test.js new file mode 100644 index 00000000000..bddb8492853 --- /dev/null +++ b/test/jasmine/tests/plot_interact_test.js @@ -0,0 +1,142 @@ +var d3 = require('d3'); + +var Plotly = require('@src/index'); +var Fx = require('@src/plots/cartesian/graph_interact'); + +var createGraphDiv = require('../assets/create_graph_div'); +var destroyGraphDiv = require('../assets/destroy_graph_div'); + + +describe('Test plot structure', function () { + 'use strict'; + + afterEach(destroyGraphDiv); + + describe('cartesian plots', function() { + describe('scatter traces', function() { + var mock = require('@mocks/14.json'); + + beforeEach(function(done) { + Plotly.plot(createGraphDiv(), mock.data, mock.layout).then(done); + }); + + it('has one *subplot xy* node', function() { + var nodes = d3.selectAll('g.subplot.xy'); + expect(nodes[0].length).toEqual(1); + }); + + it('has one *scatterlayer* node', function() { + var nodes = d3.selectAll('g.scatterlayer'); + expect(nodes[0].length).toEqual(1); + }); + + it('has as many *trace scatter* nodes as there are traces', function() { + var nodes = d3.selectAll('g.trace.scatter'); + expect(nodes[0].length).toEqual(mock.data.length); + }); + + it('has as many *point* nodes as there are traces', function() { + var nodes = d3.selectAll('path.point'); + + var Npts = 0; + mock.data.forEach(function(trace) { + Npts += trace.x.length; + }); + + expect(nodes[0].length).toEqual(Npts); + }); + + it('responds to hover', function() { + var gd = document.getElementById('graph'); + + var evt = { + clientX: gd.layout.width/ 2, + clientY: gd.layout.height / 2 + }; + + Fx.hover('graph', evt, 'xy'); + + var hoverTrace = gd._hoverdata[0]; + + expect(hoverTrace.curveNumber).toEqual(0); + expect(hoverTrace.pointNumber).toEqual(17); + expect(hoverTrace.x).toEqual(0.388); + expect(hoverTrace.y).toEqual(1); + + expect(d3.selectAll('g.axistext')[0].length).toEqual(1); + expect(d3.selectAll('g.hovertext')[0].length).toEqual(1); + }); + }); + + describe('pie traces', function() { + var mock = require('@mocks/pie_simple.json'); + + beforeEach(function(done) { + Plotly.plot(createGraphDiv(), mock.data, mock.layout).then(done); + }); + + it('has as many *slice* nodes as there are pie items', function() { + var nodes = d3.selectAll('g.slice'); + + var Npts = 0; + mock.data.forEach(function(trace) { + Npts += trace.values.length; + }); + + expect(nodes[0].length).toEqual(Npts); + }); + }); + + }); + + describe('geo plots', function() { + var mock = require('@mocks/geo_first.json'); + + beforeEach(function(done) { + Plotly.plot(createGraphDiv(), mock.data, mock.layout).then(done); + }); + + it('has as many *choroplethlocation* nodes as there are choropleth locations', function() { + var nodes = d3.selectAll('path.choroplethlocation'); + + var Npts = 0; + mock.data.forEach(function(trace) { + var items = trace.locations; + if(items) Npts += items.length; + }); + + expect(nodes[0].length).toEqual(Npts); + }); + + it('has as many *point* nodes as there are marker points', function() { + var nodes = d3.selectAll('path.point'); + + var Npts = 0; + mock.data.forEach(function(trace) { + var items = trace.lat; + if(items) Npts += items.length; + }); + + expect(nodes[0].length).toEqual(Npts); + }); + }); + + describe('polar plots', function() { + var mock = require('@mocks/polar_scatter.json'); + + beforeEach(function(done) { + Plotly.plot(createGraphDiv(), mock.data, mock.layout).then(done); + }); + + it('has as many *mark dot* nodes as there are points', function() { + var nodes = d3.selectAll('path.mark.dot'); + + var Npts = 0; + mock.data.forEach(function(trace) { + Npts += trace.r.length; + }); + + expect(nodes[0].length).toEqual(Npts); + }); + }); +});