-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Page objects with Nightwatch v3 (TypeScript)
Nightwatch v3 has much better TypeScript support for Page Objects. In order to make full use of it, follow the below steps:
-
For this doc, we'll consider that you have the folder structure for Nightwatch tests as follows:
my-project/ |-- nightwatch/ | |-- pages/ | | |-- myPage.ts | |-- tsconfig.json |-- test/ | |-- myTest.ts |-- types/ | |-- nightwatch.d.ts |-- tsconfig.json |-- package.json
-
Now, let's define a page object named
myPage
innightwatch/pages/myPage.ts
as below:// nightwatch/pages/myPage.ts import { PageObjectModel, EnhancedPageObject } from 'nightwatch' const myPageCommands = { assertTitle(this: EnhancedPageObject) { return this.assert.titleEquals('MyTitle'); } }; const myPage = { url: 'myUrl', commands: [myPageCommands], elements: { myButton: { selector: '.buttonClass', } } } satisfies PageObjectModel; export interface MyPage extends EnhancedPageObject<typeof myPageCommands, typeof myPage.elements> {} export default myPage;
In the above page object definition, notice that we've defined it as
const myPage = {} satisfies PageObjectModel
instead ofconst myPage: PageObjectModel = {}
. Doing so helps preserve the literal type of the page object and all its components when they are passed toElementPageObject
, instead of downleveling them to take the loose types defined inPageObjectModel
.
-
Now that we've defined our page object, let's write a test using this page object at
test/myTest.ts
as below:// test/myTest.ts describe('myPage Demo', function() { it('navigate to page, assert the title and then click on myButton', function() { const myPage = browser.page.myPage(); myPage .navigate() .assertTitle() .click('@myButton'); }); });
On copying the above example to your IDE, you'll notice some red squiggles under .assertTitle()
. This is because even though we are using the page object defined earlier correctly, TypeScript does not know the exact type for myPage
and the commands we have added to it.
There are two ways to let TypeScript know of the type for myPage
:
-
The easier way would be to use the
MyPage
interface exported from the page object definition file directly in the test file:// test/myTest.ts import { MyPage } from '../nightwatch/pages/myPage'; describe('myPage Demo', function() { it('navigate to page, assert the title and then click on myButton', function() { const myPage = browser.page.myPage() as MyPage; // if the above give error (which it ideally shouldn't), // first assert the type of `myPage()` to `unknown` and then to `MyPage`. const myPage = browser.page.myPage() as unknown as MyPage; myPage .navigate() .assertTitle() .click('@myButton'); }); });
-
The second and the correct way of achieving this (which wouldn't require you to assert the type of the page object every time you use it) would be the following:
-
Create a
types/nightwatch.d.ts
file in the root of your project, and declare the types for your page objects in it:// types/nightwatch.d.ts import 'nightwatch'; import {MyPage} from '../nightwatch/pages/myPage'; declare module 'nightwatch' { interface NightwatchCustomPageObjects { myPage(): MyPage; } }
-
After creating the above file, the type errors in your test file should go away. If they don't, go to
tsconfig.json
file present in the root of your project, and add"files"
and"include"
properties to it:// tsconfig.json { "compilerOptions": { ... }, "files": ["./types/nightwatch.d.ts"] // path to the recently created nightwatch.d.ts file. "include": ["test", "nightwatch"] // directories in which your nightwatch tests are present, where the above type declaration file should be applied. }
-
Make sure your
nightwatch/tsconfig.json
extends the roottsconfig.json
so that the above added properties are auto-applied in there:// nightwatch/tsconfig.json { "extends": "../tsconfig", "compilerOptions": { // all configs here }, "include": ["."] }
Following the above 3 steps, the types for your declared page objects should appear automatically when you do
browser.page.myPage()
. -
If you're using the root tsconfig.json
file to compile your code and can't add test directory to the "include" property, create a new tsconfig.json file in the directory where your tests are present and set it to the following:
// test/tsconfig.json
{
"extends": "../tsconfig", // path to the root tsconfig
"include": ["."], // apply the declaration file to the current directory
}
But make sure that the "files" property is still present in the root tsconfig.json
.
2. Should I remove the "compilerOptions" from nightwatch/tsconfig.json
if they're the same as my root tsconfig.json
?
Ideally, you should not remove or modify any config present in "compilerOptions" from nightwatch/tsconfig.json
file, unless you absolutely need to. These are the best possible configs that are used by ts-node
while running Nightwatch tests and changing these might affect your test runs.