-
Notifications
You must be signed in to change notification settings - Fork 0
Setup screenshot testing #47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: staging
Are you sure you want to change the base?
Conversation
Co-authored-by: brian.steven.anders <brian.steven.anders@gmail.com>
|
Cursor Agent can help with this pull request. Just |
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
Pull Request Review: Screenshot TestingSummaryThis PR introduces visual regression testing using Playwright and pixelmatch. The implementation is solid overall, but I've identified several areas for improvement around code quality, error handling, performance, and maintainability. 🟢 Strengths
🔴 Issues & Recommendations1. Code Quality & Best PracticesMissing Error Recovery (test/screenshot.test.mjs:228-242)The test catches errors per-file but continues execution. However, if a production page returns 404 or has broken content, the test will fail without clear diagnostics. Recommendation: Add response status checking: const response = await page.goto(productionUrl, { waitUntil: 'networkidle' });
if (!response || response.status() !== 200) {
throw new Error(`Production page returned status ${response?.status()}`);
}Hard-coded Timeout (test/screenshot.test.mjs:66)await page.waitForTimeout(1000);This arbitrary 1-second delay is fragile and slows down tests unnecessarily. Recommendation: Replace with deterministic waiting: // Wait for specific condition instead of arbitrary time
await page.waitForSelector('body.loaded', { state: 'attached', timeout: 5000 })
.catch(() => {}); // Optional: gracefully handle if no .loaded class existsMissing Viewport Height Differences (test/screenshot.test.mjs:19-22)Both viewports have the same height (800px). This might miss issues with content that differs at different viewport heights. Recommendation: Consider varying heights or using fullPage screenshots (which you already do, so the height might be less critical). 2. Potential BugsRace Condition in PNG Parsing (test/screenshot.test.mjs:82-89)The Recommendation: This is probably fine, but for robustness: function loadPNG(buffer) {
return new Promise((resolve, reject) => {
const img = new PNG();
img.on('error', reject);
img.on('parsed', function() { resolve(this); });
img.parse(buffer);
});
}Unsafe Path Sanitization (test/screenshot.test.mjs:212)const safePath = relativePath.replace(/[^a-zA-Z0-9]/g, '_').replace(/\.html$/, '');This could create filename collisions. For example:
Recommendation: Use a hash or preserve directory structure: const safePath = relativePath
.replace(/\.html$/, '')
.replace(/\//g, '_')
.replace(/[^a-zA-Z0-9_-]/g, '_');Or better yet, preserve directory structure in the screenshots folder. 3. Performance ConsiderationsSerial Screenshot Processing (test/screenshot.test.mjs:194-267)Each HTML file is processed sequentially with nested viewport loops. For a site with many pages, this will be very slow. Recommendation: Process files in parallel with concurrency control: import pLimit from 'p-limit';
const limit = pLimit(4); // 4 concurrent pages
const results = await Promise.all(
htmlFiles.map(htmlFile => limit(async () => {
// ... screenshot logic
}))
);This could reduce test time from O(n) to O(n/4). Browser Context Reuse (test/screenshot.test.mjs:189)Only one context is created for all tests, which is good. However, you could potentially reuse pages per viewport to avoid recreation overhead. 4. Security ConcernsArbitrary File System Access (test/screenshot.test.mjs:43)The test directly accesses
Current Status: ✅ Looks safe - the Production URL Construction (test/screenshot.test.mjs:202)const productionUrl = `${productionBaseUrl}${urlPath}`;Current Status: ✅ Safe - 5. Test CoverageMissing Test CasesThe test doesn't verify:
Recommendations: // Add threshold check
const ACCEPTABLE_DIFF_THRESHOLD = 0.1; // 0.1% difference
if (diffResult.diffPercentage > ACCEPTABLE_DIFF_THRESHOLD) {
// Mark as warning, not failure
results.push({...result, warning: 'Diff exceeds threshold'});
}No CI Integration GuidanceThe PR doesn't include CI workflow updates. Screenshots tests should probably:
6. MaintainabilityMagic Numbers (test/screenshot.test.mjs:107)threshold: 0.1,This threshold should be a named constant with documentation. Recommendation: // Threshold of 0.1 allows for minor anti-aliasing differences
// while catching real visual regressions
const PIXEL_DIFF_THRESHOLD = 0.1;Missing JSDoc (test/screenshot.test.mjs:26-40, 43-58, etc.)Helper functions lack documentation explaining their purpose and parameters. Recommendation: Add JSDoc comments, especially for complex functions like 🔒 Security Assessment✅ No security vulnerabilities identified. The test:
📊 Performance ImpactBuild Time: The production build runs during Recommendation: Add this to the README: 🧪 Test Coverage GapThe new test file isn't covered by any other tests. Consider:
📝 Documentation Needs
Missing:
✅ Final VerdictApproval Status: ✅ Approve with recommendations This is a valuable addition to the test suite. The issues identified are mostly nice-to-haves that can be addressed in follow-up PRs. The core functionality is sound. Must-Fix Before MergeNone - all issues are minor improvements. Should-Fix Soon
Nice-to-Have
Great work on adding visual regression testing! 🎉 Generated with Claude Code |
Set up screenshot testing to visually compare local builds against the production site.
This PR introduces a new
npm run test:screenshotcommand that builds the site, then uses Playwright (WebKit) to render each HTML file from the local build andbriananders.comat 1280px and 400px viewports. It performs a pixel-by-pixel comparison usingpixelmatchto identify visual differences, saving local, production, and diff screenshots totest/screenshots/along with a JSON report. This helps catch visual regressions and inconsistencies.