Files
2026-07-13 13:12:33 +08:00

100 lines
4.1 KiB
TypeScript

import { test, expect, type Page } from '@playwright/test'
const CONTROL_URL = '/control/'
const RAW_KEY_PATTERN = /agent:[a-z0-9_-]+:[a-z0-9_-]+:/i
async function openHub(page: Page) {
await page.goto(CONTROL_URL + 'sessions')
await page.waitForSelector('.conn-pill', { timeout: 10000 })
await page.waitForSelector('.conn-pill.connected', { timeout: 10000 }).catch(() => {})
// Let the session ledger settle before inspecting it.
await page.waitForTimeout(800)
await expect(page.locator('.hub-ledger, .hub-state').first()).toBeVisible()
}
async function openDrawerOnFirstRow(page: Page) {
const rows = page.locator('.hub-row')
test.skip((await rows.count()) === 0, 'No sessions on this gateway; seed sessions to exercise the drawer')
await rows.first().locator('.hub-row__main').click()
await expect(page.locator('.inspect-drawer')).toBeVisible()
}
test.describe('Session inspect drawer', () => {
test('row click opens the drawer with a transcript preview', async ({ page }) => {
await openHub(page)
await openDrawerOnFirstRow(page)
const drawer = page.locator('.inspect-drawer')
await expect(drawer).toHaveAttribute('role', 'dialog')
await expect(drawer).toHaveAttribute('aria-modal', 'true')
// Row click no longer navigates; inspection happens in place.
await expect(page).toHaveURL(/\/sessions$/)
// Transcript settles into messages, an honest empty state, or an
// ErrorState — never a blank region.
await expect(drawer.locator('.inspect-msg, .inspect-empty, .error-state').first())
.toBeVisible({ timeout: 10000 })
const messageCount = await drawer.locator('.inspect-msg').count()
if (messageCount > 0) {
const text = (await drawer.locator('.inspect-msg').first().innerText()).trim()
expect(text.length).toBeGreaterThan(0)
}
// The raw session key is copyable but never displayed.
await expect(drawer.getByRole('button', { name: 'Copy session key' })).toBeVisible()
const headerText = await drawer.locator('.inspect-head').innerText()
const metaText = await drawer.locator('.inspect-meta').innerText()
expect(headerText + metaText).not.toMatch(RAW_KEY_PATTERN)
})
test('Escape closes the drawer', async ({ page }) => {
await openHub(page)
await openDrawerOnFirstRow(page)
await page.keyboard.press('Escape')
await expect(page.locator('.inspect-drawer')).toHaveCount(0)
await expect(page).toHaveURL(/\/sessions$/)
})
test('Open in chat navigates with the session param', async ({ page }) => {
await openHub(page)
await openDrawerOnFirstRow(page)
await page.locator('.inspect-drawer').getByRole('button', { name: 'Open in chat', exact: true }).click()
await expect(page).toHaveURL(/\/chat\?session=/)
})
test('abort action is absent on idle sessions', async ({ page }) => {
await openHub(page)
const idleRows = page.locator('.hub-row').filter({
hasNot: page.locator('.hub-row__status--running, .hub-row__status--queued'),
})
test.skip((await idleRows.count()) === 0, 'No idle sessions on this gateway')
await idleRows.first().locator('.hub-row__main').click()
const drawer = page.locator('.inspect-drawer')
await expect(drawer).toBeVisible()
await expect(drawer.getByRole('button', { name: /Abort/ })).toHaveCount(0)
await expect(drawer.getByRole('button', { name: 'Open in chat' })).toBeVisible()
})
test('Load earlier extends the transcript when more history exists', async ({ page }) => {
await openHub(page)
await openDrawerOnFirstRow(page)
const drawer = page.locator('.inspect-drawer')
await expect(drawer.locator('.inspect-msg, .inspect-empty, .error-state').first())
.toBeVisible({ timeout: 10000 })
const earlier = drawer.getByRole('button', { name: 'Load earlier' })
test.skip((await earlier.count()) === 0, 'Transcript fits one page on this gateway')
const before = await drawer.locator('.inspect-msg').count()
await earlier.click()
await expect
.poll(async () => drawer.locator('.inspect-msg').count(), { timeout: 10000 })
.toBeGreaterThan(before)
})
})