-2

I'm working with Playwright and TypeScript and have noticed that Page Object Models (POMs) always require a page parameter for initialization.

Since the page object is only available from the beforeEach block onwards, I'm forced to use this pattern:

test.describe('describe name', () => {
  let potentialSnippetPage: PotentialSnippetPage

  test.beforeEach(async ({ page }) => {
    potentialSnippetPage = new PotentialSnippetPage(page);
  });

  test('test title', async () => {
    await potentialSnippetPage.someMethod();
  });
});

This approach feels cumbersome because I have to declare the POM variable with let first, then initialize it properly in the beforeEach block with the actual page object.

Is there a way to handle POM initialization in Playwright without beforeEach and let?

I'm looking for patterns that would reduce the repetitive setup while maintaining type safety.

0

2 Answers 2

2

This precise case is covered in the docs, using fixtures:

import { test as base } from '@playwright/test';
import { PotentialSnippetPage } from './potential-snippet-page';

const test = base.extend<{ potentialSnippetPage: PotentialSnippetPage }>({
  potentialSnippetPage: async ({ page }, use) => {
    const potentialSnippetPage = new PotentialSnippetPage(page);
    await potentialSnippetPage.goto();
    await use(potentialSnippetPage);
  },
});

test.describe('describe name', () => {
  test('test title', async ({ potentialSnippetPage }) => {
    await potentialSnippetPage.someMethod();
  });
});

Where const test = ... can be exported/imported and hidden away in another file, if desired.

That said, the let/beforeEach pattern isn't that bad, IMO. It's a ubiquitous idiom in the Jest world, which Playwright was modeled after.


Related questions:

Sign up to request clarification or add additional context in comments.

Comments

-1

This is a pattern I've used quite successfully. Create a dedicated function and "test context" object, which encapsulates the hooks and data required for each test case:

type TestContext = { potentialSnippetPage: PotentialSnippetPage };

function createTestContext(): TestContext {
  let context: Partial<TestContext> = {};

  test.beforeEach(async ({ page }) => {
    context.potentialSnippetPage = new PotentialSnippetPage(page);
  });

  // Fudge the type system, trusting that the test hooks will properly hydrate the object.
  return context as TestContext;
}

Then in your test:

test.describe('describe name', () => {
  let context = createTestContext();

  test('test title', async () => {
    await context.potentialSnippetPage.someMethod();
  });
});

1 Comment

Shouldn't something that belongs to a test, in this case test.beforeEach(), only be used within a test context? Yes, it is clear that its function is only used within a test, but still. However, this approach requires an independent type to be defined for each POM. I had already considered a similar approach, but then rejected it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.