* feat: persist AI conversations and add chat history Store transcripts and attachment previews in IndexedDB, separate conversation history from transport lifetime, and add document-aware switching with shared Storybook coverage. Preserve interrupted activity and guard stale writes and async switches. * test: group chat history tests by domain * feat: simplify chat history navigation and diagnostics Use a compact header with searchable document-scoped history and a correctly anchored conversation menu. Move diagnostic copying into the menu with copy-result feedback, and separate Storybook fixtures from composition. * fix: address chat history review findings * test: cover harness shutdown and restored chat scrolling * fix: preserve Portless proxy port in MCP routes * docs: clarify UI animation conventions * feat: configure reasoning display and animate disclosure * fix: separate transcript following from reasoning disclosure * fix: restore selected chat after document recovery * refactor: separate chat history persistence and sessions * style: format reasoning story imports
92 lines
3.8 KiB
TypeScript
92 lines
3.8 KiB
TypeScript
import { test, expect } from '#tests/helpers/chat/fixture'
|
|
import { finishReasoning, installReasoningTransport } from '#tests/helpers/chat/reasoning'
|
|
|
|
test('expand while thinking auto-collapses but preserves manual opening', async ({
|
|
configuredChat: chat,
|
|
page
|
|
}) => {
|
|
await page.getByTestId('provider-settings-trigger').click()
|
|
await page.getByRole('combobox', { name: 'Reasoning display' }).click()
|
|
await page.getByRole('option', { name: 'Expand while thinking', exact: true }).click()
|
|
await page.getByTestId('app-settings-done').click()
|
|
await installReasoningTransport(page)
|
|
await chat.submit('Inspect the layout')
|
|
const trigger = page.locator('[data-slot="chat-reasoning-trigger"]').last()
|
|
await expect(trigger).toHaveAttribute('aria-expanded', 'true')
|
|
await finishReasoning(page)
|
|
await expect(trigger).toHaveAttribute('aria-expanded', 'false')
|
|
await chat.submit('Inspect it again')
|
|
await expect(trigger).toHaveAttribute('aria-expanded', 'true')
|
|
await trigger.click()
|
|
await trigger.click()
|
|
await finishReasoning(page)
|
|
await expect(chat.assistantMessage()).toContainText('Finished inspecting')
|
|
await page.waitForTimeout(1200)
|
|
await expect(trigger).toHaveAttribute('aria-expanded', 'true')
|
|
})
|
|
|
|
test('collapsed reasoning respects reduced motion and does not scroll old content to the bottom', async ({
|
|
configuredChat: chat,
|
|
page
|
|
}) => {
|
|
await page.emulateMedia({ reducedMotion: 'reduce' })
|
|
await chat.submit('Show reasoning')
|
|
const trigger = page.locator('[data-slot="chat-reasoning-trigger"]').first()
|
|
await expect(trigger).toHaveAttribute('aria-expanded', 'false')
|
|
await chat.submit(Array.from({ length: 60 }, (_, i) => `Follow-up line ${i}`).join('\n'))
|
|
await expect(chat.assistantMessage()).toBeVisible()
|
|
const viewport = page.getByTestId('chat-panel').locator('[data-reka-scroll-area-viewport]')
|
|
await viewport.evaluate((element) => {
|
|
element.scrollTop = 0
|
|
})
|
|
await trigger.click()
|
|
await expect(trigger).toHaveAttribute('aria-expanded', 'true')
|
|
const content = page.locator('[data-slot="chat-reasoning-content"]').first()
|
|
expect(await content.evaluate((element) => getComputedStyle(element).animationName)).toBe('none')
|
|
await expect
|
|
.poll(() =>
|
|
viewport.evaluate(
|
|
(element) => element.scrollHeight - element.scrollTop - element.clientHeight
|
|
)
|
|
)
|
|
.toBeGreaterThan(500)
|
|
expect(await viewport.evaluate((element) => element.scrollTop)).toBeLessThan(30)
|
|
await page.getByRole('button', { name: 'Jump to latest' }).click()
|
|
await expect
|
|
.poll(() =>
|
|
viewport.evaluate(
|
|
(element) => element.scrollHeight - element.scrollTop - element.clientHeight
|
|
)
|
|
)
|
|
.toBeLessThan(3)
|
|
})
|
|
|
|
test('reasoning preferences use AI settings and persist across reload', async ({
|
|
configuredChat: chat,
|
|
page
|
|
}) => {
|
|
await page.getByTestId('provider-settings-trigger').click()
|
|
const display = page.getByRole('combobox', { name: 'Reasoning display' })
|
|
await expect(display).toContainText('Collapsed by default')
|
|
await display.click()
|
|
await page.getByRole('option', { name: 'Expanded by default', exact: true }).click()
|
|
await page.getByTestId('app-settings-done').click()
|
|
await chat.submit('Show your reasoning')
|
|
const trigger = page.locator('[data-slot="chat-reasoning-trigger"]')
|
|
await expect(trigger).toHaveAttribute('aria-expanded', 'true')
|
|
await trigger.click()
|
|
await expect(trigger).toHaveAttribute('aria-expanded', 'false')
|
|
await expect
|
|
.poll(() =>
|
|
page
|
|
.locator('[data-slot="chat-reasoning-content"]')
|
|
.evaluate((element) => getComputedStyle(element).animationName)
|
|
)
|
|
.toBe('collapsible-up')
|
|
await expect(page.locator('[data-slot="chat-reasoning-content"]')).toBeHidden()
|
|
await page.reload()
|
|
await chat.chatTab.click()
|
|
await page.getByTestId('provider-settings-trigger').click()
|
|
await expect(display).toContainText('Expanded by default')
|
|
})
|