SPB Git

spb/zyquo-cloud-web Public MIT

Zyquo Cloud Web — every cloud model, one beautiful chat, entirely in your browser.

TypeScript 81.9% CSS 8.9% JavaScript 7.5% Shell 1.1% HTML 0.6%
3.1 KB · 86 lines javascript
Raw Blame History
1/*2 *  gate-phase2.mjs3 *  Zyquo Cloud Web4 *5 *  Author: Simon-Pierre Boucher6 *  Mail: contact@spboucher.ai7 *8 *  Phase 2 gate check: drives a real Chromium browser against the dev server,9 *  seeds a provider key from .keys.local.json into localStorage, runs the bare10 *  stream test view, and asserts the completion arrived TOKEN-BY-TOKEN11 *  (multiple incremental output snapshots) with working Stop cancellation.12 *  Usage: node scripts/gate-phase2.mjs [provider] [baseURL]13 */1415import { chromium } from 'playwright'16import { readFileSync } from 'node:fs'17import { fileURLToPath } from 'node:url'18import { dirname, join } from 'node:path'1920const root = dirname(dirname(fileURLToPath(import.meta.url)))21const keys = JSON.parse(readFileSync(join(root, '.keys.local.json'), 'utf8'))22const provider = process.argv[2] ?? 'openai'23const baseURL = process.argv[3] ?? 'http://localhost:5173'2425const browser = await chromium.launch()26const page = await browser.newPage()2728await page.goto(baseURL)29await page.evaluate(30  ([p, k]) => localStorage.setItem('zyquo.cloud.web.keys', JSON.stringify({ [p]: k })),31  [provider, keys[provider]]32)33await page.goto(`${baseURL}/#stream-test`)34await page.reload()3536await page.selectOption('[data-testid="provider"]', provider)37await page.fill('[data-testid="prompt"]', 'Write a 150-word paragraph about clouds.')38await page.click('[data-testid="run"]')3940// Sample the output as it streams to prove token-by-token rendering.41const snapshots = []42for (let i = 0; i < 600; i++) {43  const status = (await page.textContent('[data-testid="status"]'))?.replace('status: ', '') ?? ''44  const output = await page.textContent('[data-testid="output"]')45  if (output && (snapshots.length === 0 || output !== snapshots[snapshots.length - 1])) {46    snapshots.push(output)47  }48  if (49    status.startsWith('finished') ||50    /invalidAPIKey|badRequest|serverError|networkError|rateLimited|invalidResponse/.test(status)51  ) {52    break53  }54  await page.waitForTimeout(50)55}5657const status = await page.textContent('[data-testid="status"]')58const tokens = await page.textContent('[data-testid="tokens"]')59const finalOutput = snapshots[snapshots.length - 1] ?? ''6061console.log(`provider:  ${provider}`)62console.log(`status:    ${status}`)63console.log(`usage:     ${tokens}`)64console.log(`snapshots: ${snapshots.length} incremental output states`)65console.log(`final out: ${finalOutput.slice(0, 120).replace(/\n/g, ' / ')}`)6667// Cancellation check: start a second stream and stop it immediately.68await page.click('[data-testid="run"]')69await page.waitForTimeout(700)70await page.click('[data-testid="stop"]')71await page.waitForTimeout(700)72const stopStatus = await page.textContent('[data-testid="status"]')73console.log(`stop test: ${stopStatus}`)7475await browser.close()7677const streamedIncrementally = snapshots.length >= 378const finished = status?.includes('finished')79const stopped = stopStatus?.includes('cancelled') || stopStatus?.includes('stopped')80if (streamedIncrementally && finished && stopped) {81  console.log('GATE PASS: real token-by-token streaming + cancellation in a real browser')82} else {83  console.error('GATE FAIL')84  process.exit(1)85}86