typescriptplaywright

Difference in declaring variables


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

Solution

  • 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();