I'm doing an end-to-end testing using protractor on an angular 2 project and i'm required to test a login operation. The problem here is that when login button is clicked the expect statement still gets the login page's URL as supposed to home page. If I wait, then the test succeeds. I have read somewhere in the documentation that protractor waits for processes to complete automatically, so,
wait()
method?fakeAsync
, async
or atleast whenStable
like in unit
test?Here's my test case
it("User successfully login",()=>{
browser.get(browser.baseUrl + '/account/login').then(() => {
return expect(browser.getCurrentUrl()).toBe(browser.baseUrl + "/account/login")
}).then(()=>{
element(by.tagName('input[type=text]')).sendKeys('username');
element(by.tagName('input[type=password]')).sendKeys('password');
element(by.css('button[type=submit]')).click().then(()=>{
return expect(browser.getCurrentUrl()).toBe(browser.baseUrl + "/home");
});
});
});
Note:
browser.wait()
method. I sort of solved this problem. I don't know if it works for other scenarios or if it's the correct approach. I added
browser.wait(EC.urlContains(browser.baseUrl + "/account/login"), 10000);
browser.waitForAngularEnabled(false);
browser.wait(EC.urlContains(browser.baseUrl + "/home");
just after click() event and it seems to work for now.
What causes the error:
click()
returns a promise, which protractor waits to be resolved. It is probably resolved the moment, when the login-request got sent to the server and not when the homepage is loaded. Therefore the execution of the next step starts before the page is loaded.
Why not wait()
:
I'm not sure, on what base you got the instruction not to use browser.wait()
. Most probably you shouldn't use browser.sleep()
(because that command is always obsolete), but if you don't want to use browser.wait()
your developers may not once leave the initially opened Angular Page.
fakeAsync
, whenStable
and more:
Protractor uses whenStable
by default within browser.waitForAngular()
, which is always called after a promise and as long as you didn't switch off browser.waitForAngularEnabled(false)
... which you only need to switch off for non-angular pages or if your developers use i.e. long lasting Macrotasks, that would lead to timeouts.
Further because the Selenium Control Flow will get deprecated in probably November 2018 (read about here), there is async/await
available.
Read this important guide here and also some further infos from protractor about it here
Now in your case:
You don't need to use then()
s in your case, as you don't need to resolve the promise immediately. expect()
will automatically resolve your promise, so there is no need for a then()
there.
In fact Protractor executes all commands line by line as if they were all wrapped within then()
s, so it automatically keeps your execution synchronous (as long as you don't start an async task by using then()
in the code.
So here my suggestion for your code:
it("User successfully login",()=>{
var EC = protractor.ExpectedConditions; //or within onPrepare() of conf.js put global.EC = protractor.ExpectedConditions ... to make it everywhere available.
browser.get(browser.baseUrl + '/account/login');
//next line lets protractor wait max 5 seconds for your url to become, what you want.
browser.wait(EC.urlContains(browser.baseUrl + "/account/login"), 5000);
//additionally after the URL appeared, use this command to wait until the page is fully loaded.
browser.waitForAngular();
expect(browser.getCurrentUrl()).toBe(browser.baseUrl + "/account/login");
element(by.tagName('input[type=text]')).sendKeys('username');
element(by.tagName('input[type=password]')).sendKeys('password');
element(by.css('button[type=submit]')).click();
//next line lets protractor wait max 5 seconds for your url to become, what you want.
//as here a new page is loaded and the click()-promise is resolved before, use this:
browser.wait(EC.urlContains(browser.baseUrl + "/account/login"), 5000);
//additionally after the URL appeared, use this command to wait until the page is fully loaded.
browser.waitForAngular();
expect(browser.getCurrentUrl()).toBe(browser.baseUrl + "/home");
});
If you get now timeouts, then that is a separate issue to debug. Check this Protractor site for infos about timeouts and check this SO-Answer here for how to debug timeouts.