Files
dify/web/app/components/workflow/skill/file-tree/tree/menu-item.spec.tsx
yyh 9e10b73b54 refactor(skill): replace @remixicon/react imports with CSS icon classes
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.
2026-02-09 19:51:05 +08:00

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()
})
})
})