feat: add AI-powered visual regression testing infrastructure
- Add docker-compose.web-testing.yml with vlmkit service - Add 5 test scripts: capture, compare, pipeline, console-monitor, link-checker - Add vrt.config.json with 3 viewports + maskSelectors - Add package.json with @mizchi/vlmkit and Node >=24 - Add tests/README.md with VRT documentation - Update .gitignore for test artifacts Based on APAW issue #144 / milestone #106
This commit is contained in:
127
tests/scripts/console-error-monitor-standalone.js
Normal file
127
tests/scripts/console-error-monitor-standalone.js
Normal file
@@ -0,0 +1,127 @@
|
||||
const { chromium } = require('playwright');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const TARGET_URL = process.env.TARGET_URL;
|
||||
const PAGES = (process.env.PAGES || '/').split(',').map(p => p.trim());
|
||||
const REPORTS_DIR = process.env.REPORTS_DIR || './reports';
|
||||
|
||||
async function monitorPage(context, pagePath) {
|
||||
const page = await context.newPage();
|
||||
const fullUrl = `${TARGET_URL}${pagePath}`;
|
||||
const consoleErrors = [];
|
||||
const networkErrors = [];
|
||||
|
||||
page.on('console', msg => {
|
||||
if (msg.type() === 'error') {
|
||||
consoleErrors.push({
|
||||
type: 'console_error',
|
||||
text: msg.text(),
|
||||
location: msg.location(),
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
page.on('pageerror', err => {
|
||||
consoleErrors.push({
|
||||
type: 'page_error',
|
||||
text: err.message,
|
||||
stack: err.stack,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
});
|
||||
|
||||
page.on('requestfailed', req => {
|
||||
networkErrors.push({
|
||||
type: 'network_failure',
|
||||
url: req.url(),
|
||||
method: req.method(),
|
||||
failure: req.failure()?.errorText || 'unknown',
|
||||
resourceType: req.resourceType(),
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
});
|
||||
|
||||
page.on('response', res => {
|
||||
if (res.status() >= 400) {
|
||||
networkErrors.push({
|
||||
type: 'http_error',
|
||||
url: res.url(),
|
||||
status: res.status(),
|
||||
statusText: res.statusText(),
|
||||
method: res.request().method(),
|
||||
resourceType: res.request().resourceType(),
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
await page.goto(fullUrl, { waitUntil: 'networkidle', timeout: 30000 });
|
||||
await page.waitForTimeout(1000);
|
||||
} finally {
|
||||
await page.close();
|
||||
}
|
||||
|
||||
return { pagePath, consoleErrors, networkErrors };
|
||||
}
|
||||
|
||||
async function main() {
|
||||
if (!TARGET_URL) {
|
||||
console.error('TARGET_URL environment variable is required');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const context = await browser.newContext();
|
||||
const results = [];
|
||||
|
||||
try {
|
||||
for (const pagePath of PAGES) {
|
||||
try {
|
||||
const result = await monitorPage(context, pagePath);
|
||||
results.push(result);
|
||||
console.log(`Checked ${pagePath}: ${result.consoleErrors.length} console errors, ${result.networkErrors.length} network errors`);
|
||||
} catch (err) {
|
||||
results.push({ pagePath, error: err.message, consoleErrors: [], networkErrors: [] });
|
||||
console.error(`Failed ${pagePath}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
|
||||
const totalConsoleErrors = results.reduce((sum, r) => sum + r.consoleErrors.length, 0);
|
||||
const totalNetworkErrors = results.reduce((sum, r) => sum + r.networkErrors.length, 0);
|
||||
const pagesWithErrors = results.filter(r => r.consoleErrors.length > 0 || r.networkErrors.length > 0).length;
|
||||
|
||||
const report = {
|
||||
timestamp: new Date().toISOString(),
|
||||
targetUrl: TARGET_URL,
|
||||
pages: PAGES,
|
||||
summary: {
|
||||
totalPages: results.length,
|
||||
pagesWithErrors,
|
||||
totalConsoleErrors,
|
||||
totalNetworkErrors,
|
||||
hasErrors: totalConsoleErrors > 0 || totalNetworkErrors > 0,
|
||||
},
|
||||
results,
|
||||
};
|
||||
|
||||
fs.mkdirSync(REPORTS_DIR, { recursive: true });
|
||||
const reportPath = path.join(REPORTS_DIR, 'console-error-report.json');
|
||||
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
|
||||
console.log(`Report saved to ${reportPath}`);
|
||||
|
||||
if (totalConsoleErrors > 0 || totalNetworkErrors > 0) {
|
||||
console.error(`Found ${totalConsoleErrors} console errors and ${totalNetworkErrors} network errors across ${pagesWithErrors} page(s)`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error('Fatal error:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user