← 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); });