Files
hanzo.ai/e2e/gates/status.spec.ts
zeekay 4fd349aa06 beta: the portal says less, and opting in and sharing are one press
Prose that carried no decision or fact is gone: the hero, the questions, the
gate and every step are a heading and at most a line. What scores is said once
(the leaderboard step), and every claim the cloud does not keep is out.

Step 1 is one share card (the member's link, Copy, the device's share sheet on
phones, seven channels as icons, Customize behind a text button) and a quiet
invite-by-email card. Step 3 is one searched, grouped list with one control a
row: On when `opens` says so, Get access when it does not, Requested at once
after the press. Stars and follows moved under the leaderboard as Earn more.

The 1,800-line dashboard is a shell over share, rank, betas and alpha; what
they read is one hook (standing.ts); the questions are data (program.ts).
Dead access, card, hint and referral components are deleted. Every press is
44px, every input too, and a gate walks each screen at 390px.

scripts/brands and scripts/prebuild are TypeScript now; the three marks the
share row draws ship in a JSON only /beta loads. /beta's title and card say
what the page says.

Claude-Session: https://claude.ai/code/session_017vrVus6PPBgP5YRzKV1iqW
2026-10-26 03:29:13 -07:00

76 lines
3.9 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, type Route } from '@playwright/test'
import { serveExport } from './export'
import { signIn } from '../session'
/**
* /BETA/STATUS IS THE BETA MEMBER'S OWN PAGE, the account menu's "Beta status".
*
* Signed out it asks for the account; signed in it reads the beta record and
* the plan: no record, apply; applied and unpaid, Pro's checkout first; paid,
* the access, the referral and feedback.
*/
let base: string
let stop: () => Promise<void>
test.beforeAll(async () => {
const served = await serveExport()
base = served.url
stop = served.close
})
test.afterAll(async () => stop())
const local = (page: Page) => page.route('**/*', (route) => (new URL(route.request().url()).origin === base ? route.continue() : route.abort()))
const reply = (route: Route, json: unknown, status = 200) => {
const req = route.request()
const headers = { 'access-control-allow-origin': req.headers().origin ?? base, 'access-control-allow-credentials': 'true', 'access-control-allow-headers': 'content-type, authorization, x-org-id' }
if (req.method() === 'OPTIONS') return route.fulfill({ status: 204, headers })
return route.fulfill({ status, headers, json })
}
/** Billing on a plan, and the beta record as given (null: none on file). */
const world = async (page: Page, plan: string, record: { program: string } | null) => {
await page.route('**/v1/billing/tier', (route) => reply(route, { plan, tier: { name: plan === 'free' ? 'free' : 'pro' } }))
await page.route('**/v1/billing/balance', (route) => reply(route, { available: 0 }))
await page.route('**/v1/marketing/beta', (route) => (record ? reply(route, record) : reply(route, { error: 'not found' }, 404)))
await page.route('**/v1/referral', (route) => reply(route, { code: 'ada7', link: 'https://hanzo.ai/?ref=ada7', counts: { total: 0, signup: 0, qualified: 0 }, referrals: [] }))
}
test('signed out, the page asks for the account and comes back here', async ({ page }) => {
await local(page)
await page.goto(`${base}/beta/status`)
await expect(page.getByRole('heading', { name: 'Your Hanzo beta.' })).toBeVisible({ timeout: 20_000 })
await expect(page.getByRole('link', { name: 'Sign in' }).last()).toHaveAttribute('href', '/login?next=%2Fbeta%2Fstatus')
})
test('signed in with no application, the page sends the reader to apply', async ({ page }) => {
await local(page)
await signIn(page, { name: 'ada', email: '[email protected]' })
await world(page, 'free', null)
await page.goto(`${base}/beta/status`)
await expect(page.getByRole('heading', { name: 'You have not joined the beta yet.' })).toBeVisible({ timeout: 20_000 })
await expect(page.getByRole('link', { name: 'Apply to the beta' })).toHaveAttribute('href', '/beta')
})
test('applied and unpaid, Pro’s checkout comes first, with the referral', async ({ page }) => {
await local(page)
await signIn(page, { name: 'ada', email: '[email protected]' })
await world(page, 'free', { program: 'beta' })
await page.goto(`${base}/beta/status`)
await expect(page.getByRole('heading', { name: 'Your beta application is in.' })).toBeVisible({ timeout: 20_000 })
const pay = new URL((await page.getByRole('link', { name: 'Continue with plan' }).first().getAttribute('href'))!, base)
expect(pay.pathname).toBe('/pay/cart')
expect(Object.fromEntries(pay.searchParams)).toMatchObject({ coupon: '50OFFBETA' })
await expect(page.getByText('Your link')).toBeVisible()
await expect(page.getByRole('link', { name: 'Referral dashboard' })).toHaveAttribute('href', '/referral')
})
test('paid, the page is the beta access', async ({ page }) => {
await local(page)
await signIn(page, { name: 'ada', email: '[email protected]' })
await world(page, 'dev', { program: 'beta' })
await page.goto(`${base}/beta/status`)
await expect(page.getByRole('heading', { name: 'You’re in the Hanzo beta.' })).toBeVisible({ timeout: 20_000 })
await expect(page.getByRole('link', { name: /Continue to checkout|Continue with plan/ })).toHaveCount(0)
})