fbpx
Uncategorized

A Beginner’s Guide To Test Automation With Javascript (Nightwatch.js). Part 2.

How to write a script in Nightwatch.js

Welcome to the “A beginners guide to test automation with Javascript(Nightwatch.js)” blog series part 2! If you have missed out on the first part, you can read it here.

In this article we will look into the following and as always – feel free to skip to any part you are the most interested in:

Code used in this article can be found in Loadero’s public GitHub examples repository here.

Prerequisites

The most useful Nightwatch.js commands

Nightwatch.js framework has tons of functions at your disposal that are really well-documented (check its documentation for yourself). These functions range from basic user interactions such as clicks and inputs to more sophisticated ones such as changing the browser window’s size or setting cookies. They all come in handy once in a while but there are some that will be used pretty much all the time. Let’s take a look at them, shall we?

.url()

As you might have already noticed, this tiny function is usually at the beginning of any script. The reason for that is simple – it opens the desired website and, without calling it, you wouldn’t be able to automate other actions. 

I might add that this function has a second use. It can retrieve the current website’s URL via a callback (check out the example below). To learn more about .url(), look into Nightwatch.js documentation here.

// Example usage of .url()

// Opens specified website
client.url('https://google.com');

// Retrieves current website’s URL
client.url(({ value }) => console.log(value)); // => https://google.com

P.S. What exactly that ({ value }) means you will learn in part 4 of this blog series but at the moment you can check MDN docs on object destructuring if you want to learn more.

.waitForElement…()

Even though in the previous part .waitForElementVisible() has been looked at, there is more to this command. First of all, .waitForElementVisible() is not the only command that waits until the element is at some state because visible is not the only possible element’s state. An HTML element can have any of the following states:

  • Present – element is present in HTML DOM.
  • Visible – element is visible for the end user. If you want to look into what defines the element being visible, we recommend checking out WebDriver’s documentation on element displayedness.

Nightwatch.js enables you to wait until the element is (not) present or visible using any of the following functions:

  • .waitForElementVisible()
  • .waitForElementNotVisible()
  • .waitForElementPresent()
  • .waitForElementNotPresent()

Each function must have only the element selector (uses CSS selectors by default) passed as an argument. All other arguments are optional (you can check available parameters, for example, for .waitForElementVisible() function here)  but we really recommend explicitly passing timeout, which by default is 5 seconds as per default configuration. This parameter defines the time after which the function should fail if the element fails to meet the expected state. For example, if you use .waitForElementVisible('some.selector', 10 * 1000) and the element is not visible within 10 seconds, the test stops its execution because the assertion failed.

This function is usually used to validate whether the element has reached the specified state. For example, once the page is opened, it is recommended that it is checked if the main container is loaded before interacting with the further elements, that way you make sure that the website is actually loaded. Another use case is when an element is checked to be visible before clicking on it.

// Example usage of .waitForElementVisible()

// Without timeout argument (by default it is 5 seconds)
client.waitForElementVisible('.main-container');

// With timeout argument
client.waitForElementVisible('.main-container', 10 * 1000);

.click()

This function is one of the most simple functions in Nightwatch.js. You only have to pass the element’s selector you want to click on. In general, we recommend calling .waitForElementVisible() beforehand. Yes, exactly ...Visible. That way you assure that the element is actually visible, and, most importantly, interactable so the click command successfully executes. To learn more about this command check out its documentation here.

// Example usage of .click()
client
    .waitForElementVisible('.some-element')
    .click('.some-element');

Tip: Websites often don’t correctly update the element currently in focus. For instance, when clicking on the submit button, the form isn’t submitted. This usually happens because the input form was focused and this click only removed the focus from it and didn’t change the focus to the clicked button. In such cases, the element, i.e., the button, has to be clicked twice, otherwise, the desired functionality will not be triggered. Before double-clicking all elements, check if that’s your case.

.setValue()

Usually, users have to enter some text themselves, be it a search input box, registration form or just some modal with input fields. This function has 2 required fields: a selector and input value. To learn more about this command, check out Nightwatch documentation for it here.

// Example usage of .setValue()

// Enters “john.doe@example.com” into the field and sends ENTER keypress
client
    .setValue('.trial input', 'john.doe@example.com')
    .setValue('.trial input', client.Keys.ENTER);

Tip: client.Keys is a map consisting of various UTF-8 characters that are usually used for imitating user keypresses, e.g., ESCAPE or ENTER. Most of the WebDriver specified keys are implemented in Nightwatch.js and can be used from the list here.

.pause()

The .pause() function does literally what it claims to do – it suspends the script execution for the specified time.

In the previous blog post we have looked at pauses only as a means for manually validating the script’s execution. That is the most common use case for pauses.

Important: Using JavaScript’s setTimeout() will produce unexpected  and inconsistent results due to Nightwatch.js command queue which will be explained in the next part.

Another use for this command is to generate data. Yes, stopping script execution does not necessarily mean not doing anything. For instance, when having a video and audio call with multiple participants using WebRTC protocol, the end user is not actively navigating the website but rather providing input for the camera and microphone. This can be easily simulated by having a pause in the script (hence not navigating the website) and providing a fake audio and video input. During that pause, the participant will continue having a call and will be generating various WebRTC statistics.

Important: When testing WebRTC solutions, always add a pause, for at least 1 minute, to gather the necessary metrics to analyze them later. This data will be collected in WebRTC internals dump that has many metrics that can help understanding potential problems for the application under test. Check out this article to learn more about WebRTC solution automated testing.

The only argument that has to be passed for .pause() is the pause time in milliseconds. More info about .pause() can be found here.

// Example usage of .pause()
client.pause(5 * 1000);

.saveScreenshot() / .takeScreenshot()

Either you use the original Nightwatch.js command .saveScreenshot() or Loadero’s custom command .takeScreenshot(), they essentially do the same thing – take a screenshot of the current view. 

The difference is that by using .takeScreenshot() in the Loadero script, the screenshot will be available in the test run artifacts. Also, .takeScreenshot() allows passing exitOnFail parameter, which will stop test execution if an error occurs during the command’s execution. To learn more about this custom command, check out its documentation here.

// example usage of .takeScreenshot()
client.takeScreenshot('screenshot.png');

.perform()

To be frank, this is one of the most confusing commands in the whole Nightwatch.js framework but bear with me – it will make sense. This function allows passing a callback function as an argument that will be executed before calling the next Nightwatch.js function. .perform() documentation can be found here. What’s more, this callback function has 3 distinct flavors, a.k.a. options:

1) No parameters – only a callback function has to be passed. It is run right away without waiting for its execution to end before calling the next Nightwatch.js command. This comes in handy when you have to work with the command queue which will be looked at in the next part of this series.

// example usage of .perform(() => {})
client.perform(() => someCallbackFunction());

2) One parameter (done) – allows asynchronous execution of the callback by providing a done() callback function to indicate that the callback has finished running. This usually is used for executing functions that must be executed before proceeding, e.g., retrieve data from some API endpoint or establish a connection with the database. Because Nightwatch.js won’t wait until the callback function has finished its execution before calling the next command, done() function has to be called to indicate the end of the execution. This behavior is similar to JavaScript’s Promise resolve()/reject().

// Example usage of .perform(done => {})
client
    .perform(done => {
        retrieveDataFromDatabase();

        done();
    })

Important: Nightwatch.js has a default internal timeout when done() is used. If the function doesn’t complete within 10 seconds, then the script fails. To avoid such inconvenience, Loadero created .performTimed() custom command (check it out here) that allows overriding default timeout and works just like .perform().

3) Two parameters (api , done) – allows asynchronous execution with Nightwatch API object passed in as the first argument (this object is the same as client we have used so far) and done callback function as the second argument. This is rarely used for regular web UI automation. This api object is mostly useful when creating custom commands but we will not look into those since that is out of this article’s scope.

// Example usage of .perform((api, done) => {})
client.perform((api, done) => {
    api.waitForElementVisible('.someElement', 10 * 1000);

    someFunction();

    done();
});

The final script

All these Nightwatch.js commands can be put to use in the following scenario:

  1. Open Github homepage and wait for search bar (.search-input-container) to load.
  2. Click on the search bar and type in “Nightwatch”.
  3. Click on the first search result (.ActionListItem).
  4. Assert that first result contains “nightwatch” (a .search-match).
  5. Click on the first result (.search-title a).
  6. Pause the script for 5 seconds for the page to load fully using .pause().
  7. Save a screenshot of the page.
  8. Log in console that the script has finished its execution using .perform(done => {}).
module.exports = {
    test: client => {
        client
            .url('https://github.com/')
            .waitForElementVisible('.search-input-container', 10 * 1000)
            .click('.search-input-container')
            .setValue('.FormControl-input', 'Nightwatch')
            .click('.ActionListItem')
            .assert.textContains('a .search-match', 'nightwatch')
            .click('.search-title a')
            .pause(5 * 1000)
            .saveScreenshot('nightwatch.png')
            .perform(done => {
                console.log('The script has finished its execution');
                done();
            });
    }
};

The code for this final script can be found in Loadero’s public GitHub repository here.

Summary

Today you’ve learned the most common Nightwatch.js commands and various uses for each of them. By knowing that almost every command allows passing a callback as a parameter, now you know how to handle Nightwatch.js responses inside the callback itself.

In case you have any questions or want to learn more Nightwatch.js commands, we recommend checking out Nightwatch.js official documentation and their GitHub page. Don’t forget to apply to Loadero‘s free trial to run your code on a cloud platform in multiple locations with various configurations.