dify/web/app/components/base/drawer-plus/index.spec.tsx

448 lines
11 KiB
TypeScript

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