Files
hanzo.ai/e2e/gates/share.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

99 lines
5.2 KiB
TypeScript

import { expect, test, type Page, type Route } from '@playwright/test'
import { serveExport } from './export'
import { heard, person, stream } from './heard'
import { signIn } from '../session'
/**
* SHARING, MEASURED WHERE IT CAN BE.
*
* Nobody can see a post go out on another network, and nothing here pretends to.
* What the cloud can know is what comes back: the member's own link leaves with the
* channel it was pressed for, the stream hears the press, a visitor who follows the
* link arrives with the code and the channel kept, and the code is claimed once, when
* they sign in. The score counts the claim (+1), not the press.
*/
let base: string
let stop: () => Promise<void>
test.beforeAll(async () => {
const served = await serveExport()
base = served.url
stop = served.close
})
test.afterAll(async () => stop())
/** Nothing off this machine answers, popups included. */
const local = (page: Page) => page.context().route('**/*', (route) => (new URL(route.request().url()).origin === base ? route.continue() : route.abort()))
const reply = (route: Route, json: unknown) => {
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',
'access-control-allow-methods': 'GET, POST, PUT',
}
if (req.method() === 'OPTIONS') return route.fulfill({ status: 204, headers })
return route.fulfill({ headers, json })
}
const CHANNELS = ['x', 'linkedin', 'facebook', 'bluesky', 'reddit', 'hn', 'email'] as const
const NAMES: Record<(typeof CHANNELS)[number], string> = { x: 'X', linkedin: 'LinkedIn', facebook: 'Facebook', bluesky: 'Bluesky', reddit: 'Reddit', hn: 'Hacker News', email: 'Email' }
test('each channel leaves with the member\'s own link tagged for it, and a press reaches the stream once, named for the channel', async ({ page }) => {
await local(page)
await person(page)
await signIn(page, { name: 'ada', email: '[email protected]' })
await page.route('**/v1/marketing/beta', (route) => reply(route, { applied: true, program: 'beta', betas: [], points: 0, place: 7, of: 40, actions: [] }))
await page.route('**/v1/flag/platform', (route) => reply(route, { flags: {} }))
await page.route('**/v1/referral', (route) => reply(route, { code: 'ada7', link: 'https://hanzo.ai/beta?ref=ada7', counts: { total: 0, signup: 0, qualified: 0 }, referrals: [] }))
const seen = await stream(page)
await page.goto(`${base}/beta?checkout=success&subscription=sub_1`)
for (const channel of CHANNELS) {
const link = page.getByRole('link', { name: `Share on ${NAMES[channel]}`, exact: true })
await expect(link).toBeVisible({ timeout: 20_000 })
const to = decodeURIComponent((await link.getAttribute('href')) ?? '')
expect(to, `${channel} carries the member's code`).toContain('ref=ada7')
expect(to).toContain(`utm_source=${channel}`)
expect(to).toContain('utm_medium=' + (channel === 'email' ? 'email' : 'social'))
expect(to).toContain('utm_campaign=beta_referral')
// A mail link opens the mail app, not a page; every other channel opens its own.
const popup = channel === 'email' ? null : page.context().waitForEvent('page')
await link.click()
if (popup) await (await popup).close()
const props = await heard(seen, 'waitlist_shared', (p) => p.method === channel)
expect(props).toMatchObject({ method: channel, program: 'beta', from: 'beta' })
}
// One press, one event: no channel is heard twice.
const shared = seen.filter((e) => e.event === 'waitlist_shared').map((e) => e.properties?.method)
expect(shared).toEqual([...CHANNELS])
})
test('a visitor who follows a shared link keeps the code and the channel, and the code is claimed once they sign in', async ({ page }) => {
await local(page)
await person(page)
const seen = await stream(page)
const claims: unknown[] = []
await page.route('**/v1/referral/claim', (route) => {
if (route.request().method() === 'POST') claims.push(JSON.parse(route.request().postData() || '{}'))
return reply(route, { recorded: true })
})
await page.goto(`${base}/beta?ref=ada7&utm_source=linkedin&utm_medium=social&utm_campaign=beta_referral`)
await heard(seen, 'beta_viewed')
expect(await page.evaluate(() => localStorage.getItem('hz_ref'))).toBe('ada7')
const kept = await page.evaluate(() => sessionStorage.getItem('hz_campaign') ?? '')
expect(Object.fromEntries(new URLSearchParams(kept))).toMatchObject({ ref: 'ada7', utm_source: 'linkedin', utm_medium: 'social', utm_campaign: 'beta_referral' })
expect(claims, 'a stranger claims nothing').toHaveLength(0)
// The same browser signs in: the code is claimed, once, and let go.
await signIn(page, { name: 'bob', email: '[email protected]' })
await page.route('**/v1/marketing/beta', (route) => reply(route, { applied: false, place: 0, of: 0, points: 0, betas: [], actions: [] }))
await page.route('**/v1/flag/platform', (route) => reply(route, { flags: {} }))
await page.goto(`${base}/beta`)
await expect.poll(() => claims.length, { timeout: 20_000 }).toBe(1)
expect(claims[0]).toEqual({ code: 'ada7' })
await expect.poll(() => page.evaluate(() => localStorage.getItem('hz_ref'))).toBeNull()
})