#!/usr/bin/env node /** * Serves the evidence report over HTTP. * * The report is a plain folder of files and opens perfectly well from disk, but * a localhost URL is easier to share with a colleague on the same machine, and * some browsers treat file:// pages more suspiciously than http://. * * node scripts/serve-report.mjs http://localhost:9400 * node scripts/serve-report.mjs --port 8123 * node scripts/serve-report.mjs --no-open */ import { createServer } from 'node:http'; import { createReadStream, existsSync, statSync } from 'node:fs'; import { spawn } from 'node:child_process'; import { dirname, extname, join, normalize, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..'); const DIR = join(ROOT, 'e2e', 'evidence-report'); const args = process.argv.slice(2); const portFlag = args.indexOf('--port'); const PORT = portFlag !== -1 ? Number(args[portFlag + 1]) : 9400; const shouldOpen = !args.includes('--no-open'); if (!existsSync(join(DIR, 'index.html'))) { console.error(`No report at ${DIR}\nRun the suite first: npm run test:e2e`); process.exit(1); } const TYPES = { '.html': 'text/html; charset=utf-8', '.md': 'text/markdown; charset=utf-8', '.json': 'application/json; charset=utf-8', '.png': 'image/png', '.jpg': 'image/jpeg', '.pdf': 'application/pdf', '.css': 'text/css; charset=utf-8', '.js': 'text/javascript; charset=utf-8', }; const server = createServer((req, res) => { const requested = decodeURIComponent((req.url ?? '/').split('?')[0]); const relative = requested === '/' ? 'index.html' : requested.replace(/^\/+/, ''); // normalize collapses any ".." before it can escape the report directory. const target = join(DIR, normalize(relative)); if (!target.startsWith(DIR)) { res.writeHead(403).end('Forbidden'); return; } if (!existsSync(target) || statSync(target).isDirectory()) { res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' }); res.end(`Not found: ${relative}\n\nThis server only holds the evidence report.`); return; } res.writeHead(200, { 'Content-Type': TYPES[extname(target).toLowerCase()] ?? 'application/octet-stream', 'Cache-Control': 'no-store', }); createReadStream(target).pipe(res); }); server.on('error', (err) => { if (err.code === 'EADDRINUSE') { console.error( `Port ${PORT} is already in use.\n` + `Try another: npm run report:serve -- --port ${PORT + 1}`, ); process.exit(1); } throw err; }); server.listen(PORT, () => { const url = `http://localhost:${PORT}/`; console.log( [ '', ` Test report: ${url}`, ` PDF: ${url}TravelHub-test-report.pdf`, ` Markdown: ${url}report.md`, '', ' Press Ctrl+C to stop.', '', ].join('\n'), ); if (shouldOpen) { const [cmd, cmdArgs] = process.platform === 'win32' ? ['cmd', ['/c', 'start', '', url]] : process.platform === 'darwin' ? ['open', [url]] : ['xdg-open', [url]]; spawn(cmd, cmdArgs, { detached: true, stdio: 'ignore' }).unref(); } });