You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/scripts/smoke-check.mjs

147 lines
4.4 KiB

/**
* Playwright smoke check for the /test/soma/* demo pages.
*
* What it verifies per route (beyond HTTP 200):
* - No uncaught `pageerror` (thrown during component init / hydration).
* - No `console.error` (runtime exceptions that didn't throw synchronously).
* - No translation key missing warnings (`[lang] Translation key not found`).
* - No soma context-not-found warnings (`Context "X" not found`).
*
* Usage:
* node scripts/smoke-check.mjs [base-url]
* npm run smoke
*
* Requires `npm run dev` running in another terminal (or passes an explicit
* base URL). The script tries 5173-5180 if no URL is given.
*/
import { chromium } from 'playwright';
import { readdirSync, statSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
const __dirname = dirname(fileURLToPath(import.meta.url));
const ROUTES_DIR = join(__dirname, '..', 'src', 'routes', 'test', 'soma');
/** Find every `{route}/+page.svelte` under /test/soma recursively. */
function discoverRoutes(dir, prefix) {
const routes = [];
const entries = readdirSync(dir);
for (const name of entries) {
const full = join(dir, name);
const st = statSync(full);
if (st.isDirectory()) {
routes.push(...discoverRoutes(full, `${prefix}/${name}`));
} else if (name === '+page.svelte') {
routes.push(prefix || '/test/soma');
}
}
return routes;
}
async function probePort(startPort, endPort) {
for (let port = startPort; port <= endPort; port++) {
try {
const res = await fetch(`http://localhost:${port}/`, {
signal: AbortSignal.timeout(500)
});
if (res.ok || res.status === 404 || res.status === 500) {
return `http://localhost:${port}`;
}
} catch {
// connection refused — try next
}
}
return null;
}
const BASE = process.argv[2] ?? (await probePort(5173, 5180));
if (!BASE) {
console.error('Could not find a running dev server on 5173-5180.');
console.error('Start it with `npm run dev` in another terminal.');
process.exit(2);
}
console.error(`Using dev server at ${BASE}`);
const routes = discoverRoutes(ROUTES_DIR, '/test/soma').sort();
if (routes.length === 0) {
console.error('No +page.svelte files found under /test/soma.');
process.exit(2);
}
console.error(`Checking ${routes.length} routes...`);
const browser = await chromium.launch();
const ctx = await browser.newContext();
const failures = [];
const MAX_PARALLEL = 4;
async function checkRoute(route) {
const page = await ctx.newPage();
const errors = [];
const warnings = [];
page.on('pageerror', (e) => errors.push(` pageerror: ${e.message}`));
page.on('console', (m) => {
const t = m.text();
if (m.type() === 'error') {
// Filter out some noisy devtools messages unrelated to correctness.
if (t.includes('Download the React DevTools')) return;
errors.push(` console.error: ${t}`);
}
if (m.type() === 'warning' || m.type() === 'warn') {
if (
t.includes('Translation key not found') ||
t.includes('Context') ||
t.includes('[soma]') ||
t.includes('soma context')
) {
warnings.push(` console.warning: ${t}`);
}
}
});
try {
const res = await page.goto(BASE + route, {
waitUntil: 'networkidle',
timeout: 20000
});
const status = res?.status() ?? 0;
// Give reactive effects + lazy demos a moment to settle.
await page.waitForTimeout(500);
if (status !== 200 || errors.length > 0 || warnings.length > 0) {
const lines = [`FAIL ${route} (HTTP ${status})`];
errors.forEach((e) => lines.push(e));
warnings.forEach((w) => lines.push(w));
return { ok: false, lines };
}
return { ok: true, lines: [`PASS ${route}`] };
} catch (e) {
return {
ok: false,
lines: [`FAIL ${route} (navigation error: ${e.message})`]
};
} finally {
await page.close();
}
}
// Run checks with bounded concurrency so the dev server doesn't get hammered.
let cursor = 0;
async function worker() {
while (cursor < routes.length) {
const idx = cursor++;
const route = routes[idx];
const result = await checkRoute(route);
result.lines.forEach((l) => console.log(l));
if (!result.ok) failures.push(route);
}
}
await Promise.all(Array.from({ length: MAX_PARALLEL }, () => worker()));
await browser.close();
console.log('');
console.log(
failures.length === 0
? `All ${routes.length} routes passed.`
: `${failures.length}/${routes.length} routes failed: ${failures.join(', ')}`
);
process.exit(failures.length === 0 ? 0 : 1);

Powered by TurnKey Linux.