Newer
Older
bugtrail / packages / web / verify-editor.mjs
@Eugene Sukhodolskiy Eugene Sukhodolskiy 23 hours ago 5 KB Make the annotation editor comfortable to draw in
/** 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;