Latest Updated Pages
E2E Test Suite / Critical User Journeys (push) Has been skipped
E2E Test Suite / API Integration Tests (push) Has been skipped
Deploy to Production / Build & Verify (push) Failing after 13s
Ping Search Engines / Notify Search Engines (push) Successful in 3s
Deploy to Production / Pre-Deploy Tests (push) Has been skipped
Deploy to Production / Deploy to Railway (push) Has been skipped
Deploy to Production / Deploy to Render (push) Has been skipped
Deploy to Production / Deploy to VPS (PM2) (push) Has been skipped
Deploy to Production / Deploy to Fly.io (push) Has been skipped
Deploy to Production / Post-Deploy Verification (push) Has been skipped
Deploy to Production / Notify on Failure (push) Successful in 1s
E2E Test Suite / Smoke Tests (P0) (push) Failing after 9m36s
E2E Test Suite / Form Interaction Tests (push) Failing after 12m6s
E2E Test Suite / Destructive & Chaos Tests (push) Failing after 11m46s
E2E Test Suite / Cross-Browser Regression (chromium) (push) Failing after 9m31s
E2E Test Suite / Cross-Browser Regression (firefox) (push) Failing after 11m5s
E2E Test Suite / Cross-Browser Regression (webkit) (push) Failing after 15m24s
E2E Test Suite / Security Header Tests (push) Failing after 7m55s
E2E Test Suite / Test Report Summary (push) Failing after 6s
E2E Test Suite / Mobile Device Tests (push) Failing after 3h12m28s
Uptime Monitor / Health & Response Time (push) Successful in 5s
Uptime Monitor / SSL Certificate (push) Successful in 3s
Uptime Monitor / Send Alerts (push) Has been skipped
Uptime Monitor / Record Uptime Success (push) Successful in 2s

This commit is contained in:
2026-03-22 14:37:17 +05:30
parent d402256547
commit 0614ae6f85
80 changed files with 11667 additions and 687 deletions
+258
View File
@@ -0,0 +1,258 @@
/**
* Theme Screenshot Capture Script
* WorkRoot IT Solutions — Dark/Light Mode Visual Reference
*
* Generates full-page screenshots of all major pages in both themes
* across desktop, tablet, and mobile viewports.
*
* Usage:
* 1. Start dev server: npm run dev
* 2. Run this script: node scripts/capture-theme-screenshots.js
*
* Output: .agents/frontend-specialist/screenshots/{light,dark}/{page}-{viewport}.png
*
* Prerequisites:
* npm install playwright
* npx playwright install chromium
*/
const { chromium } = require('playwright');
const path = require('path');
const fs = require('fs');
// ─── Configuration ────────────────────────────────────────────────────────────
const BASE_URL = process.env.BASE_URL || 'http://localhost:4321';
const PAGES = [
{ name: 'home', path: '/' },
{ name: 'about', path: '/about' },
{ name: 'services', path: '/services' },
{ name: 'portfolio', path: '/portfolio' },
{ name: 'contact', path: '/contact' },
];
const VIEWPORTS = [
{ name: 'desktop', width: 1440, height: 900 },
{ name: 'tablet', width: 768, height: 1024 },
{ name: 'mobile', width: 390, height: 844 },
];
const THEMES = ['light', 'dark'];
/** Milliseconds to wait after theme switch for transitions to settle */
const THEME_SETTLE_MS = 300;
/** Milliseconds to wait after page load for animations to complete */
const PAGE_SETTLE_MS = 500;
/** Output directory (relative to project root) */
const OUTPUT_DIR = path.join(
__dirname,
'..',
'.agents',
'frontend-specialist',
'screenshots'
);
// ─── Helpers ──────────────────────────────────────────────────────────────────
function ensureDir(dirPath) {
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
}
function screenshotPath(theme, pageName, viewportName) {
return path.join(OUTPUT_DIR, theme, `${pageName}-${viewportName}.png`);
}
async function waitForPageReady(page) {
// Wait for network idle + animations to complete
await page.waitForLoadState('networkidle');
await page.waitForTimeout(PAGE_SETTLE_MS);
}
async function setTheme(page, theme) {
await page.evaluate((t) => {
if (t === 'dark') {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
try {
localStorage.setItem('theme', t);
} catch (_) {
// localStorage may be unavailable in some contexts
}
}, theme);
// Wait for CSS transitions to settle
await page.waitForTimeout(THEME_SETTLE_MS);
}
async function captureScreenshot(page, filePath, fullPage = true) {
await page.screenshot({
path: filePath,
fullPage,
animations: 'disabled', // Freeze CSS/JS animations for clean captures
});
const sizeKB = Math.round(fs.statSync(filePath).size / 1024);
console.log(` ✓ Saved: ${path.relative(process.cwd(), filePath)} (${sizeKB}KB)`);
}
// ─── Main ─────────────────────────────────────────────────────────────────────
async function main() {
console.log('');
console.log('═══════════════════════════════════════════════════════════');
console.log(' WorkRoot Theme Screenshot Capture');
console.log(` Base URL: ${BASE_URL}`);
console.log(` Output: ${OUTPUT_DIR}`);
console.log('═══════════════════════════════════════════════════════════');
console.log('');
// Create output directories
for (const theme of THEMES) {
ensureDir(path.join(OUTPUT_DIR, theme));
}
const browser = await chromium.launch({
headless: true,
});
let totalScreenshots = 0;
let errors = [];
try {
for (const viewport of VIEWPORTS) {
console.log(`\n── Viewport: ${viewport.name} (${viewport.width}×${viewport.height}) ──`);
const context = await browser.newContext({
viewport: { width: viewport.width, height: viewport.height },
deviceScaleFactor: 2, // Retina quality for crisp screenshots
colorScheme: 'light', // Start light; we override via JS
});
const page = await context.newPage();
// Disable animations globally for cleaner screenshots
await page.addInitScript(() => {
const style = document.createElement('style');
style.textContent = `
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
`;
document.head.appendChild(style);
});
for (const pageConfig of PAGES) {
const url = `${BASE_URL}${pageConfig.path}`;
console.log(`\n Page: ${pageConfig.name} (${url})`);
for (const theme of THEMES) {
try {
// Navigate to page
await page.goto(url, { waitUntil: 'domcontentloaded' });
await waitForPageReady(page);
// Set theme
await setTheme(page, theme);
// Capture screenshot
const filePath = screenshotPath(theme, pageConfig.name, viewport.name);
await captureScreenshot(page, filePath);
totalScreenshots++;
} catch (err) {
const label = `${theme}/${pageConfig.name}-${viewport.name}`;
errors.push({ label, error: err.message });
console.error(` ✗ Failed ${label}: ${err.message}`);
}
}
}
await context.close();
}
} finally {
await browser.close();
}
// Summary
console.log('');
console.log('═══════════════════════════════════════════════════════════');
console.log(` Done! Captured ${totalScreenshots} screenshots`);
if (errors.length > 0) {
console.log(`${errors.length} error(s):`);
for (const { label, error } of errors) {
console.log(`${label}: ${error}`);
}
}
console.log('═══════════════════════════════════════════════════════════');
console.log('');
// Generate index file listing all screenshots
generateIndex(totalScreenshots, errors);
}
// ─── Index Generator ──────────────────────────────────────────────────────────
function generateIndex(total, errors) {
const lines = [
'# Theme Screenshots Index',
'',
`Generated: ${new Date().toISOString()}`,
`Total: ${total} screenshots`,
errors.length > 0 ? `Errors: ${errors.length}` : 'All captures successful ✓',
'',
'## File Listing',
'',
];
for (const theme of THEMES) {
lines.push(`### ${theme.charAt(0).toUpperCase() + theme.slice(1)} Theme`);
lines.push('');
lines.push('| Page | Desktop | Tablet | Mobile |');
lines.push('|------|---------|--------|--------|');
for (const pageConfig of PAGES) {
const cells = VIEWPORTS.map((vp) => {
const file = `${pageConfig.name}-${vp.name}.png`;
const fullPath = path.join(OUTPUT_DIR, theme, file);
const exists = fs.existsSync(fullPath);
if (!exists) return '❌ missing';
const sizeKB = Math.round(fs.statSync(fullPath).size / 1024);
return `[${file}](./${theme}/${file}) (${sizeKB}KB)`;
});
lines.push(`| ${pageConfig.name} | ${cells.join(' | ')} |`);
}
lines.push('');
}
if (errors.length > 0) {
lines.push('## Errors');
lines.push('');
for (const { label, error } of errors) {
lines.push(`- **${label}**: ${error}`);
}
lines.push('');
}
lines.push('---');
lines.push('*Generated by `scripts/capture-theme-screenshots.js`*');
const indexPath = path.join(OUTPUT_DIR, 'INDEX.md');
fs.writeFileSync(indexPath, lines.join('\n'));
console.log(` Index written: ${path.relative(process.cwd(), indexPath)}`);
}
// ─── Entry Point ──────────────────────────────────────────────────────────────
main().catch((err) => {
console.error('Fatal error:', err);
process.exit(1);
});