/**
* Headless browser smoke test of the web panel (dev server + running API).
* Usage: node check-panel.mjs [baseURL]
*/
import { chromium } from "playwright";
import { homedir } from "node:os";
import { mkdirSync, writeFileSync } from "node:fs";
const executablePath = `${homedir()}/.cache/ms-playwright/chromium-1234/chrome-linux64/chrome`;
const base = process.argv[2] ?? "http://localhost:5173";
const email = `check-${Date.now()}@example.com`;
const password = "password123";
const nickname = "Checker";
mkdirSync("/tmp/ltt-check", { recursive: true });
const browser = await chromium.launch({ executablePath });
const page = await browser.newPage();
const errors = [];
page.on("console", (msg) => {
if (msg.type() === "error") errors.push(`console: ${msg.text()}`);
});
page.on("pageerror", (err) => errors.push(`pageerror: ${err.message}`));
page.on("requestfailed", (req) => {
errors.push(`requestfailed: ${req.url()} ${req.failure()?.errorText}`);
});
console.log("1. open panel /login");
await page.goto(`${base}/login`, { waitUntil: "networkidle" });
const bodyBg = await page.evaluate(() => getComputedStyle(document.body).backgroundColor);
const font = await page.evaluate(() => getComputedStyle(document.body).fontFamily);
console.log(` body bg: ${bodyBg}, font: ${font}`);
await page.screenshot({ path: "/tmp/ltt-check/1-login.png" });
console.log("2. go to register and create account");
await page.goto(`${base}/register`, { waitUntil: "networkidle" });
const inputs = page.locator("input");
await inputs.nth(0).fill(nickname);
await inputs.nth(1).fill(email);
await inputs.nth(2).fill(password);
await page.screenshot({ path: "/tmp/ltt-check/2-register.png" });
await page.locator("button[type=submit]").click();
await page.waitForURL(`${base}/`, { timeout: 15000 });
console.log(" landed on projects page");
console.log("3. create a project");
await page.locator("button:has-text('New project'), button:has-text('Новый проект')").first().click();
await page.waitForTimeout(300);
const modalInputs = page.locator(".gn-modal input, [class*=modal] input");
await modalInputs.first().fill("Smoke Test Project");
await page.locator("button[type=submit]").last().click();
await page.waitForTimeout(800);
await page.screenshot({ path: "/tmp/ltt-check/3-projects.png" });
console.log(" project created");
console.log("4. open project page via share link (guest context)");
const shareLink = await page.evaluate(async () => {
const r = await fetch("/api/projects", { credentials: "include" });
const projects = await r.json();
return `/p/${projects[0].share_token}`;
});
const guest = await browser.newPage();
await guest.goto(`${base}${shareLink}`, { waitUntil: "networkidle" });
await guest.screenshot({ path: "/tmp/ltt-check/4-project-public.png" });
const guestText = await guest.textContent("body");
console.log(` guest sees project: ${guestText.includes("Smoke Test Project")}`);
await guest.close();
console.log("5. settings page + language switch");
await page.goto(`${base}/settings`, { waitUntil: "networkidle" });
await page.screenshot({ path: "/tmp/ltt-check/5-settings.png" });
console.log("6. font 404 check");
const fontRequests = [];
page.on("response", (r) => {
if (r.url().includes("/assets/fonts") || r.url().includes("IBMPlex")) fontRequests.push(r.status());
});
await page.reload({ waitUntil: "networkidle" });
console.log(` font responses: ${JSON.stringify(fontRequests)}`);
console.log("\nERRORS:");
console.log(errors.length ? errors.join("\n") : " none");
await browser.close();
if (errors.length) process.exit(1);
console.log("\nOK");