玩笑测试期间来自screenfull库的TypeError

时间:2018-06-28 17:50:37

标签: reactjs typescript redux jestjs enzyme

我已经将全屏API(https://github.com/sindresorhus/screenfull.js)的包装器screenfull添加到了Redux连接的React组件中。该应用程序在其他地方(而不是在组件中)使用Typescript。 Screenfull在组件中的用法如下:

componentDidUnmount() {
  screenfull.on('change', () => this.props.toggleFullscreen());
}

componentWillUnmount() {
 screenfull.off('change', () => this.props.toggleFullscreen());
}

并且我进行了如下测试(组件测试文件中还有更多的失败,但我想我会分享第一个)

  it('mounts with initial state', () => {
    const store = configureStore(reducer);
    const container = mount(
      <Provider store={store}>
        <VideoBoundingBoxApp inIframe={false} />
      </Provider>);
    expect(container.find('VideoBoundingBoxApp')).toExist();
  });

该应用程序可以正常运行并运行,但是在运行yarn test时,出现以下错误:

console.error node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/virtual-console.js:29
      Error: Uncaught [TypeError: _screenfull2.default.on is not a function]
          at reportException (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:66:24)
          at invokeEventListeners (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:209:9)
          at HTMLUnknownElementImpl._dispatch (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:119:9)
          at HTMLUnknownElementImpl.dispatchEvent (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:82:17)
          at HTMLUnknownElementImpl.dispatchEvent (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/nodes/HTMLElement-impl.js:30:27)
          at HTMLUnknownElement.dispatchEvent (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:157:21)
          at Object.invokeGuardedCallbackDev (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:944:16)
          at invokeGuardedCallback (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:993:29)
          at commitRoot (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7157:9)
          at completeRoot (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8065:36) TypeError: _screenfull2.default.on is not a function
          at VideoBoundingBoxApp.componentDidMount (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/src/components/VideoBoundingBox/VideoBoundingBoxApp.jsx:122:28)
          at commitLifeCycles (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:5347:26)
          at commitAllLifeCycles (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7018:9)
          at HTMLUnknownElement.callCallback (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:906:14)
          at invokeEventListeners (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:193:27)
          at HTMLUnknownElementImpl._dispatch (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:119:9)
          at HTMLUnknownElementImpl.dispatchEvent (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:82:17)
          at HTMLUnknownElementImpl.dispatchEvent (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/nodes/HTMLElement-impl.js:30:27)
          at HTMLUnknownElement.dispatchEvent (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:157:21)
          at Object.invokeGuardedCallbackDev (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:944:16)
          at invokeGuardedCallback (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:993:29)
          at commitRoot (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7157:9)
          at completeRoot (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8065:36)
          at performWorkOnRoot (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8015:11)
          at performWork (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7933:9)
          at performSyncWork (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7910:5)
          at requestWork (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7810:7)
          at scheduleWorkImpl (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7685:13)
          at scheduleWork (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7645:12)
          at scheduleRootUpdate (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8273:5)
          at updateContainerAtExpirationTime (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8301:12)
          at Object.updateContainer (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8328:14)
          at Object.create (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:9009:18)
          at Object.<anonymous> (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/src/test/components/VideoBoundingBox/VideoBoundingBoxApp.test.jsx:42:49)
          at Object.asyncFn (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/jest-jasmine2/build/jasmine_async.js:82:37)
          at resolve (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/jest-jasmine2/build/queue_runner.js:52:12)
          at new Promise (<anonymous>)
          at mapper (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/jest-jasmine2/build/queue_runner.js:39:19)
          at promise.then (/Users/mschoemaker/Desktop/CrowdFlower/projects/image_annotation/node_modules/jest-jasmine2/build/queue_runner.js:73:82)
          at <anonymous>
          at process._tickCallback (internal/process/next_tick.js:188:7)

(类似于screenfull.off ...)我不确定如何解析它。我不认为这与Typescript有关。有人知道吗?

3 个答案:

答案 0 :(得分:2)

我(很好,主要是我的老板)知道了。事实证明,全屏代码可以检查document是否包含诸如requestFullscreen之类的方法,否则返回false。我通过添加

来解决此问题
// fullscreen library will cause an error in jest tests
// if it  can't find fullscreen methods on document
[
  'requestFullscreen',
  'exitFullscreen',
].forEach(each => (document[each] = () => {})); // eslint-disable-line

到我的setupTests.js

答案 1 :(得分:0)

对于单元测试中的一件事,您可能想jest.mock依赖。 _screenfull2.default.on is not a function错误可能是由于commonjs模块和es6模块之间的不匹配引起的。您如何导入全屏显示?

答案 2 :(得分:0)

@Michel Schoemaker的回答是正确的,但我想为像我这样的好奇者提供更多背景信息。幸好,screenfull的源代码是super short and easy to reason about.

查看源代码,您将在顶部看到一个自调用函数,该函数将其返回值存储在名为fn的变量中。

    // This is what I'm talking about right here. See the end of this function
    var fn = (function () {
        var val;
        var fnMap = [
            [
                'requestFullscreen',
                'exitFullscreen',
                'fullscreenElement',
                'fullscreenEnabled',
                'fullscreenchange',
                'fullscreenerror'
            ],
            // New WebKit
            [
                'webkitRequestFullscreen',
                'webkitExitFullscreen',
                'webkitFullscreenElement',
                'webkitFullscreenEnabled',
                'webkitfullscreenchange',
                'webkitfullscreenerror'

            ],
            // Old WebKit
            [
                'webkitRequestFullScreen',
                'webkitCancelFullScreen',
                'webkitCurrentFullScreenElement',
                'webkitCancelFullScreen',
                'webkitfullscreenchange',
                'webkitfullscreenerror'

            ],
            [
                'mozRequestFullScreen',
                'mozCancelFullScreen',
                'mozFullScreenElement',
                'mozFullScreenEnabled',
                'mozfullscreenchange',
                'mozfullscreenerror'
            ],
            [
                'msRequestFullscreen',
                'msExitFullscreen',
                'msFullscreenElement',
                'msFullscreenEnabled',
                'MSFullscreenChange',
                'MSFullscreenError'
            ]
        ];

        var i = 0;
        var l = fnMap.length;
        var ret = {};
        // This for loop essentially checks the current document object for the property/methods above. 
        for (; i < l; i++) {
            val = fnMap[i];
            if (val && val[1] in document) {
                for (i = 0; i < val.length; i++) {
                    ret[fnMap[0][i]] = val[i];
                }
                return ret;
            }
        }
        // If it doesn't find any of them, this whole function returns false
        // and the fn variable is set to this returned value. 
        return false;
    })();

接下来,您将在其中进一步看到对此fn变量的简单条件检查。

    // Sidenote, if you just remove the bang from the conditional, this package
    // works in testing. But obviously we don't want to do that. 
    if (!fn) {
        if (isCommonjs) {
            module.exports = {isEnabled: false};
            // If log out the value of screenfull after importing it 
            // in your test, this is what you'll see -> {isEnabled: false}
            // Thus screenfull sets this object as the exported module and returns early. 
        } else {
            window.screenfull = {isEnabled: false};
        }

        return;
    }

回到@Michel Schoemaker的答案;起作用的原因是因为我们基本上是在猴子在文档对象中修补这些属性/方法,因此Screenfull认为我们拥有兼容的环境。一旦认为我们拥有一个兼容的环境,Jest就可以照常模拟该库。

我做的事情与@Michel Schoemaker有点不同,因为我只在我的应用程序的两种方法中使用全屏显示,所以我不需要为整个测试环境进行设置。这是我的方法:

thing.spec.js

import screenfull from 'screenfull';

jest.mock('screenfull');

    //********* Fullscreen **********//
    describe('Fullscreen', () => {

        beforeEach(() => {
            document.requestFullscreen = jest.fn();
            document.exitFullscreen = jest.fn();
            document.fullscreenElement = jest.fn();
            document.fullscreenEnabled = jest.fn();
            document.fullscreenchange = jest.fn();
            document.fullscreenerror = jest.fn();
        });

        afterEach(() => {
            jest.clearAllMocks();
        });

        test('test name', (done) => {
            // Assemble

            // Act

            // Assert
            expect(screenfull.toggle).toHaveBeenCalled();
            done();
        });
    });