Using TypeScript to write Nightwatch.js Automated Tests
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.
- Create a folder to hold your tests.
- Create a source folder named
src
. This will contain your tests and page objects in either TypeScript or JavaScript. - Create a
page-objects
folder andtests
folder insidesrc
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
- Run
npm init -y
- Run
npm install nightwatch -g
if you haven't already installed Nightwatch globally. - Run
npm install nightwatch typescript @types/nightwatch geckodriver chromedriver --save-dev
- Run
nightwatch
(this will error out and create a nightwatch.conf.js file for you) - Open
nightwatch.conf.js
- Change the
src_folders
setting to./distrib/src/tests
- Change the
page_objects_path
to./distrib/src/page-objects
// nightwatch.conf.jsmodule.exports = {//...src_folders: ['./distrib/src/tests'],page_objects_path: ['./distrib/src/page-objects'],//...//Optionally change the browser for the test from firefox to chrometest_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.
- Run
npm install typescript -g
(to install TypeScript globally) - Run
tsc -init
This will create a default tsconfig.json
file.
- Open
tsconfig.json
- Uncomment the
outDir
line to read"outDir": "./distrib",
- Uncomment the line
"allowJS": true,
so your JavaScript tests and page objects will work together. - 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.jsconst 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.
- In the
src/page-objects
folder creategooglePage.ts
using the contents of the examplegooglePage.js
file above. - At the top add an import statement to bring in the Nightwatch types from
@types/nightwatch
// googlePage.tsimport { PageObjectModel, EnhancedPageObject } from 'nightwatch';
- 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.
- Add types to the page object commands
const googleCommands = {//clickSearch() {clickSearch(this: GooglePage) {
- Export your interface at the very bottom of the page object code
export default googlePage;export interface GooglePageextends 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.tsimport { 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 GooglePageextends 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.jsmodule.exports = {'Google search test': function (browser) {let google = browser.page.googlePage();.navigate().assert.titleEquals('Google').assert.visible('@searchBar').setValue('@searchBar', 'nightwatch');.clickSearch().expect.element('body').text.to.contain('Nightwatch.js | Node.js powered End-to-End testing framework');browser.end();},};
- Create a file named
googleTest.ts
undersrc/tests
and paste in the above example. - Add a line to import NightwatchTests and NightwatchBrowser from
@types/nightwatch
at the top of the test class. Also, import GooglePage. - 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.tsimport { NightwatchTests, NightwatchBrowser } from "nightwatch";import { GooglePage } from '../page-objects/googlePage';//module.exports = {const googleTest: NightwatchTests = {//...}export default googleTest;
- Inside the test, change the untyped
browser
object toNightwatchBrowser
and thegoogle
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.
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.
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();.navigate().assert.titleEquals('Google').assert.visible('@searchBar').setValue('@searchBar', 'nightwatch');.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.
- Open
package.json
- 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 😁
- 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 👇