Quantcast
Channel: Tudor Barbu's blog
Viewing all articles
Browse latest Browse all 28

Custom assertions for Nightwatchjs

$
0
0

I’ve started using Nightwatch to write my end to end tests in Javascript. I found it to be an easy to use framework, that makes use of W3C Webdriver API – formerly known as Selenium – and exposes an expressive API, which supports both a BDD-style approach as well as the more “traditional” approach based on assertions.

In both cases, the code is very expressive and easy to follow:

export default {
  'My test': browser => {
    browser
      .assert.elementPresent('#main-content')
  }
}

…and…

export default {
  'My test': browser => {
    browser
      .expect.element('#main-content').to.be.present
  }
}

Nightwatch also allows programmers to issue commands to the browser, such as clicks, setting and retrieving cookies and even modifying the window’s size to test for responsive behaviour through its extensive API. More than enough to make Nightwatch a tool to consider when writing frontend tests. However, depending on the test’s specifics, there can be some things missing.

Assert number of elements visible in Nightwatch

For example, one problem that I ran into and was missing from the API was asserting the number of visible elements that match a certain CSS selector. Nightwatch’s API does not provide support for this by default, so I had to write my own assertion. This has proven to be a bit of a difficult task, mostly due to the lack of available information, but in the end I got it working. Here are the steps I took to write the test.

The first step I had to do in order to inject a custom assertion was to update my nightwatch.conf.js file and specify a folder where the custom assertions are stored.

module.exports = {
  // ...
  custom_assertions_path: ['test/nightwatch/custom-assertions'],
  // ...
}

This was the easy part. The second part was writing the assertion itself. The assertion function – can’t be an arrow function as it needs to have context bound to it – needs to define several attributes. The minimum requirements are:

  • message – the message that will be displayed to the user
  • expected – the expected result
  • pass – a boolean function which will return whether the test has passed or not
  • value – a function that will extract the value from the object returned by the W3C Webdriver API
  • command – a function that will execute in the browser’s context* and extract the required data which will be returned wrapped in the result object

* being executed in the browser’s context it means it does not have access to the current context, for example closure variables will not be available and the this reference will point to a different object. It took me a while to get this working correctly.

In this case, the command function needs to return the number of visible elements. To decide if a node is visible, I followed the instructions here. Note: this is meant to be a generic solution and shouldn’t depend on certain libraries – such as jQuery – being available and needs to be as vanilla js as possible.

const assertion = function (selector, count, message = null) {
  this.message = message || `Asserting that there are "${count}" visible elements that match "${selector}"`
  this.expected = count

  this.pass = value => value === this.expected
  this.value = result => result.value

  this.command = callback => {
    const self = this
    return this.api.execute(
      selector => {
        let count = 0

        document.querySelectorAll(selector).forEach(node => {
          if (node.offsetHeight !== 0) {
            count++
          }
        })

        return count
      },
      [selector],
      result => { callback.call(self, result) }
    )
  }
}

export {
  assertion
}

The assertion’s ready. Now all I had to do was to call it in the test:

export default {
  'My test': browser => {
    browser
      .assert.visibileElementsCount('table tbody tr', 2)
  }
}


Viewing all articles
Browse latest Browse all 28

Trending Articles