A clean, opinionated end-to-end test automation skeleton built with Playwright, Cucumber.js (BDD), TypeScript and Allure reporting.
It demonstrates a layered architecture (Driver → BasePage → Page Objects → Steps), a strict locator strategy, explicit waits, environment-driven config, and the imperative → declarative scenario style.
The full set of rules the project is built on lives in
.claude/CONVENTIONS.md. Read it before adding tests.
| Concern | Tool |
|---|---|
| Browser automation | Playwright (@playwright/test) |
| BDD runner | Cucumber.js (@cucumber/cucumber) |
| Language | TypeScript (run via ts-node, no build step) |
| Reporting | Allure (allure-cucumberjs) + Cucumber HTML |
| Config | dotenv |
The example tests run against SauceDemo.
.
├── features/ # Gherkin specs + step definitions
│ ├── login.feature # imperative example
│ ├── product-sort.feature # declarative login + sort example
│ └── steps/
│ ├── login.steps.ts
│ ├── common.steps.ts # shared declarative steps
│ └── product.steps.ts
├── src/
│ ├── config/
│ │ └── env.config.ts # type-safe env/secrets
│ ├── core/
│ │ ├── Driver.ts # browser lifecycle (browser→context→page)
│ │ └── BasePage.ts # all low-level actions, explicit waits
│ ├── pages/
│ │ ├── LoginPage.ts # Page Object
│ │ └── ProductsPage.ts # Page Object
│ ├── support/
│ │ └── world.ts # Cucumber World (Dependency Injection)
│ └── hooks/
│ └── hooks.ts # Before/After + Allure attachments
├── cucumber.cjs # Cucumber run config
├── tsconfig.json
├── .env.example # copy to .env
└── .claude/CONVENTIONS.md # authoritative conventions
| Layer | Responsibility |
|---|---|
| Config | Read environment variables / secrets, expose them type-safe. |
| Driver | Own the browser lifecycle. Nothing else launches/closes the browser. |
| BasePage | The single place where every action (click, fill, getText...) lives, each with an explicit wait. |
| Page Object | Locators + page-specific behavior, built only from BasePage actions. |
| World | Inject the driver and page objects into steps (DIP). |
| Hooks | Browser setup/teardown, attach screenshot + trace to Allure on failure. |
| Steps | Thin bridge between Gherkin and page objects — no locators, no waits. |
- Node.js 18+
- (For the Allure HTML report) Java 8+ — required by
allure-commandline.
npm install
npx playwright install chromium # download the browser if not present
cp .env.example .env # then adjust values as needednpm test # run all features (headless)
npm run test:headed # run with a visible browser
npm run test:tag '@smoke' # run only scenarios tagged @smokenpm run report # generate the Allure report and open it
npm run allure:serve # serve the report from raw resultsThe Cucumber HTML report is also written to reports/cucumber-report.html.
All runtime configuration is environment-driven and read in src/config/env.config.ts.
Never hardcode URLs, credentials or timeouts in tests or page objects.
| Variable | Default | Description |
|---|---|---|
BASE_URL |
https://www.saucedemo.com |
Application root URL |
BROWSER |
chromium |
chromium | firefox | webkit |
HEADLESS |
true |
Set false to watch the run locally |
SLOW_MO |
0 |
Delay between actions (ms), for debugging |
DEFAULT_TIMEOUT |
30000 |
Default action/wait timeout (ms) |
STANDARD_USER / STANDARD_PASSWORD |
SauceDemo demo creds | Test user credentials |
.env is git-ignored and must never be committed.
- Page Object Model — pages extend
BasePage; steps stay thin. - Locator priority —
data-testid→id/name→ role/label → css → xpath (last resort). Prefer CSS over XPath. - Explicit waits only — no
waitForTimeout/sleeps; use web-first assertions. - Imperative → Declarative — document a flow imperatively the first time, then collapse it into a single declarative step when reused.
- SOLID — single responsibility per layer, dependencies injected via the World.
See .claude/CONVENTIONS.md for the complete rules and the
checklist for adding a new scenario.
- Write
features/<name>.feature(imperative first, declarative when reused). - Add/extend a page object under
src/pages/using BasePage actions. - Register the page in
src/support/world.ts. - Add thin steps in
features/steps/<name>.steps.ts. - Add any new env vars to
.env.example+env.config.ts. - Tag the scenario for Allure (
@severity=...,@smoke, ...). - Run
npm testand inspect Allure on failure.