1. Folder layout
Each folder has one job. Tests describe behaviour; everything else is plumbing — and tests never touch selectors directly. They go through POMs.
my-framework/
├── tests/ # *.spec.ts — describes behaviour only
├── pages/ # Page object classes (selectors live here)
├── fixtures/ # test.extend() — authed page, seeded data
├── utils/ # logger, date helpers, API helpers
├── config/ # env-specific URLs, users, feature flags
└── playwright.config.ts # the one entry point2. Centralized config
One playwright.config.ts, three environments. Everything that changes between dev / staging / prod comes from env vars — never hard-coded.
import { defineConfig } from '@playwright/test';
import 'dotenv/config';
export default defineConfig({
timeout: Number(process.env.TIMEOUT ?? 30_000),
retries: process.env.CI ? 2 : 0,
use: {
baseURL: process.env.BASE_URL!,
trace: 'on-first-retry',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
});3. Logging — winston + per-test log files
Every action logs to logs/<test-name>.log. When CI fails at 3am, you read the log instead of re-running the test.
// utils/logger.ts
import { createLogger, format, transports } from 'winston';
export const makeLogger = (testName: string) =>
createLogger({
level: 'info',
format: format.combine(format.timestamp(), format.simple()),
transports: [
new transports.File({ filename: `logs/${testName}.log` }),
],
});4. Reporting
- Allure — trends across runs, ownership tags, history.
- Playwright HTML — one-off debug with trace viewer baked in.
- Slack — team-visible alert on red builds, with a link to the run.
reporter: [
['list'],
['html', { outputFolder: 'playwright-report', open: 'never' }],
['allure-playwright'],
],5. Retries & flake quarantine
Retry twice in CI, zero times locally — local retries hide real bugs. Persistently-flaky tests move to a flaky/ folder, NOT .skip — visibility is the cure.
retries: process.env.CI ? 2 : 0,
// flaky/ has its own project so reds don't block CI
projects: [
{ name: 'main', testDir: './tests' },
{ name: 'flaky', testDir: './flaky', retries: 3 },
],6. The 12-factor test
- Hermetic — no shared state between tests. Each test seeds and cleans up its own data.
- Deterministic — same input, same result, every run.
- Fast — under 60 seconds per test. Past that, split it.
- Isolated — runs in any order, in parallel, on any worker.
- Owned — every test has a name in CODEOWNERS.
7. Hands-on capstone
Ship your framework. Push it to a public GitHub repo with: README, the workflow from Module 08, at least one POM, one API test, one visual test, and a green CI badge in the README.
Recruiters will read this repo before they read your CV.
8. You are Automation-Ready
Capstone complete
You are Automation-Ready 🎉
You've shipped a production Playwright framework end-to-end — locators, assertions, POM, API, CI/CD, visual diffs, and architecture. Time to tell the world.