vitaly-t
vitaly-t

Reputation: 25840

Testing a library that changes DOM directly

I wrote a tiny library that manipulates DOM directly, using window and document to search and change the DOM. And now I'm trying to figure out how to test it.

So far I have tried a combination of jest + jsdom:

const jsdom = require('jsdom');
const {JSDOM} = jsdom;

const dom = new JSDOM('<!DOCTYPE html><p>Hello world</p>');

// these two lines do not seem to do anything useful:
global.window = dom.window;
global.document = dom.window.document;

test('can globally access and search the DOM', () => {
    // this doesn't work
    expect(global.document.querySelectorAll('p').length).toBe(1);
});

Am I doing something wrong here, or am I entirely wrong with the testing approach? And if so, what would be the right way to approach such testing?

I feel a bit lost here, between what's called e2e testing and Unit testing, because in my case this seems to be something right in between.

Ideally, I would want to end up with this thing tested against multiple browsers, and to get some sort of test coverage. But that's more like the next step. Right now I cannot get it to work at all.

I know how fast things are moving in the Web world, so if I'm wrong completely, I appreciate if you can point me at what's the right way to do it today.


UPDATE

Here's my complete code, after a few suggestions from @duxfox, since with this approach my library is now missing event DOMContentLoaded which is supposed to trigger its DOM processing.

const jsdom = require('jsdom');
const {JSDOM} = jsdom;

const { document } = (new JSDOM('<!DOCTYPE html><p>Hello world</p>')).window;
global.document = document;

global.window = document.defaultView;
window.console = global.console;

Object.keys(document.defaultView).forEach((property) => {
    if (typeof global[property] === 'undefined') {
        global[property] = document.defaultView[property];
    }
});

global.navigator = {
    userAgent: 'node.js'
};

// Here I'm trying to follow a suggestion from Luis:
// https://stackoverflow.com/questions/36803733/jsdom-dispatchevent-addeventlistener-doesnt-seem-to-work
window.addEventListener('DOMContentLoaded', function (ev) {
    console.log('DOMContentLoaded called!'); // this is not called.
    /*
    console.log('window click', ev.target.constructor.name,
        ev.currentTarget.constructor.name);*/
});

// loading my library here:
// const root = require('../src');

test('something', () => {

    // This now works, but my library is missing
    // event DOMContentLoaded to start processing the DOM

    expect(document.querySelectorAll('p').length).toBe(1);
});

Upvotes: 2

Views: 1140

Answers (1)

Abdul Ahmad
Abdul Ahmad

Reputation: 10021

Below is a jsdom setup script that I use with mocha/chai/jsdom/enzyme

looks like the global.window should be set to

dom.window.document.defaultView

rather than

dom.window

Full script, which adds other global properties:

var jsdom = require('jsdom');
const { JSDOM } = jsdom;

const { document } = (new JSDOM('')).window;
global.document = document;

global.window = document.defaultView;
window.console = global.console;

Object.keys(document.defaultView).forEach((property) => {
  if (typeof global[property] === 'undefined') {
    global[property] = document.defaultView[property];
  }
});

global.navigator = {
  userAgent: 'node.js'
};

Upvotes: 3

Related Questions