4
votes

I am writing a react component which will load a URL within an iframe, then when the iframe's onLoad event fires it will call contentWindow.postMessage(). I'd like to prove this functionality using Jest, Enzyme, and JSDOM.

My component wraps react-iframe and looks pretty simple:

export class FilteredIframe extends React.PureComponent<FilteredIframeProps> {
  onload = (e:Window) => {
    console.log("ONLOAD CALLED");
    if (this.props.filters) {
        e.postMessage(this.props.filters, this.props.url);
    }
  }
  render() {
    return (<Iframe url={this.props.url}
        display="initial"
        position="static"
        onLoad={this.onload}
    />);
  }
}

I'm trying to figure out how to get enzyme/jsdom to test this, but I'm failing:

test("Posts message once the frame has loaded", async () => {
  const payLoad = { data: "data" };
  const result = mount(<FilteredIframe url="https:///www.bing.com" filters={payLoad}/>);
})

When running this in jest, I never see the "ONLOAD CALLED" message in the console. Is there some sort of special thing I need to do for jsdom or enzyme to make it actually call onLoad?

4

4 Answers

0
votes

I revisited this and figured out I can call onLoad() of the iframe inside my component directly. I now have something like this:

test("Posts message once the frame has loaded", async () => {
    const payLoad = { data: "data" };
    const result = mount(<FilteredIframe url="https:///www.bing.com" filters={payLoad} />);
    const iframe = result.find("iframe");

    //mock contentWindow so we can examine messages
    let receivedFilters = {};
    const mockIFrameContents = {
        contentWindow : {
            postMessage: function (filters, url) {
                receivedFilters = filters;
            }
        }
    }
    result.instance().setIframeRef(mockIFrameContents);

    //Signal the contents have loaded
    iframe.props().onLoad();
    expect(receivedFilters === payLoad).toBeTruthy();
});

I also modified the component a little to use a ref for the iframe itself, and use the ref's contentWindow rather than the event target. But the real answer here was just to mock up the iframe contentWindow and call it's onLoad() directly, rather than try to get it to actually load something.

0
votes

Forcing an update on a mounted wrapper worked for me.

<iframe onLoad={this.iframeLoaded}></iframe>

and test like so...

const mountWrapper = mount(<App />);
let container;

describe('iframe', () => {
    beforeEach(() => {
        container = mountWrapper.find('iframe');
    });

    it('calls iframeLoaded() when loaded', () => {
        const spy = jest.spyOn(mountWrapper.instance(), 'iframeLoaded');
        mountWrapper.instance().forceUpdate();
        container.simulate('load');
        expect(spy).toHaveBeenCalledTimes(1);
    });
});
0
votes

You need to attach mounted iframe to document, there is attachTo option for mount to do this.

0
votes

OPs answer had the pieces I needed. If you don't need the iframe loaded, but just the trigger (like if the iframe src is a pdf), trigger onLoad and update.

act(() => {
      result.find('iframe').props().onLoad();
});
result.update();