Writing Tests

Using the preferred CSS selector model to locate elements on a page, Nightwatch makes it very easy to write automated End-to-End tests.

Create a separate folder for tests in your project, e.g.: tests. Each file inside it will be loaded as a test suite by the Nightwatch test runner. Here's a basic test suite example:

module.exports = {
  'Demo test Google' : function (browser) {
    browser
      .url('https://www.google.com')
      .waitForElementVisible('body')
      .setValue('input[type=text]', 'nightwatch')
      .waitForElementVisible('input[name=btnK]')
      .click('input[name=btnK]')
      .pause(1000)
      .assert.containsText('#main', 'Night Watch')
      .end();
  }
};

Remember always to call the .end() method when you want to close your test, in order for the browser session to be properly closed.

A test can have multiple steps, if needed:

module.exports = {
  'step one: navigate to google' : function (browser) {
    browser
      .url('https://www.google.com')
      .waitForElementVisible('body', 1000)
      .setValue('input[type=text]', 'nightwatch')
      .waitForElementVisible('input[name=btnK]', 1000)
  },

  'step two: click input' : function (browser) {
    browser
      .click('input[name=btnK]')
      .pause(1000)
      .assert.containsText('#main', 'Night Watch')
      .end();
  }
};

Tests could also be written in this format:

this.demoTestGoogle = function (browser) {
  browser
    .url('https://www.google.com')
    .waitForElementVisible('body', 1000)
    .setValue('input[type=text]', 'nightwatch')
    .waitForElementVisible('input[name=btnK]', 1000)
    .click('input[name=btnK]')
    .pause(1000)
    .assert.containsText('#main', 'The Night Watch')
    .end();
};

Using ES6 async/await [beta]

Starting with version v1.1 it is also possible to write tests as an ES6 async function. Simply by doing so, it will enable the API commands to return a promise and thus making it possible to use the await operator to retrieve the result, instead of the callback, as it is now.

This greatly improves the readability and ease of writing of tests. However, please note that it will no longer be possible to chain the API commands when using an async function.

The assert and expect APIs will still work as before v1.1 and they will support chaining, since the assertions do not return anything.
module.exports = {
  'demo test async': async function (browser) {
    // get the available window handles
    const result = await browser.windowHandles();
    console.log('result', result);

    // switch to the second window
    // await is not necessary here since we're not interested in the result
    browser.switchWindow(result.value[1]);
  }
};

Callbacks and async testcases

Callbacks can still be used as before and if the callback returns a Promise, the result of the promise will be the result of the command. See below for an example:

module.exports = {
  'demo test async': async function (browser) {
    // get the available window handles
    const value = await browser.windowHandles(function(result) {
      // we only want the value, not the entire result object
      return Promise.resolve(result.value);
    });

    console.log('value', value);

    // switch to the second window
    browser.switchWindow(value[1]);
  }
};

Using XPath selectors

Nightwatch supports xpath selectors also. To switch to xpath instead of css selectors as the locate strategy, in your test call the method useXpath(), as seen in the example below. To switch back to CSS, call useCss().

To always use xpath by default set the property "use_xpath": true in your test settings.

this.demoTestGoogle = function (browser) {
  browser
    .useXpath() // every selector now must be xpath
    .click("//tr[@data-recordid]/span[text()='Search Text']")
    .useCss() // we're back to CSS now
    .setValue('input[type=text]', 'nightwatch')
};

Expect Assertions

The Nightwatch API supports out of the box a BDD-style expect assertion library which greatly improves the flexibility as well as readability of the assertions.

The expect assertions use a subset of the Expect api from the Chai framework and are available for elements only at this point. Here's an example:

module.exports = {
  'Demo test Google' : function (browser) {
    browser
      .url('https://google.no')
      .pause(1000);

    // expect element <body> to be present in 1000ms
    browser.expect.element('body').to.be.present.before(1000);

    // expect element <#lst-ib> to have css property 'display'
    browser.expect.element('#lst-ib').to.have.css('display');

    // expect element <body> to have attribute 'class' which contains text 'vasq'
    browser.expect.element('body').to.have.attribute('class').which.contains('vasq');

    // expect element <#lst-ib> to be an input tag
    browser.expect.element('#lst-ib').to.be.an('input');

    // expect element <#lst-ib> to be visible
    browser.expect.element('#lst-ib').to.be.visible;

    browser.end();
  }
};


The expect interface provides a much more flexible and fluid language for defining assertions, significantly improved over the existing assert interface. The only downside is that it's not possible to chain assertions anymore and at this point custom messages aren't yet supported.


For a complete list of available expect assertions, refer to the API docs.

Using before[Each] and after[Each] hooks Suggest edits

Nightwatch provides the standard before/after and beforeEach/afterEach hooks to be used in the tests.

The before and after will run before and after the execution of the test suite respectively, while beforeEach and afterEach are ran before and after each testcase (test step).

All methods have the Nightwatch instance passed as argument.

Example:

module.exports = {
  before : function(browser) {
    console.log('Setting up...');
  },

  after : function(browser) {
    console.log('Closing down...');
  },

  beforeEach : function(browser) {

  },

  afterEach : function(browser) {

  },

  'step one' : function (browser) {
    browser
     // ...
  },

  'step two' : function (browser) {
    browser
    // ...
      .end();
  }
};

In the example above the sequence of method calls will be as follows: before(), beforeEach(), "step one", afterEach(), beforeEach(), "step two", afterEach(), after().

Asynchronous before[Each] and after[Each] Suggest edits

All the before[Each] and after[Each] methods can also perform asynchronous operations, in which case they will require the callback passed as the second argument.

The done function must be called as the last step when the async operation completes. Not calling it will result in a timeout error.

Example with beforeEach & afterEach:

module.exports = {
  beforeEach: function(browser, done) {
    // performing an async operation
    setTimeout(function() {
      // finished async duties
      done();
    }, 100);
  },

  afterEach: function(browser, done) {
    // performing an async operation
    setTimeout(function() {
      // finished async duties
      done();
    }, 200);
  }
};

Controlling the done invocation timeout

By default the done invocation timeout is set to 10 seconds (2 seconds for unit tests). In some cases this might not be sufficient and to avoid a timeout error, you can increase this timeout by defining an asyncHookTimeout property (in milliseconds) in your external globals file (see below for details on external globals).

For an example, refer to the provided globalsModule example.

Explicitly failing the test

Failing the test intentionally in a test hook can be achieved by calling done with an Error argument:

module.exports = {
  afterEach: function(browser, done) {
    // performing an async operation
    performAsync(function(err) {
      if (err) {
        done(err);
        return;
      }
      // ...
    });
  }
};

External Globals Suggest edits

Most of the time it's more useful to have your globals defined in an external file, specified in the globals_path property, instead of having them defined in nightwatch.json.

You can overwrite globals per environment as needed. Say you have your tests running locally and also against a remote staging server. Most of the times you will need some different setting up.

Global Hooks

The same set of hooks as per test suite is also available globally, outside the scope of the test. See the below example for more details. In the case of global hooks, the beforeEach and afterEach refers to a test suite (i.e. test file), and are ran before and after a test suite.

Global Settings

There are a number of globals which are holding test settings and can control test execution. These are detailed in the provided globalsModule sample.

Example:

module.exports = {
  'default' : {
    isLocal : true,
  },

  'integration' : {
    isLocal : false
  },

  // External before hook is ran at the beginning of the tests run, before creating the Selenium session
  before: function(done) {
    // run this only for the local-env
    if (this.isLocal) {
      // start the local server
      App.startServer(function() {
        // server listening
        done();
      });
    } else {
      done();
    }
  },

  // External after hook is ran at the very end of the tests run, after closing the Selenium session
  after: function(done) {
    // run this only for the local-env
    if (this.isLocal) {
      // start the local server
      App.stopServer(function() {
        // shutting down
        done();
      });
    } else {
      done();
    }
  },

  // This will be run before each test suite is started
  beforeEach: function(browser, done) {
    // getting the session info
    browser.status(function(result) {
      console.log(result.value);
      done();
    });
  },

  // This will be run after each test suite is finished
  afterEach: function(browser, done) {
    console.log(browser.currentTest);
    done();
  }
};

Global Reporter

The global reporter is invoked before calling the built-in junit reporter (or a custom reporter specified using the --reporter CLI option).

Example:

module.exports = {
  reporter : function(results, cb) {
    console.log('results', results);
    cb();
  }
};

Nightwatch Runner

Nightwatch includes a command-line test runner which makes it easy to run tests and generate useful output. Please refer to the Installation section for details on how to get the runner installed. There are a few different options on how to use the test runner, depending on your installation type.

Global

If you have installed Nightwatch globally (with -g option), the binary nightwatch will be available anywhere:

nightwatch [source] [options]

Project specific

If you have Nightwatch installed as a dependency of your project, you can refer the binary from the node_modules/.bin folder:


Linux and MacOSX:
./node_modules/.bin/nightwatch [source] [options]


Windows:

Create a file nightwatch.js and add the following line:

require('nightwatch/bin/runner.js');

Then run as follows:

node nightwatch.js [source] [options]

Tests source

The optional source argument can be either one or more files or an entire folder. This can be located irrespectively of the src_folders setting.


Example - single test:
nightwatch tests/one/firstTest.js


Example - 2 individual tests:
nightwatch tests/one/firstTest.js tests/secondTest.js


Example - 1 individual test and 1 folder:
nightwatch tests/one/test.js tests/utils

The test runner supports a number of run-time options to be passed to. To view all, run the following:

nightwatch --help
Name Shortname default description
--config -c ./nightwatch.json The location of the nightwatch.json file - the configuration file which the runner uses and which also includes the Selenium WebDriver options.
--output -o tests_output The location where the JUnit XML reports will be saved.
--reporter -r junit Name of a predefined reporter (e.g. junit) or path to a custom reporter file to use.

The custom reporter interface looks like:
module.exports = {
  write(results, options, done) {
    done();
  }
};
--env -e default Which testing environment to use - defined in nightwatch.json
--verbose Shows extended selenium command logging during the session
--version -v Shows the version number
--test -t Runs only the specified test suite/module. By default the runner will attempt to run all tests in the src_folders settings folder(s) and their subfolders.
--testcase Used only together with --test. Runs the specified testcase from the current suite/module.
--group -g Runs only the specified group or several (comma separated) of tests (subfolder). Tests are grouped by being placed in the same subfolder.
--skipgroup -s Skip one or several (comma separated) group of tests.
--filter -f Specify a filter (glob expression) as the file name format to use when loading the test files.
--tag -a Filter test modules by tags. Only tests that have the specified tags will be loaded.
--skiptags Skips tests that have the specified tag or tags (comma separated).
--retries Retries failed or errored testcases up to the specified number of times. Retrying a testcase will also retry the beforeEach and afterEach hooks, if any.
--suiteRetries Retries failed or errored testsuites (test modules) up to the specified number of times. Retrying a testsuite will also retry the before and after hooks (in addition to the global beforeEach and afterEach respectively), if any are defined on the testsuite.

You can define multiple sections (environments) of test settings so you could overwrite specific values per environment.

A "default" environment is required. All the other environments are inheriting from default and can overwrite settings as needed.

An environment inherits all the base settings and all the settings defined under the "default" environment.

{
  ...
  "test_settings" : {
    "default" : {
      "launch_url" : "http://localhost",
      "globals" : {
        "myGlobalVar" : "some value",
        "otherGlobal" : "some other value"
      }
    },

    "integration" : {
      "launch_url" : "http://staging.host",
      "globals" : {
        "myGlobalVar" : "other value"
      }
    }
  }
}

The key of the settings group can be passed then to the runner as the `--env` argument to use the specified settings, like so:
nightwatch --env integration

This can be useful if you need to have different settings for your local machine and the Continuous Integration server.

Nightwatch makes it possible to organize your test scripts into groups and run them as needed. To group tests together just place them in the same sub-folder. The folder name is the name of the group.

Example:

lib/
  ├── selenium-server-standalone.jar
custom-commands/
  ├── loginUser.js
  ├── attachPicture.js
tests/
  ├── logingroup
  |   ├── login_test.js
  |   └── otherlogin_test.js
  ├── addressbook
  |   ├── addressbook_test.js
  |   └── contact_test.js
  ├── chat
  |   ├── chatwindow_test.js
  |   ├── chatmessage_test.js
  |   └── otherchat_test.js
  └── smoketests
      ├── smoke_test.js
      └── othersmoke_test.js

To run only the smoketests group you would do the following:

nightwatch --group smoketests

Also, if you would want to skip running the smoketests group you would do the following:

nightwatch --skipgroup smoketests

To skip multiple groups, just add them as comma-separated:

nightwatch --skipgroup addressbook,chat

Test Tags Suggest edits

You can also selectively target tests to run based on tags, such that a test may be belong to multiple tags. For example, you might have a login test that belongs to a login suite as well as a sanity suite.

The tagging can be accomplished by adding the @tags property to a test module:

module.exports = {
  '@tags': ['login', 'sanity'],
  'demo login test': function (browser) {
     // test code
  }
};

To select which tags to run, use the --tag command line flag:

nightwatch --tag login


Specify multiple tags as:

nightwatch --tag login --tag something_else


To skip running tests with a specific tag, use the --skiptags flag:

nightwatch --skiptags login

Or to skip multiple tags, add each tag you want to skip as comma-separated:
nightwatch --skiptags login,something_else

To prevent a test module from running, simply set the disabled attribute in that module to true, like so:

module.exports = {
  '@disabled': true, // This will prevent the test module from running.

  'sample test': function (browser) {
    // test code
  }
};

This can be useful if you don't want to run certain tests that are known to be failing.

Disabling Individual testcases

Disabling individual testcases isn't currently supported out of the box. However it can be achieved relatively straightforward with a simple work-around. By simply converting the test method to a string, Nightwatch will ignore it.

Here's an example:

module.exports = {
  'sample test': function (browser) {
    // test code
  },

  // disabled
  'other sample test': ''+function (browser) {
    // test code
  }
};

Nightwatch supports running the tests in parallel in two main ways:

  • via test workers
  • by running multiple test environments in parallel

Via Workers

When this is enabled the test runner will launch a configurable number of child processes and then distribute the loaded tests over to be ran in parallel.

To enable test workers, set the test_workers top-level property, like so:

{
  "test_workers": {
    "enabled": true,
    "workers": "auto"
  }
}

or, simply:

{"test_workers": true}


The workers option configures how many child processes can run concurrently.

  • "auto" - determined by number of CPUs e.g. 4 CPUs means 4 workers
  • {number} - specifies an exact number of workers

Test concurrency is done at the file level. Each test file will fill a test worker slot. Individual tests/steps in a test file will not run concurrently.

Version 0.9 brings improved support for displaying output when running tests in parallel. We recommend setting detailed_output to false in your test settings for improved output readability.

Multiple Environments

nightwatch -e default,chrome

The above will run two environments named default and chrome in parallel.

Terminal Output

Each environment will be run as a separate child_process and the output will be sent to the main process.

To make the output easier to read, Nightwatch by default buffers the output from each child process and displays everything at the end, grouped by environment.

If you'd like to disable the output buffering and see the output from each child process as it is sent to stdout, simply set the property "live_output" : true on the top level in your nightwatch.json (e.g. after selenium).
You can create a separate environment per browser (by chaining desiredCapabilities) and then run them in parallel. In addition, using the filter and exclude options tests can be split per environment in order to be ran in parallel.

Starting with version 0.8 Nightwatch is bundled with a custom version of the popular Mocha test runner which allows running tests using Mocha, thus taking advantage of its interfaces and reporters.

Usage

There are two main ways in which you can use Mocha with Nightwatch.

From Nightwatch

Mocha is used as an alternative test runner to the built-in one. This is done by specifying the "test_runner" option in the nightwatch.json configuration file.

Custom options can also be specified for Mocha:


{
  ...
  "test_runner" : {
    "type" : "mocha",
    "options" : {
      "ui" : "bdd",
      "reporter" : "list"
    }
  }
  ...
}

or simply:

{
  "test_runner" : "mocha"
}

A complete list of Mocha options that are supported can be found here.

The test_runner option can also be specified at test environment level:

{
  "test_settings" : {
    "mocha_tests" : {
      "test_runner" : {
        "type" : "mocha",
        "options" : {
          "ui" : "bdd",
          "reporter" : "list"
        }
      }
    }
  }
  ...
}

Example

Writing a test in Mocha is the same as writing it in Nightwatch. Each testcase receives the browser object, hooks also receiving a done callback for async operations.

describe('Google demo test for Mocha', function() {

  describe('with Nightwatch', function() {

    before(function(browser, done) {
      done();
    });

    after(function(browser, done) {
      browser.end(function() {
        done();
      });
    });

    afterEach(function(browser, done) {
      done();
    });

    beforeEach(function(browser, done) {
      done();
    });

    it('uses BDD to run the Google simple test', function(browser) {
      browser
        .url('https://google.com')
        .expect.element('body').to.be.present.before(1000);

      browser.setValue('input[type=text]', ['nightwatch', browser.Keys.ENTER])
        .pause(1000)
        .assert.containsText('#main', 'Night Watch');
    });
  });
});

When using the mocha test runner from Nightwatch some cli options are not available, like --retries, --suiteRetries, --reporter.

Using the standard Mocha

Running Nightwatch tests with the standard Mocha it's also possible, though a bit more boilerplate code is involved and you need to manage the selenium server.

Example


var nightwatch = require('nightwatch');

describe('Github', function() {
  var client = nightwatch.initClient({
    silent : true
  });

  var browser = client.api();

  this.timeout(99999999);

  before(function() {

    browser.perform(function() {
      console.log('beforeAll')
    });

  });

  beforeEach(function(done) {
    browser.perform(function() {
      console.log('beforeEach')
    });

    client.start(done);
  });


  it('Demo test GitHub', function (done) {
    browser
      .url('https://github.com/nightwatchjs/nightwatch')
      .waitForElementVisible('body', 5000)
      .assert.title('nightwatchjs/nightwatch · GitHub')
      .waitForElementVisible('body', 1000)
      .assert.visible('.container .breadcrumb a span')
      .assert.containsText('.container .breadcrumb a span', 'nightwatch', 'Checking project title is set to nightwatch');

    client.start(done);
  });

  afterEach(function() {
    browser.perform(function() {
      console.log('afterEach')
    });
  });

  after(function(done) {
    browser.end(function() {
      console.log('afterAll')
    });

    client.start(done);
  });

});

Since v1.0, Nightwatch exports an API through which it may be used without a configuration file. The runTests() public method which may be used for running the tests inside a custom runner.

The return value is a promise.

Syntax


  Nightwatch.runTests([testSource], [settings]);

Example

const Nightwatch = require('nightwatch');

Nightwatch.runTests('/path/to/tests_folder', {
  // various settings
}).then(function() {
  // Tests finished
}).catch(function(err) {
  // An error occurred
});

You can also build your complete custom runner, based on the Nightwatch CLI runner:

Custom Runner Example

const Nightwatch = require('nightwatch');

// read the CLI arguments
Nightwatch.cli(function(argv) {
  argv.source = argv[''].slice(0);

  // create the Nightwatch CLI runner
  const runner = Nightwatch.CliRunner(argv);

  // setup and run tests
  runner
    .setup()
    .startWebDriver()
    .then( => runner.runTests())
    .then( => runner.stopWebDriver())
    .catch(err => console.error(err));
});

Writing Custom Commands

Most of the time you will need to extend the Nightwatch commands to suit your own application needs. Doing that is only a matter of creating a separate folder and defining your own commands inside, each in its own file.

Then specify the path to that folder in the nightwatch.json file, as the custom_commands_path property. The command name is the name of the file itself.

There are two main ways in which you can define a custom command:

1. Function-style commands

This is the simplest form in which commands are defined, however they are also quite limited.

The command module needs to export a command function, which needs to call at least one Nightwatch api method (such as .execute()). This is due to a limitation of how the asynchronous queueing system of commands works. You can also wrap everything in a .perform() call. Client commands like execute and perform are available via this.

module.exports.command = function(file, callback) {
  var self = this;
  var imageData;
  var fs = require('fs');

  try {
    var originalData = fs.readFileSync(file);
    var base64Image = new Buffer(originalData, 'binary').toString('base64');
    imageData = 'data:image/jpeg;base64,' + base64Image;
  } catch (err) {
    console.log(err);
    throw "Unable to open file: " + file;
  }

  this.execute(function(data) {
    // execute application specific code
    App.resizePicture(data);
    return true;
  },
  [imageData], // arguments array to be passed
  function(result) {
    if (typeof callback === "function") {
      callback.call(self, result);
    }
  });

  return this;
};

The example above defines a command (e.g. resizePicture.js) which loads an image file as data-URI and calls a method named resizePicture (via .execute()), defined inside the application.

With this command, the test will look something like:

module.exports = {
  "testing resize picture" : function (browser) {
    browser
      .url("http://app.host")
      .waitForElementVisible("body")
      .resizePicture("/var/www/pics/moon.jpg")
      .assert.element(".container .picture-large")
      .end();
  }
};

2. Class-style commands

This is how most of the Nightwatch's own commands are written. Your command module needs to export a class constructor with a command instance method representing the command function. Commands written like this should inherit from EventEmitter and manually signal the complete event, to indicate command completion.

Class-based command methods are run in the context (the value of this) of the class instance. The browser object is available as this.api.

The example below is the .pause() command, written as an ES6 class:

const EventEmitter = require('events');

class Pause extends EventEmitter {
  command(ms, cb) {
    // If we don't pass the milliseconds, the client will
    // be suspended indefinitely
    if (!ms) {
      return this;
    }

    setTimeout(() => {
      // if we have a callback, call it right before the complete event
      if (cb) {
        cb.call(this.api);
      }

      this.emit('complete');
    }, ms);

    return this;
  }
}

module.exports = Pause;


The "complete" event

Signaling the complete event needs to be done inside an asynchronous action (e.g. a setTimeout call). Command classes that do not extend EventEmitter will be treated similar to command functions, requiring that the command method calls at least one Nightwatch api method to be able to complete.

Nightwatch allows you to even define your own assertions, extending the available .assert and .verify namespaces.

Assertions implement a simple interface which is shared between built-in assertions and custom ones:


exports.assertion = function() {

  /**
   * The message which will be used in the test output and
   * inside the XML reports
   * @type {string}
   */
  this.message;

  /**
   * A value to perform the assertion on. If a function is
   * defined, its result will be used.
   * @type {function|*}
   */
  this.expected;

  /**
   * The method which performs the actual assertion. It is
   * called with the result of the value method as the argument.
   * @type {function}
   */
  this.pass = function(value) {

  };

  /**
   * The method which returns the value to be used on the
   * assertion. It is called with the result of the command's
   * callback as argument.
   * @type {function}
   */
  this.value = function(result) {

  };

  /**
   * Performs a protocol command/action and its result is
   * passed to the value method via the callback argument.
   * @type {function}
   */
  this.command = function(callback) {

    return this;
  };

};

Custom assertions also inherit from EventEmitter. To see some examples, check the assertions module on Github:
/nightwatch/tree/master/lib/selenium/assertions

If you'd like to define your own reporter in addition to the built-in ones (stdout and junit-xml) you can do so in two ways:

The --reporter command-line argument

Interface:

module.exports = {
  write : function(results, options, done) {
    done();
  }
};

The reporter method in your external globals file.

See the provided globalsModule.js for an example.

Example:


module.exports = {
  reporter : function(results, done) {
    console.log(results);
    done();
  }
};

Using Page Objects

The Page Objects methodology is a popular pattern to write end-to-end tests by wrapping the pages or page fragments of a web app into objects. The purpose of a page object is to allow a software client to do anything and see anything that a human can by abstracting away the underlying html actions needed to access and manipulate the page.

A comprehensive introduction to Page Objects can be found in this article.

As of version 1.0 Nightwatch does not support legacy page objects written prior to version 0.7.

Configuring Page Objects

To create a page object simply create an object with properties that describe the page. Each page object should be located in a separate file, located in a designated folder. Nightwatch reads the page objects from the folder (or folders) specified in the page_objects_path configuration property.

The page_objects_path property can also be an array of folders, allowing you thus to logically split the page objects into smaller groups.

The Url property

You can optionally add a url property that designates the page's URL. To navigate to the page, you can call the navigate method on the page object.

The URL will usually be defined as a string:


module.exports = {
  url: 'https://google.com',
  elements: {}
};

It can also be a function in case the URL is dynamic. One use case for this is to support different test environments. You can create a function that gets called in the context of the page, thus allowing you to do:


module.exports = {
  url: function() { 
    return this.api.launchUrl + '/login'; 
  },
  elements: {}
};

Most of the time, you will want to define elements on your page that your tests will interact with through commands and assertions. This is made simple using the elements property so that all your elements are defined in a single place. Especially in larger integration tests, using elements will go a long way to keep test code DRY.

Switching between css and xpath locate strategies is handled internally so you don't need to call useXpath and useCss in your tests. The default locateStrategy is css but you can also specify xpath:


module.exports = {
  elements: {
    searchBar: {
      selector: 'input[type=text]'
    },
    submit: {
      selector: '//[@name="q"]',
      locateStrategy: 'xpath'
    }
  }
};


Or if you're creating elements with the same locate strategy as is default, you can use the shorthand:


module.exports = {
  elements: {
    searchBar: 'input[type=text]'
  }
};


Using the elements property allows you to refer to the element by its name with an "@" prefix, rather than selector, when calling element commands and assertions (click, etc).

Optionally, you can define an array of objects:


var sharedElements = {
  mailLink: 'a[href*="mail.google.com"]'
};

module.exports = {
  elements: [
    sharedElements,
    { searchBar: 'input[type=text]' }
  ]
};


Putting elements and url together, say you have the following defined above saved as a google.js file:


module.exports = {
  url: 'https://google.com',
  elements: {
    searchBar: {
      selector: 'input[type=text]'
    },
    submit: {
      selector: '//[@name="q"]',
      locateStrategy: 'xpath'
    }
  }
};


In your tests you will use it as follows:


module.exports = {
  'Test': function (browser) {
    var google = browser.page.google();

    google.navigate()
      .assert.title('Google')
      .assert.visible('@searchBar')
      .setValue('@searchBar', 'nightwatch')
      .click('@submit');

    browser.end();
  }
};

Element Properties

To support greater flexibility in interacting with page object elements, an element can be specified as an object, which needs to contain at least the selector property. Next to the selector, other properties can be specified. Here's the full list:

  • selector - the element selector name (e.g.: '@searchBar')
  • locateStrategy - e.g. 'css selector'
  • index - used to target a specific element in a query that results in multiple elements returned. Normally, only the first element is used (index = 0) but using the index property, you can specify any element within the result.
  • abortOnFailure - used to overwrite this setting when using waitForElement* commands
  • timeout - used to overwrite the default timeout for when using waitForElement* commands or assertions
  • retryInterval - used to overwrite the default retry interval for when using waitForElement* commands or assertions
  • suppressNotFoundErrors - Some element commands like .click() or .getText() will throw a NoSuchElement error if the element cannot be located, causing the test to fail. If this option is set to true then this error is ignored.

Say in the example above, the searchBar element selector returns 3 elements and you are interested in the second element.


module.exports = {
  elements: {
    searchBar: {
      selector: 'input[type=text]',
      index: 2
    }
  }
};

You can also override what the page element defined by specifying the element as an object selectors in commands, which can also receive the index:


module.exports = {
  'Test': function (browser) {
    var google = browser.page.google();

    google
      .navigate()
      .assert.title('Google');

    google.waitForElementVisible('@searchBar') // 2nd input element
    google.waitForElementVisible({selector:'@searchBar', index:1}, function(result){}); // 1st div

    google.click('@submit');

    browser.end();
  }
};


Pseudo-selectors [beta]

When using named page object elements (starting with '@') you can also use CSS pseudo-selectors (starting with v1.1).


module.exports = {
  'Test': function (browser) {
    google.waitForElementVisible('@searchBar:first-child');

    google.waitForElementVisible('@searchBar:nth-child(1)');
  }
};

Sometimes it is useful to define sections of a page. Sections do 2 things:

  • Provide a level of namespacing under the page
  • Provide element-level nesting so that any element defined within a section is a descendant of its parent section in the DOM

You can create sections using the sections property:


module.exports = {
  sections: {
    menu: {
      selector: '#gb',
      elements: {
        mail: {
          selector: 'a[href="mail"]'
        },
        images: {
          selector: 'a[href="imghp"]'
        }
      }
    }
  }
};


Your tests would use it as follows:


module.exports = {
  'Test': function (browser) {
    var google = browser.page.google();
    google.expect.section('@menu').to.be.visible;

    var menuSection = google.section.menu;
    menuSection.expect.element('@mail').to.be.visible;
    menuSection.expect.element('@images').to.be.visible;

    menuSection.click('@mail');

    browser.end();
  }
};


Note that every command and assertion on a section (other than expect assertions) returns that section for chaining. If desired, you can nest sections under other sections for complex DOM structures.

Example of nesting page object sections:


module.exports = {
  sections: {
    menu: {
      selector: '#gb',
      elements: {
        mail: {
          selector: 'a[href="mail"]'
        },
        images: {
          selector: 'a[href="imghp"]'
        }
      },
      sections: {
        apps: {
          selector: 'div.gb_pc',
          elements: {
            myAccount: {
              selector: '#gb192'
            },
            googlePlus: {
              selector: '#gb119'
            }
          }
        }
      }
    }
  }
};


Using a nested section in your test is straightforward:


module.exports = {
  'Test': function (browser) {
    var google = browser.page.google();
    google.expect.section('@menu').to.be.visible;

    var menuSection = google.section.menu;
    var appSection = menuSection.section.apps;
    menuSection.click('@appSection');

    appSection.expect.element('@myAccount').to.be.visible;
    appSection.expect.element('@googlePlus').to.be.visible;

    browser.end();
  }
};

You can add commands to your page object using the commands property. This is a useful way to encapsulate logic about the page that would otherwise live in a test, or multiple tests.

Nightwatch will call the command on the context of the page or section. Client commands like pause are available via this.api. For chaining, each function should return the page object or section.

In this case, a command is used to encapsulate logic for clicking the submit button:


var googleCommands = {
  submit: function() {
    this.api.pause(1000);
    return this.waitForElementVisible('@submitButton', 1000)
      .click('@submitButton')
      .waitForElementNotPresent('@submitButton');
  }
};

module.exports = {
  commands: [googleCommands],
  elements: {
    searchBar: {
      selector: 'input[type=text]'
    },
    submitButton: {
      selector: 'input[name=btnK]'
    }
  }
};


Then the test is simply:


module.exports = {
  'Test': function (browser) {
    var google = browser.page.google();
    google.setValue('@searchBar', 'nightwatch')
      .submit();

    browser.end();
  }
};

Writing Unit Tests

Unit testing in Nightwatch has been refined in version 0.9. Unit tests now written in Nightwatch are also fully compatible with Mocha's Exports interface, so you can use either test runners.

Unit Tests Mode

Nightwatch automatically attempts to connect to the WebDriver server and create a session. When running unit tests this needs to be disabled and the runner needs to be made aware that it is operating in unit testing mode.

This can be done in two ways:

Setting unit_tests_mode=true

This is a global option. Set the unit_tests_mode option to true in the nightwatch.json:

{
  "src_folders" : ["tests"],

  "unit_tests_mode": true
}

Adding @unitTest property per test

You can set the @unitTest property to true if you'd like to have individual test suites as unit tests.

const assert = require('assert');

module.exports = {
  '@unitTest' : true,

  'demo UnitTest' : function (done) {
    assert.equal('TEST', 'TEST');
    setTimeout(function() {
      done();
    }, 10);
  }
};

Assertion framework

Starting with 0.9, in the improved support for unit tests, the browser object is no longer passed as an argument to the test. The only argument passed now is the done callback to be used for asynchronous tests.

You can use whatever assertion framework you like. Chai.js is quite a good one and very flexible.

Example

Here's a subset of the unit test for the utils.js Nightwatch module:

var assert = require('assert');
var common = require('../../common.js');
var Utils = common.require('util/utils.js');

module.exports = {
  'test Utils' : {
    testFormatElapsedTime : function() {

      var resultMs = Utils.formatElapsedTime(999);
      assert.equal(resultMs, '999ms');

      var resultSec = Utils.formatElapsedTime(1999);
      assert.equal(resultSec, '1.999s');

      var resultMin = Utils.formatElapsedTime(122299, true);
      assert.equal(resultMin, '2m 2s / 122299ms');
    },

    testMakeFnAsync : function() {
      function asyncFn(cb) {
        cb();
      }

      function syncFn() {}

      var convertedFn = Utils.makeFnAsync(1, syncFn);
      var called = false;
      convertedFn(function() {
        called = true;
      });

      assert.equal(Utils.makeFnAsync(1, asyncFn), asyncFn);
      assert.ok(called);
    }
  }
};

Asynchronous Unit Tests

The argument to the test function is the optional done callback which signals the test is complete. If present, the callback must be called when the async operation finishes.

Example

Here's unit test which checks if Nightwatch throws an error if you don't invoke the done callback within a set time (10 ms).


module.exports = {
  const assert = require('assert');

  module.exports = {
    'demo UnitTest' : function (done) {
      assert.equal('TEST', 'TEST');

      setTimeout(function() {
done(); }, 10); } }; };

Using a Combined Configuration

Below it's an example of how you can combine end-to-end tests and unit tests in the same nightwatch.json configuration file. Notice the usage of exclude and filter properties.

An empty exclude means we want to reset its value and rely only on filter.

{
  "src_folders" : ["./examples/tests", "./examples/unittests"],
  "output_folder" : "./examples/reports",


  "webdriver" : {
    "start_process": true,
    "server_path": "node_modules/.bin/chromedriver",
    "port": 9515
  },

  "test_settings" : {
    "default" : {
      "launch_url" : "http://localhost",
      "desiredCapabilities": {
        "browserName": "chrome"
      },
      "exclude" : "./examples/unittests/"
    },

    "unittests" : {
      "unit_tests_mode" : true,
      "filter" : "./examples/unittests/",
      "exclude" : ""
    }
  }
}

Code Coverage

At the moment, Nightwatch doesn't provide a coverage reporter but it is something that's being planned for a future release. In the meantime you can write a custom reporter which will output coverage data. See the custom reporter section for details and the Mocha HTMLCov reporter for how the reporter should look like.

3rd party coverage service

There are some hosted services which provide the reporting and metrics for you in a modern web interface. These services will typically require coverage data in LCOV format. Nightwatch uses coveralls.io.

For details on how an LCOV reporter should look like and how to integrate with your project, you can check out the mocha-lcov-reporter.