Rewiring a module containing DOM manipulation
Hi,
I'm trying to use rewire from a Jest test. Jest comes with jsdom and document object is defined inside the test. However, when I try to run
const app = rewire("../app.js")
it fails with the error:
TypeError: Cannot read property 'querySelector' of undefined
when it tries to run the line
const myForm = document.querySelector("#myForm");
When I replace the rewire call with require, the file get imported successfully.
Is there a way to solve this? Am I doing something wrong? Any help will be appreciated!
Hi, It seems methods of document object are lost after rewire. My workaround for similar problem is:
const f = document.getElementById.bind(document);
const mymodule = rewire('...');
options.__set__("document", {
getElementById: f
});
// access document object inside mymodule
cf: https://stackoverflow.com/questions/10743596/why-are-certain-function-calls-termed-illegal-invocations-in-javascript
The workaround will not work if the imported file performs DOM manipulation at the time it's being loaded rather than inside a function call to be executed later.
Granted, by default Jest's native jsdom fails in that case too, but explicitly loading jsdom via Jest's setupFiles flag easily fixes the issue in case of regular "require" but remains broken when using "rewire".
It's not foolproof, but I came up with a different way to get around this. Basically I duplicated the file I wanted to test, injecting the jsdom directly into the file. I also used my actual HTML in the jsdom. I make no claims of it being a perfect (or perfectly written) solution, but it seems to work.
const fs = require('fs')
const rewire = require('rewire')
const pug = require('pug')
const rawIndex = fs.readFileSync('./public/javascripts/index.js', 'utf-8')
// this would a a readFileSync if you had a plain HTML file
const indexHTML = pug.compileFile('./views/index.pug')()
const mockDOM = `
'use strict';
const { JSDOM } = require('jsdom')
const dom = new JSDOM(\`${indexHTML}\`)
document = dom.window.document
window = dom.window
`
fs.writeFileSync('./__mocks__/index.jsdom.js', mockDOM + rawIndex, 'utf-8')
const index = rewire('../__mocks__/index.jsdom.js')
afterAll(() => {
fs.unlinkSync('./__mocks__/index.jsdom.js')
})