104 lines
3.7 KiB
JavaScript
104 lines
3.7 KiB
JavaScript
/**
|
|
* Console-error crawler using Playwright.
|
|
* Logs in, visits all sidebar nav links, reports JS errors/exceptions.
|
|
*
|
|
* Usage:
|
|
* npm install -D @playwright/test && npx playwright install chromium
|
|
* node scripts/console-check.mjs
|
|
*/
|
|
import { chromium } from '@playwright/test';
|
|
|
|
const BASE = process.env.APP_URL ?? 'https://app.nimuli.com';
|
|
const EMAIL = 'nexxo@nimuli.com';
|
|
const PASS = 'NimuliDev2026!';
|
|
|
|
const errors = [];
|
|
const warnings = [];
|
|
|
|
console.log(`\n${'═'.repeat(45)}`);
|
|
console.log(' Nimuli — Console Error Check');
|
|
console.log(` ${new Date().toISOString()}`);
|
|
console.log(` Base: ${BASE}`);
|
|
console.log(`${'═'.repeat(45)}\n`);
|
|
|
|
const browser = await chromium.launch({ headless: true });
|
|
const ctx = await browser.newContext({
|
|
ignoreHTTPSErrors: true,
|
|
viewport: { width: 1280, height: 800 },
|
|
});
|
|
const page = await ctx.newPage();
|
|
|
|
page.on('console', msg => {
|
|
if (msg.type() === 'error') {
|
|
errors.push({ url: page.url(), text: msg.text() });
|
|
} else if (msg.type() === 'warning') {
|
|
warnings.push({ url: page.url(), text: msg.text() });
|
|
}
|
|
});
|
|
page.on('pageerror', err => {
|
|
errors.push({ url: page.url(), text: `PageError: ${err.message}` });
|
|
});
|
|
|
|
// ── Login ────────────────────────────────────
|
|
console.log('[ LOGIN ]');
|
|
await page.goto(`${BASE}/login`, { waitUntil: 'networkidle' });
|
|
await page.fill('input[name=email]', EMAIL);
|
|
await page.fill('input[name=password]', PASS);
|
|
await page.click('button[type=submit]');
|
|
await page.waitForLoadState('networkidle');
|
|
console.log(` ✓ Logged in: ${page.url()}`);
|
|
|
|
// ── Collect nav links ─────────────────────────
|
|
const navLocators = page.locator('[data-nav-link]');
|
|
const navCount = await navLocators.count();
|
|
const navLinks = [];
|
|
for (let i = 0; i < navCount; i++) {
|
|
const href = await navLocators.nth(i).getAttribute('href');
|
|
if (href && !href.endsWith('#') && !navLinks.includes(href)) {
|
|
navLinks.push(href);
|
|
}
|
|
}
|
|
|
|
console.log(`\n[ CRAWLING ${navLinks.length} NAV LINKS ]`);
|
|
|
|
for (const link of navLinks) {
|
|
const pre = errors.length;
|
|
await page.goto(link, { waitUntil: 'networkidle' });
|
|
await page.waitForTimeout(400);
|
|
const newErrs = errors.length - pre;
|
|
const status = newErrs > 0 ? `✗ ${newErrs} new error(s)` : '✓ clean';
|
|
let path = link;
|
|
try { path = new URL(link).pathname; } catch { /* keep full link */ }
|
|
console.log(` ${status}: ${path}`);
|
|
}
|
|
|
|
// ── Profile pages ─────────────────────────────
|
|
console.log('\n[ PROFILE PAGES ]');
|
|
for (const path of ['/profile', '/profile/personal', '/profile/preferences', '/profile/security']) {
|
|
const pre = errors.length;
|
|
await page.goto(`${BASE}${path}`, { waitUntil: 'networkidle' });
|
|
await page.waitForTimeout(300);
|
|
const newErrs = errors.length - pre;
|
|
console.log(` ${newErrs > 0 ? '✗' : '✓'} ${path}`);
|
|
}
|
|
|
|
await browser.close();
|
|
|
|
// ── Report ────────────────────────────────────
|
|
console.log(`\n${'═'.repeat(45)}`);
|
|
if (errors.length > 0) {
|
|
console.error(` ❌ ${errors.length} console error(s):`);
|
|
for (const e of errors) {
|
|
let path = e.url;
|
|
try { path = new URL(e.url).pathname; } catch { /* keep full */ }
|
|
console.error(` ${path}: ${e.text.slice(0, 140)}`);
|
|
}
|
|
console.error(`${'═'.repeat(45)}\n`);
|
|
process.exit(1);
|
|
}
|
|
if (warnings.length > 0) {
|
|
console.warn(` ⚠ ${warnings.length} console warning(s) (non-blocking)`);
|
|
}
|
|
console.log(' ✅ No console errors');
|
|
console.log(`${'═'.repeat(45)}\n`);
|