#!/usr/bin/env node /** * Quick capture + element check for Analytics Hierarchy Section */ const { chromium } = require('playwright'); const fs = require('fs'); const path = require('path'); const TARGET_URL = process.env.TARGET_URL || 'http://localhost:3002'; const OUTPUT_DIR = process.env.OUTPUT_DIR || '/app/tests/visual/current'; (async () => { if (!fs.existsSync(OUTPUT_DIR)) { fs.mkdirSync(OUTPUT_DIR, { recursive: true }); } const browser = await chromium.launch({ headless: true, args: ['--disable-setuid-sandbox', '--no-sandbox'], }); const page = await browser.newPage({ viewport: { width: 1280, height: 900 }, }); console.log(`Navigating to: ${TARGET_URL}`); await page.goto(TARGET_URL, { waitUntil: 'networkidle', timeout: 60000 }); await page.waitForTimeout(3000); // Scroll to "Аналитическая иерархия" const heading = page.locator('text=Аналитическая иерархия').first(); if (await heading.isVisible().catch(() => false)) { console.log('Scrolling to Аналитическая иерархия section...'); await heading.scrollIntoViewIfNeeded(); await page.evaluate(() => window.scrollBy(0, -60)); await page.waitForTimeout(1500); } else { console.log('Heading not found, fallback scroll'); await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight / 3)); await page.waitForTimeout(1500); } // Scroll further down to reveal cards 3 and 4 (heatmap, commands table) await page.evaluate(() => window.scrollBy(0, 900)); await page.waitForTimeout(1000); const screenshotPath = path.join(OUTPUT_DIR, 'analytics_section.png'); await page.screenshot({ path: screenshotPath, fullPage: false }); console.log(`Screenshot saved to: ${screenshotPath}`); // Check for each card's evidence (use Russian text as it appears in the page) const checks = [ { label: 'Model tree with collapsible categories', text: 'Модели → Категории → Агенты' }, { label: 'Category bars', text: 'Дистрибуция по категориям' }, { label: 'Fit-score heatmap', text: 'Fit-score распределение' }, { label: 'Commands table', text: 'Команды' }, ]; const results = { visible: {}, issues: [] }; for (const c of checks) { const found = await page.locator(`text=${c.text}`).first().isVisible({ timeout: 3000 }).catch(() => false); if (found) { const textContent = await page.locator(`text=${c.text}`).first().textContent({ timeout: 3000 }).catch(() => ''); results.visible[c.label] = textContent; } else { results.issues.push(`${c.label} (searching text "${c.text}") — NOT FOUND`); } } const reportPath = path.join(OUTPUT_DIR, 'analytics_section_report.json'); fs.writeFileSync(reportPath, JSON.stringify(results, null, 2)); console.log(`Report saved to: ${reportPath}`); // Also write summary to stdout console.log('\n=== Scan Results ==='); if (Object.keys(results.visible).length === 4) { console.log('All 4 analytics cards are visible.'); } else { console.log(`Visible: ${Object.keys(results.visible).join(', ')}`); console.log(`Missing: ${results.issues.join(', ')}`); } await browser.close(); })().catch((err) => { console.error('Fatal error:', err); process.exit(1); });