/** * Set up mock 2D context, for usage in unit tests. * * Adapted from: https://github.com/Cristy94/canvas-mock */ var jsdom = require('jsdom'); var jsdom_global = require('jsdom-global'); 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(){}, text: function(){}, fillText: function(){}, restore: function(){}, beginPath: function(){}, moveTo: function(){}, lineTo: function(){}, closePath: function(){}, stroke: function(){}, translate: function(){}, scale: function(){}, rotate: function(){}, circle: 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} window - current global window object. This can possibly come from module 'jsdom', * when running under node.js. * @private */ function overrideCreateElement(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); } }; } /** * 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 'style' * property on a newly created svg element. * * @param {object} window - current global window object. This can possibly come from module 'jsdom', * when running under node.js. * @private */ function overrideCreateElementNS(window) { var d = window.document; var f = window.document.createElementNS; window.document.createElementNS = function(namespaceURI, qualifiedName) { if (namespaceURI === 'http://www.w3.org/2000/svg') { var result = f.call(d, namespaceURI, qualifiedName); if (result.style == undefined) { result.style = {}; return result; } } }; } /** * Initialize the mock, jsdom and jsdom_global for unit test usage. * * Suppresses a warning from `jsdom` on usage of `getContext()`. A mock definition is added for * it, so the message is not relevant. * * @param {string} [html=''] html definitions which should be added to the jsdom definition * @returns {function} function to call in after(), to clean up for `jsdom_global` */ function mockify(html = '') { // Start of message that we want to suppress. let msg = 'Error: Not implemented: HTMLCanvasElement.prototype.getContext' + ' (without installing the canvas npm package)'; // Override default virtual console of jsdom const virtualConsole = new jsdom.VirtualConsole(); // Set up a simple 'mock' console output. Only 'error' needs to be overridden let myConsole = { error: (msg) => { if (msg.indexOf(msg) === 0) { //console.error('all is well'); } else { // All other messages pass through console.error(msg); } } }; // Using the global catch instead of specific event handler, because I couldn't get them to work virtualConsole.sendTo(myConsole); let cleanupFunction = jsdom_global( html, { skipWindowCheck: true, virtualConsole: virtualConsole} ); overrideCreateElement(window); // The actual initialization of canvas-mock overrideCreateElementNS(window); return cleanupFunction; } module.exports = mockify;