maCopy/scripts/visual-walkthrough.mjs
ilia e9461ed4f3
All checks were successful
CI / skip-ci-check (pull_request) Successful in 4s
CI / secret-scan (pull_request) Successful in 3s
CI / node-ci (pull_request) Successful in 17s
Add copy-only, color swatches, DB speed pragmas, and living docs
- Copy only (right-click -> Copy): write to clipboard without
  auto-pasting or hiding the window
- Color swatches: hex/rgb/hsl entries show a preview chip
- SQLite WAL + synchronous=NORMAL so the UI can read while the
  clipboard poller writes
- Memoize list rows (React.memo) so selection/focus changes don't
  re-render every row's preview
- Add docs/GUIDE.md (living install + usage doc), docs/SHARING.md,
  docs/TESTING.md, docs/LAUNCH-AT-LOGIN.md, docs/PRODUCT.md,
  CHANGELOG.md, ROADMAP.md
- Add homelab Gitea Actions CI (.gitea/workflows/ci.yml) + gitleaks
  allowlist
- Bump version to 0.2.0
2026-07-14 21:58:31 -04:00

103 lines
2.7 KiB
JavaScript

/**
* Headed visual walkthrough — opens Chromium (headless: false) so you can watch.
*
* npm run test:visual
*/
import { chromium } from "playwright";
import { spawn } from "node:child_process";
import { setTimeout as sleep } from "node:timers/promises";
const PORT = 1420;
const BASE = `http://127.0.0.1:${PORT}/visual.html`;
async function portOpen(port) {
try {
const res = await fetch(`http://127.0.0.1:${port}/visual.html`);
return res.status < 500;
} catch {
return false;
}
}
async function ensureVite() {
if (await portOpen(PORT)) return null;
const child = spawn("npm", ["run", "dev", "--", "--host", "127.0.0.1"], {
cwd: process.cwd(),
stdio: "ignore",
detached: true,
});
for (let i = 0; i < 40; i++) {
await sleep(250);
if (await portOpen(PORT)) return child;
}
throw new Error("Vite did not start on :1420");
}
async function main() {
const vite = await ensureVite();
console.log("Opening headed Chromium — watch the window…\n");
const browser = await chromium.launch({
headless: false,
slowMo: 400,
});
const page = await browser.newPage({ viewport: { width: 480, height: 640 } });
await page.goto(BASE, { waitUntil: "networkidle" });
// 1) Show banner
await page.waitForSelector('[data-testid="banner"]');
await sleep(800);
// 2) Type in search
const input = page.getByPlaceholder("Search…");
await input.click();
await input.fill("Meeting");
await sleep(900);
await input.fill("");
await sleep(500);
// 3) Hover / focus list rows
await page.getByText("Meeting notes").hover();
await sleep(600);
// 4) Open context menu + Paste plain
await page.getByTestId("open-menu").click();
await sleep(700);
await page.getByText("Paste plain").click();
await sleep(1000);
// 5) Click redacted password row (pastes full secret into banner)
await page.getByText("(password)").click({ modifiers: [] });
await sleep(1200);
// 6) Alt-click card row for plain
await page.getByText("(card)").click({ modifiers: ["Alt"] });
await sleep(1200);
// 7) Show transform menu → Pretty JSON not relevant; show uppercase on meeting
await page.getByText("Meeting notes").click({ button: "right" });
await sleep(600);
await page.getByText("UPPERCASE").click();
await sleep(1500);
const banner = await page.getByTestId("banner").innerText();
console.log("Final banner:", banner);
console.log("\nLeaving the window open for 5s so you can look…");
await sleep(5000);
await browser.close();
if (vite) {
try {
process.kill(-vite.pid);
} catch {
/* ignore */
}
}
console.log("Visual walkthrough finished.");
}
main().catch((err) => {
console.error(err);
process.exit(1);
});