End to End testing of React apps with Nightwatch

Adam WardeckiAdam Wardecki

In the mid of 2015 our front-end team took the challenge of rebuilding the entire Dashboard from scratch.

Why we joined the dark side

In the mid of 2015 our front-end team took the challenge of rebuilding the entire Dashboard from scratch. In a matter of three months we built a new version using the React library. Since it was hard to keep up with writing unit tests at such demanding pace we decided that end-to-end (e2e) will be our go-to test strategy.

The most obvious choice for e2e tests is Selenium but there are many language bindings and frameworks to choose from. Eventually we settled on Nightwatch.js for a number of reasons:

In this post I’ll show you how to setup a simple Nightwatch project with using the Page Object Pattern. The finished code for this tutorial is in the part-1 folder of the syncano-testing-examples repository. You can grab the fully working example from there, or follow the tutorial steps to make it from scratch.


First thing you need to do is to install Node.js if you don’t yet have it. You can find the installation instructions on the Node.js project page. Once you have node installed, you can take advantage of it’s package manager called npm.

Go to your terminal, create an empty repository and cd into it. Next, type npm init. You can skip the steps of initialising package.json file by pressing enter several times and typing ‘yes’ at the end.

Once you have a package.json file, while in the same directory, type npm install nightwatch --save-dev. This will install the latest version of nightwatch into the node_modules directory inside your project and save it in your package.json file as a development dependency.

Next, in order to be able to run the tests, we need to download the Selenium standalone server. We could do this manually and take it from the projects’ website but lets use npm to handle this:

  "name": "syncano-testing-examples",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "e2e-setup": "node_modules/selenium-standalone/bin/selenium-standalone install"
  "author": "",
  "license": "ISC",
    "devDependencies": {
    "babel-cli": "^6.11.4",
    "babel-core": "^6.11.4",
    "babel-loader": "6.2.4",
    "babel-plugin-add-module-exports": "^0.2.1",
    "babel-preset-es2015": "^6.9.0",
    "nightwatch": "^0.9.5",
    "selenium-standalone": "5.1.1"

Now running npm e2e-setup will download the latest version of selenium server and chromedriver (which will be needed for running tests in Chrome browser)


Nightwatch relies on nightwatch.json as the configuration file for the test runs. It should be placed in projects root directory. It specifies various configuration settings like test environments (browsers, resolutions), test file paths and selenium-specific settings. This is how the configuration file can look like:

  "src_folders": ["tests"],
  "output_folder": "reports",
  "custom_commands_path": "",
  "custom_assertions_path": "",
  "page_objects_path": "pages",
  "globals_path": "globals",

  "selenium": {
    "start_process": true,
    "server_path": "./node_modules/selenium-standalone/.selenium/selenium-server/2.53.0-server.jar",
    "log_path": "./reports",
    "host": "",
    "port": 4444,
    "cli_args": {
      "webdriver.chrome.driver": "./node_modules/selenium-standalone/.selenium/chromedriver/2.21-x64-chromedriver"
  "test_settings": {
    "default": {
      "launch_url": "https://dashboard.syncano.io",
      "selenium_port": 4444,
      "selenium_host": "localhost",
      "silent": true,
      "desiredCapabilities": {
        "browserName": "chrome",
        "javascriptEnabled": true,
        "acceptSslCerts": true

I'll go through the important parts of the nightwatch.json file:

test_settings is an object where you specify the test environments. The important bit in the default environment is the desiredCapabilities object where we specify the chrome as the browserName so that Nightwatch will run the test against it.

Adding ECMAScript 6 to nightwatch

We are writing the Syncano Dashboard according to the ECMAScript 6 specs and we wanted to do the same for Nightwatch. In order to be able to do that, you'll have to add a nightwatch.conf.js file to the root of your project. The file should contain these couple of lines:


module.exports = require('./nightwatch.json');  

Bang! You can now write your tests in ECMAS 6

Edit: things have changed since I've written this article. Now you'll need to add es2015 preset in .babelrc config file and add add-module-exports plugin and do npm i babel-plugin-add-module-exports babel-preset-es2015 --save-dev. Everything should work after that. See the syncano-testing-examples repo for details

The Tests

Before we get to the test code there are only two things left to do:

(If you are on a windows machine than the command will be SET instead of export)

Test if a user can log in to the application

In the root of your project create a tests directory. Create a testLogin.js file and paste there this code:

export default {  
  'User Logs in': (client) => {
    const loginPage = client.page.loginPage();
    const instancesPage = client.page.instancesPage();

      .login(process.env.EMAIL, process.env.PASSWORD);

    instancesPage.expect.element([email protected]').text.to.contain('Your first instance.');


This is a test that is checking if a user is able to log in to the application. As you can see the code is simple:

The way to achieve this sort of clarity within a test, where the business logic is presented clearly and test can be easily understood even by non tech-saavy people is by introducing the Page Object pattern. loginPage and instancesPage objects contain all the methods and ui elements that are needed to make interactions within that page.

Log in Page Object

Page Objects files should be created in a pages folder. Create one in the root of your project. Next, create a loginPage.js file that will contain this code:

const loginCommands = {  
  login(email, pass) {
    return this
      .waitForElementVisible([email protected]')
      .setValue([email protected]', email)
      .setValue([email protected]', pass)
      .waitForElementVisible([email protected]')
      .click([email protected]')

export default {  
  url: 'https://dashboard.syncano.io/#/login',
  commands: [loginCommands],
  elements: {
    emailInput: {
      selector: 'input[type=text]'
    passInput: {
      selector: 'input[name=password]'
    loginButton: {
      selector: 'button[type=submit]'

The file contains an object loginCommands that stores a login method. The login method waits for an email input element to be visible, sets the values of email and password fields, waits for login button to be visible and finally clicks the button. We actually could write these steps in the "User Logs in" test. If we are planning to create a bigger test suite though then it makes sense to encapsulate that logic into a single method that can be reused in multiple test scenarios.

Apart from the loginCommands there's a second object defined below which is actually the Page Object that we instantiate in the testLogin.js file with this line:

const loginPage = client.page.loginPage();

as you can see the Page Object contains:

As you've probably noticed there's an @ prefix used before the locators both inside the test and in the loginCommands object. This tells Nightwatch that it should refer to the key declared in the elements property inside the Page Object.

Instances Page Object

Now let's create a second file in the pages folder that will be named instancesPage.js. It should contain the following code:

export default {  
  elements: {
    instancesListDescription: {
      selector: '//div[@class="description-field col-flex-1"]',
      locateStrategy: 'xpath'

It's a lot simpler than the loginPage file since it only has a single instancesListDescription element. What is interesting about this element is that it's not a CSS selector as the elements in the loginPage.js file but an XPath selector. You can use XPath selectors by adding a locateStrategy: xpath property to the desired element.

The instancesListDescription element is used in the 11 line of the loginPage.js file to assert if a login was successful.

    instancesPage.expect.element([email protected]').to.be.visible;

As you can see the assertion is verbose and readable because Nightwatch relies on Chai Expect library which allows for use of these BDD-style assertions.

Global configuration

There's one last piece of the puzzle missing in order to be able to run the tests. Nightwatch commands like waitForElementVisible() or the assertions require the timeout parameter to be passed along the element, so that the test throws an error when that timeout limit is reached. So normally the waitForElementVisible() method would look like this:

waitForElementVisible([email protected]', 3000)

similarly the assertion would also have to have the timeout specified:

instancesPage.expect.element([email protected]').to.be.visible.after(3000);

Where 3000 is the amount of milliseconds after which the test throws an element not visible exception. Fortunately we can move that value outside the test so that the code is cleaner. In order to do that create a globals.js file in the root of your project and paste there this code:

export default {  
  waitForConditionTimeout: 10000,

Now all the Nightwatch methods that require a timeout will have this global 10 second timeout specified as default. You can still define a special timeout for single calls if needed.

Running the test

That's it! The only thing left to do is to run the test. In the terminal, go to your projects' root directory (where the nightwatch.json file is in) and run this command:


With a bit of luck you should see a console output similar to this one:

Starting selenium server... started - PID:  13085

[Test Login] Test Suite

Running:  User Logs in  
 ✔ Element <input[type=text]> was visible after 87 milliseconds.
 ✔ Element <button[type=submit]> was visible after 43 milliseconds.
 ✔ Expected element <//div[@class="description-field col-flex-1"]> text to contain: "Your first instance." - condition was met in 2474ms

OK. 3 assertions passed. (10.437s)  

Well done! You've run your first Nightwatch test.


In this article you've learned how to:

This just the beginning in terms of what can be achieved with Nightwatch. If you'd like to learn more, see these posts:

If you have any questions or just want to say hi, drop me a line.

Build powerful apps in half the time

Use our serverless platform to set up your backend in minutes.

Learn more
Adam Wardecki

Adam Wardecki

Tester, Software Engineer and Product Owner @ Syncano