Mock img Element in Jest

Ramin Mousavi
2 min readNov 21, 2020

--

I recently started implementing some tests for one of my open source libraries. the library is providing some high level APIs to work with HTML Canvas. inserting an image into the canvas is one of the features. but here let’s simplify the test by removing canvas parts. so this is the unit test:

test('it should load the image', () => {
const img = document.createElement('img')
img.src = '<img url>'
waitForImage(img).then(loadedImage => {
except(loadedImage.naturalWidth).toBe(1200)
})
})
function waitForImage(img) {
return new Promise(resolve => {
img.onload = () => resolve(img)
})
}

but the test will fail because of timeout issue when you run the test suite

● Rotation › it should load the image: Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Error:

From the error’s description, it seems Jest is trying to load the image but a timeout is happening.

in order to fix the issue we need to mock the document.createElement function to bypass the timeout

first add the setupFile.js or setupFile.ts into your jest configuration

{
setupFiles: ['/path/to/jest/config/setupFile.js']
}

and then let’s override the createElement function

global.document.createElement = (function (create) {  return function () {
const element: HTMLElement = create.apply(this, arguments)

if (element.tagName === 'IMG') {
setTimeout(() => {
element.onload(new Event('load'))
}, 100)
}
return element
}
})(document.createElement)

the above snippet mocking img element and calls onload function with a 100ms delay

we can even make it better. let’s say we need to test different size of images. so I change my test suite to:

test('it should load the image', () => {const img = document.createElement('img')
img.src = '1200x800'
waitForImage(img).then(loadedImage => {
except(loadedImage.naturalWidth).toBe(1200)
})
})
function waitForImage(img) {
return new Promise(resolve => {
img.onload = () => resolve(img)
})
}

and change setupFile

global.document.createElement = (function (create) {return function () {
const element: HTMLElement = create.apply(this, arguments)

if (element.tagName === 'IMG') {
setTimeout(() => {
const [w, h] = element.getAttribute('src').split('x')
element.setAttribute('width', w)
element.setAttribute('naturalWidth', w)
element.setAttribute('height', h)
element.setAttribute('naturalHeight', h)
element.onload(new Event('load'))
}, 100)
}
return element
}
})(document.createElement)

the change fakes the image’s width and height based on the given src in test suite.

in the same way you can add more logics to override function based on your needs

enjoy testing!

--

--

Ramin Mousavi
Ramin Mousavi

Written by Ramin Mousavi

0 Followers

Creator of https://github.com/pikasojs/pikaso, Senior Full Stack Developer at rechat.com

No responses yet