I am trying to test my React (for the browser) code in what I think is an entirely conventional way: Mocha as the test-runner, JSDom to simulate the browser, Enzyme to check the results.
My problem is this: whenever I manually set the focus on a component, React throws an exception. The problem is deep inside React:
activeElement.attachEvent('onpropertychange', handlePropertyChange);
The active element is set, but as a JSDom HTMLInputElement
, it does not have an attachEvent
. I have found if I hacked the file node_modules/jsdom/lib/jsdom/living/generated/HTMLInputElement.js
to give that class empty methods named attachEvent
and detachEvent
, the exception goes away.
But clearly, that is not the right solution.
The comments on the function, and some fragmentary information I have found elsewhere, suggests this is a shim meant for antique versions of IE, not JSDom at all. The function involved, startWatchingForValueChange
is only invoked if the flag isInputEventSupported
is not set, and setting it requires another canUseDOM
to be set. Forcibly setting either of those flags causes other problems.
Typing up this question, I figured it out.
As the testing system was set up, when I initialized JSDom, I also set a global afterEach()
to reset the contents of the DOM after each test. This did not directly create a problem, but it did mean the order of initialization was necessarily like this:
So when React was initialized, it looked around and saw no working DOM, and figured, "Damn, I must be running on IE or something." After that, it was all down-hill.
So I confidently restructured it like this:
And that... did nothing.
Then I realized the problem was, I was doing this:
import { JSDOM } from "jsdom";
import Enzyme from "enzyme";
import Adapter from "enzyme-adapter-react-16";
global.jsdom = initDom();
...
React was actually initializing itself when it was imported — by the import of Enzyme!
So I confidently rewrote it like this:
import { JSDOM } from "jsdom";
global.jsdom = initDom();
import Enzyme from "enzyme";
import Adapter from "enzyme-adapter-react-16";
And that... did nothing.
Because the import
statement effectively gets hoisted to the top of the file.
So I confidently rewrote it like this:
import { JSDOM } from "jsdom";
let jsdom = new JSDOM("");
...
const Enzyme = require("enzyme");
const Adapter = require("enzyme-adapter-react-16");
And that... fixed it.