Browse Source

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
mbroad/code-climate-coverage-develop
wimrijnders 7 years ago
committed by Yotam Berkowitz
parent
commit
06b2623860
6 changed files with 118 additions and 8 deletions
  1. +13
    -0
      README.md
  2. +5
    -6
      package.json
  3. +2
    -0
      test/Graph3d.test.js
  4. +2
    -0
      test/Label.test.js
  5. +6
    -2
      test/Network.test.js
  6. +90
    -0
      test/canvas-mock.js

+ 13
- 0
README.md View File

@ -36,6 +36,16 @@ Link via cdnjs: http://cdnjs.com
Or download the library from the github project: Or download the library from the github project:
[https://github.com/almende/vis.git](https://github.com/almende/vis.git). [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 ## Load
To use a component, include the javascript and css files of vis in your web page: 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. There is also an [demo-project](https://github.com/mojoaxel/vis-webpack-demo) showing the integration of vis.js using webpack.
## Test ## Test
To test the library, install the project dependencies once: To test the library, install the project dependencies once:

+ 5
- 6
package.json View File

@ -34,29 +34,28 @@
}, },
"dependencies": { "dependencies": {
"emitter-component": "^1.1.1", "emitter-component": "^1.1.1",
"moment": "^2.18.1",
"propagating-hammerjs": "^1.4.6",
"hammerjs": "^2.0.8", "hammerjs": "^2.0.8",
"keycharm": "^0.2.0"
"keycharm": "^0.2.0",
"moment": "^2.18.1",
"propagating-hammerjs": "^1.4.6"
}, },
"devDependencies": { "devDependencies": {
"async": "^2.5.0", "async": "^2.5.0",
"babel-core": "^6.25.0", "babel-core": "^6.25.0",
"babel-loader": "^7.1.1", "babel-loader": "^7.1.1",
"babel-polyfill": "^6.23.0",
"babel-plugin-transform-es3-member-expression-literals": "^6.22.0", "babel-plugin-transform-es3-member-expression-literals": "^6.22.0",
"babel-plugin-transform-es3-property-literals": "^6.22.0", "babel-plugin-transform-es3-property-literals": "^6.22.0",
"babel-plugin-transform-runtime": "^6.23.0", "babel-plugin-transform-runtime": "^6.23.0",
"babel-polyfill": "^6.23.0",
"babel-preset-es2015": "^6.24.1", "babel-preset-es2015": "^6.24.1",
"babel-runtime": "^6.23.0", "babel-runtime": "^6.23.0",
"babelify": "^7.3.0", "babelify": "^7.3.0",
"canvas": "^1.6.5",
"clean-css": "^4.1.7", "clean-css": "^4.1.7",
"eslint": "^4.3.0", "eslint": "^4.3.0",
"gulp": "^3.9.1", "gulp": "^3.9.1",
"gulp-eslint": "^4.0.0",
"gulp-clean-css": "^3.7.0", "gulp-clean-css": "^3.7.0",
"gulp-concat": "^2.6.1", "gulp-concat": "^2.6.1",
"gulp-eslint": "^4.0.0",
"gulp-rename": "^1.2.2", "gulp-rename": "^1.2.2",
"gulp-util": "^3.0.8", "gulp-util": "^3.0.8",
"jsdom": "9.12.0", "jsdom": "9.12.0",

+ 2
- 0
test/Graph3d.test.js View File

@ -2,6 +2,7 @@ var assert = require('assert');
var vis = require('../dist/vis'); var vis = require('../dist/vis');
var Graph3d = vis.Graph3d; var Graph3d = vis.Graph3d;
var jsdom_global = require('jsdom-global'); var jsdom_global = require('jsdom-global');
var canvasMockify = require('./canvas-mock');
var stdout = require('test-console').stdout; var stdout = require('test-console').stdout;
var Validator = require("./../lib/shared/Validator").default; var Validator = require("./../lib/shared/Validator").default;
//var {printStyle} = require('./../lib/shared/Validator'); //var {printStyle} = require('./../lib/shared/Validator');
@ -16,6 +17,7 @@ describe('Graph3d', function () {
"<div id='mygraph'></div>", "<div id='mygraph'></div>",
{ skipWindowCheck: true} { skipWindowCheck: true}
); );
canvasMockify(window);
this.container = document.getElementById('mygraph'); this.container = document.getElementById('mygraph');
}); });

+ 2
- 0
test/Label.test.js View File

@ -13,6 +13,7 @@ var Label = require('../lib/network/modules/components/shared/Label').default;
var NodesHandler = require('../lib/network/modules/NodesHandler').default; var NodesHandler = require('../lib/network/modules/NodesHandler').default;
var util = require('../lib/util'); var util = require('../lib/util');
var jsdom_global = require('jsdom-global'); var jsdom_global = require('jsdom-global');
var canvasMockify = require('./canvas-mock');
var vis = require('../dist/vis'); var vis = require('../dist/vis');
var Network = vis.network; var Network = vis.network;
@ -324,6 +325,7 @@ describe('Network Label', function() {
"<div id='mynetwork'></div>", "<div id='mynetwork'></div>",
{ skipWindowCheck: true} { skipWindowCheck: true}
); );
canvasMockify(window);
this.container = document.getElementById('mynetwork'); this.container = document.getElementById('mynetwork');
}); });

+ 6
- 2
test/Network.test.js View File

@ -14,9 +14,10 @@ var fs = require('fs');
var assert = require('assert'); var assert = require('assert');
var vis = require('../dist/vis'); var vis = require('../dist/vis');
var Network = vis.network; var Network = vis.network;
var jsdom_global = require('jsdom-global');
var stdout = require('test-console').stdout; var stdout = require('test-console').stdout;
var Validator = require("./../lib/shared/Validator").default; 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 {allOptions, configureOptions} = require('./../lib/network/options.js');
//var {printStyle} = require('./../lib/shared/Validator'); //var {printStyle} = require('./../lib/shared/Validator');
@ -222,6 +223,8 @@ function checkFontProperties(fontItem, checkStrict = true) {
describe('Network', function () { describe('Network', function () {
before(function() { before(function() {
@ -229,6 +232,7 @@ describe('Network', function () {
"<div id='mynetwork'></div>", "<div id='mynetwork'></div>",
{ skipWindowCheck: true} { skipWindowCheck: true}
); );
canvasMockify(window);
this.container = document.getElementById('mynetwork'); this.container = document.getElementById('mynetwork');
}); });
@ -324,6 +328,7 @@ describe('Network', function () {
* The real deterrent is eslint rule 'guard-for-in`. * The real deterrent is eslint rule 'guard-for-in`.
*/ */
it('can deal with added fields in Array.prototype', function (done) { 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 Array.prototype.foo = 1; // Just add anything to the prototype
Object.prototype.bar = 2; // Let's screw up hashes as well Object.prototype.bar = 2; // Let's screw up hashes as well
@ -688,7 +693,6 @@ describe('Edge', function () {
describe('Clustering', function () { describe('Clustering', function () {
it('properly handles options allowSingleNodeCluster', function() { it('properly handles options allowSingleNodeCluster', function() {
var [network, data, numNodes, numEdges] = createSampleNetwork(); var [network, data, numNodes, numEdges] = createSampleNetwork();
data.edges.update({from: 1, to: 11,}); data.edges.update({from: 1, to: 11,});

+ 90
- 0
test/canvas-mock.js View File

@ -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;

Loading…
Cancel
Save