Reputation: 2337
Can anyone help me with testing of following function
function onload(cb){
const image = 'http://placehold.it/350x150'
const img = new Image()
img.src = image
img.onload = () => {
cb()
}
}
In my test file Image
is available via jsdom. I want that I can test that cb
is calledOnce
Upvotes: 9
Views: 5375
Reputation: 1276
onload.ts:
export function onload(cb: CallableFunction) {
const image = 'http://placehold.it/350x150';
const img = new Image();
img.src = image;
img.onload = () => {
cb();
};
}
onload.test.ts:
// @vitest-environment jsdom
import { describe, expect, it, vi, afterEach, beforeEach } from 'vitest';
import { onload } from './onload.ts';
describe(`fn onload`, () => {
const event = {} as Event;
let img: HTMLImageElement;
beforeEach(() => {
vi.spyOn(window, 'Image').mockImplementation(() => {
img = {} as HTMLImageElement;
return img;
});
});
afterEach(() => {
vi.restoreAllMocks();
});
it('should call the callback function once on image load', () => {
const cb = vi.fn();
onload(cb);
img.onload?.(event);
expect(cb).toHaveBeenCalledOnce();
});
});
Upvotes: 0
Reputation: 713
Initializing JSDOM with resources: 'usable'
should normally be enough with the latest API (v10+
):
const jsdom = new JSDOM('<!doctype html><html><body></body></html>',{
resources: 'usable'
});
const { window } = jsdom;
It requires the canvas
(canvas or canvas-prebuilt) module as stated in JSDOM doc.
Using the latest canvas
module, you can mock the window.Image
to trigger load
events.
So first create a mock for the window.Image
class:
import CanvasImage from 'canvas/lib/image';
import fs from 'fs';
import path from 'path';
import { URL } from 'url';
import request from 'request';
// Where to fetch assets on the file system when path are provided.
const ASSETS_DIRECTORY = path.join(__dirname,'.');
const WindowImage = function() {
// Reimplemented the following class:
// https://github.com/tmpvar/jsdom/blob/master/lib/jsdom/living/nodes/HTMLImageElement-impl.js
// https://github.com/Automattic/node-canvas#imagesrcbuffer
let source;
let image;
let onload;
let onerror;
return {
set src (value) {
// TODO Throw errors to the Image.onerror handler.
const onDataLoaded = function (data) {
image = new CanvasImage();
image.onload = () => {
if (onload) {
onload(image);
}
};
image.onerror = err => {
if (onerror) {
onerror(err);
}
};
image.src = data;
source = value;
};
// URL or path?
let url;
try {
url = new URL(value);
} catch (e) {}
// Fetch the data.
if (url) {
request(url.href, (error, response, body) => {
if (response && response.statusCode !== undefined && response.statusCode !== 200) {
throw new Error("Status code: " + response.statusCode);
}
onDataLoaded(body);
});
} else {
// Assume it is a file path: try a local file read.
fs.readFile(path.join(ASSETS_DIRECTORY,value),function (err,data) {
if (err) {
throw err;
}
onDataLoaded(data);
});
}
},
set onload (handler) {
onload = handler;
},
set onerror (handler) {
onerror = handler;
},
get src () {
return source;
},
// TODO Should allows to modify height and width
// + add natural height and width
// Cf. https://github.com/tmpvar/jsdom/blob/master/lib/jsdom/living/nodes/HTMLImageElement-impl.js#L51
get width () {
return image && image.width;
},
get height () {
return image && image.height;
}
};
};
Then you can mock window.Image
in your tests, by redefining the one used by JSDOM:
window.Image = WindowImage;
This is not really elegant, but it gives you control over how image data is loaded, as canvas.Image.src
can normally take any Buffer.
Upvotes: 7