Files
hanzo.ai/e2e/gates/inbox.spec.ts
zooqueenandHanzo Dev 5babb44718 The rail names a room by its address, as the pane does
A room's row read `#<last sender>`, so two Slack rooms with the same
last speaker shared one name and the list disagreed with the header.
roomName is the one rule, in network.tsx beside speaker: the handle for
a DM, the network's room id otherwise.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-15 12:14:15 -07:00

82 lines
2.9 KiB
TypeScript

import { test, expect } from '@playwright/test'
import { serveExport } from './export'
import { signIn, answer } from '../session'
/**
* THE INBOX DRAWS WHAT ARRIVED.
*
* Nothing covered this room with anything in it — only its signed-out sentence
* — so a break in the list would have shipped behind a green suite. It reads
* `/v1/channels/inbox`, which is live on production; what it draws was never
* measured.
*
* The shape is the platform's: `{ messages, cursor }` where a message carries a
* numeric `id`, its `channel`, an opaque platform `sender`, `text`, and
* `createdAt` in SECONDS. A room's kind is "dm" or "thread". Getting that wrong
* renders the empty state, which reads exactly like a broken read.
*/
const now = Math.floor(Date.now() / 1000)
const INBOX = {
messages: [
{
id: 2,
channel: 'slack',
account: 'acme',
roomId: 'r1',
roomKind: 'thread',
sender: 'U031THJJ7F1',
text: 'deploy is green',
createdAt: now,
},
{
id: 1,
channel: 'email',
account: 'acme',
roomId: 'r2',
roomKind: 'dm',
sender: '[email protected]',
text: 'invoice attached',
createdAt: now - 60,
},
],
cursor: 2,
}
test('the inbox lists what arrived, and waits to be asked which to read', async ({ page }) => {
await signIn(page)
await answer(page, '/v1/channels/inbox', INBOX)
const site = await serveExport()
try {
await page.setViewportSize({ width: 1440, height: 900 })
await page.goto(`${site.url}/inbox`)
const room = page.locator('main')
await expect(room).toContainText('deploy is green', { timeout: 20_000 })
await expect(room).toContainText('invoice attached')
// Both transports are drawn, and the sender is the platform's own handle —
// the schema declares a Hanzo user behind it and every measured message
// omits one, so a handle is what there is.
await expect(room).toContainText('U031THJJ7F1')
// A room is named by its own address in the list, as in the pane: never by
// whoever spoke in it last, which gave two rooms one name.
await expect(page.getByText('#r1', { exact: true })).toBeVisible()
await expect(page.getByText('#U031THJJ7F1', { exact: true })).toHaveCount(0)
// THE LIST STANDS BESIDE WHAT IS OPEN rather than being replaced by it, so
// with nothing picked the room says so instead of drawing a blank half.
// "a conversation", not "a message": the pane holds this platform's rooms
// and every network's arrivals in one list now, and a word that names only
// half of what is in it would send a reader looking for the other half.
await expect(room).toContainText('Pick a conversation to read it')
// And the empty state is absent, which is what a read that never landed
// would have left on screen.
expect(await room.innerText(), 'the inbox read nothing').not.toContain('Nothing has arrived yet')
} finally {
await site.close()
}
})