dify/web/app/components/base/tab-header/index.spec.tsx

115 lines
3.9 KiB
TypeScript

import { render, screen, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { describe, expect, it, vi } from 'vitest'
import TabHeader from './index'
describe('TabHeader Component', () => {
const mockItems = [
{ id: 'tab1', name: 'General' },
{ id: 'tab2', name: 'Settings' },
{ id: 'tab3', name: 'Profile', isRight: true },
{ id: 'tab4', name: 'Disabled Tab', disabled: true },
]
it('should render all items with correct names', () => {
render(<TabHeader items={mockItems} value="tab1" onChange={() => { }} />)
expect(screen.getByText('General')).toBeInTheDocument()
expect(screen.getByText('Settings')).toBeInTheDocument()
expect(screen.getByText('Profile')).toBeInTheDocument()
expect(screen.getByText('Disabled Tab')).toBeInTheDocument()
})
it('should separate items into left and right containers correctly', () => {
render(<TabHeader items={mockItems} value="tab1" onChange={() => { }} />)
const leftContainer = screen.getByTestId('tab-header-left')
const rightContainer = screen.getByTestId('tab-header-right')
// Verify children count
expect(leftContainer.children.length).toBe(3)
expect(rightContainer.children.length).toBe(1)
// Verify specific item placement using within and toContainElement
const profileTab = screen.getByTestId('tab-header-item-tab3')
expect(rightContainer).toContainElement(profileTab)
const disabledTab = screen.getByTestId('tab-header-item-tab4')
expect(leftContainer).toContainElement(disabledTab)
})
it('should apply active styles to the selected tab', () => {
const activeClass = 'custom-active-style'
render(
<TabHeader
items={mockItems}
value="tab2"
activeItemClassName={activeClass}
onChange={() => { }}
/>,
)
const activeTab = screen.getByTestId('tab-header-item-tab2')
expect(activeTab).toHaveClass('border-components-tab-active')
expect(activeTab).toHaveClass(activeClass)
const inactiveTab = screen.getByTestId('tab-header-item-tab1')
expect(inactiveTab).toHaveClass('text-text-tertiary')
})
it('should call onChange when a non-disabled tab is clicked', async () => {
const user = userEvent.setup()
const handleChange = vi.fn()
render(<TabHeader items={mockItems} value="tab1" onChange={handleChange} />)
await user.click(screen.getByText('Settings'))
expect(handleChange).toHaveBeenCalledWith('tab2')
})
it('should not call onChange when a disabled tab is clicked', async () => {
const user = userEvent.setup()
const handleChange = vi.fn()
render(<TabHeader items={mockItems} value="tab1" onChange={handleChange} />)
const disabledTab = screen.getByTestId('tab-header-item-tab4')
expect(disabledTab).toHaveClass('cursor-not-allowed')
await user.click(disabledTab)
expect(handleChange).not.toHaveBeenCalled()
})
it('should render icon and extra content when provided', () => {
const itemsWithExtras = [
{
id: 'extra',
name: 'Extra',
icon: <span data-testid="tab-icon">🚀</span>,
extra: <span data-testid="tab-extra">New</span>,
},
]
render(<TabHeader items={itemsWithExtras} value="extra" onChange={() => { }} />)
expect(screen.getByTestId('tab-icon')).toBeInTheDocument()
expect(screen.getByTestId('tab-extra')).toBeInTheDocument()
})
it('should apply custom class names for items and wrappers', () => {
render(
<TabHeader
items={mockItems}
value="tab1"
itemClassName="my-text-class"
itemWrapClassName="my-wrap-class"
onChange={() => { }}
/>,
)
const tabWrap = screen.getByTestId('tab-header-item-tab1')
// We target the inner div for the name class check
const tabText = within(tabWrap).getByText('General')
expect(tabWrap).toHaveClass('my-wrap-class')
expect(tabText).toHaveClass('my-text-class')
})
})