/** * Email capture — the customer "Mail Client Setup" section on the Email page. * * Captures, as the demo customer: * - whp-email-autodiscovery.png the Mail Client Setup accordion section, * expanded, with the per-domain autodiscovery * DNS records table + copyable zone block. * * Viewport-only (1440x900), redacted for our multi-server fleet: the mail-server * hostname becomes a neutral placeholder, while the brand demo domain * (whp-demo.anhh.co) is kept visible on purpose. * * Read-only: expands an accordion section for the screenshot, never saves. */ import { chromium, type Page } from 'playwright'; import { mkdir } from 'node:fs/promises'; import { resolve, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const OUT_DIR = resolve(__dirname, '../../src/assets/screenshots/whp'); function need(name: string): string { const v = process.env[name]; if (!v) throw new Error(`missing env: ${name}`); return v; } const BASE = need('WHP_BASE'); const USER = need('WHP_USER'); const PASS = need('WHP_PASS'); const HIDE_CSS = `.navbar-text, .brand-full { visibility: hidden !important; }`; async function login(page: Page) { await page.goto(`${BASE}/login.php`, { waitUntil: 'domcontentloaded' }); await page.fill('input[name="user"]', USER); await page.fill('input[name="password"]', PASS); await page.click('button[type="submit"]'); await page.waitForLoadState('networkidle'); } /** * Neutralise fleet-identifying text before the screenshot. The brand demo * domain (anhh.co) is intentionally preserved; the mail-server host and any * server hostnames/IPs are swapped for placeholders. */ async function redact(page: Page) { await page.addStyleTag({ content: HIDE_CSS }); await page.evaluate(() => { const swaps: [RegExp, string][] = [ [/mail\d+\.cloud-hosting\.io/gi, '.cloud-hosting.io'], [/whp\d+(-[a-z0-9]+)?\.cloud-hosting\.io/gi, '.cloud-hosting.io'], [/WHP\d+(-[A-Z0-9]+)?\b/g, ''], [/whp\d+(-[a-z0-9]+)?\b/gi, ''], [/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g, ''], ]; const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT); const nodes: Text[] = []; let n: Node | null = walker.nextNode(); while (n) { nodes.push(n as Text); n = walker.nextNode(); } for (const node of nodes) { let v = node.nodeValue ?? ''; for (const [re, rep] of swaps) v = v.replace(re, rep); if (v !== node.nodeValue) node.nodeValue = v; } }); } async function main() { await mkdir(OUT_DIR, { recursive: true }); const browser = await chromium.launch({ headless: true }); const ctx = await browser.newContext({ ignoreHTTPSErrors: true, viewport: { width: 1440, height: 900 }, deviceScaleFactor: 2, }); const page = await ctx.newPage(); try { await login(page); await page.goto(`${BASE}/index.php?page=email-management`, { waitUntil: 'networkidle' }); // Expand the (collapsed-by-default) "Mail Client Setup" accordion section. await page.locator('button[data-bs-target="#mail-client-setup"]').click(); await page.waitForSelector('#custMailDnsDomain', { state: 'visible' }); // The zone
 is populated on DOMContentLoaded; re-run to be safe.
    await page.evaluate(() => {
      const fn = (window as unknown as { renderCustMailDns?: () => void }).renderCustMailDns;
      if (typeof fn === 'function') fn();
    });
    await page.waitForTimeout(500);

    await redact(page);
    const item = page.locator('#mail-client-setup').locator('xpath=ancestor::div[contains(@class,"accordion-item")]');
    await item.scrollIntoViewIfNeeded();
    await page.waitForTimeout(300);
    const path = resolve(OUT_DIR, 'whp-email-autodiscovery.png');
    await item.screenshot({ path });
    console.log(`captured whp-email-autodiscovery -> ${path}`);
  } finally {
    await browser.close();
  }
}

main().catch((err) => { console.error(err); process.exit(1); });