Doesn't work with latest Create React App
ztolley opened this issue ยท 17 comments
Steps to reproduce:
-
npx create-react-app myapp --template typescript
-
cd myapp
-
npm install jest-canvas-mock
4: Edit App.test.tsx and add the following test
import "jest-canvas-mock";
test("Canvas support works with context", () => {
const canvas: HTMLCanvasElement = document.createElement("canvas");
const context = canvas.getContext("2d");
expect(context).not.toBeUndefined();
});
- Run
npm run test
The test should be able to create a canvas context but it comes back undefined
Same here. It worked with "react-scripts": "^3.4.4"
but not with "react-scripts": "4.0.0"
. Probably due to the different Jest version used by the CRA 4? In your tests, according to your package.json
, you use "jest": "^25.3.0"
. CRA 4 uses Jest 26. It would be very much appreciated if you could upgrade to Jest 26 / CRA 4. Thank you!
Update: As far as I can tell, Jest / jsdom can handle canvas
elements if the library node canvas is installed as a peer dependency.
https://github.com/jsdom/jsdom#canvas-support
https://www.npmjs.com/package/canvas
Thus, making this library obsolete, at least for testing purposes?
My restriction is I cannot do this, hence using this package
@albert-schilling Do you have any resource how to make it working?
What I did:
-
Added peerDependencies to my package.json
"peerDependencies": { "canvas": "^2.6.1" }
-
npm install
but no success...
Thanks!
@mregula
I think it suffices if you install canvas
as a regular package. npm i --save-dev canvas
At least, this is all we did.
I guess JSDOM tries to import canvas
from your node_modules folder and uses the library if it can import it.
Thanks @albert-schilling !
What I did: npm install --save-dev canvas
. If your component draws on canvas and uses img.onload to put the data into canvas, you need to mock also Image object in your jest test file:
import { Image } from "canvas";
beforeAll(() => {
(global as any).Image = Image;
});
I'm running in to the same problem here. I follow the steps outlines above by @ztolley and I also update the file setupTests.ts
as suggested here adding the line import 'jest-canvas-mock'
but the result doesn't change, the test still fails.
Afterwards I follow the suggestion of @albert-schilling installing node canvas and the test passed !, jsdom indeed was able to instantiate a canvas object.
However it is my understanding that node canvas is less portable then jest-canvas-mock
because it relies on Cairo v1.10.0 libraries, and so using jest-canvas-mock
would make the code more easily portable.
Hey! Following up on that, the node canvas
library indeed fixes the issue but I am not sure that would work when the test cannot control the creation of the canvas directly. For example if the canvas creation is happening inside a React component. How would you go about that?
For example:
test('test canvas', () => {
render(<canvas data-testid="canvas" />);
let canvas = screen.getByTestId("canvas")
expect(canvas.getContext("2d")).not.toBeUndefined()
});
Actually, the solution is to remove all dependencies to jest-canvas-dom
and install canvas
instead, that's it. Node takes care of the rest. The test above works just fine after that change.
Actually, the solution is to remove all dependencies to
jest-canvas-dom
and installcanvas
instead, that's it. Node takes care of the rest. The test above works just fine after that change.
The lib canvas
is quite limited, it doesn't support Video element as an argument and many other things. Any suggestion for use this in lasted CRA?
I'm having this same problem. While using canvas
works, it is not a good solution if you need to actually assert on your canvas, as you don't get any spy/mock functionality if you use the canvas
library.
jest-canvas-mock
does not work with CRA 4 (react-scripts@4.0.3
) - I tried this with freshly started project and it fails to work. On the other hand, with Parcel it works like a charm (tested with https://github.com/kolorobot/parcel-starters/tree/master/parcel-react-typescript-starter)
Using canvas
is not an option for me neither.
- Are there any plans to fix this?
- Is there any solution that could work with CRA?
Let me look into this, but I believe that this package doesn't work with the latest react-create-app is because of the fact that the jest configuration file has resetMocks
to true.
I was testing against a file that had
beforeEach(() => {
jest.resetAllMocks();
})
which is functionally equivalent to having resetMocks set to true. So try flipping the resetMocks
flag in the jest configuration file to false and the library should work.
- I created new CRA (5.0.0) app
- I added the following configuration to
package.json
:
"jest": {
"resetMocks": false
}
Now the tests do not fail anymore.
Source code of my dummy component:
// @ts-nocheck
import * as React from 'react';
import { createRef, Ref, useState } from 'react';
type CanvasProps = {
onUpdate?: (dataURL: string) => void,
}
const Canvas: React.FC<CanvasProps> = ({ onUpdate }) => {
const canvasRef: Ref<HTMLCanvasElement> = createRef();
const draw = () => {
// https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes
const canvas = canvasRef.current;
const ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.arc(75, 75, 50, 0, Math.PI * 2, true);
ctx.moveTo(110, 75);
ctx.arc(75, 75, 35, 0, Math.PI, false);
ctx.moveTo(65, 65);
ctx.arc(60, 65, 5, 0, Math.PI * 2, true);
ctx.moveTo(95, 65);
ctx.arc(90, 65, 5, 0, Math.PI * 2, true);
ctx.stroke();
onUpdate && onUpdate(canvas.toDataURL());
};
const clear = () => {
const canvas = canvasRef.current;
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
onUpdate && onUpdate(canvas.toDataURL());
};
return (
<>
<div>
<button onClick={draw}>Draw</button>
<button onClick={clear}>Clear</button>
</div>
<div>
<canvas data-testid='canvas' ref={canvasRef} width={150} height={150}
style={{ border: '1px solid #000' }} />
</div>
</>
);
};
export default Canvas;
And the test:
// @ts-nocheck
import * as React from 'react';
import 'jest-canvas-mock';
import Canvas from './Canvas';
import { getByTestId, getByText, render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
describe('Canvas', () => {
it('renders empty canvas', async () => {
const { container } = render(<Canvas />);
expect(container).toMatchSnapshot();
});
it('renders canvas element', async () => {
const { container } = render(<Canvas />);
userEvent.click(getByText(container, 'Draw'));
expect(container).toMatchSnapshot();
});
it('validates canvas state after draw was clicked', () => {
const { container } = render(<Canvas />);
userEvent.click(getByText(container, 'Draw'));
const canvas: HTMLCanvasElement = getByTestId(container, 'canvas');
const ctx = canvas.getContext('2d');
const events = ctx.__getEvents();
expect(events).toMatchSnapshot();
});
it('gets updated with `dataURL` on draw', () => {
const callback = jest.fn();
const { container } = render(<Canvas onUpdate={callback} />);
userEvent.click(getByText(container, 'Draw'));
expect(callback).toBeCalledWith('data:image/png;base64,00');
});
});
So, @ggayowsky you were right as it goes to the workaround.
Same here, but import "jest-canvas-mock";
and "resetMocks": false
solved my error. Thank you very much :)
would the maintainers accept a PR to expose mockWindow
https://github.com/hustcc/jest-canvas-mock/blob/master/src/index.js#L6, so that my test can setup the mock itself?
I'm working in a codebase where lots of existing tests use resetAllMocks
, which breaks this library.