Search code examples
javascriptiframefunctional-testinginternleadfoot

How to proceed AFTER switching frames in InternJS


Can someone tell me how to proceed with referencing an element within an iframe after the switching of frames is done? I've already looked at the solution presented in How to switch iframes InternJS to no avail, and the information in intern Functional Testing with Frames is just not applicable (yet.) The following script returns an error of Cannot read property 'apply' of undefined type: TypeError:

return Remote
    .findAllByTagName('iframe')
    .then(function (frames) {
        return new Remote.constructor(Remote.session)
            .switchToFrame(frames[0])
            .getProperty('title')
            .then(function (result) {
                expect(result).to.equal('Rich text editor, rtDescAttach');
            });
    });

The only reason I can see that the script is failing is that the frame was not located correctly. There are two on the page and I need the first one. Once this is done I'd really like to put the reference to the frame in a page object (which is where I feel it belongs) but I have to be able to successfully locate it first, so no putting the cart before the horse. Suggestions and help greatly appreciated.


Solution

  • Your example is actually pretty close. The main issue is that getProperty('title') won't work in the way it's being used. getProperty is an element method, and at the point you're calling it you don't have a valid element on the context stack. Assuming you're trying to get the title of the iframe page, you'll need to use an execute callback, like:

    .switchToFrame(frames[0])
    .execute(function () {
        return document.title;
    })
    .then(function (title) {
        // assert
    })
    

    Leadfoot has a getPageTitle callback, but it always returns the title of the top-level document (the one who's title is in the browser titlebar or tab).

    Another minor issue is that the more canonical way to access the remote in a callback is via the parent property, like:

    .then(function (frames) {
        return this.parent
            .switchToFrame(frames[0])
            // ...
    })
    

    If you want to access elements within an iframe, you'll need to switch frames, reset the search context, and then find the elements, like:

    .findAllByTagName('iframe')
    .then(function (frames) {
        return this.parent
            // clear the search context in this callback
            .end(Infinity)
            // switch to the first frame
            .switchToFrame(frames[0])
            // find an element in the frame, examine its text content
            .findById('foo')
            .getVisibleText()
            .then(function (text) {
                assert.equal(text, 'expected content');
            })
            // switch back to the parent frame when finished
            .switchToParentFrame()
    })
    // continue testing in parent frame
    

    A couple things to note:

    1. The search context is local to the command chain, so changes on the this.parent-based command chain don't persist on the parent command chain. Basically, there's no need to call .end() at the end of the command chain in the callback.
    2. The active frame is not local to the command chain, so if you change the frame on a this.parent-based chain, you'll need to reset it if you want to return to the parent frame after the callback.