import puppeteer from "puppeteer-core"; const CHROME = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"; const BASE = "http://localhost:4004"; const browser = await puppeteer.launch({ executablePath: CHROME, headless: "new", args: ["--no-sandbox", "--disable-setuid-sandbox"], defaultViewport: { width: 1280, height: 900 }, }); const page = await browser.newPage(); await page.goto(`${BASE}/login`, { waitUntil: "networkidle0" }); await page.type('input[name="identity"]', "david@rjbulk.local"); await page.type('input[name="password"]', "ChangeMe123!"); await Promise.all([ page.click('button[type="submit"]'), page.waitForNavigation({ waitUntil: "networkidle0" }), ]); // 1. Customers page — no search await page.goto(`${BASE}/customers`, { waitUntil: "networkidle0" }); await page.screenshot({ path: "/tmp/rjbulk-screens/cust-01-no-search.png", fullPage: true }); // 2. Search "Cash" await page.type('input[name="q"]', "Cash"); await Promise.all([ page.click('button[type="submit"]'), page.waitForNavigation({ waitUntil: "networkidle0" }), ]); await page.screenshot({ path: "/tmp/rjbulk-screens/cust-02-search-cash.png", fullPage: true }); // 3. Search "sj" → just SJ Coetzee await page.goto(`${BASE}/customers?q=sj`, { waitUntil: "networkidle0" }); await page.screenshot({ path: "/tmp/rjbulk-screens/cust-03-search-sj.png", fullPage: true }); // 4. Search "zzz" → empty state await page.goto(`${BASE}/customers?q=zzz`, { waitUntil: "networkidle0" }); await page.screenshot({ path: "/tmp/rjbulk-screens/cust-04-search-empty.png", fullPage: true }); // 5. Cash Customers row → open menu → show "Protected record" await page.goto(`${BASE}/customers`, { waitUntil: "networkidle0" }); const clicked = await page.evaluate(() => { const links = Array.from(document.querySelectorAll("a")); const cashLink = links.find(a => a.textContent.includes("Cash Customers")); if (!cashLink) return false; const tr = cashLink.closest("tr"); if (!tr) return false; const btn = tr.querySelector("button"); if (!btn) return false; btn.click(); return true; }); console.log("Cash Customers row clicked:", clicked); await new Promise(r => setTimeout(r, 600)); await page.screenshot({ path: "/tmp/rjbulk-screens/cust-05-cash-menu.png", fullPage: true }); // 6. SJ Coetzee row → open menu → delete should be available (not protected) await page.goto(`${BASE}/customers`, { waitUntil: "networkidle0" }); const clicked2 = await page.evaluate(() => { const links = Array.from(document.querySelectorAll("a")); const sjLink = links.find(a => a.textContent.trim() === "SJ Coetzee"); if (!sjLink) return false; const tr = sjLink.closest("tr"); if (!tr) return false; const btn = tr.querySelector("button"); if (!btn) return false; btn.click(); return true; }); console.log("SJ Coetzee row clicked:", clicked2); await new Promise(r => setTimeout(r, 600)); await page.screenshot({ path: "/tmp/rjbulk-screens/cust-06-sj-menu.png", fullPage: true }); await browser.close(); console.log("done");