transcript-view-row.client.spec.tsx 3.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. // @vitest-environment jsdom
  2. import { afterEach, describe, expect, it, vi } from 'vitest'
  3. import { cleanup, fireEvent, render, screen } from '@testing-library/react'
  4. import type { SessionListState } from '@deepseek-ai/dsh-api-session-controller/client'
  5. import type { WorkspaceSnapshot } from '@deepseek-ai/dsh-api-workspace-controller/client'
  6. import type { SessionPendingInteractionSnapshot } from '@deepseek-ai/dsh-client-ui-session/client'
  7. import type { GlobalStandardProps } from '@deepseek-ai/dsh-client-ui-slots'
  8. import { createSnapshotStore } from '@deepseek-ai/dsh-client-store'
  9. import { bindSnapshotSelector, makeTranslate } from '@deepseek-ai/dsh-client-test-runtime'
  10. import { TranscriptViewRow, type TranscriptViewRowProps } from '../src/client/settings/TranscriptViewRow.tsx'
  11. import { en, zh } from '../src/client/locale.ts'
  12. afterEach(cleanup)
  13. function emptySessions() {
  14. return bindSnapshotSelector(createSnapshotStore<SessionListState>({
  15. ids: [], byId: {}, current: undefined, phase: 'ready', subagentsByParent: {}, jobsBySession: {}, currentAddress: undefined,
  16. }))
  17. }
  18. function emptyWorkspaces() {
  19. return bindSnapshotSelector(createSnapshotStore<WorkspaceSnapshot>({
  20. items: [], archivedSessionIds: [], state: 'idle', phase: 'ready', error: null,
  21. }))
  22. }
  23. function noPendingInteraction() {
  24. return bindSnapshotSelector(createSnapshotStore<SessionPendingInteractionSnapshot>(new Map()))
  25. }
  26. // The resource hook the resources plugin merges into GlobalStandardProps; this row reads no address.
  27. const useResource = (() => ({ status: 'none' as const, value: undefined, failure: undefined })) as GlobalStandardProps['useResource']
  28. function mount(mode: 'normal' | 'compact' = 'compact', dictionary: typeof en | typeof zh = en) {
  29. const source = createSnapshotStore(mode)
  30. const setTranscriptView = vi.fn((next: 'normal' | 'compact') => { source.set(next) })
  31. const props: TranscriptViewRowProps = {
  32. usePanelInfo: selector => selector({ activePanelId: null }),
  33. useSessions: emptySessions(),
  34. useSessionPendingInteraction: noPendingInteraction(),
  35. useWorkspaces: emptyWorkspaces(),
  36. useResource,
  37. useTranscriptView: bindSnapshotSelector(source),
  38. setTranscriptView,
  39. t: makeTranslate(dictionary),
  40. }
  41. render(<TranscriptViewRow {...props} />)
  42. return { setTranscriptView }
  43. }
  44. describe('TranscriptViewRow', () => {
  45. it('explains the preference and shows Compact by default', () => {
  46. mount()
  47. expect(screen.getByText('Conversation display')).toBeDefined()
  48. expect(screen.getByText('Controls process content in completed turns')).toBeDefined()
  49. expect(screen.getByRole('button', { name: /Compact/ }).getAttribute('aria-expanded')).toBe('false')
  50. })
  51. it('selects Normal and follows the mirrored value', () => {
  52. const b = mount()
  53. fireEvent.click(screen.getByRole('button', { name: /Compact/ }))
  54. fireEvent.click(screen.getByRole('menuitem', { name: 'Normal' }))
  55. expect(b.setTranscriptView).toHaveBeenCalledWith('normal')
  56. const trigger = screen.getByRole('button', { name: /Normal/ })
  57. fireEvent.click(trigger)
  58. expect(screen.getByRole('menuitem', { name: 'Compact' })).toBeDefined()
  59. fireEvent.pointerDown(document.body)
  60. expect(screen.queryByRole('menuitem', { name: 'Compact' })).toBeNull()
  61. })
  62. it('shows the conversation-display values in Chinese', () => {
  63. mount('compact', zh)
  64. fireEvent.click(screen.getByRole('button', { name: '紧凑' }))
  65. fireEvent.click(screen.getByRole('menuitem', { name: '标准' }))
  66. expect(screen.getByRole('button', { name: '标准' })).toBeDefined()
  67. })
  68. })