Swaps the rendering layer only. Chat store logic, keyboard handling, and the /chat-run Socket.IO contract are untouched, so a mixed UI cannot hide a chat regression behind a library swap. Arco's dropdowns take their items from a #content slot of a-doption children rather than a droplist prop, and Arco Triggers cannot be nested, so the two toolbar menus lost their a-tooltip in favour of a native title. Both rules are recorded in docs/arco-mapping.md, and tests/e2e/chat-input-dropdown.spec.ts plus tests/e2e/chat-rendering-smoke.spec.ts keep the menus, the streaming markdown rendering, and the no-Arco-inside-the-bubble red line from regressing silently.
66 lines
2.7 KiB
TypeScript
66 lines
2.7 KiB
TypeScript
import { expect, test } from '@playwright/test'
|
|
import { authenticate, mockChatSocket, mockHermesApi, TEST_ACCESS_KEY } from './fixtures'
|
|
|
|
const sampleSession = {
|
|
id: 'sess-1',
|
|
title: 'First session',
|
|
source: 'cli',
|
|
created_at: 1760000000000,
|
|
updated_at: 1760000000000,
|
|
message_count: 2,
|
|
input_tokens: 0,
|
|
output_tokens: 0,
|
|
reasoning_tokens: 0,
|
|
}
|
|
|
|
// Arco 的 a-dropdown 没有 droplist prop:菜单内容必须走 #content 插槽 + a-doption。
|
|
// 传 :droplist 会被静默忽略,弹层能打开但列表是空的(历史上就是这么坏的)。
|
|
test('reasoning effort dropdown renders its options and applies the selection', async ({ page }) => {
|
|
await authenticate(page, TEST_ACCESS_KEY, 'research')
|
|
await mockHermesApi(page, { sessions: [sampleSession] })
|
|
await mockChatSocket(page)
|
|
|
|
await page.goto('/#/hermes/chat')
|
|
|
|
await page.locator('.reasoning-effort-button').click()
|
|
const popup = page.locator('.arco-trigger-popup').filter({ hasText: 'Minimal' })
|
|
await expect(popup).toBeVisible()
|
|
await expect(popup.locator('.arco-dropdown-option')).toHaveCount(7)
|
|
|
|
await popup.locator('.arco-dropdown-option', { hasText: 'High' }).first().click()
|
|
await expect(page.locator('.reasoning-effort-label')).toHaveText('High')
|
|
})
|
|
|
|
test('input settings dropdown renders its options and toggles auto play voice', async ({ page }) => {
|
|
await authenticate(page, TEST_ACCESS_KEY, 'research')
|
|
await mockHermesApi(page, { sessions: [sampleSession] })
|
|
await mockChatSocket(page)
|
|
|
|
await page.goto('/#/hermes/chat')
|
|
|
|
await page.locator('.input-settings-button').click()
|
|
const popup = page.locator('.arco-trigger-popup').filter({ hasText: 'Auto-play voice' })
|
|
await expect(popup).toBeVisible()
|
|
await expect(popup.locator('.arco-dropdown-option')).toHaveCount(2)
|
|
|
|
await popup.locator('.arco-dropdown-option').first().click()
|
|
await expect(page.locator('.settings-check').first()).toHaveText('✓')
|
|
})
|
|
|
|
test('session context menu renders flat items and its export submenu', async ({ page }) => {
|
|
await authenticate(page, TEST_ACCESS_KEY, 'research')
|
|
await mockHermesApi(page, { sessions: [sampleSession] })
|
|
await mockChatSocket(page)
|
|
|
|
await page.goto('/#/hermes/chat')
|
|
|
|
await page.locator('.session-item').first().click({ button: 'right' })
|
|
const popup = page.locator('.arco-trigger-popup').filter({ hasText: 'Rename' })
|
|
await expect(popup).toBeVisible()
|
|
// pin / rename / archive / workspace / model / export(submenu) / open-link / copy-link / copy-id
|
|
await expect(popup.locator('.arco-dropdown-option')).toHaveCount(9)
|
|
|
|
await popup.locator('.arco-dropdown-option', { hasText: 'Export' }).click()
|
|
await expect(page.locator('.arco-trigger-popup').filter({ hasText: 'Compressed' })).toBeVisible()
|
|
})
|