approval-command.client.spec.tsx 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. // @vitest-environment jsdom
  2. import { Context } from '@deepseek-ai/cordis'
  3. import type { ChatSnapshot, UseChat } from '@deepseek-ai/dsh-client-ui-chat/client'
  4. import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
  5. import { render, screen } from '@testing-library/react'
  6. import { describe, expect, it } from 'vitest'
  7. import { ApprovalCommand, commandOf } from '../src/client/chat/ApprovalCommand.tsx'
  8. import { apply as nodeApply } from '../src/index.ts'
  9. function props(
  10. nodes: readonly unknown[],
  11. callId = 'call-1',
  12. ): PropsRuntime<'conversation.approval.detail'> {
  13. const snapshot = {
  14. nodes: { values: () => nodes },
  15. } as unknown as ChatSnapshot
  16. const useChat = ((selector: (value: ChatSnapshot) => unknown) => selector(snapshot)) as UseChat
  17. return { callId, useChat } as PropsRuntime<'conversation.approval.detail'>
  18. }
  19. describe('commandOf', () => {
  20. it('accepts only a string command from valid JSON arguments', () => {
  21. expect(commandOf(undefined)).toBeUndefined()
  22. expect(commandOf({ callId: 'c1', argsRaw: '{' })).toBeUndefined()
  23. expect(commandOf({ callId: 'c1', argsRaw: '{}' })).toBeUndefined()
  24. expect(commandOf({ callId: 'c1', argsRaw: '{"command":42}' })).toBeUndefined()
  25. expect(commandOf({ callId: 'c1', argsRaw: '{"command":"pnpm test"}' })).toBe('pnpm test')
  26. })
  27. })
  28. describe('ApprovalCommand', () => {
  29. it('renders the running correlated Tool command', () => {
  30. render(<ApprovalCommand {...props([
  31. { kind: 'assistant-step', data: {} },
  32. { kind: 'tool-call', data: { root: { callId: 'other', argsRaw: '{"command":"wrong"}' } } },
  33. { kind: 'tool-call', data: { root: { callId: 'call-1', argsRaw: '{"command":"pnpm test"}' } } },
  34. ] as never)} />)
  35. expect(screen.getByText('pnpm test')).toBeTruthy()
  36. })
  37. it('omits absent, uncorrelated, and settled Tool calls', () => {
  38. const { container, rerender } = render(<ApprovalCommand {...props([
  39. { kind: 'assistant-step', data: {} },
  40. { kind: 'tool-call', data: { root: undefined } },
  41. { kind: 'tool-call', data: { root: { callId: 'other', argsRaw: '{}' } } },
  42. {
  43. kind: 'tool-call',
  44. data: { root: { kind: 'tool-result', callId: 'call-1', argsRaw: '{"command":"ignored"}' } },
  45. },
  46. ] as never)} />)
  47. expect(container.textContent).toBe('')
  48. rerender(<ApprovalCommand {...props([
  49. { kind: 'tool-call', data: { root: { callId: 'call-1', argsRaw: '{}' } } },
  50. ] as never)} />)
  51. expect(container.textContent).toBe('')
  52. })
  53. })
  54. describe('ui-chat package entries', () => {
  55. it('keeps the Host half optional', () => {
  56. const ctx = new Context()
  57. expect(() => { nodeApply(ctx) }).not.toThrow()
  58. })
  59. })