← back to Dw Yolo Loop
Artmura landing: add Playwright component click-through (39/39 live regression test)
4ec71ec936ce81019b1bc7994e92be43738350ea · 2026-06-12 11:53:44 -0700 · Steve Abrams
Files touched
A artmura-site/clickthrough.js
Diff
commit 4ec71ec936ce81019b1bc7994e92be43738350ea
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Fri Jun 12 11:53:44 2026 -0700
Artmura landing: add Playwright component click-through (39/39 live regression test)
---
artmura-site/clickthrough.js | 151 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 151 insertions(+)
diff --git a/artmura-site/clickthrough.js b/artmura-site/clickthrough.js
new file mode 100644
index 0000000..45a8bf3
--- /dev/null
+++ b/artmura-site/clickthrough.js
@@ -0,0 +1,151 @@
+/* Live click-through of every Artmura landing component. Reports PASS/FAIL per component. */
+const { chromium } = require('/Users/stevestudio2/Projects/ventura-corridor/node_modules/playwright');
+const BASE = process.env.BASE || 'https://artmura.designerwallcoverings.com';
+const results = [];
+const ok = (n, cond, extra='') => { results.push({ n, pass: !!cond, extra }); console.log(`${cond?'✓':'✗'} ${n}${extra?' — '+extra:''}`); };
+
+(async () => {
+ const browser = await chromium.launch();
+ const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
+ const errs = []; page.on('console', m => { if (m.type()==='error') errs.push(m.text()); });
+ page.on('pageerror', e => errs.push(e.message));
+
+ // ---------- HOMEPAGE ----------
+ await page.goto(BASE, { waitUntil: 'networkidle' });
+ await page.waitForSelector('.card', { timeout: 15000 });
+
+ const wm = (await page.textContent('#cUL'))?.trim();
+ ok('Wordmark = Designer Wallcoverings', wm === 'Designer Wallcoverings', wm);
+ ok('Wordmark links to DW', (await page.getAttribute('#cUL','href'))?.includes('designerwallcoverings.com'));
+
+ const navTxt = await page.$$eval('#cUR a', as => as.map(a=>a.textContent.trim()+' → '+a.getAttribute('href')));
+ ok('Nav rendered (4 links)', navTxt.length === 4, navTxt.join(' | '));
+ ok('Nav "Shop the Collection" → /collections/artmura', navTxt.some(t=>/Shop the Collection/.test(t) && /collections\/artmura/.test(t)));
+
+ ok('Hero eyebrow', (await page.textContent('#heroEyebrow'))?.includes('Designer Wallcoverings'));
+ ok('Hero H1 = Artmura', (await page.textContent('#heroH1'))?.trim() === 'Artmura');
+ const slides = await page.$$('.hero .slide'); ok('Hero rotating slides present', slides.length >= 2, slides.length+' slides');
+
+ const total = parseInt(await page.textContent('#cnt'), 10);
+ ok('Grid count shown', total === 161, total+' products');
+ const cardCount = (await page.$$('.card')).length;
+ ok('Grid renders cards', cardCount === total, cardCount+' cards');
+
+ // NO visible prices anywhere on the page
+ const bodyTxt = await page.textContent('body');
+ ok('No $ prices visible on homepage', !/\$\d/.test(bodyTxt));
+ ok('Cards show "View →"', (await page.textContent('.card .pr'))?.includes('View'));
+
+ // book chips filter
+ const chips = await page.$$('#chips .chip');
+ ok('Book chips present', chips.length >= 2, chips.length+' chips');
+ if (chips.length > 1) {
+ await chips[1].click(); await page.waitForTimeout(400);
+ const after = parseInt(await page.textContent('#cnt'),10);
+ ok('Book chip filters grid', after > 0 && after <= total, after+' after filter');
+ await (await page.$$('#chips .chip'))[0].click(); await page.waitForTimeout(300); // reset to All
+ }
+
+ // color filter
+ const dots = await page.$$('#colorbar .cdot');
+ ok('Color filter bar present', dots.length >= 2, dots.length+' color buckets');
+ if (dots.length > 1) {
+ await dots[1].click(); await page.waitForTimeout(400);
+ const after = parseInt(await page.textContent('#cnt'),10);
+ ok('Color filter narrows grid', after > 0 && after <= total, after+' items');
+ await (await page.$$('#colorbar .cdot'))[0].click(); await page.waitForTimeout(300);
+ }
+
+ // pattern select
+ const patOpts = await page.$$eval('#pattern option', os => os.length);
+ ok('Pattern select populated', patOpts > 1, patOpts+' options');
+
+ // sort select (price options removed)
+ const sortOpts = await page.$$eval('#sort option', os => os.map(o=>o.value));
+ ok('Sort options present', sortOpts.length >= 5, sortOpts.join(','));
+ ok('Price sort options removed', !sortOpts.includes('price_asc') && !sortOpts.includes('price_desc'));
+ await page.selectOption('#sort','color'); await page.waitForTimeout(400);
+ ok('Sort by Color works (grid still full)', (await page.$$('.card')).length === total);
+
+ // density slider
+ await page.fill('#density','6').catch(()=>{});
+ await page.$eval('#density', el => { el.value='6'; el.dispatchEvent(new Event('input',{bubbles:true})); });
+ await page.waitForTimeout(200);
+ const cols = await page.$eval(':root', () => getComputedStyle(document.documentElement).getPropertyValue('--cols'));
+ ok('Density slider sets --cols', cols.trim()==='6', '--cols='+cols.trim());
+
+ // homepage share cluster
+ const shareBtns = await page.$$('#shareCollection .sbtn');
+ ok('Homepage share icons (5)', shareBtns.length === 5, shareBtns.length+' icons');
+ const pinHref = await page.getAttribute('#shareCollection .sbtn.pin','href');
+ ok('Pinterest share → real collection', /pinterest\.com/.test(pinHref) && /collections%2Fartmura/.test(pinHref));
+
+ // shopbar
+ ok('Shopbar → real collection', (await page.getAttribute('#shopbar','href'))?.includes('/collections/artmura'));
+
+ // ---------- PDP (click a card) ----------
+ const firstHandle = await page.$eval('.card', c => c.dataset.h);
+ await page.click('.card'); await page.waitForURL(/\/product\//, { timeout: 10000 });
+ await page.waitForSelector('.pdp .rail h1', { timeout: 10000 });
+ ok('Card click → PDP route', page.url().includes('/product/'+firstHandle), page.url());
+
+ ok('PDP wordmark = Designer Wallcoverings', (await page.textContent('.corner.ul'))?.trim()==='Designer Wallcoverings');
+ ok('PDP breadcrumb starts at DW', (await page.textContent('.crumbs'))?.includes('Designer Wallcoverings'));
+ ok('PDP title present', (await page.textContent('.rail h1'))?.length > 0);
+
+ const pdpBody = await page.textContent('.pdp');
+ ok('PDP shows NO $ price', !/\$\d/.test(pdpBody));
+
+ const cta = await page.getAttribute('.rail .cta','href');
+ ok('PDP main CTA → real DW product', /designerwallcoverings\.com\/products\//.test(cta), cta);
+ const sampleCta = (await page.$$('.rail .cta'))[1];
+ ok('PDP sample CTA → real product', /designerwallcoverings\.com\/products\//.test(await sampleCta.getAttribute('href')));
+
+ const specRows = await page.$$('.spec .row');
+ ok('PDP spec table rows', specRows.length >= 4, specRows.length+' rows');
+
+ // gallery thumb swap
+ const thumbs = await page.$$('.thumbs .t');
+ if (thumbs.length > 1) {
+ const before = await page.$eval('#main', el => el.style.backgroundImage);
+ await thumbs[1].click(); await page.waitForTimeout(250);
+ const after = await page.$eval('#main', el => el.style.backgroundImage);
+ ok('Gallery thumb swaps main image', before !== after, thumbs.length+' thumbs');
+ } else ok('Gallery (single image, no thumbs)', true, '1 image');
+
+ // share on PDP → real product
+ const pdpShare = await page.$$('#share .sbtn');
+ ok('PDP share icons (5)', pdpShare.length === 5, pdpShare.length+' icons');
+ const pdpPin = await page.getAttribute('#share .sbtn.pin','href');
+ ok('PDP Pinterest → real product', /pinterest\.com/.test(pdpPin) && /products/.test(decodeURIComponent(pdpPin)));
+
+ // pairs well with
+ await page.waitForTimeout(600);
+ const pairsHidden = await page.getAttribute('#pairs','hidden');
+ const pairCards = await page.$$('#pairGrid .pcard');
+ ok('"Pairs well with" renders', pairsHidden===null && pairCards.length>0, pairCards.length+' recs');
+
+ // canonical → real product
+ const canon = await page.$eval('link[rel=canonical]', l => l.href).catch(()=>null);
+ ok('PDP canonical → real DW product', canon && /designerwallcoverings\.com\/products\//.test(canon), canon||'');
+
+ // click a "pairs" card → navigates to another PDP
+ if (pairCards.length) {
+ await pairCards[0].click(); await page.waitForTimeout(600);
+ ok('Pairs card navigates', page.url().includes('/product/'));
+ }
+
+ // ---------- real collection page resolves ----------
+ const collResp = await page.goto('https://www.designerwallcoverings.com/collections/artmura', { waitUntil:'domcontentloaded' });
+ ok('Real /collections/artmura is live', collResp.status()===200, 'http '+collResp.status());
+
+ ok('No console/page errors', errs.length===0, errs.slice(0,3).join(' | '));
+
+ await page.screenshot({ path: '/tmp/artmura-home.png' }).catch(()=>{});
+ await browser.close();
+
+ const passed = results.filter(r=>r.pass).length;
+ console.log(`\n=== ${passed}/${results.length} components PASS ===`);
+ const fails = results.filter(r=>!r.pass);
+ if (fails.length) { console.log('FAILS:'); fails.forEach(f=>console.log(' ✗ '+f.n+(f.extra?' ('+f.extra+')':''))); process.exit(1); }
+})().catch(e => { console.error('RUNNER ERROR', e); process.exit(2); });
← 8382b47 Artmura: apply Cost/.75/.85 retail markup + hide prices on l
·
back to Dw Yolo Loop
·
Vendor-landing template: generalize to any DW line + dw-vend 584bf8a →