Using TypeScript Async/Await With Selenium NodeJS Bindings

In previous post I described how I used TypeScript with Babel to get my JavaScript Selenium tests to be executed by Node.

I wanted to dedicate this separate post to an example of the Selenium code that I wrote, because there are many tricks and gotchas that need your full attention.

That’s mainly around the use of async/await of course. Instead of write a paragraph about each piece of the code, I have embedded my points in the code itself.

The sample simply goes to the Angular2 Github repository, and reports the title and date of the first page of open issues in the repository.

<code>// Following `reference` comments allow Node & WebDriver autocomplete.
// This is needed as both are not written in TypeScript.
// You can download them via TypeScript Definitions package manager:
//      npm install tsd -g
//      tsd install node
//      tsd install selenium-webdriver

/// <reference path="typings/node/node.d.ts" />
/// <reference path="typings/selenium-webdriver/selenium-webdriver.d.ts" />

// If you are not using babel-runtime (see prev post), 
//      you can try this instead. You'll still need babel processing.
// This worked in atom-typescript, didn't work in gulp run.
// Not good to have to write it in every file anyway
require("babel/polyfill");

// The selenium-webdriver offers multiple exports
// Each of them works like namespace
// To use, you need:
//      npm install selenium-webdriver
import * as webdriver from "selenium-webdriver";
import * as chrome from "selenium-webdriver/chrome";

// This is optional good way to manage chromedriver binaries
//      npm install selenium-binaries
// You see I can mix `import` and `require` in same file!
var chromeDriverPath =
    require('selenium-binaries').chromedriver;
process.env.CHROME_DRIVER_PATH = chromeDriverPath;

// Shorten the names we'll use a lot
var By = webdriver.By,
    until = webdriver.until;

// Configure WebDriver. Nothing async yet.
// Could also move into next function no problem.
var driver = new webdriver.Builder()
    .withCapabilities(webdriver.Capabilities.chrome())
    .build();

// You can only use `await` inside an `async` function.
// Hence the IIFE (immediately-invoked function expression) wrapping
(async function() {

    // Some code to ensure browser is closed if I get an error
    // That's trial and error with Selenium, and googling...
    await webdriver.promise.controlFlow().on('uncaughtException',
        async function(e) {
            await driver.quit();
        });
    await process.on('SIGINT', async function() {
        try {
            process.exit();
        } finally {
            await driver.quit();
        }
    });

    // I'll store the results here then console log them.
    // I could log them directly, at 1st I didn't know how
    //      and now I'm keeping it to show more cool tips 
    var results = [];

    try {

        // Most WebDriver functions return special promises 
        //      that allow you to call the next function fluently:
        //      `driver.get(...).findElements(...)`
        // But that's not cool for debugging which part failed
        //      & doesn't work with loops, etc.
        // So, I `await` most of the `promise`s.
        await driver.get("https://github.com/angular/angular/issues");

        var issues = await driver
            .findElements(By.css(".table-list-cell.issue-title"));

        // This is worth its own post:
        // When I use `async` function in `map` (to use `await` in it),
        //      I get an array of promises, not results
        // So, I use Promise.all to
        //      get the results in one promise I can `await`
        results = await Promise.all(issues.map(async function(issue) {

            // Could have used `await` here or did it fluently
            // Just showing different styles.
            var titleContainer = issue
                .findElement(By.css(".issue-title-link"));
            var title = await titleContainer.getText();

            var dateContainer = issue.
                findElement(By.tagName("time"));
            var date = await dateContainer.getAttribute("title");

            return { title: title, date: date };
        }));

        // console doesn't get flushed inside `async` functions
        // Surprisingly, I can `await` it to work!
        await console.log(results);

    } finally {
        await driver.quit();
    }

} ()); // As mentioned above, the function executes itself
</code>

Prerequisites

In case you wonder, here are the packages used above, in a single NPM install command

<code>npm install selenium-webdriver babel selenium-binaries
</code>

This doesn’t involve the build configuration to get this compiled and running.

Check out my other post to learn how to setup the build for this.

Finally, I hope those two articles combined help you write your own asynchronous code with ease, with Selenium or any other purpose.

Share With Friends:

How did I learn that?

As a bonus for coming here, I'm giving away a free newsletter for web developers that you can sign up for from here.

It's not an anything-and-everything link list. It's thoughtfully collected picks of articles and tools, that focus on Angular 2+, ASP.NET (4.x/MVC5 and Core), and other fullstack developer goodies.

Take it for a test ride, and you may unsubscribe any time.

You might also want to support me by checking these out [Thanks]: