webdriverio / webdriverio

Next-gen browser and mobile automation test framework for Node.js
http://webdriver.io
MIT License
9.1k stars 2.52k forks source link

[šŸ› Bug]: Error clicking in "a" element in chromedriver version 103 #8482

Closed carri747 closed 2 years ago

carri747 commented 2 years ago

Have you read the Contributing Guidelines on issues?

WebdriverIO Version

7.16.13

Node.js Version

15.14.0

Mode

WDIO Testrunner

Which capabilities are you using?

browserName: 'chrome',
        'goog:chromeOptions': {
            args: [
                '--disable-infobars',
                '--no-sandbox',
                '--disable-gpu',
                '--disable-setuid-sandbox',
                '--disable-dev-shm-usage',
                '--window-size=1920,1080'
            ],
            prefs: {
                ['profile.default_content_setting_values.images']: 2
            }
        }

S.O: Windows 10

What happened?

I updated chromedriver version from 102 to 103 due to chrome updated the version and the behavior that worked before the upgrade not it's not working properly now.

The situation:

  1. I open a main page where I have a lot of link to click
  2. I find a specific link with a xpath
  3. I click in the link found previously, no problem to found the xpath.
  4. The driver click in the link
  5. Open the next page associated to the link
  6. Fail with a timeout because it's not possible to click in the previous link. Make not sense because the driver clicked in the link before

What is your expected behavior?

No timeout trying to click in the link. The same code works with following version:

How to reproduce the bug.

In a cucumber step click in "a" element in html

This is more or less the code to reproduce

Scenario: Click in link
 Given I click in link

Given('I select sale {string} on landing page', async function (sale: string) {
    const commonPage = new CommonPageObject();
    await commonPage.clickSaleOnLandingPage(saleData);
});

   async clickSaleOnLandingPage(saleData: IAuctionEvent) {
         await (await this.specificSaleOnLandingPage(saleData)).click();   
    }

  async specificSaleOnLandingPage(saleData: IAuctionEvent): Promise<WebdriverIO.Element> {
        return $(`//a[contains(@href, "/sso?SaleID=${saleData.saleId}&SaleNumber=${saleData.saleNumber}")]`);
    }

Relevant log output

1) Sale Page labels in "English" language And I select sale "AUCTIONEVENT-SWITZERLAND" on landing page
[chrome 103.0.5060.66 windows #0-2] Error: Can't call elementClick on element with selector "//a[contains(@href, "/sso?SaleID=28937&SaleNumber=19645")]" because element wasn't found
[chrome 103.0.5060.66 windows #0-2] Error: Can't call elementClick on element with selector "//a[contains(@href, "/sso?SaleID=28937&SaleNumber=19645")]" because element wasn't found
[chrome 103.0.5060.66 windows #0-2]     at implicitWait (C:\Dev\git\Christies\AutomationTest\node_modules\webdriverio\build\utils\implicitWait.js:34:19)
[chrome 103.0.5060.66 windows #0-2]     at async C:\Dev\git\Christies\AutomationTest\node_modules\webdriverio\build\utils\refetchElement.js:29:16
[chrome 103.0.5060.66 windows #0-2]     at async Element.elementErrorHandlerCallbackFn (C:\Dev\git\Christies\AutomationTest\node_modules\webdriverio\build\middlewares.js:41:37)
[chrome 103.0.5060.66 windows #0-2]     at async Element.elementErrorHandlerCallbackFn (C:\Dev\git\Christies\AutomationTest\node_modules\webdriverio\build\middlewares.js:24:32)
[chrome 103.0.5060.66 windows #0-2]     at async Element.runCommandWithHooks (C:\Dev\git\Christies\AutomationTest\node_modules\@wdio\sync\build\wrapCommand.js:105:25)
[chrome 103.0.5060.66 windows #0-2]     at Element.runCommandWithHooks (C:\Dev\git\Christies\AutomationTest\node_modules\@wdio\sync\build\wrapCommand.js:100:24)
[chrome 103.0.5060.66 windows #0-2]     at Element.wrapCommandFn (C:\Dev\git\Christies\AutomationTest\node_modules\@wdio\sync\build\wrapCommand.js:67:44)
[chrome 103.0.5060.66 windows #0-2]     at Element.<anonymous> (C:\Dev\git\Christies\AutomationTest\node_modules\@wdio\utils\build\shim.js:270:24)
[chrome 103.0.5060.66 windows #0-2]     at Element.elementErrorHandlerCallback (C:\Dev\git\Christies\AutomationTest\node_modules\webdriverio\build\middlewares.js:48:12)
[chrome 103.0.5060.66 windows #0-2]     at CommonPageObject.<anonymous> (C:\Dev\git\Christies\AutomationTest\src\pageObject\commonPageObject.ts:35:19)
[chrome 103.0.5060.66 windows #0-2]     at fulfilled (C:\Dev\git\Christies\AutomationTest\src\pageObject\commonPageObject.ts:5:58)
[chrome 103.0.5060.66 windows #0-2]     at processTicksAndRejections (node:internal/process/task_queues:94:5)

Code of Conduct

Is there an existing issue for this?

christian-bromann commented 2 years ago

Can you provide a reproducible example?

carri747 commented 2 years ago

Scenario: test Given test click

Given('test click', async function () {
    await browser.url('https://onlineonly.christies.com/s/marc-chagall-colour-life-prints-artists-books-formerly-artists-estate/lots/3244');

    let commonPage = new CommonPageObject();
    let language = await commonPage.testLanguage();
    await (await $('#onetrust-accept-btn-handler')).click();
    await language.click();
//    await browser.pause(5000);
    await (await $('//a[@lang="en"]')).click();    

});
export default class CommonPageObject {
  async testLanguage(): Promise<ChainablePromiseElement<Promise<WebdriverIO.Element>>> {
        return $('//a[@lang="zh"]');
    }
}

You can see that WDIO click cookie popup to accept and after that change the language page to Chinese and after change the language again to English. Result:

I try the same with wdio 7.20.5 changing this Promise<ChainablePromiseElement<Promise>> to this Promise

erwinheitzman commented 2 years ago

This works just fine however there are a few noteworthy things:

  1. the window size matters, if the window is too small it will not show the language buttons
  2. you are using an incorrect selector //a[@lang="zh"] should be //a[@lang="zh-cn"]
  3. there's a privacy dialog that interrupts the test, first wait for the dialog, accept the cookies, then you are free continue
erwinheitzman commented 2 years ago

Apologies, I accidentally ran the above with puppeteer instead of chromedriver. When using Chromedriver this is indeed an issue and I would say that this seems to be an issue in the Chromedriver and not with WebdriverIO, would you agree @christian-bromann ?

erwinheitzman commented 2 years ago

Reproduction path that works in puppeteer but not in chromedriver:

Given('I am on the home page', async () => {
    await browser.setWindowSize(2000, 2000);
    await browser.url('https://onlineonly.christies.com/s/marc-chagall-colour-life-prints-artists-books-formerly-artists-estate/lots/3244');
    await $('#onetrust-accept-btn-handler').waitForDisplayed();
    await $('#onetrust-accept-btn-handler').click();
    await $('#onetrust-accept-btn-handler').waitForDisplayed({ reverse: true }); // make sure the dialog is gone before continuing to prevent flakyness
    await $('//a[@lang="zh-cn"]').click();
    await $('//a[@lang="zh-cn"]').waitForExist({ reverse: true }); // wait for language to be switched
    await $('//a[@lang="zh"]').waitForExist(); // verify that the new languages have loaded
    await $('//a[@lang="en"]').click();  
    await browser.pause(3000); // visually verify that the language changes from en to zh-cn back to en
});
christian-bromann commented 2 years ago

@erwinheitzman thanks for investigating. It is unlikely that this is a problem with WebdriverIO but I don't want to be draw any conclusions without knowing more. Do you see any difference how the browser behaves between devtools and webdriver? My assumption is that the xPath (//a[contains(@href, "/sso?SaleID=28937&SaleNumber=19645")]) is interpreted differently. Can we try with a different selector?

erwinheitzman commented 2 years ago

Sure, I can test this later today and will report the findings here

erwinheitzman commented 2 years ago

Using CSS selectors I got the same result: Error: Can't call elementClick on element with selector "a[lang="zh-cn"]" because element wasn't found

christian-bromann commented 2 years ago

Is the element actually there?

erwinheitzman commented 2 years ago

Well yeah because it passes in puppeteer so the element is rendered.

shlomobraverman commented 2 years ago

I'm experiencing the same bug. And as soon as I added after the clicking, wait a few seconds it somehow worked

mbjaved005 commented 2 years ago

I've encountered the same problem described in the 5th and 6th points. The button is clicked and the navigation is successful, but the command(click in this case) times out. Setting the retry timeout a little higher and the pageLoadStrategy to 'none' helps a bit but is definitely not the required solution here. The call still times out when multiple features are executed simultaneously especially using saucelabs.

carri747 commented 2 years ago

I found the same error in Edge. I don't know but maybe it's something regarding because both of them are base in Chromium

praveendvd commented 2 years ago

@christian-bromann and @erwinheitzman

The issue with this is that chromedriver throws error after clicking the element :

Request failed with status 500 due to unknown error: cannot determine loading status

and after that it retries to find the element again but by that time the language changes and element is no longer present

praveendvd commented 2 years ago

https://bugs.chromium.org/p/chromedriver/issues/detail?id=3361 ,

praveendvd commented 2 years ago

Just wait for all iframes:

        await browser.setWindowSize(2000, 2000);
        await browser.url('https://onlineonly.christies.com/s/marc-chagall-colour-life-prints-artists-books-formerly-artists-estate/lots/3244');
        await $('#onetrust-accept-btn-handler').waitForDisplayed();
        await $('#onetrust-accept-btn-handler').click();
        await $('#onetrust-accept-btn-handler').waitForDisplayed({ reverse: true }); // make sure the dialog is gone before continuing to prevent flakyness
        try {
            await browser.waitUntil(
                async () => await $$('//iframe').length === 5,
                {
                    timeout: 5 * 1000, // 5 seconds
                    timeoutMsg: 'Message on failure'
                }
            );
        } catch (e) {
            console.log(e);
        }
        await $('//a[@lang="zh-cn"]').click();
erwinheitzman commented 2 years ago

@praveendvd that shouldn't be needed though, especially since that bug is resolved. It might be that it is reintroduced by accident.

EDIT: also the element isn't inside an iframe so I see no reason as to how this is connected

praveendvd commented 2 years ago

@erwinheitzman yes but waiting for iframe or adding an implicit wait fixes the issue , it doesnt seems to be wdio related

christian-bromann commented 2 years ago

browser work in mysterious ways šŸ˜‰ it seems likely that a WebDriver error response unknown error: cannot determine loading status is related to some iframes not being loaded.

Thanks for the investigation @praveendvd I agree with your resolution here that this is not really something that can be fixed within WebdriverIO.