CAPSTONE PROJECT · LESSON 24 OF 24
Capstone: a complete regression workflow
Combine user journeys, validation, persistence and integration evidence.
- regression suite
- persistence
- negative tests
- release checklist
Your runnable checkpoint
Extract into a new practice folder. Each download contains tests, configuration, the complete local practice app and an offline lesson. Use Node.js 22 or 24. Each checkpoint is independent.
Install dependencies and browser binaries using the commands below. The test runner starts the practice server on port 4174. Tests target localhost, never the production EduPoly site.
1. Understand the concepts
A regression suite protects important user outcomes. Start with a short smoke path, then add focused checks for validation, persistence and integrations. Avoid one enormous test that makes every later assertion depend on every earlier feature.
This capstone checks task creation, completion, persistence after reload, filtering and deletion. A separate negative scenario verifies invalid input. Both create their own state and can run independently.
The complete-course ZIP includes an all-tests workspace combining the lesson tests, shared fixtures, page object and practice app. Its default project is Chromium. Optional multi-browser and visual configurations are included for deliberate expanded runs.
Before calling a release verified, run type checking, the normal suite and the relevant browser projects. Inspect unexpected skips and retries. Review visual baselines separately; never update them automatically to hide a regression.
Your next project is to adapt this structure to a staging application you own. Replace lab locators with accessible product controls, replace demo state with real test-account login, isolate backend records and retain only useful failure evidence. Keep destructive setup away from production.
2. Build and run
These are complete test files and configuration. The download also includes app/ and server.mjs; preserve their paths. Stop any manually started practice server before running the suite.
npm install
npx playwright install chromium
npm run check
npm testtests/lesson.spec.ts
import { test, expect } from '@playwright/test';
test('task lifecycle survives reload', async ({ page }) => {
await page.goto('/');
await page.getByLabel('Task title', { exact: true }).fill('Release checklist');
await page.getByRole('button', { name: 'Add task', exact: true }).click();
let row = page.getByTestId('task-row').filter({ hasText: 'Release checklist' });
await row.getByRole('button', { name: 'Complete', exact: true }).click();
await page.reload();
row = page.getByTestId('task-row').filter({ hasText: 'Release checklist' });
await expect(row).toHaveAttribute('data-done', 'true');
await page.getByLabel('Filter tasks').selectOption('done');
await expect(row).toHaveCount(1);
await row.getByRole('button', { name: 'Delete', exact: true }).click();
await expect(page.getByTestId('task-row')).toHaveCount(0);
});
test('validation protects an empty list', async ({ page }) => {
await page.goto('/');
await page.getByRole('button', { name: 'Add task', exact: true }).click();
await expect(page.getByRole('alert')).toContainText('Enter a title');
await expect(page.getByTestId('task-row')).toHaveCount(0);
});
playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 1 : 0,
workers: process.env.CI ? 2 : undefined,
timeout: 30000,
expect: { timeout: 5000 },
reporter: [['list'], ['html', { open: 'never' }]],
use: { baseURL: 'http://127.0.0.1:4174', trace: 'retain-on-failure', screenshot: 'only-on-failure' },
projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
webServer: { command: 'node server.mjs', url: 'http://127.0.0.1:4174', reuseExistingServer: false, timeout: 15000 }
});
package.json
{
"name": "edupoly-playwright-capstone-regression-suite",
"version": "1.0.0",
"private": true,
"scripts": {
"test": "playwright test",
"test:headed": "playwright test --headed",
"test:ui": "playwright test --ui",
"check": "tsc --noEmit",
"report": "playwright show-report",
"serve": "node server.mjs"
},
"devDependencies": {
"@playwright/test": "1.56.1",
"typescript": "5.9.3",
"@types/node": "22.20.5"
}
}
tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "CommonJS",
"moduleResolution": "Node",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"types": [
"node"
],
"noEmit": true
},
"include": [
"tests/**/*.ts",
"pages/**/*.ts",
"playwright.config.ts"
]
}
.gitignore
node_modules/
playwright-report/
test-results/
playwright/.auth/
.env
3. Trace the code and check the result
- The page reload checks real localStorage persistence.
- The test locates the task by its title after the reload.
- Validation is isolated in its own scenario.
Verify your result
- A completed task survives reload and can be deleted.
- Invalid input creates no task.
- The all-tests workspace runs the combined course suite.
4. Practice independently
Add a test that cancelling Reset practice tasks preserves a completed task.
Show the explanation
Create and complete a task, register dialog.dismiss(), click reset, then assert the same row still has data-done="true". Keep all setup inside that scenario.
When something goes wrong
If a browser executable is missing, run npx playwright install chromium in the project folder. If port 4174 is occupied, stop your manual practice server. Read assertion call logs for mismatched locators or expectations. Use npm run report to inspect failures; do not add fixed sleeps or broad resets to hide them.
