← back to Marketing Command Center

scripts/screenrecord-posts.js

93 lines

// /screenrecord harness v2 — nav by REAL label text (nav <a>s have no href/data-panel).
const { chromium } = require('playwright');
const fs = require('fs');
const path = require('path');

const BASE = 'https://marketing.designerwallcoverings.com';
const CREDS = { username: 'admin', password: 'DW2024!' };
const ROOT = path.join(__dirname, '..', 'screenrecord');
const LOG = path.join(ROOT, 'debug-log.jsonl');
fs.mkdirSync(path.join(ROOT, 'rec'), { recursive: true });
const append = o => fs.appendFileSync(LOG, JSON.stringify(o) + '\n');

// label text (regex-safe fragment) → short id
const PANELS = [
  ['Streams Board', 'board'],
  ['Social Scheduler', 'social'],
  ['Engine', 'engine'],
  ['Composer', 'composer'],
  ['Campaign Templates', 'templates'],
  ['Suggested Copy', 'copy'],
  ['Marketing Calendar', 'calendar'],
];
const prior = fs.existsSync(LOG) ? fs.readFileSync(LOG, 'utf8').trim().split('\n').filter(Boolean).map(l => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean) : [];
const priorBad = new Set(prior.filter(r => r.errors && r.errors.length).map(r => r.id));
function orderFor(run, els) {
  if (run === 0) return els;
  if (run === 1) return [...els].reverse();
  if (run === 2) return [...els].sort((a, b) => (b[1] === 'engine') - (a[1] === 'engine'));
  if (run === 3) { const s = [...els]; for (let i = s.length - 1; i > 0; i--) { const j = (i * 7 + run * 13) % (i + 1); [s[i], s[j]] = [s[j], s[i]]; } return s; }
  return [...els].sort((a, b) => priorBad.has(b[1]) - priorBad.has(a[1]));
}

async function inspectCards(page) {
  return await page.evaluate(() => {
    const sels = ['.bd-card', '.card', '[class*="card"]', '.post', '[class*="post"]', '.engine-card', '.sched-card', 'article'];
    const seen = new Set(); const cards = [];
    for (const s of sels) for (const el of document.querySelectorAll(s)) {
      if (seen.has(el)) continue; seen.add(el);
      const r = el.getBoundingClientRect();
      if (r.width < 40 || r.height < 20) continue;
      cards.push(el);
    }
    let blank = 0, withText = 0, withImg = 0, brokenImg = 0;
    const samples = [];
    for (const el of cards) {
      const txt = (el.innerText || '').trim();
      const imgs = [...el.querySelectorAll('img')];
      const loaded = imgs.filter(i => i.complete && i.naturalWidth > 0);
      const broken = imgs.filter(i => i.complete && i.naturalWidth === 0 && i.getAttribute('src'));
      if (txt.length < 2 && loaded.length === 0) blank++;
      if (txt.length >= 2) withText++;
      if (loaded.length) withImg++;
      brokenImg += broken.length;
      if (samples.length < 3 && (txt.length < 2 || broken.length)) samples.push({ textLen: txt.length, imgs: imgs.length, loaded: loaded.length, broken: broken.length, brokenSrc: (broken[0] && broken[0].getAttribute('src') || '').slice(0, 90), cls: el.className.slice(0, 40) });
    }
    const title = (document.querySelector('.panel-title, h1, h2')?.innerText || '').trim().slice(0, 50);
    return { title, total: cards.length, blank, withText, withImg, brokenImg, samples };
  });
}

(async () => {
  for (let run = 0; run < 5; run++) {
    const browser = await chromium.launch();
    const ctx = await browser.newContext({ viewport: { width: 1600, height: 900 }, httpCredentials: CREDS, recordVideo: { dir: path.join(ROOT, 'rec', `run${run}`) } });
    const page = await ctx.newPage();
    const errs = [], api = [], bad = [];
    page.on('console', m => { if (m.type() === 'error') errs.push(m.text().slice(0, 200)); });
    page.on('pageerror', e => errs.push(('PAGEERROR ' + e).slice(0, 200)));
    page.on('response', async r => {
      const u = r.url();
      if (r.status() >= 400) bad.push(r.status() + ' ' + u.replace(BASE, '').slice(0, 110));
      if (/\/api\//.test(u)) { let len = -1; try { len = (await r.body()).length; } catch {} api.push({ url: u.replace(BASE, ''), status: r.status(), len }); }
    });
    await page.goto(BASE + '/', { waitUntil: 'domcontentloaded' });
    await page.waitForTimeout(2500);

    for (const [label, id] of orderFor(run, PANELS)) {
      errs.length = 0; api.length = 0; bad.length = 0;
      let clicked = false;
      try {
        const el = page.locator('a', { hasText: new RegExp(label.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i') }).first();
        if (await el.count()) { await el.click({ timeout: 4000 }); clicked = true; }
      } catch {}
      await page.waitForTimeout(2800);
      let cards = null; try { cards = await inspectCards(page); } catch (e) { cards = { err: '' + e }; }
      append({ run, id, label, clicked, title: cards.title, cards, api: api.slice(0, 10), bad: [...new Set(bad)].slice(0, 8), errors: [...new Set(errs)].slice(0, 6) });
      console.log(`run${run} ${id.padEnd(9)} title="${cards.title}" clicked=${clicked} cards=${cards.total} blank=${cards.blank} text=${cards.withText} img=${cards.withImg} broken=${cards.brokenImg} 4xx=${[...new Set(bad)].length} err=${[...new Set(errs)].length}`);
    }
    await ctx.close(); await browser.close();
  }
  console.log('DONE →', LOG);
})().catch(e => { console.error('FATAL', e); process.exit(1); });