HomeSoftware TestingSmart Home
 
  

Using TypeScript to write Nightwatch.js Automated Tests

May 2, 2022

Nightwatch supports TypeScript! This post will provide documentation and provide examples of how to write your tests and page objects using Nightwatch and TypeScript leveraging the @types/nightwatch definitions and Nightwatch 2.0.

There wasn't comprehensive guide that I found, while learning myself, that showed how to take the Nightwatch TypeScript type definitions and use them in a real-world UI test automation suite using the page object model, so...

This guide will show you step-by-step how to create a TypeScript-enabled Nightwatch automated test suite

This should help you win over internal adoption of Nightwatch with engineers and test engineers who prefer using TypeScript over JavaScript.

Tests within the same test project can be written in either JavaScript or TypeScript so you can slowly convert any existing tests over time or let your engineers work in either language they prefer. This is because TypeScript transpiles into JavaScript.

Continue reading or watch the lesson below.

Creating a TypeScript Nightwatch Test Suite

The following steps will create an example Nightwatch test suite written in TypeScript. How to create Nightwatch tests using the page object model pattern with TypeScript will be covered here clearly as well (that was the missing piece for me).

Let's start this Nightwatch TypeScript example by creating an empty suite.

  1. Create a folder to hold your tests.
  2. Create a source folder named src. This will contain your tests and page objects in either TypeScript or JavaScript.
  3. Create a page-objects folder and tests folder inside src

Below is the example structure we are headed towards. /distrib is automatically created when we run the TypeScript transpiler in later steps.

/distrib
└───/src
└───nightwatch.conf.js
└───/page-objects
│ │
│ └───testPage1.js
│ └───testPage2.js
│ └───existingPage.js
└───/tests
└───test1.js
└───test2.js
└───existingTest.js
/src
└───/page-objects
│ │
│ └───testPage1.ts
│ └───testPage2.ts
│ └───existingPage.js
└───/tests
└───test1.ts
└───test2.ts
└───existingTest.js
  1. Run npm init -y
  2. Run npm install nightwatch -g if you haven't already installed Nightwatch globally.
  3. Run npm install nightwatch typescript @types/nightwatch geckodriver chromedriver --save-dev
  4. Run nightwatch (this will error out and create a nightwatch.conf.js file for you)
  5. Open nightwatch.conf.js
  6. Change the src_folders setting to ./distrib/src/tests
  7. Change the page_objects_path to ./distrib/src/page-objects
// nightwatch.conf.js
module.exports = {
//...
src_folders: ['./distrib/src/tests'],
page_objects_path: ['./distrib/src/page-objects'],
//...
//Optionally change the browser for the test from firefox to chrome
test_settings: {
//...
default: {
desiredCapabilities: {
browserName : 'chrome'
},
}
}
}

This tells Nightwatch to look for the transpiled versions of your test and page object code from /src in /distrib/src for actual execution by Nightwatch using the JavaScript versions of the files.

  1. Run npm install typescript -g (to install TypeScript globally)
  2. Run tsc -init

This will create a default tsconfig.json file.

  1. Open tsconfig.json
  2. Uncomment the outDir line to read "outDir": "./distrib",
  3. Uncomment the line "allowJS": true, so your JavaScript tests and page objects will work together.
  4. Save and close tsconfig.json

Now that we have the configuration set and packages downloaded let's write our first Nightwatch.js page object in TypeScript.

Writing a Nightwatch Page Object in TypeScript

Using the page object model in your tests make them easier to understand and maintain. If you have an existing large project using JavaScript the TypeScript POM files can work alongside them seamlessly.

A typical JavaScript page object in Nightwatch.js may look something like this

// googlePage.js
const googleCommands = {
clickSearch() {
return this.waitForElementVisible('@submit', 10000)
.click('@submit')
.waitForElementNotPresent('@submit');
},
};
module.exports = {
url: 'http://www.google.com',
commands: [googleCommands],
elements: {
searchBar: {
selector: 'input[type=text]',
},
submit: {
selector: 'input[name=btnK]',
},
},
};

The TypeScript version is very similar, but we will be adding types to get the advantages of type checking and intellisense as we write our code. In addition, the module.exports style will change slightly and we will be exporting the page object as an interface in TypeScript to use in our tests in the next section.

In the following steps we will convert this JavaScript page object model to TypeScript for use in Nightwatch.

  1. In the src/page-objects folder create googlePage.ts using the contents of the example googlePage.js file above.
  2. At the top add an import statement to bring in the Nightwatch types from @types/nightwatch
// googlePage.ts
import { PageObjectModel, EnhancedPageObject } from 'nightwatch';
  1. Change module.exports = to
//module.exports = {
const googlePage: PageObjectModel = {

This creates googlePage as a Nightwatch PageObjectModel type in TypeScript which gives us type checking so we know what valid and invalid members are which is great because it helps prevent mistakes while coding and let's intellisense help you learn what members exist on it as you type.

Nightwatch TypeScript intellisense
  1. Add types to the page object commands
const googleCommands = {
//clickSearch() {
clickSearch(this: GooglePage) {
  1. Export your interface at the very bottom of the page object code
export default googlePage;
export interface GooglePage
extends EnhancedPageObject<typeof googleCommands,
typeof googlePage.elements> { }

EnhancedPageObject can take the types for your custom commands, elements, and optionally any sections you may optionally be using as a third parameter.

This is what gives you intellisense and type checking for your page objects when you use them in your tests later.

// googlePage.ts
import { PageObjectModel, EnhancedPageObject } from 'nightwatch';
const googleCommands = {
clickSearch(this: GooglePage) {
return this.waitForElementVisible('@submit', 10000)
.click('@submit')
.waitForElementNotPresent('@submit');
},
};
const googlePage: PageObjectModel = {
url: 'http://www.google.com',
commands: [googleCommands],
elements: {
searchBar: {
selector: 'input[type=text]',
},
submit: {
selector: 'input[name=btnK]',
},
},
};
export default googlePage;
export interface GooglePage
extends EnhancedPageObject<typeof googleCommands,
typeof googlePage.elements> { }

So now that we have created a Nightwatch TypeScript page object model let's use it in a test also written in TypeScript.

Writing a Nightwatch Test in TypeScript using the Page Object Model

In the next steps we'll use this example of a Nightwatch JavaScript test and convert it to TypeScript.

// googleTest.js
module.exports = {
'Google search test': function (browser) {
let google = browser.page.googlePage();
google
.navigate()
.assert.titleEquals('Google')
.assert.visible('@searchBar')
.setValue('@searchBar', 'nightwatch');
google
.clickSearch()
.expect.element('body')
.text.to.contain(
'Nightwatch.js | Node.js powered End-to-End testing framework'
);
browser.end();
},
};
  1. Create a file named googleTest.ts under src/tests and paste in the above example.
  2. Add a line to import NightwatchTests and NightwatchBrowser from @types/nightwatch at the top of the test class. Also, import GooglePage.
  3. Change module.exports to a named constant of type NightwatchTests and export it at the very bottom of the test class as shown below
// googleTest.ts
import { NightwatchTests, NightwatchBrowser } from "nightwatch";
import { GooglePage } from '../page-objects/googlePage';
//module.exports = {
const googleTest: NightwatchTests = {
//...
}
export default googleTest;
  1. Inside the test, change the untyped browser object to NightwatchBrowser and the google variable to our page object model type, GooglePage, that we made in the prior section
// 'Google search test': function (browser) {
// let google = browser.page.googlePage();
'Google search test': function (browser: NightwatchBrowser) {
const google: GooglePage = browser.page.googlePage();

Now that everything has types our IDE, like Visual Studio Code, will provide helpful intellisense when we hover over the methods exposed in the Nightwatch API or our page objects.

Nightwatch intellisense for titleEquals

Our custom commands in the page object model will also show up because of the use of typeof when we exported our page object model interface.

Nightwatch intellisense for titleEquals

The final test Nightwatch test written in TypeScript should look like this

import { NightwatchTests, NightwatchBrowser } from 'nightwatch';
import { GooglePage } from '../page-objects/googlePage';
const googleTest: NightwatchTests = {
'Google search test': (browser: NightwatchBrowser) => {
let google: GooglePage = browser.page.googlePage();
google
.navigate()
.assert.titleEquals('Google')
.assert.visible('@searchBar')
.setValue('@searchBar', 'nightwatch');
google
.clickSearch()
.expect.element('body')
.text.to.contain(
'Nightwatch.js | Node.js powered End-to-End testing framework'
);
browser.end();
},
};
export default googleTest;

Now that we have our page object model and automated test written we can run our test suite.

How to Run a Nightwatch Test in TypeScript

TypeScript needs to be transpiled, converted from TypeScript to JavaScript, using the TypeScript compiler, tsc. The best workflow I've found for executing the TypeScript test suite is by modifying the npm test command.

  1. Open package.json
  2. Change the scripts section to the below example
"scripts": {
"test": "tsc && nightwatch"
},

tsc will convert any .ts tests and page objects and move them over to the distrib folder along with any existing .js page objects and tests you have in the suite.

nightwatch will execute both your TypeScript and JavaScript tests in your suite when run this way 😁

  1. Run npm test from your command line to run your tests.
> tsc && nightwatch
[Google Test] Test Suite
────────────────────────────────────────────────
⠸ Starting ChromeDriver on port 9515...
ℹ Connected to ChromeDriver on port 9515 (751ms).
Using: chrome (101.0.4951.41) on WINDOWS.
Running Google search test:
───────────────────────────────────────────────────────────────────────────────────────────────────
ℹ Loaded url http://www.google.com in 1605ms
√ Testing if the page title equals 'Google' (6ms)
√ Testing if element <Element [name=@searchBar]> is visible (40ms)
√ Element <input[name=btnK]> was visible after 532 milliseconds.
√ Element <input[name=btnK]> was not present after 9 milliseconds.
√ Expected element <body> text to contain: "Nightwatch.js | Node.js powered End-to-End testing framework" (292ms)
OK. 5 assertions passed. (4.149s)

From here you can add more automated tests in your test suite written in either JavaScript or TypeScript and npm run will execute them for you.

The full source code for this example is on my GitHub

Final Thoughts

I'm excited that I'm finally able to write my Nightwatch tests in TypeScript. If you are converting a large suite, like I am, you can slowly convert existing tests and page objects by swapping the file extension from .js to .ts using the patterns I showed above without needing to do a big-bang conversion on your entire project.

The DefinitelyTyped @types/nightwatch project is community-driven and is continually being updated to include the remaining types in Nightwatch 2.0. If you have additions or improvements you can submit pull requests to DefinitelyTyped/nightwatch. Special thanks to vaibhavsingh97 who is a top contributor and answerer of questions for that project and Luke Bickell who helped figure out recursive page objects.

If you are a Nightwatch beginner be sure to watch my Software Testing Playlist

Please share the link to this article if you enjoyed it and if you have any questions or comments please reach out through my social links below 👇

Photo of David Mello

David Mello

Breaker of software, geek, meme enthusiast.

 

DavidMello.com © 2023