/** Verify the fullscreen annotation editor on the report page. */
import { chromium } from "playwright-core";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { homedir } from "node:os";
const root = dirname(fileURLToPath(import.meta.url));
const SERVER = "http://localhost:8001";
const WEB = "http://localhost:5173";
const stamp = Date.now();
const EMAIL = `editor-fs-${stamp}@example.com`;
const PASSWORD = "editor-fs-pass";
const executablePath = join(homedir(), ".cache/ms-playwright/chromium-1234/chrome-linux64/chrome");
const browser = await chromium.launch({ executablePath, headless: true });
async function api(path, options = {}) {
const r = await fetch(SERVER + path, options);
if (!r.ok) throw new Error(`API ${path} -> ${r.status}: ${await r.text()}`);
return r.json();
}
await api("/api/auth/register", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ nickname: `editor-fs-${stamp}`, email: EMAIL, password: PASSWORD }),
});
const login = await api("/api/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json", "X-Client": "extension" },
body: JSON.stringify({ email: EMAIL, password: PASSWORD }),
});
const auth = { Authorization: `Bearer ${login.token}` };
const project = await api("/api/projects", {
method: "POST",
headers: { "Content-Type": "application/json", ...auth },
body: JSON.stringify({ name: "Fullscreen Editor" }),
});
// a hi-res screenshot so the inline view would normally be small
const maker = await browser.newPage({ viewport: { width: 1200, height: 800 }, deviceScaleFactor: 2 });
await maker.setContent(`<body style="margin:0;background:#1f2335">
<h1 style="color:#c0caf5;font:700 48px monospace;padding:60px">Editor size test</h1></body>`);
const png = await maker.screenshot({ path: "/tmp/ltt-shots/editor-src.png" });
await maker.close();
const form = new FormData();
form.append("file", new Blob([png], { type: "image/png" }), "editor.png");
const up = await api("/api/uploads", { method: "POST", headers: auth, body: form });
const note = await api("/api/reports", {
method: "POST",
headers: { "Content-Type": "application/json", ...auth },
body: JSON.stringify({
project_id: project.id,
type: "element_note",
title: "Fullscreen editor",
environment: { user_agent: "verify", browser: "Chromium", os: "Linux", viewport: { w: 1200, h: 800 }, dpr: 2, language: "en" },
attachment_ids: [up.file_id],
}),
});
// sign in through the web UI (cookie session)
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
await page.goto(`${WEB}/login`);
// GnLoginCard renders the email field as type="text"
await page.locator("input.input").first().fill(EMAIL);
await page.locator('input[type="password"]').fill(PASSWORD);
await page.click("button[type=submit]");
await page.waitForSelector("text=Fullscreen Editor", { timeout: 15000 });
await page.goto(`${WEB}/r/${note.share_token}`);
await page.waitForSelector(".screenshot-expand", { timeout: 15000 });
await page.waitForTimeout(800);
const inlineCanvas = await page.evaluate(() => {
const c = document.querySelector(".screenshot-frame canvas");
const r = c.getBoundingClientRect();
return { w: Math.round(r.width), h: Math.round(r.height) };
});
console.log("inline canvas:", JSON.stringify(inlineCanvas));
await page.click(".screenshot-viewer-actions button");
await page.waitForSelector(".editor-overlay canvas", { timeout: 5000 });
await page.waitForTimeout(800);
const overlay = await page.evaluate(() => {
const overlayEl = document.querySelector(".editor-overlay");
const o = overlayEl.getBoundingClientRect();
const c = document.querySelector(".editor-overlay canvas");
const r = c.getBoundingClientRect();
return {
overlay: { w: Math.round(o.width), h: Math.round(o.height) },
canvas: { w: Math.round(r.width), h: Math.round(r.height) },
};
});
console.log("editor overlay:", JSON.stringify(overlay));
await page.screenshot({ path: "/tmp/ltt-shots/21-fullscreen-editor.png" });
// draw a rect to confirm pointer input works on the big canvas
const canvasBox = await page.locator(".editor-overlay canvas").boundingBox();
await page.mouse.move(canvasBox.x + canvasBox.width * 0.2, canvasBox.y + canvasBox.height * 0.2);
await page.mouse.down();
await page.mouse.move(canvasBox.x + canvasBox.width * 0.7, canvasBox.y + canvasBox.height * 0.6, { steps: 5 });
await page.mouse.up();
await page.waitForTimeout(300);
await page.screenshot({ path: "/tmp/ltt-shots/22-fullscreen-editor-drawn.png" });
// Escape cancels back to the inline view
await page.keyboard.press("Escape");
await page.waitForSelector(".screenshot-frame canvas", { timeout: 5000 });
const stillInline = await page.evaluate(() => !document.querySelector(".editor-overlay"));
console.log("escape closed editor:", stillInline);
const bigger = overlay.canvas.w > inlineCanvas.w && overlay.canvas.h > inlineCanvas.h;
console.log(bigger ? "EDITOR BIGGER: OK" : "EDITOR NOT BIGGER");
await browser.close();
process.exitCode = bigger && stillInline ? 0 : 1;