I have some kind of class that saves an elements f.e. for "Home" page.
export class HomePage {
protected static readonly passwordInputField = ()=> new InputElement(getPage().locator("#Password"));
protected static readonly enterButton = new ButtonElement(getPage().getByRole("button", { name: "Enter" }));
}
getPage() is defined in my fixtures class
export type TestOptions = {
customFixture: string;
};
let customPage: Page;
export const test = base.extend<TestOptions>({
customFixture: [async ({ page }, use) => {
customPage = page;
await page.goto("/");
await use("");
await page.close();
}, {auto: true}]
});
export function getPage(): Page {
return customPage;
}
The question is next, why is declaring passwordInputField correct, but when I declare enterButton I recieve error:
TypeError: Cannot read properties of undefined (reading 'getByRole')playwright
The issue lies in when the code inside the static properties gets executed.
the passwordInputField
works because it is declared as a function (arrow function), so getPage()
is only called when you invoke HomePage.passwordInputField()
. This delays execution until customPage is already set.
While, enterButton
is initialized immediately when the HomePage class is loaded.
In other word, this line protected static readonly enterButton = new ButtonElement(getPage().getByRole("button", { name: "Enter" }));
executes immediately when the class is loaded, trying to call getPage().getByRole()
at module evaluation time, and at this point/time, customPage
is likely still undefined.
To resolve it Make enterButton
a function as well (just like passwordInputField
):
protected static readonly enterButton = () => new ButtonElement(getPage().getByRole("button", { name: "Enter" }));
Then call it like:
HomePage.enterButton().click();