DIAGNOSTICS AND DELIVERY · LESSON 20 OF 24
Screenshots and visual regression checks
Capture evidence and compare reviewed baselines in a stable environment.
- screenshot
- toHaveScreenshot
- baselines
- artifacts
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 screenshot attachment records what happened; a visual comparison checks the image against an approved baseline. These are different tests. A screenshot existing does not prove the interface looks correct.
Visual results depend on operating system, fonts, browser build, viewport and rendering. Generate and compare baselines in the same controlled environment. Avoid copying a baseline from one platform and assuming it is universal.
The first test always captures and attaches the task section. The visual comparison is opt-in through the visual project. No universal image baseline is shipped. First run npx playwright test --project=visual --update-snapshots, inspect the generated image, then rerun without the update flag.
Never accept new screenshots automatically just to turn a red build green. Review differences against the intended design. Mask volatile content only when that content is not what the test is meant to verify.
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 test -- --project=chromium
# Optional: generate and review a visual baseline
npx playwright test --project=visual --update-snapshots
npx playwright test --project=visualtests/lesson.spec.ts
import { test, expect } from '@playwright/test';
test('attach a screenshot as evidence', async ({ page }, testInfo) => {
await page.goto('/');
const section = page.getByRole('region', { name: 'Tasks', exact: true });
const image = await section.screenshot({ animations: 'disabled' });
expect(image.length).toBeGreaterThan(0);
await testInfo.attach('task-section', { body: image, contentType: 'image/png' });
});
test('compare a reviewed visual baseline', async ({ page }, testInfo) => {
test.skip(testInfo.project.name !== 'visual', 'Run the visual project after reviewing its baseline');
await page.goto('/');
await expect(page.getByRole('region', { name: 'Tasks', exact: true })).toHaveScreenshot('tasks.png', { animations: 'disabled' });
});
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'] } }, { name: 'visual', use: { ...devices['Desktop Chrome'] }, testMatch: '**/lesson.spec.ts' }],
webServer: { command: 'node server.mjs', url: 'http://127.0.0.1:4174', reuseExistingServer: false, timeout: 15000 }
});
package.json
{
"name": "edupoly-playwright-screenshots-and-visual-comparisons",
"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
- A named region scopes the screenshot to the task interface.
- The explicit skip prevents an accidental missing-baseline failure in the default course run.
- toHaveScreenshot performs comparison, not just image capture.
Verify your result
- The Chromium project attaches a screenshot and skips the opt-in comparison.
- Generate, review and rerun the visual baseline in the same environment.
4. Practice independently
Change a button color after approving a baseline and inspect the failure.
Show the explanation
Run the visual project without --update-snapshots. Inspect expected, actual and diff images. Restore the color or approve the change only if it is intended.
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.
