End-to-end testing with protractor for nonangular applications using modern ES6 javascript syntax

I will not repeat how testing is important and how you should test your application to add confidence and to verify correctness. If you are reading this you want to start E2E testing your application and searching what tools to use.

E2E Testing definition from angular docs

As applications grow in size and complexity, it becomes unrealistic to rely on manual testing to verify the correctness of new features, catch bugs and notice regressions. Unit tests are the first line of defense for catching bugs, but sometimes issues come up with integration between components which can’t be captured in a unit test. End-to-end tests are made to find these problems.

Protractor test runner

Protractor is an end-to-end test framework for AngularJS applications. Protractor runs tests against your application running in a real browser, interacting with it as a user would.

Protractor can be used for non angular applications

browser.ignoreSynchronization = true; will stop waiting for angular and will allow us to use protractor for testing server rendered or single page web applications not written using angular framework.

Example of running protractor test

Test runner will start one or more browser instances and output results in terminal.

console output from running protractor with jasmine-spec-reporter

Motivation to create protractor-starter

The greatest barrier for starting with end-to-end testing is tooling setup. 
I want to give you a good starting point for your own E2E testing.

protractor-starter: https://github.com/marcelmokos/protractor-starter

Goals

  • Modern javascript ES6 syntax
  • No configuration required
  • One command for setup and run
  • Linting and automatic code style fixing using eslint build in, specially for protractor and jasmine
  • Test examples using page object pattern

Modern ES6 Javascript

I can not see ES5 syntax anymore, this example is written exclusively in ES6.
There is no visible compilation step, you have correct line numbers in error stack traces and logs which are important for developer experience.

E2E tests a can use a feature of in-memory babel compilation. File .babelrc is setting presets for compilation.

The protractor test runner than can be called with prepended babel-node command and we do not have to think about compilation in the protractor config and tests.

“protractor”: “babel-node node_modules/.bin/protractor protractor-conf.js”,

Note that babel-node is not suitable for running node server in production because compiled source code is held in memory but is ideal for compiling E2E tests.

Async Await Promises in tests

Alternative syntax to Promise.then() allows awaiting for promise resolve without chaining.

WebDriverJS (and thus, Protractor) APIs are entirely asynchronous. All functions return promises. WebDriver Control Flow will try to resolve all promises in the queue before continuing. Async functions in body of expect(), beforeAll(), beforeEach(), afterAll(), afterEach() will resolve promises before continuing to next code block.

Example function

Function element.getAttribute("id") returns Promise resolving to <String>. Function getInputsLabelElement returns Promise resolving to <ElementFinder>. 
Function is declared as async and is awaiting inputId value.

Async function example

Example test

When async await is used then there is more control over test run and Promise chaining and the callback function is avoided.

Async test example

Why not use typescript

I was writing protractor tests using typescript and I feel that it is unnecessary to complicate tests with types and interfaces. Intellisense in IDE will provide autocompletion and eslint will take care of most common mistakes and styling problems for protractor with recommended settings.

Provided protractor configuration

The configuration is simple run every test file in "spec" folder with "*.spec.js" extension.

specs: [“spec/**/*.spec.js”],
protractor configuration

Why jasmine-spec-reporter

Default reporter was not working well when more browser instances were used. Default jasmine dot reporter was providing only summary on the end of the tests and jasmine-spec-reporter is providing detailed output during test run.

Get protractor starter

fork protractor-starter or clone repository directly

git clone https://github.com/marcelmokos/protractor-starter.git

Run tests

On single browser instance

yarn test

On multiple browser instances in parallel

yarn run test:multiple // this run will ignore fdescribe and run all test files

One command for setup and run. Both commands will install and run linting before starting protractor.

Why "yarn" instead of "npm"

This decision was done because "yarn" is faster.

To make it simple on every run all dependencies are installed. All dependencies are in project and nothing other then node and yarn needs to be installed globally.

Strong linting and automatic code style fixing

Set up linting is not easy and set up linting to work with testing frameworks can be even more complicated.

This project has linting setup in .eslintrc.yml config and inherit settings from eslint-config-airbnb-base and eslint-plugin-protractor.

This project has linting setup in .eslintrc.yml setting is opinionated but defaults came from eslint-airbnb-config. Incompatible style is marked as error to not allowing to commit incompatible style.

Automatic fixes for fixable problems like missing semicolons is performed by prettier and eslint -fix. Which helps maintain code style. 
Goal is to improve code readability, style and ease code reviews.

There are developers that do not use proper code editor or IDE which will provide linting and suggestions eslint is command tool and will run everywhere.

VCS pre-commit hook linting

Pre-commit hooks are scripts that run before each commit. 
 
Decision of strong linting requires setting up tool to not allow inconsistent code to be committed into a repository.

"pre-commit": [
"lint:pre-commit"
],

Eslint rules that are enforced only in pre-commit hook are

  • "no-console", "no-debuger" allows using console for debugging but will not allow to commit debugging statements
  • "protractor/no-browser-pause" allows to use browser.pause() for debugging but will not allow to commit this statement

Page object pattern and feature testing

Another barrier is that there are no good examples of tests with page Object pattern.

This project includes examples of testing single page app using Page Object pattern and feature testing.

spec
├── creatix
│ ├── Common.spec.js
│ ├── Homepage.spec.js
│ ├── Navigation.spec.js
│ ├── ContactForm.spec.js
│ └── pages // page objects
│ ├── About.js
│ ├── Blog.js
│ ├── Careers.js
│ ├── Common.js
│ ├── Contact.js
│ ├── ContactForm.js
│ ├── Homepage.js
│ └── Work.js
└── utils
├── Page.js
└── UIComponent.js

Page object pattern

All selectors and functions should be placed in Page Object Class. Class should holds selectors and actions for given page or component.
 
Page object files should export only one Page Class as default export and extends spec/utils/Page.js whitch extends spec/utils/UIElement.js and implement abstract property ‘selector’.

Example of Page object pattern base class

This will allow you to use isDisplayed and waitUntilDisplayed functions and you can avoid repeating boilerplate for this functionality which is needed because there is no synchronisation as with angular application.

Page object pattern example
Page object pattern usage example

Feature testing

The files structure of the project should wrap around features instead of pages.

Dependencies

- node https://nodejs.org/en/download/
- yarn https://yarnpkg.com/en/docs/install
- chrome https://www.google.com/chrome/browser/desktop/index.html

Documentation

protractor-starter

Protractor

Jasmine

Eslint