mirror of https://github.com/langgenius/dify.git
318 lines
11 KiB
TypeScript
318 lines
11 KiB
TypeScript
import { fireEvent, render, screen } from '@testing-library/react'
|
|
import { EffectColor } from './chunk-structure/types'
|
|
import OptionCard from './option-card'
|
|
|
|
// Note: react-i18next is globally mocked in vitest.setup.ts
|
|
|
|
describe('OptionCard', () => {
|
|
const defaultProps = {
|
|
id: 'test-id',
|
|
title: 'Test Title',
|
|
}
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
describe('Rendering', () => {
|
|
it('should render without crashing', () => {
|
|
render(<OptionCard {...defaultProps} />)
|
|
expect(screen.getByText('Test Title')).toBeInTheDocument()
|
|
})
|
|
|
|
it('should render title', () => {
|
|
render(<OptionCard {...defaultProps} title="Custom Title" />)
|
|
expect(screen.getByText('Custom Title')).toBeInTheDocument()
|
|
})
|
|
|
|
it('should render description when provided', () => {
|
|
render(<OptionCard {...defaultProps} description="Test Description" />)
|
|
expect(screen.getByText('Test Description')).toBeInTheDocument()
|
|
})
|
|
|
|
it('should not render description when not provided', () => {
|
|
render(<OptionCard {...defaultProps} />)
|
|
expect(screen.queryByText(/description/i)).not.toBeInTheDocument()
|
|
})
|
|
|
|
it('should render icon when provided', () => {
|
|
render(<OptionCard {...defaultProps} icon={<span data-testid="test-icon">Icon</span>} />)
|
|
expect(screen.getByTestId('test-icon')).toBeInTheDocument()
|
|
})
|
|
|
|
it('should not render icon container when icon is not provided', () => {
|
|
const { container } = render(<OptionCard {...defaultProps} />)
|
|
const iconContainers = container.querySelectorAll('.size-6')
|
|
expect(iconContainers).toHaveLength(0)
|
|
})
|
|
})
|
|
|
|
describe('Active State', () => {
|
|
it('should apply active styles when isActive is true', () => {
|
|
const { container } = render(<OptionCard {...defaultProps} isActive={true} />)
|
|
const card = container.firstChild
|
|
expect(card).toHaveClass('ring-[1px]')
|
|
})
|
|
|
|
it('should not apply active styles when isActive is false', () => {
|
|
const { container } = render(<OptionCard {...defaultProps} isActive={false} />)
|
|
const card = container.firstChild
|
|
expect(card).not.toHaveClass('ring-[1px]')
|
|
})
|
|
|
|
it('should apply iconActiveColor when isActive is true and icon is present', () => {
|
|
const { container } = render(
|
|
<OptionCard
|
|
{...defaultProps}
|
|
isActive={true}
|
|
icon={<span>Icon</span>}
|
|
iconActiveColor="text-red-500"
|
|
/>,
|
|
)
|
|
const iconContainer = container.querySelector('.text-red-500')
|
|
expect(iconContainer).toBeInTheDocument()
|
|
})
|
|
})
|
|
|
|
describe('Disabled State', () => {
|
|
it('should apply disabled styles when disabled is true', () => {
|
|
const { container } = render(<OptionCard {...defaultProps} disabled={true} />)
|
|
const card = container.firstChild
|
|
expect(card).toHaveClass('cursor-not-allowed')
|
|
expect(card).toHaveClass('opacity-50')
|
|
})
|
|
|
|
it('should not call onClick when disabled', () => {
|
|
const handleClick = vi.fn()
|
|
render(<OptionCard {...defaultProps} disabled={true} onClick={handleClick} />)
|
|
|
|
const card = screen.getByText('Test Title').closest('div')?.parentElement?.parentElement
|
|
fireEvent.click(card!)
|
|
|
|
expect(handleClick).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('should not call onClick when isActive', () => {
|
|
const handleClick = vi.fn()
|
|
render(<OptionCard {...defaultProps} isActive={true} onClick={handleClick} />)
|
|
|
|
const card = screen.getByText('Test Title').closest('div')?.parentElement?.parentElement
|
|
fireEvent.click(card!)
|
|
|
|
expect(handleClick).not.toHaveBeenCalled()
|
|
})
|
|
})
|
|
|
|
describe('Recommended Badge', () => {
|
|
it('should render recommended badge when isRecommended is true', () => {
|
|
render(<OptionCard {...defaultProps} isRecommended={true} />)
|
|
// Badge uses translation key
|
|
expect(screen.getByText(/stepTwo\.recommend/)).toBeInTheDocument()
|
|
})
|
|
|
|
it('should not render recommended badge when isRecommended is false', () => {
|
|
render(<OptionCard {...defaultProps} isRecommended={false} />)
|
|
expect(screen.queryByText(/stepTwo\.recommend/)).not.toBeInTheDocument()
|
|
})
|
|
})
|
|
|
|
describe('Effect Color', () => {
|
|
it('should render effect color when effectColor and showEffectColor are provided', () => {
|
|
const { container } = render(
|
|
<OptionCard {...defaultProps} effectColor={EffectColor.indigo} showEffectColor={true} />,
|
|
)
|
|
const effectElement = container.querySelector('.blur-\\[80px\\]')
|
|
expect(effectElement).toBeInTheDocument()
|
|
})
|
|
|
|
it('should not render effect color when showEffectColor is false', () => {
|
|
const { container } = render(
|
|
<OptionCard {...defaultProps} effectColor={EffectColor.indigo} showEffectColor={false} />,
|
|
)
|
|
const effectElement = container.querySelector('.blur-\\[80px\\]')
|
|
expect(effectElement).not.toBeInTheDocument()
|
|
})
|
|
|
|
it('should not render effect color when effectColor is not provided', () => {
|
|
const { container } = render(
|
|
<OptionCard {...defaultProps} showEffectColor={true} />,
|
|
)
|
|
const effectElement = container.querySelector('.blur-\\[80px\\]')
|
|
expect(effectElement).not.toBeInTheDocument()
|
|
})
|
|
|
|
it('should apply indigo effect color class', () => {
|
|
const { container } = render(
|
|
<OptionCard {...defaultProps} effectColor={EffectColor.indigo} showEffectColor={true} />,
|
|
)
|
|
const effectElement = container.querySelector('.bg-util-colors-indigo-indigo-600')
|
|
expect(effectElement).toBeInTheDocument()
|
|
})
|
|
|
|
it('should apply blueLight effect color class', () => {
|
|
const { container } = render(
|
|
<OptionCard {...defaultProps} effectColor={EffectColor.blueLight} showEffectColor={true} />,
|
|
)
|
|
const effectElement = container.querySelector('.bg-util-colors-blue-light-blue-light-600')
|
|
expect(effectElement).toBeInTheDocument()
|
|
})
|
|
|
|
it('should apply orange effect color class', () => {
|
|
const { container } = render(
|
|
<OptionCard {...defaultProps} effectColor={EffectColor.orange} showEffectColor={true} />,
|
|
)
|
|
const effectElement = container.querySelector('.bg-util-colors-orange-orange-500')
|
|
expect(effectElement).toBeInTheDocument()
|
|
})
|
|
|
|
it('should apply purple effect color class', () => {
|
|
const { container } = render(
|
|
<OptionCard {...defaultProps} effectColor={EffectColor.purple} showEffectColor={true} />,
|
|
)
|
|
const effectElement = container.querySelector('.bg-util-colors-purple-purple-600')
|
|
expect(effectElement).toBeInTheDocument()
|
|
})
|
|
})
|
|
|
|
describe('Children', () => {
|
|
it('should render children when children and showChildren are provided', () => {
|
|
render(
|
|
<OptionCard {...defaultProps} showChildren={true}>
|
|
<div data-testid="child-content">Child Content</div>
|
|
</OptionCard>,
|
|
)
|
|
expect(screen.getByTestId('child-content')).toBeInTheDocument()
|
|
})
|
|
|
|
it('should not render children when showChildren is false', () => {
|
|
render(
|
|
<OptionCard {...defaultProps} showChildren={false}>
|
|
<div data-testid="child-content">Child Content</div>
|
|
</OptionCard>,
|
|
)
|
|
expect(screen.queryByTestId('child-content')).not.toBeInTheDocument()
|
|
})
|
|
|
|
it('should not render children container when children is not provided', () => {
|
|
const { container } = render(
|
|
<OptionCard {...defaultProps} showChildren={true} />,
|
|
)
|
|
const childContainer = container.querySelector('.bg-components-panel-bg')
|
|
expect(childContainer).not.toBeInTheDocument()
|
|
})
|
|
|
|
it('should render arrow shape when children are shown', () => {
|
|
const { container } = render(
|
|
<OptionCard {...defaultProps} showChildren={true}>
|
|
<div>Child</div>
|
|
</OptionCard>,
|
|
)
|
|
// ArrowShape renders an SVG
|
|
const childSection = container.querySelector('.bg-components-panel-bg')
|
|
expect(childSection).toBeInTheDocument()
|
|
})
|
|
})
|
|
|
|
describe('User Interactions', () => {
|
|
it('should call onClick with id when clicked', () => {
|
|
const handleClick = vi.fn()
|
|
render(<OptionCard {...defaultProps} id="my-id" onClick={handleClick} />)
|
|
|
|
const card = screen.getByText('Test Title').closest('div')?.parentElement?.parentElement
|
|
fireEvent.click(card!)
|
|
|
|
expect(handleClick).toHaveBeenCalledWith('my-id')
|
|
})
|
|
|
|
it('should have cursor-pointer class', () => {
|
|
const { container } = render(<OptionCard {...defaultProps} />)
|
|
const card = container.firstChild
|
|
expect(card).toHaveClass('cursor-pointer')
|
|
})
|
|
})
|
|
|
|
describe('Props', () => {
|
|
it('should apply custom className', () => {
|
|
const { container } = render(<OptionCard {...defaultProps} className="custom-class" />)
|
|
const innerContainer = container.querySelector('.custom-class')
|
|
expect(innerContainer).toBeInTheDocument()
|
|
})
|
|
|
|
it('should forward ref', () => {
|
|
const ref = vi.fn()
|
|
render(<OptionCard {...defaultProps} ref={ref} />)
|
|
expect(ref).toHaveBeenCalled()
|
|
})
|
|
})
|
|
|
|
describe('Edge Cases', () => {
|
|
it('should handle empty title', () => {
|
|
render(<OptionCard {...defaultProps} title="" />)
|
|
// Component should still render
|
|
const { container } = render(<OptionCard {...defaultProps} title="" />)
|
|
expect(container.firstChild).toBeInTheDocument()
|
|
})
|
|
|
|
it('should handle complex id types', () => {
|
|
const handleClick = vi.fn()
|
|
const complexId = { key: 'value' }
|
|
render(<OptionCard {...defaultProps} id={complexId} onClick={handleClick} />)
|
|
|
|
const card = screen.getByText('Test Title').closest('div')?.parentElement?.parentElement
|
|
fireEvent.click(card!)
|
|
|
|
expect(handleClick).toHaveBeenCalledWith(complexId)
|
|
})
|
|
|
|
it('should handle numeric id', () => {
|
|
const handleClick = vi.fn()
|
|
render(<OptionCard {...defaultProps} id={123} onClick={handleClick} />)
|
|
|
|
const card = screen.getByText('Test Title').closest('div')?.parentElement?.parentElement
|
|
fireEvent.click(card!)
|
|
|
|
expect(handleClick).toHaveBeenCalledWith(123)
|
|
})
|
|
|
|
it('should handle long title', () => {
|
|
const longTitle = 'A'.repeat(200)
|
|
render(<OptionCard {...defaultProps} title={longTitle} />)
|
|
expect(screen.getByText(longTitle)).toBeInTheDocument()
|
|
})
|
|
|
|
it('should handle long description', () => {
|
|
const longDesc = 'B'.repeat(500)
|
|
render(<OptionCard {...defaultProps} description={longDesc} />)
|
|
expect(screen.getByText(longDesc)).toBeInTheDocument()
|
|
})
|
|
|
|
it('should handle all props together', () => {
|
|
const handleClick = vi.fn()
|
|
render(
|
|
<OptionCard
|
|
id="full-test"
|
|
title="Full Test"
|
|
description="Full Description"
|
|
icon={<span data-testid="full-icon">Icon</span>}
|
|
iconActiveColor="text-blue-500"
|
|
isActive={true}
|
|
isRecommended={true}
|
|
effectColor={EffectColor.indigo}
|
|
showEffectColor={true}
|
|
disabled={false}
|
|
onClick={handleClick}
|
|
className="full-class"
|
|
showChildren={true}
|
|
>
|
|
<div data-testid="full-children">Children</div>
|
|
</OptionCard>,
|
|
)
|
|
|
|
expect(screen.getByText('Full Test')).toBeInTheDocument()
|
|
expect(screen.getByText('Full Description')).toBeInTheDocument()
|
|
expect(screen.getByTestId('full-icon')).toBeInTheDocument()
|
|
expect(screen.getByTestId('full-children')).toBeInTheDocument()
|
|
})
|
|
})
|
|
})
|