← back to Wallco Ai
add PDP theme smoke test — verifies every theme renders + v11 Wallpaper/Mural toggle re-wires all dependent surfaces
8919030bd2961b6078a1934f6f658ba6abfb3809 · 2026-06-01 16:48:16 -0700 · Steve Abrams
Files touched
A scripts/smoke-test-themes.js
Diff
commit 8919030bd2961b6078a1934f6f658ba6abfb3809
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Mon Jun 1 16:48:16 2026 -0700
add PDP theme smoke test — verifies every theme renders + v11 Wallpaper/Mural toggle re-wires all dependent surfaces
---
scripts/smoke-test-themes.js | 168 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 168 insertions(+)
diff --git a/scripts/smoke-test-themes.js b/scripts/smoke-test-themes.js
new file mode 100644
index 0000000..18f9c95
--- /dev/null
+++ b/scripts/smoke-test-themes.js
@@ -0,0 +1,168 @@
+#!/usr/bin/env node
+/**
+ * smoke-test-themes.js — PDP theme smoke test.
+ *
+ * Standing rule (Steve 2026-06-01): every PDP theme must be FULLY WIRED and
+ * covered by a smoke test. This proves two things against a running site:
+ * 1. EVERY theme variant (/design/:id/<slug>) renders from /api/design —
+ * i.e. the loading shell is replaced and the design title/price appear,
+ * with no uncaught page errors. Catches a theme that ships but never wires
+ * to the data layer.
+ * 2. The v11-best Wallpaper⇄Wall Mural format toggle re-renders EVERY
+ * mode-dependent surface (sold-by line, last spec row, Buy CTA label,
+ * at-scale preview badge/pill, calculator). Catches a dead toggle that
+ * looks right but does nothing.
+ *
+ * Usage:
+ * node scripts/smoke-test-themes.js # live, auto-picks a published design
+ * BASE=http://127.0.0.1:9905 node scripts/smoke-test-themes.js
+ * node scripts/smoke-test-themes.js <designId> <BASE>
+ *
+ * Playwright is resolved from a sibling project if not installed locally.
+ */
+'use strict';
+
+function loadChromium() {
+ const tries = [
+ 'playwright',
+ '/Users/stevestudio2/Projects/animals/node_modules/playwright',
+ ];
+ for (const t of tries) { try { return require(t).chromium; } catch {} }
+ console.error('FATAL: playwright not found (tried: ' + tries.join(', ') + ')');
+ process.exit(2);
+}
+
+const BASE = process.argv[3] || process.env.BASE || 'https://wallco.ai';
+const THEME_SLUGS = [
+ 'compact','bento','tabs','hero','vertical','drawer','editorial','spec',
+ 'config','stack','best','roomsplit','roomdrawer','roomzine','roomsticky',
+ 'roommin','roomshow',
+];
+
+let PASS = 0, FAIL = 0;
+const fails = [];
+function ok(name) { PASS++; console.log(' ✓ ' + name); }
+function bad(name, why) { FAIL++; fails.push(name + ' — ' + why); console.log(' ✗ ' + name + ' — ' + why); }
+
+async function pickDesignId() {
+ const https = BASE.startsWith('https') ? require('https') : require('http');
+ return new Promise((resolve) => {
+ https.get(BASE + '/api/designs?limit=8', (r) => {
+ let s = ''; r.on('data', c => s += c); r.on('end', () => {
+ try {
+ const d = JSON.parse(s);
+ const items = d.designs || d.items || (Array.isArray(d) ? d : []);
+ resolve(items.length ? items[0].id : null);
+ } catch { resolve(null); }
+ });
+ }).on('error', () => resolve(null));
+ });
+}
+
+(async () => {
+ const id = process.argv[2] || await pickDesignId();
+ if (!id) { console.error('FATAL: no design id (API gave none)'); process.exit(2); }
+ console.log(`\nPDP theme smoke test — base=${BASE} design=#${id}\n`);
+
+ const chromium = loadChromium();
+ let browser;
+ try {
+ browser = await chromium.launch();
+ } catch (e) {
+ // Bundled browser missing — fall back to the system Google Chrome install.
+ browser = await chromium.launch({ channel: 'chrome' });
+ }
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1100 } });
+
+ // ── Part 1: every theme variant renders from /api/design ────────────────
+ console.log('Part 1 — all theme variants render:');
+ for (const slug of THEME_SLUGS) {
+ const page = await ctx.newPage();
+ const errs = [];
+ page.on('pageerror', e => errs.push(e.message));
+ try {
+ const resp = await page.goto(`${BASE}/design/${id}/${slug}`, { waitUntil: 'domcontentloaded', timeout: 25000 });
+ if (!resp || resp.status() !== 200) { bad(slug, 'HTTP ' + (resp && resp.status())); await page.close(); continue; }
+ // loading shell must be replaced by real content
+ await page.waitForFunction(() => {
+ const b = document.body && document.body.innerText || '';
+ return b.length > 80 && !/Loading design/i.test(b);
+ }, { timeout: 15000 });
+ const txt = await page.evaluate(() => document.body.innerText);
+ if (errs.length) bad(slug, 'pageerror: ' + errs[0]);
+ else if (/Design not found|error/i.test(txt) && txt.length < 200) bad(slug, 'rendered error state');
+ else ok(slug + ' renders');
+ } catch (e) {
+ bad(slug, e.message.split('\n')[0]);
+ }
+ await page.close();
+ }
+
+ // ── Part 2: v11-best format toggle re-wires every dependent surface ─────
+ console.log('\nPart 2 — v11-best Wallpaper⇄Wall Mural toggle:');
+ const page = await ctx.newPage();
+ const perr = [];
+ page.on('pageerror', e => perr.push(e.message));
+ try {
+ await page.goto(`${BASE}/design/${id}/best`, { waitUntil: 'domcontentloaded', timeout: 25000 });
+ await page.waitForSelector('.fmt-tab[data-mode="mural"]', { timeout: 15000 });
+ const grab = () => page.evaluate(() => {
+ const t = s => { const el = document.querySelector(s); return el ? el.textContent.trim() : null; };
+ const pill = document.querySelector('#scale-pill');
+ return {
+ soldby: t('#meta-soldby'),
+ specsold:t('#spec-soldby'),
+ lastk: t('#spec-lastk'),
+ lastv: t('#spec-lastv'),
+ cta: t('#cta-buy'),
+ badge: t('#atscale-badge'),
+ pillHidden: pill ? getComputedStyle(pill).display === 'none' : null,
+ muralSel: document.querySelector('.fmt-tab[data-mode="mural"]')?.getAttribute('aria-selected'),
+ wallSel: document.querySelector('.fmt-tab[data-mode="wallpaper"]')?.getAttribute('aria-selected'),
+ total: t('#calc-total'),
+ };
+ });
+
+ const before = await grab();
+
+ // → Wall Mural
+ await page.click('.fmt-tab[data-mode="mural"]');
+ await page.waitForTimeout(150);
+ const mu = await grab();
+ (mu.muralSel === 'true') ? ok('mural tab becomes selected') : bad('mural tab selected', mu.muralSel);
+ (/Per panel/i.test(mu.soldby || '')) ? ok('meta sold-by → "Per panel…"') : bad('meta sold-by', mu.soldby);
+ (mu.specsold === mu.soldby) ? ok('spec sold-by matches') : bad('spec sold-by', mu.specsold);
+ (mu.lastk === 'Panel size') ? ok('last spec key → "Panel size"') : bad('last spec key', mu.lastk);
+ (/×\s*11\s*ft/.test(mu.lastv || '')) ? ok('last spec val → panel "… × 11 ft"'): bad('last spec val', mu.lastv);
+ (mu.cta === 'Order wall mural →') ? ok('Buy CTA → "Order wall mural →"') : bad('buy cta', mu.cta);
+ (/Wall mural/i.test(mu.badge || '')) ? ok('at-scale badge → mural copy') : bad('atscale badge', mu.badge);
+ (mu.pillHidden === true) ? ok('scale pill hidden in mural mode') : bad('scale pill hidden', mu.pillHidden);
+
+ // → back to Wallpaper
+ await page.click('.fmt-tab[data-mode="wallpaper"]');
+ await page.waitForTimeout(150);
+ const wp = await grab();
+ (wp.wallSel === 'true') ? ok('wallpaper tab becomes selected') : bad('wallpaper tab selected', wp.wallSel);
+ (/Per roll/i.test(wp.soldby || '')) ? ok('meta sold-by → "Per roll…"') : bad('meta sold-by(revert)', wp.soldby);
+ (wp.lastk === 'Roll length') ? ok('last spec key → "Roll length"') : bad('last spec key(revert)', wp.lastk);
+ (wp.lastv === '27 ft') ? ok('last spec val → "27 ft"') : bad('last spec val(revert)', wp.lastv);
+ (wp.cta === 'Buy by the roll →') ? ok('Buy CTA → "Buy by the roll →"') : bad('buy cta(revert)', wp.cta);
+ (/At-scale/i.test(wp.badge || '')) ? ok('at-scale badge → tile copy') : bad('atscale badge(revert)', wp.badge);
+ (wp.pillHidden === false) ? ok('scale pill visible in tile mode') : bad('scale pill visible', wp.pillHidden);
+
+ // mode-dependent surfaces actually differ between modes
+ (mu.lastk !== wp.lastk && mu.cta !== wp.cta && mu.soldby !== wp.soldby)
+ ? ok('toggle genuinely re-renders (mural ≠ wallpaper)')
+ : bad('toggle no-op', 'mural and wallpaper render identical');
+
+ perr.length ? bad('v11-best page errors', perr[0]) : ok('no v11-best page errors');
+ } catch (e) {
+ bad('v11-best toggle', e.message.split('\n')[0]);
+ }
+ await page.close();
+ await browser.close();
+
+ console.log(`\n──────── ${PASS} passed, ${FAIL} failed ────────`);
+ if (FAIL) { console.log('FAILURES:'); fails.forEach(f => console.log(' • ' + f)); process.exit(1); }
+ console.log('All theme smoke checks passed.');
+})().catch(e => { console.error('SMOKE HARNESS ERROR:', e); process.exit(1); });
← dafdb2d generator: replace dead DW-orders palette with style-guide p
·
back to Wallco Ai
·
deploy: run PDP theme smoke test as step [5b/6] after health a1e2cc7 →