Monday, August 21, 2017

async / await boilerplate in protractor

Leave a Comment

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.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment