Files
cannaiq/backend/scripts/test-treez-brands-detailed.ts
Kelly 698995e46f chore: bump task worker version comment
Force new git SHA to avoid CI scientific notation bug.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-14 02:02:30 -07:00

184 lines
5.6 KiB
TypeScript

/**
* Detailed brand section analysis
*/
import puppeteer, { Page } from 'puppeteer';
const STORE_ID = 'best';
async function sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function bypassAgeGate(page: Page): Promise<void> {
const ageGate = await page.$('[data-testid="age-gate-modal"]');
if (ageGate) {
console.log(' Age gate detected, bypassing...');
const btn = await page.$('[data-testid="age-gate-submit-button"]');
if (btn) await btn.click();
await sleep(2000);
}
}
async function main() {
console.log('='.repeat(60));
console.log('Detailed Brand Section Analysis');
console.log('='.repeat(60));
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
const page = await browser.newPage();
await page.setViewport({ width: 1920, height: 1080 });
await page.setRequestInterception(true);
page.on('request', (req) => {
if (['image', 'font', 'media'].includes(req.resourceType())) {
req.abort();
} else {
req.continue();
}
});
const url = `https://${STORE_ID}.treez.io/onlinemenu/brands?customerType=ADULT`;
console.log(`\nNavigating to ${url}`);
await page.goto(url, { waitUntil: 'networkidle2', timeout: 60000 });
await sleep(3000);
await bypassAgeGate(page);
await sleep(2000);
// Scroll multiple times to load all content
console.log('\n[1] Scrolling to load all content...');
let previousHeight = 0;
let scrollCount = 0;
for (let i = 0; i < 30; i++) {
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
await sleep(1500);
const currentHeight = await page.evaluate(() => document.body.scrollHeight);
const productCount = await page.evaluate(() =>
document.querySelectorAll('a[href*="/product/"]').length
);
console.log(` Scroll ${i + 1}: height=${currentHeight}, products=${productCount}`);
if (currentHeight === previousHeight) {
scrollCount++;
if (scrollCount >= 3) break;
} else {
scrollCount = 0;
}
previousHeight = currentHeight;
}
// Look at ALL h2/h3 headers on page
console.log('\n[2] Finding ALL h2/h3 headers on page...');
const headers = await page.evaluate(() => {
const results: { tag: string; text: string; parentClass: string }[] = [];
document.querySelectorAll('h2, h3').forEach((el: Element) => {
results.push({
tag: el.tagName,
text: el.textContent?.trim().slice(0, 80) || '',
parentClass: el.parentElement?.className?.slice(0, 50) || '',
});
});
return results;
});
console.log(`Found ${headers.length} headers:`);
headers.forEach((h: { tag: string; text: string }) =>
console.log(` [${h.tag}] "${h.text}"`)
);
// Get products grouped by their section heading
console.log('\n[3] Getting products per section...');
const sectionProducts = await page.evaluate(() => {
const results: { heading: string; products: number }[] = [];
// Find all sections that contain products
document.querySelectorAll('[class*="products_product__section"]').forEach((section: Element) => {
const heading = section.querySelector('h2, h3');
const headingText = heading?.textContent?.trim() || 'Unknown';
const products = section.querySelectorAll('a[href*="/product/"]');
results.push({
heading: headingText,
products: products.length,
});
});
return results;
});
console.log(`Found ${sectionProducts.length} brand sections:`);
let totalProducts = 0;
sectionProducts.forEach((s: { heading: string; products: number }) => {
console.log(` ${s.heading}: ${s.products} products`);
totalProducts += s.products;
});
console.log(`\nTotal products across all sections: ${totalProducts}`);
// Also extract brand from each product's URL/card
console.log('\n[4] Extracting brand from product URLs/cards...');
const brandCounts = await page.evaluate(() => {
const byBrand: Record<string, number> = {};
const seen = new Set<string>();
document.querySelectorAll('a[href*="/product/"]').forEach((a: Element) => {
const href = a.getAttribute('href') || '';
const img = a.querySelector('img');
const name = img?.getAttribute('alt') || '';
if (!name || seen.has(name)) return;
seen.add(name);
// Try to find brand from the card
const brandEl = a.querySelector('[class*="brand"], [class*="Brand"], span, p');
let brand = '';
// Try various methods to find brand
const allSpans = a.querySelectorAll('span, p');
allSpans.forEach((span: Element) => {
const text = span.textContent?.trim() || '';
if (text && text.length < 50 && text !== name && !text.includes('$')) {
if (!brand) brand = text;
}
});
// Fallback: get brand from parent section heading
if (!brand) {
const section = a.closest('[class*="products_product__section"]');
const heading = section?.querySelector('h2, h3');
brand = heading?.textContent?.trim() || 'Unknown';
}
byBrand[brand] = (byBrand[brand] || 0) + 1;
});
return byBrand;
});
console.log('Products by brand:');
Object.entries(brandCounts)
.sort((a, b) => (b[1] as number) - (a[1] as number))
.forEach(([brand, count]) => {
console.log(` ${brand}: ${count}`);
});
const uniqueTotal = Object.values(brandCounts).reduce((sum: number, c) => sum + (c as number), 0);
console.log(`\nTotal unique products: ${uniqueTotal}`);
await browser.close();
}
main().catch(console.error);