Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

async / await boilerplate in protractor

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?

like image 762
dzhg Avatar asked Nov 16 '16 23:11

dzhg


2 Answers

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

like image 186
Xotabu4 Avatar answered Nov 11 '22 22:11

Xotabu4


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.

like image 31
Aluan Haddad Avatar answered Nov 11 '22 23:11

Aluan Haddad