mirror of https://github.com/langgenius/dify.git
240 lines
8.1 KiB
TypeScript
240 lines
8.1 KiB
TypeScript
import type { CreateExternalAPIReq, FormSchema } from '../declarations'
|
|
import { fireEvent, render, screen } from '@testing-library/react'
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import Form from './Form'
|
|
|
|
// Mock context for i18n doc link
|
|
vi.mock('@/context/i18n', () => ({
|
|
useDocLink: () => (path: string) => `https://docs.example.com${path}`,
|
|
}))
|
|
|
|
describe('Form', () => {
|
|
const defaultFormSchemas: FormSchema[] = [
|
|
{
|
|
variable: 'name',
|
|
type: 'text',
|
|
label: { en_US: 'Name', zh_CN: '名称' },
|
|
required: true,
|
|
},
|
|
{
|
|
variable: 'endpoint',
|
|
type: 'text',
|
|
label: { en_US: 'API Endpoint', zh_CN: 'API 端点' },
|
|
required: true,
|
|
},
|
|
{
|
|
variable: 'api_key',
|
|
type: 'secret',
|
|
label: { en_US: 'API Key', zh_CN: 'API 密钥' },
|
|
required: true,
|
|
},
|
|
]
|
|
|
|
const defaultValue: CreateExternalAPIReq = {
|
|
name: '',
|
|
settings: {
|
|
endpoint: '',
|
|
api_key: '',
|
|
},
|
|
}
|
|
|
|
const defaultProps = {
|
|
value: defaultValue,
|
|
onChange: vi.fn(),
|
|
formSchemas: defaultFormSchemas,
|
|
}
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
describe('Rendering', () => {
|
|
it('should render without crashing', () => {
|
|
const { container } = render(<Form {...defaultProps} />)
|
|
expect(container.querySelector('form')).toBeInTheDocument()
|
|
})
|
|
|
|
it('should render all form fields based on formSchemas', () => {
|
|
render(<Form {...defaultProps} />)
|
|
expect(screen.getByLabelText(/name/i)).toBeInTheDocument()
|
|
expect(screen.getByLabelText(/api endpoint/i)).toBeInTheDocument()
|
|
expect(screen.getByLabelText(/api key/i)).toBeInTheDocument()
|
|
})
|
|
|
|
it('should render required indicator for required fields', () => {
|
|
render(<Form {...defaultProps} />)
|
|
const labels = screen.getAllByText('*')
|
|
expect(labels.length).toBe(3) // All 3 fields are required
|
|
})
|
|
|
|
it('should render documentation link for endpoint field', () => {
|
|
render(<Form {...defaultProps} />)
|
|
const docLink = screen.getByText('dataset.externalAPIPanelDocumentation')
|
|
expect(docLink).toBeInTheDocument()
|
|
expect(docLink.closest('a')).toHaveAttribute('href', expect.stringContaining('docs.example.com'))
|
|
})
|
|
|
|
it('should render password type input for secret fields', () => {
|
|
render(<Form {...defaultProps} />)
|
|
const apiKeyInput = screen.getByLabelText(/api key/i)
|
|
expect(apiKeyInput).toHaveAttribute('type', 'password')
|
|
})
|
|
|
|
it('should render text type input for text fields', () => {
|
|
render(<Form {...defaultProps} />)
|
|
const nameInput = screen.getByLabelText(/name/i)
|
|
expect(nameInput).toHaveAttribute('type', 'text')
|
|
})
|
|
})
|
|
|
|
describe('Props', () => {
|
|
it('should apply custom className to form', () => {
|
|
const { container } = render(<Form {...defaultProps} className="custom-form-class" />)
|
|
expect(container.querySelector('form')).toHaveClass('custom-form-class')
|
|
})
|
|
|
|
it('should apply itemClassName to form items', () => {
|
|
const { container } = render(<Form {...defaultProps} itemClassName="custom-item-class" />)
|
|
const items = container.querySelectorAll('.custom-item-class')
|
|
expect(items.length).toBe(3)
|
|
})
|
|
|
|
it('should apply fieldLabelClassName to labels', () => {
|
|
const { container } = render(<Form {...defaultProps} fieldLabelClassName="custom-label-class" />)
|
|
const labels = container.querySelectorAll('label.custom-label-class')
|
|
expect(labels.length).toBe(3)
|
|
})
|
|
|
|
it('should apply inputClassName to inputs', () => {
|
|
render(<Form {...defaultProps} inputClassName="custom-input-class" />)
|
|
const inputs = screen.getAllByRole('textbox')
|
|
inputs.forEach((input) => {
|
|
expect(input).toHaveClass('custom-input-class')
|
|
})
|
|
})
|
|
|
|
it('should display initial values', () => {
|
|
const valueWithData: CreateExternalAPIReq = {
|
|
name: 'Test API',
|
|
settings: {
|
|
endpoint: 'https://api.example.com',
|
|
api_key: 'secret-key',
|
|
},
|
|
}
|
|
render(<Form {...defaultProps} value={valueWithData} />)
|
|
expect(screen.getByLabelText(/name/i)).toHaveValue('Test API')
|
|
expect(screen.getByLabelText(/api endpoint/i)).toHaveValue('https://api.example.com')
|
|
expect(screen.getByLabelText(/api key/i)).toHaveValue('secret-key')
|
|
})
|
|
})
|
|
|
|
describe('User Interactions', () => {
|
|
it('should call onChange when name field changes', () => {
|
|
const onChange = vi.fn()
|
|
render(<Form {...defaultProps} onChange={onChange} />)
|
|
|
|
const nameInput = screen.getByLabelText(/name/i)
|
|
fireEvent.change(nameInput, { target: { value: 'New API Name' } })
|
|
|
|
expect(onChange).toHaveBeenCalledWith({
|
|
name: 'New API Name',
|
|
settings: { endpoint: '', api_key: '' },
|
|
})
|
|
})
|
|
|
|
it('should call onChange when endpoint field changes', () => {
|
|
const onChange = vi.fn()
|
|
render(<Form {...defaultProps} onChange={onChange} />)
|
|
|
|
const endpointInput = screen.getByLabelText(/api endpoint/i)
|
|
fireEvent.change(endpointInput, { target: { value: 'https://new-api.example.com' } })
|
|
|
|
expect(onChange).toHaveBeenCalledWith({
|
|
name: '',
|
|
settings: { endpoint: 'https://new-api.example.com', api_key: '' },
|
|
})
|
|
})
|
|
|
|
it('should call onChange when api_key field changes', () => {
|
|
const onChange = vi.fn()
|
|
render(<Form {...defaultProps} onChange={onChange} />)
|
|
|
|
const apiKeyInput = screen.getByLabelText(/api key/i)
|
|
fireEvent.change(apiKeyInput, { target: { value: 'new-secret-key' } })
|
|
|
|
expect(onChange).toHaveBeenCalledWith({
|
|
name: '',
|
|
settings: { endpoint: '', api_key: 'new-secret-key' },
|
|
})
|
|
})
|
|
|
|
it('should update settings without affecting name', () => {
|
|
const onChange = vi.fn()
|
|
const initialValue: CreateExternalAPIReq = {
|
|
name: 'Existing Name',
|
|
settings: { endpoint: '', api_key: '' },
|
|
}
|
|
render(<Form {...defaultProps} value={initialValue} onChange={onChange} />)
|
|
|
|
const endpointInput = screen.getByLabelText(/api endpoint/i)
|
|
fireEvent.change(endpointInput, { target: { value: 'https://api.example.com' } })
|
|
|
|
expect(onChange).toHaveBeenCalledWith({
|
|
name: 'Existing Name',
|
|
settings: { endpoint: 'https://api.example.com', api_key: '' },
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('Edge Cases', () => {
|
|
it('should handle empty formSchemas', () => {
|
|
const { container } = render(<Form {...defaultProps} formSchemas={[]} />)
|
|
expect(container.querySelector('form')).toBeInTheDocument()
|
|
expect(screen.queryByRole('textbox')).not.toBeInTheDocument()
|
|
})
|
|
|
|
it('should handle optional field (required: false)', () => {
|
|
const schemasWithOptional: FormSchema[] = [
|
|
{
|
|
variable: 'description',
|
|
type: 'text',
|
|
label: { en_US: 'Description' },
|
|
required: false,
|
|
},
|
|
]
|
|
render(<Form {...defaultProps} formSchemas={schemasWithOptional} />)
|
|
expect(screen.queryByText('*')).not.toBeInTheDocument()
|
|
})
|
|
|
|
it('should fallback to en_US label when current language label is not available', () => {
|
|
const schemasWithEnOnly: FormSchema[] = [
|
|
{
|
|
variable: 'test',
|
|
type: 'text',
|
|
label: { en_US: 'Test Field' },
|
|
required: false,
|
|
},
|
|
]
|
|
render(<Form {...defaultProps} formSchemas={schemasWithEnOnly} />)
|
|
expect(screen.getByLabelText(/test field/i)).toBeInTheDocument()
|
|
})
|
|
|
|
it('should preserve existing settings when updating one field', () => {
|
|
const onChange = vi.fn()
|
|
const initialValue: CreateExternalAPIReq = {
|
|
name: '',
|
|
settings: { endpoint: 'https://existing.com', api_key: 'existing-key' },
|
|
}
|
|
render(<Form {...defaultProps} value={initialValue} onChange={onChange} />)
|
|
|
|
const endpointInput = screen.getByLabelText(/api endpoint/i)
|
|
fireEvent.change(endpointInput, { target: { value: 'https://new.com' } })
|
|
|
|
expect(onChange).toHaveBeenCalledWith({
|
|
name: '',
|
|
settings: { endpoint: 'https://new.com', api_key: 'existing-key' },
|
|
})
|
|
})
|
|
})
|
|
})
|