JSFoo Pune 2019

End-to-End testing made easy with Nightwatch

Hi, I'm Ratan Kulshreshtha

  • IT Developer at Telstra India
  • Pythonista and Djangonaut
  • Linux User
  • Love Vue.js
  • Rustacean
  • Twitter, GitHub: @RatanShreshtha

What And Why?

  • End-to-End (E2E) testing solution for browser-based apps and websites.
  • Based on Node.js and selenium

What?

Why?

  • Testing is crucial
  • Find Bugs
  • Validation & verification
  • Feedback
  • E2E Testing lets you test and experiences an end user the complete system

Why use Nightwatch.js ?

Features

  • Clean Syntax
  • Built-in test runner
  • Selenium server
  • Cloud services support
  • CSS & Xpath support
  • Continous integration support
  • Easy to extend

Community

Nightwatch.js has great community it has

  • 85,375+ weekly downloads from npm
  • 8,800+ stars
  • 800+ forks

Great Documentation

Nightwatch.js uses the W3C WebDriver API, but it protects you from the very verbose and cryptic documentation of W3C WebDriver

vs

Getting Started

Pre-Requisites

  • Node.js
  • Java
  • Terminal or CMD
  • Text Editor of your Choice

Installation

$ npm install [-g] nightwatch
Add -g option to make nightwatch runner available globally in your system

Selenium Server Setup

$ # For sanity check  
$ java -jar selenium-server-standalone-3.13.0.jar

Configuration

Test runner expects a configuration file to be passed, by default a nightwatch.json. A nightwatch.conf.js file will also be loaded by if found.

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

  "selenium" : {
    "start_process" : false,
    "server_path" : "",
    "log_path" : "",
    "port" : 4444,
    "cli_args" : {
      "webdriver.chrome.driver" : "",
      "webdriver.gecko.driver" : "",
      "webdriver.edge.driver" : ""
    }
  },

  "test_settings" : {
    "default" : {
      "launch_url" : "http://localhost",
      "selenium_port"  : 4444,
      "selenium_host"  : "localhost",
      "silent": true,
      "screenshots" : {
        "enabled" : false,
        "path" : ""
      },
      "desiredCapabilities": {
        "browserName": "firefox",
        "marionette": true
      }
    },

    "chrome" : {
      "desiredCapabilities": {
        "browserName": "chrome"
      }
    },

    "edge" : {
      "desiredCapabilities": {
        "browserName": "MicrosoftEdge"
      }
    }
  }
}

Writing Your First Test

Make a file 01_hello_world.js in tests directory

module.exports = {
  tags: ['hello_world'],
  'Hello, World!': function(browser) {
    browser
      .url('http://www.google.co.in') // Go to a url
      .waitForElementVisible('body', 2000) // wait till page loads
      .assert.title('Google') // Make sure Site title matches
      .assert.visible('input[type=text]')
      .setValue('input[type=text]', 'Hello, World!') // send values
      .waitForElementVisible('input[name=btnK]', 1000)
      .click('input[name=btnK]') // click on search button
      .pause(4000)
      .assert.containsText('#main', 'Hello, World!')
      .end();
  }
};
$ # If Installed globally
$ nightwatch -c ./nightwatch.json --env default
$ # If Installed locally
$ ./node_modules/nightwatch/bin/nightwatch -c nightwatch.json --env default

Running nightwatch

Test Results

Commands

clearValue
click
deleteCookie
deleteCookies
end
getAttribute
getCookie
getCookies
getCssProperty
getElementSize
getLocation
getLocationInView
getTagName
getText
getTitle
getValue
init
injectScript
isVisible
maximizeWindow
moveToElement
pause
resizeWindow
saveScreenshot
setCookie
setValue
submitForm
switchWindow
urlHash
waitForElementNotPresent
waitForElementNotVisible
waitForElementPresent
waitForElementVisible

Assertions

attributeEquals
containsText
cssClassPresent
cssClassNotPresent
cssProperty
elementPresent
elementNotPresent
hidden
title
urlContains
value
valueContains
visible

Data Driven Tests

To avoid hard-coding the values create a directory named data or any other directory for storing data.

 

Then specify the path to that folder inside the nightwatch.json file, as the globals_path property.

No Hardcoding

module.exports = {
  url: 'https://www.google.co.in/',
  searchKeyword: process.env.searchKeyword || 'JSFoo Pune 2019',
};

Make a .js file and export all the data

{
  "src_folders": ["tests"],
  "output_folder": "reports",
   ....
   ....
  "globals_path": "data/google",
   ....
   ....
}

nightwatch.json

Then access the data as browser.globals in your tests.

module.exports = {
  tags: ['google_data_driven'],
  'Google  JSFoo Pune 2019': function(browser) {
    browser
      .url(browser.globals.url) // Go to a url
      .waitForElementVisible('body', 2000) // wait till page loads
      .assert.title('Google')  // Make sure Site title matches
      .saveScreenshot('screenshots/google_home_page,png')
      .assert.visible('input[type=text]')
      .setValue('input[type=text]', browser.globals.searchKeyword)
      .waitForElementVisible('input[name=btnI]', 1000)
      .click('input[name=btnI]') // click on search button
      .pause(5000)
      .waitForElementVisible('body', 2000)
      .assert.title('JSFoo Pune 2019 - JSFoo + Meta Refresh 2018')
      .saveScreenshot('screenshots/JSFoo_Pune_2019_home_page.png')
      .end();
  }
};

Use the data in tests

Customisation

Custom Commands

To make custom nightwatch command create a separate folder and defining your own commands inside there, each one inside its own file.

 

Then specify the path to that folder inside the nightwatch.json file, as the custom_commands_path property.

Custom Commands

You can handle WindowEventHandlers using custom commands in your tests.

 

This might be helpful if you have a flow where you ask your user to confirm before leaving the page.

Custom Commands

// A custom Nightwatch command.
// The command name is the filename.
// Example usage:
//
//   browser.hasOnBeforeUnload(function(result) {})
//
// For more information on custom assertions see:
// http://nightwatchjs.org/guide/#writing-custom-commands

module.exports.command = function(callback) {
  var self = this;

  this.execute(function() {
    return window && typeof window.onbeforeunload === 'function';
  }, [], function(result) {
    callback.call(self, result.value);
  });

  return this;
};

Custom Assertions

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

 

Make a folder where you have your custom assertions then specify the path to that folder inside the nightwatch.json file, as the custom_assertions_path property.

Custom Assertions

For example if you want to know how many elements of a particular kind are present on the page.

 

This maybe useful if you are building a pagination with multiple options to select number of items in a page.

Custom Assertions

// A custom Nightwatch assertion.
// The assertion name is the filename.
// Example usage:
//
//   browser.assert.elementCount(selector, count)
//
// For more information on custom assertions see:
// http://nightwatchjs.org/guide#writing-custom-assertions

exports.assertion = function elementCount (selector, count) {
  this.message = `Testing if element <${selector}> has count: ${count}`
  this.expected = count
  this.pass = val => val === count
  this.value = res => res.value
  function evaluator (_selector) {
    return document.querySelectorAll(_selector).length
  }
  this.command = cb => this.api.execute(evaluator, [selector], cb)
}
{
  "src_folders": ["tests"],
  "output_folder": "reports",
  "page_objects_path": "pages",
  "custom_assertions_path": "custom-assertions",
  "custom_commands_path": "custom-commands",
   ....
   ....
   ....
   ....
}

nightwatch.json

Demo

Let's Pray To Demo Gods

Resources

Thank You!

Questions And Feedback

End-to-End testing made easy with Nightwatch @ JSFoo Pune 2019

By Ratan Kulshreshtha

End-to-End testing made easy with Nightwatch @ JSFoo Pune 2019

  • 1,082