Testing MutationObserver with Jest

2020-02-10 15:09发布

问题:

I wrote a script with the main purpose of adding new elements to some table's cells.

The test is done with something like that:

document.body.innerHTML = `
<body>
    <div id="${containerID}">
        <table>
            <tr id="meta-1"><td> </td></tr>
            <tr id="meta-2"><td> </td></tr>
            <tr id="meta-3"><td> </td></tr>
            <tr id="no-meta-1"><td> </td></tr>
        </table>
    </div>
</body>
`;

    const element = document.querySelector(`#${containerID}`);

    const subject = new WPMLCFInfoHelper(containerID);
    subject.addInfo();

    expect(mockWPMLCFInfoInit).toHaveBeenCalledTimes(3);

mockWPMLCFInfoInit, when called, is what tells me that the element has been added to the cell.

Part of the code is using MutationObserver to call again mockWPMLCFInfoInit when a new row is added to a table:

new MutationObserver((mutations) => {
    mutations.map((mutation) => {
        mutation.addedNodes && Array.from(mutation.addedNodes).filter((node) => {
            console.log('New row added');
            return node.tagName.toLowerCase() === 'tr';
        }).map((element) => WPMLCFInfoHelper.addInfo(element))
    });
}).observe(metasTable, {
    subtree:   true,
    childList: true
});

WPMLCFInfoHelper.addInfo is the real version of mockWPMLCFInfoInit (which is a mocked method, of course).

From the above test, if add something like that...

const table = element.querySelector(`table`);
var row = table.insertRow(0);

console.log('New row added'); never gets called. To be sure, I've also tried adding the required cells in the new row.

Of course, a manual test is telling me that the code works.

Searching around, my understanding is that MutationObserver is not supported and there is no plan to support it.

Fair enough, but in this case, how can I test this part of my code? Except manually, that is :)

回答1:

I know I'm late to the party here, but in my jest setup file, I simply added the following mock MutationObserver class.

global.MutationObserver = class {
    constructor(callback) {}
    disconnect() {}
    observe(element, initObject) {}
};

This obviously won't allow you to test that the observer does what you want, but will allow the rest of your code's tests to run which is the path to a working solution.



回答2:

The problem is actually appears because of JSDom doesn't support MutationObserver, so you have to provide an appropriate polyfill.

Little tricky thought may not the best solution (let's use library intend for compatibility with IE9-10).

  • you can take opensource project like this one https://github.com/webmodules/mutation-observer which represents similar logic
  • import to your test file and make global

Step 1 (install this library to devDependencies)

npm install --save-dev mutation-observer

Step 2 (Import and make global)

import MutationObserver from 'mutation-observer'
global.MutationObserver = MutationObserver 

test('your test case', () => { 
   ...
})


回答3:

You can use mutationobserver-shim.

Add this in setup.js

import "mutationobserver-shim"

and install

npm i -D mutationobserver-shim


回答4:

Addition for TypeScript users:

declare the module with adding a file called: mutation-observer.d.ts

/// <reference path="../../node_modules/mutation-observer" />
declare module "mutation-observer";

Then in your jest file.

import MutationObserver from 'mutation-observer'
(global as any).MutationObserver = MutationObserver


标签: jestjs