Files
hanzo.ai/e2e/gates/apex.spec.ts
zeekay 934d12c2d8 The hero says up to 200× faster, as the decision clock measures it, and the agents' menu drops its clipboard note
A cached Kai decision is 0.3 ms against a hosted call's 133 ms, which
the clock on the page states as over 200×; the hero and the page's
description now say the same.
2026-10-18 14:43:01 -07:00

210 lines
10 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 { read, serveExport } from './export'
import { contract, issuer, type Options, type Seen } from './issuer'
/**
* THE APEX IS THE LANDING FOR A STRANGER AND THE APP FOR A MEMBER.
*
* `/` is two things at once and neither may cost the other. It is hanzo.ai's
* search result and its share card, so the export has to keep answering a
* crawler with the whole landing and the metadata that describes it; and it is
* the address a reader who is ALREADY signed in types, who wants the app and
* not the pitch for it — so for them `/` IS the app (app/_apex.tsx), at the same
* address, with nowhere to be sent.
*
* `output: 'export'` decides HOW. There is no server at runtime — no
* middleware, no `redirect()`, no cookie read while rendering — so `/` ships as
* one static file with the landing in it, the same file for everybody, and the
* only thing that can tell the two readers apart runs in the browser. The
* consequence this file exists for is that a naive answer PAINTS THE PITCH
* FIRST and swaps it afterwards, which is worse than not doing it at all.
*
* So the measurement is a frame, not a URL: every frame the document draws
* before the app is mounted is asked whether the landing's header is on it.
*/
/** What `app/page.tsx` declares. The head has to keep saying exactly this. */
const TITLE = 'Hanzo AI — Better, Faster, Cheaper. Pick Three.'
const DESCRIPTION =
'Better, cheaper, faster. Pick all three. Frontier AI up to 200× faster, 400× cheaper. Build with autonomous coding agents, foundation models, and open cloud infrastructure.'
const PAINTS = 'gate.paints'
/**
* Record, per document, whether the browser ever painted it.
*
* Installed before anything else in every document, so the observer is already
* watching when the apex's own boot script runs. `buffered: true` because a
* paint that happened before the observer was constructed is still a paint.
* `requestAnimationFrame` is kept beside it as the weaker of the two signals —
* it fires at a rendering opportunity, which precedes a paint but does not
* prove one — because a run where the frame fired and the paint did not is
* worth being able to read in the failure message.
*/
async function watchPaint(page: Page): Promise<void> {
await page.addInitScript((key: string) => {
const here = location.pathname
const mark = (how: string) => {
try {
const seen = JSON.parse(localStorage.getItem(key) || '{}')
if (!seen[here]) seen[here] = []
if (!seen[here].includes(how)) seen[here].push(how)
localStorage.setItem(key, JSON.stringify(seen))
} catch {
/* a browser that refuses storage cannot be measured this way; the
assertions below read an absent record as an absent paint, so the
store refusing would report a false pass — it does not refuse here */
}
}
try {
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) mark(entry.name)
}).observe({ type: 'paint', buffered: true })
} catch {
/* no paint timing in this engine */
}
requestAnimationFrame(() => mark('frame'))
}, PAINTS)
}
const paints = (page: Page): Promise<Record<string, string[]>> =>
page.evaluate((key: string) => JSON.parse(localStorage.getItem(key) || '{}'), PAINTS)
/**
* A session that can be ENDED — the one thing `signIn` cannot give.
*
* The shared helper writes its tokens from an init script, which runs in every
* document of the tab. That is right for every other spec and fatal for this
* one: `logout()` clears the browser and navigates, and the init script puts
* the tokens straight back before the page it lands on can read them, so the
* reader arrives signed in again and is forwarded into the room. Seeded once,
* with the tab itself remembering that it has been, the sign-out is real.
*/
async function seedOnce(page: Page, options?: Options): Promise<Seen> {
const seen = await issuer(page, options)
await page.addInitScript(() => {
if (sessionStorage.getItem('gate.seeded')) return
sessionStorage.setItem('gate.seeded', '1')
const part = (v: unknown) =>
btoa(JSON.stringify(v)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
const exp = Math.floor(Date.now() / 1000) + 3600
const token = `${part({ alg: 'none', typ: 'JWT' })}.${part({
sub: 'gate',
name: 'gate',
email: '[email protected]',
exp,
orgs: [{ org: 'hanzo', role: 'owner' }],
})}.`
localStorage.setItem('hanzo_iam_access_token', token)
localStorage.setItem('hanzo_iam_id_token', token)
localStorage.setItem('hanzo_iam_expires_at', String(exp * 1000))
})
return seen
}
test.describe('the apex', () => {
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('still describes itself to a crawler', async () => {
// The bytes, not the browser: a crawler reads the file and runs none of it.
const html = read('index.html')
expect(html, 'the apex title').toContain(`<title>${TITLE}</title>`)
expect(html, 'the apex description').toContain(DESCRIPTION)
expect(html, 'the canonical is the apex itself').toContain(
'<link rel="canonical" href="https://hanzo.ai"/>',
)
expect(html, 'the share card').toContain('property="og:title"')
expect(html, 'the share card names the apex').toContain('content="https://hanzo.ai"')
expect(html, 'the twitter card').toContain('name="twitter:card"')
// And the landing itself is still IN the file. The forwarding is a script;
// if it had become a build-time decision this would be a shell.
expect(html, 'the landing ships in the apex document').toContain('data-slot="masthead"')
expect(html.length, 'the whole landing, not a stub').toBeGreaterThan(100_000)
})
test('answers a stranger with the landing, and stays at /', async ({ page }) => {
await watchPaint(page)
await page.goto(`${base}/`)
await expect(page.locator('header[data-slot="masthead"]')).toBeVisible({ timeout: 20_000 })
expect(new URL(page.url()).pathname, 'nobody signed in, nobody forwarded').toBe('/')
// The landing painted, which is both the point and the proof that the probe
// above can see a paint at all — an assertion that only ever reads "no
// paint" is an assertion that would pass on a broken probe. Polled: the
// observer hears a paint in a task after the frame, so the masthead can be
// laid out and visible a moment before its paint is on the record.
await expect
.poll(async () => (await paints(page))['/'] ?? [], { message: 'the landing paints for a stranger' })
.toContain('first-contentful-paint')
})
test('answers a signed-in reader with the app, at /, and never paints the pitch at them', async ({ page }) => {
// Every frame until the app is up: is the landing's header on it?
await page.addInitScript(() => {
const seen: string[] = []
;(window as unknown as { __frames: string[] }).__frames = seen
const look = () => {
if (document.querySelector('[data-slot="app-rail"]')) return
const header = document.querySelector('header[data-slot="masthead"]')
if (header?.checkVisibility()) seen.push('landing')
requestAnimationFrame(look)
}
requestAnimationFrame(look)
})
await page.route(
(u) => u.pathname.startsWith('/v1/') && !u.pathname.startsWith('/v1/iam/'),
(route) => {
const path = new URL(route.request().url()).pathname
// A reader who pays: the app stands behind the organization gate (app/_web.tsx).
if (path === '/v1/billing/tier') return route.fulfill({ json: { tier: { name: 'dev', displayName: 'Dev' }, balance: { currency: 'usd', prepaidAvailable: 0 } } })
if (path === '/v1/billing/balance') return route.fulfill({ json: { balance: 0, holds: 0, available: 0 } })
return route.request().url().includes('/stream')
? route.fulfill({ status: 200, contentType: 'text/event-stream', body: '' })
: route.fulfill({ json: { sessions: [], data: [] } })
},
)
await seedOnce(page)
await page.goto(base + '/')
await expect(page.locator('main textarea').first(), 'the app opens in Chat').toBeVisible({ timeout: 20_000 })
const column = page.getByRole('navigation', { name: 'Hanzo' })
await expect(page.getByRole('navigation', { name: 'Products' }).getByRole('button', { name: 'Chat', exact: true }), 'the app’s rail leads with Chat').toHaveAttribute('aria-current', 'page')
await expect(column.getByText('Hanzo AI', { exact: true }), 'the app names itself').toBeVisible()
expect(new URL(page.url()).pathname, 'the app is the apex itself, not a trip').toBe('/')
expect(await page.evaluate(() => (window as unknown as { __frames: string[] }).__frames), 'no frame showed the landing').toEqual([])
await expect(page.locator('header[data-slot="masthead"]')).toBeHidden()
})
test('signs out on this site: IAM is told by calls answered here, and the page is /login?from=logout', async ({ page }) => {
// Nothing of hanzo.id is navigated to. The tokens go back and the session
// ends with calls to this origin (lib/auth/client `useSignOut`, @hanzo/iam
// `end`), and the page a sign-out lands on does not ask for the session
// just ended.
const seen = await seedOnce(page, { site: base })
const posted = () => seen.posted.map((p) => p.path)
await page.goto(`${base}/pricing`)
const me = page.locator('header[data-slot="masthead"] button[aria-haspopup="menu"]')
await expect(me).toBeVisible({ timeout: 20_000 })
await me.click()
const out = page.getByRole('menuitem', { name: 'Sign out' })
await expect(out).toBeVisible({ timeout: 20_000 })
await out.click()
await page.waitForURL((u) => u.pathname === '/login' && u.searchParams.get('from') === 'logout', { timeout: 20_000 })
await expect(page.getByRole('region', { name: /^(Log in|Sign up)$/ }).getByLabel('Email'), 'the sign-in, to come straight back').toBeVisible({ timeout: 20_000 })
expect(posted(), 'the tokens handed back and the session ended, here').toEqual(expect.arrayContaining(['/v1/iam/oauth/revoke', '/v1/iam/oauth/logout']))
expect(posted(), 'no ask for the session just ended').not.toContain('/v1/iam/login')
contract(seen)
expect(await page.evaluate(() => localStorage.getItem('hanzo_iam_access_token')), 'the browser is signed out').toBeNull()
})
})