2
votes

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);
    });

}
2
would need to see the html of the page to help. Using protractor means you probably shouldn't have to do any waiting as it takes care of all that for you. I presume your locator for the element is incorrect. - Simon N
Thank you @Simon . This there is an array of divs with mat-icons nested inside them. and i am trying to get that mat-icon with the id of which is [id]="item.id" - Divya
For a start you are finding one element (menuBtn) and then trying to click menuBtn[0]. You haven't got an array of elements. If you want all elements you need elements.all. I will add code I think should work as an answer - Simon N
An array came out in the browser console so I added it that way. But later I replaced the xpath with simple id which is const menuBtn = element(by.id(pageElement)); and I still get same problem. - Divya
Dont you need to await on your locators as well? await element(by.xpath('.//mat-icon[@id="' + pageElement + '"]')); - Gunderson

2 Answers

2
votes

@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);
}
0
votes

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);
});