diff --git a/.github/qa/feature-review.mjs b/.github/qa/feature-review.mjs index 7da406cf3..d63062c92 100644 --- a/.github/qa/feature-review.mjs +++ b/.github/qa/feature-review.mjs @@ -305,7 +305,19 @@ or {"sourceTest":"...","skipReason":"source search could not prove how the test const observed = await page.evaluate(() => { const grid = document.querySelector('.consonant-CardsGrid'); const cards = grid ? [...grid.querySelectorAll('.consonant-Card')] : [...document.querySelectorAll('.consonant-Card')]; - return cards.slice(0, 12).map((card, index) => { + // Non-visual DOM artifacts (JSON-LD structured data). Card extraction + // alone is blind to script/meta tags, which made any assertion about + // them unverifiable and an automatic FAIL. Capture them explicitly. + const jsonLd = [...document.querySelectorAll('script[type="application/ld+json"]')].slice(0, 4) + .map((scriptEl, index) => ({ + n: index + 1, + parent: scriptEl.parentElement + ? `${scriptEl.parentElement.tagName.toLowerCase()}${scriptEl.parentElement.className ? `.${String(scriptEl.parentElement.className).trim().split(/\s+/)[0]}` : ''}` + : '', + attrs: [...scriptEl.attributes].map((a) => a.name).join(' '), + text: (scriptEl.textContent || '').slice(0, 1500), + })); + const cardData = cards.slice(0, 12).map((card, index) => { const title = card.querySelector('[class*="-title"]'); const links = [...card.querySelectorAll('a,button')].slice(0, 6).map((element) => ({ tag: element.tagName.toLowerCase(), @@ -322,6 +334,7 @@ or {"sourceTest":"...","skipReason":"source search could not prove how the test links, }; }); + return { cards: cardData, jsonLd }; }); console.log('[observed] ' + JSON.stringify(observed)); await page.screenshot({ path: '/tmp/feature-render.png', fullPage: true }).catch(() => {}); @@ -337,7 +350,10 @@ Expected, copied from that test: ${plan.expected} Source mapping evidence: ${JSON.stringify(plan.mappingEvidence)} Rendered first-collection cards (id, title, text, links/buttons): -${JSON.stringify(observed).slice(0, 6000) || '(no cards rendered)'} +${JSON.stringify(observed.cards).slice(0, 6000) || '(no cards rendered)'} + +Structured data blocks on the page (script[type="application/ld+json"], with parent element and content): +${JSON.stringify(observed.jsonLd).slice(0, 6000) || '(none present)'} Does the rendered DOM satisfy ONLY the selected test assertion? Do not introduce new expectations. Respond with ONLY JSON: {"verdict":"PASS"|"FAIL","reason":"one or two sentences citing observed vs expected"}`, 1500); const res = extractJson(check); @@ -352,7 +368,8 @@ Does the rendered DOM satisfy ONLY the selected test assertion? Do not introduce **Fixture cards:** ${plan.cards.length} **Expected:** ${plan.expected} **Rendered (first collection):** -${observed.map((item) => `- ${item.n}. ${item.title || item.text.slice(0, 50)}${item.links.length ? ` [${item.links.map((link) => `${link.testId || link.tag}${link.href ? ` ${link.href}` : ''}`).join(', ')}]` : ''}`).join('\n') || '_(no cards rendered)_'} +${observed.cards.map((item) => `- ${item.n}. ${item.title || item.text.slice(0, 50)}${item.links.length ? ` [${item.links.map((link) => `${link.testId || link.tag}${link.href ? ` ${link.href}` : ''}`).join(', ')}]` : ''}`).join('\n') || '_(no cards rendered)_'} +**Structured data blocks:** ${observed.jsonLd.length}${observed.jsonLd.length ? ` (first: parent \`${observed.jsonLd[0].parent}\`, ${observed.jsonLd[0].text.length} chars)` : ''} **Verdict:** ${res.reason}`); process.exit(0); diff --git a/html/e2e/verify-local.html b/html/e2e/verify-local.html new file mode 100644 index 000000000..a9db6aeac --- /dev/null +++ b/html/e2e/verify-local.html @@ -0,0 +1,14 @@ + +JSON-LD Verify v2 + +

JSON-LD verification v2, real tags and filters

+

1. HASHED payload + plaintext filter panel
+Cards arrive with hashed tags (e.g. 4x24/l1s1). The filter config above is plaintext. +The container hashes the config forward to match. Card titles literally say which +product they are tagged with, so compare each entry's keywords to its name.

+

2. Unhashed smoke.json + matching filters
+Same mechanism without hashing. Expect keywords like Photography, Stamford, CT, Create Now.

+

3. Flag OFF control, tag should be absent.

+

Console command on each page:

+
JSON.parse(document.querySelector('script[data-caas-jsonld]').textContent).itemListElement.map(e => e.item.name + '  =>  ' + (e.item.keywords || 'NO KEYWORDS'))
+ \ No newline at end of file