angular / protractor

E2E test framework for Angular apps
http://www.protractortest.org
MIT License
8.75k stars 2.31k forks source link

Issue regarding e2e waiting for a locator #4726

Closed gijop closed 6 years ago

gijop commented 6 years ago

Hi, I'm facing the issue regarding the wait for a locator using browser.driver.findElement(by.id('abc').click();

The actual problem is, the the element is present in the DOM and it will get the click also, but it doesn't works properly. That is it shows an error 'Unalbe to locate the element'. How can I resolve this ? I doesn't need to wait for a particular amount of time but need to wait until the element get invoked ? Anybody please help ?

IgorSasovets commented 6 years ago

Hi, @gijop ! One of possible solutions is to create custom wait based on DOM using pure JS.

flucena89 commented 6 years ago

Hey, @gijop! i faced that same issuea couples days ago, but looking on the web, i found this snippe that works for me: browser.sleep(300).then(function () { browser.actions().sendKeys(protractor.Key.ENTER).perform(); callback(); });

try it and tell me if work for you too!

gijop commented 6 years ago

Hey, @flucena89 , the browser.sleep(300) option will definitely work with no doubt. But it is not a good way to wait upto the time expires, instead we need to wait until the element or anything we need get happens. So that was my issue ? Any way thank you for your suggestion ?

gijop commented 6 years ago

Hey, @IgorSasovets , How it works ?

IgorSasovets commented 6 years ago

@gijop, you can use offsetParent property of DOM element. It returns null if element is not visible on the page and object in another case. To check this, navigate to ESPN, open console and type these commands:

document.querySelector('.contentCollection.mobile-tablet-only').offsetParent
document.querySelector('.contentItem.contentItem--hero').offsetParent

As you'll see, in first case you receive null, because element presented in DOM but not visible. So, after visibility check add, for example browser.sleep(1000). You will have something like this:

return browser.executeScript('return document.querySelector(arguments[0]).offsetParent', <put_element_selector_here>).then(res => {
     if (res === null)
        return browser.sleep(<some_amount_of_time>)
})

Don't forget that element should be presented in the DOM. Otherwise, you'll get Javascript error. To avoid this, you can add additional check to your script. Also, you can use async await to do if more flexible and get possibility to execute this code in for or while loop.

maxmarkus commented 6 years ago

There are also functions provided by protractor which work most of the time (eg. NOT when you have css opacity transitions, you need browser.sleep() then).

// this is in our utilityPO page object class
public async waitClickable(elem: ElementFinder): Promise<void> {
  const maxWaitTime: number = 10000;
  const until: ProtractorExpectedConditions = protractor.ExpectedConditions;
  await browser.wait(until.elementToBeClickable(elem), maxWaitTime, 'Element taking too long to appear in the DOM');
}

Be aware, also your test needs to be async then..

it('click that button', async() => {
    const selectedBtn: ElementFinder = element(by.css('#mybutton'));
    await this.utilityPO.waitClickable(selectedBtn);
    selectedBtn.click();
gijop commented 6 years ago

@maxmarkus Thank you so much for the suggestion, but here the problem is Iam using with mixed of angular page and non-angular page. So it wont work correctly. Thats why ?

gijop commented 6 years ago

@IgorSasovets Thank you !

maxmarkus commented 6 years ago

@gijop This should work also for non-angular stuff, as it is chrome driver stuff as far as I understood. Unfortunately exactly there is currently a bug, which renders this function unusable in certain use cases. From 4-5 protractor ways that I tried, none worked also for me (modal with css opacity transition was clicking backdrop until it was fully transitioned) .. I did it with a browser.sleep for sake of simplicity. Another approach could be a setInterval every 100ms with click() inside a try/catch and as soon as the try resolves, it cancelles the setInterval. Super ugly, but might do the job until the bug is solved.

flucena89 commented 6 years ago

hey @gijop, you're right about de sleep function, i solve my problem asking angular if some element is present in the DOM, and set a timeout of 1 minto wait in the config file.

protractor continue with it process when the element in avalible on the page, this is the most efective way to wait that i found until yet

qiyigg commented 6 years ago

The ExpectedConditions should work in your case. For non-angular page, you can just disable waiting for angular (https://github.com/angular/protractor/blob/master/docs/timeouts.md#how-to-disable-waiting-for-angular)

I believe this problem should be asked in stackoverflow, since github issue should only track bug or new feature request. Close it now.