This current implementation is working very well for certain classes
export class PlaywrightDevPage {
readonly page: Page;
constructor(page: Page) {
this.page = page;
}
async goto() {
await this.page.goto('https://playwright.dev');
}
}
test('test using page with page object model', async ({ page }) => {
const devPage = new PlaywrightDevPage(page);
await devPage.goto();
});
But I want to implement a new context in the Page Object Models, but the browser is closed when the object is created.
export class AppPage {
readonly browser: Browser;
constructor(browser: Browser) {
this.browser = browser;
}
async goto() {
// Remove storage state to simplify the sample,
// the main problem is with browser.newContext: Browser has been closed
// const context = await this.browser.newContext({
// storageState: './auth.json',
// });
const context = await this.browser.newContext();
const page = await context.newPage();
await page.goto('https://playwright.dev');
}
}
test('test using context with page object model', async ({ browser }) => {
const devPage = new AppPage(browser);
devPage.goto();
});
It is throwing the following error:
browser.newContext: Browser has been closed
What is the way to implement context in Page Object Models?
In your top example,
test('test sample', async ({page}) => {
devPage = await new PlaywrightDevPage(page);
devPage.goto();
});
should be
test('test sample', async ({page}) => {
const devPage = new PlaywrightDevPage(page);
await devPage.goto();
});
Your constructor isn't async
, so there's no promise to await
. However, devPage.goto()
is async
and should be await
ed.
In your second example, you're passing a browser
as a page
. That's the wrong object. You can destructure the browser
in the test block callback and use that if you want:
test('test sample', async ({browser}) => {
const devPage = new AppPage(browser);
await devPage.goto();
});
Modify the class to keep track of context
and page
so you can use them in other functions and eventually close them (and ensure this.page
exists when you run this.page.goto
):
export class AppPage {
readonly browser: Browser;
constructor(browser: Browser) {
this.browser = browser;
}
async goto() {
this.context = await this.browser.newContext({
storageState: './auth.json',
});
this.page = await this.context.newPage();
await this.page.goto('https://playwright.dev');
}
}
As a possible design improvement, it might make sense to create a separate function that handles creating context and the page. It's a little odd that goto
is responsible for these things, breaking the single-responsibility principle.