mirror of
https://github.com/langgenius/dify.git
synced 2026-05-04 09:28:04 +08:00
refactor(web): migrate to Vitest and esm (#29974)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
This commit is contained in:
@ -62,8 +62,8 @@ describe('ActionButton', () => {
|
||||
)
|
||||
const button = screen.getByRole('button', { name: 'Custom Style' })
|
||||
expect(button).toHaveStyle({
|
||||
color: 'red',
|
||||
backgroundColor: 'blue',
|
||||
color: 'rgb(255, 0, 0)',
|
||||
backgroundColor: 'rgb(0, 0, 255)',
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -1,18 +1,20 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import '@testing-library/jest-dom'
|
||||
import AppIcon from './index'
|
||||
|
||||
// Mock emoji-mart initialization
|
||||
jest.mock('emoji-mart', () => ({
|
||||
init: jest.fn(),
|
||||
vi.mock('emoji-mart', () => ({
|
||||
init: vi.fn(),
|
||||
}))
|
||||
|
||||
// Mock emoji data
|
||||
jest.mock('@emoji-mart/data', () => ({}))
|
||||
vi.mock('@emoji-mart/data', () => ({
|
||||
default: {},
|
||||
}))
|
||||
|
||||
// Mock the ahooks useHover hook
|
||||
jest.mock('ahooks', () => ({
|
||||
useHover: jest.fn(() => false),
|
||||
// Create a controllable mock for useHover
|
||||
let mockHoverValue = false
|
||||
vi.mock('ahooks', () => ({
|
||||
useHover: vi.fn(() => mockHoverValue),
|
||||
}))
|
||||
|
||||
describe('AppIcon', () => {
|
||||
@ -31,8 +33,8 @@ describe('AppIcon', () => {
|
||||
})
|
||||
}
|
||||
|
||||
// Reset mocks
|
||||
require('ahooks').useHover.mockReset().mockReturnValue(false)
|
||||
// Reset mock hover value
|
||||
mockHoverValue = false
|
||||
})
|
||||
|
||||
it('renders default emoji when no icon or image is provided', () => {
|
||||
@ -107,7 +109,7 @@ describe('AppIcon', () => {
|
||||
})
|
||||
|
||||
it('calls onClick handler when clicked', () => {
|
||||
const handleClick = jest.fn()
|
||||
const handleClick = vi.fn()
|
||||
const { container } = render(<AppIcon onClick={handleClick} />)
|
||||
fireEvent.click(container.firstChild!)
|
||||
|
||||
@ -127,7 +129,7 @@ describe('AppIcon', () => {
|
||||
|
||||
it('displays edit icon when showEditIcon=true and hovering', () => {
|
||||
// Mock the useHover hook to return true for this test
|
||||
require('ahooks').useHover.mockReturnValue(true)
|
||||
mockHoverValue = true
|
||||
|
||||
render(<AppIcon showEditIcon />)
|
||||
const editIcon = document.querySelector('svg')
|
||||
@ -136,6 +138,7 @@ describe('AppIcon', () => {
|
||||
|
||||
it('does not display edit icon when showEditIcon=true but not hovering', () => {
|
||||
// useHover returns false by default from our mock setup
|
||||
mockHoverValue = false
|
||||
render(<AppIcon showEditIcon />)
|
||||
const editIcon = document.querySelector('svg')
|
||||
expect(editIcon).not.toBeInTheDocument()
|
||||
|
||||
@ -101,7 +101,7 @@ describe('Button', () => {
|
||||
|
||||
describe('Button events', () => {
|
||||
test('onClick should been call after clicked', async () => {
|
||||
const onClick = jest.fn()
|
||||
const onClick = vi.fn()
|
||||
const { getByRole } = render(<Button onClick={onClick}>Click me</Button>)
|
||||
fireEvent.click(getByRole('button'))
|
||||
expect(onClick).toHaveBeenCalled()
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`build chat item tree and get thread messages should get thread messages from tree6, using specified message as target 1`] = `
|
||||
exports[`build chat item tree and get thread messages > should get thread messages from tree6, using specified message as target 1`] = `
|
||||
[
|
||||
{
|
||||
"children": [
|
||||
@ -834,7 +834,7 @@ exports[`build chat item tree and get thread messages should get thread messages
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`build chat item tree and get thread messages should get thread messages from tree6, using the last message as target 1`] = `
|
||||
exports[`build chat item tree and get thread messages > should get thread messages from tree6, using the last message as target 1`] = `
|
||||
[
|
||||
{
|
||||
"children": [
|
||||
@ -1804,7 +1804,7 @@ exports[`build chat item tree and get thread messages should get thread messages
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`build chat item tree and get thread messages should work with partial messages 1 1`] = `
|
||||
exports[`build chat item tree and get thread messages > should work with partial messages 1 1`] = `
|
||||
[
|
||||
{
|
||||
"children": [
|
||||
@ -2155,7 +2155,7 @@ exports[`build chat item tree and get thread messages should work with partial m
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`build chat item tree and get thread messages should work with partial messages 2 1`] = `
|
||||
exports[`build chat item tree and get thread messages > should work with partial messages 2 1`] = `
|
||||
[
|
||||
{
|
||||
"children": [
|
||||
@ -2327,7 +2327,7 @@ exports[`build chat item tree and get thread messages should work with partial m
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`build chat item tree and get thread messages should work with real world messages 1`] = `
|
||||
exports[`build chat item tree and get thread messages > should work with real world messages 1`] = `
|
||||
[
|
||||
{
|
||||
"children": [
|
||||
|
||||
@ -26,7 +26,7 @@ describe('Checkbox Component', () => {
|
||||
})
|
||||
|
||||
it('handles click events when not disabled', () => {
|
||||
const onCheck = jest.fn()
|
||||
const onCheck = vi.fn()
|
||||
render(<Checkbox {...mockProps} onCheck={onCheck} />)
|
||||
const checkbox = screen.getByTestId('checkbox-test')
|
||||
|
||||
@ -35,7 +35,7 @@ describe('Checkbox Component', () => {
|
||||
})
|
||||
|
||||
it('does not handle click events when disabled', () => {
|
||||
const onCheck = jest.fn()
|
||||
const onCheck = vi.fn()
|
||||
render(<Checkbox {...mockProps} disabled onCheck={onCheck} />)
|
||||
const checkbox = screen.getByTestId('checkbox-test')
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ import dayjs from '../utils/dayjs'
|
||||
import { isDayjsObject } from '../utils/dayjs'
|
||||
import type { TimePickerProps } from '../types'
|
||||
|
||||
jest.mock('react-i18next', () => ({
|
||||
vi.mock('react-i18next', () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => {
|
||||
if (key === 'time.defaultPlaceholder') return 'Pick a time...'
|
||||
@ -17,7 +17,7 @@ jest.mock('react-i18next', () => ({
|
||||
}),
|
||||
}))
|
||||
|
||||
jest.mock('@/app/components/base/portal-to-follow-elem', () => ({
|
||||
vi.mock('@/app/components/base/portal-to-follow-elem', () => ({
|
||||
PortalToFollowElem: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
||||
PortalToFollowElemTrigger: ({ children, onClick }: { children: React.ReactNode, onClick: (e: React.MouseEvent) => void }) => (
|
||||
<div onClick={onClick}>{children}</div>
|
||||
@ -27,27 +27,22 @@ jest.mock('@/app/components/base/portal-to-follow-elem', () => ({
|
||||
),
|
||||
}))
|
||||
|
||||
jest.mock('./options', () => () => <div data-testid="time-options" />)
|
||||
jest.mock('./header', () => () => <div data-testid="time-header" />)
|
||||
jest.mock('@/app/components/base/timezone-label', () => {
|
||||
return function MockTimezoneLabel({ timezone, inline, className }: { timezone: string, inline?: boolean, className?: string }) {
|
||||
return (
|
||||
<span data-testid="timezone-label" data-timezone={timezone} data-inline={inline} className={className}>
|
||||
UTC+8
|
||||
</span>
|
||||
)
|
||||
}
|
||||
})
|
||||
vi.mock('./options', () => ({
|
||||
default: () => <div data-testid="time-options" />,
|
||||
}))
|
||||
vi.mock('./header', () => ({
|
||||
default: () => <div data-testid="time-header" />,
|
||||
}))
|
||||
|
||||
describe('TimePicker', () => {
|
||||
const baseProps: Pick<TimePickerProps, 'onChange' | 'onClear' | 'value'> = {
|
||||
onChange: jest.fn(),
|
||||
onClear: jest.fn(),
|
||||
onChange: vi.fn(),
|
||||
onClear: vi.fn(),
|
||||
value: undefined,
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
test('renders formatted value for string input (Issue #26692 regression)', () => {
|
||||
@ -86,7 +81,7 @@ describe('TimePicker', () => {
|
||||
})
|
||||
|
||||
test('selecting current time emits timezone-aware value', () => {
|
||||
const onChange = jest.fn()
|
||||
const onChange = vi.fn()
|
||||
render(
|
||||
<TimePicker
|
||||
{...baseProps}
|
||||
@ -114,7 +109,7 @@ describe('TimePicker', () => {
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.queryByTestId('timezone-label')).not.toBeInTheDocument()
|
||||
expect(screen.queryByTitle(/Timezone: Asia\/Shanghai/)).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
test('should not display timezone label when showTimezone is false', () => {
|
||||
@ -127,7 +122,7 @@ describe('TimePicker', () => {
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.queryByTestId('timezone-label')).not.toBeInTheDocument()
|
||||
expect(screen.queryByTitle(/Timezone: Asia\/Shanghai/)).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
test('should display timezone label when showTimezone is true', () => {
|
||||
@ -140,23 +135,9 @@ describe('TimePicker', () => {
|
||||
/>,
|
||||
)
|
||||
|
||||
const timezoneLabel = screen.getByTestId('timezone-label')
|
||||
const timezoneLabel = screen.getByTitle(/Timezone: Asia\/Shanghai/)
|
||||
expect(timezoneLabel).toBeInTheDocument()
|
||||
expect(timezoneLabel).toHaveAttribute('data-timezone', 'Asia/Shanghai')
|
||||
})
|
||||
|
||||
test('should pass inline prop to timezone label', () => {
|
||||
render(
|
||||
<TimePicker
|
||||
{...baseProps}
|
||||
value="12:00 AM"
|
||||
timezone="America/New_York"
|
||||
showTimezone={true}
|
||||
/>,
|
||||
)
|
||||
|
||||
const timezoneLabel = screen.getByTestId('timezone-label')
|
||||
expect(timezoneLabel).toHaveAttribute('data-inline', 'true')
|
||||
expect(timezoneLabel).toHaveTextContent(/UTC[+-]\d+/)
|
||||
})
|
||||
|
||||
test('should not display timezone label when showTimezone is true but timezone is not provided', () => {
|
||||
@ -168,21 +149,7 @@ describe('TimePicker', () => {
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.queryByTestId('timezone-label')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
test('should apply shrink-0 and text-xs classes to timezone label', () => {
|
||||
render(
|
||||
<TimePicker
|
||||
{...baseProps}
|
||||
value="12:00 AM"
|
||||
timezone="Europe/London"
|
||||
showTimezone={true}
|
||||
/>,
|
||||
)
|
||||
|
||||
const timezoneLabel = screen.getByTestId('timezone-label')
|
||||
expect(timezoneLabel).toHaveClass('shrink-0', 'text-xs')
|
||||
expect(screen.queryByTitle(/Timezone:/)).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { render } from '@testing-library/react'
|
||||
import '@testing-library/jest-dom'
|
||||
import Divider from './index'
|
||||
|
||||
describe('Divider', () => {
|
||||
|
||||
@ -7,7 +7,7 @@ import type { IDrawerProps } from './index'
|
||||
let capturedDialogOnClose: (() => void) | null = null
|
||||
|
||||
// Mock @headlessui/react
|
||||
jest.mock('@headlessui/react', () => ({
|
||||
vi.mock('@headlessui/react', () => ({
|
||||
Dialog: ({ children, open, onClose, className, unmount }: {
|
||||
children: React.ReactNode
|
||||
open: boolean
|
||||
@ -55,7 +55,7 @@ jest.mock('@headlessui/react', () => ({
|
||||
}))
|
||||
|
||||
// Mock XMarkIcon
|
||||
jest.mock('@heroicons/react/24/outline', () => ({
|
||||
vi.mock('@heroicons/react/24/outline', () => ({
|
||||
XMarkIcon: ({ className, onClick }: { className: string; onClick?: () => void }) => (
|
||||
<svg data-testid="close-icon" className={className} onClick={onClick} />
|
||||
),
|
||||
@ -64,7 +64,7 @@ jest.mock('@heroicons/react/24/outline', () => ({
|
||||
// Helper function to render Drawer with default props
|
||||
const defaultProps: IDrawerProps = {
|
||||
isOpen: true,
|
||||
onClose: jest.fn(),
|
||||
onClose: vi.fn(),
|
||||
children: <div data-testid="drawer-content">Content</div>,
|
||||
}
|
||||
|
||||
@ -75,7 +75,7 @@ const renderDrawer = (props: Partial<IDrawerProps> = {}) => {
|
||||
|
||||
describe('Drawer', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
capturedDialogOnClose = null
|
||||
})
|
||||
|
||||
@ -188,7 +188,7 @@ describe('Drawer', () => {
|
||||
|
||||
it('should call onClose when close icon is clicked', () => {
|
||||
// Arrange
|
||||
const onClose = jest.fn()
|
||||
const onClose = vi.fn()
|
||||
renderDrawer({ showClose: true, onClose })
|
||||
|
||||
// Act
|
||||
@ -237,7 +237,7 @@ describe('Drawer', () => {
|
||||
|
||||
it('should call onClose when backdrop is clicked and clickOutsideNotOpen is false', () => {
|
||||
// Arrange
|
||||
const onClose = jest.fn()
|
||||
const onClose = vi.fn()
|
||||
renderDrawer({ onClose, clickOutsideNotOpen: false })
|
||||
|
||||
// Act
|
||||
@ -249,7 +249,7 @@ describe('Drawer', () => {
|
||||
|
||||
it('should not call onClose when backdrop is clicked and clickOutsideNotOpen is true', () => {
|
||||
// Arrange
|
||||
const onClose = jest.fn()
|
||||
const onClose = vi.fn()
|
||||
renderDrawer({ onClose, clickOutsideNotOpen: true })
|
||||
|
||||
// Act
|
||||
@ -294,7 +294,7 @@ describe('Drawer', () => {
|
||||
|
||||
it('should call onCancel when cancel button is clicked', () => {
|
||||
// Arrange
|
||||
const onCancel = jest.fn()
|
||||
const onCancel = vi.fn()
|
||||
renderDrawer({ onCancel })
|
||||
|
||||
// Act
|
||||
@ -307,7 +307,7 @@ describe('Drawer', () => {
|
||||
|
||||
it('should call onOk when save button is clicked', () => {
|
||||
// Arrange
|
||||
const onOk = jest.fn()
|
||||
const onOk = vi.fn()
|
||||
renderDrawer({ onOk })
|
||||
|
||||
// Act
|
||||
@ -496,7 +496,7 @@ describe('Drawer', () => {
|
||||
|
||||
it('should handle rapid open/close toggles', () => {
|
||||
// Arrange
|
||||
const onClose = jest.fn()
|
||||
const onClose = vi.fn()
|
||||
const { rerender } = render(
|
||||
<Drawer {...defaultProps} isOpen={true} onClose={onClose}>
|
||||
<div>Content</div>
|
||||
@ -556,7 +556,7 @@ describe('Drawer', () => {
|
||||
// Arrange
|
||||
const minimalProps: IDrawerProps = {
|
||||
isOpen: true,
|
||||
onClose: jest.fn(),
|
||||
onClose: vi.fn(),
|
||||
children: <div>Minimal Content</div>,
|
||||
}
|
||||
|
||||
@ -582,7 +582,7 @@ describe('Drawer', () => {
|
||||
|
||||
it('should handle noOverlay with clickOutsideNotOpen', () => {
|
||||
// Arrange
|
||||
const onClose = jest.fn()
|
||||
const onClose = vi.fn()
|
||||
|
||||
// Act
|
||||
renderDrawer({
|
||||
@ -600,7 +600,7 @@ describe('Drawer', () => {
|
||||
describe('Dialog onClose Callback', () => {
|
||||
it('should call onClose when Dialog triggers close and clickOutsideNotOpen is false', () => {
|
||||
// Arrange
|
||||
const onClose = jest.fn()
|
||||
const onClose = vi.fn()
|
||||
renderDrawer({ onClose, clickOutsideNotOpen: false })
|
||||
|
||||
// Act - Simulate Dialog's onClose (e.g., pressing Escape)
|
||||
@ -612,7 +612,7 @@ describe('Drawer', () => {
|
||||
|
||||
it('should not call onClose when Dialog triggers close and clickOutsideNotOpen is true', () => {
|
||||
// Arrange
|
||||
const onClose = jest.fn()
|
||||
const onClose = vi.fn()
|
||||
renderDrawer({ onClose, clickOutsideNotOpen: true })
|
||||
|
||||
// Act - Simulate Dialog's onClose (e.g., pressing Escape)
|
||||
@ -624,7 +624,7 @@ describe('Drawer', () => {
|
||||
|
||||
it('should call onClose by default when Dialog triggers close', () => {
|
||||
// Arrange
|
||||
const onClose = jest.fn()
|
||||
const onClose = vi.fn()
|
||||
renderDrawer({ onClose })
|
||||
|
||||
// Act
|
||||
@ -639,7 +639,7 @@ describe('Drawer', () => {
|
||||
describe('Event Handler Interactions', () => {
|
||||
it('should handle multiple consecutive close icon clicks', () => {
|
||||
// Arrange
|
||||
const onClose = jest.fn()
|
||||
const onClose = vi.fn()
|
||||
renderDrawer({ showClose: true, onClose })
|
||||
|
||||
// Act
|
||||
@ -654,7 +654,7 @@ describe('Drawer', () => {
|
||||
|
||||
it('should handle onCancel and onOk being the same function', () => {
|
||||
// Arrange
|
||||
const handler = jest.fn()
|
||||
const handler = vi.fn()
|
||||
renderDrawer({ onCancel: handler, onOk: handler })
|
||||
|
||||
// Act
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import type { MockInstance } from 'vitest'
|
||||
import mime from 'mime'
|
||||
import { upload } from '@/service/base'
|
||||
import {
|
||||
@ -19,32 +20,32 @@ import { SupportUploadFileTypes } from '@/app/components/workflow/types'
|
||||
import { TransferMethod } from '@/types/app'
|
||||
import { FILE_EXTS } from '../prompt-editor/constants'
|
||||
|
||||
jest.mock('mime', () => ({
|
||||
vi.mock('mime', () => ({
|
||||
__esModule: true,
|
||||
default: {
|
||||
getAllExtensions: jest.fn(),
|
||||
getAllExtensions: vi.fn(),
|
||||
},
|
||||
}))
|
||||
|
||||
jest.mock('@/service/base', () => ({
|
||||
upload: jest.fn(),
|
||||
vi.mock('@/service/base', () => ({
|
||||
upload: vi.fn(),
|
||||
}))
|
||||
|
||||
describe('file-uploader utils', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('fileUpload', () => {
|
||||
it('should handle successful file upload', () => {
|
||||
const mockFile = new File(['test'], 'test.txt')
|
||||
const mockCallbacks = {
|
||||
onProgressCallback: jest.fn(),
|
||||
onSuccessCallback: jest.fn(),
|
||||
onErrorCallback: jest.fn(),
|
||||
onProgressCallback: vi.fn(),
|
||||
onSuccessCallback: vi.fn(),
|
||||
onErrorCallback: vi.fn(),
|
||||
}
|
||||
|
||||
jest.mocked(upload).mockResolvedValue({ id: '123' })
|
||||
vi.mocked(upload).mockResolvedValue({ id: '123' })
|
||||
|
||||
fileUpload({
|
||||
file: mockFile,
|
||||
@ -57,27 +58,27 @@ describe('file-uploader utils', () => {
|
||||
|
||||
describe('getFileExtension', () => {
|
||||
it('should get extension from mimetype', () => {
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['pdf']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['pdf']))
|
||||
expect(getFileExtension('file', 'application/pdf')).toBe('pdf')
|
||||
})
|
||||
|
||||
it('should get extension from mimetype and file name 1', () => {
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['pdf']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['pdf']))
|
||||
expect(getFileExtension('file.pdf', 'application/pdf')).toBe('pdf')
|
||||
})
|
||||
|
||||
it('should get extension from mimetype with multiple ext candidates with filename hint', () => {
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['der', 'crt', 'pem']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['der', 'crt', 'pem']))
|
||||
expect(getFileExtension('file.pem', 'application/x-x509-ca-cert')).toBe('pem')
|
||||
})
|
||||
|
||||
it('should get extension from mimetype with multiple ext candidates without filename hint', () => {
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['der', 'crt', 'pem']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['der', 'crt', 'pem']))
|
||||
expect(getFileExtension('file', 'application/x-x509-ca-cert')).toBe('der')
|
||||
})
|
||||
|
||||
it('should get extension from filename if mimetype fails', () => {
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(null)
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(null)
|
||||
expect(getFileExtension('file.txt', '')).toBe('txt')
|
||||
expect(getFileExtension('file.txt.docx', '')).toBe('docx')
|
||||
expect(getFileExtension('file', '')).toBe('')
|
||||
@ -90,157 +91,157 @@ describe('file-uploader utils', () => {
|
||||
|
||||
describe('getFileAppearanceType', () => {
|
||||
it('should identify gif files', () => {
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['gif']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['gif']))
|
||||
expect(getFileAppearanceType('image.gif', 'image/gif'))
|
||||
.toBe(FileAppearanceTypeEnum.gif)
|
||||
})
|
||||
|
||||
it('should identify image files', () => {
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['jpg']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['jpg']))
|
||||
expect(getFileAppearanceType('image.jpg', 'image/jpeg'))
|
||||
.toBe(FileAppearanceTypeEnum.image)
|
||||
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['jpeg']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['jpeg']))
|
||||
expect(getFileAppearanceType('image.jpeg', 'image/jpeg'))
|
||||
.toBe(FileAppearanceTypeEnum.image)
|
||||
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['png']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['png']))
|
||||
expect(getFileAppearanceType('image.png', 'image/png'))
|
||||
.toBe(FileAppearanceTypeEnum.image)
|
||||
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['webp']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['webp']))
|
||||
expect(getFileAppearanceType('image.webp', 'image/webp'))
|
||||
.toBe(FileAppearanceTypeEnum.image)
|
||||
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['svg']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['svg']))
|
||||
expect(getFileAppearanceType('image.svg', 'image/svgxml'))
|
||||
.toBe(FileAppearanceTypeEnum.image)
|
||||
})
|
||||
|
||||
it('should identify video files', () => {
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['mp4']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['mp4']))
|
||||
expect(getFileAppearanceType('video.mp4', 'video/mp4'))
|
||||
.toBe(FileAppearanceTypeEnum.video)
|
||||
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['mov']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['mov']))
|
||||
expect(getFileAppearanceType('video.mov', 'video/quicktime'))
|
||||
.toBe(FileAppearanceTypeEnum.video)
|
||||
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['mpeg']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['mpeg']))
|
||||
expect(getFileAppearanceType('video.mpeg', 'video/mpeg'))
|
||||
.toBe(FileAppearanceTypeEnum.video)
|
||||
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['webm']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['webm']))
|
||||
expect(getFileAppearanceType('video.web', 'video/webm'))
|
||||
.toBe(FileAppearanceTypeEnum.video)
|
||||
})
|
||||
|
||||
it('should identify audio files', () => {
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['mp3']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['mp3']))
|
||||
expect(getFileAppearanceType('audio.mp3', 'audio/mpeg'))
|
||||
.toBe(FileAppearanceTypeEnum.audio)
|
||||
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['m4a']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['m4a']))
|
||||
expect(getFileAppearanceType('audio.m4a', 'audio/mp4'))
|
||||
.toBe(FileAppearanceTypeEnum.audio)
|
||||
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['wav']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['wav']))
|
||||
expect(getFileAppearanceType('audio.wav', 'audio/vnd.wav'))
|
||||
.toBe(FileAppearanceTypeEnum.audio)
|
||||
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['amr']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['amr']))
|
||||
expect(getFileAppearanceType('audio.amr', 'audio/AMR'))
|
||||
.toBe(FileAppearanceTypeEnum.audio)
|
||||
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['mpga']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['mpga']))
|
||||
expect(getFileAppearanceType('audio.mpga', 'audio/mpeg'))
|
||||
.toBe(FileAppearanceTypeEnum.audio)
|
||||
})
|
||||
|
||||
it('should identify code files', () => {
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['html']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['html']))
|
||||
expect(getFileAppearanceType('index.html', 'text/html'))
|
||||
.toBe(FileAppearanceTypeEnum.code)
|
||||
})
|
||||
|
||||
it('should identify PDF files', () => {
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['pdf']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['pdf']))
|
||||
expect(getFileAppearanceType('doc.pdf', 'application/pdf'))
|
||||
.toBe(FileAppearanceTypeEnum.pdf)
|
||||
})
|
||||
|
||||
it('should identify markdown files', () => {
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['md']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['md']))
|
||||
expect(getFileAppearanceType('file.md', 'text/markdown'))
|
||||
.toBe(FileAppearanceTypeEnum.markdown)
|
||||
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['markdown']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['markdown']))
|
||||
expect(getFileAppearanceType('file.markdown', 'text/markdown'))
|
||||
.toBe(FileAppearanceTypeEnum.markdown)
|
||||
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['mdx']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['mdx']))
|
||||
expect(getFileAppearanceType('file.mdx', 'text/mdx'))
|
||||
.toBe(FileAppearanceTypeEnum.markdown)
|
||||
})
|
||||
|
||||
it('should identify excel files', () => {
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['xlsx']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['xlsx']))
|
||||
expect(getFileAppearanceType('doc.xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'))
|
||||
.toBe(FileAppearanceTypeEnum.excel)
|
||||
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['xls']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['xls']))
|
||||
expect(getFileAppearanceType('doc.xls', 'application/vnd.ms-excel'))
|
||||
.toBe(FileAppearanceTypeEnum.excel)
|
||||
})
|
||||
|
||||
it('should identify word files', () => {
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['doc']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['doc']))
|
||||
expect(getFileAppearanceType('doc.doc', 'application/msword'))
|
||||
.toBe(FileAppearanceTypeEnum.word)
|
||||
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['docx']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['docx']))
|
||||
expect(getFileAppearanceType('doc.docx', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'))
|
||||
.toBe(FileAppearanceTypeEnum.word)
|
||||
})
|
||||
|
||||
it('should identify word files', () => {
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['ppt']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['ppt']))
|
||||
expect(getFileAppearanceType('doc.ppt', 'application/vnd.ms-powerpoint'))
|
||||
.toBe(FileAppearanceTypeEnum.ppt)
|
||||
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['pptx']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['pptx']))
|
||||
expect(getFileAppearanceType('doc.pptx', 'application/vnd.openxmlformats-officedocument.presentationml.presentation'))
|
||||
.toBe(FileAppearanceTypeEnum.ppt)
|
||||
})
|
||||
|
||||
it('should identify document files', () => {
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['txt']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['txt']))
|
||||
expect(getFileAppearanceType('file.txt', 'text/plain'))
|
||||
.toBe(FileAppearanceTypeEnum.document)
|
||||
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['csv']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['csv']))
|
||||
expect(getFileAppearanceType('file.csv', 'text/csv'))
|
||||
.toBe(FileAppearanceTypeEnum.document)
|
||||
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['msg']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['msg']))
|
||||
expect(getFileAppearanceType('file.msg', 'application/vnd.ms-outlook'))
|
||||
.toBe(FileAppearanceTypeEnum.document)
|
||||
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['eml']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['eml']))
|
||||
expect(getFileAppearanceType('file.eml', 'message/rfc822'))
|
||||
.toBe(FileAppearanceTypeEnum.document)
|
||||
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['xml']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['xml']))
|
||||
expect(getFileAppearanceType('file.xml', 'application/rssxml'))
|
||||
.toBe(FileAppearanceTypeEnum.document)
|
||||
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['epub']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['epub']))
|
||||
expect(getFileAppearanceType('file.epub', 'application/epubzip'))
|
||||
.toBe(FileAppearanceTypeEnum.document)
|
||||
})
|
||||
|
||||
it('should handle null mime extension', () => {
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(null)
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(null)
|
||||
expect(getFileAppearanceType('file.txt', 'text/plain'))
|
||||
.toBe(FileAppearanceTypeEnum.document)
|
||||
})
|
||||
@ -284,7 +285,7 @@ describe('file-uploader utils', () => {
|
||||
|
||||
describe('getProcessedFilesFromResponse', () => {
|
||||
beforeEach(() => {
|
||||
jest.mocked(mime.getAllExtensions).mockImplementation((mimeType: string) => {
|
||||
vi.mocked(mime.getAllExtensions).mockImplementation((mimeType: string) => {
|
||||
const mimeMap: Record<string, Set<string>> = {
|
||||
'image/jpeg': new Set(['jpg', 'jpeg']),
|
||||
'image/png': new Set(['png']),
|
||||
@ -601,7 +602,7 @@ describe('file-uploader utils', () => {
|
||||
|
||||
describe('isAllowedFileExtension', () => {
|
||||
it('should validate allowed file extensions', () => {
|
||||
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['pdf']))
|
||||
vi.mocked(mime.getAllExtensions).mockReturnValue(new Set(['pdf']))
|
||||
expect(isAllowedFileExtension(
|
||||
'test.pdf',
|
||||
'application/pdf',
|
||||
@ -785,9 +786,9 @@ describe('file-uploader utils', () => {
|
||||
|
||||
describe('downloadFile', () => {
|
||||
let mockAnchor: HTMLAnchorElement
|
||||
let createElementMock: jest.SpyInstance
|
||||
let appendChildMock: jest.SpyInstance
|
||||
let removeChildMock: jest.SpyInstance
|
||||
let createElementMock: MockInstance
|
||||
let appendChildMock: MockInstance
|
||||
let removeChildMock: MockInstance
|
||||
|
||||
beforeEach(() => {
|
||||
// Mock createElement and appendChild
|
||||
@ -797,20 +798,20 @@ describe('file-uploader utils', () => {
|
||||
style: { display: '' },
|
||||
target: '',
|
||||
title: '',
|
||||
click: jest.fn(),
|
||||
click: vi.fn(),
|
||||
} as unknown as HTMLAnchorElement
|
||||
|
||||
createElementMock = jest.spyOn(document, 'createElement').mockReturnValue(mockAnchor as any)
|
||||
appendChildMock = jest.spyOn(document.body, 'appendChild').mockImplementation((node: Node) => {
|
||||
createElementMock = vi.spyOn(document, 'createElement').mockReturnValue(mockAnchor as any)
|
||||
appendChildMock = vi.spyOn(document.body, 'appendChild').mockImplementation((node: Node) => {
|
||||
return node
|
||||
})
|
||||
removeChildMock = jest.spyOn(document.body, 'removeChild').mockImplementation((node: Node) => {
|
||||
removeChildMock = vi.spyOn(document.body, 'removeChild').mockImplementation((node: Node) => {
|
||||
return node
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks()
|
||||
vi.resetAllMocks()
|
||||
})
|
||||
|
||||
it('should create and trigger download with correct attributes', () => {
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import '@testing-library/jest-dom'
|
||||
import React from 'react'
|
||||
import type { IconData } from './IconBase'
|
||||
import IconBase from './IconBase'
|
||||
import * as utils from './utils'
|
||||
|
||||
// Mock the utils module
|
||||
jest.mock('./utils', () => ({
|
||||
generate: jest.fn((icon, key, props) => (
|
||||
vi.mock('./utils', () => ({
|
||||
generate: vi.fn((icon, key, props) => (
|
||||
<svg
|
||||
data-testid="mock-svg"
|
||||
key={key}
|
||||
@ -25,7 +24,7 @@ describe('IconBase Component', () => {
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('renders properly with required props', () => {
|
||||
@ -48,7 +47,7 @@ describe('IconBase Component', () => {
|
||||
})
|
||||
|
||||
it('handles onClick events', () => {
|
||||
const handleClick = jest.fn()
|
||||
const handleClick = vi.fn()
|
||||
render(<IconBase data={mockData} onClick={handleClick} />)
|
||||
const svg = screen.getByTestId('mock-svg')
|
||||
fireEvent.click(svg)
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import type { AbstractNode } from './utils'
|
||||
import { generate, normalizeAttrs } from './utils'
|
||||
import { render } from '@testing-library/react'
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
describe('generate icon base utils', () => {
|
||||
describe('normalizeAttrs', () => {
|
||||
@ -41,7 +40,7 @@ describe('generate icon base utils', () => {
|
||||
const { container } = render(generate(node, 'key'))
|
||||
// to svg element
|
||||
expect(container.firstChild).toHaveClass('container')
|
||||
expect(container.querySelector('span')).toHaveStyle({ color: 'blue' })
|
||||
expect(container.querySelector('span')).toHaveStyle({ color: 'rgb(0, 0, 255)' })
|
||||
})
|
||||
|
||||
// add not has children
|
||||
|
||||
@ -3,7 +3,7 @@ import { cleanup, fireEvent, render } from '@testing-library/react'
|
||||
import InlineDeleteConfirm from './index'
|
||||
|
||||
// Mock react-i18next
|
||||
jest.mock('react-i18next', () => ({
|
||||
vi.mock('react-i18next', () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => {
|
||||
const translations: Record<string, string> = {
|
||||
@ -22,8 +22,8 @@ afterEach(cleanup)
|
||||
describe('InlineDeleteConfirm', () => {
|
||||
describe('Rendering', () => {
|
||||
test('should render with default text', () => {
|
||||
const onConfirm = jest.fn()
|
||||
const onCancel = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onCancel = vi.fn()
|
||||
const { getByText } = render(
|
||||
<InlineDeleteConfirm onConfirm={onConfirm} onCancel={onCancel} />,
|
||||
)
|
||||
@ -34,8 +34,8 @@ describe('InlineDeleteConfirm', () => {
|
||||
})
|
||||
|
||||
test('should render with custom text', () => {
|
||||
const onConfirm = jest.fn()
|
||||
const onCancel = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onCancel = vi.fn()
|
||||
const { getByText } = render(
|
||||
<InlineDeleteConfirm
|
||||
title="Remove?"
|
||||
@ -52,8 +52,8 @@ describe('InlineDeleteConfirm', () => {
|
||||
})
|
||||
|
||||
test('should have proper ARIA attributes', () => {
|
||||
const onConfirm = jest.fn()
|
||||
const onCancel = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onCancel = vi.fn()
|
||||
const { container } = render(
|
||||
<InlineDeleteConfirm onConfirm={onConfirm} onCancel={onCancel} />,
|
||||
)
|
||||
@ -66,8 +66,8 @@ describe('InlineDeleteConfirm', () => {
|
||||
|
||||
describe('Button interactions', () => {
|
||||
test('should call onCancel when cancel button is clicked', () => {
|
||||
const onConfirm = jest.fn()
|
||||
const onCancel = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onCancel = vi.fn()
|
||||
const { getByText } = render(
|
||||
<InlineDeleteConfirm onConfirm={onConfirm} onCancel={onCancel} />,
|
||||
)
|
||||
@ -78,8 +78,8 @@ describe('InlineDeleteConfirm', () => {
|
||||
})
|
||||
|
||||
test('should call onConfirm when confirm button is clicked', () => {
|
||||
const onConfirm = jest.fn()
|
||||
const onCancel = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onCancel = vi.fn()
|
||||
const { getByText } = render(
|
||||
<InlineDeleteConfirm onConfirm={onConfirm} onCancel={onCancel} />,
|
||||
)
|
||||
@ -92,8 +92,8 @@ describe('InlineDeleteConfirm', () => {
|
||||
|
||||
describe('Variant prop', () => {
|
||||
test('should render with delete variant by default', () => {
|
||||
const onConfirm = jest.fn()
|
||||
const onCancel = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onCancel = vi.fn()
|
||||
const { getByText } = render(
|
||||
<InlineDeleteConfirm onConfirm={onConfirm} onCancel={onCancel} />,
|
||||
)
|
||||
@ -103,8 +103,8 @@ describe('InlineDeleteConfirm', () => {
|
||||
})
|
||||
|
||||
test('should render without destructive class for warning variant', () => {
|
||||
const onConfirm = jest.fn()
|
||||
const onCancel = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onCancel = vi.fn()
|
||||
const { getByText } = render(
|
||||
<InlineDeleteConfirm
|
||||
variant="warning"
|
||||
@ -118,8 +118,8 @@ describe('InlineDeleteConfirm', () => {
|
||||
})
|
||||
|
||||
test('should render without destructive class for info variant', () => {
|
||||
const onConfirm = jest.fn()
|
||||
const onCancel = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onCancel = vi.fn()
|
||||
const { getByText } = render(
|
||||
<InlineDeleteConfirm
|
||||
variant="info"
|
||||
@ -135,8 +135,8 @@ describe('InlineDeleteConfirm', () => {
|
||||
|
||||
describe('Custom className', () => {
|
||||
test('should apply custom className to wrapper', () => {
|
||||
const onConfirm = jest.fn()
|
||||
const onCancel = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onCancel = vi.fn()
|
||||
const { container } = render(
|
||||
<InlineDeleteConfirm
|
||||
className="custom-class"
|
||||
|
||||
@ -3,11 +3,11 @@ import { InputNumber } from './index'
|
||||
|
||||
describe('InputNumber Component', () => {
|
||||
const defaultProps = {
|
||||
onChange: jest.fn(),
|
||||
onChange: vi.fn(),
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks()
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('renders input with default values', () => {
|
||||
|
||||
@ -1,13 +1,17 @@
|
||||
import React from 'react'
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import '@testing-library/jest-dom'
|
||||
import InputWithCopy from './index'
|
||||
|
||||
// Create a mock function that we can track using vi.hoisted
|
||||
const mockCopyToClipboard = vi.hoisted(() => vi.fn(() => true))
|
||||
|
||||
// Mock the copy-to-clipboard library
|
||||
jest.mock('copy-to-clipboard', () => jest.fn(() => true))
|
||||
vi.mock('copy-to-clipboard', () => ({
|
||||
default: mockCopyToClipboard,
|
||||
}))
|
||||
|
||||
// Mock the i18n hook
|
||||
jest.mock('react-i18next', () => ({
|
||||
vi.mock('react-i18next', () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => {
|
||||
const translations: Record<string, string> = {
|
||||
@ -22,17 +26,18 @@ jest.mock('react-i18next', () => ({
|
||||
}))
|
||||
|
||||
// Mock lodash-es debounce
|
||||
jest.mock('lodash-es', () => ({
|
||||
vi.mock('lodash-es', () => ({
|
||||
debounce: (fn: any) => fn,
|
||||
}))
|
||||
|
||||
describe('InputWithCopy component', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
mockCopyToClipboard.mockClear()
|
||||
})
|
||||
|
||||
it('renders correctly with default props', () => {
|
||||
const mockOnChange = jest.fn()
|
||||
const mockOnChange = vi.fn()
|
||||
render(<InputWithCopy value="test value" onChange={mockOnChange} />)
|
||||
const input = screen.getByDisplayValue('test value')
|
||||
const copyButton = screen.getByRole('button')
|
||||
@ -41,7 +46,7 @@ describe('InputWithCopy component', () => {
|
||||
})
|
||||
|
||||
it('hides copy button when showCopyButton is false', () => {
|
||||
const mockOnChange = jest.fn()
|
||||
const mockOnChange = vi.fn()
|
||||
render(<InputWithCopy value="test value" onChange={mockOnChange} showCopyButton={false} />)
|
||||
const input = screen.getByDisplayValue('test value')
|
||||
const copyButton = screen.queryByRole('button')
|
||||
@ -50,30 +55,28 @@ describe('InputWithCopy component', () => {
|
||||
})
|
||||
|
||||
it('copies input value when copy button is clicked', async () => {
|
||||
const copyToClipboard = require('copy-to-clipboard')
|
||||
const mockOnChange = jest.fn()
|
||||
const mockOnChange = vi.fn()
|
||||
render(<InputWithCopy value="test value" onChange={mockOnChange} />)
|
||||
|
||||
const copyButton = screen.getByRole('button')
|
||||
fireEvent.click(copyButton)
|
||||
|
||||
expect(copyToClipboard).toHaveBeenCalledWith('test value')
|
||||
expect(mockCopyToClipboard).toHaveBeenCalledWith('test value')
|
||||
})
|
||||
|
||||
it('copies custom value when copyValue prop is provided', async () => {
|
||||
const copyToClipboard = require('copy-to-clipboard')
|
||||
const mockOnChange = jest.fn()
|
||||
const mockOnChange = vi.fn()
|
||||
render(<InputWithCopy value="display value" onChange={mockOnChange} copyValue="custom copy value" />)
|
||||
|
||||
const copyButton = screen.getByRole('button')
|
||||
fireEvent.click(copyButton)
|
||||
|
||||
expect(copyToClipboard).toHaveBeenCalledWith('custom copy value')
|
||||
expect(mockCopyToClipboard).toHaveBeenCalledWith('custom copy value')
|
||||
})
|
||||
|
||||
it('calls onCopy callback when copy button is clicked', async () => {
|
||||
const onCopyMock = jest.fn()
|
||||
const mockOnChange = jest.fn()
|
||||
const onCopyMock = vi.fn()
|
||||
const mockOnChange = vi.fn()
|
||||
render(<InputWithCopy value="test value" onChange={mockOnChange} onCopy={onCopyMock} />)
|
||||
|
||||
const copyButton = screen.getByRole('button')
|
||||
@ -83,7 +86,7 @@ describe('InputWithCopy component', () => {
|
||||
})
|
||||
|
||||
it('shows copied state after successful copy', async () => {
|
||||
const mockOnChange = jest.fn()
|
||||
const mockOnChange = vi.fn()
|
||||
render(<InputWithCopy value="test value" onChange={mockOnChange} />)
|
||||
|
||||
const copyButton = screen.getByRole('button')
|
||||
@ -99,7 +102,7 @@ describe('InputWithCopy component', () => {
|
||||
})
|
||||
|
||||
it('passes through all input props correctly', () => {
|
||||
const mockOnChange = jest.fn()
|
||||
const mockOnChange = vi.fn()
|
||||
render(
|
||||
<InputWithCopy
|
||||
value="test value"
|
||||
@ -119,8 +122,7 @@ describe('InputWithCopy component', () => {
|
||||
})
|
||||
|
||||
it('handles empty value correctly', () => {
|
||||
const copyToClipboard = require('copy-to-clipboard')
|
||||
const mockOnChange = jest.fn()
|
||||
const mockOnChange = vi.fn()
|
||||
render(<InputWithCopy value="" onChange={mockOnChange} />)
|
||||
const input = screen.getByRole('textbox')
|
||||
const copyButton = screen.getByRole('button')
|
||||
@ -129,11 +131,11 @@ describe('InputWithCopy component', () => {
|
||||
expect(copyButton).toBeInTheDocument()
|
||||
|
||||
fireEvent.click(copyButton)
|
||||
expect(copyToClipboard).toHaveBeenCalledWith('')
|
||||
expect(mockCopyToClipboard).toHaveBeenCalledWith('')
|
||||
})
|
||||
|
||||
it('maintains focus on input after copy', async () => {
|
||||
const mockOnChange = jest.fn()
|
||||
const mockOnChange = vi.fn()
|
||||
render(<InputWithCopy value="test value" onChange={mockOnChange} />)
|
||||
|
||||
const input = screen.getByDisplayValue('test value')
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
import React from 'react'
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import '@testing-library/jest-dom'
|
||||
import Input, { inputVariants } from './index'
|
||||
|
||||
// Mock the i18n hook
|
||||
jest.mock('react-i18next', () => ({
|
||||
vi.mock('react-i18next', () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => {
|
||||
const translations: Record<string, string> = {
|
||||
@ -71,7 +70,7 @@ describe('Input component', () => {
|
||||
})
|
||||
|
||||
it('calls onClear when clear icon is clicked', () => {
|
||||
const onClear = jest.fn()
|
||||
const onClear = vi.fn()
|
||||
render(<Input showClearIcon value="test" onClear={onClear} />)
|
||||
const clearIconContainer = document.querySelector('.group')
|
||||
fireEvent.click(clearIconContainer!)
|
||||
@ -106,7 +105,7 @@ describe('Input component', () => {
|
||||
render(<Input className={customClass} styleCss={customStyle} />)
|
||||
const input = screen.getByPlaceholderText('Please input')
|
||||
expect(input).toHaveClass(customClass)
|
||||
expect(input).toHaveStyle('color: red')
|
||||
expect(input).toHaveStyle({ color: 'rgb(255, 0, 0)' })
|
||||
})
|
||||
|
||||
it('applies large size variant correctly', () => {
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import React from 'react'
|
||||
import { render } from '@testing-library/react'
|
||||
import '@testing-library/jest-dom'
|
||||
import Loading from './index'
|
||||
|
||||
describe('Loading Component', () => {
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import './style.css'
|
||||
type ILoadingProps = {
|
||||
@ -7,8 +10,15 @@ type ILoadingProps = {
|
||||
const Loading = (
|
||||
{ type = 'area' }: ILoadingProps = { type: 'area' },
|
||||
) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className={`flex w-full items-center justify-center ${type === 'app' ? 'h-full' : ''}`}>
|
||||
<div
|
||||
className={`flex w-full items-center justify-center ${type === 'app' ? 'h-full' : ''}`}
|
||||
role='status'
|
||||
aria-live='polite'
|
||||
aria-label={t('appApi.loading')}
|
||||
>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className='spin-animation'>
|
||||
<g clipPath="url(#clip0_324_2488)">
|
||||
<path d="M15 0H10C9.44772 0 9 0.447715 9 1V6C9 6.55228 9.44772 7 10 7H15C15.5523 7 16 6.55228 16 6V1C16 0.447715 15.5523 0 15 0Z" fill="#1C64F2" />
|
||||
|
||||
@ -1,8 +1,20 @@
|
||||
import React from 'react'
|
||||
import { cleanup, fireEvent, render } from '@testing-library/react'
|
||||
import '@testing-library/jest-dom'
|
||||
import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '.'
|
||||
|
||||
const useFloatingMock = vi.fn()
|
||||
|
||||
vi.mock('@floating-ui/react', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('@floating-ui/react')>()
|
||||
return {
|
||||
...actual,
|
||||
useFloating: (...args: Parameters<typeof actual.useFloating>) => {
|
||||
useFloatingMock(...args)
|
||||
return actual.useFloating(...args)
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
afterEach(cleanup)
|
||||
|
||||
describe('PortalToFollowElem', () => {
|
||||
@ -10,7 +22,7 @@ describe('PortalToFollowElem', () => {
|
||||
test('should throw error when using context outside provider', () => {
|
||||
// Suppress console.error for this test
|
||||
const originalError = console.error
|
||||
console.error = jest.fn()
|
||||
console.error = vi.fn()
|
||||
|
||||
expect(() => {
|
||||
render(
|
||||
@ -81,7 +93,7 @@ describe('PortalToFollowElem', () => {
|
||||
|
||||
describe('Controlled behavior', () => {
|
||||
test('should call onOpenChange when interaction happens', () => {
|
||||
const handleOpenChange = jest.fn()
|
||||
const handleOpenChange = vi.fn()
|
||||
|
||||
const { getByText } = render(
|
||||
<PortalToFollowElem onOpenChange={handleOpenChange} >
|
||||
@ -100,9 +112,6 @@ describe('PortalToFollowElem', () => {
|
||||
|
||||
describe('Configuration options', () => {
|
||||
test('should accept placement prop', () => {
|
||||
// Since we can't easily test actual positioning, we'll check if the prop is passed correctly
|
||||
const useFloatingMock = jest.spyOn(require('@floating-ui/react'), 'useFloating')
|
||||
|
||||
render(
|
||||
<PortalToFollowElem placement='top-start' >
|
||||
<PortalToFollowElemTrigger>Trigger</PortalToFollowElemTrigger>
|
||||
|
||||
@ -18,6 +18,9 @@ const RadioUI: FC<Props> = ({
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
role="radio"
|
||||
aria-checked={isChecked}
|
||||
aria-disabled={disabled}
|
||||
className={cn(
|
||||
'size-4 rounded-full',
|
||||
isChecked && !disabled && 'border-[5px] border-components-radio-border-checked hover:border-components-radio-border-checked-hover',
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import '@testing-library/jest-dom'
|
||||
import SegmentedControl from './index'
|
||||
|
||||
describe('SegmentedControl', () => {
|
||||
@ -15,7 +14,7 @@ describe('SegmentedControl', () => {
|
||||
{ value: 'option3', text: 'Option 3' },
|
||||
]
|
||||
|
||||
const onSelectMock = jest.fn((value: string | number | symbol) => value)
|
||||
const onSelectMock = vi.fn((value: string | number | symbol) => value)
|
||||
|
||||
beforeEach(() => {
|
||||
onSelectMock.mockClear()
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import React from 'react'
|
||||
import { render } from '@testing-library/react'
|
||||
import '@testing-library/jest-dom'
|
||||
import Spinner from './index'
|
||||
|
||||
describe('Spinner component', () => {
|
||||
|
||||
@ -3,7 +3,7 @@ import { render, screen } from '@testing-library/react'
|
||||
import TimezoneLabel from '../index'
|
||||
|
||||
// Mock the convertTimezoneToOffsetStr function
|
||||
jest.mock('@/app/components/base/date-and-time-picker/utils/dayjs', () => ({
|
||||
vi.mock('@/app/components/base/date-and-time-picker/utils/dayjs', () => ({
|
||||
convertTimezoneToOffsetStr: (timezone?: string) => {
|
||||
if (!timezone) return 'UTC+0'
|
||||
|
||||
|
||||
@ -2,12 +2,8 @@ import type { ReactNode } from 'react'
|
||||
import React from 'react'
|
||||
import { act, render, screen, waitFor } from '@testing-library/react'
|
||||
import Toast, { ToastProvider, useToastContext } from '.'
|
||||
import '@testing-library/jest-dom'
|
||||
import { noop } from 'lodash-es'
|
||||
|
||||
// Mock timers for testing timeouts
|
||||
jest.useFakeTimers()
|
||||
|
||||
const TestComponent = () => {
|
||||
const { notify, close } = useToastContext()
|
||||
|
||||
@ -22,6 +18,15 @@ const TestComponent = () => {
|
||||
}
|
||||
|
||||
describe('Toast', () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers({ shouldAdvanceTime: true })
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.runOnlyPendingTimers()
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
describe('Toast Component', () => {
|
||||
test('renders toast with correct type and message', () => {
|
||||
render(
|
||||
@ -138,7 +143,7 @@ describe('Toast', () => {
|
||||
|
||||
// Fast-forward timer
|
||||
act(() => {
|
||||
jest.advanceTimersByTime(3000) // Default for info type is 3000ms
|
||||
vi.advanceTimersByTime(3000) // Default for info type is 3000ms
|
||||
})
|
||||
|
||||
// Toast should be gone
|
||||
@ -160,7 +165,7 @@ describe('Toast', () => {
|
||||
|
||||
// Fast-forward timer
|
||||
act(() => {
|
||||
jest.advanceTimersByTime(6000) // Default for warning type is 6000ms
|
||||
vi.advanceTimersByTime(6000) // Default for warning type is 6000ms
|
||||
})
|
||||
|
||||
// Toast should be removed
|
||||
@ -170,7 +175,7 @@ describe('Toast', () => {
|
||||
})
|
||||
|
||||
test('calls onClose callback after duration', async () => {
|
||||
const onCloseMock = jest.fn()
|
||||
const onCloseMock = vi.fn()
|
||||
act(() => {
|
||||
Toast.notify({
|
||||
message: 'Closing notification',
|
||||
@ -181,7 +186,7 @@ describe('Toast', () => {
|
||||
|
||||
// Fast-forward timer
|
||||
act(() => {
|
||||
jest.advanceTimersByTime(3000) // Default for success type is 3000ms
|
||||
vi.advanceTimersByTime(3000) // Default for success type is 3000ms
|
||||
})
|
||||
|
||||
// onClose should be called
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import React from 'react'
|
||||
import { act, cleanup, fireEvent, render, screen } from '@testing-library/react'
|
||||
import '@testing-library/jest-dom'
|
||||
import Tooltip from './index'
|
||||
|
||||
afterEach(cleanup)
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import '@testing-library/jest-dom'
|
||||
import { z } from 'zod'
|
||||
import withValidation from '.'
|
||||
import { noop } from 'lodash-es'
|
||||
@ -17,11 +16,11 @@ describe('withValidation HOC', () => {
|
||||
const WrappedComponent = withValidation(TestComponent, schema)
|
||||
|
||||
beforeAll(() => {
|
||||
jest.spyOn(console, 'error').mockImplementation(noop)
|
||||
vi.spyOn(console, 'error').mockImplementation(noop)
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.restoreAllMocks()
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
it('renders the component when validation passes', () => {
|
||||
|
||||
Reference in New Issue
Block a user