Divya
Divya

Reputation: 83

Async/await logic in protractor tests doesn't detect a page element

I am new to writing e2e tests in protractor. I started using async await in protractor tests and added browser wait as well. Below is my code. The problem the menuBtn is never found by protractor and it timesout. I tried using ExpectedCOnditions of visilityOf and presenceOf. but they all hang there and never get resolved. I thought that I am giving a wrong locator. However when use $x('xPath on line 3') it detects it on the web page. Could anyone please help on this.

async menuNav(title, pageElement) {
    browser.sleep(3000);
    // browser.waitForAngular();
    const menuBtn = element(by.xpath('.//mat-icon[@id="' + pageElement + '"]'));

    console.log('menu btn: ', menuBtn.locator());
    await browser.wait(() => {
        return browser.isElementPresent(menuBtn);
    }, 3000);

    await menuBtn[0].click();
    const  menuTitle = element(by.xpath('.//*[contains(text(),\''  + title + '\')]'));
    browser.sleep(3000);
    menuTitle.isPresent().then(result => {
        expect(result).toBe(true);
        console.log('inside is present');
        console.log(title);
    });

}

This is how the HTML looks like

<div *ngFor="let item of items"
    class="item"
    placement="right"
    container="body"
    containerClass="sidebar-tooltip">

    <mat-icon class="icon" [id]="item.id" [svgIcon]="item.icon"></mat-icon>
  </div>

Based on comments from @Gunderson and @SimonN I added await before the element locators. But I thought that they are just definitions since I am not using protractor locators like findByElement. However I changed the code and it still blocks at menuBtn.click and this my code now:

async menuNav(title, pageElement) {
    browser.sleep(3000)
    const menuBtn = await element(by.xpath('.//mat-icon[@id="' + pageElement + '"]'));

    browser.wait(() => {
        return browser.isElementPresent(menuBtn);
    }, 3000);

    return menuBtn.isPresent().then(elementAvailable =>  {
        browser.wait(this.EC.elementToBeClickable(menuBtn), 3000).then(async () => {
            await menuBtn.click();
            const menuTitle = await element(by.xpath('.//*[contains(text(),\''  + title + '")]'));
            return browser.wait(this.EC.presenceOf(menuTitle), 3000).then(() => {
                return true;
            });
        });
    }).catch(error => {
        console.log('error: ', error);
    });

}

Upvotes: 2

Views: 2026

Answers (2)

Gunderson
Gunderson

Reputation: 3268

@yong is right in his comments, you are missing await in various places. It needs to be in front of all Protractor functions, including locators. You shouldn't need any sleeps either, but I'll leave them there for now:

async menuNav(title, pageElement) {
    await browser.sleep(3000);  <-- missed await here
    const menuBtn = await element(by.xpath('.//mat-icon[@id="' + pageElement + '"]')); <-- missed await here

    await menuBtn[0].click(); <-- this isn't an ElementArray, so not sure why [0] is there
    const  menuTitle = await element(by.xpath('.//*[contains(text(),\''  + title + '\')]')); <-- missed await here
    await browser.sleep(3000); <-- missed await here
    // no need to use .then() here either
    expect(await menuTitle.isPresent()).toBe(true);
}

Upvotes: 2

Simon N
Simon N

Reputation: 337

I think using async/await and promise chains don't really work too well together so that might cause you a few issues (although not the exact issue you're facing. Here's what I would try to resolve your problem. Try this:

    async menuNav(title, pageElement) {
const menuBtn = element(by.xpath('.//mat-icon[@id="' + pageElement + '"]'));
console.log('menu btn: ', menuBtn.locator());

await menuBtn.click();
const  menuTitle = element(by.xpath('.//*[contains(text(),\''  + title + '\')]'));
browser.sleep(3000);
menuTitle.isPresent().then(result => {
    expect(result).toBe(true);
    console.log('inside is present');
    console.log(title);
});

}

If there are multiple elements with id of item.id I would do this:

    async menuNav(title, pageElement) {
const menuBtn = element.all(by.xpath('.//mat-icon[@id="' + pageElement + '"]'));
console.log('menu btn: ', menuBtn.locator());

await menuBtn[0].click();
const  menuTitle = element(by.xpath('.//*[contains(text(),\''  + title + '\')]'));
browser.sleep(3000);
menuTitle.isPresent().then(result => {
    expect(result).toBe(true);
    console.log('inside is present');
    console.log(title);
});

Upvotes: 0

Related Questions