Skip to content

Repository files navigation

Playwright UI Automation Framework (TypeScript)

Playwright Tests Report Playwright TypeScript

132 test executions across Chromium, Firefox, WebKit and mobile Chrome — sharded 4 ways in CI, with the merged HTML report published on every run.

A production-shaped UI automation framework, not a tutorial suite. It covers authentication, catalogue sorting, cart state and end-to-end checkout against a public e-commerce demo application.

📊 Open the latest test report →


What this demonstrates

Capability Where to look
Page Object Model with a shared base class src/pages/
Component objects for cross-page UI src/pages/components/HeaderComponent.ts
Custom fixtures and dependency injection src/fixtures/test-fixtures.ts
Data-driven negative testing tests/auth/login.spec.ts
Test data builders src/data/checkout.ts
Environment configuration src/config/env.ts
Tag-based suite selection (@smoke / @regression / @e2e) any spec file
Cross-browser + mobile projects playwright.config.ts
CI sharding, report merging, Pages publishing .github/workflows/ci.yml

Test coverage

Suite Tests Focus
Authentication 7 Valid sign-in, locked-out account, four rejected-credential cases, sign-out, direct-URL access control
Product catalogue 6 Item count, all four sort orders, set-preservation after re-sorting
Shopping cart 7 Badge counting, add/remove, state persistence across navigation, price consistency
Checkout 7 Full end-to-end order, total arithmetic, three required-field validations, cancel behaviour, post-order cart state
Configuration 5 Environment-variable resolution — regression guard for the CI defect below

33 tests × 4 browser projects = 132 executions per run.

Quick start

npm ci
npx playwright install
npm test
npm run test:smoke        # tagged @smoke only — fast pipeline gate
npm run test:regression   # full regression pass
npm run test:chromium     # single browser
npm run test:headed       # watch it run
npm run test:ui           # Playwright UI mode for debugging
npm run report            # open the HTML report
npm run typecheck         # strict TypeScript, no test execution

No .env file is required — every setting has a working default. Copy .env.example to .env to point the suite at a different host.

Structure

src/
├── config/env.ts              # environment resolution (dev / staging / prod)
├── data/                      # typed test data and builders
│   ├── users.ts               # accounts by intent, plus expected error copy
│   ├── products.ts            # catalogue and sort options
│   └── checkout.ts            # checkout detail builder
├── fixtures/test-fixtures.ts  # page-object injection + authenticated session
└── pages/                     # page objects
    ├── BasePage.ts            # navigation, readiness, money parsing
    ├── LoginPage.ts
    ├── InventoryPage.ts
    ├── CartPage.ts
    ├── CheckoutPage.ts        # three checkout steps
    └── components/            # header/menu shared across pages
tests/
├── auth/       inventory/     cart/       checkout/

Design decisions

Page objects return locators; specs make the assertions. A page object that asserts internally produces failure messages pointing at a helper three files away. Keeping expect in the spec means a red test names the business rule that broke.

Money is compared in cents. Floating-point currency arithmetic makes 0.1 + 0.2 !== 0.3 surface as a spurious checkout failure. BasePage.parseMoneyToCents parses the rendered string once and every comparison is integer.

Prices are read from the page, never hardcoded. The catalogue data selects which product to use; the expected value comes from the application. A price change is then a content update, not a suite-wide failure.

Data-driven cases generate one test each. The four rejected-login scenarios are separate tests rather than a loop inside one test, so a regression names the exact input that broke instead of collapsing four scenarios into one red line.

Retries are diagnostic, not cosmetic. CI retries twice to absorb public-demo-site flakiness; anything that only passes on retry is reported as flaky rather than silently green.

Artefacts only on failure. Traces, screenshots and video are captured with retain-on-failure. Recording everything inflates CI storage without adding diagnostic value.

Teardown failures are not swallowed. The authenticated fixture resets cart state after each test, checking explicitly for a closed or signed-out page rather than wrapping the reset in a bare catch. A blanket catch hides a broken reset, which then surfaces as an unrelated test failing later — this actually happened while building the suite, and the explicit checks are the fix.

Defects found in the application under test

Two real issues surfaced while writing this suite:

  1. "Reset App State" leaves stale buttons. The action clears the cart badge but the catalogue continues showing "Remove" until the page is reloaded — the UI and the cart state disagree. The framework reloads after reset (HeaderComponent.resetAppState) so the defect cannot leak into the next test.

  2. The menu toggle's test id is unclickable. data-test="open-menu" is on the icon <img>, which the real <button> overlays, so clicking the documented test id fails with "intercepts pointer events". Those two locators target the button by id, with a comment explaining the exception.

A defect the pipeline caught that local runs could not

The very first CI run failed on all four shards while the identical suite passed on a developer machine — the useful kind of failure.

Cause. The workflow passes TEST_USER_PASSWORD: ${{ secrets.TEST_USER_PASSWORD }}. When that secret is not configured, GitHub interpolates an empty string rather than leaving the variable unset. The framework read it as process.env.TEST_USER_PASSWORD ?? 'secret_sauce', and ?? falls back only on null/undefined — never on ''. Every password went blank, so the app answered "Password is required" where the tests expected a credentials error.

Fix. envValue treats blank and whitespace-only values as unset, and every environment read now goes through it.

Guard. tests/config/env.spec.ts pins all five cases — unset, empty, whitespace, configured, and a value that legitimately contains spaces. Reproduce the original failure condition with:

TEST_USER_PASSWORD="" npm run test:chromium

This is the argument for running the suite in CI rather than trusting a green local run: the environment difference was the bug.

CI pipeline

typecheck ─→ test (4 parallel shards) ─→ merge reports ─→ publish to Pages
  • Runs on push, pull request, nightly at 18:00 UTC, and on demand with a suite selector
  • Each shard emits a blob report; a merge job stitches them into one HTML report
  • Blob reports upload even when tests fail — a failing run's report is the one worth reading
  • forbidOnly fails the build on a stray test.only
  • In-progress runs are cancelled when a newer commit lands on the same branch

Notes

  • The application under test is saucedemo.com, a public demo site published for automation practice. Its credentials are printed on its own sign-in page.
  • All three environment entries resolve to that single host. The pattern is the point — swapping in real dev/staging hosts is a one-line change per entry, and no test or page object needs to know which environment it runs against.
  • This repository contains no code, data, or credentials from any employer. Every line was written for this portfolio.

Licence

MIT

About

Playwright + TypeScript UI automation framework: Page Object Model, custom fixtures, data-driven tests, 4-way CI sharding and published HTML reports. 112 cross-browser test executions per run.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages