Button click within an Iframe nested in another Iframe

I have this button that’s nested in a form within a document, that’s also nested in two iframes. I’ve been able to get access elements of the first iframe and able to get the name of the iframe that is nested within. The problem is I can’t seem to preform a click action on the button in question.

    const iframeHandle = page.waitForSelector('//iframe');
    const frame = iframeHandle.contentFrame();

    let confirmFrameHandle = frame.waitForSelector('iframe[id*="SomeID"]');
    let confirmFrame = confirmFrameHandle.contentFrame();

    console.log("name",confirmFrame.name()); // This returns the name of the Iframe I wish to work on at least

//Cannot seem to find this button within the Iframe!
    const button = confirmFrame.waitForSelector('//button[text()="Confirm"]');
    button.click();

I’ve tried console logging the forms around the button as well to no avail. Wondering if there is anyway about this.

Hi @maxahudson,

Welcome to the forum and thanks for your question.

I need to investigate this a little further, but I was wondering if you had a webpage that I can run the same test against to help you out?

that’s also nested in two iframes

I see that you were able to get the reference to the first iframe, but don’t you need the test to go one level down to the second iframe?

The problem is I can’t seem to preform a click action on the button in question.

Does the test timeout?

Cheers,
Ankur

@maxahudson

I was able to find the button with this selector:

const button = confirmFrame.waitForSelector('button[text="Confirm"]');

Although the button is found, I can’t perform the click() because k6 panicked:

panic: runtime error: invalid memory address or nil pointer dereference
github.com/grafana/xk6-browser/common.(*Page).getFrameElement(0xc000501520, 0xc0008161c0)
        github.com/grafana/xk6-browser@v1.1.0/common/page.go:274 +0x2d6
github.com/grafana/xk6-browser/common.(*Frame).FrameElement(0xc0008161c0)
        github.com/grafana/xk6-browser@v1.1.0/common/frame.go:834 +0x106
...

(It seems to me, that there is a bug somewhere, because the FrameSession doesn’t get the AttachedToTarget event)

WORKAROUND: I was able to do the click() action with this:

 confirmFrame.evaluate(() => {document.getElementById("confirmButton").click(); });

Hi @bandorko,

That’s a nice workaround!

I haven’t tried to reproduce this bug locally yet. Still, it should be quite easy to find this bug. It would be great if you could post your test HTML and script here to quicken up our process (maybe or @maxahudson). So we can reproduce the error and possibly come up with a fix.

Thanks!

@inancgumus

In my case these were the HTMLs

index.html:

<body>
  <iframe id="myIframe" name="myIframe" src="iframe.html"></iframe>
  <button id="myButton" onclick="console.log('myButton pressed')">Button outside</button>
</body>

iframe.html:

<body>
  <iframe id="SomeID" name="subIframe" src="iframe1.html"></iframe>
  <button id="iframeButton" onclick="console.log('iframeButton pressed')" text="IframeButton">Iframe button</button>
</body>

iframe1.html:

<body>
  <button id="confirmButton" name="iframe1Button" onclick="console.log('Confirm button Pressed')" text="Confirm">Confirm</button>
</body>

test script:

export default async function () {
  const page = browser.newPage();
    
    try {
      await page.goto('http://localhost:9080');
      const iframeHandle = page.waitForSelector('//iframe');
      const frame = iframeHandle.contentFrame();
      const button1 = frame.waitForSelector('button[text="IframeButton"]');
      await button1.click();  // this works perfectly

      const confirmFrameHandle = frame.waitForSelector('iframe[id*="SomeID"]');
      const confirmFrame = confirmFrameHandle.contentFrame();
      const button2 = confirmFrame.waitForSelector('button[text="Confirm"]');
      await button2.click(); // panicked
      //confirmFrame.evaluate(() => {document.getElementById('confirmButton').click(); });
    } finally {
        page.close();
    }
}
1 Like

@ankur
The webpage only generates the second Iframe in the test environment (which requires access) as it simulates accepting a card payment.
I do go one level down and the test times out when trying for the button.

<iframe id="iframe_..." name="iframe_..." frameborder="0" allowtransparency="true" style="width:100%; height:100%;"
    src="....."></iframe>

<html>
<head></head>
<body>
    <section id="overlay" class="overlay" role="dialog" aria-labelledby="overlayTitle">
        <iframe id="someID" name="someName" class="SomeClass" style="opacity: 1; transform: scale(1);"></iframe>
        <html>
        <head></head>
        <body style="">
            <div class="vertical-center">
                <div class="container">
                    <div id="a-form">
                        <p class="text-center">
                        </p>
                        <form action="https://..." method="post">
                            <button type="submit" id="Y" class="btn btn-sm btn-success">Yes (Y)</button>
                            <input type="hidden" value="RandomGeneratedValueForYes" name="Hash">
                            <input type="hidden" value="RandomGeneratedValueForYes" name="P">
                        </form>
                        <form action="https://...." method="post">
                            <button type="submit" id="N" class="btn btn-sm btn-danger">No (N)</button>
                            <input type="hidden" value="RandomGeneratedValueForNo" name="Hash">
                            <input type="hidden" value="RandomGeneratedValueForNo" name="P">
                        </form>
                    </div>
                </div>
            </div>
        </body>
        </html>
        </iframe>
    </section>
</body>

</html>
</iframe>

@bandorko
I tried the work around to no avail, it may be due to the fact it’s nested within a form, not quite sure myself.
Thanks in advanced, also surprised at your fast replies.

@maxahudson

On my computer I was able to click the Yes button also with the workaround. I had to fix the HTML you posted, because it is not valid. (there are 4 </iframe> , but just 2 <iframe>, and I had to split it into 3 HTMLs because as far as I know iframe tag doesn’t support content, but you have to use the src attribute instead)

try this, it works for me:

      const confirmFrameHandle = frame.waitForSelector('iframe[id*="someID"]');
      console.log('confirmFrame found');
      const confirmFrame = confirmFrameHandle.contentFrame();
      const button2 = confirmFrame.waitForSelector('button[id="Y"]');
      console.log('button found');
      confirmFrame.evaluate(() => {document.getElementById('Y').click(); });
      console.log('button clicked');
      //button2.click(); // this is panicking

My apologies, the closing tags at the end are the right ones.
I’m still not able to find the button at all, keeps running till timeout at the waitForSelector part.
I get
panic: GoError: dispose: canceled
After the 7 minute mark.
These iFrames are created by a third party, so have no control over the layouts of the html. Have also enabled bypassCSP: true, just in case, even though I never had the issue occur in the console.

@maxahudson

Which log messages do you see in the console? confirmFrame found, button found, button clicked. I’d like to know where the script stuck.
And which version of k6 do you use?

I get the ConfirmFrame found then it just runs for 7 minutes, so never gets to the button found part.
I’m running k6 v0.46.0 Which I assume is the latest.

INFO[0028] confirmFrame Found                            source=console
panic: GoError: dispose: canceled
running (07m54.9s), 1/1 VUs, 0 complete and 0 interrupted iterations
goroutine 88 [running]:--------------------------] 1 VUs  07m54.9s/10m0s  0/1 shared iters
go.k6.io/k6/js/common.Throw(0x13dc680?, {0x16b8ac0?, 0xc0012bed40?})
        go.k6.io/k6/js/common/util.go:20 +0x4d
github.com/grafana/xk6-browser/k6ext.Panic.func1(0x11b7000?, {0xc001005ed0?, 0x1247501?, 0xc000de4b40?})
        github.com/grafana/xk6-browser@v1.0.2/k6ext/panic.go:35 +0x4a
github.com/grafana/xk6-browser/k6ext.sharedPanic({0x16ca030, 0xc002b1e0f0}, 0xc000c13a30, {0xc001005ed0?, 0x1, 0x1})
        github.com/grafana/xk6-browser@v1.0.2/k6ext/panic.go:64 +0x265
github.com/grafana/xk6-browser/k6ext.Panic({0x16ca030?, 0xc002b1e0f0?}, {0x13dc680?, 0xc000bfc2f0?}, {0xc001005ed0?, 0xc000bfc300?, 0x1156d20?})
        github.com/grafana/xk6-browser@v1.0.2/k6ext/panic.go:37 +0x65
github.com/grafana/xk6-browser/common.(*BaseJSHandle).Dispose(0xc000978140)
        github.com/grafana/xk6-browser@v1.0.2/common/js_handle.go:70 +0xa9
github.com/grafana/xk6-browser/common.(*Frame).detach(0xc000b82000)
        github.com/grafana/xk6-browser@v1.0.2/common/frame.go:196 +0x15e
github.com/grafana/xk6-browser/common.(*FrameManager).removeFramesRecursively(0xc001156180, 0xc001d15f40?)
        github.com/grafana/xk6-browser@v1.0.2/common/frame_manager.go:415 +0x2a6
github.com/grafana/xk6-browser/common.(*FrameManager).frameDetached(0xc001156180, {0xc001d15f40, 0x20}, {0x13d51ae, 0x6})
        github.com/grafana/xk6-browser@v1.0.2/common/frame_manager.go:186 +0x166
github.com/grafana/xk6-browser/common.(*FrameSession).onFrameDetached(0xc0014b54a0, {0xc001d15f40, 0x20}, {0x13d51ae, 0x6})
        github.com/grafana/xk6-browser@v1.0.2/common/frame_session.go:710 +0x185
github.com/grafana/xk6-browser/common.(*FrameSession).initEvents.func1()
        github.com/grafana/xk6-browser@v1.0.2/common/frame_session.go:237 +0x5a7
created by github.com/grafana/xk6-browser/common.(*FrameSession).initEvents
        github.com/grafana/xk6-browser@v1.0.2/common/frame_session.go:211 +0x1ab

@maxahudson

At the moment the latest version is v0.47.0, but for me it works with v0.46.0 also.
So I think there may be some difference between our HTML contents. I assume, that, the iframe with someID is not the only one in your case, because I can reproduce your behavior, when I modify my iframe.html, and add an another iframe before the SomeID iframe. In this case my script doesn’t find the button either. (If I add the second iframe below the someID, then the script finds the button)

So, can you try this? Maybe it will work for you.

      const iframeHandle = page.waitForSelector('//iframe');
      const frame = iframeHandle.contentFrame();    // this is the outer frame
      //const confirmFrameHandle = frame.waitForSelector('iframe[id*="someID"]');
      //console.log('confirmFrame found');
      //const confirmFrame = confirmFrameHandle.contentFrame();
      //const button2 = confirmFrame.waitForSelector('button[id="Y"]');
      //console.log('button found');
      //confirmFrame.evaluate(() => {document.getElementById('Y').click(); });
      frame.evaluate(() => {document.getElementById('someID').contentWindow.document.getElementById('Y').click();});

Thanks, @bandorko. I’ve reported this as a bug here.

2 Likes

I saw that you’ve released a supposed fix for this in v0.48.0, although I still seem to be getting a panic. Is there some form of document that could be of use?

Hi, @maxahudson, we haven’t yet released support for this functionality. It’ll probably land in v0.50.0.

1 Like

@inancgumus @maxahudson :

The testcase I mentioned in Button click within an Iframe nested in another Iframe - #7 by bandorko is not working with v0.47.0, but working with v0.48.0 and v0.49.0.

So If you run it with at least v0.48.0 and still get the panic, then please copy the error message here, because it may be an another panic.

2 Likes