mirror of
https://github.com/langgenius/dify.git
synced 2026-04-18 04:16:28 +08:00
147 lines
5.0 KiB
TypeScript
147 lines
5.0 KiB
TypeScript
import { fireEvent, render, screen } from '@testing-library/react'
|
|
import * as React from 'react'
|
|
import { describe, expect, it, vi } from 'vitest'
|
|
import { BlockEnum } from '@/app/components/workflow/types'
|
|
import { useLocale } from '@/context/i18n'
|
|
import ToolCallItem from '../tool-call'
|
|
|
|
vi.mock('@/app/components/workflow/nodes/_base/components/editor/code-editor', () => ({
|
|
default: ({ title, value }: { title: React.ReactNode, value: string | object }) => (
|
|
<div data-testid="code-editor">
|
|
<div data-testid="code-editor-title">{title}</div>
|
|
<div data-testid="code-editor-value">{JSON.stringify(value)}</div>
|
|
</div>
|
|
),
|
|
}))
|
|
|
|
vi.mock('@/app/components/workflow/block-icon', () => ({
|
|
default: ({ type }: { type: BlockEnum }) => <div data-testid="block-icon" data-type={type} />,
|
|
}))
|
|
|
|
vi.mock('@/context/i18n', () => ({
|
|
useLocale: vi.fn(() => 'en'),
|
|
}))
|
|
|
|
const mockToolCall = {
|
|
status: 'success',
|
|
error: null,
|
|
tool_name: 'test_tool',
|
|
tool_label: { en: 'Test Tool Label' },
|
|
tool_icon: 'icon',
|
|
time_cost: 1.5,
|
|
tool_input: { query: 'hello' },
|
|
tool_output: { result: 'world' },
|
|
}
|
|
|
|
describe('ToolCallItem', () => {
|
|
it('should render tool name correctly for LLM', () => {
|
|
render(<ToolCallItem toolCall={mockToolCall} isLLM={true} />)
|
|
expect(screen.getByText('LLM')).toBeInTheDocument()
|
|
expect(screen.getByTestId('block-icon')).toHaveAttribute('data-type', BlockEnum.LLM)
|
|
})
|
|
|
|
it('should render tool name from label for non-LLM', () => {
|
|
render(<ToolCallItem toolCall={mockToolCall} isLLM={false} />)
|
|
expect(screen.getByText('Test Tool Label')).toBeInTheDocument()
|
|
expect(screen.getByTestId('block-icon')).toHaveAttribute('data-type', BlockEnum.Tool)
|
|
})
|
|
|
|
it('should fallback to locale key with underscores when hyphenated key is missing', () => {
|
|
vi.mocked(useLocale).mockReturnValueOnce('en-US')
|
|
const fallbackLocaleToolCall = {
|
|
...mockToolCall,
|
|
tool_label: { en_US: 'Fallback Label' },
|
|
}
|
|
|
|
render(<ToolCallItem toolCall={fallbackLocaleToolCall} isLLM={false} />)
|
|
expect(screen.getByText('Fallback Label')).toBeInTheDocument()
|
|
})
|
|
|
|
it('should format time correctly', () => {
|
|
render(<ToolCallItem toolCall={mockToolCall} isLLM={false} />)
|
|
expect(screen.getByText('1.500 s')).toBeInTheDocument()
|
|
|
|
// Test ms format
|
|
render(<ToolCallItem toolCall={{ ...mockToolCall, time_cost: 0.5 }} isLLM={false} />)
|
|
expect(screen.getByText('500.000 ms')).toBeInTheDocument()
|
|
|
|
// Test minute format
|
|
render(<ToolCallItem toolCall={{ ...mockToolCall, time_cost: 65 }} isLLM={false} />)
|
|
expect(screen.getByText('1 m 5.000 s')).toBeInTheDocument()
|
|
})
|
|
|
|
it('should format token count in K units', () => {
|
|
render(<ToolCallItem toolCall={mockToolCall} isLLM={true} tokens={1200} />)
|
|
expect(screen.getByText('1.2K tokens')).toBeInTheDocument()
|
|
})
|
|
|
|
it('should format token count without unit for small values', () => {
|
|
render(<ToolCallItem toolCall={mockToolCall} isLLM={true} tokens={800} />)
|
|
expect(screen.getByText('800 tokens')).toBeInTheDocument()
|
|
})
|
|
|
|
it('should format token count in M units', () => {
|
|
render(<ToolCallItem toolCall={mockToolCall} isLLM={true} tokens={1200000} />)
|
|
expect(screen.getByText('1.2M tokens')).toBeInTheDocument()
|
|
})
|
|
|
|
it('should handle collapse/expand', () => {
|
|
render(<ToolCallItem toolCall={mockToolCall} isLLM={false} />)
|
|
|
|
expect(screen.queryByTestId('code-editor')).not.toBeInTheDocument()
|
|
|
|
fireEvent.click(screen.getByText(/Test Tool Label/i))
|
|
expect(screen.getAllByTestId('code-editor')).toHaveLength(2)
|
|
})
|
|
|
|
it('should display error message when status is error', () => {
|
|
const errorToolCall = {
|
|
...mockToolCall,
|
|
status: 'error',
|
|
error: 'Something went wrong',
|
|
}
|
|
render(<ToolCallItem toolCall={errorToolCall} isLLM={false} />)
|
|
|
|
fireEvent.click(screen.getByText(/Test Tool Label/i))
|
|
expect(screen.getByText('Something went wrong')).toBeInTheDocument()
|
|
})
|
|
|
|
it('should display LLM specific fields when expanded', () => {
|
|
render(
|
|
<ToolCallItem
|
|
toolCall={mockToolCall}
|
|
isLLM={true}
|
|
observation="test observation"
|
|
finalAnswer="test final answer"
|
|
isFinal={true}
|
|
/>,
|
|
)
|
|
|
|
fireEvent.click(screen.getByText('LLM'))
|
|
|
|
const titles = screen.getAllByTestId('code-editor-title')
|
|
const titleTexts = titles.map(t => t.textContent)
|
|
|
|
expect(titleTexts).toContain('INPUT')
|
|
expect(titleTexts).toContain('OUTPUT')
|
|
expect(titleTexts).toContain('OBSERVATION')
|
|
expect(titleTexts).toContain('FINAL ANSWER')
|
|
})
|
|
|
|
it('should display THOUGHT instead of FINAL ANSWER when isFinal is false', () => {
|
|
render(
|
|
<ToolCallItem
|
|
toolCall={mockToolCall}
|
|
isLLM={true}
|
|
observation="test observation"
|
|
finalAnswer="test thought"
|
|
isFinal={false}
|
|
/>,
|
|
)
|
|
|
|
fireEvent.click(screen.getByText('LLM'))
|
|
expect(screen.getByText('THOUGHT')).toBeInTheDocument()
|
|
expect(screen.queryByText('FINAL ANSWER')).not.toBeInTheDocument()
|
|
})
|
|
})
|