cell.spec.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. // @vitest-environment jsdom
  2. /**
  3. * TrajectoryCell presentation: kind tags, ellipsis-hosting text, Message
  4. * metric columns, own-duration formatting, and selected ring.
  5. */
  6. import { afterEach, describe, expect, it } from 'vitest'
  7. import { cleanup, render, screen } from '@testing-library/react'
  8. import {
  9. formatElapsedSeconds,
  10. TrajectoryCell,
  11. type TrajectoryCellKind,
  12. } from '../src/client/TrajectoryCell.tsx'
  13. afterEach(cleanup)
  14. describe('formatElapsedSeconds', () => {
  15. it('formats known durations and uses an em dash when absent', () => {
  16. expect(formatElapsedSeconds(null)).toBe('—')
  17. expect(formatElapsedSeconds(235)).toBe('+235s')
  18. expect(formatElapsedSeconds(235.0)).toBe('+235s')
  19. expect(formatElapsedSeconds(235.2)).toBe('+235.2s')
  20. expect(formatElapsedSeconds(235.25)).toBe('+235.3s')
  21. expect(formatElapsedSeconds(0)).toBe('+0s')
  22. expect(formatElapsedSeconds(Number.NaN)).toBe('—')
  23. })
  24. })
  25. describe('TrajectoryCell', () => {
  26. it('renders index, kind tag, text, and time for a Tool row', () => {
  27. render(
  28. <TrajectoryCell
  29. index={6}
  30. kind="tool"
  31. text="bash · Read src/index.ts"
  32. timeSeconds={5}
  33. />,
  34. )
  35. expect(screen.getByText('#6')).toBeTruthy()
  36. expect(screen.getByText('Tool')).toBeTruthy()
  37. expect(screen.getByText('bash · Read src/index.ts')).toBeTruthy()
  38. expect(screen.getByText('+5s')).toBeTruthy()
  39. })
  40. it('Message rows expose Input / Output / Think metric columns before time', () => {
  41. const { container } = render(
  42. <TrajectoryCell
  43. index={3}
  44. kind="message"
  45. text="Let me now read the actual source files to understa..."
  46. timeSeconds={235.2}
  47. input={136}
  48. output={381}
  49. think={155}
  50. />,
  51. )
  52. expect(screen.getByText('Message')).toBeTruthy()
  53. expect(screen.getByText('136')).toBeTruthy()
  54. expect(screen.getByText('381')).toBeTruthy()
  55. expect(screen.getByText('155')).toBeTruthy()
  56. expect(screen.getByText('+235.2s')).toBeTruthy()
  57. const texts = [...container.querySelectorAll('span')].map((el) => el.textContent)
  58. expect(texts.indexOf('136')).toBeLessThan(texts.indexOf('381'))
  59. expect(texts.indexOf('381')).toBeLessThan(texts.indexOf('155'))
  60. expect(texts.indexOf('155')).toBeLessThan(texts.indexOf('+235.2s'))
  61. })
  62. it('selected marks the row for the brand-primary inset ring', () => {
  63. const { container } = render(
  64. <TrajectoryCell index={15} kind="message" text="pictur..." timeSeconds={123.6} selected />,
  65. )
  66. expect(container.firstElementChild?.getAttribute('data-selected')).toBe('true')
  67. })
  68. it.each([
  69. ['user', 'User'],
  70. ['tool', 'Tool'],
  71. ] as const)('kind %s shows the %s tag and no metric columns', (kind: TrajectoryCellKind, label: string) => {
  72. const { container } = render(
  73. <TrajectoryCell index={1} kind={kind} text="summary" timeSeconds={kind === 'user' ? 0 : null} input={1} output={2} think={3} />,
  74. )
  75. expect(screen.getByText(label)).toBeTruthy()
  76. expect(container.querySelector('[data-kind]')?.getAttribute('data-kind')).toBe(kind)
  77. expect(screen.queryByText('1')).toBeNull()
  78. expect(screen.queryByText('2')).toBeNull()
  79. expect(screen.queryByText('3')).toBeNull()
  80. })
  81. })