mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
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.
80 lines
3.3 KiB
TypeScript
80 lines
3.3 KiB
TypeScript
import { expect, test, type Page } from '@playwright/test'
|
|
import { serveExport } from './export'
|
|
import { signIn } from '../session'
|
|
import { ready } from './chat'
|
|
|
|
/**
|
|
* THE APP UNDER THE ORGANIZATION GATE (app/_web.tsx, @hanzo/app `App` → `Orgs`).
|
|
*
|
|
* Off hanzo.team no plan stands in front of the app, so billing decides nothing
|
|
* here: a plan read that never answers holds nothing, and one that fails as the
|
|
* tab comes back takes nothing away, the reader's draft included. While the
|
|
* session itself settles the app is drawn under a veil and is inert there
|
|
* (@hanzo/app Orgs.test.ts holds that half).
|
|
*/
|
|
|
|
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 TIER = { user: 'gate', tier: { name: 'dev', displayName: 'Pro' }, balance: { currency: 'usd', effectiveAvailable: 0 } }
|
|
|
|
/** Every platform read answered empty but the plan, which answers when `plan` resolves. */
|
|
async function platform(page: Page, plan: Promise<void>) {
|
|
await page.route(
|
|
(u) => u.pathname.startsWith('/v1/') && !u.pathname.startsWith('/v1/iam/'),
|
|
async (route) => {
|
|
const path = new URL(route.request().url()).pathname
|
|
if (path === '/v1/billing/tier') {
|
|
await plan
|
|
return route.fulfill({ json: TIER })
|
|
}
|
|
if (path === '/v1/billing/balance') return route.fulfill({ json: { balance: 0, holds: 0, available: 0 } })
|
|
if (path.endsWith('/stream')) return route.fulfill({ status: 200, contentType: 'text/event-stream', body: '' })
|
|
return route.fulfill({ json: {} })
|
|
},
|
|
)
|
|
}
|
|
|
|
test('a plan read that never answers holds nothing: the app opens and takes keys', async ({ page }) => {
|
|
await signIn(page)
|
|
await platform(page, new Promise<void>(() => {}))
|
|
await page.setViewportSize({ width: 1440, height: 900 })
|
|
await page.goto(`${base}/chat`)
|
|
await ready(page)
|
|
const held = page.locator('[data-slot="held"]')
|
|
await expect(held).not.toHaveAttribute('inert', '')
|
|
await expect(held).not.toHaveAttribute('aria-hidden', 'true')
|
|
const field = page.getByRole('textbox', { name: 'Ask anything' })
|
|
await field.click()
|
|
await page.keyboard.type('asked while billing says nothing')
|
|
await expect(field).toHaveValue('asked while billing says nothing')
|
|
})
|
|
|
|
test('a billing read that fails as the tab comes back keeps the reader in the room, with their draft', async ({ page }) => {
|
|
await signIn(page)
|
|
await platform(page, Promise.resolve())
|
|
await page.setViewportSize({ width: 1440, height: 900 })
|
|
await page.goto(`${base}/chat`)
|
|
await ready(page)
|
|
const field = page.getByRole('textbox', { name: 'Ask anything' })
|
|
await field.fill('half a question')
|
|
|
|
// Billing fails from here on, and the tab comes back.
|
|
await page.route(
|
|
(u) => u.pathname === '/v1/billing/tier',
|
|
(route) => route.fulfill({ status: 502, json: { status: 502, detail: 'billing is down' } }),
|
|
)
|
|
await page.evaluate(() => window.dispatchEvent(new Event('focus')))
|
|
await page.waitForTimeout(1000)
|
|
|
|
await expect(page.locator('[data-slot="held"]')).not.toHaveAttribute('inert', '')
|
|
await expect(page.getByRole('region', { name: /Subscribe|Choose your plan|Organizations/ })).toHaveCount(0)
|
|
await expect(field, 'the draft is where it was').toHaveValue('half a question')
|
|
})
|