Издевательский "документ" в шутку

Я пытаюсь написать тесты для моих проектов веб-компонентов в шутку. Я уже использую babel с предустановкой es2015. Перед загрузкой js файла я столкнулся с проблемой. Я выполнил фрагмент кода, в котором объект document имеет объект currentScript. Но в тестовом контексте оно равно null. Поэтому я думал о насмешливости. Но jest.fn() самом деле не помогает. Как я могу справиться с этой проблемой?

Кусок кода, в котором шутка не срабатывает.

var currentScriptElement = document._currentScript || document.currentScript;
var importDoc = currentScriptElement.ownerDocument;

Тестовый случай я написал. component.test.js

import * as Component from './sample-component.js';

describe('component test', function() {
  it('check instance', function() {
    console.log(Component);
    expect(Component).toBeDefined();
  });
});

Ниже приведена ошибка, вызванная шуткой

Test suite failed to run

    TypeError: Cannot read property 'ownerDocument' of null

      at src/components/sample-component/sample-component.js:4:39

Обновление: согласно предложению Андреаса Кёберле, я добавил несколько глобальных варсов и попытался насмехаться, как после

__DEV__.document.currentScript = document._currentScript = {
  ownerDocument: ''
};
__DEV__.window = {
  document: __DEV__.document
}
__DEV__.document.registerElement = jest.fn();

import * as Component from './arc-sample-component.js';

describe('component test', function() {
  it('check instance', function() {
    console.log(Component);
    expect(Component).toBeDefined();
  });
});

Но не повезло

Обновление: я пробовал код выше __dev__. Также, установив документ как глобальный.

Ответ 1

Я решил это использовать свойство setUpFiles в шутку. Это будет выполняться после jsdom и перед каждым тестом, который идеально подходит для меня.

Установите setupFiles в конфигурации Jest, например:

"setupFiles": ["<rootDir>/browserMock.js"]


// browserMock.js
Object.defineProperty(document, 'currentScript', {
  value: document.createElement('script'),
});

Идеальной ситуацией будет загрузка webcomponents.js для заполнения jsdom.

Ответ 2

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

__mocks __/client.js

import { JSDOM } from "jsdom"
const dom = new JSDOM()
global.document = dom.window.document
global.window = dom.window

Тогда в вашем jest config:

"setupFiles": [
  "./__mocks__/client.js"
],

Ответ 3

Я мог бы решить эту же проблему, используя global модуль области на nodejs, установив документ с макетом документа, в моем случае, getElementsByClassName:

// My simple mock file
export default {
    getElementsByClassName: () => {
        return [{
            className: 'welcome'
        }]
    }
};

// Your test file
import document from './name.component.mock.js';
global.document = {
    getElementsByClassName: document.getElementsByClassName
};

Ответ 4

Если вы, как я, вы хотите сделать фальшивый документ неопределенным (например, для тестов на стороне сервера/клиента), я смог использовать object.defineProperty внутри своих тестовых наборов без необходимости использовать setupFiles

Пример:

beforeAll(() => {
  Object.defineProperty(global, 'document', {});
})

Ответ 5

Если вам нужно определить тестовые значения для свойств, есть несколько более гранулированный подход. Каждое свойство необходимо определить индивидуально, и также необходимо сделать свойства writeable:

Object.defineProperty(window.document, 'URL', {
  writable: true,
  value: 'someurl'
});

См.: https://github.com/facebook/jest/issues/890

Это работало для меня, используя Jest 21.2.1 и Node v8.11.1

Ответ 6

Я боролся с насмешливым документом для проекта, в котором я участвую. Я вызываю document.querySelector() внутри компонента React и должен убедиться, что он работает правильно. В конечном счете это то, что сработало для меня:

it('should test something', () => {
    const spyFunc = jest.fn();
    Object.defineProperty(global.document, 'querySelector', { value: spyFunc });
    <run some test>
    expect(spyFunc).toHaveBeenCalled()
});