I'm trying to use async
/ await
with protractor
in TypeScript
. I'm following the example at: https://github.com/angular/protractor/tree/master/exampleTypescript/asyncAwait
It's working fine in my experiments. However, I have to use await
on every calls related to browser interactions.
For example:
I have a page object for a login page:
login.ts:
import {browser, element, by, By, $, $$, ExpectedConditions} from "protractor"; import { DashboardPage } from "./dashboard"; export class LoginPage { usernameInput = element(by.id("username")); passwordInput = element(by.id("password")); loginButton = element(by.id("login_button")); async get() { await browser.get(login_url); return this; } async getTitle() { let title = await browser.getTitle(); return title; } async typeUsername(username: string) { await this.usernameInput.sendKeys(username); } async typePassword(password: string) { await this.passwordInput.sendKeys(password); } async login() { await this.loginButton.click(); return new DashboardPage(); } }
LoginSpec.ts:
import {browser, element, by, By, $, $$, ExpectedConditions} from "protractor"; import { LoginPage } from "../pages/login"; describe("Login Page", function() { beforeEach(() => { // login page is not an angular page. browser.ignoreSynchronization = true; }); afterEach(() => { browser.ignoreSynchronization = false; }); it("should go to dashboard page after successfully login", async (): Promise<any> => { let loginPage = new LoginPage(); await loginPage.get(); await loginPage.typeUsername(username); await loginPage.typePassword(password); let dashboard = await loginPage.login(); expect(await dashboard.getTitle()).toEqual(`Dashboard`); }); });
In the above test spec, I have to use many await
for all the calls to browser interactions. That introduces a lot of boilerplate for await
.
The question is, is there any idea or way to reduce the boilerplate? Also, is this the right way to use async
/ await
with protractor
?
2 Answers
Answers 1
Unfortunately without all these awaits - your code won't be synchronized properly. There are couple of cases when you could omit pasting await - but most of the cases this is necessary, since awaits makes your promises schedule one by one.
Async/Await is still relying on Promises. So your await code works identically to this:
it("should go to dashboard page after successfully login", (): Promise<any> => { let loginPage = new LoginPage(); return loginPage.get().then(() => { return loginPage.typeUsername(username); }).then(() => { return loginPage.typePassword(password); }).then(()=> { return loginPage.login(); }).then((dashboard)=> { return expect(dashboard.getTitle()).toEqual(`Dashboard`); }) });
So consider awaits like a nice syntax sugar over promise chaining: https://github.com/SeleniumHQ/selenium/wiki/WebDriverJs#option-1-use-classic-promise-chaining
But there is a way to synchronize without putting awaits\thens\generators\callbacks . It called 'fibers'. Unfortunately protractor does not support this, but WebdriverIO uses it to synchronize code: https://www.facebook.com/ProtractorAngularJS/posts/1950761568515087
Answers 2
You can easily run these in sequence without using await
, but I recommend sticking with await for readability.
Here is how your code might look without await
.
import { LoginPage } from "../pages/login"; describe("Login Page", function() { // mutable crap it("should go to dashboard page after successfully login", () => { const loginPage = new LoginPage(); const asyncs = [ () => loginPage.get(), () => loginPage.typeUsername(username), () => loginPage.typePassword(password), () => loginPage.login(), (dashboard: Dashboard) => dashboard.getTitle() ]; const tilePromise = asyncs.reduce((promised, next) => promised.then(next)); // note the return. this is required return titlePromise.then(dashboardTitle => { expect(dashboardTitle).toEqual('Dashboard'); }); }); });
Again, I think await
is preferable from a readability point of view, but there is nothing in the language that mandates that you use it.
0 comments:
Post a Comment