Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Protractor not finding element

I'm having trouble getting protractor to behave as expected when testing my angular app. My spec file looks like this:

describe('app login', function() {
   it('should allow admin user to log in', function() {
       browser.get('http://localhost:3008');

    //we can find the log in link
    expect(element(by.id('login-link')).getText()).toContain('Log in');

    //open login dialog
    element(by.id('login-link')).click();
    browser.ignoreSynchronization = true;
    browser.sleep(1000);

    //enter credentials
    element(by.id('login-username')).sendKeys('User1');
    element(by.id('login-password')).sendKeys('Password1');
    browser.sleep(1000);

    //log in
    var el = element(by.id('login-btn'));
    //WORKS IF BELOW LINE IS COMMENTED OUT 
    el.click();
    browser.sleep(1000);

    //display confirms login
    expect(element(by.id('user-display')).getText()).toContain('User1');

  });
});

Note that I was getting synchronization errors at the start which is why I have the ignoreSynchronization flag set to true and all those browser.sleeps.

Now here's the thing: The test will go through fine if I remove the el.click() statement (and the final expect call). However, once that line is included, I get NoSuchElementError: No element found using locator: By.id("login-username"). Notice that this element is not the one I am actually trying to click, which is part of the bizarreness.

like image 977
see sharper Avatar asked Aug 11 '15 05:08

see sharper


1 Answers

let me start by breaking down your question in 3 parts. Let's take it a step at a time:
1)ignoreSynchronization flag set to true: It instructs protractor to not wait for Angular promises. This is used when you are i)testing scenarios around waiting for $http or $timeout to resolve ii)testing against a non-angular site. These are the times when you should set ignoreSynchronization setting to true. Ask yourself is it needed for your test pre-requisite? Going by your explanation above, I believe you can set it to false and continue your testing safely. By setting this to true when it is not needed you are instructing protractor to not attempt to synchronize with the page before performing actions. This can be harmful because Protractor will not wait until $timeouts and $http calls have been processed, which can cause tests to become flaky. more on: https://github.com/angular/protractor/blob/9891d430aff477c5feb80ae01b48356866820132/lib/protractor.js#L158


You can do away with unnecessary browser.sleep() once you set this flag to False.

2)Protractor not finding element: There are various reasons for which an element is not detected by Protractor:
-Element does not fit it the display window.
-Element is not visible, but present.
-Asynchronous task not handled properly.
-Another element is overlayed on the other element

I suggest you try out:

  1. Handling all your asynchronous event using either promises, async/await or callbacks.
describe('app login', function() {
  it('should allow admin user to log in', async() => {
    await browser.get('http://localhost:3008');

    //we can find the log in link
    expect(await element(by.id('login-link')).getText()).toContain('Log in');

    //open login dialog
    await element(by.id('login-link')).click();
    //try putting this setting in your protractor.conf file instead of describe 
    //block
    //browser.ignoreSynchronization = false;
    
    //enter credentials
    await element(by.id('login-username')).sendKeys('User1');
    await element(by.id('login-password')).sendKeys('Password1');

    //log in
    var el = element(by.id('login-btn'));
    //WORKS IF BELOW LINE IS COMMENTED OUT 
    await el.click();

    //display confirms login
    expect(await element(by.id('user-display')).getText()).toContain('User1');

    });
});

More info on how to change your code: https://jasmine.github.io/tutorials/async



3)Excessive use of browser.sleep(1000);: In your code, using sleep() renders it fragile and will sometimes fail if the system runs slower than normal. It is an invitation to making your test fail flaky, often in non-reproducible scenarios. So unless you want to spend your time in futile debugging, stop using it.

Instead use these wait strategies: explicit waits, waitforAngular; to make sure your asynchronous events are completed before going to the next step. Incase you need to know more on this, leave a comment below, otherwise I am keeping this answer strictly to the question you have asked. Ask away!

like image 131
Puja Bhattacharya Avatar answered Sep 29 '22 12:09

Puja Bhattacharya