mirror of https://github.com/langgenius/dify.git
test: add unit tests for some base components (#32201)
This commit is contained in:
parent
32350f7a04
commit
f953331f91
|
|
@ -0,0 +1,34 @@
|
|||
import { render, screen } from '@testing-library/react'
|
||||
import AnswerIcon from '.'
|
||||
|
||||
describe('AnswerIcon', () => {
|
||||
it('renders default emoji when no icon or image is provided', () => {
|
||||
const { container } = render(<AnswerIcon />)
|
||||
const emojiElement = container.querySelector('em-emoji')
|
||||
expect(emojiElement).toBeInTheDocument()
|
||||
expect(emojiElement).toHaveAttribute('id', '🤖')
|
||||
})
|
||||
|
||||
it('renders with custom emoji when icon is provided', () => {
|
||||
const { container } = render(<AnswerIcon icon="smile" />)
|
||||
const emojiElement = container.querySelector('em-emoji')
|
||||
expect(emojiElement).toBeInTheDocument()
|
||||
expect(emojiElement).toHaveAttribute('id', 'smile')
|
||||
})
|
||||
it('renders image when iconType is image and imageUrl is provided', () => {
|
||||
render(<AnswerIcon iconType="image" imageUrl="test-image.jpg" />)
|
||||
const imgElement = screen.getByAltText('answer icon')
|
||||
expect(imgElement).toBeInTheDocument()
|
||||
expect(imgElement).toHaveAttribute('src', 'test-image.jpg')
|
||||
})
|
||||
|
||||
it('applies custom background color', () => {
|
||||
const { container } = render(<AnswerIcon background="#FF5500" />)
|
||||
expect(container.firstChild).toHaveStyle('background: #FF5500')
|
||||
})
|
||||
|
||||
it('uses default background color when no background is provided for non-image icons', () => {
|
||||
const { container } = render(<AnswerIcon />)
|
||||
expect(container.firstChild).toHaveStyle('background: #D5F5F6')
|
||||
})
|
||||
})
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
import { fireEvent, render } from '@testing-library/react'
|
||||
import CopyIcon from '.'
|
||||
|
||||
const copy = vi.fn()
|
||||
const reset = vi.fn()
|
||||
let copied = false
|
||||
|
||||
vi.mock('foxact/use-clipboard', () => ({
|
||||
useClipboard: () => ({
|
||||
copy,
|
||||
reset,
|
||||
copied,
|
||||
}),
|
||||
}))
|
||||
|
||||
describe('copy icon component', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks()
|
||||
copied = false
|
||||
})
|
||||
|
||||
it('renders normally', () => {
|
||||
const { container } = render(<CopyIcon content="this is some test content for the copy icon component" />)
|
||||
expect(container.querySelector('svg')).not.toBeNull()
|
||||
})
|
||||
|
||||
it('shows copy icon initially', () => {
|
||||
const { container } = render(<CopyIcon content="this is some test content for the copy icon component" />)
|
||||
const icon = container.querySelector('[data-icon="Copy"]')
|
||||
expect(icon).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('shows copy check icon when copied', () => {
|
||||
copied = true
|
||||
const { container } = render(<CopyIcon content="this is some test content for the copy icon component" />)
|
||||
const icon = container.querySelector('[data-icon="CopyCheck"]')
|
||||
expect(icon).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('handles copy when clicked', () => {
|
||||
const { container } = render(<CopyIcon content="this is some test content for the copy icon component" />)
|
||||
const icon = container.querySelector('[data-icon="Copy"]')
|
||||
fireEvent.click(icon as Element)
|
||||
expect(copy).toBeCalledTimes(1)
|
||||
})
|
||||
|
||||
it('resets on mouse leave', () => {
|
||||
const { container } = render(<CopyIcon content="this is some test content for the copy icon component" />)
|
||||
const icon = container.querySelector('[data-icon="Copy"]')
|
||||
const div = icon?.parentElement as HTMLElement
|
||||
fireEvent.mouseLeave(div)
|
||||
expect(reset).toBeCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import { render, screen } from '@testing-library/react'
|
||||
import CornerLabel from '.'
|
||||
|
||||
describe('CornerLabel', () => {
|
||||
it('renders the label correctly', () => {
|
||||
render(<CornerLabel label="Test Label" />)
|
||||
expect(screen.getByText('Test Label')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('applies custom class names', () => {
|
||||
const { container } = render(<CornerLabel label="Test Label" className="custom-class" labelClassName="custom-label-class" />)
|
||||
expect(container.querySelector('.custom-class')).toBeInTheDocument()
|
||||
expect(container.querySelector('.custom-label-class')).toBeInTheDocument()
|
||||
expect(screen.getByText('Test Label')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
|
@ -0,0 +1,447 @@
|
|||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import * as React from 'react'
|
||||
import DrawerPlus from '.'
|
||||
|
||||
vi.mock('@/hooks/use-breakpoints', () => ({
|
||||
default: () => 'desktop',
|
||||
MediaType: { mobile: 'mobile', desktop: 'desktop', tablet: 'tablet' },
|
||||
}))
|
||||
|
||||
describe('DrawerPlus', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('Rendering', () => {
|
||||
it('should not render when isShow is false', () => {
|
||||
render(
|
||||
<DrawerPlus
|
||||
isShow={false}
|
||||
onHide={() => {}}
|
||||
title="Test Drawer"
|
||||
body={<div>Content</div>}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render when isShow is true', () => {
|
||||
const bodyContent = <div>Body Content</div>
|
||||
render(
|
||||
<DrawerPlus
|
||||
isShow={true}
|
||||
onHide={() => {}}
|
||||
title="Test Drawer"
|
||||
body={bodyContent}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByRole('dialog')).toBeInTheDocument()
|
||||
expect(screen.getByText('Test Drawer')).toBeInTheDocument()
|
||||
expect(screen.getByText('Body Content')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render footer when provided', () => {
|
||||
const footerContent = <div>Footer Content</div>
|
||||
render(
|
||||
<DrawerPlus
|
||||
isShow={true}
|
||||
onHide={() => {}}
|
||||
title="Test Drawer"
|
||||
body={<div>Body</div>}
|
||||
foot={footerContent}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByText('Footer Content')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render JSX element as title', () => {
|
||||
const titleElement = <h1 data-testid="custom-title">Custom Title</h1>
|
||||
render(
|
||||
<DrawerPlus
|
||||
isShow={true}
|
||||
onHide={() => {}}
|
||||
title={titleElement}
|
||||
body={<div>Body</div>}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByTestId('custom-title')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render titleDescription when provided', () => {
|
||||
render(
|
||||
<DrawerPlus
|
||||
isShow={true}
|
||||
onHide={() => {}}
|
||||
title="Test Drawer"
|
||||
titleDescription="Description text"
|
||||
body={<div>Body</div>}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByText('Description text')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should not render titleDescription when not provided', () => {
|
||||
render(
|
||||
<DrawerPlus
|
||||
isShow={true}
|
||||
onHide={() => {}}
|
||||
title="Test Drawer"
|
||||
body={<div>Body</div>}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.queryByText(/Description/)).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render JSX element as titleDescription', () => {
|
||||
const descElement = <span data-testid="custom-desc">Custom Description</span>
|
||||
render(
|
||||
<DrawerPlus
|
||||
isShow={true}
|
||||
onHide={() => {}}
|
||||
title="Test"
|
||||
titleDescription={descElement}
|
||||
body={<div>Body</div>}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByTestId('custom-desc')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Props - Display Options', () => {
|
||||
it('should apply default maxWidthClassName', () => {
|
||||
render(
|
||||
<DrawerPlus
|
||||
isShow={true}
|
||||
onHide={() => {}}
|
||||
title="Test"
|
||||
body={<div>Body</div>}
|
||||
/>,
|
||||
)
|
||||
const innerPanel = screen.getByText('Test').closest('.bg-components-panel-bg')
|
||||
const outerPanel = innerPanel?.parentElement
|
||||
expect(outerPanel?.className).toContain('!max-w-[640px]')
|
||||
})
|
||||
|
||||
it('should apply custom maxWidthClassName', () => {
|
||||
render(
|
||||
<DrawerPlus
|
||||
isShow={true}
|
||||
onHide={() => {}}
|
||||
title="Test"
|
||||
body={<div>Body</div>}
|
||||
maxWidthClassName="!max-w-[800px]"
|
||||
/>,
|
||||
)
|
||||
|
||||
const innerPanel = screen.getByText('Test').closest('.bg-components-panel-bg')
|
||||
const outerPanel = innerPanel?.parentElement
|
||||
expect(outerPanel?.className).toContain('!max-w-[800px]')
|
||||
})
|
||||
|
||||
it('should apply custom panelClassName', () => {
|
||||
render(
|
||||
<DrawerPlus
|
||||
isShow={true}
|
||||
onHide={() => {}}
|
||||
title="Test"
|
||||
body={<div>Body</div>}
|
||||
panelClassName="custom-panel"
|
||||
/>,
|
||||
)
|
||||
|
||||
const innerPanel = screen.getByText('Test').closest('.bg-components-panel-bg')
|
||||
const outerPanel = innerPanel?.parentElement
|
||||
expect(outerPanel?.className).toContain('custom-panel')
|
||||
})
|
||||
|
||||
it('should apply custom dialogClassName', () => {
|
||||
render(
|
||||
<DrawerPlus
|
||||
isShow={true}
|
||||
onHide={() => {}}
|
||||
title="Test"
|
||||
body={<div>Body</div>}
|
||||
dialogClassName="custom-dialog"
|
||||
/>,
|
||||
)
|
||||
|
||||
const dialog = screen.getByRole('dialog')
|
||||
expect(dialog.className).toContain('custom-dialog')
|
||||
})
|
||||
|
||||
it('should apply custom contentClassName', () => {
|
||||
render(
|
||||
<DrawerPlus
|
||||
isShow={true}
|
||||
onHide={() => {}}
|
||||
title="Test"
|
||||
body={<div>Body</div>}
|
||||
contentClassName="custom-content"
|
||||
/>,
|
||||
)
|
||||
const title = screen.getByText('Test')
|
||||
const header = title.closest('.shrink-0.border-b.border-divider-subtle')
|
||||
const content = header?.parentElement
|
||||
expect(content?.className).toContain('custom-content')
|
||||
})
|
||||
|
||||
it('should apply custom headerClassName', () => {
|
||||
render(
|
||||
<DrawerPlus
|
||||
isShow={true}
|
||||
onHide={() => {}}
|
||||
title="Test"
|
||||
body={<div>Body</div>}
|
||||
headerClassName="custom-header"
|
||||
/>,
|
||||
)
|
||||
|
||||
const title = screen.getByText('Test')
|
||||
const header = title.closest('.shrink-0.border-b.border-divider-subtle')
|
||||
expect(header?.className).toContain('custom-header')
|
||||
})
|
||||
|
||||
it('should apply custom height', () => {
|
||||
render(
|
||||
<DrawerPlus
|
||||
isShow={true}
|
||||
onHide={() => {}}
|
||||
title="Test"
|
||||
body={<div>Body</div>}
|
||||
height="500px"
|
||||
/>,
|
||||
)
|
||||
|
||||
const title = screen.getByText('Test')
|
||||
const header = title.closest('.shrink-0.border-b.border-divider-subtle')
|
||||
const content = header?.parentElement
|
||||
expect(content?.getAttribute('style')).toContain('height: 500px')
|
||||
})
|
||||
|
||||
it('should use default height', () => {
|
||||
render(
|
||||
<DrawerPlus
|
||||
isShow={true}
|
||||
onHide={() => {}}
|
||||
title="Test"
|
||||
body={<div>Body</div>}
|
||||
/>,
|
||||
)
|
||||
|
||||
const title = screen.getByText('Test')
|
||||
const header = title.closest('.shrink-0.border-b.border-divider-subtle')
|
||||
const content = header?.parentElement
|
||||
expect(content?.getAttribute('style')).toContain('calc(100vh - 72px)')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Event Handlers', () => {
|
||||
it('should call onHide when close button is clicked', () => {
|
||||
const handleHide = vi.fn()
|
||||
render(
|
||||
<DrawerPlus
|
||||
isShow={true}
|
||||
onHide={handleHide}
|
||||
title="Test"
|
||||
body={<div>Body</div>}
|
||||
/>,
|
||||
)
|
||||
|
||||
const title = screen.getByText('Test')
|
||||
const headerRight = title.nextElementSibling // .flex items-center
|
||||
const closeDiv = headerRight?.querySelector('.cursor-pointer') as HTMLElement
|
||||
|
||||
fireEvent.click(closeDiv)
|
||||
expect(handleHide).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Complex Content', () => {
|
||||
it('should render complex JSX elements in body', () => {
|
||||
const complexBody = (
|
||||
<div>
|
||||
<h2>Header</h2>
|
||||
<p>Paragraph</p>
|
||||
<button>Action Button</button>
|
||||
</div>
|
||||
)
|
||||
|
||||
render(
|
||||
<DrawerPlus
|
||||
isShow={true}
|
||||
onHide={() => {}}
|
||||
title="Test"
|
||||
body={complexBody}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByText('Header')).toBeInTheDocument()
|
||||
expect(screen.getByText('Paragraph')).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: 'Action Button' })).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render complex footer', () => {
|
||||
const complexFooter = (
|
||||
<div className="footer-actions">
|
||||
<button>Cancel</button>
|
||||
<button>Save</button>
|
||||
</div>
|
||||
)
|
||||
|
||||
render(
|
||||
<DrawerPlus
|
||||
isShow={true}
|
||||
onHide={() => {}}
|
||||
title="Test"
|
||||
body={<div>Body</div>}
|
||||
foot={complexFooter}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: 'Save' })).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Edge Cases', () => {
|
||||
it('should handle empty title', () => {
|
||||
render(
|
||||
<DrawerPlus
|
||||
isShow={true}
|
||||
onHide={() => {}}
|
||||
title=""
|
||||
body={<div>Body</div>}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByRole('dialog')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should handle undefined titleDescription', () => {
|
||||
render(
|
||||
<DrawerPlus
|
||||
isShow={true}
|
||||
onHide={() => {}}
|
||||
title="Test"
|
||||
titleDescription={undefined}
|
||||
body={<div>Body</div>}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByRole('dialog')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should handle rapid isShow toggle', () => {
|
||||
const { rerender } = render(
|
||||
<DrawerPlus
|
||||
isShow={true}
|
||||
onHide={() => {}}
|
||||
title="Test"
|
||||
body={<div>Body</div>}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByRole('dialog')).toBeInTheDocument()
|
||||
|
||||
rerender(
|
||||
<DrawerPlus
|
||||
isShow={false}
|
||||
onHide={() => {}}
|
||||
title="Test"
|
||||
body={<div>Body</div>}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
|
||||
|
||||
rerender(
|
||||
<DrawerPlus
|
||||
isShow={true}
|
||||
onHide={() => {}}
|
||||
title="Test"
|
||||
body={<div>Body</div>}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByRole('dialog')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should handle special characters in title', () => {
|
||||
const specialTitle = 'Test <> & " \' | Drawer'
|
||||
render(
|
||||
<DrawerPlus
|
||||
isShow={true}
|
||||
onHide={() => {}}
|
||||
title={specialTitle}
|
||||
body={<div>Body</div>}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByText(specialTitle)).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should handle empty body content', () => {
|
||||
render(
|
||||
<DrawerPlus
|
||||
isShow={true}
|
||||
onHide={() => {}}
|
||||
title="Test"
|
||||
body={<div></div>}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByRole('dialog')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should apply both custom maxWidth and panel classNames', () => {
|
||||
render(
|
||||
<DrawerPlus
|
||||
isShow={true}
|
||||
onHide={() => {}}
|
||||
title="Test"
|
||||
body={<div>Body</div>}
|
||||
maxWidthClassName="!max-w-[500px]"
|
||||
panelClassName="custom-style"
|
||||
/>,
|
||||
)
|
||||
|
||||
const innerPanel = screen.getByText('Test').closest('.bg-components-panel-bg')
|
||||
const outerPanel = innerPanel?.parentElement
|
||||
expect(outerPanel?.className).toContain('!max-w-[500px]')
|
||||
expect(outerPanel?.className).toContain('custom-style')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Memoization', () => {
|
||||
it('should be memoized and not re-render on parent changes', () => {
|
||||
const { rerender } = render(
|
||||
<DrawerPlus
|
||||
isShow={true}
|
||||
onHide={() => {}}
|
||||
title="Test"
|
||||
body={<div>Body</div>}
|
||||
/>,
|
||||
)
|
||||
|
||||
const dialog = screen.getByRole('dialog')
|
||||
|
||||
rerender(
|
||||
<DrawerPlus
|
||||
isShow={true}
|
||||
onHide={() => {}}
|
||||
title="Test"
|
||||
body={<div>Body</div>}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(dialog).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -0,0 +1,225 @@
|
|||
import { act, cleanup, fireEvent, render, screen } from '@testing-library/react'
|
||||
import Dropdown from './index'
|
||||
|
||||
describe('Dropdown Component', () => {
|
||||
const mockItems = [
|
||||
{ value: 'option1', text: 'Option 1' },
|
||||
{ value: 'option2', text: 'Option 2' },
|
||||
]
|
||||
const mockSecondItems = [
|
||||
{ value: 'option3', text: 'Option 3' },
|
||||
]
|
||||
const onSelect = vi.fn()
|
||||
|
||||
afterEach(() => {
|
||||
cleanup()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('renders default trigger properly', () => {
|
||||
const { container } = render(
|
||||
<Dropdown items={mockItems} onSelect={onSelect} />,
|
||||
)
|
||||
const trigger = container.querySelector('button')
|
||||
expect(trigger).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders custom trigger when provided', () => {
|
||||
render(
|
||||
<Dropdown
|
||||
items={mockItems}
|
||||
onSelect={onSelect}
|
||||
renderTrigger={open => <button data-testid="custom-trigger">{open ? 'Open' : 'Closed'}</button>}
|
||||
/>,
|
||||
)
|
||||
const trigger = screen.getByTestId('custom-trigger')
|
||||
expect(trigger).toBeInTheDocument()
|
||||
expect(trigger).toHaveTextContent('Closed')
|
||||
})
|
||||
|
||||
it('opens dropdown menu on trigger click and shows items', async () => {
|
||||
render(
|
||||
<Dropdown items={mockItems} onSelect={onSelect} />,
|
||||
)
|
||||
const trigger = screen.getByRole('button')
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(trigger)
|
||||
})
|
||||
|
||||
// Dropdown items are rendered in a portal (document.body)
|
||||
expect(screen.getByText('Option 1')).toBeInTheDocument()
|
||||
expect(screen.getByText('Option 2')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('calls onSelect and closes dropdown when an item is clicked', async () => {
|
||||
render(
|
||||
<Dropdown items={mockItems} onSelect={onSelect} />,
|
||||
)
|
||||
const trigger = screen.getByRole('button')
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(trigger)
|
||||
})
|
||||
|
||||
const option1 = screen.getByText('Option 1')
|
||||
await act(async () => {
|
||||
fireEvent.click(option1)
|
||||
})
|
||||
|
||||
expect(onSelect).toHaveBeenCalledWith(mockItems[0])
|
||||
expect(screen.queryByText('Option 1')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('calls onSelect and closes dropdown when a second item is clicked', async () => {
|
||||
render(
|
||||
<Dropdown items={mockItems} secondItems={mockSecondItems} onSelect={onSelect} />,
|
||||
)
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByRole('button'))
|
||||
})
|
||||
|
||||
const option3 = screen.getByText('Option 3')
|
||||
await act(async () => {
|
||||
fireEvent.click(option3)
|
||||
})
|
||||
expect(onSelect).toHaveBeenCalledWith(mockSecondItems[0])
|
||||
expect(screen.queryByText('Option 3')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders second items and divider when provided', async () => {
|
||||
render(
|
||||
<Dropdown
|
||||
items={mockItems}
|
||||
secondItems={mockSecondItems}
|
||||
onSelect={onSelect}
|
||||
/>,
|
||||
)
|
||||
const trigger = screen.getByRole('button')
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(trigger)
|
||||
})
|
||||
|
||||
expect(screen.getByText('Option 1')).toBeInTheDocument()
|
||||
expect(screen.getByText('Option 3')).toBeInTheDocument()
|
||||
|
||||
// Check for divider (h-px bg-divider-regular)
|
||||
const divider = document.body.querySelector('.bg-divider-regular.h-px')
|
||||
expect(divider).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('applies custom classNames', async () => {
|
||||
const popupClass = 'custom-popup'
|
||||
const itemClass = 'custom-item'
|
||||
const secondItemClass = 'custom-second-item'
|
||||
|
||||
render(
|
||||
<Dropdown
|
||||
items={mockItems}
|
||||
secondItems={mockSecondItems}
|
||||
onSelect={onSelect}
|
||||
popupClassName={popupClass}
|
||||
itemClassName={itemClass}
|
||||
secondItemClassName={secondItemClass}
|
||||
/>,
|
||||
)
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByRole('button'))
|
||||
})
|
||||
|
||||
const popup = document.body.querySelector(`.${popupClass}`)
|
||||
expect(popup).toBeInTheDocument()
|
||||
|
||||
const items = screen.getAllByText('Option 1')
|
||||
expect(items[0]).toHaveClass(itemClass)
|
||||
|
||||
const secondItems = screen.getAllByText('Option 3')
|
||||
expect(secondItems[0]).toHaveClass(secondItemClass)
|
||||
})
|
||||
|
||||
it('applies open class to trigger when menu is open', async () => {
|
||||
render(<Dropdown items={mockItems} onSelect={onSelect} />)
|
||||
const trigger = screen.getByRole('button')
|
||||
await act(async () => {
|
||||
fireEvent.click(trigger)
|
||||
})
|
||||
expect(trigger).toHaveClass('bg-divider-regular')
|
||||
})
|
||||
|
||||
it('handles JSX elements as item text', async () => {
|
||||
const itemsWithJSX = [
|
||||
{ value: 'jsx', text: <span data-testid="jsx-item">JSX Content</span> },
|
||||
]
|
||||
render(
|
||||
<Dropdown items={itemsWithJSX} onSelect={onSelect} />,
|
||||
)
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByRole('button'))
|
||||
})
|
||||
|
||||
expect(screen.getByTestId('jsx-item')).toBeInTheDocument()
|
||||
expect(screen.getByText('JSX Content')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('does not render items section if items list is empty', async () => {
|
||||
render(
|
||||
<Dropdown items={[]} secondItems={mockSecondItems} onSelect={onSelect} />,
|
||||
)
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByRole('button'))
|
||||
})
|
||||
|
||||
const p1Divs = document.body.querySelectorAll('.p-1')
|
||||
expect(p1Divs.length).toBe(1)
|
||||
expect(screen.queryByText('Option 1')).not.toBeInTheDocument()
|
||||
expect(screen.getByText('Option 3')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('does not render divider if only one section is provided', async () => {
|
||||
const { rerender } = render(
|
||||
<Dropdown items={mockItems} onSelect={onSelect} />,
|
||||
)
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByRole('button'))
|
||||
})
|
||||
expect(document.body.querySelector('.bg-divider-regular.h-px')).not.toBeInTheDocument()
|
||||
|
||||
await act(async () => {
|
||||
rerender(
|
||||
<Dropdown items={[]} secondItems={mockSecondItems} onSelect={onSelect} />,
|
||||
)
|
||||
})
|
||||
expect(document.body.querySelector('.bg-divider-regular.h-px')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders nothing if both item lists are empty', async () => {
|
||||
render(<Dropdown items={[]} secondItems={[]} onSelect={onSelect} />)
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByRole('button'))
|
||||
})
|
||||
const popup = document.body.querySelector('.bg-components-panel-bg')
|
||||
expect(popup?.children.length).toBe(0)
|
||||
})
|
||||
|
||||
it('passes triggerProps to ActionButton and applies custom className', () => {
|
||||
render(
|
||||
<Dropdown
|
||||
items={mockItems}
|
||||
onSelect={onSelect}
|
||||
triggerProps={{
|
||||
'disabled': true,
|
||||
'aria-label': 'dropdown-trigger',
|
||||
'className': 'custom-trigger-class',
|
||||
}}
|
||||
/>,
|
||||
)
|
||||
const trigger = screen.getByLabelText('dropdown-trigger')
|
||||
expect(trigger).toBeDisabled()
|
||||
expect(trigger).toHaveClass('custom-trigger-class')
|
||||
})
|
||||
})
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import { render } from '@testing-library/react'
|
||||
import Effect from '.'
|
||||
|
||||
describe('Effect', () => {
|
||||
it('applies custom class names', () => {
|
||||
const { container } = render(<Effect className="custom-class" />)
|
||||
expect(container.firstChild).toHaveClass('custom-class')
|
||||
})
|
||||
})
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
import { render, screen } from '@testing-library/react'
|
||||
import { EncryptedBottom } from '.'
|
||||
|
||||
describe('EncryptedBottom', () => {
|
||||
it('applies custom class names', () => {
|
||||
const { container } = render(<EncryptedBottom className="custom-class" />)
|
||||
expect(container.firstChild).toHaveClass('custom-class')
|
||||
})
|
||||
|
||||
it('passes keys', async () => {
|
||||
render(<EncryptedBottom frontTextKey="provider.encrypted.front" backTextKey="provider.encrypted.back" />)
|
||||
expect(await screen.findByText(/provider.encrypted.front/i)).toBeInTheDocument()
|
||||
expect(await screen.findByText(/provider.encrypted.back/i)).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
import { render } from '@testing-library/react'
|
||||
import FileIcon from '.'
|
||||
|
||||
describe('File icon component', () => {
|
||||
const testCases = [
|
||||
{ type: 'csv', icon: 'Csv' },
|
||||
{ type: 'doc', icon: 'Doc' },
|
||||
{ type: 'docx', icon: 'Docx' },
|
||||
{ type: 'htm', icon: 'Html' },
|
||||
{ type: 'html', icon: 'Html' },
|
||||
{ type: 'md', icon: 'Md' },
|
||||
{ type: 'mdx', icon: 'Md' },
|
||||
{ type: 'markdown', icon: 'Md' },
|
||||
{ type: 'pdf', icon: 'Pdf' },
|
||||
{ type: 'xls', icon: 'Xlsx' },
|
||||
{ type: 'xlsx', icon: 'Xlsx' },
|
||||
{ type: 'notion', icon: 'Notion' },
|
||||
{ type: 'something-else', icon: 'Unknown' },
|
||||
{ type: 'txt', icon: 'Txt' },
|
||||
{ type: 'json', icon: 'Json' },
|
||||
]
|
||||
|
||||
it.each(testCases)('renders $icon icon for type $type', ({ type, icon }) => {
|
||||
const { container } = render(<FileIcon type={type} />)
|
||||
const iconElement = container.querySelector(`[data-icon="${icon}"]`)
|
||||
expect(iconElement).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
import { render, screen } from '@testing-library/react'
|
||||
import NodeStatus, { NodeStatusEnum } from '.'
|
||||
|
||||
describe('NodeStatus', () => {
|
||||
it('renders with default status (warning) and default message', () => {
|
||||
const { container } = render(<NodeStatus />)
|
||||
|
||||
expect(screen.getByText('Warning')).toBeInTheDocument()
|
||||
// Default warning class
|
||||
expect(container.firstChild).toHaveClass('bg-state-warning-hover')
|
||||
expect(container.firstChild).toHaveClass('text-text-warning')
|
||||
})
|
||||
|
||||
it('renders with error status and default message', () => {
|
||||
const { container } = render(<NodeStatus status={NodeStatusEnum.error} />)
|
||||
|
||||
expect(screen.getByText('Error')).toBeInTheDocument()
|
||||
expect(container.firstChild).toHaveClass('bg-state-destructive-hover')
|
||||
expect(container.firstChild).toHaveClass('text-text-destructive')
|
||||
})
|
||||
|
||||
it('renders with custom message', () => {
|
||||
render(<NodeStatus message="Custom Message" />)
|
||||
expect(screen.getByText('Custom Message')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders children correctly', () => {
|
||||
render(
|
||||
<NodeStatus>
|
||||
<span data-testid="child">Child Element</span>
|
||||
</NodeStatus>,
|
||||
)
|
||||
expect(screen.getByTestId('child')).toBeInTheDocument()
|
||||
expect(screen.getByText('Child Element')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('applies custom className', () => {
|
||||
const { container } = render(<NodeStatus className="custom-test-class" />)
|
||||
expect(container.firstChild).toHaveClass('custom-test-class')
|
||||
})
|
||||
|
||||
it('applies styleCss correctly', () => {
|
||||
const { container } = render(<NodeStatus styleCss={{ color: 'red' }} />)
|
||||
expect(container.firstChild).toHaveStyle({ color: 'rgb(255, 0, 0)' })
|
||||
})
|
||||
|
||||
it('applies iconClassName to the icon', () => {
|
||||
const { container } = render(<NodeStatus iconClassName="custom-icon-class" />)
|
||||
// The icon is the first child of the div
|
||||
const icon = container.querySelector('.custom-icon-class')
|
||||
expect(icon).toBeInTheDocument()
|
||||
expect(icon).toHaveClass('h-3.5')
|
||||
expect(icon).toHaveClass('w-3.5')
|
||||
})
|
||||
|
||||
it('passes additional HTML attributes to the container', () => {
|
||||
render(<NodeStatus data-testid="node-status-container" id="my-id" />)
|
||||
const container = screen.getByTestId('node-status-container')
|
||||
expect(container).toHaveAttribute('id', 'my-id')
|
||||
})
|
||||
})
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
import { render, screen } from '@testing-library/react'
|
||||
import NotionIcon from '.'
|
||||
|
||||
describe('Notion Icon', () => {
|
||||
it('applies custom class names', () => {
|
||||
const { container } = render(<NotionIcon className="custom-class" />)
|
||||
expect(container.firstChild).toHaveClass('custom-class')
|
||||
})
|
||||
|
||||
it('renders image on http url', () => {
|
||||
render(<NotionIcon src="http://example.com/image.png" />)
|
||||
expect(screen.getByAltText('workspace icon')).toHaveAttribute('src', 'http://example.com/image.png')
|
||||
})
|
||||
|
||||
it('renders image on https url', () => {
|
||||
render(<NotionIcon src="https://example.com/image.png" />)
|
||||
expect(screen.getByAltText('workspace icon')).toHaveAttribute('src', 'https://example.com/image.png')
|
||||
})
|
||||
|
||||
it('renders div on non-http url', () => {
|
||||
render(<NotionIcon src="example.com/image.png" />)
|
||||
expect(screen.getByText('example.com/image.png')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders name when no url is provided', () => {
|
||||
render(<NotionIcon name="test-name" />)
|
||||
expect(screen.getByText('T')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders image on type url for page', () => {
|
||||
render(<NotionIcon type="page" src={{ type: 'url', url: 'https://example.com/image.png', emoji: null }} />)
|
||||
expect(screen.getByAltText('page icon')).toHaveAttribute('src', 'https://example.com/image.png')
|
||||
})
|
||||
|
||||
it('renders blank image on type url if no url is passed for page', () => {
|
||||
render(<NotionIcon type="page" src={{ type: 'url', url: null, emoji: null }} />)
|
||||
expect(screen.getByAltText('page icon')).not.toHaveAttribute('src')
|
||||
})
|
||||
|
||||
it('renders emoji on type emoji for page', () => {
|
||||
render(<NotionIcon type="page" src={{ type: 'emoji', url: null, emoji: '🚀' }} />)
|
||||
expect(screen.getByText('🚀')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders icon on url for page', () => {
|
||||
const { container } = render(<NotionIcon type="page" src="https://example.com/image.png" />)
|
||||
expect(container.querySelector('svg')).not.toBeNull()
|
||||
})
|
||||
})
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
import { render, screen } from '@testing-library/react'
|
||||
import PremiumBadge from './index'
|
||||
|
||||
describe('PremiumBadge', () => {
|
||||
it('renders with default props', () => {
|
||||
render(<PremiumBadge>Premium</PremiumBadge>)
|
||||
const badge = screen.getByText('Premium')
|
||||
expect(badge).toBeInTheDocument()
|
||||
expect(badge).toHaveClass('premium-badge-m')
|
||||
expect(badge).toHaveClass('premium-badge-blue')
|
||||
})
|
||||
|
||||
it('renders with custom size and color', () => {
|
||||
render(
|
||||
<PremiumBadge size="s" color="indigo">
|
||||
Premium
|
||||
</PremiumBadge>,
|
||||
)
|
||||
const badge = screen.getByText('Premium')
|
||||
expect(badge).toBeInTheDocument()
|
||||
expect(badge).toHaveClass('premium-badge-s')
|
||||
expect(badge).toHaveClass('premium-badge-indigo')
|
||||
})
|
||||
|
||||
it('applies allowHover class when allowHover is true', () => {
|
||||
render(
|
||||
<PremiumBadge allowHover>
|
||||
Premium
|
||||
</PremiumBadge>,
|
||||
)
|
||||
const badge = screen.getByText('Premium')
|
||||
expect(badge).toBeInTheDocument()
|
||||
expect(badge).toHaveClass('allowHover')
|
||||
})
|
||||
|
||||
it('applies custom styles', () => {
|
||||
render(
|
||||
<PremiumBadge styleCss={{ backgroundColor: 'red' }}>
|
||||
Premium
|
||||
</PremiumBadge>,
|
||||
)
|
||||
const badge = screen.getByText('Premium')
|
||||
expect(badge).toBeInTheDocument()
|
||||
expect(badge).toHaveStyle('background-color: rgb(255, 0, 0)') // Note: React converts 'red' to 'rgb(255, 0, 0)'
|
||||
})
|
||||
})
|
||||
Loading…
Reference in New Issue