Files
cannaiq/backend/scripts/test-treez-inventory.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

167 lines
5.4 KiB
TypeScript

import puppeteer from 'puppeteer';
async function sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function main() {
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 });
// Go to a product detail page
await page.goto('https://shop.bestdispensary.com/brand/dime', {
waitUntil: 'networkidle2',
timeout: 60000
});
await sleep(3000);
// Bypass age gate
const ageGate = await page.$('[data-testid="age-gate-modal"]');
if (ageGate) {
const btn = await page.$('[data-testid="age-gate-submit-button"]');
if (btn) await btn.click();
await sleep(2000);
}
// Get first product URL
const productUrl = await page.evaluate(() => {
const a = document.querySelector('a[href*="/product/"]');
return a ? 'https://shop.bestdispensary.com' + a.getAttribute('href') : null;
});
if (!productUrl) {
console.log('No product found');
await browser.close();
return;
}
console.log('Checking product: ' + productUrl + '\n');
await page.goto(productUrl, { waitUntil: 'networkidle2', timeout: 30000 });
await sleep(2000);
// Look for inventory/stock info
const inventoryData = await page.evaluate(() => {
const data: any = {};
// Check for stock/inventory elements
const stockSelectors = [
'[class*="stock"]',
'[class*="Stock"]',
'[class*="inventory"]',
'[class*="Inventory"]',
'[class*="quantity"]',
'[class*="Quantity"]',
'[class*="available"]',
'[class*="Available"]',
'[class*="in-stock"]',
'[class*="out-of-stock"]',
'[data-stock]',
'[data-quantity]',
'[data-inventory]',
];
data.stockElements = [];
stockSelectors.forEach(sel => {
document.querySelectorAll(sel).forEach(el => {
data.stockElements.push({
selector: sel,
text: el.textContent?.trim().slice(0, 100),
dataAttrs: Object.keys((el as HTMLElement).dataset || {}),
});
});
});
// Check for "Add to cart" button state (disabled = out of stock)
const addToCartBtn = document.querySelector('button[class*="add"], button[class*="cart"]');
data.addToCartBtn = {
found: !!addToCartBtn,
disabled: (addToCartBtn as HTMLButtonElement)?.disabled,
text: addToCartBtn?.textContent?.trim(),
};
// Check page source for inventory keywords
const bodyText = document.body.innerText;
data.hasStockText = bodyText.includes('stock') || bodyText.includes('Stock');
data.hasInventoryText = bodyText.includes('inventory') || bodyText.includes('Inventory');
data.hasQuantityText = bodyText.includes('quantity') || bodyText.includes('Quantity');
data.hasAvailableText = bodyText.includes('available') || bodyText.includes('Available');
// Get all data attributes on the page
data.allDataAttrs = [];
document.querySelectorAll('[data-product-id], [data-sku], [data-variant]').forEach(el => {
const attrs: any = {};
Object.entries((el as HTMLElement).dataset).forEach(([k, v]) => {
attrs[k] = v;
});
if (Object.keys(attrs).length > 0) {
data.allDataAttrs.push(attrs);
}
});
// Check for JSON-LD or schema data
const scripts = document.querySelectorAll('script[type="application/ld+json"]');
data.jsonLd = [];
scripts.forEach(s => {
try {
const json = JSON.parse(s.textContent || '');
data.jsonLd.push(json);
} catch {}
});
// Check Next.js data
const nextData = document.getElementById('__NEXT_DATA__');
if (nextData) {
try {
const json = JSON.parse(nextData.textContent || '');
data.hasNextData = true;
data.nextDataKeys = Object.keys(json);
// Look for product data in props
if (json.props?.pageProps?.product) {
data.productFromNext = json.props.pageProps.product;
}
if (json.props?.pageProps) {
data.pagePropsKeys = Object.keys(json.props.pageProps);
}
} catch {}
}
return data;
});
console.log('Inventory Analysis:\n');
console.log('Stock elements found: ' + inventoryData.stockElements.length);
inventoryData.stockElements.forEach((s: any) => {
console.log(' - ' + s.selector + ': "' + s.text + '"');
});
console.log('\nAdd to Cart button: ' + JSON.stringify(inventoryData.addToCartBtn));
console.log('\nText checks:');
console.log(' Has "stock": ' + inventoryData.hasStockText);
console.log(' Has "inventory": ' + inventoryData.hasInventoryText);
console.log(' Has "quantity": ' + inventoryData.hasQuantityText);
console.log(' Has "available": ' + inventoryData.hasAvailableText);
console.log('\nData attributes: ' + JSON.stringify(inventoryData.allDataAttrs));
console.log('\nJSON-LD: ' + JSON.stringify(inventoryData.jsonLd, null, 2));
if (inventoryData.hasNextData) {
console.log('\nNext.js data found!');
console.log(' Keys: ' + inventoryData.nextDataKeys);
console.log(' Page props keys: ' + inventoryData.pagePropsKeys);
if (inventoryData.productFromNext) {
console.log('\n Product data from Next.js:');
console.log(JSON.stringify(inventoryData.productFromNext, null, 2));
}
}
await browser.close();
}
main().catch(console.error);