Skip to content
2 min read

Building a TDD UI + API Framework in Playwright

How I structure a maintainable Playwright + TypeScript framework that covers both UI and API layers, wired into CI/CD from day one.

  • Playwright
  • TypeScript
  • Testing

A good automation framework is less about the tool and more about the structure around it. Here's the shape I keep coming back to for Playwright projects that need to cover both the UI and the API.

Start with the layers

I split tests into clear layers so each one has a single responsibility:

  • UI specs drive the browser through user journeys.
  • API specs hit endpoints directly for fast, deterministic coverage.
  • Fixtures wire up shared setup (auth, test data, page objects).
import { test as base } from "@playwright/test";
import { LoginPage } from "./pages/login.page";
 
export const test = base.extend<{ loginPage: LoginPage }>({
  loginPage: async ({ page }, use) => {
    await use(new LoginPage(page));
  },
});

Page Object Model, but lightweight

The Page Object Model keeps selectors out of specs, but it's easy to over-engineer. I keep page objects thin — locators and a few intent-revealing methods — and let the specs describe the behaviour.

CI/CD from day one

The framework only earns its keep when it runs on every change. I wire Playwright into GitHub Actions early so the team gets fast feedback and a shift-left safety net rather than a slow, end-of-sprint regression crunch.

The best time to add CI is when you have one passing test. The second best time is now.

That's the core of it: clear layers, thin page objects, and automation that runs continuously.