Files

135 lines
6.4 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 { serveExport } from './export'
import { bare, config, person } from './heard'
import releases from '../../data/releases.json' with { type: 'json' }
/**
* HANZO.APP IS WHERE THE APP IS GOT.
*
* Its root is this export's /app, served by the edge with the export's assets
* (universe static-sites `hanzo-app`); every other address there is hanzo.ai's.
* The page offers three things and each must work: Download is a build the
* release carries, for the reader's own platform first; Install CLI puts the
* one install line on the clipboard; Open in web is the chat at hanzo.ai. It
* reports to hanzo.app's own GA4 stream, and hanzo.ai keeps its own.
*
* hanzo.app is HSTS-preloaded, so it is routed rather than resolved: `/` is
* answered with the export's app page and everything else with the export's own
* file, which is what the edge does. Nothing leaves this machine.
*/
const HANZO_APP = 'G-P3G0R2PX3D'
const HANZO_AI = 'G-RBVVVLFZDR'
const MAC = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36'
let base: string
let stop: () => Promise<void>
test.beforeAll(async () => {
const served = await serveExport()
base = served.url
stop = served.close
})
test.afterAll(async () => stop())
/** hanzo.app as the edge answers it; hanzo.ai stubbed; returns the GA4 ids asked for. */
async function edge(page: Page): Promise<{ ga: string[]; ai: string[] }> {
await person(page)
await page.route('**/*', (route) => (new URL(route.request().url()).origin === base ? route.continue() : route.abort()))
const ga: string[] = []
const ai: string[] = []
await config(page)
await page.route('https://www.googletagmanager.com/**', (route) => {
ga.push(new URL(route.request().url()).searchParams.get('id') ?? '')
return route.fulfill({ status: 200, contentType: 'text/javascript', body: '' })
})
await page.route('https://hanzo.app/**', async (route) => {
const u = new URL(route.request().url())
const path = u.pathname === '/' ? '/app' : u.pathname
return route.fulfill({ response: await route.fetch({ url: `${base}${path}${u.search}` }) })
})
await page.route('https://hanzo.ai/**', (route) => {
ai.push(route.request().url())
return route.fulfill({ status: 200, contentType: 'text/html', body: '<!doctype html><title>Hanzo</title>' })
})
return { ga, ai }
}
test.describe('hanzo.app', () => {
test.use({ userAgent: MAC, permissions: ['clipboard-read', 'clipboard-write'] })
test('is the page that gets the app: Download, Install CLI, Open in web', async ({ page }) => {
await edge(page)
await page.goto('https://hanzo.app/')
await expect(page.getByRole('heading', { level: 1, name: 'Get the Hanzo App' })).toBeVisible()
for (const choice of ['Download', 'Install CLI', 'Open in web']) await expect(page.getByRole('heading', { level: 2, name: choice })).toBeVisible()
expect(await page.locator('link[rel=canonical]').getAttribute('href')).toBe('https://hanzo.app')
})
test('Download leads with the reader’s own build, one the release carries', async ({ page }) => {
await edge(page)
await page.goto('https://hanzo.app/')
const own = page.getByRole('link', { name: /^Download for macOS/ })
await expect(own).toBeVisible()
// The desktop app once its release carries builds, and the CLI until then (data/downloads `NATIVE`).
const shelf = releases as Record<string, { platforms: Record<string, { url: string }> }>
const native = Object.keys(shelf.desktop.platforms).length ? shelf.desktop : shelf.cli
const built = Object.values(native.platforms).map((p) => p.url)
expect(built).toContain(await own.getAttribute('href'))
// Every other platform is a row of its own.
await expect(page.getByRole('link', { name: /^Download .* for Windows$/ })).toBeVisible()
await expect(page.getByRole('link', { name: 'All downloads and checksums →' })).toHaveAttribute('href', 'https://hanzo.ai/download')
})
test('Install CLI puts the install line on the clipboard', async ({ page }) => {
await edge(page)
await page.goto('https://hanzo.app/')
await expect(page.getByText('curl -fsSL hanzo.sh | sh', { exact: true })).toBeVisible()
// The line is in the export's HTML; the button copies only once React has hydrated it.
await page.locator('html[data-hydrated]').waitFor({ state: 'attached' })
await page.getByRole('button', { name: 'Copy the install command' }).click()
await expect(page.getByRole('button', { name: 'Copied' })).toBeVisible()
expect(await page.evaluate(() => navigator.clipboard.readText())).toBe('curl -fsSL hanzo.sh | sh')
})
test('Open in web opens the chat at hanzo.ai, in this tab', async ({ page }) => {
const { ai } = await edge(page)
await page.goto('https://hanzo.app/')
await page.getByRole('link', { name: 'Open Hanzo' }).click()
await page.waitForURL((u) => bare(u.href) === 'https://hanzo.ai/chat')
expect(ai.map(bare)).toEqual(['https://hanzo.ai/chat'])
expect(page.context().pages()).toHaveLength(1)
})
test('reports to hanzo.app’s own GA4 stream', async ({ page }) => {
const { ga } = await edge(page)
await page.goto('https://hanzo.app/')
await expect.poll(() => ga).toEqual([HANZO_APP])
const config = await page.evaluate(() =>
((window as unknown as { dataLayer: IArguments[] }).dataLayer ?? []).map((a) => Array.from(a)).find((a) => a[0] === 'config'),
)
expect(config?.[1]).toBe(HANZO_APP)
})
})
test('the same page on hanzo.ai reports to hanzo.ai’s stream', async ({ page }) => {
const { ga } = await edge(page)
await page.goto(`${base}/app`)
await expect.poll(() => ga).toEqual([HANZO_AI])
})
test('a phone is not offered a build it cannot open', async ({ browser }) => {
const phone = await browser.newContext({
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1',
viewport: { width: 390, height: 844 },
hasTouch: true,
})
const page = await phone.newPage()
await edge(page)
await page.goto('https://hanzo.app/')
await expect(page.getByText('There is no build for a phone. Open the app in this browser instead.')).toBeVisible()
await expect(page.getByRole('link', { name: /^Download for / })).toHaveCount(0)
await expect(page.getByRole('link', { name: 'Open Hanzo' })).toBeVisible()
await phone.close()
})