I'm working on a redux project where we're using paper.js for some drawing features. We're testing our code in docker containers with mocha (unit tests) and nightwatch (integration tests, incl. selenium server). For installing dependencies we're using npm (npm install paper).
All tests are working on my machine. If I'm pushing the code to my repository (BitBucket) the Continuous Integration tool (Bamboo) starts building the docker containers and runs the tests. Then i'll get the following error:
Console output (Bamboo log):
Error: Canvas [object HTMLCanvasElement] is unable toprovide a 2D context.
at Object.CanvasProvider.getCanvas (/home/nexboard/Nexboard/node_modules/paper/dist/paper-full.js:12919:10)
at Object.CanvasProvider.getContext (/home/nexboard/Nexboard/node_modules/paper/dist/paper-full.js:12934:21)
at new <anonymous> (/home/nexboard/Nexboard/node_modules/paper/dist/paper-full.js:13133:27)
at Object.<anonymous> (/home/nexboard/Nexboard/node_modules/paper/dist/paper-full.js:12947:17)
at Object.<anonymous> (/home/nexboard/Nexboard/node_modules/paper/dist/paper-full.js:14623:3)
at Module._compile (module.js:541:32)
at Module.replacementCompile (/home/nexboard/node_modules/nyc/node_modules/append-transform/index.js:63:13)
at module.exports (/home/nexboard/node_modules/nyc/node_modules/default-require-extensions/js.js:8:9)
at /home/nexboard/node_modules/nyc/node_modules/append-transform/index.js:67:4
at require.extensions.(anonymous function) (/home/nexboard/node_modules/babel-register/lib/node.js:154:7)
at /home/nexboard/node_modules/nyc/node_modules/append-transform/index.js:67:4
at require.extensions.(anonymous function) (/home/nexboard/Nexboard/node_modules/babel-register/lib/node.js:154:7)
at Object.<anonymous> (/home/nexboard/node_modules/nyc/node_modules/append-transform/index.js:67:4)
at Module.load (module.js:458:32)
at tryModuleLoad (module.js:417:12)
at Function.Module._load (module.js:409:3)
at Module.require (module.js:468:17)
at require (internal/module.js:20:19)
at Object.<anonymous> (/home/nexboard/Nexboard/src/js/whiteboard/canvas.jsx:1:13)
at ...
When mocha runs my tests and creates a main object, it fails requiring paper.js. I don't think it's because of my docker compose configuration so I'm leaving that one out. The code which is failing in paper.js are the following lines:
if (this.canvases.length) {
canvas = this.canvases.pop();
} else {
// This get's called (at the first time)
canvas = document.createElement('canvas');
clear = false;
}
var ctx = canvas.getContext('2d');
if (!ctx) {
throw new Error('Canvas ' + canvas +
' is unable toprovide a 2D context.');
}
Because the integration tests are working I have the following thesis:
The problem could be that you need a DOM to create a canvas context but I'm not sure how this stuff is working - especially in a testing environment. What did you do to make your unit tests be successfully?
The regarding code if you like to keep a look:
Main.jsx:
import React from 'react';
import Toolbar from '../userinterface/Toolbar/Toolbar.jsx';
import Canvas from '../whiteboard/canvas.jsx';
export function Main() {
return (
<main className="main">
<div id="content">
<Toolbar />
</div>
<div id="background"></div>
<Canvas />
</main>
);
}
export default Main;
canvas.jsx
import React from 'react';
import paper from 'paper';
var Canvas = React.createClass({
componentDidMount: () => {
console.log("Canvas did mount")
console.log(paper)
paper.setup('whiteboard');
var path = new paper.Path();
path.strokeColor = 'black';
var start = new paper.Point(100, 100);
path.moveTo(start);
path.lineTo(start.add([ 200, -50 ]));
paper.view.draw();
},
render: () => {
return (
<canvas id="whiteboard">
</canvas>
);
}
})
export default Canvas
Thanks for having a look at my problem.
Kind regards,
Thomas
What container are these tests run in? PhantomJS? Node? An actual browser?
I just figured out that I'm also having this problem on my machine. So it's possible that this is an error produced by using a docker container.
@lehni: I started my project with a redux template from a friend of mine. So it might be that i'm just doing some thing wrong :)
It's seems that I'm using jsdom for mocha. Before executing tests the following lines are executed:
require('babel-register')();
var jsdom = require('jsdom').jsdom;
var exposedProperties = ['window', 'navigator', 'document'];
global.document = jsdom('');
global.window = document.defaultView;
Object.keys(document.defaultView).forEach((property) => {
if (typeof global[property] === 'undefined') {
exposedProperties.push(property);
global[property] = document.defaultView[property];
}
});
global.navigator = {
userAgent: 'node.js'
};
Maybe this is jsdom bug. I'll have a look into this tomorrow.
It's crucial to know what container these tests are running in, so I'm asking again: Is it Node.js or a browser? Something in between?
If it does run in Node.js, then the exports into global that you quote above will probably make Paper.js falsely think that it's running in a browser environment. Tinkering with globals is bad practice, and JSDOM isn't a full browser replacement, so the mistake here I'd say is on the side of whatever package provides this code.
But it may also be that JSDOM is not able to create a canvas element because you don't have node-canvas installed (defined in optionalDependencies). Try that.
I'm using Node.js. In a node console on my mac machine I can run the following code require('jsdom').jsdom('').createElement('canvas').getContext('2d')
and it will return a canvas context.
When i connect to my docker container and run the same command in the console it returns null.
My docker container is based on alpine-node (a minimal linux distribution).
Can you inspect the log file of the process of installing the dependencies on docker? It probably fails to compile node-canvas because of unmet dependencies. Here what I tell travis CI to install:
- libcairo2-dev
- libpango1.0-dev
- libssl-dev
- libjpeg62-dev
- libgif-dev
In my logs I found the following error installing node-canvas:
> [email protected] install /home/nexboard/node_modules/canvas
> node-gyp rebuild
[91m/bin/sh: ./util/has_lib.sh: not found
[0m[91mgyp: Call to './util/has_lib.sh freetype' returned exit status 127 while in binding.gyp. while trying to load binding.gyp
[0m[91mgyp[0m[91m ERR! configure error
[0m[91mgyp[0m[91m ERR! stack Error: `gyp` failed with exit code: 1
[0m[91mgyp ERR! stack at ChildProcess.onCpExit (/usr/lib/node_modules/npm/node_modules/node-gyp/lib/configure.js:305:16)
[0m[91mgyp [0m[91mERR![0m[91m [0m[91mstack at emitTwo (events.js:106:13)
gyp ERR! stack at ChildProcess.emit (events.js:191:7)
gyp ERR! stack at Process.ChildProcess._handle.onexit (internal/child_process.js:204:12)
[0m[91mgyp[0m[91m [0m[91mERR! System Linux 4.4.0-28-generic
[0m[91mgyp ERR! command "/usr/bin/node" "/usr/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
gyp ERR! cwd /home/nexboard/node_modules/canvas
[0m[91mgyp ERR! node -v v6.3.0
gyp ERR! node-gyp -v v3.3.1
gyp [0m[91mERR![0m[91m not ok[0m[91m
[0m[91mnpm [0m[91mWARN [0m[91minstall:[email protected][0m[91m [email protected] install: `node-gyp rebuild`
npm WARN install:[email protected] Exit status 1
This is a typical problem for using minimal linux distribution. That's why I switched to an trusty ubuntu image. Then I installed the libraries you suggested, but I still receive the same emtpy canvas context as before.
apt-get install -y libcairo2-dev libpango1.0-dev libssl-dev libjpeg62-dev libgif-dev
node
> require('jsdom').jsdom('').createElement('canvas').getContext('2d')
null
What did you mean with installing canvas? Do i have to connect jsdom and node-canvas manually?
EDIT:
After installing (only) node-canvas the following code snippet returns a context:
new (require('./node_modules/canvas/index.js'))(200, 200).getContext('2d')
But paper.js already installs jsdom and canvas on its own, am I right?
Okay I think I fixed this bug. I tried to install node-canvas on my docker machine but I ran into several problems with node-gyp. I fixed this after looking at hundreds of different github issues for node-canvas. Someone suggested to look at the node-canvas installation instructions :facepalm: . In the end I was missing "pkg-config". Here my installation command for node-canvas:
apt-get update && apt-get install -y libcairo2-dev libpango1.0-dev libssl-dev libjpeg62-dev libgif-dev pkg-config
I'll run the final tests over night and write back on monday if everything works fine.
PS: Do I have to install jsdom and canvas manually or can I just run "npm install paper --optional" or something like that?
Great! Glad to hear you found the culprit. I shall add pkg-config to the README also. I think optional dependencies are installed by default, but don't cause an error if they fail. And if you don't want them, you choose --no-optional.
Most helpful comment
Great! Glad to hear you found the culprit. I shall add
pkg-configto the README also. I think optional dependencies are installed by default, but don't cause an error if they fail. And if you don't want them, you choose--no-optional.