Cristy94 / canvas-mock

I created this to be used when running Phaser.js in a Node.js environment. This can be used instead of node-canvas to simulate the existance of canvas.
MIT License
5 stars 1 forks source link

More general way of adding getContext() to a mock canvas object. #2

Open wimrijnders opened 7 years ago

wimrijnders commented 7 years ago

The issue with the current way of doing it, is that the change is not global; i.e. the mock canvas object is reset during regular usage by any component. This makes it kind of useless for unit tests.

The following works for me, I hope you can adapt something similar:

var myHackedInCanvas;    // Use one global canvas instance for all calls to createElement('canvas');

function replaceCanvasContext (el) {
  // The previous export
}

/**
 * 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 possibly 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 (myHackedInCanvas === undefined) {
        myHackedInCanvas = f.call(d, 'canvas');
        replaceCanvasContext(myHackedInCanvas);
      }
      return myHackedInCanvas;
    } else {
      return f.call(d, param);
    }
  };
}

module.exports = mockify;

This does change the object to pass in, so in a sense breaking. I personally do not care, but you may think different. Perhaps you know a better way.

hustcc commented 6 years ago

For testcase of echarts-for-react, we should mock echarts which is a chart library based on canvas.

So do something by a jest mock plugin: jest-canvas-mock, and use it to test echarts, works fine.