JavaScript testing using Nightwatch

Last updated on
2 January 2024

This documentation needs review. See "Help improve this page" in the sidebar.

Setup

How to set up Nightwatch is explained in the core README file for tests, core/tests/README.md.

Alternatively, if you use DDEV, you can use DDEV Selenium Standalone Chrome.

Creating a test

To create your own test, add a file in your module: your_module/tests/src/Nightwatch/Tests/exampleTest.js.
In that file, write some JS:

module.exports = {
  '@tags': ['your_module'],
  before: function(browser) {
    browser
      .drupalInstall();
  },
  after: function(browser) {
    browser
      .drupalUninstall();
  },
  'Visit a test page and create some test page': (browser) => {
    browser
      .drupalRelativeURL('/test-page')
      .waitForElementVisible('body', 1000)
      .assert.containsText('body', 'Test page text')
      .drupalRelativeURL('/node/add/page')
      .setValue('input[name=title]', 'A new node')
      .setValue('input[name="body[0][value]"]', 'The main body')
      .click('#edit-submit')
      .end();
  },

};

Remember to use @tags to tag your test with your modules' machine name so it gets picked up by the test runner on drupal.org.

Nightwatch Command Reference

Writing Unit tests in Nightwatch

It is possible to use Nightwatch for unit testing JavaScript. In order to unit test your JavaScript, it is important that the JavaScript is written in a way that is testable. Writing unit-testable JavaScript is not specific to Drupal, and you should look around the Internet for examples of how to do it.

You can mark your test as a unit test by setting:

'@unitTest' : true

Here is an example test

const assert = require('assert');
const testScript = require('../../Scripts/unitTestScript');

const dataProvider = [
  {input: 'value_1', expected: 'value_1Test'},
  {input: 'value_2', expected: 'value_2Test'},
  {input: 'value_3', expected: 'value_3Test'},
];

module.exports = {
  '@tags': ['unit_test'],
  '@unitTest' : true,
  'example unit test' : function (done) {
    dataProvider.forEach(function (values) {
      assert.strictEqual(testScript.testMethod(values.input), values.expected);
    });
    setTimeout(function() {
      done();
    }, 10);
  }
};

Writing Accessibility tests in Nightwatch

Here is the core accessibility test that tests multiple URLs for accessibility issues (taken from a11yTestAdmin.js). Drupal's Nightwatch implementation now also leverages Deque's axe accessibility engine to evaluate for known accessibility errors. This sample list of URLs should cover most basic elements from the Drupal UI. 

const argv = require('minimist')(process.argv.slice(2));

const adminTest = {
  '@tags': ['core', 'a11y', 'a11y:admin'],

  before(browser) {
    browser.drupalInstall({ installProfile: 'nightwatch_a11y_testing' });
    // If an admin theme other than Claro is being used for testing, install it.
    if (argv.adminTheme && argv.adminTheme !== browser.globals.adminTheme) {
      browser.drupalEnableTheme(argv.adminTheme, true);
    }
  },
  after(browser) {
    browser.drupalUninstall();
  },
};
const testCases = [
  { name: 'User Edit', path: '/user/1/edit' },
  { name: 'Create Article', path: '/user/1/edit' },
  { name: 'Create Page', path: '/node/add/page?destination=/admin/content' },
  { name: 'Content Page', path: '/admin/content' },
  { name: 'Structure Page', path: '/admin/structure' },
  { name: 'Add content type', path: '/admin/structure/types/add' },
  { name: 'Add vocabulary', path: '/admin/structure/taxonomy/add' },
  // @todo remove the skipped rules below in https://drupal.org/i/3318394.
  {
    name: 'Structure | Block',
    path: '/admin/structure/block',
    options: {
      rules: {
        'color-contrast': { enabled: false },
        'duplicate-id-active': { enabled: false },
        region: { enabled: false },
      },
    },
  },
];

testCases.forEach((testCase) => {
  adminTest[`Accessibility - Admin Theme: ${testCase.name}`] = (browser) => {
    browser.drupalLoginAsAdmin(() => {
      browser
        .drupalRelativeURL(testCase.path)
        .axeInject()
        .axeRun('body', testCase.options || {});
    });
  };
});

module.exports = adminTest;

Help improve this page

Page status: Needs review

You can: