为什么使用 ReactTestUtils 将 iframe 渲染到 jsdom 中不提供 contentWindow

Why does rendering an iframe into jsdom with ReactTestUtils not provide a contentWindow?

本文关键字:jsdom contentWindow ReactTestUtils iframe 为什么      更新时间:2023-09-26

我正在尝试测试一个渲染iframe并将标记直接注入该iframe的React组件。(我没有尝试在 iframe 中加载 URL。

该组件实际上在浏览器中运行良好,但到目前为止似乎不可能为其编写测试。我不知道为什么。

这是一个缩短的测试用例,它证明了失败。


    // Set up DOM
    var jsdom = require( 'jsdom' ).jsdom;
    global.document = jsdom( '' );
    global.window = document.defaultView;
    global.navigator = document.defaultView.navigator;
    // Prepare React
    var ReactTestUtils = require( 'react-addons-test-utils' );
    var React = require( 'react' );
    var Frame = React.createClass( {
      componentDidMount() {
        console.log( 'iframe loaded. adding content' );
        const iframe = this.refs.iframe;
        console.log( 'adding content to', iframe.contentWindow ); // Should not be null
        iframe.contentWindow.document.open();
        iframe.contentWindow.document.write( 'Hello World!' );
        iframe.contentWindow.document.close();
      },
      render() {
        console.log( 'rendering iframe' );
        return React.createElement( 'iframe', { ref: 'iframe' } );
      }
    } );
    // Render the component
    ReactTestUtils.renderIntoDocument( React.createElement( Frame ) );

要运行它,你需要安装以下 npm 包:jsdom、react、react-addons-test-utils。然后,您应该能够使用 node 运行上述代码。

我非常彻底地测试了您的代码,最终发现问题ReactTestUtils.renderIntoDocument

ReactTestUtils.renderIntoDocument为组件创建了一个容器,但不再附加到 DOM,因此contentWindow对于iframe元素为 null。如果您阅读ReactTestUtils源中的评论,您会注意到他们正在考虑重命名renderIntoDocument因为从技术上讲它没有!解决方案似乎是直接使用ReactDOM

以下是来自ReactTestUtils源的一些代码:

var ReactTestUtils = {
  renderIntoDocument: function (instance) {
    var div = document.createElement('div');
    // None of our tests actually require attaching the container to the
    // DOM, and doing so creates a mess that we rely on test isolation to
    // clean up, so we're going to stop honoring the name of this method
    // (and probably rename it eventually) if no problems arise.
    // document.documentElement.appendChild(div);
    return ReactDOM.render(instance, div);
  },
 }

ReactDOM与测试用例一起使用可以:

// Set up DOM
var jsdom = require( 'jsdom' ).jsdom;
global.document = jsdom( '' );
global.window = document.defaultView;
global.navigator = document.defaultView.navigator;
// Prepare React
var ReactTestUtils = require( 'react-addons-test-utils' );
var React = require( 'react' ); 
var ReactDOM = require('react-dom')
var Frame = React.createClass( {
  componentDidMount() {
    console.log( 'iframe loaded. adding content' );
    const iframe = this.refs.iframe;
    console.log( 'adding content to', iframe.contentWindow ); // Should not be null
    iframe.contentWindow.document.open();
    iframe.contentWindow.document.write( 'Hello World!' );
    iframe.contentWindow.document.close();
    // Should log "Hello World!"
    console.log(iframe.contentWindow.document.body.innerHTML);
  },
  render() {
    console.log( 'rendering iframe' );
    return React.createElement( 'iframe', { ref: 'iframe' } );
  }
} );
// Render the component
// NOPE 
//ReactTestUtils.renderIntoDocument( React.createElement( Frame ) );
// YUP
ReactDOM.render(React.createElement(Frame), document.body)

太糟糕了,这在ReactTestUtils的文档中没有提到.

您需要在

contentWindow 可用之前将 iframe 附加到文档中。

检查此代码:

var iframe = document.createElement('iframe');
console.log(iframe.contentWindow);
document.body.appendChild(iframe);
console.log(iframe.contentWindow);