1 of 48

www.drupaleurope.org

2 of 48

Salva Molina

Browser Testing

Session URL: https://www.drupaleurope.org/session/browser-testing-nightwatchjs

with Nightwatch.js

13/9/2018

3 of 48

Salva Molina

PHP Engineer

@salva_bg

Drupal.org: slv_

Freelance

Drupal & Symfony.

Security Audits.

Devops.

4 of 48

Me

5 of 48

Nightwatch.js

  • Introduction & Motivation
  • 4 Artifacts to rule them all
  • Nightwatch & Drupal

6 of 48

A somewhat familiar Friday for every developer.

The watermelon effect

7 of 48

Deploying to production (no tests)

8 of 48

9 of 48

Introduction

What’s Nightwatch.js

10 of 48

Introduction

What’s Nightwatch.js

11 of 48

Test Runner with parallel execution support.

JUnit-compliant XML reports.

assert-like and expect-like validations.

Hooks: before, beforeEach, after, afterEach.

Unit Testing support.

Main Features

The bread and butter

12 of 48

"src_folders" : [

"./tests/nightwatch/tests/"

],

"output_folder" : "{...}/reports",

"custom_commands_path" : "{...}/commands",

"custom_assertions_path" : "{...}/assertions",

"page_objects_path" : [

"{...}/pages/example"

],

"globals_path" : "{...}/global.js",

First Stop: Nightwatch.json

Paths

13 of 48

"selenium" : {

"start_process" : true,

"server_path" : "./tests/bin/selenium-server-standalone-3.13.0.jar",

"log_path" : "",

"port" : 34567,

"cli_args" : {

"webdriver.chrome.driver" : "./tests/bin/chromedriver"

}

},

First Stop: Nightwatch.json

Selenium and Webdrivers

14 of 48

"dev" : {

"launch_url" : "http://127.0.0.1:8000",

"selenium_port" : 9515,

"selenium_host" : "127.0.0.1",

"default_path_prefix" : "",

"desiredCapabilities": {

"browserName": "chrome"

}

}

First Stop: Nightwatch.json

Environments

15 of 48

var selenium = require('selenium-server');

var chromedriver = require('chromedriver');

module.exports = (function(settings) {

settings.selenium.server_path = selenium.path;

settings.selenium.cli_args["webdriver.chrome.driver"] = chromedriver.path;

return settings;

})(require('./nightwatch.json'));

Fully-customized setup

Nightwatch.conf.js

16 of 48

Main header

Subtitle

4 Artifacts to rule them all

17 of 48

Commands.

Page Objects.

Asserts.

Global Data.

Artifacts

Tests.

18 of 48

Many provided by default, like:

.closeWindow.

.setValue.

.saveScreenshot.

.click.

Commands

Generic actions to perform across the site

19 of 48

Custom Commands

commands/clickWithMessage.js

exports.command = function (selector, message) {

// Click and display a message for the action.

this.click(selector, function() {

if (this.globals.test_settings.disable_colors === true) {

console.log(' ✔ ' + message);

}

else {

console.log('\033[92m ✔ \033[0m' + message);

}

});

return this;

};

20 of 48

Martin Fowler.

Page objects are a classic example of encapsulation - they hide the details of the UI structure and widgetry from other components (the tests).

21 of 48

Page Objects

pages/myPage.js

22 of 48

Page Objects

pages/myPage.js

23 of 48

Page Objects

pages/myPage.js

24 of 48

Page Objects

pages/myPage.js

25 of 48

Assertions

assertions/myAssertion.js

exports.assertion = function(selector, comparedValue, msg) {

this.message = msg || util.format('Testing if value of <%s> does not equal: "%s".', selector, comparedValue);

this.expected = comparedValue;

this.command = function(callback) {};

this.value = function(result) {};

this.pass = function(value) {};

};

26 of 48

BDD Expect Assertions

checkFieldAttribute: function(fieldName, attr, Value) {

this

.expect.element(fieldName).to.have.attribute(attr).equals(Value);

return this;

},

Language Chains: to, be, been, is, that, which, and, has, have, with, at, does, of.

27 of 48

Global Data

data/globals.js

user: {

username: 'test3',

password: 'AcceptableUserPassword_-',

},

unexisting_user: {

username: 'nightwatch_' + Date.now(),

firstname: 'NW_First',

display_name: 'NW_First' + 'Surname' + Date.now(),

email: 'nightwatch_' + Date.now() + '@.example.net'

},

28 of 48

Simple Test with & w/o Page Objects.

Demo 1

29 of 48

Remote testing via Jenkins

Dependencies on the remote Server

30 of 48

Scenario

31 of 48

1

Scenario

32 of 48

1

2

Scenario

33 of 48

1

2

3

Scenario

34 of 48

Scenario

1

2

4

3

35 of 48

CI Configuration

"ci" : {

"launch_url" : "http://{http_auth_user}:{http_auth_pass}@127.0.0.1:8000",

"selenium_port" : 9515,

"selenium_host" : "127.0.0.1",

"default_path_prefix" : "",

"selenium" : {

"start_process": false

},

"desiredCapabilities": {

"browserName": "chrome",

"chromeOptions": {

"args": ["--headless"]

}

}

}

36 of 48

LIVE DEMO:�Graphical representation

37 of 48

Remote Testing via Jenkins

Demo 2

38 of 48

Drupal commands

Nightwatch & Drupal

39 of 48

CreateRole, CreateUser, Login, Logout, RelativeURL, UserIsLoggedIn.

Commands

Available in 8.6.0

Install, LoginAsAdmin, Uninstall.

Script-based

Web-based

40 of 48

Don’t need to go through any Nightwatch documentation.

Very simple and fast setup. No custom package.json needed:

yarn install

yarn test:nightwatch

Drupal Setup

Pros

41 of 48

Page Objects not supported yet.

Some use cases not covered (no multiple env setup).

Need to download some other npm dependencies that you might not want for remote testing.

Still lots to do. Can’t easily enable modules, place blocks, etc.

Drupal Setup

Cons

42 of 48

Module developer: YES! or… yes?

For customer sites: I would not.

Instead: Use your own setup, and simply point its config to the commands provided by core that are somewhat usable outside of core testing.

Commands

So, should I use Core commands for my tests?

43 of 48

Why Nightwatch?

44 of 48

Why Nightwatch?

45 of 48

Resources

Some links, tools and interesting reads

46 of 48

DevOps + Infrastructure

13/9/2018

TRACK SUPPORTED BY

47 of 48

Questions?

48 of 48

Danke fürs Kommen

@salva_bg

salva.momo[at]gmail.com

www.adevfromtheplains.com