← back to Commercialrealestate

5x/graphics-drill-assert.js

82 lines

// TK-10091 — prove every datum on graphics.html drills to a filtered/detail view.
// Verifies: 6 charts each have an onClick handler; stat-bar + expiring-table cells are real
// <a> links with correct hrefs; and the drill targets actually resolve (condos ?status/?city,
// crcp ?broker modal). Basic-auth aware (fetch() 401s on URL-only creds, so use httpCredentials).
const pw = require('/Users/macstudio3/.claude/skills/browserbase/node_modules/playwright-core');
const EXEC = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
const BASE = process.env.BASE || 'http://127.0.0.1:9943';
const CRED = { username: 'admin', password: 'DW2024!' };
const fail = [], pass = [];
const ok = (c, m) => (c ? pass : fail).push(m);

(async () => {
  const b = await pw.chromium.launch({ executablePath: EXEC, headless: true });
  const ctx = await b.newContext({ httpCredentials: CRED, viewport: { width: 1320, height: 1200 } });
  const pg = await ctx.newPage();
  const perr = []; pg.on('pageerror', e => perr.push(e.message));

  // ---- graphics.html: charts wired + anchors correct ----
  await pg.goto(BASE + '/graphics.html', { waitUntil: 'networkidle' });
  await pg.waitForTimeout(1200);
  const g = await pg.evaluate(() => {
    const inst = window.Chart ? Object.values(window.Chart.instances) : [];
    const onClicks = inst.map(c => typeof (c.config.options.onClick));
    const statHrefs = [...document.querySelectorAll('#stats a.statlink')].map(a => a.getAttribute('href'));
    const expLinks = [...document.querySelectorAll('#expTable tbody a.dl')].map(a => a.getAttribute('href'));
    const loadErr = /load error/.test(document.querySelector('#stats')?.textContent || '');
    return { charts: inst.length, onClicks, statHrefs, expLinks, loadErr };
  });
  ok(!g.loadErr, 'graphics stats loaded (no load error)');
  ok(g.charts === 6, `6 charts rendered (got ${g.charts})`);
  ok(g.onClicks.length === 6 && g.onClicks.every(t => t === 'function'), `all 6 charts have onClick (${g.onClicks.join(',')})`);
  ok(g.statHrefs.includes('/condos.html?status=fha_expired'), 'stat "FHA-expired" → condos?status=fha_expired');
  ok(g.statHrefs.includes('/condos.html?status=fha_approved'), 'stat "FHA-approved" → condos?status=fha_approved');
  ok(g.statHrefs.includes('/crcp.html'), 'stat "brokers/firms" → crcp.html');
  ok(g.statHrefs.includes('#expCard'), 'stat "lapsing <12mo" → #expCard anchor');
  ok(g.expLinks.length >= 5 && g.expLinks.some(h => h.includes('status=fha_expired&q=')), 'expiring rows link project → condos?status=fha_expired&q=');
  ok(g.expLinks.some(h => h.startsWith('/condos.html?city=')), 'expiring rows link city → condos?city=');

  // ---- drill 1: condos ?status=fha_expired lands filtered ----
  await pg.goto(BASE + '/condos.html?status=fha_expired', { waitUntil: 'networkidle' });
  await pg.waitForTimeout(1000);
  const c1 = await pg.evaluate(() => ({
    activeChip: document.querySelector('#fWarr .chip.active')?.dataset.warr || null,
    count: document.querySelector('#count')?.textContent || ''
  }));
  ok(c1.activeChip === 'fha_expired', `condos status deep-link active (chip=${c1.activeChip})`);

  // ---- drill 2: condos ?city=Torrance resolves case-insensitively + filters ----
  await pg.goto(BASE + '/condos.html?city=TORRANCE', { waitUntil: 'networkidle' });
  await pg.waitForTimeout(1000);
  const c2 = await pg.evaluate(() => {
    const active = [...document.querySelectorAll('#fCity .chip.active')].map(x => x.dataset.cy);
    return { active, count: document.querySelector('#count')?.textContent || '' };
  });
  ok(c2.active.length === 1 && /torrance/i.test(c2.active[0]), `condos ?city=TORRANCE resolved to real chip (${c2.active.join('|')})`);

  // ---- drill 3: crcp ?broker=293 opens the broker modal ----
  await pg.goto(BASE + '/crcp.html?broker=293&name=Errol%20Spiro', { waitUntil: 'networkidle' });
  await pg.waitForTimeout(1400);
  const c3 = await pg.evaluate(() => ({
    open: document.querySelector('#ov')?.classList.contains('on'),
    heading: document.querySelector('#mbody h2')?.textContent || ''
  }));
  ok(c3.open && /Errol Spiro/.test(c3.heading), `crcp ?broker=293 opened modal (${c3.heading.slice(0, 40)})`);

  // ---- drill 4: crcp ?firm=... opens the firm modal ----
  await pg.goto(BASE + '/crcp.html?firm=' + encodeURIComponent('Lyon Stahl Investment real Estate'), { waitUntil: 'networkidle' });
  await pg.waitForTimeout(1400);
  const c4 = await pg.evaluate(() => ({
    open: document.querySelector('#ov')?.classList.contains('on'),
    heading: document.querySelector('#mbody h2')?.textContent || ''
  }));
  ok(c4.open && /Lyon Stahl/i.test(c4.heading), `crcp ?firm deep-link opened firm modal (${c4.heading.slice(0, 40)})`);

  ok(perr.length === 0, `no page errors (${perr.join(' | ') || 'clean'})`);

  await b.close();
  console.log('\n  PASS (' + pass.length + '):'); pass.forEach(p => console.log('   ✓ ' + p));
  if (fail.length) { console.log('\n  FAIL (' + fail.length + '):'); fail.forEach(f => console.log('   ✗ ' + f)); process.exit(1); }
  console.log('\n  ✅ all drill assertions passed');
})().catch(e => { console.error('HARNESS ERROR:', e.message); process.exit(2); });