Writing Tests

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

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.

Basic Example

Here's a basic test suite example which opens the search engine Ecosia.org, searches for the term "nightwatch", then verifies if the term first result is the Nightwatch.js website.

module.exports = {
  'Demo test ecosia.org' : function (browser) {
    browser
      .url('https://www.ecosia.org/')
      .waitForElementVisible('body')
      .assert.titleContains('Ecosia')
      .assert.visible('input[type=search]')
      .setValue('input[type=search]', 'nightwatch')
      .assert.visible('button[type=submit]')
      .click('button[type=submit]')
      .assert.containsText('.mainline-results', 'Nightwatch.js')
      .end();
  }
};

A test can have multiple steps, if needed:

module.exports = {
  'step one: navigate to ecosia.org': function (browser) {
    browser
      .url('https://www.ecosia.org')
      .waitForElementVisible('body')
      .assert.titleContains('Ecosia')
      .assert.visible('input[type=search]')
      .setValue('input[type=search]', 'nightwatch')
      .assert.visible('button[type=submit]');
  },

  'step two: click submit' : function (browser) {
    browser
      .click('button[type=submit]')
      .assert.containsText('.mainline-results', 'Nightwatch.js')
      .end();
  }
};

Finding & Interacting with Elements

Finding elements on a page is by far one of the most common functions during an end-to-end test. Nightwatch provides several techniques of locating elements and also an extensible assertion framework to perform verifications on them.

Elements are searched for from the document root, using either a CSS selector or an Xpath selector. It is possible to use other locator strategies as well, refer to the Webdriver docs for details.

Elements are internally identified using a web element reference id. When interacting with elements, this step happens behind the scenes and Nightwatch has automatic retry mechanisms for locating the element before interacting with it or performing assertions.

Implicit Waits
When interacting with elements, Nightwatch polls the DOM for a configurable duration when trying to find any element. If the element is not found, a NoSuchElementError error is thrown.

In the example above, the command setValue is first performing internally the element lookup and then calling the element set value command. The test can be simplified like so:

module.exports = {
  'Demo test ecosia.org': function (browser) {
    browser
      .url('https://www.ecosia.org/')
      .setValue('input[type=search]', 'nightwatch')
      .click('button[type=submit]')
      .assert.containsText('.mainline-results', 'Nightwatch.js')
      .end();
  }
};

For more info on element related commands, see the API Reference pages.

Writing Assertions

Assertions are what determines if the test passes or not. Assertions are different than commands in the sense that assertions may use one or more commands internally.

Assertions are also automatically retried using the same mechanism as for implicit waiting on finding elements.

In the example above there are four assertions: waitForElementVisible, assert.atitleContains, assert.visible on two separate elements, and assert.containsText.

Assertions and their status (pass/fail) are displayed in the terminal. Commands (such as setValue or url) are only shown in the verbose output.

Test Output

Assertion Failures

When an assertion fails, an AssertionError is thrown and the Nightwatch test runner detects it and marks the entire test as failed.

A single test can have multiple assertions and, by default, if an assertion happens to fail the rest of the test is aborted. If the test suite has multiple steps or multiple testcases, the rest of steps/testcases will also be skipped.

Controlling Behaviour on Failure

Sometimes it is desirable for the rest of the assertions to continue, even if an assertion happens to fail mid-way. This can be achieved in several ways:

  1. Using .verify instead of .assert - the assertion failure will be logged but the test execution will continue; the test is still marked as failed at the end;
  2. Using element selector objects and set abortOnFailure to false, e.g.:
    browser.setValue({selector: 'input[type=search]', abortOnFailure: false}, 'nightwatch')

Running the remaining test steps/test cases is possible by setting "skip_testcases_on_fail" to false in your configuration file. Refer to the Configuration section for details.

Ending the Session on Failure

By default, when an assertion fails, the test runner ends the session and closes the browser window so it can continue with the remaining test suites.

However, if you wish to keep the browser window open whenever an assertion failure occurs, you can set "end_session_on_fail" to false. This can be useful for performing debugging.

Refer to the Configuration section for details.

Using BDD describe [beta]

Version 1.3 brings native support in Nightwatch for using the popular BDD interface for writing tests. No further configuration is necessary.

It is possible to run tests written in BDD describe and Exports interfaces together. Prior to this version, users had to use the Mocha test runner but now it is possible without additional plugins or libraries. The BDD interface in Nightwatch provides the common describe(), context(), test(), it(), specify(), before(), after(), beforeEach(), and afterEach() functions.

At this point Nightwatch doesn't support nested describe/context declarations. Only the top-level one is supported, which defines the name for the test suite.

Basic Example:


describe('Ecosia', function() {

  test('demo test', function(browser) {
    browser
      .url('https://www.ecosia.org/')
      .setValue('input[type=search]', 'nightwatch')
      .click('button[type=submit]')
      .assert.containsText('.mainline-results', 'Nightwatch.js')
      .end();
  });

});

In addition to the usual BDD syntax, Nightwatch provides a few ways for defining own behaviour.

- Test suite specific capabilities


describe('homepage test with describe', function() {
  // testsuite specific capabilities
  this.desiredCapabilities = {
    browserName: 'firefox'
  };

  test('...', function() {...});
});

- Test suite specific retries


describe('homepage test with describe', function() {
  // how many time to retry a failed testcase inside this test suite
   this.retries(3);

   // how many times to retry the current test suite in case of an assertion failure or error
   this.suiteRetries(2);

   test('...', function() {...});
});

Complete BDD Syntax


describe('homepage test with describe', function() {
  // testsuite specific capabilities
  // this.desiredCapabilities = {};

  // Enable this if the current test is a unit/integration test (i.e. no Webdriver session will be created)
  // this.unitTest = false

  // Set this to false if you'd like the browser window to be kept open in case of a failure or error (useful for debugging)
  // this.endSessionOnFail = true

  // Set this to false if you'd like the rest of the test cases/test steps to be executed in the event of an assertion failure/error
  // this.skipTestcasesOnFail = true

  // Set this to true if you'd like this test suite to be skipped by the test runner
  // this.disabled = false

  // this.retries(3);
  // this.suiteRetries(2);

  // Control the assertion and element commands timeout until when an element should be located or assertion passed
  // this.timeout(1000)

  // Controll the polling interval between re-tries for assertions or element commands
  // this.retryInterval(100);

  before(function(browser) {
    this.homepage = browser.page.home();
  });

  test('startHomepage', () => {
    this.homepage.navigate();
    this.homepage.expect.section('@indexContainer').to.be.not.visible;
  });


  // Run only this testcase
  /
  test.only('startHomepage', () => {
    this.homepage.navigate();
  });
  / 

  // skipped testcase: equivalent to: test.skip(), it.skip(), and xit()
  xtest('async testcase', async browser => {
    const result = await browser.getText('#navigation');
    console.log('result', result.value)
  });

  test('version dropdown is enabled', browser => {
    const navigation = this.homepage.section.navigation;
    const navbarHeader = navigation.section.navbarHeader;

    navbarHeader.expect.element('@versionDropdown').to.be.enabled;
  });

  after(browser => browser.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.value1);
  }
};

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(value1);
  }
};

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 Configuration.

module.exports = {
  demoTest: 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')
  }
};

You can also use xpath directly on a single command or assertion, by passing an element selector object, like so:

module.exports = {
  demoTest: function (browser) {
    browser
      .click({
        selector: '//tr[@data-recordid]/span[text()='Search Text']',
        locateStrategy: 'xpath'
      })
      .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.


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
--env -e default Which testing environment to use - defined in nightwatch.json
--config -c ./nightwatch.json Path to configuration file; nightwatch.conf.js or nightwatch.json are read by default if present. Check the Configuration section.
--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.
--timeout Set the global timeout for assertion retries before an assertion fails. The various timeout values are defined in the Globals section.
--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();
}
};
--output -o tests_output The location where the JUnit XML reports will be saved.
--headless Launch the browser (Chrome or Firefox) in headless mode.
--verbose Shows extended selenium command logging during the session
--help -h Shows this help.
--version -v Shows the version number

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 1.3 Nightwatch is supports writing tests using the BDD describe interface which perhaps removes the need for using Mocha.

Still, you can use Mocha and take advantage of its interfaces and reporters.

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();
  }
};

Using async/await in custom commands

You can also use ES6 async/await syntax inside function-style custom commands. Here's an example:

module.exports = {
  command: async function () {
    this.url('http://nightwatchjs.org');
    this.waitForElementVisible('section#index-container');

    const result = await this.elements('css selector', '#index-container ul.features li');
    this.assert.strictEqual(result.value.length, 7, 'Feature elements number is correct');
  }
};

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. Class-based custom commands should inherit from EventEmitter and have to manually signal the complete in order 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 Events = require('events');

module.exports = class CustomPause extends Events {
  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);
  }
}

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

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

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


exports.assertion = function(definition, expectedText, msg) {

  // If the custom commands operates with DOM elements, this options should be set
  // this.options = {
  //   elementSelector: true
  // };

  /**
   * Returns the message format which will be used to output the message in the console and also
   *  the arguments which will be used for replace the place holders, used in the order of appearance
   * 
   * The message format also takes into account whether the .not negate has been used
   *
   * @return {{args: [], message: string}}
   */
   this.formatMessage = function() {
     // Use this.negate to determine if ".not" is in use
     // Example: 
     const message = `Testing if the page title ${this.negate ? 'doesn\'t equal %s' : 'equals %s'}`;

     return {
       message,
       args: [`'${expected}'`]
     }
   };

  /**
    * Returns the expected value of the assertion which is displayed in the case of a failure
    *
    * @return {string}
    */
   this.expected = function() {
     return this.negate ? `is not '${expectedText}'` : `is '${expectedText}'`;
   };

   /**
    * Given the value, the condition used to evaluate if the assertion is passed
    * @param {*} value
    * @return {Boolean}
    */
   this.evaluate = function(value) {
     if (typeof value != 'string') {
       return false;
     }

     return value.includes(expectedText);
   };

  /**
    * Called with the result object of the command to retrieve the value which is to be evaluated
    *
    * @param {Object} result
    * @return {*}
    */
   this.value = function(result) {
     return result.value;
   };

  /**
    * When defined, this method is called by the assertion runner with the command result, to determine if the
    *  value can be retrieved successfully from the result object
    *
    * @param result
    * @return {boolean|*}
    */
   this.failure = function(result) {
     return result === false || result && result.status === -1;
   };

   /**
    * When defined, this method is called by the assertion runner with the command result to determine the actual
    *  state of the assertion in the event of a failure
    *
    * @param {Boolean} passed
    * @return {string}
    */
   this.actual = function(passed) {
     return passed ? `contains '${expectedText}'` : `does not contain '${expectedText}'`;
   };

  /**
    * The command which is to be executed by the assertion runner; Nightwatch api is available as this.api
    * @param {function} callback
    */
  this.command = function(callback) {
     // Example: this.api.getText(definition, callback);

     setTimeout(function() {
       // The object containing a "value" property will be passed to the .value() method to determine the value w
       // which is to be evaluated (by the .evaluate() method)
       callback({
         value: ''
       });

     }, 1000);
}; };

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.