document-title.client.spec.tsx 4.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  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, retainedBy: { mainView: 1 }, blank: false, updatedAt: 1 } },
  20. phase: 'ready',
  21. subagentsByParent: {},
  22. jobsBySession: {},
  23. })
  24. const panelInfo = createSnapshotStore<PanelInfo>({ activePanelId: null })
  25. return {
  26. sessionId, sessions, panelInfo,
  27. props: { useSessions: bindSnapshotSelector(sessions), usePanelInfo: bindSnapshotSelector(panelInfo) },
  28. }
  29. }
  30. describe('DocumentTitle', () => {
  31. it('projects a durable title and restores the product title', () => {
  32. const { sessionId, sessions, props } = titleSources()
  33. document.title = 'stale title'
  34. const mounted = render(<DocumentTitle {...props} productTitle="DeepSeek Harness" />)
  35. expect(document.title).toBe('DeepSeek Harness')
  36. act(() => { sessions.update((state) => { state.byId[sessionId]!.title = 'First title' }) })
  37. expect(document.title).toBe('First title — DeepSeek Harness')
  38. act(() => { sessions.update((state) => { state.byId[sessionId]!.title = 'Revised title' }) })
  39. expect(document.title).toBe('Revised title — DeepSeek Harness')
  40. act(() => {
  41. const state = sessions.getSnapshot()
  42. sessions.set({
  43. ...state,
  44. byId: { ...state.byId, [sessionId]: { ...state.byId[sessionId]!, retainedBy: {} } },
  45. })
  46. })
  47. expect(document.title).toBe('DeepSeek Harness')
  48. mounted.unmount()
  49. expect(document.title).toBe('DeepSeek Harness')
  50. })
  51. it('uses the localized product title supplied by the frame', () => {
  52. const { sessionId, sessions, props } = titleSources()
  53. sessions.update((state) => { state.byId[sessionId]!.title = 'First title' })
  54. const mounted = render(<DocumentTitle {...props} productTitle="DSH Local Build" />)
  55. expect(document.title).toBe('First title — DSH Local Build')
  56. mounted.unmount()
  57. expect(document.title).toBe('DSH Local Build')
  58. })
  59. it('keeps the product title across global panels and restores the latest Session title on return', () => {
  60. const { sessionId, sessions, panelInfo, props } = titleSources()
  61. sessions.update((state) => { state.byId[sessionId]!.title = 'Session title' })
  62. render(<DocumentTitle {...props} productTitle="Product" />)
  63. expect(document.title).toBe('Session title — Product')
  64. act(() => { panelInfo.set({ activePanelId: 'panel-a' as MainPanelId }) })
  65. expect(document.title).toBe('Product')
  66. act(() => { sessions.update((state) => { state.byId[sessionId]!.title = 'Updated title' }) })
  67. expect(document.title).toBe('Product')
  68. act(() => { panelInfo.set({ activePanelId: 'panel-b' as MainPanelId }) })
  69. expect(document.title).toBe('Product')
  70. expect(sessions.getSnapshot().byId[sessionId]?.retainedBy.mainView).toBe(1)
  71. act(() => { panelInfo.set({ activePanelId: null }) })
  72. expect(document.title).toBe('Updated title — Product')
  73. })
  74. it('uses the product title when the current Session row is not available', () => {
  75. const { sessions, props } = titleSources()
  76. sessions.update((state) => { state.byId = {}; state.ids = [] })
  77. render(<DocumentTitle {...props} productTitle="Product" />)
  78. expect(document.title).toBe('Product')
  79. })
  80. })