← back to Approval Viewer

verification/full-approval-e2e.mjs

80 lines

import assert from 'node:assert/strict';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { spawn } from 'node:child_process';
import { createRequire } from 'node:module';

const require = createRequire('/Users/macstudio3/Projects/Designer-Wallcoverings/package.json');
const { chromium } = require('playwright');
const projectRoot = path.resolve(new URL('..', import.meta.url).pathname);
const port = 19789;
const base = `http://127.0.0.1:${port}`;
const decisionsPath = path.join(os.tmpdir(), `approval-viewer-ui-${Date.now()}.jsonl`);
const screenshotPath = new URL('./full-approval-e2e.png', import.meta.url);
const reportPath = new URL('./e2e-proof.json', import.meta.url);
const server = spawn(process.execPath, ['server.js'], {
  cwd: projectRoot,
  env: { ...process.env, PORT: String(port), DECISIONS_PATH: decisionsPath },
  stdio: ['ignore', 'pipe', 'pipe'],
});

async function waitForServer() {
  for (let attempt = 0; attempt < 40; attempt += 1) {
    try {
      const response = await fetch(`${base}/api/items`);
      if (response.ok) return;
    } catch {}
    await new Promise((resolve) => setTimeout(resolve, 100));
  }
  throw new Error('temporary approval viewer did not start');
}

const startedAt = new Date().toISOString();
let browser;
try {
  await waitForServer();
  browser = await chromium.launch({ headless: true, executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome' });
  const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } });
  await page.goto(base, { waitUntil: 'networkidle' });
  await page.getByRole('heading', { name: 'Approval command center' }).waitFor();
  const initialCount = await page.locator('.card').count();
  assert(initialCount > 1, 'expected multiple approval chips');

  await page.locator('.card').first().getByRole('button', { name: 'Approve fully' }).click();
  await page.locator('.card').first().getByText('full approval').waitFor();

  await page.getByPlaceholder('Search tickets…').fill('dw-reels');
  const filteredCount = await page.locator('.card').count();
  assert(filteredCount > 0 && filteredCount < initialCount, 'search did not scope the visible chips');
  await page.getByRole('button', { name: `Approve all shown (${filteredCount})` }).click();
  await page.locator('.card').first().getByText('full approval').waitFor();
  await page.screenshot({ path: screenshotPath.pathname, fullPage: true });

  const records = fs.readFileSync(decisionsPath, 'utf8').trim().split('\n').map(JSON.parse);
  assert.equal(records.length, 1 + filteredCount);
  assert(records.every((record) => record.action === 'approve' && record.approvalScope === 'full'));
  assert(records.every((record) => record.writePerformed === false));

  const unknown = await fetch(`${base}/api/decisions/approve-all`, {
    method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ids: ['unknown-chip'] }),
  });
  assert.equal(unknown.status, 404);

  const report = {
    ticket: 'TK-11000', intent: 'Allow immediate full approval of any individual chip and every chip in the filtered view',
    riskTier: 'R2 local user-facing flow', environment: base, startedAt, finishedAt: new Date().toISOString(),
    assertions: {
      pageLoaded: 'PASS', individualFullApproval: 'PASS', filteredBulkFullApproval: 'PASS',
      appendOnlyAudit: 'PASS', unknownChipRejected: 'PASS', externalActionPerformed: false,
      initialCount, filteredCount, recordedDecisions: records.length,
    },
    artifacts: { screenshot: screenshotPath.pathname, isolatedDecisionLog: decisionsPath }, verdict: 'PASS',
  };
  fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
  console.log(JSON.stringify(report, null, 2));
} finally {
  if (browser) await browser.close();
  server.kill('SIGTERM');
}