rewire icon indicating copy to clipboard operation
rewire copied to clipboard

Rewiring a module containing DOM manipulation

Open lkaratun opened this issue 7 years ago • 3 comments

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!

lkaratun avatar Nov 10 '18 16:11 lkaratun

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

akuroda avatar Feb 09 '19 15:02 akuroda

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".

atsepkov avatar Apr 27 '19 06:04 atsepkov

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')
})

garrettwgg avatar Jun 12 '20 18:06 garrettwgg