End-to-end test automation for saucedemo.com using Playwright and TypeScript.
| Tool | Purpose |
|---|---|
| Playwright | Browser automation & assertions |
| TypeScript | Type-safe test code |
| @faker-js/faker | Randomised test data generation |
| Docker | Reproducible CI execution |
| Husky + lint-staged | Pre-commit formatting |
| Prettier | Code formatting |
npm install
npx playwright install # download browsers
npm test # all tests, headless
npm run test:headed # headed, 4 workers
npm run test:ui # Playwright UI mode
npm run report # open last HTML report# requires Docker Desktop to be running
npm run docker:build
npm run docker:test # results mounted to playwright-report/ and test-results/├── dtos/ # Zod schemas + inferred TypeScript types
│ ├── user.dto.ts
│ ├── product.dto.ts
│ └── checkout.dto.ts
├── factories/ # Test data factories (faker-backed)
│ └── checkout.factory.ts
├── fixtures/ # Playwright test fixtures
│ ├── base-ui-test.ts # Authenticated — page object DI for all UI tests
│ └── base-unauth-ui-test.ts # Unauthenticated — for login-page tests
├── flows/ # Reusable multi-step workflows
│ └── checkout-flow.ts
├── pages/ # Page Object Models
│ ├── base-page.ts
│ ├── login-page.ts
│ ├── cart-page.ts
│ ├── components/
│ │ └── burger-menu.ts
│ ├── products/
│ │ ├── products-page.ts
│ │ └── product-details-page.ts
│ └── checkout/
│ ├── checkout-user-information-page.ts
│ ├── checkout-details-page.ts
│ └── checkout-success-page.ts
├── utils/ # Shared test helpers
│ ├── wait-utils.ts
│ ├── performance-utils.ts
│ ├── checkout-utils.ts
│ └── screenshot-utils.ts
├── types/ # Shared TypeScript types
│ └── sort-types.ts
├── data/ # Test data repository
│ ├── test-data/
│ │ ├── user-data.ts
│ │ ├── product-data.ts
│ │ ├── error-messages.ts
│ │ └── checkout-data.ts
│ └── screenshots/ # Visual baseline images
├── tests/
│ ├── auth.setup.ts # One-time session save → .auth/state.json
│ ├── ui-tests/
│ │ ├── authentication/
│ │ ├── cart/
│ │ ├── checkout/
│ │ ├── products/
│ │ └── navigation/
│ ├── visual-tests/
│ └── performance/
├── .github/
│ ├── agents/ # Custom Copilot agents (see below)
│ └── skills/ # Custom Copilot skills (see below)
├── playwright.config.ts
└── tsconfig.json
Path aliases (defined in tsconfig.json):
| Alias | Resolves to |
|---|---|
@pages/* |
pages/* |
@fixtures/* |
fixtures/* |
@utils/* |
utils/* |
@flows/* |
flows/* |
@dtos/* |
dtos/* |
@factories/* |
factories/* |
@data/* |
data/* |
| Type | Location | Runs on |
|---|---|---|
| UI E2E | tests/ui-tests/ |
Chromium + Safari Mobile |
| Visual regression | tests/visual-tests/ |
Chromium + Safari Mobile |
| Performance | tests/performance/ |
Chromium only |
npm run test:visual # visual tests only
npm run test:visual:update # regenerate visual baselines
npm run test:performance # performance tests only
npx playwright test --grep @cart # by tag
npx playwright test tests/ui-tests/authentication # by foldertests/auth.setup.ts runs once before all test projects. It logs in and saves the browser session to .auth/state.json. Every test project loads that session via storageState — all tests start already logged in.
- For tests that require an unauthenticated browser (login-page tests, redirect checks), import
testfrom@fixtures/base-unauth-ui-test. - Never call
loginPage.goto()orloginPage.login()inside abeforeEach— navigate directly to the page under test (e.g.productsPage.goto()).
| Tag | Meaning |
|---|---|
@authentication, @cart, @checkout, @products, @navigation |
Feature area |
@positive, @negative, @edge-case |
Scenario type |
@visual, @performance |
Test category |
@known-issue |
Documents a real app bug; excluded from blocking CI |
@known-issue tests are excluded from the default npm test run via --grep-invert @known-issue.
Priority order enforced in all page objects:
[data-test="…"]attributes (most stable)- Role-based (
getByRole) - CSS class selectors — legacy only, never used in new code
All validator.expect*() methods use Playwright's auto-retrying expect() API (toBeVisible, toHaveText, toHaveCount). No waitForNetworkIdle or waitForTimeout are used in page objects.
Key settings in playwright.config.ts:
| Setting | Value | Reason |
|---|---|---|
fullyParallel |
true |
Each test gets an isolated context |
retries |
1 (local) / 2 (CI) |
Absorbs transient failures without masking real ones |
screenshot |
only-on-failure |
Captured automatically without overhead |
trace |
retain-on-failure |
Available on first failure without overhead |
timeout |
15 000 ms |
Appropriate for a fast demo app |
Projects: Desktop Chromium (1280×720) and iPhone 14 Pro / Safari Mobile (393×852).
Visual diff tolerance: maxDiffPixels: 100, threshold: 0.1%.
Baselines are stored under data/screenshots/. Visual tests are generated on Windows — run baseline updates on the same OS to avoid rendering differences.
- Prettier —
npm run formatformats all.tsfiles - Husky — runs Prettier automatically on staged files before every commit (enabled by
npm installvia thepreparescript) - TypeScript strict mode — all implicit
anytypes are errors - Explicit access modifiers —
public/private/readonlyon every class member
Custom agents and skills for assisted development live in .github/:
| Agent | Role | When to use |
|---|---|---|
@Planning Assistant |
Read-only architecture review | Before writing any code — get a file-level plan first |
@UI Test Builder |
Implement and modify tests | Writing new tests or editing existing specs |
@Test Optimizer |
Improve existing tests | Fix flaky, slow, or incorrect tests |
Each agent has explicit scope guards and will redirect to the correct agent when asked outside its scope.
| Skill | Purpose |
|---|---|
auth-pattern-check |
Detects forbidden loginPage.login() calls in tests — enforces storageState convention |
run-and-verify |
Runs a specific test file and reports pass/fail with context after any code change |
selector-audit |
Audits page objects for non-data-test selectors and flags CSS class usage |
- SauceDemo stores state in the browser only — no backend database; each test starts clean
- Product data, prices, and error messages are static (no dynamic API)
- Performance threshold of 2 000 ms is appropriate for the demo environment
- Visual baselines are generated on Windows; CI must also run on Windows to avoid rendering differences
- No API layer to set up state programmatically — all setup is done via the UI
- Visual tests cannot run on Linux/Mac CI without regenerating baselines
- Performance tests cover the products page only
@known-issuetests document confirmed app bugs and are excluded from blocking CI
| Tool | Purpose |
|---|---|
| Playwright | Browser automation & assertions |
| TypeScript | Type-safe test code |
| Docker | Reproducible CI execution |
| Husky + lint-staged | Pre-commit formatting |
| Prettier | Code formatting |
npm install
npm test # all tests, headless
npm run test:headed # headed, 4 workers
npm run test:ui # Playwright UI mode
npm run report # open last HTML report# requires Docker Desktop to be running
npm run docker:build
npm run docker:test # results mounted to playwright-report/ and test-results/├── fixtures/ # Playwright test fixtures
│ └── base-ui-test.ts # Page object DI for all UI tests
├── flows/ # Reusable multi-step workflows
│ └── checkout-flow.ts
├── pages/ # Page Object Models
│ ├── base-page.ts
│ ├── login-page.ts
│ ├── cart-page.ts
│ ├── components/
│ │ └── burger-menu.ts
│ ├── products/
│ └── checkout/
├── utils/ # Shared test helpers
│ ├── wait-utils.ts
│ ├── performance-utils.ts
│ ├── checkout-utils.ts
│ └── screenshot-utils.ts
├── types/ # Shared TypeScript types
│ └── sort-types.ts
├── data/ # Test data repository
│ ├── test-data/
│ │ ├── user-data.ts
│ │ ├── product-data.ts
│ │ ├── error-messages.ts
│ │ └── checkout-data.ts
│ └── screenshots/ # Visual baseline images
├── tests/
│ ├── ui-tests/
│ │ ├── authentication/
│ │ ├── cart/
│ │ ├── checkout/
│ │ ├── products/
│ │ └── navigation/
│ ├── visual-tests/
│ └── performance/
├── .github/
│ ├── agents/ # Custom Copilot agents
│ └── workflows/
├── playwright.config.ts
└── tsconfig.json
Path aliases (defined in tsconfig.json):
@/*→ project root (e.g.@/pages/login-page)@data/*→data/*
| Type | Location | Runs on |
|---|---|---|
| UI E2E | tests/ui-tests/ |
Chromium + Safari Mobile |
| Visual regression | tests/visual-tests/ |
Chromium + Safari Mobile |
| Performance | tests/performance/ |
Chromium only |
npm run test:visual # visual tests only
npm run test:performance # performance tests only
npx playwright test --grep @cart # by tag
npx playwright test tests/ui-tests/authentication # by folder| Tag | Meaning |
|---|---|
@authentication, @cart, @checkout, @products, @navigation |
Feature area |
@positive, @negative, @edge-case |
Scenario type |
@visual, @performance |
Test category |
@known-issue |
Documents a real app bug; excluded from blocking CI |
All locators, actions, and assertions live in pages/. Tests never use raw locators directly.
BasePageprovidesgoto()andcompareScreenshot()via the template method pattern- Shared UI elements (burger menu) are extracted into
pages/components/
fixtures/base-ui-test.ts extends Playwright's test with typed page-object fixtures. Tests declare only what they need; setup and teardown are automatic.
flows/checkout-flow.ts wraps multi-step workflows that span multiple pages. Tests that aren't about the checkout steps themselves call a single flow method rather than repeating the setup sequence.
Test data is centralised in data/test-data/. Parameterised test cases use forEach loops to avoid duplicating test logic for every combination.
negativeTestCases.forEach(({ name, credentials, expectedError }) => {
test(name, async ({ loginPage }) => {
await loginPage.login(credentials);
await loginPage.verifyErrorMessageIsDisplayed(expectedError);
});
});Stateless helpers in utils/ cover:
WaitUtils—waitForDomContentLoaded()for post-interaction stabilisationPerformanceUtils— wall-clock timer withassertLoadTime()ScreenshotUtils— configurable retry wrapper aroundtoHaveScreenshot()with font-ready stabilisationCheckoutUtils— shared checkout data helpers
Priority order enforced in all page objects:
[data-test="…"]attributes (most stable)- Role-based (
getByRole) - CSS class selectors (legacy only, not used in new code)
All verify*() methods use Playwright's auto-retrying expect() API (toBeVisible, toHaveText, toHaveCount). No waitForNetworkIdle or waitForTimeout are used in page objects.
Key settings in playwright.config.ts:
| Setting | Value | Reason |
|---|---|---|
fullyParallel |
true |
Each test gets an isolated context |
retries |
0 (local) |
Set to 1 in CI for transient failures |
screenshot |
on |
Change to only-on-failure for faster local runs |
trace |
retain-on-failure |
Available on first failure without overhead |
timeout |
15000ms |
Appropriate for a fast demo app |
Projects: Desktop Chromium (1280×720) and iPhone 14 Pro / Safari Mobile (393×852).
- Prettier —
npm run formatformats all.tsfiles - Husky — runs Prettier automatically on staged files before every commit (
npm installenables hooks via thepreparescript) - TypeScript strict mode — all implicit
anytypes are errors - Explicit access modifiers —
public/privateon every method
- SauceDemo stores state in the browser only — no backend database; each test starts clean
- Product data, prices, and error messages are static (no dynamic API)
- Performance threshold of 2000ms is appropriate for the demo environment
- Visual baselines are generated on Windows; CI must also run on Windows to avoid rendering differences
- No API layer to set up state programmatically — all setup is done via the UI
- Visual tests cannot run on Linux/Mac CI without regenerating baselines
- Performance tests cover the products page only
@known-issuetests document 6 confirmed app bugs and are excluded from blocking CI