Как написать тесты с использованием jQuery в ts-jest при использовании импорта ES?

Я пытаюсь написать модульные тесты, используя TypeScript, Jest и jQuery. Я продвинулся довольно далеко, но когда я запускаю тесты, я получаю «jQuery требует окна с документом».

Судя по всему, проблема в том, что import * as $ from "jquery" (см. этот вопрос) также инициализирует jQuery, но в то время окна нет. Что мне нужно, так это «ленивая» версия модуля jQuery, вроде того, что обычно делает $(document).ready(...), но для самого символа $.

Предыстория: у меня установлен jQuery как npm install @types/jquery. Я использую jsdom для создания поддельного окна. Я использую ts-jest для переноса файлов TS в JavaScript для jest. Тесты запускаются с npx jest --watch или npm t.

Я бы не хотел использовать global.$ в файле настройки Jest, потому что многие тесты не не нужны накладные расходы.


person Aaron Digulla    schedule 12.07.2019    source источник


Ответы (1)


У меня есть решение, но вы не можете использовать $ в тестируемом коде, поэтому оно работает только для кода, который вы написали/можете изменить.

Вместо того, чтобы использовать $ напрямую, оберните его в провайдере:

export class JQueryProvider {
    constructor(public jq: any) {
        // empty
    }

    get(selector: string): JQuery {
        return this.jq(selector)
    }
}

Таким образом, мы можем скрыть детали происхождения $. Также метод get() приведет результат к правильному типу, даже если ввод jq имеет тип any.

Этот код должен войти в общий служебный модуль.

Тесты должны включать модуль с этим кодом установки:

import { JQueryProvider } from "./utils"
import { JSDOM } from "jsdom";

export type WithJQueryFunction = (jq: JQueryProvider) => void;

export function withJQuery(fn: WithJQueryFunction): void {
    // Create window before loading jquery
    const window = new JSDOM().window 

    // Create jQuery instance
    const $ = require('jquery')(window);

    // Create provider to avoid the global variable $
    const jq = new JQueryProvider($)

    fn(jq)
}

Обратите внимание, что я использую require() вместо import, так как не знаю, как добиться такого же эффекта.

Теперь тесты будут выглядеть так:

withJQuery((jq) => {
    describe('jQuery tests', () => {
        it('empty div', () => {
            const parent = jq.get('<div>')
            expect(parent[0]).toMatchSnapshot()
        })      

        it('empty div with class', () => {
            const parent = jq.get('<div>').addClass('foo')
            expect(parent[0]).toMatchSnapshot()
        })      
    })
})

Пример производственного кода:

export class DivAppender implements Appender {
    constructor(public jq: JQueryProvider, public parent: JQuery, public dateFormat = TIME_FORMAT) {
        // empty
    }

    append(event: LogEvent): void {
        const element = this.jq.get('<div>').addClass('event')
        ...

const appender = new DivAppender(new JQueryProvider($), $('body'))
person Aaron Digulla    schedule 12.07.2019