refactor(tool-selector): remove unused components and consolidate import (#31018)

Co-authored-by: CodingOnStar <hanxujiang@dify.ai>
This commit is contained in:
Coding On Star
2026-01-26 14:24:00 +08:00
committed by GitHub
parent dd988d42c2
commit d941201a3e
32 changed files with 13659 additions and 1211 deletions

View File

@ -0,0 +1,259 @@
import { render, screen } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { Theme } from '@/types/app'
import IconWithTooltip from './icon-with-tooltip'
// Mock Tooltip component
vi.mock('@/app/components/base/tooltip', () => ({
default: ({
children,
popupContent,
popupClassName,
}: {
children: React.ReactNode
popupContent?: string
popupClassName?: string
}) => (
<div data-testid="tooltip" data-popup-content={popupContent} data-popup-classname={popupClassName}>
{children}
</div>
),
}))
// Mock icon components
const MockLightIcon = ({ className }: { className?: string }) => (
<div data-testid="light-icon" className={className}>Light Icon</div>
)
const MockDarkIcon = ({ className }: { className?: string }) => (
<div data-testid="dark-icon" className={className}>Dark Icon</div>
)
describe('IconWithTooltip', () => {
beforeEach(() => {
vi.clearAllMocks()
})
describe('Rendering', () => {
it('should render without crashing', () => {
render(
<IconWithTooltip
theme={Theme.light}
BadgeIconLight={MockLightIcon}
BadgeIconDark={MockDarkIcon}
/>,
)
expect(screen.getByTestId('tooltip')).toBeInTheDocument()
})
it('should render Tooltip wrapper', () => {
render(
<IconWithTooltip
theme={Theme.light}
BadgeIconLight={MockLightIcon}
BadgeIconDark={MockDarkIcon}
popupContent="Test tooltip"
/>,
)
expect(screen.getByTestId('tooltip')).toHaveAttribute('data-popup-content', 'Test tooltip')
})
it('should apply correct popupClassName to Tooltip', () => {
render(
<IconWithTooltip
theme={Theme.light}
BadgeIconLight={MockLightIcon}
BadgeIconDark={MockDarkIcon}
/>,
)
const tooltip = screen.getByTestId('tooltip')
expect(tooltip).toHaveAttribute('data-popup-classname')
expect(tooltip.getAttribute('data-popup-classname')).toContain('border-components-panel-border')
})
})
describe('Theme Handling', () => {
it('should render light icon when theme is light', () => {
render(
<IconWithTooltip
theme={Theme.light}
BadgeIconLight={MockLightIcon}
BadgeIconDark={MockDarkIcon}
/>,
)
expect(screen.getByTestId('light-icon')).toBeInTheDocument()
expect(screen.queryByTestId('dark-icon')).not.toBeInTheDocument()
})
it('should render dark icon when theme is dark', () => {
render(
<IconWithTooltip
theme={Theme.dark}
BadgeIconLight={MockLightIcon}
BadgeIconDark={MockDarkIcon}
/>,
)
expect(screen.getByTestId('dark-icon')).toBeInTheDocument()
expect(screen.queryByTestId('light-icon')).not.toBeInTheDocument()
})
it('should render light icon when theme is system (not dark)', () => {
render(
<IconWithTooltip
theme={'system' as Theme}
BadgeIconLight={MockLightIcon}
BadgeIconDark={MockDarkIcon}
/>,
)
// When theme is not 'dark', it should use light icon
expect(screen.getByTestId('light-icon')).toBeInTheDocument()
})
})
describe('Props', () => {
it('should apply custom className to icon', () => {
render(
<IconWithTooltip
className="custom-class"
theme={Theme.light}
BadgeIconLight={MockLightIcon}
BadgeIconDark={MockDarkIcon}
/>,
)
const icon = screen.getByTestId('light-icon')
expect(icon).toHaveClass('custom-class')
})
it('should apply default h-5 w-5 class to icon', () => {
render(
<IconWithTooltip
theme={Theme.light}
BadgeIconLight={MockLightIcon}
BadgeIconDark={MockDarkIcon}
/>,
)
const icon = screen.getByTestId('light-icon')
expect(icon).toHaveClass('h-5')
expect(icon).toHaveClass('w-5')
})
it('should merge custom className with default classes', () => {
render(
<IconWithTooltip
className="ml-2"
theme={Theme.light}
BadgeIconLight={MockLightIcon}
BadgeIconDark={MockDarkIcon}
/>,
)
const icon = screen.getByTestId('light-icon')
expect(icon).toHaveClass('h-5')
expect(icon).toHaveClass('w-5')
expect(icon).toHaveClass('ml-2')
})
it('should pass popupContent to Tooltip', () => {
render(
<IconWithTooltip
theme={Theme.light}
BadgeIconLight={MockLightIcon}
BadgeIconDark={MockDarkIcon}
popupContent="Custom tooltip content"
/>,
)
expect(screen.getByTestId('tooltip')).toHaveAttribute(
'data-popup-content',
'Custom tooltip content',
)
})
it('should handle undefined popupContent', () => {
render(
<IconWithTooltip
theme={Theme.light}
BadgeIconLight={MockLightIcon}
BadgeIconDark={MockDarkIcon}
/>,
)
expect(screen.getByTestId('tooltip')).toBeInTheDocument()
})
})
describe('Memoization', () => {
it('should be wrapped with React.memo', () => {
// The component is exported as React.memo(IconWithTooltip)
expect(IconWithTooltip).toBeDefined()
// Check if it's a memo component
expect(typeof IconWithTooltip).toBe('object')
})
})
describe('Container Structure', () => {
it('should render icon inside flex container', () => {
const { container } = render(
<IconWithTooltip
theme={Theme.light}
BadgeIconLight={MockLightIcon}
BadgeIconDark={MockDarkIcon}
/>,
)
const flexContainer = container.querySelector('.flex.shrink-0.items-center.justify-center')
expect(flexContainer).toBeInTheDocument()
})
})
describe('Edge Cases', () => {
it('should handle empty className', () => {
render(
<IconWithTooltip
className=""
theme={Theme.light}
BadgeIconLight={MockLightIcon}
BadgeIconDark={MockDarkIcon}
/>,
)
expect(screen.getByTestId('light-icon')).toBeInTheDocument()
})
it('should handle long popupContent', () => {
const longContent = 'A'.repeat(500)
render(
<IconWithTooltip
theme={Theme.light}
BadgeIconLight={MockLightIcon}
BadgeIconDark={MockDarkIcon}
popupContent={longContent}
/>,
)
expect(screen.getByTestId('tooltip')).toHaveAttribute('data-popup-content', longContent)
})
it('should handle special characters in popupContent', () => {
const specialContent = '<script>alert("xss")</script> & "quotes"'
render(
<IconWithTooltip
theme={Theme.light}
BadgeIconLight={MockLightIcon}
BadgeIconDark={MockDarkIcon}
popupContent={specialContent}
/>,
)
expect(screen.getByTestId('tooltip')).toHaveAttribute('data-popup-content', specialContent)
})
})
})

View File

@ -0,0 +1,205 @@
import type { ComponentProps } from 'react'
import { render, screen } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { Theme } from '@/types/app'
import Partner from './partner'
// Mock useTheme hook
const mockUseTheme = vi.fn()
vi.mock('@/hooks/use-theme', () => ({
default: () => mockUseTheme(),
}))
// Mock IconWithTooltip to directly test Partner's behavior
type IconWithTooltipProps = ComponentProps<typeof import('./icon-with-tooltip').default>
const mockIconWithTooltip = vi.fn()
vi.mock('./icon-with-tooltip', () => ({
default: (props: IconWithTooltipProps) => {
mockIconWithTooltip(props)
const { theme, BadgeIconLight, BadgeIconDark, className, popupContent } = props
const isDark = theme === Theme.dark
const Icon = isDark ? BadgeIconDark : BadgeIconLight
return (
<div data-testid="icon-with-tooltip" data-popup-content={popupContent} data-theme={theme}>
<Icon className={className} data-testid={isDark ? 'partner-dark-icon' : 'partner-light-icon'} />
</div>
)
},
}))
// Mock Partner icons
vi.mock('@/app/components/base/icons/src/public/plugins/PartnerDark', () => ({
default: ({ className, ...rest }: { className?: string }) => (
<div data-testid="partner-dark-icon" className={className} {...rest}>PartnerDark</div>
),
}))
vi.mock('@/app/components/base/icons/src/public/plugins/PartnerLight', () => ({
default: ({ className, ...rest }: { className?: string }) => (
<div data-testid="partner-light-icon" className={className} {...rest}>PartnerLight</div>
),
}))
describe('Partner', () => {
beforeEach(() => {
vi.clearAllMocks()
mockUseTheme.mockReturnValue({ theme: Theme.light })
mockIconWithTooltip.mockClear()
})
describe('Rendering', () => {
it('should render without crashing', () => {
render(<Partner text="Partner Tip" />)
expect(screen.getByTestId('icon-with-tooltip')).toBeInTheDocument()
})
it('should call useTheme hook', () => {
render(<Partner text="Partner" />)
expect(mockUseTheme).toHaveBeenCalled()
})
it('should pass text prop as popupContent to IconWithTooltip', () => {
render(<Partner text="This is a partner" />)
expect(screen.getByTestId('icon-with-tooltip')).toHaveAttribute(
'data-popup-content',
'This is a partner',
)
expect(mockIconWithTooltip).toHaveBeenCalledWith(
expect.objectContaining({ popupContent: 'This is a partner' }),
)
})
it('should pass theme from useTheme to IconWithTooltip', () => {
mockUseTheme.mockReturnValue({ theme: Theme.light })
render(<Partner text="Partner" />)
expect(mockIconWithTooltip).toHaveBeenCalledWith(
expect.objectContaining({ theme: Theme.light }),
)
})
it('should render light icon in light theme', () => {
mockUseTheme.mockReturnValue({ theme: Theme.light })
render(<Partner text="Partner" />)
expect(screen.getByTestId('partner-light-icon')).toBeInTheDocument()
})
it('should render dark icon in dark theme', () => {
mockUseTheme.mockReturnValue({ theme: Theme.dark })
render(<Partner text="Partner" />)
expect(screen.getByTestId('partner-dark-icon')).toBeInTheDocument()
})
})
describe('Props', () => {
it('should pass className to IconWithTooltip', () => {
render(<Partner className="custom-class" text="Partner" />)
expect(mockIconWithTooltip).toHaveBeenCalledWith(
expect.objectContaining({ className: 'custom-class' }),
)
})
it('should pass correct BadgeIcon components to IconWithTooltip', () => {
render(<Partner text="Partner" />)
expect(mockIconWithTooltip).toHaveBeenCalledWith(
expect.objectContaining({
BadgeIconLight: expect.any(Function),
BadgeIconDark: expect.any(Function),
}),
)
})
})
describe('Theme Handling', () => {
it('should handle light theme correctly', () => {
mockUseTheme.mockReturnValue({ theme: Theme.light })
render(<Partner text="Partner" />)
expect(mockUseTheme).toHaveBeenCalled()
expect(mockIconWithTooltip).toHaveBeenCalledWith(
expect.objectContaining({ theme: Theme.light }),
)
expect(screen.getByTestId('partner-light-icon')).toBeInTheDocument()
})
it('should handle dark theme correctly', () => {
mockUseTheme.mockReturnValue({ theme: Theme.dark })
render(<Partner text="Partner" />)
expect(mockUseTheme).toHaveBeenCalled()
expect(mockIconWithTooltip).toHaveBeenCalledWith(
expect.objectContaining({ theme: Theme.dark }),
)
expect(screen.getByTestId('partner-dark-icon')).toBeInTheDocument()
})
it('should pass updated theme when theme changes', () => {
mockUseTheme.mockReturnValue({ theme: Theme.light })
const { rerender } = render(<Partner text="Partner" />)
expect(mockIconWithTooltip).toHaveBeenLastCalledWith(
expect.objectContaining({ theme: Theme.light }),
)
mockIconWithTooltip.mockClear()
mockUseTheme.mockReturnValue({ theme: Theme.dark })
rerender(<Partner text="Partner" />)
expect(mockIconWithTooltip).toHaveBeenLastCalledWith(
expect.objectContaining({ theme: Theme.dark }),
)
})
})
describe('Edge Cases', () => {
it('should handle empty text', () => {
render(<Partner text="" />)
expect(mockIconWithTooltip).toHaveBeenCalledWith(
expect.objectContaining({ popupContent: '' }),
)
})
it('should handle long text', () => {
const longText = 'A'.repeat(500)
render(<Partner text={longText} />)
expect(mockIconWithTooltip).toHaveBeenCalledWith(
expect.objectContaining({ popupContent: longText }),
)
})
it('should handle special characters in text', () => {
const specialText = '<script>alert("xss")</script>'
render(<Partner text={specialText} />)
expect(mockIconWithTooltip).toHaveBeenCalledWith(
expect.objectContaining({ popupContent: specialText }),
)
})
it('should handle undefined className', () => {
render(<Partner text="Partner" />)
expect(mockIconWithTooltip).toHaveBeenCalledWith(
expect.objectContaining({ className: undefined }),
)
})
it('should always call useTheme to get current theme', () => {
render(<Partner text="Partner 1" />)
expect(mockUseTheme).toHaveBeenCalledTimes(1)
mockUseTheme.mockClear()
render(<Partner text="Partner 2" />)
expect(mockUseTheme).toHaveBeenCalledTimes(1)
})
})
})