← back to Momentum Landing

scripts/e2e-verify.js

96 lines

#!/usr/bin/env node
/* Structural E2E + real-WebKit verification of the Hollywood Wallcoverings viewer.
 * Usage: E2E_URL="http://admin:DW2024!@127.0.0.1:PORT/" node scripts/e2e-verify.js */
const path = require('path');
const URL = process.env.E2E_URL;
if (!URL) { console.error('set E2E_URL'); process.exit(2); }
const PROOF = path.join(__dirname, '..', 'docs', 'proof');
const results = [];
function check(name, ok, detail) { results.push({ name, ok, detail }); console.log((ok ? 'PASS' : 'FAIL') + '  ' + name + (detail ? '  — ' + detail : '')); }

(async () => {
  const { chromium, webkit } = require('playwright');

  // ── 1. Structural E2E (chromium) ──────────────────────────────
  {
    const b = await chromium.launch();
    const pg = await b.newPage();
    await pg.goto(URL, { waitUntil: 'networkidle', timeout: 60000 });
    await pg.waitForSelector('.card', { timeout: 30000 });
    const m = await pg.evaluate(() => ({
      gridChildren: document.getElementById('grid').children.length,
      cards: document.querySelectorAll('.card').length,
      emptyAnchorGridChildren: [...document.getElementById('grid').children].filter(c => c.tagName === 'A' && !c.textContent.trim()).length,
      anchorWrappedCards: document.querySelectorAll('a > .card, a.card').length,
    }));
    check('[chromium] #grid.children === .card count', m.gridChildren === m.cards, `grid=${m.gridChildren} cards=${m.cards}`);
    check('[chromium] zero empty <a> grid children', m.emptyAnchorGridChildren === 0, `empty-a=${m.emptyAnchorGridChildren}`);
    check('[chromium] no <a>-wrapped cards', m.anchorWrappedCards === 0, `wrapped=${m.anchorWrappedCards}`);
    await b.close();
  }

  // ── 2. Real WebKit, creds in URL ──────────────────────────────
  {
    const b = await webkit.launch();
    const pg = await b.newPage();
    await pg.goto(URL, { waitUntil: 'networkidle', timeout: 60000 });
    await pg.waitForSelector('.card', { timeout: 30000 });

    const cards = await pg.locator('.card').count();
    check('[webkit] grid renders cards>0', cards > 0, `cards=${cards}`);

    // dw_sku chip present on cards (standing rule)
    const dwChips = await pg.locator('.card .dwsku').count();
    check('[webkit] dw_sku chip on cards', dwChips > 0, `dwsku chips=${dwChips}`);
    const dwText = await pg.locator('.card .dwsku').first().textContent();
    check('[webkit] dw_sku chip has DWHD text', /DWHD-\d+/.test(dwText || ''), `text="${dwText}"`);

    // created-date chip present with ISO title (standing rule)
    const whenChip = pg.locator('.card .when').first();
    const whenCount = await pg.locator('.card .when').count();
    const whenTitle = whenCount ? await whenChip.getAttribute('title') : '';
    check('[webkit] created date+time chip present', whenCount > 0, `when chips=${whenCount}`);
    check('[webkit] created chip has ISO in title=', /\d{4}-\d{2}-\d{2}T/.test(whenTitle || ''), `title="${whenTitle}"`);

    // panel field-tables present + collapsed on load
    const detailsTotal = await pg.locator('#facetbox details').count();
    const detailsOpen = await pg.locator('#facetbox details[open]').count();
    check('[webkit] panel field-tables present', detailsTotal >= 8, `details=${detailsTotal}`);
    check('[webkit] all field-tables collapsed on load', detailsOpen === 0, `open=${detailsOpen}`);

    // a value-click filters (open Category table, click first value, count drops)
    const before = await pg.locator('.card').count();
    await pg.locator('#facetbox details[data-f="category"] summary').click();
    await pg.waitForTimeout(200);
    const firstVal = await pg.locator('#facetbox details[data-f="category"] .frow').first().textContent();
    await pg.locator('#facetbox details[data-f="category"] .frow').first().click();
    await pg.waitForTimeout(400);
    const totalTxt = await pg.locator('#total').textContent();
    const activeRow = await pg.locator('#facetbox details[data-f="category"] .frow.active').count();
    check('[webkit] value-click filters (active row set)', activeRow === 1, `active=${activeRow}, total="${totalTxt}", clicked="${(firstVal||'').trim()}"`);

    // density changes --fs
    const fsBefore = await pg.evaluate(() => getComputedStyle(document.documentElement).getPropertyValue('--fs').trim());
    await pg.evaluate(() => { const d = document.getElementById('density'); d.value = 14; d.dispatchEvent(new Event('input')); });
    await pg.waitForTimeout(150);
    const fsAfter = await pg.evaluate(() => getComputedStyle(document.documentElement).getPropertyValue('--fs').trim());
    const colsAfter = await pg.evaluate(() => getComputedStyle(document.documentElement).getPropertyValue('--cols').trim());
    check('[webkit] density changes --fs', fsBefore !== fsAfter, `--fs ${fsBefore} -> ${fsAfter}, --cols=${colsAfter}`);

    // a card click sets navigation (data-href present + resolves to /product/)
    // clear filter first for a clean card
    await pg.locator('#clearf').click().catch(() => {});
    await pg.waitForTimeout(300);
    const href = await pg.locator('.card[data-href]').first().getAttribute('data-href');
    check('[webkit] card has data-href navigation', /^\/product\//.test(href || ''), `data-href="${href}"`);

    await pg.screenshot({ path: path.join(PROOF, 'webkit-grid.png'), fullPage: false });
    // screenshot with a filter+details open state too
    await b.close();
  }

  const failed = results.filter(r => !r.ok);
  console.log(`\n${results.length - failed.length}/${results.length} checks passed.`);
  process.exit(failed.length ? 1 : 0);
})().catch(e => { console.error('E2E ERROR:', e); process.exit(3); });