Files
hanzo.ai/e2e/gates/gen.spec.ts

101 lines
5.4 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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('the products menu names Dev and Gen, and Gen opens its page', async ({ page }) => {
await page.setViewportSize({ width: 1440, height: 900 })
// From Dev's page, which wears the site's header: Gen is the next door over.
await page.goto(`${base}/dev`, { waitUntil: 'load' })
const products = page.getByRole('banner').getByRole('button', { name: 'Products', exact: true })
const plane = page.locator('#menu-products')
await expect(async () => {
if (!(await plane.isVisible())) await products.click()
await expect(plane).toBeVisible({ timeout: 1000 })
}).toPass({ timeout: 15_000 })
await expect(plane.getByRole('link', { name: 'Hanzo Dev', exact: true })).toHaveAttribute('href', '/dev')
const gen = plane.getByRole('link', { name: 'Hanzo 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 })
})