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.
71 lines
3.4 KiB
TypeScript
71 lines
3.4 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
||
import { serveExport } from './export'
|
||
import { answer, signIn } from '../session'
|
||
|
||
/**
|
||
* A STARTER DOES WHAT IT SAYS.
|
||
*
|
||
* Three suggestions sit under an empty chat's composer (@hanzo/build
|
||
* `Suggestions`, since @hanzo/app 0.1.83): the person's activity first, then
|
||
* what they told us at signup, then Hanzo's starters to fill the three. The
|
||
* whole of their value is that pressing one does it, and nothing checked that:
|
||
* a row that stopped working would still draw, still take a press, and read as
|
||
* decoration nobody had noticed was inert.
|
||
*
|
||
* IT SENDS NOW, where it used to seed. `065a987e` made a press put the question
|
||
* in the field to finish; the suggestions that replaced the four fixed openings
|
||
* are actions, each one done on the press ("Pressing a row DOES it", build
|
||
* suggestions.tsx): a `send` row is asked in this chat at once. So the
|
||
* assertion is the turn on the wire and in the transcript, not the field.
|
||
*
|
||
* A person with no activity and nothing kept from signup is the case held here:
|
||
* the account's kept rows are fresh and empty (`GET /v1/pref`), so the three are
|
||
* Hanzo's starters and no model is asked to write any.
|
||
*/
|
||
const STARTER = 'Ask what Hanzo can do for you'
|
||
const PROMPT = 'What can you do for me? Give me three concrete things to try today.'
|
||
const ROWS = ['Ask what Hanzo can do for you', 'Build a landing page with Dev', 'Get a cited brief on AI agents']
|
||
|
||
test('a starter does what it says', async ({ page }) => {
|
||
await signIn(page)
|
||
await answer(page, '/v1/pref', { prefs: { suggest: { at: new Date().toISOString(), sig: '', rows: [], dismissed: [] } } })
|
||
const sent: { messages?: { role: string; content: unknown }[] }[] = []
|
||
await page.route(
|
||
(url) => url.pathname === '/v1/chat/completions',
|
||
async (route) => {
|
||
sent.push(JSON.parse(route.request().postData() || '{}'))
|
||
await route.fulfill({
|
||
status: 200,
|
||
contentType: 'text/event-stream',
|
||
body: `data: ${JSON.stringify({ id: 'x', choices: [{ index: 0, delta: { role: 'assistant', content: 'Three things.' } }] })}\n\ndata: [DONE]\n\n`,
|
||
})
|
||
},
|
||
)
|
||
const site = await serveExport()
|
||
try {
|
||
await page.setViewportSize({ width: 1440, height: 900 })
|
||
await page.goto(`${site.url}/chat`)
|
||
|
||
const field = page.locator('textarea').first()
|
||
await expect(field).toBeVisible({ timeout: 20_000 })
|
||
await expect(field).toHaveValue('')
|
||
|
||
const rows = page.getByRole('list', { name: 'Suggestions' })
|
||
await expect(rows).toBeVisible({ timeout: 20_000 })
|
||
await expect(rows.getByRole('listitem')).toHaveCount(ROWS.length)
|
||
for (const title of ROWS) await expect(rows.getByRole('button', { name: title, exact: true })).toBeVisible()
|
||
expect(sent, 'no model was asked to write the rows').toEqual([])
|
||
|
||
await rows.getByRole('button', { name: STARTER, exact: true }).click()
|
||
|
||
// The press asked its question here, at once: on the wire and in the transcript.
|
||
await expect.poll(() => sent.length, { timeout: 20_000 }).toBeGreaterThan(0)
|
||
expect(JSON.stringify(sent[0].messages), 'the starter’s own question').toContain(PROMPT)
|
||
await expect(page.locator('main').getByText(PROMPT)).toBeVisible()
|
||
await expect(page.locator('main').getByText('Three things.')).toBeVisible({ timeout: 20_000 })
|
||
await expect(rows, 'the suggestions go once something is said').toHaveCount(0)
|
||
} finally {
|
||
await site.close()
|
||
}
|
||
})
|