A production-style end-to-end test automation framework built with Playwright and TypeScript, demonstrating the architecture I use professionally: Page Object Model, custom fixtures, data-driven testing, tag-based suites, and cross-browser-ready CI with GitHub Actions.
Target application: saucedemo.com — a stable public demo e-commerce app.
├── .github/workflows/playwright.yml # CI: type-check, test run, report artifacts
├── playwright.config.ts # Projects, retries, reporting, trace strategy
├── src/
│ ├── pages/ # Page Object Model
│ │ ├── base.page.ts # Shared header/cart/session behaviour
│ │ ├── login.page.ts
│ │ ├── inventory.page.ts
│ │ ├── cart.page.ts
│ │ └── checkout.page.ts # Info → overview → completion journey
│ ├── fixtures/
│ │ └── pages.fixture.ts # Page-object DI + `loggedIn` session fixture
│ └── data/
│ ├── users.data.ts # Test users + data-driven negative credentials
│ └── checkout.data.ts
└── tests/
├── login.spec.ts # Auth: positive, locked-out, negatives, slow user, session
├── inventory.spec.ts # Catalogue, all four sort orders, known-defect guard
├── cart.spec.ts # Badge, add/remove, persistence
└── checkout.spec.ts # Full E2E purchase + totals consistency + field validation
- Page objects expose locators and actions, never assertions. Assertions stay in the specs, so the same page object serves positive and negative scenarios.
- Custom fixtures over
beforeEachboilerplate. Tests declareloggedInand receive an authenticated session; page objects are injected rather than constructed in every file. - Data-driven negatives. Invalid-credential cases are generated from a data table — one reported test per case, one line to add a new one.
- Tag-based suites.
@smokeruns the critical path in under a minute;@regressionis the full suite, run on every push and pull request. - Cross-browser by configuration, Chromium by default. Browser coverage is a one-line project entry in
playwright.config.ts, so Firefox and WebKit can be enabled whenever a browser-specific regression justifies them. CI runs Chromium alone to keep the feedback loop short. - Stable selectors. All locators use the app's
data-testattributes (configured viatestIdAttribute), never brittle CSS chains or XPath. - CI flake strategy. One retry on CI only, with trace/video captured on failure — local runs stay strict so flakiness gets fixed, not hidden.
- Known defects are pinned by tests, not tribal knowledge. SauceDemo's
problem_userserves a deliberately broken catalogue; a test pins that behaviour so it stays visible in the report. If the application is ever corrected the test fails and forces the decision — a skipped test would rot silently instead.
npm ci
npx playwright install --with-deps chromium
npm test # full suite
npm run test:smoke # @smoke tests only
npm run test:regression # @regression tests only
npm run test:headed # visible browser (headless is the default)
npm run test:ui # Playwright UI mode
npm run report # open the HTML reportEvery push and pull request type-checks the codebase, runs the suite on Chromium, and uploads the HTML report as a build artifact. The pipeline is cross-browser ready — adding Firefox or WebKit is a project entry in playwright.config.ts — but runs a single browser by default, because a browser matrix multiplies runner time for coverage this application does not need. The workflow is also manually triggerable from the Actions tab.
I'm a Test Automation Engineer with years of experience building automation frameworks (Playwright, Nightwatch.js, Selenium) and CI pipelines that have cut release cycle times by ~30%. This repo mirrors the patterns from my professional work, which lives in private company repositories.