- Add tests/scripts/lib/gitea-client.js: Gitea API client with auth, comments,
attachments, and Markdown report formatters for visual and console reports
- Add tests/scripts/lib/browser-launcher.js: shared Playwright launch config with
--dns-resolution-order=hostname-first, realistic UA, and navigateTo() helper
using waitUntil:'commit' + waitForLoadState('domcontentloaded')
- Add tests/scripts/e2e-booking-flow-v2.js: full E2E scenario for irina-vik.ru
(register → book service → login → personal cabinet) with Gitea reporting
- Update visual-test-pipeline.js: GITEA_ISSUE env var, Gitea comment+attachment
posting, browser-launcher integration, waitUntil:'commit' navigation
- Update console-error-monitor-standalone.js: same Gitea + DNS fixes
- Update capture-screenshots.js: browser-launcher integration, DNS fix
- Update docker-compose.web-testing.yml: NETWORK_MODE env var (bridge),
DNS_RESOLUTION_ORDER, GITEA_USER/PASSWORD env passthrough, e2e-booking service
- Update tests/package.json: pin playwright to exact 1.52.0 (matches Docker image)
- Update .gitignore: add tests/visual/e2e/ for E2E screenshots
- Update .kilo/agents/visual-tester.md: Docker networking note, Gitea scripts,
e2e-booking service, updated script table
- Update .kilo/commands/web-test.md: Docker Networking section, --issue flag,
Gitea Integration section, e2e-booking service
- Update .kilo/commands/e2e-test.md: complete rewrite — Docker-based Playwright,
no more MCP dependency, proper service table, Gitea integration docs
- Update .kilo/capability-index.yaml: add gitea_integration, e2e_booking_flow,
docker_networking capabilities to visual-tester; add routing entries
176 lines
6.1 KiB
JavaScript
176 lines
6.1 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Console Error Monitor (Standalone)
|
|
*
|
|
* Captures console errors from web pages using Playwright directly
|
|
* (no Playwright MCP dependency). Detects JS errors, network failures, warnings.
|
|
*
|
|
* Usage: node console-error-monitor-standalone.js
|
|
*
|
|
* Environment:
|
|
* TARGET_URL - App URL (default: http://host.docker.internal:3000)
|
|
* REPORTS_DIR - Reports output dir
|
|
* GITEA_ISSUE - Gitea issue number to post results (optional)
|
|
*/
|
|
|
|
const { chromium } = require('playwright');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const gitea = require('./lib/gitea-client');
|
|
const { BASE_ARGS } = require('./lib/browser-launcher');
|
|
|
|
const TARGET_URL = process.env.TARGET_URL || 'http://host.docker.internal:3000';
|
|
const REPORTS_DIR = process.env.REPORTS_DIR || path.join(__dirname, '..', 'reports');
|
|
const GITEA_ISSUE = parseInt(process.env.GITEA_ISSUE, 10) || null;
|
|
|
|
const PAGES = [
|
|
{ name: 'homepage', path: '/' },
|
|
{ name: 'admin-login', path: '/admin/login' },
|
|
];
|
|
|
|
const VIEWPORT = { width: 1280, height: 720 };
|
|
|
|
async function main() {
|
|
console.log('═══════════════════════════════════════════════════');
|
|
console.log(' Console Error Monitor (Standalone)');
|
|
console.log('═══════════════════════════════════════════════════\n');
|
|
console.log(`Target: ${TARGET_URL}\n`);
|
|
|
|
if (!fs.existsSync(REPORTS_DIR)) fs.mkdirSync(REPORTS_DIR, { recursive: true });
|
|
|
|
const browser = await chromium.launch({
|
|
headless: true,
|
|
args: [...BASE_ARGS, '--disable-setuid-sandbox'],
|
|
});
|
|
|
|
const allErrors = [];
|
|
const allWarnings = [];
|
|
const allNetworkErrors = [];
|
|
|
|
for (const pageConf of PAGES) {
|
|
const url = `${TARGET_URL}${pageConf.path}`;
|
|
console.log(`🔍 Checking: ${pageConf.name} (${url})`);
|
|
|
|
const context = await browser.newContext({ viewport: VIEWPORT, deviceScaleFactor: 1 });
|
|
const page = await context.newPage();
|
|
|
|
const consoleErrors = [];
|
|
const consoleWarnings = [];
|
|
const networkErrors = [];
|
|
|
|
page.on('console', msg => {
|
|
if (msg.type() === 'error') {
|
|
consoleErrors.push({ text: msg.text(), location: msg.location() });
|
|
} else if (msg.type() === 'warning') {
|
|
consoleWarnings.push({ text: msg.text(), location: msg.location() });
|
|
}
|
|
});
|
|
|
|
page.on('requestfailed', request => {
|
|
networkErrors.push({
|
|
url: request.url(),
|
|
method: request.method(),
|
|
failure: request.failure()?.errorText || 'Unknown',
|
|
});
|
|
});
|
|
|
|
page.on('response', response => {
|
|
if (response.status() >= 400) {
|
|
networkErrors.push({
|
|
url: response.url(),
|
|
status: response.status(),
|
|
method: response.request().method(),
|
|
});
|
|
}
|
|
});
|
|
|
|
try {
|
|
const response = await page.goto(url, { waitUntil: 'commit', timeout: 30000 });
|
|
await page.waitForLoadState('domcontentloaded', { timeout: 15000 }).catch(() => {});
|
|
await page.waitForTimeout(2000);
|
|
|
|
if (!response || response.status() >= 400) {
|
|
console.log(` ❌ HTTP ${response?.status() || 'no response'}`);
|
|
} else {
|
|
console.log(` ✅ HTTP ${response.status()}`);
|
|
}
|
|
} catch (err) {
|
|
console.log(` ❌ Navigation error: ${err.message}`);
|
|
}
|
|
|
|
if (consoleErrors.length > 0) {
|
|
console.log(` ❌ Console errors: ${consoleErrors.length}`);
|
|
consoleErrors.forEach(e => console.log(` - ${e.text.slice(0, 100)}`));
|
|
} else {
|
|
console.log(` ✅ No console errors`);
|
|
}
|
|
|
|
if (consoleWarnings.length > 0) {
|
|
console.log(` ⚠️ Console warnings: ${consoleWarnings.length}`);
|
|
consoleWarnings.forEach(w => console.log(` - ${w.text.slice(0, 100)}`));
|
|
}
|
|
|
|
if (networkErrors.length > 0) {
|
|
console.log(` ❌ Network errors: ${networkErrors.length}`);
|
|
networkErrors.forEach(e => console.log(` - ${e.status || e.failure} ${e.url.slice(0, 80)}`));
|
|
} else {
|
|
console.log(` ✅ No network errors`);
|
|
}
|
|
|
|
allErrors.push(...consoleErrors.map(e => ({ ...e, page: pageConf.name })));
|
|
allWarnings.push(...consoleWarnings.map(w => ({ ...w, page: pageConf.name })));
|
|
allNetworkErrors.push(...networkErrors.map(e => ({ ...e, page: pageConf.name })));
|
|
|
|
await context.close();
|
|
console.log('');
|
|
}
|
|
|
|
await browser.close();
|
|
|
|
const totalIssues = allErrors.length + allNetworkErrors.length;
|
|
|
|
const report = {
|
|
timestamp: new Date().toISOString(),
|
|
targetUrl: TARGET_URL,
|
|
pages: PAGES.map(p => p.name),
|
|
summary: {
|
|
consoleErrors: allErrors.length,
|
|
consoleWarnings: allWarnings.length,
|
|
networkErrors: allNetworkErrors.length,
|
|
totalIssues,
|
|
},
|
|
consoleErrors: allErrors,
|
|
consoleWarnings: allWarnings,
|
|
networkErrors: allNetworkErrors,
|
|
};
|
|
|
|
const reportPath = path.join(REPORTS_DIR, 'console-error-report.json');
|
|
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
|
|
|
|
console.log('═══════════════════════════════════════════════════');
|
|
console.log(` 📊 Results:`);
|
|
console.log(` Console errors: ${allErrors.length}`);
|
|
console.log(` Console warnings: ${allWarnings.length}`);
|
|
console.log(` Network errors: ${allNetworkErrors.length}`);
|
|
console.log(` Total issues: ${totalIssues}`);
|
|
console.log(` 📄 Report: ${reportPath}`);
|
|
console.log('═══════════════════════════════════════════════════\n');
|
|
|
|
if (GITEA_ISSUE) {
|
|
try {
|
|
console.log(`📤 Posting results to Gitea Issue #${GITEA_ISSUE}...`);
|
|
const commentBody = gitea.formatConsoleReport(report);
|
|
await gitea.postComment(GITEA_ISSUE, commentBody);
|
|
console.log(' ✅ Posted comment to Gitea');
|
|
} catch (err) {
|
|
console.error(` ❌ Gitea posting failed: ${err.message}`);
|
|
}
|
|
}
|
|
|
|
process.exit(totalIssues > 0 ? 1 : 0);
|
|
}
|
|
|
|
main().catch(err => {
|
|
console.error('Fatal:', err);
|
|
process.exit(1);
|
|
}); |