You may be familiar with the PageObject Model (POM) and how CodeceptJS supports it out of the box (see Page Objects | CodeceptJS). Using POM with just a few Page Objects (POs) is simple, but as your project grows and you accumulate hundreds or more POs, managing them can become unwieldy. Injecting POs into the configuration file can clutter it up, and the step.d.ts
file can suffer from the same problem.
To manage POs effectively in this situation, here are two possible solutions:
- Export your POs as a module, as shown in the following example from https://github.com/kobenguyent/codeceptjs-playwright-fun/blob/main/pages/Login.ts:
const { I } = inject();
export const LoginPage = {
text: {
signInBtn: 'Sign in',
usernameTbx: 'Username or email address',
passwordTbx: 'Password'
}
}
Create a file called index.ts
in the folder that contains all your POs, with the following code:
export { LoginPage } from 'path/to/PO'
With this setup, you can easily access the POs in your test file:
import { LoginPage } from './path/to/PO'
// Use the PO
LoginPage.text.signInBtn
Your IDE should automatically suggest importing the PO when you type LoginPage
.
Note: if you want to have a testscript to automatically export all POs in index.ts
, you could try this exportPO.ts · GitHub
- Another approach is to use the Singleton Pattern to get the POs. First, create a Singleton Injector (see svelte-fun/Injector.ts at main · kobenguyent/svelte-fun · GitHub):
import Bookstore from './pages/Bookstore'
class Injector {
private static isInitialized: boolean
private static bookstorePage: Bookstore
constructor() {
Injector.isInitialized = true
}
public static getPageObjects() {
if (!this.isInitialized) {
this.bookstorePage = new Bookstore()
return {
bookstorePage: this.bookstorePage,
}
}
}
}
export = Injector
In your test file (see svelte-fun/Books_test.ts at main · kobenguyent/svelte-fun · GitHub), you can then import the injector and retrieve the POs:
import Injector from './Injector'
const { I } = inject()
const { bookstorePage } = Injector.getPageObjects()
Feature('Bookstore')
Before(() => {
I.amOnPage('/')
})
Scenario('Bookstore is showing', async () => {
await I.checkVisualDifferences('bookstore.png')
})
Scenario('Adding new book', async () => {
bookstorePage.addNewBook()
I.see('This is a new book')
})
Scenario('Add a book to cart', async () => {
bookstorePage.addNewBook()
bookstorePage.buyBook()
I.see('Total Price: 120')
})
These are two possible solutions to managing POs effectively with CodeceptJS. If you have another approach, we’d love to hear about it!