Files
zeekay 6c65e3e941 @hanzo/app 0.2.0: the app SDK, renamed from @hanzo/rooms
Every import, the catalog pin and the docs name @hanzo/app and
github.com/hanzoai/app. lib/rooms.ts is lib/app.ts and
components/rooms.tsx is components/app.tsx.
2026-10-22 19:07:20 -07:00

83 lines
3.1 KiB
TypeScript

import { test, expect } from '@playwright/test'
import { SEAL } from '@hanzo/app/lib/share'
/**
* THE SHARE TOKEN LEAVES THE ADDRESS BEFORE ANYTHING READS IT.
*
* `SEAL` is the first script in the document head. It runs as the page is
* parsed and again on every fragment change, with its listeners registered
* before any tag's, so an ad tag's own history listener reads an address that
* no longer carries the token. These run it against a stand-in window.
*/
const TOKEN = 'Zq3LwX0p-Tf9_aB7kQmN2rS8vY1cD4eF6gH5jK0lMnO'
function page(href: string) {
const u = new URL(href)
const listeners: Record<string, (() => void)[]> = {}
const stored: Record<string, string> = {}
const win: Record<string, unknown> = {}
const location = { pathname: u.pathname, search: u.search, hash: u.hash }
const history = {
replaceState: (_s: unknown, _t: string, url: string) => {
const next = new URL(url, 'https://hanzo.ai')
location.pathname = next.pathname
location.search = next.search
location.hash = next.hash
},
}
const sessionStorage = { setItem: (k: string, v: string) => (stored[k] = v) }
const addEventListener = (type: string, fn: () => void) => (listeners[type] ??= []).push(fn)
const run = new Function('location', 'history', 'sessionStorage', 'addEventListener', 'window', SEAL)
run(location, history, sessionStorage, addEventListener, win)
return {
location,
stored,
win,
fire(type: string, hash: string) {
location.hash = hash
for (const fn of listeners[type] ?? []) fn()
},
}
}
test.describe('the share token leaves the address', () => {
for (const path of ['/chat/shared', '/chat/shared/', '/chat/shared?utm_source=x', '/chat/shared/index.html', '/chat/shared.html', '/Chat/Shared', '/chat//shared']) {
test(`on ${path}`, () => {
const p = page(`https://hanzo.ai${path}#${TOKEN}`)
expect(p.location.hash).toBe('')
expect(p.stored['hanzo:share:open']).toBe(TOKEN)
expect(p.win.hzShare).toBe(TOKEN)
})
}
test('on any page whose fragment has a token shape, the not-found page included', () => {
const p = page(`https://hanzo.ai/no/such/page#${TOKEN}`)
expect(p.location.hash).toBe('')
expect(p.stored['hanzo:share:open']).toBe(TOKEN)
})
test('an ordinary fragment elsewhere is left alone', () => {
const p = page('https://hanzo.ai/pricing#choices')
expect(p.location.hash).toBe('#choices')
expect(p.stored['hanzo:share:open']).toBeUndefined()
})
test('a fragment that does not decode is taken as written', () => {
const p = page('https://hanzo.ai/chat/shared#%E0%A4%A')
expect(p.location.hash).toBe('')
expect(p.stored['hanzo:share:open']).toBe('%E0%A4%A')
})
test('a link pasted into an open tab is taken on hashchange and on popstate', () => {
const p = page('https://hanzo.ai/chat/shared')
const second = TOKEN.replace('Z', 'Y')
p.fire('hashchange', `#${TOKEN}`)
expect(p.location.hash).toBe('')
expect(p.stored['hanzo:share:open']).toBe(TOKEN)
p.fire('popstate', `#${second}`)
expect(p.location.hash).toBe('')
expect(p.stored['hanzo:share:open']).toBe(second)
})
})