// SPDX-FileCopyrightText: 2026 Stan Grams // // SPDX-License-Identifier: GPL-2.0-or-later import assert from "node:assert/strict"; import { readFile } from "node:fs/promises"; import http from "node:http"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { chromium } from "playwright-core"; const frontendDir = path.dirname(path.dirname(fileURLToPath(import.meta.url))); const webDir = path.resolve(frontendDir, "../assets/web"); const generatedDir = path.join(webDir, "generated"); const jsonRoutes = new Map([ ["/auth/session", { authenticated: true, role: "control", auth_disabled: true }], ["/decoders", []], ["/rigs", { rigs: [], active_remote: null }], ["/bandplan.json", {}], ["/api/recorder/status", []], ["/api/recorder/files", []], ]); const contentTypes = new Map([ [".css", "text/css; charset=utf-8"], [".html", "text/html; charset=utf-8"], [".js", "application/javascript; charset=utf-8"], [".json", "application/json; charset=utf-8"], [".png", "image/png"], [".woff2", "font/woff2"], ]); function assetPath(urlPath) { if (urlPath === "/") return path.join(webDir, "index.html"); if (urlPath.startsWith("/vendor/")) return path.join(webDir, urlPath); const generated = path.join(generatedDir, path.basename(urlPath)); if (urlPath.endsWith(".js")) return generated; return path.join(webDir, urlPath); } const server = http.createServer(async (request, response) => { const url = new URL(request.url ?? "/", "http://127.0.0.1"); if (jsonRoutes.has(url.pathname)) { response.writeHead(200, { "content-type": "application/json" }); response.end(JSON.stringify(jsonRoutes.get(url.pathname))); return; } if (url.pathname === "/audio") { response.writeHead(404).end(); return; } if (["/events", "/decode", "/spectrum", "/meter"].includes(url.pathname)) { response.writeHead(200, { "cache-control": "no-cache", connection: "keep-alive", "content-type": "text/event-stream", }); response.write(": browser smoke stream\n\n"); return; } try { const file = assetPath(url.pathname); const bytes = await readFile(file); response.writeHead(200, { "content-type": contentTypes.get(path.extname(file)) ?? "application/octet-stream", }); response.end(bytes); } catch { response.writeHead(404).end(); } }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); const address = server.address(); assert(address && typeof address === "object"); const executablePath = process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH ?? "/usr/bin/chromium"; const browser = await chromium.launch({ executablePath, headless: true, args: ["--no-sandbox"] }); const page = await browser.newPage(); const runtimeErrors = []; page.on("pageerror", (error) => runtimeErrors.push(error.message)); try { await page.goto(`http://127.0.0.1:${address.port}/`, { waitUntil: "networkidle" }); await page.locator("#content").waitFor({ state: "visible" }); assert.equal(await page.locator("#auth-gate").isVisible(), false); assert.equal(await page.locator("#tab-main").isVisible(), true); await page.locator('.tab[data-tab="about"]').click(); await page.locator("#tab-about").waitFor({ state: "visible" }); assert.equal(new URL(page.url()).pathname, "/about"); await page.goBack(); await page.locator("#tab-main").waitFor({ state: "visible" }); assert.equal(new URL(page.url()).pathname, "/"); assert.deepEqual(runtimeErrors, []); } finally { await browser.close(); await new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve())); }