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

63 lines
2.7 KiB
TypeScript

import { test, expect } from '@playwright/test'
const CONTROL_URL = '/control/'
const LIVE = process.env.OPENSQUILLA_E2E_LIVE === '1'
test.describe('Assistant receipt meta', () => {
test('draft chat renders no receipt meta or usage popover', async ({ page }) => {
await page.goto(CONTROL_URL + 'chat/new')
await page.waitForSelector('.conn-pill', { timeout: 10000 })
await expect(page.locator('.msg-ai-meta')).toHaveCount(0)
await expect(page.locator('.msg-meta-popover')).toHaveCount(0)
})
test('live run keeps cost and savings inline and tucks token details into the popover', async ({ page }) => {
test.skip(!LIVE, 'Live gateway test; set OPENSQUILLA_E2E_LIVE=1 to run.')
test.setTimeout(240000)
await page.goto(CONTROL_URL + 'chat/new')
await page.waitForSelector('.conn-pill', { timeout: 10000 })
await page.locator('.chat-textarea').fill('Reply with the single word ok.')
await page.locator('.chat-send-btn[aria-label="Send"]').click()
const meta = page.locator('.msg-ai .msg-ai-meta').last()
await expect(meta).toBeVisible({ timeout: 180000 })
// Cost and savings stay inline on the meta line — no interaction needed.
await expect(meta.locator('.msg-meta__cost')).toContainText('$', { timeout: 60000 })
await expect(meta.locator('.savings-indicator')).toBeVisible()
await expect(meta.locator('.savings-indicator')).toContainText(/Saved ~\d+%/)
// Token/cache/think details are no longer inline on the meta line.
await expect(meta).not.toContainText('cache:')
await expect(meta).not.toContainText('think:')
await expect(meta).not.toContainText('↑')
// The usage popover opens from a focusable trigger and lists token details.
const trigger = meta.getByRole('button', { name: 'Usage details' })
await expect(trigger).toBeVisible()
await trigger.click()
const popover = meta.locator('.msg-meta-popover')
await expect(popover).toBeVisible()
await expect(popover.locator('.msg-meta-popover__label').first()).toHaveText('tokens')
await expect(popover).toContainText('↑')
const labels = await popover.locator('.msg-meta-popover__label').allTextContents()
for (const label of labels) {
expect(['tokens', 'cache', 'think']).toContain(label)
}
// Escape closes the popover and returns focus to the trigger.
await page.keyboard.press('Escape')
await expect(popover).toHaveCount(0)
await expect(trigger).toBeFocused()
// At 390px the receipt stays a single compact row.
await page.setViewportSize({ width: 390, height: 844 })
const box = await meta.boundingBox()
expect(box).not.toBeNull()
expect(box!.height).toBeLessThanOrEqual(30)
})
})