javascriptjquerywebpackvitestwebpack-encore

How to set global window for jQuery in a vitest test (jQuery requires a window with a document)?


Probably less important: Setup is done with webpack Encore which generates a webpack config file, we use yarn as a package manager, so vitest is installed with yarn.

Here is a simplified problem:

// vitest.config.js
import path from 'path';

export default {
    resolve: {
        alias: {
            Common: path.resolve(__dirname, 'assets/packages')
        }
    }
};
// MyFooUsejq.js
import $ from 'jquery';

export default class MyFooUsejq {
    #$myDiv;
    constructor(selector) {
        this.#$myDiv = $(selector);
    }

    get innerHtml() {
        return this.#$myDiv.html();
    }
}
// MyFooUsejq.test.js
import { expect, test } from 'vitest';
import MyFooUsejq from 'Common/MyFooUsejq'; // Common is an alias configured with webpack.
import { JSDOM } from 'jsdom';

test('use jquery', () => {
    const jsdom = new JSDOM(`<!DOCTYPE html><html lang="en"><body><div id="foo">hello</div></body></html>`);
    global.window = jsdom.window;
    global.document = jsdom.window.document;
    const foo = new MyFooUsejq('#foo');
    expect(foo.innerHtml).toBe('hello');
});

Running yarn vitest --config=vitest.config.js MyFooUsejq.test.js gives error:

Error: jQuery requires a window with a document

According to https://stackoverflow.com/a/52589859/5233188 setting a global window object should work for jQuery to detect it.

How can I keep import $ from 'jquery'; and set a window for jQuery in MyFooUsejq?


Solution

  • As Heretic Monkey advised, jQuery needs to be loaded AFTER setting the global window, but instead of using dynamic imports in all modules that import jQuery, I added a dynamic import inside the test for Common/MyFooUsejq. But I also think I set global.window and global.document wrong.

    Working test:

    // MyFooUsejq.test.js
    import { expect, test } from 'vitest';
    import { JSDOM } from 'jsdom';
    
    test('use jquery', async () => {
        const jsdom = new JSDOM(`<!DOCTYPE html><html lang="en"><body><div id="foo">hello</div></body></html>`);
        global.window = jsdom.window;
    
        const { default: MyFooUsejq } = await import('Pupil/Common/MyFooUsejq');
        const foo = new MyFooUsejq('#foo');
        expect(foo.innerHtml).toBe('hello');
    });