← back to Ventura Corridor

scripts/verify_adst_patch.ts

95 lines

/**
 * Verify the patched ads_transparency selectors on 3 known-active domains.
 * Run: npx tsx scripts/verify_adst_patch.ts
 */
import { chromium, Browser } from 'playwright';

const DOMAINS = ['clearchoice.com', 'discounttirecenters.com', 'paymentcloud.com'];

async function scrapeOne(browser: Browser, domain: string) {
  const url = `https://adstransparency.google.com/?domain=${encodeURIComponent(domain)}&region=US`;
  const ctx = await browser.newContext({
    viewport: { width: 1280, height: 1600 },
    userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36',
  });
  const page = await ctx.newPage();
  try {
    await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 25_000 });
    await page.waitForFunction(
      () => document.querySelector('creative-preview, .ads-count') !== null
            || document.body.innerText.includes('No ads to show')
            || document.body.innerText.includes('No advertisers'),
      { timeout: 20_000 }
    ).catch(() => {});
    await page.waitForTimeout(1500);

    return await page.evaluate(() => {
      const out: any = { domain: window.location.href };

      const advEl = document.querySelector(
        'creative-preview .advertiser-name, .thumbnail-container .advertiser-name'
      );
      out.advertiser_name = advEl ? (advEl as HTMLElement).innerText.trim() : null;

      const adsCountEl = document.querySelector('.ads-count');
      if (adsCountEl) {
        const raw = (adsCountEl as HTMLElement).innerText.trim();
        const m = raw.match(/([\d,]+)/);
        out.ad_count_raw = raw;
        out.ad_count = m ? parseInt(m[1].replace(/,/g, ''), 10) : null;
      } else {
        const cardCount = document.querySelectorAll('creative-preview').length;
        out.ad_count_raw = `${cardCount} (card count fallback)`;
        out.ad_count = cardCount > 0 ? cardCount : null;
      }

      const cards = Array.from(document.querySelectorAll('creative-preview'));
      out.creatives_count = cards.length;
      out.creatives = cards.slice(0, 3).map((c) => {
        const a = c.querySelector('a');
        const img = c.querySelector('html-renderer img, img');
        const nameEl = c.querySelector('.advertiser-name');
        const ariaLabel = a?.getAttribute('aria-label') || undefined;
        const href = a?.getAttribute('href') || undefined;
        const creativeIdMatch = href ? href.match(/creative\/(CR\w+)/) : null;
        return {
          headline: ariaLabel,
          body: nameEl ? (nameEl as HTMLElement).innerText.trim() : undefined,
          thumb: (img && (img as HTMLImageElement).src) || undefined,
          creative_id: creativeIdMatch ? creativeIdMatch[1] : undefined,
        };
      });

      const txt = document.body.innerText;
      out.no_results = /No ads to show|No advertisers|We didn't find any/i.test(txt);
      return out;
    });
  } finally {
    await ctx.close().catch(() => {});
  }
}

const browser = await chromium.launch({ headless: true });
for (const domain of DOMAINS) {
  process.stdout.write(`\n=== ${domain} ===\n`);
  try {
    const r = await scrapeOne(browser, domain);
    console.log(`  advertiser_name : ${r.advertiser_name}`);
    console.log(`  ad_count_raw    : ${r.ad_count_raw}`);
    console.log(`  ad_count        : ${r.ad_count}`);
    console.log(`  creatives_count : ${r.creatives_count}`);
    console.log(`  no_results      : ${r.no_results}`);
    r.creatives.forEach((c: any, i: number) => {
      console.log(`  creative[${i}]:`);
      console.log(`    headline   : ${c.headline}`);
      console.log(`    body       : ${c.body}`);
      console.log(`    thumb      : ${c.thumb?.slice(0, 80)}`);
      console.log(`    creative_id: ${c.creative_id}`);
    });
  } catch (e: any) {
    console.log(`  ERROR: ${e.message}`);
  }
}
await browser.close();
console.log('\nDone.');