DEV Community

James
James

Posted on

Testing iframes in React with Enzyme

I recently worked on an unusual and somewhat anachronistic requirement, presenting a marketing/sales form using an iframe inside a React.js app, with a seamless UX to boot. The integration uses postMessage for cross-domain page communication, which proved an interesting challenge for unit-testing.

The stripped down example below demonstrates the use of a React function component and a callback ref. The component sends a message to the page within the iframe once it has loaded.

function IframeComponent({ domain, path }) {
  let iframeRef;
  const post = (msg) => iframeRef.contentWindow.postMessage(JSON.stringify(msg), domain);
  const onIframeLoad = () => post({foo:'bar'});
  const getRef = (el) => iframeRef = el;
  return (
      <iframe
        ref={getRef}
        src={domain + path}
        onLoad={onIframeLoad}
      />
  );
}
Enter fullscreen mode Exit fullscreen mode

I wanted to test the load behaviour without having to load the contents of the iframe, my solution (after a lot of digging around on the internet) was to use a test double for the iframe by gaining access to, and invoking the ref callback.

The test below is written with Mocha, Chai, Enzyme and Sinon

describe('IframeComponent', () => {
  it('should invoke load message correctly', () => {
    const domain = 'https://my.domain.com';
    const wrapper = shallow(<IframeComponent domain={domain} path="/page"/>);
    const iframe = wrapper.find('iframe');
    const spy = mockIframePostMessage(iframe);
    const props = iframe.props();

    expect(props.src).to.be.equal(domain + path);
    expect(spy.called).to.be.equal(false);

    iframe.simulate('load');

    expect(spy.called).to.be.equal(true);
    expect(spy.args[0][0]).to.be.equal(JSON.stringify({ foo: 'bar' }));
    expect(spy.args[0][1]).to.be.equal(domain);
  });
});
Enter fullscreen mode Exit fullscreen mode

And below shows how, using Enzyme we can find the iframe React node and invoke its ref callback, providing the simple test double.

function mockIframePostMessage(iframe) {
  const postMessage = sinon.spy();
  const iframeMock = {
    contentWindow: {
      postMessage,
    },
  };
  iframe.getNode().ref(iframeMock);
  return postMessage;
}
Enter fullscreen mode Exit fullscreen mode

Note: I believe getNode has now been replaced by getElement in the latest version of Enzyme.

This approach provides a nice, simple way to write fast tests for iframes in React πŸš€

Top comments (0)