document-title.client.spec.tsx 3.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. // @vitest-environment jsdom
  2. import { afterEach, beforeEach, describe, expect, it } from 'vitest'
  3. import { act, cleanup, render } from '@testing-library/react'
  4. import { createSnapshotStore } from '@deepseek-ai/dsh-client-store'
  5. import { bindSnapshotSelector } from '@deepseek-ai/dsh-client-test-runtime'
  6. import type { SessionListState } from '@deepseek-ai/dsh-api-session-controller/client'
  7. import type { SessionId } from '@deepseek-ai/dsh-session/types'
  8. import { DocumentTitle } from '../src/client/DocumentTitle.tsx'
  9. import type { MainPanelId, PanelInfo } from '../src/client/service.ts'
  10. let originalTitle: string
  11. beforeEach(() => { originalTitle = document.title })
  12. afterEach(() => {
  13. try { cleanup() } finally { document.title = originalTitle }
  14. })
  15. function titleSources() {
  16. const sessionId = 'session-title' as SessionId
  17. const sessions = createSnapshotStore<SessionListState>({
  18. ids: [sessionId],
  19. byId: { [sessionId]: { id: sessionId, displayTitle: 'Test', running: false, blank: false, updatedAt: 1 } },
  20. current: sessionId,
  21. phase: 'ready',
  22. subagentsByParent: {},
  23. jobsBySession: {},
  24. currentAddress: undefined,
  25. })
  26. const panelInfo = createSnapshotStore<PanelInfo>({ activePanelId: null })
  27. return {
  28. sessionId, sessions, panelInfo,
  29. props: { useSessions: bindSnapshotSelector(sessions), usePanelInfo: bindSnapshotSelector(panelInfo) },
  30. }
  31. }
  32. describe('DocumentTitle', () => {
  33. it('projects a durable title and restores the product title', () => {
  34. const { sessionId, sessions, props } = titleSources()
  35. document.title = 'stale title'
  36. const mounted = render(<DocumentTitle {...props} productTitle="DeepSeek Harness" />)
  37. expect(document.title).toBe('DeepSeek Harness')
  38. act(() => { sessions.update((state) => { state.byId[sessionId]!.title = 'First title' }) })
  39. expect(document.title).toBe('First title — DeepSeek Harness')
  40. act(() => { sessions.update((state) => { state.byId[sessionId]!.title = 'Revised title' }) })
  41. expect(document.title).toBe('Revised title — DeepSeek Harness')
  42. act(() => { sessions.update((state) => { state.current = undefined }) })
  43. expect(document.title).toBe('DeepSeek Harness')
  44. mounted.unmount()
  45. expect(document.title).toBe('DeepSeek Harness')
  46. })
  47. it('uses the localized product title supplied by the frame', () => {
  48. const { sessionId, sessions, props } = titleSources()
  49. sessions.update((state) => { state.byId[sessionId]!.title = 'First title' })
  50. const mounted = render(<DocumentTitle {...props} productTitle="DSH Local Build" />)
  51. expect(document.title).toBe('First title — DSH Local Build')
  52. mounted.unmount()
  53. expect(document.title).toBe('DSH Local Build')
  54. })
  55. it('keeps the product title across global panels and restores the latest Session title on return', () => {
  56. const { sessionId, sessions, panelInfo, props } = titleSources()
  57. sessions.update((state) => { state.byId[sessionId]!.title = 'Session title' })
  58. render(<DocumentTitle {...props} productTitle="Product" />)
  59. expect(document.title).toBe('Session title — Product')
  60. act(() => { panelInfo.set({ activePanelId: 'panel-a' as MainPanelId }) })
  61. expect(document.title).toBe('Product')
  62. act(() => { sessions.update((state) => { state.byId[sessionId]!.title = 'Updated title' }) })
  63. expect(document.title).toBe('Product')
  64. act(() => { panelInfo.set({ activePanelId: 'panel-b' as MainPanelId }) })
  65. expect(document.title).toBe('Product')
  66. expect(sessions.getSnapshot().current).toBe(sessionId)
  67. act(() => { panelInfo.set({ activePanelId: null }) })
  68. expect(document.title).toBe('Updated title — Product')
  69. })
  70. it('uses the product title when the current Session row is not available', () => {
  71. const { sessions, props } = titleSources()
  72. sessions.update((state) => { state.byId = {}; state.ids = [] })
  73. render(<DocumentTitle {...props} productTitle="Product" />)
  74. expect(document.title).toBe('Product')
  75. })
  76. })