mirror of
https://github.com/langgenius/dify.git
synced 2026-05-06 02:18:08 +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:
@ -6,7 +6,7 @@ import type { DocumentContextValue } from '@/app/components/datasets/documents/d
|
||||
import type { SegmentListContextValue } from '@/app/components/datasets/documents/detail/completed'
|
||||
|
||||
// Mock react-i18next - external dependency
|
||||
jest.mock('react-i18next', () => ({
|
||||
vi.mock('react-i18next', () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string, options?: { count?: number }) => {
|
||||
if (key === 'datasetDocuments.segment.characters')
|
||||
@ -25,7 +25,7 @@ jest.mock('react-i18next', () => ({
|
||||
const mockDocForm = { current: ChunkingMode.text }
|
||||
const mockParentMode = { current: 'paragraph' as ParentMode }
|
||||
|
||||
jest.mock('../../context', () => ({
|
||||
vi.mock('../../context', () => ({
|
||||
useDocumentContext: (selector: (value: DocumentContextValue) => unknown) => {
|
||||
const value: DocumentContextValue = {
|
||||
datasetId: 'test-dataset-id',
|
||||
@ -38,12 +38,12 @@ jest.mock('../../context', () => ({
|
||||
}))
|
||||
|
||||
const mockIsCollapsed = { current: true }
|
||||
jest.mock('../index', () => ({
|
||||
vi.mock('../index', () => ({
|
||||
useSegmentListContext: (selector: (value: SegmentListContextValue) => unknown) => {
|
||||
const value: SegmentListContextValue = {
|
||||
isCollapsed: mockIsCollapsed.current,
|
||||
fullScreen: false,
|
||||
toggleFullScreen: jest.fn(),
|
||||
toggleFullScreen: vi.fn(),
|
||||
currSegment: { showModal: false },
|
||||
currChildChunk: { showModal: false },
|
||||
}
|
||||
@ -56,7 +56,7 @@ jest.mock('../index', () => ({
|
||||
// ============================================================================
|
||||
|
||||
// StatusItem uses React Query hooks which require QueryClientProvider
|
||||
jest.mock('../../../status-item', () => ({
|
||||
vi.mock('../../../status-item', () => ({
|
||||
__esModule: true,
|
||||
default: ({ status, reverse, textCls }: { status: string; reverse?: boolean; textCls?: string }) => (
|
||||
<div data-testid="status-item" data-status={status} data-reverse={reverse} className={textCls}>
|
||||
@ -66,7 +66,7 @@ jest.mock('../../../status-item', () => ({
|
||||
}))
|
||||
|
||||
// ImageList has deep dependency: FileThumb → file-uploader → react-pdf-highlighter (ESM)
|
||||
jest.mock('@/app/components/datasets/common/image-list', () => ({
|
||||
vi.mock('@/app/components/datasets/common/image-list', () => ({
|
||||
__esModule: true,
|
||||
default: ({ images, size, className }: { images: Array<{ sourceUrl: string; name: string }>; size?: string; className?: string }) => (
|
||||
<div data-testid="image-list" data-image-count={images.length} data-size={size} className={className}>
|
||||
@ -78,7 +78,7 @@ jest.mock('@/app/components/datasets/common/image-list', () => ({
|
||||
}))
|
||||
|
||||
// Markdown uses next/dynamic and react-syntax-highlighter (ESM)
|
||||
jest.mock('@/app/components/base/markdown', () => ({
|
||||
vi.mock('@/app/components/base/markdown', () => ({
|
||||
__esModule: true,
|
||||
Markdown: ({ content, className }: { content: string; className?: string }) => (
|
||||
<div data-testid="markdown" className={`markdown-body ${className || ''}`}>{content}</div>
|
||||
@ -148,7 +148,7 @@ const defaultFocused = { segmentIndex: false, segmentContent: false }
|
||||
|
||||
describe('SegmentCard', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
mockDocForm.current = ChunkingMode.text
|
||||
mockParentMode.current = 'paragraph'
|
||||
mockIsCollapsed.current = true
|
||||
@ -341,7 +341,7 @@ describe('SegmentCard', () => {
|
||||
// --------------------------------------------------------------------------
|
||||
describe('Callbacks', () => {
|
||||
it('should call onClick when card is clicked in general mode', () => {
|
||||
const onClick = jest.fn()
|
||||
const onClick = vi.fn()
|
||||
const detail = createMockSegmentDetail()
|
||||
mockDocForm.current = ChunkingMode.text
|
||||
|
||||
@ -356,7 +356,7 @@ describe('SegmentCard', () => {
|
||||
})
|
||||
|
||||
it('should not call onClick when card is clicked in full-doc mode', () => {
|
||||
const onClick = jest.fn()
|
||||
const onClick = vi.fn()
|
||||
const detail = createMockSegmentDetail()
|
||||
mockDocForm.current = ChunkingMode.parentChild
|
||||
mockParentMode.current = 'full-doc'
|
||||
@ -372,7 +372,7 @@ describe('SegmentCard', () => {
|
||||
})
|
||||
|
||||
it('should call onClick when view more button is clicked in full-doc mode', () => {
|
||||
const onClick = jest.fn()
|
||||
const onClick = vi.fn()
|
||||
const detail = createMockSegmentDetail()
|
||||
mockDocForm.current = ChunkingMode.parentChild
|
||||
mockParentMode.current = 'full-doc'
|
||||
@ -386,7 +386,7 @@ describe('SegmentCard', () => {
|
||||
})
|
||||
|
||||
it('should call onClickEdit when edit button is clicked', () => {
|
||||
const onClickEdit = jest.fn()
|
||||
const onClickEdit = vi.fn()
|
||||
const detail = createMockSegmentDetail()
|
||||
|
||||
render(
|
||||
@ -406,7 +406,7 @@ describe('SegmentCard', () => {
|
||||
})
|
||||
|
||||
it('should call onDelete when confirm delete is clicked', async () => {
|
||||
const onDelete = jest.fn().mockResolvedValue(undefined)
|
||||
const onDelete = vi.fn().mockResolvedValue(undefined)
|
||||
const detail = createMockSegmentDetail({ id: 'test-segment-id' })
|
||||
|
||||
render(
|
||||
@ -434,7 +434,7 @@ describe('SegmentCard', () => {
|
||||
})
|
||||
|
||||
it('should call onChangeSwitch when switch is toggled', async () => {
|
||||
const onChangeSwitch = jest.fn().mockResolvedValue(undefined)
|
||||
const onChangeSwitch = vi.fn().mockResolvedValue(undefined)
|
||||
const detail = createMockSegmentDetail({ id: 'test-segment-id', enabled: true, status: 'completed' })
|
||||
|
||||
render(
|
||||
@ -456,8 +456,8 @@ describe('SegmentCard', () => {
|
||||
})
|
||||
|
||||
it('should stop propagation when edit button is clicked', () => {
|
||||
const onClick = jest.fn()
|
||||
const onClickEdit = jest.fn()
|
||||
const onClick = vi.fn()
|
||||
const onClickEdit = vi.fn()
|
||||
const detail = createMockSegmentDetail()
|
||||
|
||||
render(
|
||||
@ -479,7 +479,7 @@ describe('SegmentCard', () => {
|
||||
})
|
||||
|
||||
it('should stop propagation when switch area is clicked', () => {
|
||||
const onClick = jest.fn()
|
||||
const onClick = vi.fn()
|
||||
const detail = createMockSegmentDetail({ status: 'completed' })
|
||||
|
||||
render(
|
||||
@ -712,7 +712,7 @@ describe('SegmentCard', () => {
|
||||
it('should call handleAddNewChildChunk when add button is clicked', () => {
|
||||
mockDocForm.current = ChunkingMode.parentChild
|
||||
mockParentMode.current = 'paragraph'
|
||||
const handleAddNewChildChunk = jest.fn()
|
||||
const handleAddNewChildChunk = vi.fn()
|
||||
const childChunks = [createMockChildChunk()]
|
||||
const detail = createMockSegmentDetail({ id: 'parent-id', child_chunks: childChunks })
|
||||
|
||||
@ -991,13 +991,13 @@ describe('SegmentCard', () => {
|
||||
<SegmentCard
|
||||
loading={false}
|
||||
detail={detail}
|
||||
onClick={jest.fn()}
|
||||
onChangeSwitch={jest.fn()}
|
||||
onDelete={jest.fn()}
|
||||
onDeleteChildChunk={jest.fn()}
|
||||
handleAddNewChildChunk={jest.fn()}
|
||||
onClickSlice={jest.fn()}
|
||||
onClickEdit={jest.fn()}
|
||||
onClick={vi.fn()}
|
||||
onChangeSwitch={vi.fn()}
|
||||
onDelete={vi.fn()}
|
||||
onDeleteChildChunk={vi.fn()}
|
||||
handleAddNewChildChunk={vi.fn()}
|
||||
onClickSlice={vi.fn()}
|
||||
onClickEdit={vi.fn()}
|
||||
className="full-props-class"
|
||||
archived={false}
|
||||
embeddingAvailable={true}
|
||||
|
||||
@ -5,9 +5,9 @@ import { DatasourceType } from '@/models/pipeline'
|
||||
import type { PipelineExecutionLogResponse } from '@/models/pipeline'
|
||||
|
||||
// Mock Next.js router
|
||||
const mockPush = jest.fn()
|
||||
const mockBack = jest.fn()
|
||||
jest.mock('next/navigation', () => ({
|
||||
const mockPush = vi.fn()
|
||||
const mockBack = vi.fn()
|
||||
vi.mock('next/navigation', () => ({
|
||||
useRouter: () => ({
|
||||
push: mockPush,
|
||||
back: mockBack,
|
||||
@ -16,16 +16,16 @@ jest.mock('next/navigation', () => ({
|
||||
|
||||
// Mock dataset detail context
|
||||
const mockPipelineId = 'pipeline-123'
|
||||
jest.mock('@/context/dataset-detail', () => ({
|
||||
vi.mock('@/context/dataset-detail', () => ({
|
||||
useDatasetDetailContextWithSelector: (selector: (state: { dataset: { pipeline_id: string; doc_form: string } }) => unknown) =>
|
||||
selector({ dataset: { pipeline_id: mockPipelineId, doc_form: 'text_model' } }),
|
||||
}))
|
||||
|
||||
// Mock API hooks for PipelineSettings
|
||||
const mockUsePipelineExecutionLog = jest.fn()
|
||||
const mockMutateAsync = jest.fn()
|
||||
const mockUseRunPublishedPipeline = jest.fn()
|
||||
jest.mock('@/service/use-pipeline', () => ({
|
||||
const mockUsePipelineExecutionLog = vi.fn()
|
||||
const mockMutateAsync = vi.fn()
|
||||
const mockUseRunPublishedPipeline = vi.fn()
|
||||
vi.mock('@/service/use-pipeline', () => ({
|
||||
usePipelineExecutionLog: (params: { dataset_id: string; document_id: string }) => mockUsePipelineExecutionLog(params),
|
||||
useRunPublishedPipeline: () => mockUseRunPublishedPipeline(),
|
||||
// For ProcessDocuments component
|
||||
@ -36,16 +36,16 @@ jest.mock('@/service/use-pipeline', () => ({
|
||||
}))
|
||||
|
||||
// Mock document invalidation hooks
|
||||
const mockInvalidDocumentList = jest.fn()
|
||||
const mockInvalidDocumentDetail = jest.fn()
|
||||
jest.mock('@/service/knowledge/use-document', () => ({
|
||||
const mockInvalidDocumentList = vi.fn()
|
||||
const mockInvalidDocumentDetail = vi.fn()
|
||||
vi.mock('@/service/knowledge/use-document', () => ({
|
||||
useInvalidDocumentList: () => mockInvalidDocumentList,
|
||||
useInvalidDocumentDetail: () => mockInvalidDocumentDetail,
|
||||
}))
|
||||
|
||||
// Mock Form component in ProcessDocuments - internal dependencies are too complex
|
||||
jest.mock('../../../create-from-pipeline/process-documents/form', () => {
|
||||
return function MockForm({
|
||||
vi.mock('../../../create-from-pipeline/process-documents/form', () => ({
|
||||
default: function MockForm({
|
||||
ref,
|
||||
initialData,
|
||||
configurations,
|
||||
@ -84,12 +84,12 @@ jest.mock('../../../create-from-pipeline/process-documents/form', () => {
|
||||
</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
}))
|
||||
|
||||
// Mock ChunkPreview - has complex internal state and many dependencies
|
||||
jest.mock('../../../create-from-pipeline/preview/chunk-preview', () => {
|
||||
return function MockChunkPreview({
|
||||
vi.mock('../../../create-from-pipeline/preview/chunk-preview', () => ({
|
||||
default: function MockChunkPreview({
|
||||
dataSourceType,
|
||||
localFiles,
|
||||
onlineDocuments,
|
||||
@ -120,8 +120,8 @@ jest.mock('../../../create-from-pipeline/preview/chunk-preview', () => {
|
||||
<span data-testid="has-estimate-data">{String(!!estimateData)}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
}))
|
||||
|
||||
// Test utilities
|
||||
const createQueryClient = () =>
|
||||
@ -163,7 +163,7 @@ const createDefaultProps = () => ({
|
||||
|
||||
describe('PipelineSettings', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
mockPush.mockClear()
|
||||
mockBack.mockClear()
|
||||
mockMutateAsync.mockClear()
|
||||
|
||||
@ -6,14 +6,14 @@ import type { RAGPipelineVariable } from '@/models/pipeline'
|
||||
|
||||
// Mock dataset detail context - required for useInputVariables hook
|
||||
const mockPipelineId = 'pipeline-123'
|
||||
jest.mock('@/context/dataset-detail', () => ({
|
||||
vi.mock('@/context/dataset-detail', () => ({
|
||||
useDatasetDetailContextWithSelector: (selector: (state: { dataset: { pipeline_id: string } }) => string) =>
|
||||
selector({ dataset: { pipeline_id: mockPipelineId } }),
|
||||
}))
|
||||
|
||||
// Mock API call for pipeline processing params
|
||||
const mockParamsConfig = jest.fn()
|
||||
jest.mock('@/service/use-pipeline', () => ({
|
||||
const mockParamsConfig = vi.fn()
|
||||
vi.mock('@/service/use-pipeline', () => ({
|
||||
usePublishedPipelineProcessingParams: () => ({
|
||||
data: mockParamsConfig(),
|
||||
isFetching: false,
|
||||
@ -22,8 +22,8 @@ jest.mock('@/service/use-pipeline', () => ({
|
||||
|
||||
// Mock Form component - internal dependencies (useAppForm, BaseField) are too complex
|
||||
// Keep the mock minimal and focused on testing the integration
|
||||
jest.mock('../../../../create-from-pipeline/process-documents/form', () => {
|
||||
return function MockForm({
|
||||
vi.mock('../../../../create-from-pipeline/process-documents/form', () => ({
|
||||
default: function MockForm({
|
||||
ref,
|
||||
initialData,
|
||||
configurations,
|
||||
@ -69,8 +69,8 @@ jest.mock('../../../../create-from-pipeline/process-documents/form', () => {
|
||||
</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
}))
|
||||
|
||||
// Test utilities
|
||||
const createQueryClient = () =>
|
||||
@ -114,15 +114,15 @@ const createDefaultProps = (overrides: Partial<{
|
||||
lastRunInputData: {},
|
||||
isRunning: false,
|
||||
ref: { current: null } as React.RefObject<{ submit: () => void } | null>,
|
||||
onProcess: jest.fn(),
|
||||
onPreview: jest.fn(),
|
||||
onSubmit: jest.fn(),
|
||||
onProcess: vi.fn(),
|
||||
onPreview: vi.fn(),
|
||||
onSubmit: vi.fn(),
|
||||
...overrides,
|
||||
})
|
||||
|
||||
describe('ProcessDocuments', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
// Default: return empty variables
|
||||
mockParamsConfig.mockReturnValue({ variables: [] })
|
||||
})
|
||||
@ -253,7 +253,7 @@ describe('ProcessDocuments', () => {
|
||||
it('should expose submit method via ref', () => {
|
||||
// Arrange
|
||||
const ref = { current: null } as React.RefObject<{ submit: () => void } | null>
|
||||
const onSubmit = jest.fn()
|
||||
const onSubmit = vi.fn()
|
||||
const props = createDefaultProps({ ref, onSubmit })
|
||||
|
||||
// Act
|
||||
@ -278,7 +278,7 @@ describe('ProcessDocuments', () => {
|
||||
describe('onProcess', () => {
|
||||
it('should call onProcess when Save and Process button is clicked', () => {
|
||||
// Arrange
|
||||
const onProcess = jest.fn()
|
||||
const onProcess = vi.fn()
|
||||
const props = createDefaultProps({ onProcess })
|
||||
|
||||
// Act
|
||||
@ -291,7 +291,7 @@ describe('ProcessDocuments', () => {
|
||||
|
||||
it('should not call onProcess when button is disabled due to isRunning', () => {
|
||||
// Arrange
|
||||
const onProcess = jest.fn()
|
||||
const onProcess = vi.fn()
|
||||
const props = createDefaultProps({ onProcess, isRunning: true })
|
||||
|
||||
// Act
|
||||
@ -306,7 +306,7 @@ describe('ProcessDocuments', () => {
|
||||
describe('onPreview', () => {
|
||||
it('should call onPreview when preview button is clicked', () => {
|
||||
// Arrange
|
||||
const onPreview = jest.fn()
|
||||
const onPreview = vi.fn()
|
||||
const props = createDefaultProps({ onPreview })
|
||||
|
||||
// Act
|
||||
@ -325,7 +325,7 @@ describe('ProcessDocuments', () => {
|
||||
createMockVariable({ variable: 'chunk_size', label: 'Chunk Size', type: PipelineInputVarType.number, default_value: '100' }),
|
||||
]
|
||||
mockParamsConfig.mockReturnValue({ variables })
|
||||
const onSubmit = jest.fn()
|
||||
const onSubmit = vi.fn()
|
||||
const props = createDefaultProps({ onSubmit })
|
||||
|
||||
// Act
|
||||
@ -477,7 +477,7 @@ describe('ProcessDocuments', () => {
|
||||
createMockVariable({ variable: 'field2', label: 'Field 2', type: PipelineInputVarType.number, default_value: '42' }),
|
||||
]
|
||||
mockParamsConfig.mockReturnValue({ variables })
|
||||
const onSubmit = jest.fn()
|
||||
const onSubmit = vi.fn()
|
||||
const props = createDefaultProps({ onSubmit })
|
||||
|
||||
// Act
|
||||
@ -527,8 +527,8 @@ describe('ProcessDocuments', () => {
|
||||
createMockVariable({ variable: 'setting', label: 'Setting', type: PipelineInputVarType.textInput, default_value: 'initial' }),
|
||||
]
|
||||
mockParamsConfig.mockReturnValue({ variables })
|
||||
const onProcess = jest.fn()
|
||||
const onSubmit = jest.fn()
|
||||
const onProcess = vi.fn()
|
||||
const onSubmit = vi.fn()
|
||||
const props = createDefaultProps({ onProcess, onSubmit })
|
||||
|
||||
// Act
|
||||
|
||||
Reference in New Issue
Block a user