mirror of
https://github.com/langgenius/dify.git
synced 2026-05-04 17:38:04 +08:00
chore: add test case for download components (#29569)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@ -0,0 +1,121 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||||
|
import CSVUploader, { type Props } from './csv-uploader'
|
||||||
|
import { ToastContext } from '@/app/components/base/toast'
|
||||||
|
|
||||||
|
jest.mock('react-i18next', () => ({
|
||||||
|
useTranslation: () => ({
|
||||||
|
t: (key: string) => key,
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
|
||||||
|
describe('CSVUploader', () => {
|
||||||
|
const notify = jest.fn()
|
||||||
|
const updateFile = jest.fn()
|
||||||
|
|
||||||
|
const getDropElements = () => {
|
||||||
|
const title = screen.getByText('appAnnotation.batchModal.csvUploadTitle')
|
||||||
|
const dropZone = title.parentElement?.parentElement as HTMLDivElement | null
|
||||||
|
if (!dropZone || !dropZone.parentElement)
|
||||||
|
throw new Error('Drop zone not found')
|
||||||
|
const dropContainer = dropZone.parentElement as HTMLDivElement
|
||||||
|
return { dropZone, dropContainer }
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderComponent = (props?: Partial<Props>) => {
|
||||||
|
const mergedProps: Props = {
|
||||||
|
file: undefined,
|
||||||
|
updateFile,
|
||||||
|
...props,
|
||||||
|
}
|
||||||
|
return render(
|
||||||
|
<ToastContext.Provider value={{ notify, close: jest.fn() }}>
|
||||||
|
<CSVUploader {...mergedProps} />
|
||||||
|
</ToastContext.Provider>,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should open the file picker when clicking browse', () => {
|
||||||
|
const clickSpy = jest.spyOn(HTMLInputElement.prototype, 'click')
|
||||||
|
renderComponent()
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByText('appAnnotation.batchModal.browse'))
|
||||||
|
|
||||||
|
expect(clickSpy).toHaveBeenCalledTimes(1)
|
||||||
|
clickSpy.mockRestore()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should toggle dragging styles and upload the dropped file', async () => {
|
||||||
|
const file = new File(['content'], 'input.csv', { type: 'text/csv' })
|
||||||
|
renderComponent()
|
||||||
|
const { dropZone, dropContainer } = getDropElements()
|
||||||
|
|
||||||
|
fireEvent.dragEnter(dropContainer)
|
||||||
|
expect(dropZone.className).toContain('border-components-dropzone-border-accent')
|
||||||
|
expect(dropZone.className).toContain('bg-components-dropzone-bg-accent')
|
||||||
|
|
||||||
|
fireEvent.drop(dropContainer, { dataTransfer: { files: [file] } })
|
||||||
|
|
||||||
|
await waitFor(() => expect(updateFile).toHaveBeenCalledWith(file))
|
||||||
|
expect(dropZone.className).not.toContain('border-components-dropzone-border-accent')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should ignore drop events without dataTransfer', () => {
|
||||||
|
renderComponent()
|
||||||
|
const { dropContainer } = getDropElements()
|
||||||
|
|
||||||
|
fireEvent.drop(dropContainer)
|
||||||
|
|
||||||
|
expect(updateFile).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should show an error when multiple files are dropped', async () => {
|
||||||
|
const fileA = new File(['a'], 'a.csv', { type: 'text/csv' })
|
||||||
|
const fileB = new File(['b'], 'b.csv', { type: 'text/csv' })
|
||||||
|
renderComponent()
|
||||||
|
const { dropContainer } = getDropElements()
|
||||||
|
|
||||||
|
fireEvent.drop(dropContainer, { dataTransfer: { files: [fileA, fileB] } })
|
||||||
|
|
||||||
|
await waitFor(() => expect(notify).toHaveBeenCalledWith({
|
||||||
|
type: 'error',
|
||||||
|
message: 'datasetCreation.stepOne.uploader.validation.count',
|
||||||
|
}))
|
||||||
|
expect(updateFile).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should propagate file selection changes through input change event', () => {
|
||||||
|
const file = new File(['row'], 'selected.csv', { type: 'text/csv' })
|
||||||
|
const { container } = renderComponent()
|
||||||
|
const fileInput = container.querySelector('input[type="file"]') as HTMLInputElement
|
||||||
|
|
||||||
|
fireEvent.change(fileInput, { target: { files: [file] } })
|
||||||
|
|
||||||
|
expect(updateFile).toHaveBeenCalledWith(file)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should render selected file details and allow change/removal', () => {
|
||||||
|
const file = new File(['data'], 'report.csv', { type: 'text/csv' })
|
||||||
|
const { container } = renderComponent({ file })
|
||||||
|
const fileInput = container.querySelector('input[type="file"]') as HTMLInputElement
|
||||||
|
|
||||||
|
expect(screen.getByText('report')).toBeInTheDocument()
|
||||||
|
expect(screen.getByText('.csv')).toBeInTheDocument()
|
||||||
|
|
||||||
|
const clickSpy = jest.spyOn(HTMLInputElement.prototype, 'click')
|
||||||
|
fireEvent.click(screen.getByText('datasetCreation.stepOne.uploader.change'))
|
||||||
|
expect(clickSpy).toHaveBeenCalled()
|
||||||
|
clickSpy.mockRestore()
|
||||||
|
|
||||||
|
const valueSetter = jest.spyOn(fileInput, 'value', 'set')
|
||||||
|
const removeTrigger = screen.getByTestId('remove-file-button')
|
||||||
|
fireEvent.click(removeTrigger)
|
||||||
|
|
||||||
|
expect(updateFile).toHaveBeenCalledWith()
|
||||||
|
expect(valueSetter).toHaveBeenCalledWith('')
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -114,7 +114,7 @@ const CSVUploader: FC<Props> = ({
|
|||||||
<div className='hidden items-center group-hover:flex'>
|
<div className='hidden items-center group-hover:flex'>
|
||||||
<Button variant='secondary' onClick={selectHandle}>{t('datasetCreation.stepOne.uploader.change')}</Button>
|
<Button variant='secondary' onClick={selectHandle}>{t('datasetCreation.stepOne.uploader.change')}</Button>
|
||||||
<div className='mx-2 h-4 w-px bg-divider-regular' />
|
<div className='mx-2 h-4 w-px bg-divider-regular' />
|
||||||
<div className='cursor-pointer p-2' onClick={removeFile}>
|
<div className='cursor-pointer p-2' onClick={removeFile} data-testid="remove-file-button">
|
||||||
<RiDeleteBinLine className='h-4 w-4 text-text-tertiary' />
|
<RiDeleteBinLine className='h-4 w-4 text-text-tertiary' />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -0,0 +1,53 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { render, screen } from '@testing-library/react'
|
||||||
|
import ResDownload from './index'
|
||||||
|
|
||||||
|
jest.mock('react-i18next', () => ({
|
||||||
|
useTranslation: () => ({
|
||||||
|
t: (key: string) => key,
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
|
||||||
|
const mockType = { Link: 'mock-link' }
|
||||||
|
let capturedProps: Record<string, unknown> | undefined
|
||||||
|
|
||||||
|
jest.mock('react-papaparse', () => ({
|
||||||
|
useCSVDownloader: () => {
|
||||||
|
const CSVDownloader = ({ children, ...props }: React.PropsWithChildren<Record<string, unknown>>) => {
|
||||||
|
capturedProps = props
|
||||||
|
return <div data-testid="csv-downloader" className={props.className as string}>{children}</div>
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
CSVDownloader,
|
||||||
|
Type: mockType,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
describe('ResDownload', () => {
|
||||||
|
const values = [{ text: 'Hello' }]
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
capturedProps = undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should render desktop download button with CSV downloader props', () => {
|
||||||
|
render(<ResDownload isMobile={false} values={values} />)
|
||||||
|
|
||||||
|
expect(screen.getByTestId('csv-downloader')).toBeInTheDocument()
|
||||||
|
expect(screen.getByText('common.operation.download')).toBeInTheDocument()
|
||||||
|
expect(capturedProps?.data).toEqual(values)
|
||||||
|
expect(capturedProps?.filename).toBe('result')
|
||||||
|
expect(capturedProps?.bom).toBe(true)
|
||||||
|
expect(capturedProps?.type).toBe(mockType.Link)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should render mobile action button without desktop label', () => {
|
||||||
|
render(<ResDownload isMobile={true} values={values} />)
|
||||||
|
|
||||||
|
expect(screen.getByTestId('csv-downloader')).toBeInTheDocument()
|
||||||
|
expect(screen.queryByText('common.operation.download')).not.toBeInTheDocument()
|
||||||
|
expect(screen.getByRole('button')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user