mirror of
https://github.com/langgenius/dify.git
synced 2026-04-21 03:07:39 +08:00
Migrate all Remixicon component imports in workflow/skill to Tailwind CSS icon utility classes (i-ri-*), reducing JS bundle size. Update MenuItem to accept string icon classes alongside React components. Adjust test selectors that relied on SVG element queries.
134 lines
3.7 KiB
TypeScript
134 lines
3.7 KiB
TypeScript
import type { MenuItemProps } from './menu-item'
|
|
import { fireEvent, render, screen } from '@testing-library/react'
|
|
import MenuItem from './menu-item'
|
|
|
|
const MockIcon = (props: React.SVGProps<SVGSVGElement>) => <svg {...props} />
|
|
|
|
const defaultProps: MenuItemProps = {
|
|
icon: MockIcon,
|
|
label: 'Rename',
|
|
onClick: vi.fn(),
|
|
}
|
|
|
|
const renderMenuItem = (overrides: Partial<MenuItemProps> = {}) => {
|
|
const props = { ...defaultProps, ...overrides }
|
|
return {
|
|
...render(<MenuItem {...props} />),
|
|
onClick: props.onClick,
|
|
}
|
|
}
|
|
|
|
describe('MenuItem', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
// Menu item should render its interactive label and style variants.
|
|
describe('Rendering', () => {
|
|
it('should render a button with the provided label', () => {
|
|
// Arrange
|
|
renderMenuItem()
|
|
|
|
// Assert
|
|
expect(screen.getByRole('button', { name: /rename/i })).toBeInTheDocument()
|
|
})
|
|
|
|
it('should apply destructive variant styles when variant is destructive', () => {
|
|
// Arrange
|
|
renderMenuItem({ variant: 'destructive', label: 'Delete' })
|
|
|
|
// Act
|
|
const button = screen.getByRole('button', { name: /delete/i })
|
|
|
|
// Assert
|
|
expect(button).toHaveClass('group')
|
|
expect(button).toHaveClass('hover:bg-state-destructive-hover')
|
|
})
|
|
})
|
|
|
|
// Optional props should alter the visible content.
|
|
describe('Props', () => {
|
|
it('should render keyboard shortcut hints when kbd has values', () => {
|
|
// Arrange
|
|
renderMenuItem({ kbd: ['k'] })
|
|
|
|
// Assert
|
|
expect(screen.getByText('k')).toBeInTheDocument()
|
|
})
|
|
|
|
it('should not render keyboard shortcut hints when kbd is empty', () => {
|
|
// Arrange
|
|
renderMenuItem({ kbd: [] })
|
|
|
|
// Assert
|
|
expect(screen.queryByText('k')).not.toBeInTheDocument()
|
|
})
|
|
|
|
it('should show tooltip content when hovering the tooltip trigger', async () => {
|
|
// Arrange
|
|
const tooltipText = 'Show help'
|
|
const { container } = renderMenuItem({ tooltip: tooltipText })
|
|
const tooltipIcon = container.querySelector('.i-ri-question-line')
|
|
|
|
// Act
|
|
expect(tooltipIcon).toBeTruthy()
|
|
fireEvent.mouseEnter(tooltipIcon!)
|
|
|
|
// Assert
|
|
expect(await screen.findByText(tooltipText)).toBeInTheDocument()
|
|
})
|
|
})
|
|
|
|
// Click handling should call actions without leaking events upward.
|
|
describe('Interactions', () => {
|
|
it('should call onClick and stop click propagation when button is clicked', () => {
|
|
// Arrange
|
|
const outerClick = vi.fn()
|
|
const onClick = vi.fn()
|
|
render(
|
|
<div onClick={outerClick}>
|
|
<MenuItem {...defaultProps} onClick={onClick} />
|
|
</div>,
|
|
)
|
|
|
|
// Act
|
|
fireEvent.click(screen.getByRole('button', { name: /rename/i }))
|
|
|
|
// Assert
|
|
expect(onClick).toHaveBeenCalledTimes(1)
|
|
expect(outerClick).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('should not trigger onClick when tooltip icon is clicked', () => {
|
|
// Arrange
|
|
const onClick = vi.fn()
|
|
const { container } = renderMenuItem({ onClick, tooltip: 'Help' })
|
|
const tooltipIcon = container.querySelector('.i-ri-question-line')
|
|
|
|
// Act
|
|
expect(tooltipIcon).toBeTruthy()
|
|
fireEvent.click(tooltipIcon!)
|
|
|
|
// Assert
|
|
expect(onClick).not.toHaveBeenCalled()
|
|
})
|
|
})
|
|
|
|
// Disabled state should block interaction.
|
|
describe('Edge cases', () => {
|
|
it('should disable the button and ignore click when disabled is true', () => {
|
|
// Arrange
|
|
const onClick = vi.fn()
|
|
renderMenuItem({ onClick, disabled: true })
|
|
const button = screen.getByRole('button', { name: /rename/i })
|
|
|
|
// Act
|
|
fireEvent.click(button)
|
|
|
|
// Assert
|
|
expect(button).toBeDisabled()
|
|
expect(onClick).not.toHaveBeenCalled()
|
|
})
|
|
})
|
|
})
|