1

I have a Page Object Model that is handling browser specification but when I tried to assign a new context from browser and assign to page is not working as expected because is an async method this way is not possible to create the object without initialize with a method after the object creation.

Is possible to assing async value in the constructor?

What is the best pattern?

export class AppPage {
  readonly browser: Browser;
  readonly page: Page;
  readonly context: BrowserContext;

  public currentPage: Page;


  constructor(browser: Browser) {
    this.browser = browser;
    // Is not possible to assign in the constructor because these are async methods
    // this.context = await this.browser.newContext();
    // this.page = await context.newPage();
  }

  async initialize() {
    const context = await this.browser.newContext();
    const page = await context.newPage();
    // Is requiring to call an extra method in order ot initialized
    this.currentPage = page;
  }

  async goto() {
    await this.currentPage.goto('https://playwright.dev');
  }
}

This is the current implementation is required to initialize the page assingation.

test('test using context with page object model', async ({ browser }) => {
  const app = new AppPage(browser);
  await app.initialize();

  await app.goto();
});

Stackblitz live sample.

Tabares
  • 4,083
  • 5
  • 40
  • 47
  • As discussed in the [previous question](https://stackoverflow.com/a/75756331/6243352), don't forget to keep track of `this.context` so you can close it later. Have you read through [Async/Await Class Constructor](https://stackoverflow.com/questions/43431550/async-await-class-constructor)? Much ink has been spilled on this topic already (341k views, 24 answers, many ideas and patterns to consider). What are your goals/concerns here? Do you have to use a class or can you write a custom function(s)? You could return `this` from the `initialize` func and chain it in one line. – ggorlen Mar 17 '23 at 16:33
  • See also [Is it bad practice to have a constructor function return a Promise?](https://stackoverflow.com/questions/24398699/is-it-bad-practice-to-have-a-constructor-function-return-a-promise) – ggorlen Mar 17 '23 at 16:38
  • Perhaps what you're looking for is something like [global setup and teardown](https://playwright.dev/docs/test-advanced#global-setup-and-teardown) – ggorlen Mar 17 '23 at 19:03
  • 1
    Make `initialise` a `static` method that returns a promise for a new instance. – Bergi Mar 18 '23 at 12:16
  • The best way to implement in Plawright is using the configuration. That is provided by the framework export default: defineConfig({ use: { storageState: 'state.json', }, });. https://stackblitz.com/edit/jfgreffier-try-playwright-test-vhkqb7?file=playwright.config.ts,tests%2Fexample.spec.ts – Tabares Mar 24 '23 at 17:13
  • With this configuration is easy use page and avoid initialize method. constructor(page: Page) { this.page = page; } – Tabares Mar 24 '23 at 17:15

1 Answers1

1

One way to tackle this, is to let the constructor store a promise for the initialisation part and then every other method must await that promise first. This means that context and currentPage are no longer properties of your instance, but are the resolution values of the above promise. They can of course be retrieved from that promise multiple times, but always asynchronously.

export class AppPage {
  readonly browser: Browser;

  constructor(browser: Browser) {
    this.browser = browser;
    this.ready = this.initialize();
  }

  async initialize() {
    const context = await this.browser.newContext();
    const page = await context.newPage();
    return {context, page};
  }

  async goto() {
    const {page} = await this.ready;
    await page.goto('https://playwright.dev');
  }

  async close() {
    const {context} = await this.ready;
    await context.close();
  }
}

The test script can now omit the call to initialize:

test('test using context with page object model', async ({ browser }) => {
  const app = new AppPage(browser);
  await app.goto();
});
trincot
  • 317,000
  • 35
  • 244
  • 286
  • The best way to implement in Plawright is using the configuration. That is provided by the framework. – Tabares Mar 24 '23 at 17:10