← back to Visual Factory

scripts/clickthrough.js

88 lines

// Live click-through of the Visual Factory viewer.
// Opens :9893, screenshots the gallery, clicks each row's modal, screenshots that.

const { chromium } = require('playwright');
const path = require('node:path');
const fs = require('node:fs/promises');

const VIEWER = 'http://127.0.0.1:9893';
const ORCH   = 'http://127.0.0.1:9892';
const OUT    = path.join(__dirname, '..', 'output', 'clickthrough');

(async () => {
  await fs.mkdir(OUT, { recursive: true });
  const browser = await chromium.launch({ headless: true });
  const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 }, deviceScaleFactor: 2 });
  const page = await ctx.newPage();
  const consoleMsgs = [];
  page.on('console', m => consoleMsgs.push(`[${m.type()}] ${m.text()}`));
  page.on('pageerror', e => consoleMsgs.push(`[PAGEERROR] ${e.message}`));

  console.log('STEP 1: gallery');
  await page.goto(VIEWER, { waitUntil: 'networkidle', timeout: 15000 });
  // Give the JS poll a moment to load runs from the orch
  await page.waitForTimeout(1500);
  await page.screenshot({ path: path.join(OUT, '01-gallery.png'), fullPage: true });

  // How many cards rendered?
  const cardCount = await page.locator('.card').count();
  console.log(`  cards rendered: ${cardCount}`);

  // Activate buttons present?
  const activateCount = await page.locator('.activate-btn').count();
  console.log(`  activate buttons: ${activateCount}`);

  // STEP 2: click first card → modal
  console.log('STEP 2: click first card');
  if (cardCount > 0) {
    // Click the card body (not the activate button). We click the .meta div to avoid stopPropagation issues.
    await page.locator('.card').first().locator('.meta').click();
    await page.waitForSelector('.modal-bg.open', { timeout: 5000 });
    await page.waitForTimeout(800);
    await page.screenshot({ path: path.join(OUT, '02-modal-first.png'), fullPage: false });
    // Close the modal by clicking outside
    await page.locator('.modal-bg').click({ position: { x: 10, y: 10 } });
    await page.waitForTimeout(400);
  }

  // STEP 3: click third card (different visual_type)
  console.log('STEP 3: click third card');
  if (cardCount > 2) {
    await page.locator('.card').nth(2).locator('.meta').click();
    await page.waitForSelector('.modal-bg.open', { timeout: 5000 });
    await page.waitForTimeout(800);
    await page.screenshot({ path: path.join(OUT, '03-modal-third.png'), fullPage: false });
    await page.locator('.modal-bg').click({ position: { x: 10, y: 10 } });
    await page.waitForTimeout(400);
  }

  // STEP 4: click fifth card
  console.log('STEP 4: click fifth card');
  if (cardCount > 4) {
    await page.locator('.card').nth(4).locator('.meta').click();
    await page.waitForSelector('.modal-bg.open', { timeout: 5000 });
    await page.waitForTimeout(800);
    await page.screenshot({ path: path.join(OUT, '04-modal-fifth.png'), fullPage: false });
  }

  // Diagnostics
  await fs.writeFile(path.join(OUT, 'console.log'), consoleMsgs.join('\n'), 'utf8');
  const summary = {
    viewer_status: 'reachable',
    cards_rendered: cardCount,
    activate_buttons: activateCount,
    console_messages: consoleMsgs.length,
    screenshots: ['01-gallery.png', '02-modal-first.png', '03-modal-third.png', '04-modal-fifth.png']
  };
  await fs.writeFile(path.join(OUT, 'summary.json'), JSON.stringify(summary, null, 2));
  console.log('SUMMARY:', JSON.stringify(summary));

  // Also probe orchestrator endpoints
  const health = await (await fetch(`${ORCH}/health`)).json();
  const runs   = await (await fetch(`${ORCH}/runs`)).json();
  console.log(`ORCH /health: ${JSON.stringify(health)}`);
  console.log(`ORCH /runs: ${runs.runs?.length || 0} runs`);

  await browser.close();
})().catch(e => { console.error(e); process.exit(1); });