From 06b2623860bdc52aebf7575f287b06f98670aaa8 Mon Sep 17 00:00:00 2001 From: wimrijnders Date: Fri, 13 Oct 2017 22:49:41 +0200 Subject: [PATCH] Use mock canvas object replacing `canvas` (#3518) * Use mock canvas object replacing `canvas` Fixes #3515. A mock canvas object is added to the unit tests, which makes usage of module `canvas` voluntary. The issue with `canvas` is that it requires an external dependency to `cairo`. This complicates setting up a develop environment for `vis.js` - Removed `canvas` from `package.json` - Added section to README.md with instructions on how to install `canvas` instead. * Removed debugger statements * Updates for review * Fixes for review --- README.md | 13 +++++++ package.json | 11 +++--- test/Graph3d.test.js | 2 + test/Label.test.js | 2 + test/Network.test.js | 8 +++- test/canvas-mock.js | 90 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 118 insertions(+), 8 deletions(-) create mode 100644 test/canvas-mock.js diff --git a/README.md b/README.md index 3b965d4d..36875ebc 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,16 @@ Link via cdnjs: http://cdnjs.com Or download the library from the github project: [https://github.com/almende/vis.git](https://github.com/almende/vis.git). + +### Installing module `canvas` + +Module `canvas` is only required if you need to run `vis.js` on `node.js` and require actual output. It is not required for regular usage in a browser. + +Currently, the unit tests use a mock object for canvas which has limited but adequate functionality. If `canvas` is installed, that will be used silently in place of the mock object. + +The issue with `canvas` is that it has an external dependency to `cairo`. This needs to be installed outside of the regular install as done by `npm`. Please consult [`node-canvas` github page](https://github.com/Automattic/node-canvas/wiki#desktop) for the correct installation procecure your platform + + ## Load To use a component, include the javascript and css files of vis in your web page: @@ -322,6 +332,9 @@ module: { There is also an [demo-project](https://github.com/mojoaxel/vis-webpack-demo) showing the integration of vis.js using webpack. + + + ## Test To test the library, install the project dependencies once: diff --git a/package.json b/package.json index 42e04ea2..dd09a2ed 100644 --- a/package.json +++ b/package.json @@ -34,29 +34,28 @@ }, "dependencies": { "emitter-component": "^1.1.1", - "moment": "^2.18.1", - "propagating-hammerjs": "^1.4.6", "hammerjs": "^2.0.8", - "keycharm": "^0.2.0" + "keycharm": "^0.2.0", + "moment": "^2.18.1", + "propagating-hammerjs": "^1.4.6" }, "devDependencies": { "async": "^2.5.0", "babel-core": "^6.25.0", "babel-loader": "^7.1.1", - "babel-polyfill": "^6.23.0", "babel-plugin-transform-es3-member-expression-literals": "^6.22.0", "babel-plugin-transform-es3-property-literals": "^6.22.0", "babel-plugin-transform-runtime": "^6.23.0", + "babel-polyfill": "^6.23.0", "babel-preset-es2015": "^6.24.1", "babel-runtime": "^6.23.0", "babelify": "^7.3.0", - "canvas": "^1.6.5", "clean-css": "^4.1.7", "eslint": "^4.3.0", "gulp": "^3.9.1", - "gulp-eslint": "^4.0.0", "gulp-clean-css": "^3.7.0", "gulp-concat": "^2.6.1", + "gulp-eslint": "^4.0.0", "gulp-rename": "^1.2.2", "gulp-util": "^3.0.8", "jsdom": "9.12.0", diff --git a/test/Graph3d.test.js b/test/Graph3d.test.js index 5c5e76ea..f14136cc 100644 --- a/test/Graph3d.test.js +++ b/test/Graph3d.test.js @@ -2,6 +2,7 @@ var assert = require('assert'); var vis = require('../dist/vis'); var Graph3d = vis.Graph3d; var jsdom_global = require('jsdom-global'); +var canvasMockify = require('./canvas-mock'); var stdout = require('test-console').stdout; var Validator = require("./../lib/shared/Validator").default; //var {printStyle} = require('./../lib/shared/Validator'); @@ -16,6 +17,7 @@ describe('Graph3d', function () { "
", { skipWindowCheck: true} ); + canvasMockify(window); this.container = document.getElementById('mygraph'); }); diff --git a/test/Label.test.js b/test/Label.test.js index e92b43c7..7a7421f0 100644 --- a/test/Label.test.js +++ b/test/Label.test.js @@ -13,6 +13,7 @@ var Label = require('../lib/network/modules/components/shared/Label').default; var NodesHandler = require('../lib/network/modules/NodesHandler').default; var util = require('../lib/util'); var jsdom_global = require('jsdom-global'); +var canvasMockify = require('./canvas-mock'); var vis = require('../dist/vis'); var Network = vis.network; @@ -324,6 +325,7 @@ describe('Network Label', function() { "
", { skipWindowCheck: true} ); + canvasMockify(window); this.container = document.getElementById('mynetwork'); }); diff --git a/test/Network.test.js b/test/Network.test.js index fea29b55..19ea7428 100644 --- a/test/Network.test.js +++ b/test/Network.test.js @@ -14,9 +14,10 @@ var fs = require('fs'); var assert = require('assert'); var vis = require('../dist/vis'); var Network = vis.network; -var jsdom_global = require('jsdom-global'); var stdout = require('test-console').stdout; var Validator = require("./../lib/shared/Validator").default; +var jsdom_global = require('jsdom-global'); +var canvasMockify = require('./canvas-mock'); var {allOptions, configureOptions} = require('./../lib/network/options.js'); //var {printStyle} = require('./../lib/shared/Validator'); @@ -222,6 +223,8 @@ function checkFontProperties(fontItem, checkStrict = true) { + + describe('Network', function () { before(function() { @@ -229,6 +232,7 @@ describe('Network', function () { "
", { skipWindowCheck: true} ); + canvasMockify(window); this.container = document.getElementById('mynetwork'); }); @@ -324,6 +328,7 @@ describe('Network', function () { * The real deterrent is eslint rule 'guard-for-in`. */ it('can deal with added fields in Array.prototype', function (done) { + var canvas = window.document.createElement('canvas'); Array.prototype.foo = 1; // Just add anything to the prototype Object.prototype.bar = 2; // Let's screw up hashes as well @@ -688,7 +693,6 @@ describe('Edge', function () { describe('Clustering', function () { - it('properly handles options allowSingleNodeCluster', function() { var [network, data, numNodes, numEdges] = createSampleNetwork(); data.edges.update({from: 1, to: 11,}); diff --git a/test/canvas-mock.js b/test/canvas-mock.js new file mode 100644 index 00000000..fa7a08f9 --- /dev/null +++ b/test/canvas-mock.js @@ -0,0 +1,90 @@ +/** + * Set up mock 2D context, for usage in unit tests. + * + * Adapted from: https://github.com/Cristy94/canvas-mock + */ + +var canvasMock; // Use one canvas instance for all calls to createElement('canvas'); + + +function replaceCanvasContext (el) { + el.getContext = function() { + return { + fillRect: function() {}, + clearRect: function(){}, + getImageData: function(x, y, w, h) { + return { + data: new Array(w*h*4) + }; + }, + putImageData: function() {}, + createImageData: function(){ return []}, + setTransform: function(){}, + drawImage: function(){}, + save: function(){}, + fillText: function(){}, + restore: function(){}, + beginPath: function(){}, + moveTo: function(){}, + lineTo: function(){}, + closePath: function(){}, + stroke: function(){}, + translate: function(){}, + scale: function(){}, + rotate: function(){}, + arc: function(){}, + fill: function(){}, + + // + // Following added for vis.js unit tests + // + + measureText: function(text) { + return { + width: 12*text.length, + height: 14 + }; + }, + }; + } +}; + + +/** + * Overrides document.createElement(), in order to supply a custom canvas element. + * + * In the canvas element, getContext() is overridden in order to supply a simple + * mock object for the 2D context. For all other elements, the call functions unchanged. + * + * The override is only done if there is no 2D context already present. + * This allows for normal running in a browser, and for node.js the usage of 'canvas'. + * + * @param {object} the current global window object. This can possible come from module 'jsdom', + * when running under node.js. + */ +function mockify(window) { + var d = window.document; + var f = window.document.createElement; + + // Check if 2D context already present. That happens either when running in a browser, + // or this is node.js with 'canvas' installed. + var ctx = d.createElement('canvas').getContext('2d'); + if (ctx !== null && ctx !== undefined) { + //console.log('2D context is present, no need to override'); + return; + } + + window.document.createElement = function(param) { + if (param === 'canvas') { + if (canvasMock === undefined) { + canvasMock = f.call(d, 'canvas'); + replaceCanvasContext(canvasMock); + } + return canvasMock; + } else { + return f.call(d, param); + } + }; +} + +module.exports = mockify;