mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
The bar is Models, Dev, Platform, Pricing, Docs, Enterprise, Log in, and Get API key. The homepage is seven sections. Launch pages stay in the tree and drop out of the index through lib/launch.ts. /platform forwards to /cloud. Model and repository floors come from the snapshots the build already keeps. Co-authored-by: z <[email protected]>
96 lines
5.1 KiB
TypeScript
96 lines
5.1 KiB
TypeScript
import { expect, test, type Page } from '@playwright/test'
|
||
import { serveExport, read } from './export'
|
||
import { AGENTS } from '../../components/gen/roster'
|
||
import { sideways } from './wide'
|
||
|
||
/**
|
||
* /gen, on the bytes that ship: the harness's page.
|
||
*
|
||
* It leads with the install (`curl -fsSL https://hanzo.sh | sh -s gen`), then
|
||
* `gen` and `gen --<agent>`, keeps `hanzo dev --<agent>` as the same session
|
||
* inside the Hanzo CLI, draws every agent the harness's list names
|
||
* (components/gen/roster) with its flag, and names gen's latest release only as
|
||
* GitHub answers it when the page opens.
|
||
*/
|
||
|
||
const RELEASE = 'https://api.github.com/repos/hanzoai/gen/releases/latest'
|
||
/** GitHub's API answers any origin. */
|
||
const CORS = { 'access-control-allow-origin': '*' }
|
||
|
||
/** Waits for gui to mount the page, which is when its effects (the release read) have run. */
|
||
const hydrated = (page: Page) =>
|
||
page.waitForFunction(() => !document.documentElement.className.includes('t_unmounted'), null, { timeout: 60_000 })
|
||
|
||
let base: string
|
||
let stop: () => Promise<void>
|
||
test.beforeAll(async () => {
|
||
const served = await serveExport()
|
||
base = served.url
|
||
stop = served.close
|
||
})
|
||
test.afterAll(async () => stop())
|
||
|
||
test('the export holds /gen: its own canonical, offered to crawlers', () => {
|
||
const html = read('gen.html')
|
||
expect(html).toContain('<title>Hanzo Gen')
|
||
expect(html).toContain('<link rel="canonical" href="https://hanzo.ai/gen"/>')
|
||
expect(html).not.toContain('noindex')
|
||
expect(read('sitemap.xml'), '/gen is offered').toContain('<loc>https://hanzo.ai/gen</loc>')
|
||
})
|
||
|
||
for (const width of [1440, 390]) {
|
||
test(`/gen is the harness’s page at ${width}px: the install first, every agent and its flag, and the release GitHub names`, async ({ page }, info) => {
|
||
await page.route(RELEASE, (route) =>
|
||
route.fulfill({ headers: CORS, json: { tag_name: 'v9.8.7', html_url: 'https://github.com/hanzoai/gen/releases/tag/v9.8.7' } }),
|
||
)
|
||
await page.setViewportSize({ width, height: 900 })
|
||
await page.goto(`${base}/gen`)
|
||
await expect(page.getByRole('heading', { level: 1 })).toHaveText(/Any agent\.\s*Our rails\./, { timeout: 20_000 })
|
||
await expect(page.locator('header[data-slot="masthead"]')).toBeVisible()
|
||
await expect(page.locator('[data-agent]')).toHaveCount(AGENTS.length)
|
||
for (const a of AGENTS) await expect(page.locator(`[data-agent="${a.flag}"]`)).toContainText(a.name)
|
||
// The install leads, gen runs the agents, and hanzo dev is the same session in the CLI.
|
||
const text = await page.locator('main').innerText()
|
||
expect(text).toContain('curl -fsSL https://hanzo.sh | sh -s gen')
|
||
for (const command of ['gen --claude', 'gen --codex', 'hanzo dev --claude']) {
|
||
expect(text).toContain(command)
|
||
expect(text.indexOf('curl -fsSL https://hanzo.sh | sh -s gen'), `the install comes before ${command}`).toBeLessThan(text.indexOf(command))
|
||
}
|
||
expect(text, 'gen is released').not.toMatch(/\bcoming\b|not (yet )?released/i)
|
||
// The hero's calls are every family page's (9920b118); the install is one press away under Read.
|
||
await expect(page.getByRole('link', { name: 'Get API key', exact: true }).first()).toHaveAttribute('href', 'https://platform.hanzo.ai/keys')
|
||
await expect(page.getByRole('link', { name: 'Install curl -fsSL https://hanzo.sh | sh -s gen', exact: true })).toHaveAttribute('href', '#install')
|
||
await hydrated(page)
|
||
await expect(page.getByRole('link', { name: 'Latest release v9.8.7 →' })).toHaveAttribute('href', 'https://github.com/hanzoai/gen/releases/tag/v9.8.7', {
|
||
timeout: 30_000,
|
||
})
|
||
expect(await sideways(page), 'nothing wider than the window').toBeLessThanOrEqual(0)
|
||
await page.evaluate(() => window.scrollTo(0, 0))
|
||
await page.screenshot({ path: info.outputPath(`gen-${width}.png`) })
|
||
await page.screenshot({ path: info.outputPath(`gen-${width}-full.png`), fullPage: true })
|
||
})
|
||
}
|
||
|
||
test('/gen says nothing about a release GitHub does not answer for', async ({ page }) => {
|
||
await page.route(RELEASE, (route) => route.fulfill({ status: 403, headers: CORS, json: { message: 'rate limited' } }))
|
||
const refused = page.waitForResponse(RELEASE, { timeout: 60_000 })
|
||
await page.goto(`${base}/gen`)
|
||
await expect(page.getByRole('heading', { level: 1 })).toBeVisible({ timeout: 20_000 })
|
||
await refused
|
||
await page.waitForTimeout(1000)
|
||
await expect(page.getByText(/Latest release/)).toHaveCount(0)
|
||
})
|
||
|
||
test('Dev is a door in the bar, and Gen stays on its page', async ({ page }) => {
|
||
await page.setViewportSize({ width: 1440, height: 900 })
|
||
await page.goto(`${base}/dev`, { waitUntil: 'load' })
|
||
const bar = page.getByRole('banner')
|
||
await expect(bar.getByRole('link', { name: 'Dev', exact: true })).toHaveAttribute('href', '/dev')
|
||
await expect(bar.getByRole('link', { name: 'Hanzo Gen', exact: true })).toHaveCount(0)
|
||
const gen = page.locator('footer').getByRole('link', { name: 'Gen', exact: true })
|
||
await expect(gen).toHaveAttribute('href', '/gen')
|
||
await gen.click()
|
||
await page.waitForURL((u) => u.pathname === '/gen', { timeout: 20_000 })
|
||
await expect(page.getByRole('heading', { level: 1 })).toHaveText(/Any agent\.\s*Our rails\./, { timeout: 20_000 })
|
||
})
|