mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
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
76 lines
3.9 KiB
TypeScript
76 lines
3.9 KiB
TypeScript
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)
|
||
})
|