mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
Every import, the catalog pin and the docs name @hanzo/app and github.com/hanzoai/app. lib/rooms.ts is lib/app.ts and components/rooms.tsx is components/app.tsx.
135 lines
6.3 KiB
TypeScript
135 lines
6.3 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
|
import { serveExport } from './export'
|
|
import { serve } from './api'
|
|
import { answer, signIn } from '../session'
|
|
|
|
test.describe('E2E Local Cloud & Swarm System Verification', () => {
|
|
test('1. Local Sovereign Cloud Daemon answers native exec in <50ms', async ({ request }) => {
|
|
// THIS GATE NEVER FAILS THE BUILD ON A SERVICE THE BUILDER DOES NOT RUN.
|
|
// It asks a daemon at 127.0.0.1:8080, which exists on a developer's machine
|
|
// after `hanzo up` and nowhere in CI — so as a step inside the publishing
|
|
// job it froze the site for a reason that has nothing to do with the commit.
|
|
// `platform.spec.ts` settles the same shape the same way: reachable or the
|
|
// test skips and says so.
|
|
const up = await request.post('http://127.0.0.1:8080/v1/exec', { data: { cmd: 'true' } }).catch(() => null)
|
|
test.skip(!up || !up.ok(), 'no local cloud daemon on 127.0.0.1:8080 — run `hanzo up` to exercise this')
|
|
|
|
// 1. Bash execution
|
|
const bashRes = await request.post('http://127.0.0.1:8080/v1/exec', {
|
|
data: {
|
|
cmd: 'echo "hanzo-swarm-verified"',
|
|
},
|
|
})
|
|
expect(bashRes.status()).toBe(200)
|
|
const bashData = await bashRes.json()
|
|
expect(bashData.stdout).toContain('hanzo-swarm-verified')
|
|
expect(bashData.exit_code).toBe(0)
|
|
|
|
// 2. Python execution
|
|
const pyRes = await request.post('http://127.0.0.1:8080/v1/exec', {
|
|
data: {
|
|
code: 'print("py-swarm-" + str(2**8))',
|
|
lang: 'python',
|
|
},
|
|
})
|
|
expect(pyRes.status()).toBe(200)
|
|
const pyData = await pyRes.json()
|
|
expect(pyData.stdout).toContain('py-swarm-256')
|
|
|
|
// 3. Node.js / JavaScript execution
|
|
const jsRes = await request.post('http://127.0.0.1:8080/v1/exec', {
|
|
data: {
|
|
code: 'console.log("js-swarm-" + (40 + 2))',
|
|
lang: 'javascript',
|
|
},
|
|
})
|
|
expect(jsRes.status()).toBe(200)
|
|
const jsData = await jsRes.json()
|
|
expect(jsData.stdout).toContain('js-swarm-42')
|
|
})
|
|
|
|
test('2. Swarm Board (/board) renders 5 columns, pre-seeded cards, and handles interaction', async ({ page }) => {
|
|
await signIn(page)
|
|
const site = await serveExport()
|
|
try {
|
|
await page.setViewportSize({ width: 1440, height: 900 })
|
|
await page.goto(`${site.url}/board`)
|
|
|
|
// Verify Board header
|
|
await expect(page.locator('h1')).toContainText('Board')
|
|
|
|
// Verify all 5 Kanban columns
|
|
await expect(page.getByText('Backlog', { exact: true })).toBeVisible()
|
|
await expect(page.getByText('To do', { exact: true })).toBeVisible()
|
|
await expect(page.getByText('Doing', { exact: true })).toBeVisible()
|
|
await expect(page.getByText('Done', { exact: true })).toBeVisible()
|
|
await expect(page.getByText('Canceled', { exact: true })).toBeVisible()
|
|
|
|
// Verify Pre-seeded swarm tasks
|
|
await expect(page.getByText('Deploy 3D Gaussian Splat Whale')).toBeVisible()
|
|
await expect(page.getByText('Sovereign Cloud Exec Runner')).toBeVisible()
|
|
await expect(page.getByText('Pay & Billing Architecture Unification')).toBeVisible()
|
|
await expect(page.getByText('Autonomous Swarm Collaboration in Channels')).toBeVisible()
|
|
|
|
// Switch to Planner and back
|
|
const plannerBtn = page.locator('button[aria-pressed]').filter({ hasText: 'Planner' })
|
|
const boardBtn = page.locator('button[aria-pressed]').filter({ hasText: 'Board' })
|
|
await plannerBtn.click()
|
|
await expect(plannerBtn).toHaveAttribute('aria-pressed', 'true')
|
|
await boardBtn.click()
|
|
await expect(boardBtn).toHaveAttribute('aria-pressed', 'true')
|
|
} finally {
|
|
await site.close()
|
|
}
|
|
})
|
|
|
|
test('3. Flagship Home (/) renders the hero with its sign-up, then the plans', async ({ page }) => {
|
|
const site = await serveExport()
|
|
// The plans are the plan authority's, read when the page opens (e2e/gates/api).
|
|
await serve(page)
|
|
try {
|
|
await page.setViewportSize({ width: 1440, height: 900 })
|
|
await page.goto(`${site.url}/`)
|
|
|
|
await expect(page).toHaveTitle(/Hanzo/)
|
|
|
|
// STRUCTURE, NOT COPY. This asserted two exact marketing sentences — "The
|
|
// Autonomous App Builder." and "Build Shift Board with Hanzo" — and
|
|
// neither is on the page. A gate that names a sentence goes red on every rewrite and says
|
|
// "the hero is missing" when a line was edited, which is the failure
|
|
// e2e/gates/workspace.spec.ts already records and corrected.
|
|
const hero = page.getByRole('heading', { level: 1 })
|
|
await expect(hero).toBeVisible()
|
|
await expect(hero, 'the fold owes a heading that says something').not.toHaveText('')
|
|
// The apex is the way in, then the proof and the plans (components/home/Home.tsx).
|
|
await expect(page.getByRole('region', { name: /^(Log in|Sign up)$/ }), 'the hero carries the sign-up').toBeVisible()
|
|
await expect(page.locator('#plans').getByRole('region', { name: 'Free', exact: true }), 'the plans follow it').toBeVisible({ timeout: 15_000 })
|
|
} finally {
|
|
await site.close()
|
|
}
|
|
})
|
|
|
|
test('5. Swarm Chat (/chat) renders starter prompts, voice button, and composer', async ({ page }) => {
|
|
await signIn(page)
|
|
// Nothing kept and nothing done yet: the suggestions are Hanzo's three
|
|
// starters (@hanzo/build Suggestions; the four fixed openings went with
|
|
// @hanzo/app 0.1.83). starters.spec.ts holds what a press does.
|
|
await answer(page, '/v1/pref', { prefs: { suggest: { at: new Date().toISOString(), sig: '', rows: [], dismissed: [] } } })
|
|
const site = await serveExport()
|
|
try {
|
|
await page.setViewportSize({ width: 1440, height: 900 })
|
|
await page.goto(`${site.url}/chat`)
|
|
|
|
const main = page.locator('main')
|
|
await expect(main.getByRole('textbox', { name: 'Ask anything' }), 'the composer').toBeVisible({ timeout: 20_000 })
|
|
await expect(main.getByRole('button', { name: 'Voice mode' }), 'voice, while the draft is empty').toBeVisible()
|
|
const starters = main.getByRole('list', { name: 'Suggestions' })
|
|
await expect(starters).toBeVisible({ timeout: 20_000 })
|
|
for (const title of ['Ask what Hanzo can do for you', 'Build a landing page with Dev', 'Get a cited brief on AI agents'])
|
|
await expect(starters.getByRole('button', { name: title, exact: true })).toBeVisible()
|
|
} finally {
|
|
await site.close()
|
|
}
|
|
})
|
|
})
|