← back to Fentucci Naturals
scripts/lookbook-verify.js
71 lines
#!/usr/bin/env node
// FIX 3 — verify the 5 zero-yield lookbook COLLECTIONS pages with a REAL
// rendered browser (Playwright chromium). The plain-fetch scraper found 0
// SKU-bearing items on these Wix Pro Gallery pages; gallery items can be
// client-hydrated, so the SSR-only result must be proven, not assumed.
// Counts ALL rendered wixstatic media imgs (empty alts included — an earlier
// version wrongly junk-filtered alt="" and reported 0 rendered imgs), checks
// alts AND rendered page text for SKU-shaped tokens, and writes evidence to
// data/lookbook-verification.json.
const fs = require('fs');
const path = require('path');
const { chromium } = require('/Users/macstudio3/Projects/Designer-Wallcoverings/node_modules/playwright');
const BASE = 'https://www.tokiwausa.com';
const PAGES = ['reflectionvol1', 'reflectionsvol2', 'greenbookcollection', 'glassbeadscollection', 'textilecollection'];
// site-chrome imgs (logo strip, social icons) — identified by size/uri, not alt
const SKUISH = /^([A-Z]{1,4}-?\d{3,5}[A-D]?|005-\d{3}-003(-\d+)?|[A-Z -]{2,20}NO\.\s?\d+|[A-Za-z ]+#\d+|[A-Z]{2,6}-[A-Z0-9]{0,8}\d)$/i;
const TEXT_SKU = /\b([A-Z]{1,4}-\d{3,5}[A-D]?|005-\d{3}-003-\d+|[A-Z]{2,10}\s?NO\.\s?\d+|KP\d{4}|GB-\d{4}|PD-\d{4})\b/g;
(async () => {
const browser = await chromium.launch();
const out = { checked_at: new Date().toISOString(), method: 'playwright chromium, waitUntil load + wix-media img-count stabilization (Wix long-polls; networkidle never fires) + full scroll (lazy-load) + settle; rendered DOM imgs incl. empty alts + rendered innerText scanned for SKU-shaped tokens', pages: {} };
for (const slug of PAGES) {
const page = await browser.newPage({ userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0 Safari/537.36' });
const url = `${BASE}/${slug}`;
await page.goto(url, { waitUntil: 'load', timeout: 60000 });
let prev = -1;
for (let i = 0; i < 20; i++) {
await page.waitForTimeout(1500);
const n = await page.$$eval('img', els => els.filter(e => (e.currentSrc || e.src || '').includes('static.wixstatic.com/media')).length);
if (n === prev && n > 0 && i >= 3) break;
prev = n;
}
await page.evaluate(async () => {
for (let y = 0; y < document.body.scrollHeight; y += 600) { window.scrollTo(0, y); await new Promise(r => setTimeout(r, 150)); }
window.scrollTo(0, document.body.scrollHeight);
});
await page.waitForTimeout(6000);
const imgs = await page.$$eval('img', els => els.map(e => ({
src: e.currentSrc || e.src || '', alt: (e.alt || '').trim(),
w: e.naturalWidth, h: e.naturalHeight, visible: !!(e.offsetWidth || e.offsetHeight),
})));
const text = await page.evaluate(() => document.body.innerText);
const media = imgs.filter(i => i.src.includes('static.wixstatic.com/media'));
// gallery items = visible, substantial renders (logo strip ~152x40, icons 20x20 excluded by size)
const gallery = media.filter(i => i.visible && i.w >= 100 && i.h >= 100);
const altBases = gallery.map(i => i.alt.replace(/\.(jpe?g|png|webp)$/i, '')).filter(Boolean);
const skuAlts = [...new Set(altBases.filter(b => SKUISH.test(b)))];
const skuText = [...new Set([...text.matchAll(TEXT_SKU)].map(m => m[1]))];
out.pages[slug] = {
url,
page_title: await page.title(),
rendered_wix_media_imgs: media.length,
rendered_gallery_items: gallery.length,
gallery_items_with_alt: altBases.length,
sku_bearing_alts: skuAlts,
sku_tokens_in_rendered_text: skuText,
sample_alts: [...new Set(altBases)].slice(0, 15),
sample_gallery_srcs: gallery.slice(0, 5).map(i => i.src.slice(0, 120)),
verdict: (skuAlts.length || skuText.length)
? 'SKU-BEARING — scrape into pipeline'
: 'lookbook-only: gallery renders room-scene/editorial spreads with no per-SKU alts or SKU text',
};
console.log(slug, JSON.stringify(out.pages[slug], null, 1));
await page.close();
}
await browser.close();
fs.writeFileSync(path.join(__dirname, '..', 'data', 'lookbook-verification.json'), JSON.stringify(out, null, 2));
console.log('wrote data/lookbook-verification.json');
})();