sitespeedio / sitespeed.io

sitespeed.io is an open-source tool for comprehensive web performance analysis, enabling you to test, monitor, and optimize your website’s speed using real browsers in various environments.
https://www.sitespeed.io/
MIT License
4.74k stars 601 forks source link

switch to iframe by xpath #3644

Closed zzhao2010 closed 9 months ago

zzhao2010 commented 2 years ago

Your question

I was trying to use the switch function to switch to different iframes in the page. However it didn't work as expected. I had to use xpath because there is no id attribute defined for the iframe that I wanna switch to. As the example shows below, I had to switch to a child iframe inside a frame. It didn't throw out exception but it didn't work because the iframe and targetIframe variables returned the identical value.

const iframe = await seleniumDriver.findElement(By.xpath('//iframe[@title="accessibility title"]'));
context.log.info('The iframe is: ', iframe)
await commands.switch.toFrame(iframe)
context.log.info('Switch to the target iframe')
const targetIframe = await seleniumDriver.findElement(By.xpath('//iframe[@id="vod_iframe"]'));
context.log.info('The iframe is: ', iframe)

And then I tried to use selenium switchTo().frame function directly like below, which worked when I was just testing it as a selenium script. But it threw an exception after I ran it on sitespeed.io.. await seleniumDriver.switchTo().frame(seleniumDriver.findElement(By.xpath('//iframe[@title="accessibility title"]')));

ERROR: No page that could be associated with the error:iframe is not defined [2022-05-02 17:17:15] ERROR: ReferenceError: iframe is not defined at module.exports (/sitespeed.io/lex-record_account_professional.js:12:45) at runMicrotasks () at processTicksAndRejections (node:internal/process/task_queues:96:5) at async /usr/src/app/node_modules/browsertime/lib/core/engine/run.js:7:9 at async Iteration.run (/usr/src/app/node_modules/browsertime/lib/core/engine/iteration.js:221:9) at async Engine.runByScript (/usr/src/app/node_modules/browsertime/lib/core/engine/index.js:266:20) at async Object.analyzeUrl (/usr/src/app/lib/plugins/browsertime/analyzer.js:145:21) at async Object.processMessage (/usr/src/app/lib/plugins/browsertime/index.js:150:26)

soulgalore commented 2 years ago

Hi @zzhao2010 thanks for creating the issue!

For the thing were you can't switch using commands.switch.toFrame what's the easiest way I can reproduce that?

ERROR: No page that could be associated with the error:iframe is not defined

Can you share your full script, its hard to guess what is happening? Can you share more what you are trying to accomplish? Like the full user flow?

zzhao2010 commented 2 years ago

@soulgalore Sure. 「 Can you share your full script, its hard to guess what is happening? Can you share more what you are trying to accomplish? 」


:I am trying to measure a custom feature on a salesforce org. Basically the user flow is that I logged in a SF org. And then I click a tab called (My Account), which is gonna list out a bunch of accounts. Then I just pick an account to start measuring the performance of a feature in that account. And the issue happened at the step to switch to iframe. There are many iframes in the page. To be able to locate the account, I had to witch to different iframe (nested) twice to get the right frame to be able to identify the element. I tested it first with selenium script and it worked just fine. But it didn't work after I migrated the same logic to sitespeed.io. The script was failed at the step of accessing iframe.

「 what's the easiest way I can reproduce that? 」


:Unfortunately I cannot share with you the login info to my salesforce org. But looks like it's a generic issue.. the commands.switch.toFrame function looks like not supporting xpath as the argument. So I tried to locate the target iframe by findElementByXpath method and assign it to a variable and then pass the variable to the commands.switch.toFrame(iframe)function. it didn't work... const iframe = await seleniumDriver.findElement(By.xpath('//iframe[@title="accessibility title"]')); And then I tried to use selenium directly like I did in my selenium script and I received the error msg that I shared above.

Below is my selenium script that worked as expected.

from selenium import webdriver

baseUrl      = "https://login.salesforce.com/"
username     = "xxx"
password     = "xxx"
accountName  = "Aaa,Aaa"
browser = webdriver.Chrome()
browser.get(baseUrl)
browser.find_element_by_id("username").send_keys(username)
browser.find_element_by_id("password").send_keys(password)
browser.find_element_by_id("Login").click()
browser.implicitly_wait(10)

# Click Accounts in the menu
browser.find_element_by_xpath('//one-app-nav-bar-item-root[@data-id="0KD5f000001Jh2QGAS"]').click()
browser.implicitly_wait(20)

# Switch to the parent iframe
browser.switch_to.frame(browser.find_element_by_xpath('//iframe[@title="accessibility title"]'))
# Switch to the child iframe
browser.switch_to.frame(browser.find_element_by_xpath('//iframe[@id="vod_iframe"]'))

# Add assertion to verify if text is present
browser.find_element_by_xpath('//td[@class="dataCell"]/a[@href="https://pageperformancemonitoring--c.visualforce.com/0015f000007giMKAAY"]').click()
# assert element == accountName, "element was not found"

Below is the portion of my sitespeed.io script after login. Firstly I tried the switch.toFrame function provided. There was no id or name attribute defined for the iframe I was looking for, so I had to use xpath to identify the iframe first and then passed it to the toFrame function. However it didn't work. And then I tried to use selenium directly and it turned out not working either.

module.exports = async function(context, commands) {
    const seleniumWebdriver = context.selenium.webdriver;
    const seleniumDriver = context.selenium.driver;
    const By = seleniumWebdriver.By;
    const accountUrl = 'https://xxx.com/lightning/n/MyAccounts';
    await commands.navigate(accountUrl);
    await commands.wait.byTime(10000);
    try {
        context.log.info('Switch to the parent iframe');
        await seleniumDriver.switchTo().frame(seleniumDriver.findElement(By.xpath('//iframe[@title="accessibility title"]')));
//        const iframe = await seleniumDriver.findElement(By.xpath('//iframe[@title="accessibility title"]'));
        context.log.info('The iframe is: ', iframe)
        await commands.switch.toFrame(iframe)
        context.log.info('Switch to the target iframe');
        const targetIframe = await seleniumDriver.findElement(By.xpath('//iframe[@id="vod_iframe"]'));
        context.log.info('The iframe is: ', iframe)
        await commands.switch.toFrame(targetIframe);
        context.log.info('Find an account');
        await commands.click.byXpathAndWait('(//td[contains(text(), "Professional")]/preceding-sibling::td/a)[1]');
        await command.wait.byTime(3000);

        await commands.wait.byXpath('//lightning-button/button[contains(text(), "Record a Call")]', 8000);
        await commands.measure.start();
        await commands.click.byXpathAndWait('//lightning-button/button[contains(text(), "Record a Call")]');
        await commands.wait.byXpath('(//span[contains(., "Professional Information")])[1]', 6000);
        return commands.measure.stop();
    } catch (e) {
       throw e;
    }
};
zzhao2010 commented 2 years ago

@soulgalore Any updates for this issue?

soulgalore commented 2 years ago

@soulgalore sorry I didn't look since there wasn't an easy way to reproduce.

Looking at the Selenium docs, it looks like the id is "A number that specifies a (zero-based) index into window frames and not the id of an element"? https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_TargetLocator.html - do that work if you try?

For the code in your example, that looks right according to the docs right?

 const targetIframe = await seleniumDriver.findElement(By.xpath('//iframe[@id="vod_iframe"]'));
 context.log.info('The iframe is: ', iframe)
 await commands.switch.toFrame(targetIframe);

In your example you use Python flavour of Selenium, I wonder if there's a bug in Selenium, if you can do a simple test case that would be super helpful, thanks!

soulgalore commented 9 months ago

Didn't get any response so closing.