← back to Quadrille Showroom

proto/verify-v10.cjs

104 lines

const { chromium } = require('playwright');
const path = require('path');
const fs = require('fs');

(async () => {
  const shotsDir = path.join(__dirname, 'shots');
  fs.mkdirSync(shotsDir, { recursive: true });
  // Serve the proto same-origin (proxies /api -> backend :7690) so CORS isn't an issue,
  // exactly as it'll behave behind proto/proxy-server.cjs in real use.
  const { spawn } = require('child_process');
  const PORT = 7693;
  const srv = spawn(process.execPath, [path.join(__dirname, 'proxy-server.cjs')],
    { env: { ...process.env, PROTO_PORT: String(PORT) }, stdio: 'inherit' });
  await new Promise(r => setTimeout(r, 700));
  const fileUrl = `http://127.0.0.1:${PORT}/v10-concierge.html`;

  const browser = await chromium.launch({ headless: true });
  const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 } });
  const page = await ctx.newPage();

  const errors = [];
  page.on('console', m => { if (m.type() === 'error') errors.push(m.text()); });
  page.on('pageerror', e => errors.push('PAGEERROR: ' + e.message));

  await page.goto(fileUrl, { waitUntil: 'networkidle' });

  // wait for feed to load (concierge announces board count)
  await page.waitForFunction(() => window.PRODUCTS && window.PRODUCTS.length > 0, { timeout: 8000 })
    .catch(() => {});
  const total = await page.evaluate(() => (window.PRODUCTS || []).length);
  console.log('Products loaded:', total);

  // ---- Query 1: "show me blues" via API ----
  const q1 = await page.evaluate(() => {
    const { results } = window.runQuery('show me blues');
    const allBlue = results.every(p => /aqua|blue|navy|turquoise|capri/i.test(p.pattern_name));
    return { n: results.length, allBlue, sample: results.slice(0,3).map(p=>p.pattern_name) };
  });
  console.log('Q1 "show me blues":', q1.n, 'results, all blue-named:', q1.allBlue);
  console.log('   sample:', q1.sample);

  // ---- Query 2: "like Aga but darker" ----
  const q2 = await page.evaluate(() => {
    const { results } = window.runQuery('like Aga but darker');
    const allAga = results.every(p => /^aga/i.test(p.pattern_name));
    return { n: results.length, allAga, sample: results.slice(0,4).map(p=>p.pattern_name) };
  });
  console.log('Q2 "like Aga but darker":', q2.n, 'results, all Aga family:', q2.allAga);
  console.log('   sample:', q2.sample);

  // ---- Query 3: stripes (style) ----
  const q3 = await page.evaluate(() => {
    const { results } = window.runQuery('something with stripes');
    return { n: results.length, sample: results.slice(0,3).map(p=>p.pattern_name) };
  });
  console.log('Q3 "stripes":', q3.n, 'results; sample:', q3.sample);

  // ---- Drive the UI: click a chip and confirm boards render in DOM ----
  await page.click('text=Warm neutrals');
  await page.waitForTimeout(900);
  const boardsRendered = await page.evaluate(() => document.querySelectorAll('.board').length);
  console.log('Warm neutrals chip -> boards in DOM:', boardsRendered);

  // ---- Conversational refinement: "show me greens" then "now in blue" ----
  const conv = await page.evaluate(() => {
    const a = window.runQuery('show me greens');
    const greens = a.results.length;
    const b = window.runQuery('now in blue');   // refinement carries context, swaps color axis
    const allBlue = b.results.every(p => /aqua|blue|navy|turquoise|capri/i.test(p.pattern_name));
    return { greens, blueN: b.results.length, allBlue, line: b.line };
  });
  console.log('Refinement "show greens" -> "now in blue":', conv.greens, '->', conv.blueN,
              '| all blue:', conv.allBlue);
  console.log('   concierge said:', conv.line.replace(/<[^>]+>/g,''));

  // ---- Drive UI for beauty shot: greens, then set aside two, then "now in blue" ----
  await page.fill('#askInput', 'fresh greens');
  await page.click('.ask button');
  await page.waitForTimeout(900);
  // set aside the first three boards (dispatch via JS — they may be below the fold)
  await page.evaluate(() => {
    document.querySelectorAll('.board .setaside').forEach((b,i)=>{ if(i<3) b.click(); });
  });
  await page.waitForTimeout(300);
  const trayN = await page.evaluate(() => document.querySelectorAll('.trayItem').length);
  console.log('Set-aside tray items:', trayN);

  await page.fill('#askInput', 'now show me those in blue');
  await page.click('.ask button');
  await page.waitForTimeout(1000);
  const finalBoards = await page.evaluate(() => document.querySelectorAll('.board').length);
  console.log('Final on-table boards:', finalBoards);

  await page.screenshot({ path: path.join(shotsDir, 'v10-concierge.png'), fullPage: false });
  console.log('Screenshot -> shots/v10-concierge.png');

  console.log('\nConsole errors:', errors.length);
  errors.forEach(e => console.log('  ✗', e));

  await browser.close();
  srv.kill();
  process.exit(errors.length ? 2 : 0);
})();