【问题标题】:TypeError from screenfull library during jest test开玩笑测试期间来自screenfull库的TypeError
【发布时间】:2018-06-28 17:50:36
【问题描述】:

我已将全屏 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 类似...)我不知道如何解析它。我不认为它与打字稿有关。有人知道吗?

【问题讨论】:

    标签: reactjs typescript redux jestjs enzyme


    【解决方案1】:

    我(嗯,主要是我的老板)想通了。原来,全屏代码检查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

    【讨论】:

    • 您好,您能详细说明一下您的答案吗?我不知道该代码的确切位置。谢谢!
    【解决方案2】:

    @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 有点不同,因为我只在我的应用程序的两种方法中使用了 screenfull,所以我不需要为我的整个测试环境设置它们。这是我的方法:

    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();
            });
        });
    

    【讨论】:

      【解决方案3】:

      对于单元测试中的一件事,您可能想要jest.mock 这个依赖项。 _screenfull2.default.on is not a function 错误可能是由于 commonjs 模块和 es6 模块不匹配造成的。你是如何导入全屏的?

      【讨论】:

      • 感谢您的回复。我将它作为import screenfull from 'screenfull'; 导入,就像我的所有其他依赖项一样。
      • 当您手动测试时,它是否在单元测试之外工作?
      • 我也试过import * as screenfull from 'screenfull'const screenfull = require('screenfull'),但我得到了同样的错误
      • 如果你在实际的应用程序代码中没有这个错误,那么问题可能出在 jest 加载模块的方式上。结帐jestjs.io/docs/en/getting-started.html#using-babel
      • 你可能还想考虑 ts-jest
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-17
      • 2020-02-06
      • 2020-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多