Skip to content

Commit 62fc5b7

Browse files
performance tests
1 parent cd2cf7e commit 62fc5b7

11 files changed

+813
-233
lines changed

README.md

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ This boilerplate includes modern web features:
1616
- ♿ Accessibility features including prefers-reduced-motion support
1717
- 🔍 SEO optimized
1818
- 🧪 End-to-end testing with Playwright
19+
- 📊 Performance testing and monitoring with baselines
1920

2021
## Getting Started
2122

@@ -32,6 +33,7 @@ This boilerplate includes modern web features:
3233
- `npm run test` - Run Playwright end-to-end tests
3334
- `npm run test:ui` - Run Playwright tests with UI for debugging
3435
- `npm run test:update-snapshots` - Update visual test baselines
36+
- `npm run test:update-performance` - Update performance baselines
3537
- `npm run analyze` - Analyze the site with Lighthouse
3638

3739
## Web Components
@@ -110,7 +112,7 @@ The site respects the user's motion preferences:
110112

111113
## Testing
112114

113-
## Testing Philosophy
115+
### Testing Philosophy
114116

115117
This boilerplate embraces a practical testing philosophy:
116118

@@ -128,13 +130,15 @@ The boilerplate uses Playwright for comprehensive end-to-end testing which:
128130
- Verifies behavior across **different devices and viewports**
129131
- Ensures **accessibility standards** are met
130132
- Provides **visual regression testing** to catch unexpected UI changes
133+
- Monitors **performance metrics** against established baselines
131134

132135
#### Included Test Types
133136

134137
- **Functional tests**: Verify features work as expected
135138
- **Visual regression tests**: Catch unintended visual changes
136139
- **Accessibility tests**: Ensure the site works for all users
137140
- **Mobile responsiveness**: Test behavior on different devices
141+
- **Performance tests**: Monitor core web vitals and prevent regressions
138142

139143
#### Running Tests
140144

@@ -148,6 +152,9 @@ npm run test:staging
148152
# Update visual snapshots after intentional changes
149153
npm run test:update-snapshots
150154

155+
# Update performance baselines after optimizations
156+
npm run test:update-performance
157+
151158
# Debug tests interactively with UI
152159
npm run test:ui
153160

@@ -170,14 +177,51 @@ When making design changes, update the visual reference snapshots:
170177
npm run test:update-snapshots
171178
```
172179

173-
#### Writing Effective Tests
180+
### Performance Testing
181+
182+
This boilerplate includes a comprehensive performance testing system:
183+
184+
#### How it works
185+
186+
1. **Performance baselines**: Each component and page has baseline metrics saved in the `/performance` directory
187+
2. **Automated testing**: Tests run on each build and compare current performance against baselines
188+
3. **Threshold alerts**: Tests fail if performance degrades beyond configurable thresholds
189+
4. **Lighthouse integration**: For index pages, full Lighthouse audits are run and results tracked
190+
191+
#### Performance metrics tracked
192+
193+
- **Core Web Vitals**: First Contentful Paint (FCP), Largest Contentful Paint (LCP), Cumulative Layout Shift (CLS)
194+
- **Interaction metrics**: Total Blocking Time (TBT), Time to Interactive (TTI)
195+
- **Navigation metrics**: Time to First Byte (TTFB), DOM Load, Full Page Load
196+
- **Component-level metrics**: Toggle operation time, memory usage, etc.
197+
198+
#### Managing performance baselines
199+
200+
After intentional performance changes or optimizations, update the baselines:
201+
202+
```bash
203+
# Update performance baselines
204+
npm run test:update-performance
205+
```
206+
207+
This will run all tests with the baseline update flag and save current metrics as the new baseline.
208+
209+
#### Performance test file organization
210+
211+
- Performance tests are integrated into component-specific test files
212+
- Each test file measures specific metrics relevant to that component
213+
- The utils/performance-utils.js module provides shared testing utilities
214+
- Baseline metrics are stored in the /performance directory as JSON files
215+
216+
### Writing Effective Tests
174217

175218
When adding new features, write Playwright tests that:
176219

177220
1. Focus on **user journeys** rather than implementation details
178221
2. Use **accessibility-friendly selectors** like `getByRole` and `getByLabel`
179222
3. Test across **multiple browsers and devices**
180-
4. Include **visual regression** tests for UI components
223+
4. Include **visual regression** and **performance** tests
224+
5. Verify **accessibility** requirements are met
181225

182226
Example of a good test case:
183227

@@ -202,6 +246,10 @@ test('user can toggle theme', async ({ page }) => {
202246
document.documentElement.classList.contains('dark-mode') ? 'dark' : 'light',
203247
);
204248
expect(newTheme).not.toBe(initialTheme);
249+
250+
// Check performance metrics for the operation
251+
const metrics = await getBrowserPerformanceMetrics(page);
252+
await assertPerformanceBaseline('theme-toggle-operation', metrics);
205253
});
206254
```
207255

@@ -219,15 +267,15 @@ This project uses Playwright's visual comparison testing to ensure UI consistenc
219267

220268
### Managing snapshots
221269

222-
- **Update snapshots:** After intentional UI changes, run `npm run test:update-visual`
270+
- **Update snapshots:** After intentional UI changes, run `npm run test:update-snapshots`
223271
- **Clean temporary snapshots:** Remove diff and actual files with `npm run test:clean-snapshots`
224272
- **View differences:** Check the Playwright report for side-by-side comparison of visual changes
225273

226274
### Snapshot organization
227275

228-
- Baseline snapshots are stored in `tests/snapshots/`
229-
- Each test file has its own subfolder to organize screenshots
276+
- Baseline snapshots are stored in `/snapshots/` with the naming convention `*-baseline.png`
230277
- Only baseline snapshots are committed to git
278+
- The snapshot directory uses a flat structure for simplicity
231279

232280
### Best practices
233281

bin/baselines.js renamed to bin/regenerate-baselines.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ async function renameSnapshotsToBaselines() {
7373

7474
async function main() {
7575
try {
76-
// Ensure snapshots directory exists
76+
// Ensure snapshots directory exists
7777
try {
7878
await fs.access(SNAPSHOTS_DIR);
7979
} catch {

bin/screenshots.js renamed to bin/screenshots-generate.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,7 @@ async function waitForServer() {
5151
return false;
5252
}
5353

54-
const [serverReady] = await Promise.all([
55-
isServerReady(),
56-
setTimeout(RETRY_DELAY)
57-
]);
54+
const [serverReady] = await Promise.all([isServerReady(), setTimeout(RETRY_DELAY)]);
5855

5956
if (serverReady) {
6057
console.log('✅ Server is ready');

bin/snapshots.js renamed to bin/snapshots-clean.js

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ async function cleanSnapshots() {
1616
// Check if snapshots directory exists
1717
try {
1818
await fs.access(SNAPSHOTS_DIR);
19-
} catch {
19+
} catch (err) {
2020
console.log(`Creating snapshots directory at ${SNAPSHOTS_DIR}`);
2121
await fs.mkdir(SNAPSHOTS_DIR, { recursive: true });
2222
return;
@@ -31,21 +31,19 @@ async function cleanSnapshots() {
3131
});
3232

3333
// Delete each file that's not a baseline
34-
await Promise.all(
35-
filesToRemove.map(async (file) => {
36-
const filePath = path.join(SNAPSHOTS_DIR, file);
37-
await fs.unlink(filePath);
38-
console.log(`Deleted: ${file}`);
39-
}),
40-
);
34+
for (const file of filesToRemove) {
35+
const filePath = path.join(SNAPSHOTS_DIR, file);
36+
await fs.unlink(filePath);
37+
console.log(`Deleted: ${file}`);
38+
}
4139

4240
console.log('Snapshot cleanup complete!');
4341

4442
// Try to restore baseline files from git if any were deleted
4543
try {
4644
execSync('git checkout -- ./snapshots/*baseline*', { stdio: 'inherit' });
4745
console.log('Restored any missing baseline files from git');
48-
} catch {
46+
} catch (gitError) {
4947
// It's okay if this fails, could be that there are no git-tracked baselines yet
5048
console.log('Note: No baseline files needed to be restored from git');
5149
}

bin/snapshots-update.js

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/**
2+
* Script to regenerate snapshot baselines
3+
* This script:
4+
* 1. Runs the tests with --update-snapshots flag
5+
* 2. Renames all snapshots to include "baseline" in the filename
6+
*/
7+
8+
import { exec } from 'child_process';
9+
import fs from 'fs/promises';
10+
import path from 'path';
11+
12+
const SNAPSHOTS_DIR = path.join(process.cwd(), 'snapshots');
13+
14+
async function runTests() {
15+
console.log('Running tests with --update-snapshots flag...');
16+
17+
return new Promise((resolve, reject) => {
18+
exec('npx playwright test --update-snapshots', (error, stdout, stderr) => {
19+
if (error) {
20+
console.error('Error running tests:', error);
21+
console.error(stderr);
22+
reject(error);
23+
return;
24+
}
25+
26+
console.log(stdout);
27+
resolve();
28+
});
29+
});
30+
}
31+
32+
async function renameSnapshotsToBaselines() {
33+
console.log('Renaming snapshots to include "baseline" in the filename...');
34+
35+
try {
36+
// Ensure directory exists
37+
try {
38+
await fs.access(SNAPSHOTS_DIR);
39+
} catch {
40+
console.log(`Creating snapshots directory at ${SNAPSHOTS_DIR}`);
41+
await fs.mkdir(SNAPSHOTS_DIR, { recursive: true });
42+
}
43+
44+
const files = await fs.readdir(SNAPSHOTS_DIR);
45+
46+
const renamePromises = files.map(async (file) => {
47+
// Skip files that already have "baseline" in the name or non-image files
48+
if (file.includes('baseline') || !file.endsWith('.png')) {
49+
return;
50+
}
51+
52+
// Get file extension
53+
const ext = path.extname(file);
54+
const baseName = path.basename(file, ext);
55+
56+
// Create new filename with "baseline" suffix
57+
const newFileName = `${baseName}-baseline${ext}`;
58+
59+
// Rename the file
60+
await fs.rename(path.join(SNAPSHOTS_DIR, file), path.join(SNAPSHOTS_DIR, newFileName));
61+
62+
console.log(`Renamed: ${file} -> ${newFileName}`);
63+
});
64+
65+
await Promise.all(renamePromises);
66+
67+
console.log('All snapshots renamed successfully!');
68+
} catch (error) {
69+
console.error('Error renaming snapshots:', error);
70+
throw error;
71+
}
72+
}
73+
74+
async function main() {
75+
try {
76+
// Ensure snapshots directory exists
77+
try {
78+
await fs.access(SNAPSHOTS_DIR);
79+
} catch {
80+
console.log(`Creating snapshots directory at ${SNAPSHOTS_DIR}`);
81+
await fs.mkdir(SNAPSHOTS_DIR, { recursive: true });
82+
}
83+
84+
// Step 1: Run tests to generate new snapshots
85+
await runTests();
86+
87+
// Step 2: Rename snapshots to include "baseline"
88+
await renameSnapshotsToBaselines();
89+
90+
console.log('Baseline snapshots regenerated successfully!');
91+
} catch (error) {
92+
console.error('Failed to regenerate baseline snapshots:', error);
93+
process.exit(1);
94+
}
95+
}
96+
97+
main();

package.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,20 @@
1717
"analyze": "lighthouse http://localhost:3000 --output json --output html --output-path ./lighthouse-report.html",
1818
"kill": "npx kill-port 3000",
1919
"lint": "eslint .",
20+
"screenshots": "node bin/screenshots-generate.js",
21+
"screenshots:prod": "node -e \"process.env.BASE_URL='https://your-production-url.com'\" && node bin/screenshots-generate.js",
2022
"start": "npx serve ./www",
2123
"test": "playwright test",
22-
"test:staging": "node -e \"process.env.TEST_ENV='staging'\" && playwright test",
23-
"test:ui": "playwright test --ui",
24-
"test:update-snapshots": "playwright test --update-snapshots",
25-
"test:regenerate-baselines": "node bin/baselines.js",
24+
"test:clean-snapshots": "node bin/snapshots-clean.js",
2625
"test:debug": "playwright test --debug",
26+
"test:generate": "playwright codegen http://localhost:3000",
27+
"test:perf": "node bin/perf-update.js",
2728
"test:prod": "node -e \"process.env.TEST_ENV='production'\" && playwright test --project=chromium",
29+
"test:regenerate-baselines": "node bin/snapshots-update.js",
2830
"test:report": "playwright show-report",
29-
"test:generate": "playwright codegen http://localhost:3000",
30-
"test:performance": "playwright test performance.test.js --project=chromium",
31-
"test:clean-snapshots": "node bin/clean-snapshots.js",
32-
"screenshots": "node bin/screenshots.js",
33-
"screenshots:prod": "node -e \"process.env.BASE_URL='https://your-production-url.com'\" && node bin/screenshots.js"
31+
"test:staging": "node -e \"process.env.TEST_ENV='staging'\" && playwright test",
32+
"test:ui": "playwright test --ui",
33+
"test:update-snapshots": "playwright test --update-snapshots"
3434
},
3535
"type": "module"
3636
}

0 commit comments

Comments
 (0)