Skip to content

Introduction

@duplojs/playwright is a light layer above Playwright to structure a test suite around the tested website.

The idea is not to replace Playwright, but to add a more readable model to it:

  • a Website to carry the global test context
  • Page objects for navigable screens
  • Component objects for reusable interface fragments
  • business helpers to avoid repeating the same intentions everywhere

What the lib brings

  • a stable way to organize the test suite as it grows
  • a vocabulary closer to the tested website than to a set of locators
  • reusable interactions and assertions
  • more expressive Playwright steps in reports

The philosophy

The goal is to test a website with business objects, not to write every test directly against page.locator(...).

In concrete terms, instead of having selectors and helpers scattered across each test, the goal is to:

  • centralize the website structure
  • reuse frequent behaviors
  • keep tests intention-oriented

Minimal example

ts
import { Actions, 
createComponent
,
createPage
,
createWebsite
, type Website } from "@duplojs/playwright";
import
test
from "playwright/test";
interface TestFixtures {
website
: Website;
} const
testClient
=
test
.
extend
<TestFixtures>({
async
website
({
page
,
context
},
use
) {
const
website
=
createWebsite
({
playwrightPage
:
page
,
playwrightBrowserContext
:
context
,
envConfig
: {
baseUrl
: "https://example.com",
}, }); await
use
(
website
);
}, }); const
SearchForm
=
createComponent
(
"searchForm", {
getMainElement
({
body
}) {
return
body
.
locator
("[data-search-form]");
},
getElements
({
mainElement
}) {
return {
input
:
mainElement
.
locator
("input"),
submitButton
:
mainElement
.
locator
("button[type='submit']"),
}; }, }, ); const
HomePage
=
createPage
(
"home", {
makePath
() {
return "/"; },
getMainElement
({
body
}) {
return
body
.
locator
("main");
},
components
: [
SearchForm
],
}, );
testClient
("home page", async({
website
}) => {
const
homePage
= await
website
.
iNavigateTo
(
HomePage
);
const
searchForm
= await
homePage
.
iWantToSeeComponent
("searchForm");
await Actions.
fill
(
searchForm
, "input", "superValue");
await Actions.
click
(
searchForm
, "submitButton");
});

What is happening here

  • the extended Playwright client prepares createWebsite(...) once.
  • createPage(...) describes a navigable screen.
  • the test gets website from this client.
  • website.iNavigateTo(...) lets you speak in terms of pages, not only in terms of URLs.

Released under the MIT license.