Remix.run Logo
emmanueloga_ 2 days ago

Page Object Models trade off clarity for encapsulation. Concrete example [1]. They can make tests look "cleaner" but often obscure what's actually happening. For example:

    await page.getStarted(); // what does this actually do?
vs

    await page.locator('a', { hasText: 'Get started' }).first().click();
    await expect(page.locator('h1', { hasText: 'Installation' })).toBeVisible();
The second version is explicit and self-documenting. Tests don't always benefit from aggressive DRY, but I've seen teams adopt POMs to coordinate between SDETs and SWEs.

--

1: https://playwright.dev/docs/pom

sa46 2 days ago | parent | next [-]

> Page Object Models trade off clarity for encapsulation [and] obscure what's actually happening.

This argument also applies to using a function for abstraction.

I've just written a few dozen e2e tests with Playwright. The code looks like:

    await invoiceDetailPage.selectCustomer(page, 'Acme');
    await invoiceDetailPage.selectCustomerPoNumber(page, '1234567890');
    await invoiceDetailPage.setCustomerReleaseNumber(page, '987654321');
    ...10-15 more lines
Each of those lines is 3 to 20 lines of Playwright code. Aggressive DRY is bad, but Page Object Models are usually worth it to reduce duplication and limit churn from UI changes.
zikani_03 2 days ago | parent | prev | next [-]

I am working on a tool[1] to try to make working with playwright a bit more ergonomic and approachable. Sorry to shamelessly plug but I'd love feedback on if it is even a good idea/direction.

Hadn't considered the Page Object Model and will definitely have to consider how to incorporate that for those who want to do things that way.

---

1: https://github.com/zikani03/basi

littlecranky67 2 days ago | parent | prev | next [-]

I assume you do not use TypeScript but JavaScript? With TS your POs are basically discoverable with source-code completion and statically typed.

   await page.getStartedLink.click()
   expect(page.installationHeadline).toBeVisible()
Is much more readable, and on typing "page." you will see what props on your PO are available.

Another note on your specific example: You are probably in the US and only have a single-language project. I am a Frontend Contractor in Europe and for the past 10 years didn't have a single project that was single language, hence the "hasText" selector would always be off-limits. Instead, very often we used the react-intl identificator as text content for code execution - which would make the above example look much more unreadable without POs, while with POs the code looks the same (just your PO looks different).

michalc 2 days ago | parent | prev | next [-]

Very much agree.

It was a few years ago, and very AngularJS focused, but I posted something along these lines: https://charemza.name/blog/posts/angularjs/e2e/consider-not-...

In summary: having thing look cleaner at a glance is not helping if you’re (almost) always going to need to do more than glancing

kleyd 2 days ago | parent | prev | next [-]

It's not a trade-off of clarity just to save developers some extra typing. It's actually improving the clarity by bringing the thing you care about to the foreground: the getting started page having a table of contents with specific items.

pintxo 2 days ago | parent | prev | next [-]

Well, if you have tests with 100+ lines of such explicitness, it becomes really hard to see the high level picture of „what is tested here“.

As usually, there is a balance to be found.

rullopat 2 days ago | parent | prev [-]

So if you have a page that is common between, lets say, 30-40 tests, you prefer to copy/paste the new selectors everywhere?