mirror of
https://github.com/langgenius/dify.git
synced 2026-05-04 01:18:05 +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:
@ -1,3 +1,4 @@
|
||||
import type { MockedFunction } from 'vitest'
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import React from 'react'
|
||||
import EmptyDatasetCreationModal from './index'
|
||||
@ -5,47 +6,47 @@ import { createEmptyDataset } from '@/service/datasets'
|
||||
import { useInvalidDatasetList } from '@/service/knowledge/use-dataset'
|
||||
|
||||
// Mock Next.js router
|
||||
const mockPush = jest.fn()
|
||||
jest.mock('next/navigation', () => ({
|
||||
const mockPush = vi.fn()
|
||||
vi.mock('next/navigation', () => ({
|
||||
useRouter: () => ({
|
||||
push: mockPush,
|
||||
}),
|
||||
}))
|
||||
|
||||
// Mock createEmptyDataset API
|
||||
jest.mock('@/service/datasets', () => ({
|
||||
createEmptyDataset: jest.fn(),
|
||||
vi.mock('@/service/datasets', () => ({
|
||||
createEmptyDataset: vi.fn(),
|
||||
}))
|
||||
|
||||
// Mock useInvalidDatasetList hook
|
||||
jest.mock('@/service/knowledge/use-dataset', () => ({
|
||||
useInvalidDatasetList: jest.fn(),
|
||||
vi.mock('@/service/knowledge/use-dataset', () => ({
|
||||
useInvalidDatasetList: vi.fn(),
|
||||
}))
|
||||
|
||||
// Mock ToastContext - need to mock both createContext and useContext from use-context-selector
|
||||
const mockNotify = jest.fn()
|
||||
jest.mock('use-context-selector', () => ({
|
||||
createContext: jest.fn(() => ({
|
||||
const mockNotify = vi.fn()
|
||||
vi.mock('use-context-selector', () => ({
|
||||
createContext: vi.fn(() => ({
|
||||
Provider: ({ children }: { children: React.ReactNode }) => children,
|
||||
})),
|
||||
useContext: jest.fn(() => ({ notify: mockNotify })),
|
||||
useContext: vi.fn(() => ({ notify: mockNotify })),
|
||||
}))
|
||||
|
||||
// Type cast mocked functions
|
||||
const mockCreateEmptyDataset = createEmptyDataset as jest.MockedFunction<typeof createEmptyDataset>
|
||||
const mockInvalidDatasetList = jest.fn()
|
||||
const mockUseInvalidDatasetList = useInvalidDatasetList as jest.MockedFunction<typeof useInvalidDatasetList>
|
||||
const mockCreateEmptyDataset = createEmptyDataset as MockedFunction<typeof createEmptyDataset>
|
||||
const mockInvalidDatasetList = vi.fn()
|
||||
const mockUseInvalidDatasetList = useInvalidDatasetList as MockedFunction<typeof useInvalidDatasetList>
|
||||
|
||||
// Test data builder for props
|
||||
const createDefaultProps = (overrides?: Partial<{ show: boolean; onHide: () => void }>) => ({
|
||||
show: true,
|
||||
onHide: jest.fn(),
|
||||
onHide: vi.fn(),
|
||||
...overrides,
|
||||
})
|
||||
|
||||
describe('EmptyDatasetCreationModal', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
mockUseInvalidDatasetList.mockReturnValue(mockInvalidDatasetList)
|
||||
mockCreateEmptyDataset.mockResolvedValue({
|
||||
id: 'dataset-123',
|
||||
@ -115,7 +116,7 @@ describe('EmptyDatasetCreationModal', () => {
|
||||
describe('show prop', () => {
|
||||
it('should show modal when show is true', () => {
|
||||
// Arrange & Act
|
||||
render(<EmptyDatasetCreationModal show={true} onHide={jest.fn()} />)
|
||||
render(<EmptyDatasetCreationModal show={true} onHide={vi.fn()} />)
|
||||
|
||||
// Assert
|
||||
expect(screen.getByText('datasetCreation.stepOne.modal.title')).toBeInTheDocument()
|
||||
@ -123,7 +124,7 @@ describe('EmptyDatasetCreationModal', () => {
|
||||
|
||||
it('should hide modal when show is false', () => {
|
||||
// Arrange & Act
|
||||
render(<EmptyDatasetCreationModal show={false} onHide={jest.fn()} />)
|
||||
render(<EmptyDatasetCreationModal show={false} onHide={vi.fn()} />)
|
||||
|
||||
// Assert
|
||||
expect(screen.queryByText('datasetCreation.stepOne.modal.title')).not.toBeInTheDocument()
|
||||
@ -131,7 +132,7 @@ describe('EmptyDatasetCreationModal', () => {
|
||||
|
||||
it('should toggle visibility when show prop changes', () => {
|
||||
// Arrange
|
||||
const onHide = jest.fn()
|
||||
const onHide = vi.fn()
|
||||
const { rerender } = render(<EmptyDatasetCreationModal show={false} onHide={onHide} />)
|
||||
|
||||
// Act & Assert - Initially hidden
|
||||
@ -146,7 +147,7 @@ describe('EmptyDatasetCreationModal', () => {
|
||||
describe('onHide prop', () => {
|
||||
it('should call onHide when cancel button is clicked', () => {
|
||||
// Arrange
|
||||
const mockOnHide = jest.fn()
|
||||
const mockOnHide = vi.fn()
|
||||
render(<EmptyDatasetCreationModal show={true} onHide={mockOnHide} />)
|
||||
|
||||
// Act
|
||||
@ -159,7 +160,7 @@ describe('EmptyDatasetCreationModal', () => {
|
||||
|
||||
it('should call onHide when close icon is clicked', async () => {
|
||||
// Arrange
|
||||
const mockOnHide = jest.fn()
|
||||
const mockOnHide = vi.fn()
|
||||
render(<EmptyDatasetCreationModal show={true} onHide={mockOnHide} />)
|
||||
|
||||
// Act - Wait for modal to be rendered, then find the close span
|
||||
@ -196,7 +197,7 @@ describe('EmptyDatasetCreationModal', () => {
|
||||
|
||||
it('should persist input value when modal is hidden and shown again via rerender', () => {
|
||||
// Arrange
|
||||
const onHide = jest.fn()
|
||||
const onHide = vi.fn()
|
||||
const { rerender } = render(<EmptyDatasetCreationModal show={true} onHide={onHide} />)
|
||||
const input = screen.getByPlaceholderText('datasetCreation.stepOne.modal.placeholder') as HTMLInputElement
|
||||
|
||||
@ -237,7 +238,7 @@ describe('EmptyDatasetCreationModal', () => {
|
||||
describe('User Interactions', () => {
|
||||
it('should submit form when confirm button is clicked with valid input', async () => {
|
||||
// Arrange
|
||||
const mockOnHide = jest.fn()
|
||||
const mockOnHide = vi.fn()
|
||||
render(<EmptyDatasetCreationModal show={true} onHide={mockOnHide} />)
|
||||
const input = screen.getByPlaceholderText('datasetCreation.stepOne.modal.placeholder')
|
||||
const confirmButton = screen.getByText('datasetCreation.stepOne.modal.confirmButton')
|
||||
@ -295,7 +296,7 @@ describe('EmptyDatasetCreationModal', () => {
|
||||
|
||||
it('should allow exactly 40 characters', async () => {
|
||||
// Arrange
|
||||
const mockOnHide = jest.fn()
|
||||
const mockOnHide = vi.fn()
|
||||
render(<EmptyDatasetCreationModal show={true} onHide={mockOnHide} />)
|
||||
const input = screen.getByPlaceholderText('datasetCreation.stepOne.modal.placeholder')
|
||||
const confirmButton = screen.getByText('datasetCreation.stepOne.modal.confirmButton')
|
||||
@ -313,7 +314,7 @@ describe('EmptyDatasetCreationModal', () => {
|
||||
|
||||
it('should close modal on cancel button click', () => {
|
||||
// Arrange
|
||||
const mockOnHide = jest.fn()
|
||||
const mockOnHide = vi.fn()
|
||||
render(<EmptyDatasetCreationModal show={true} onHide={mockOnHide} />)
|
||||
const cancelButton = screen.getByText('datasetCreation.stepOne.modal.cancelButton')
|
||||
|
||||
@ -331,7 +332,7 @@ describe('EmptyDatasetCreationModal', () => {
|
||||
describe('API Calls', () => {
|
||||
it('should call createEmptyDataset with correct parameters', async () => {
|
||||
// Arrange
|
||||
const mockOnHide = jest.fn()
|
||||
const mockOnHide = vi.fn()
|
||||
render(<EmptyDatasetCreationModal show={true} onHide={mockOnHide} />)
|
||||
const input = screen.getByPlaceholderText('datasetCreation.stepOne.modal.placeholder')
|
||||
const confirmButton = screen.getByText('datasetCreation.stepOne.modal.confirmButton')
|
||||
@ -348,7 +349,7 @@ describe('EmptyDatasetCreationModal', () => {
|
||||
|
||||
it('should call invalidDatasetList after successful creation', async () => {
|
||||
// Arrange
|
||||
const mockOnHide = jest.fn()
|
||||
const mockOnHide = vi.fn()
|
||||
render(<EmptyDatasetCreationModal show={true} onHide={mockOnHide} />)
|
||||
const input = screen.getByPlaceholderText('datasetCreation.stepOne.modal.placeholder')
|
||||
const confirmButton = screen.getByText('datasetCreation.stepOne.modal.confirmButton')
|
||||
@ -365,7 +366,7 @@ describe('EmptyDatasetCreationModal', () => {
|
||||
|
||||
it('should call onHide after successful creation', async () => {
|
||||
// Arrange
|
||||
const mockOnHide = jest.fn()
|
||||
const mockOnHide = vi.fn()
|
||||
render(<EmptyDatasetCreationModal show={true} onHide={mockOnHide} />)
|
||||
const input = screen.getByPlaceholderText('datasetCreation.stepOne.modal.placeholder')
|
||||
const confirmButton = screen.getByText('datasetCreation.stepOne.modal.confirmButton')
|
||||
@ -383,7 +384,7 @@ describe('EmptyDatasetCreationModal', () => {
|
||||
it('should show error notification on API failure', async () => {
|
||||
// Arrange
|
||||
mockCreateEmptyDataset.mockRejectedValue(new Error('API Error'))
|
||||
const mockOnHide = jest.fn()
|
||||
const mockOnHide = vi.fn()
|
||||
render(<EmptyDatasetCreationModal show={true} onHide={mockOnHide} />)
|
||||
const input = screen.getByPlaceholderText('datasetCreation.stepOne.modal.placeholder')
|
||||
const confirmButton = screen.getByText('datasetCreation.stepOne.modal.confirmButton')
|
||||
@ -404,7 +405,7 @@ describe('EmptyDatasetCreationModal', () => {
|
||||
it('should not call onHide on API failure', async () => {
|
||||
// Arrange
|
||||
mockCreateEmptyDataset.mockRejectedValue(new Error('API Error'))
|
||||
const mockOnHide = jest.fn()
|
||||
const mockOnHide = vi.fn()
|
||||
render(<EmptyDatasetCreationModal show={true} onHide={mockOnHide} />)
|
||||
const input = screen.getByPlaceholderText('datasetCreation.stepOne.modal.placeholder')
|
||||
const confirmButton = screen.getByText('datasetCreation.stepOne.modal.confirmButton')
|
||||
@ -451,7 +452,7 @@ describe('EmptyDatasetCreationModal', () => {
|
||||
id: 'test-dataset-456',
|
||||
name: 'Test',
|
||||
} as ReturnType<typeof createEmptyDataset> extends Promise<infer T> ? T : never)
|
||||
const mockOnHide = jest.fn()
|
||||
const mockOnHide = vi.fn()
|
||||
render(<EmptyDatasetCreationModal show={true} onHide={mockOnHide} />)
|
||||
const input = screen.getByPlaceholderText('datasetCreation.stepOne.modal.placeholder')
|
||||
const confirmButton = screen.getByText('datasetCreation.stepOne.modal.confirmButton')
|
||||
@ -508,7 +509,7 @@ describe('EmptyDatasetCreationModal', () => {
|
||||
describe('Edge Cases', () => {
|
||||
it('should handle whitespace-only input as valid (component behavior)', async () => {
|
||||
// Arrange
|
||||
const mockOnHide = jest.fn()
|
||||
const mockOnHide = vi.fn()
|
||||
render(<EmptyDatasetCreationModal show={true} onHide={mockOnHide} />)
|
||||
const input = screen.getByPlaceholderText('datasetCreation.stepOne.modal.placeholder')
|
||||
const confirmButton = screen.getByText('datasetCreation.stepOne.modal.confirmButton')
|
||||
@ -525,7 +526,7 @@ describe('EmptyDatasetCreationModal', () => {
|
||||
|
||||
it('should handle special characters in input', async () => {
|
||||
// Arrange
|
||||
const mockOnHide = jest.fn()
|
||||
const mockOnHide = vi.fn()
|
||||
render(<EmptyDatasetCreationModal show={true} onHide={mockOnHide} />)
|
||||
const input = screen.getByPlaceholderText('datasetCreation.stepOne.modal.placeholder')
|
||||
const confirmButton = screen.getByText('datasetCreation.stepOne.modal.confirmButton')
|
||||
@ -542,7 +543,7 @@ describe('EmptyDatasetCreationModal', () => {
|
||||
|
||||
it('should handle Unicode characters in input', async () => {
|
||||
// Arrange
|
||||
const mockOnHide = jest.fn()
|
||||
const mockOnHide = vi.fn()
|
||||
render(<EmptyDatasetCreationModal show={true} onHide={mockOnHide} />)
|
||||
const input = screen.getByPlaceholderText('datasetCreation.stepOne.modal.placeholder')
|
||||
const confirmButton = screen.getByText('datasetCreation.stepOne.modal.confirmButton')
|
||||
@ -559,7 +560,7 @@ describe('EmptyDatasetCreationModal', () => {
|
||||
|
||||
it('should handle input at exactly 40 character boundary', async () => {
|
||||
// Arrange
|
||||
const mockOnHide = jest.fn()
|
||||
const mockOnHide = vi.fn()
|
||||
render(<EmptyDatasetCreationModal show={true} onHide={mockOnHide} />)
|
||||
const input = screen.getByPlaceholderText('datasetCreation.stepOne.modal.placeholder')
|
||||
const confirmButton = screen.getByText('datasetCreation.stepOne.modal.confirmButton')
|
||||
@ -599,7 +600,7 @@ describe('EmptyDatasetCreationModal', () => {
|
||||
|
||||
it('should handle rapid consecutive submits', async () => {
|
||||
// Arrange
|
||||
const mockOnHide = jest.fn()
|
||||
const mockOnHide = vi.fn()
|
||||
render(<EmptyDatasetCreationModal show={true} onHide={mockOnHide} />)
|
||||
const input = screen.getByPlaceholderText('datasetCreation.stepOne.modal.placeholder')
|
||||
const confirmButton = screen.getByText('datasetCreation.stepOne.modal.confirmButton')
|
||||
@ -618,7 +619,7 @@ describe('EmptyDatasetCreationModal', () => {
|
||||
|
||||
it('should handle input with leading/trailing spaces', async () => {
|
||||
// Arrange
|
||||
const mockOnHide = jest.fn()
|
||||
const mockOnHide = vi.fn()
|
||||
render(<EmptyDatasetCreationModal show={true} onHide={mockOnHide} />)
|
||||
const input = screen.getByPlaceholderText('datasetCreation.stepOne.modal.placeholder')
|
||||
const confirmButton = screen.getByText('datasetCreation.stepOne.modal.confirmButton')
|
||||
@ -635,7 +636,7 @@ describe('EmptyDatasetCreationModal', () => {
|
||||
|
||||
it('should handle newline characters in input (browser strips newlines)', async () => {
|
||||
// Arrange
|
||||
const mockOnHide = jest.fn()
|
||||
const mockOnHide = vi.fn()
|
||||
render(<EmptyDatasetCreationModal show={true} onHide={mockOnHide} />)
|
||||
const input = screen.getByPlaceholderText('datasetCreation.stepOne.modal.placeholder')
|
||||
const confirmButton = screen.getByText('datasetCreation.stepOne.modal.confirmButton')
|
||||
@ -719,7 +720,7 @@ describe('EmptyDatasetCreationModal', () => {
|
||||
describe('Integration', () => {
|
||||
it('should complete full successful creation flow', async () => {
|
||||
// Arrange
|
||||
const mockOnHide = jest.fn()
|
||||
const mockOnHide = vi.fn()
|
||||
mockCreateEmptyDataset.mockResolvedValue({
|
||||
id: 'new-id-789',
|
||||
name: 'Complete Flow Test',
|
||||
@ -747,7 +748,7 @@ describe('EmptyDatasetCreationModal', () => {
|
||||
|
||||
it('should handle error flow correctly', async () => {
|
||||
// Arrange
|
||||
const mockOnHide = jest.fn()
|
||||
const mockOnHide = vi.fn()
|
||||
mockCreateEmptyDataset.mockRejectedValue(new Error('Server Error'))
|
||||
render(<EmptyDatasetCreationModal show={true} onHide={mockOnHide} />)
|
||||
const input = screen.getByPlaceholderText('datasetCreation.stepOne.modal.placeholder')
|
||||
|
||||
@ -1,35 +1,40 @@
|
||||
import type { MockedFunction } from 'vitest'
|
||||
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import FilePreview from './index'
|
||||
import type { CustomFile as File } from '@/models/datasets'
|
||||
import { fetchFilePreview } from '@/service/common'
|
||||
|
||||
// Mock the fetchFilePreview service
|
||||
jest.mock('@/service/common', () => ({
|
||||
fetchFilePreview: jest.fn(),
|
||||
vi.mock('@/service/common', () => ({
|
||||
fetchFilePreview: vi.fn(),
|
||||
}))
|
||||
|
||||
const mockFetchFilePreview = fetchFilePreview as jest.MockedFunction<typeof fetchFilePreview>
|
||||
const mockFetchFilePreview = fetchFilePreview as MockedFunction<typeof fetchFilePreview>
|
||||
|
||||
// Factory function to create mock file objects
|
||||
const createMockFile = (overrides: Partial<File> = {}): File => {
|
||||
const file = new window.File(['test content'], 'test-file.txt', {
|
||||
const fileName = overrides.name ?? 'test-file.txt'
|
||||
// Create a plain object that looks like a File with CustomFile properties
|
||||
// We can't use Object.assign on a real File because 'name' is a getter-only property
|
||||
return {
|
||||
name: fileName,
|
||||
size: 1024,
|
||||
type: 'text/plain',
|
||||
}) as File
|
||||
return Object.assign(file, {
|
||||
lastModified: Date.now(),
|
||||
id: 'file-123',
|
||||
extension: 'txt',
|
||||
mime_type: 'text/plain',
|
||||
created_by: 'user-1',
|
||||
created_at: Date.now(),
|
||||
...overrides,
|
||||
})
|
||||
} as File
|
||||
}
|
||||
|
||||
// Helper to render FilePreview with default props
|
||||
const renderFilePreview = (props: Partial<{ file?: File; hidePreview: () => void }> = {}) => {
|
||||
const defaultProps = {
|
||||
file: createMockFile(),
|
||||
hidePreview: jest.fn(),
|
||||
hidePreview: vi.fn(),
|
||||
...props,
|
||||
}
|
||||
return {
|
||||
@ -48,7 +53,7 @@ const findLoadingSpinner = (container: HTMLElement) => {
|
||||
// ============================================================================
|
||||
describe('FilePreview', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
// Default successful API response
|
||||
mockFetchFilePreview.mockResolvedValue({ content: 'Preview content here' })
|
||||
})
|
||||
@ -168,7 +173,7 @@ describe('FilePreview', () => {
|
||||
|
||||
// Act - Initial render
|
||||
const { rerender, container } = render(
|
||||
<FilePreview file={file1} hidePreview={jest.fn()} />,
|
||||
<FilePreview file={file1} hidePreview={vi.fn()} />,
|
||||
)
|
||||
|
||||
// First file loading - spinner should be visible
|
||||
@ -184,7 +189,7 @@ describe('FilePreview', () => {
|
||||
})
|
||||
|
||||
// Rerender with new file
|
||||
rerender(<FilePreview file={file2} hidePreview={jest.fn()} />)
|
||||
rerender(<FilePreview file={file2} hidePreview={vi.fn()} />)
|
||||
|
||||
// Should show loading again
|
||||
await waitFor(() => {
|
||||
@ -245,14 +250,14 @@ describe('FilePreview', () => {
|
||||
|
||||
// Act
|
||||
const { rerender } = render(
|
||||
<FilePreview file={file1} hidePreview={jest.fn()} />,
|
||||
<FilePreview file={file1} hidePreview={vi.fn()} />,
|
||||
)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockFetchFilePreview).toHaveBeenCalledWith({ fileID: 'file-1' })
|
||||
})
|
||||
|
||||
rerender(<FilePreview file={file2} hidePreview={jest.fn()} />)
|
||||
rerender(<FilePreview file={file2} hidePreview={vi.fn()} />)
|
||||
|
||||
// Assert
|
||||
await waitFor(() => {
|
||||
@ -310,7 +315,7 @@ describe('FilePreview', () => {
|
||||
describe('User Interactions', () => {
|
||||
it('should call hidePreview when close button is clicked', async () => {
|
||||
// Arrange
|
||||
const hidePreview = jest.fn()
|
||||
const hidePreview = vi.fn()
|
||||
const { container } = renderFilePreview({ hidePreview })
|
||||
|
||||
// Act
|
||||
@ -323,7 +328,7 @@ describe('FilePreview', () => {
|
||||
|
||||
it('should call hidePreview with event object when clicked', async () => {
|
||||
// Arrange
|
||||
const hidePreview = jest.fn()
|
||||
const hidePreview = vi.fn()
|
||||
const { container } = renderFilePreview({ hidePreview })
|
||||
|
||||
// Act
|
||||
@ -337,7 +342,7 @@ describe('FilePreview', () => {
|
||||
|
||||
it('should handle multiple clicks on close button', async () => {
|
||||
// Arrange
|
||||
const hidePreview = jest.fn()
|
||||
const hidePreview = vi.fn()
|
||||
const { container } = renderFilePreview({ hidePreview })
|
||||
|
||||
// Act
|
||||
@ -391,7 +396,7 @@ describe('FilePreview', () => {
|
||||
|
||||
// Act
|
||||
const { rerender, container } = render(
|
||||
<FilePreview file={file1} hidePreview={jest.fn()} />,
|
||||
<FilePreview file={file1} hidePreview={vi.fn()} />,
|
||||
)
|
||||
|
||||
await waitFor(() => {
|
||||
@ -399,7 +404,7 @@ describe('FilePreview', () => {
|
||||
})
|
||||
|
||||
// Change file
|
||||
rerender(<FilePreview file={file2} hidePreview={jest.fn()} />)
|
||||
rerender(<FilePreview file={file2} hidePreview={vi.fn()} />)
|
||||
|
||||
// Assert - Loading should be shown again
|
||||
await waitFor(() => {
|
||||
@ -421,7 +426,7 @@ describe('FilePreview', () => {
|
||||
|
||||
// Act
|
||||
const { rerender } = render(
|
||||
<FilePreview file={file1} hidePreview={jest.fn()} />,
|
||||
<FilePreview file={file1} hidePreview={vi.fn()} />,
|
||||
)
|
||||
|
||||
await waitFor(() => {
|
||||
@ -429,7 +434,7 @@ describe('FilePreview', () => {
|
||||
})
|
||||
|
||||
// Change file - loading should replace content
|
||||
rerender(<FilePreview file={file2} hidePreview={jest.fn()} />)
|
||||
rerender(<FilePreview file={file2} hidePreview={vi.fn()} />)
|
||||
|
||||
// Resolve second fetch
|
||||
await act(async () => {
|
||||
@ -487,7 +492,7 @@ describe('FilePreview', () => {
|
||||
const { container } = renderFilePreview({ file })
|
||||
|
||||
// Assert - getFileName returns empty for single segment, but component still renders
|
||||
const fileNameElement = container.querySelector('.fileName')
|
||||
const fileNameElement = container.querySelector('[class*="fileName"]')
|
||||
expect(fileNameElement).toBeInTheDocument()
|
||||
// The first span (file name) should be empty
|
||||
const fileNameSpan = fileNameElement?.querySelector('span:first-child')
|
||||
@ -509,7 +514,7 @@ describe('FilePreview', () => {
|
||||
describe('hidePreview prop', () => {
|
||||
it('should accept hidePreview callback', async () => {
|
||||
// Arrange
|
||||
const hidePreview = jest.fn()
|
||||
const hidePreview = vi.fn()
|
||||
|
||||
// Act
|
||||
renderFilePreview({ hidePreview })
|
||||
@ -594,7 +599,7 @@ describe('FilePreview', () => {
|
||||
|
||||
// Assert - Should render as text, not execute scripts
|
||||
await waitFor(() => {
|
||||
const contentDiv = container.querySelector('.fileContent')
|
||||
const contentDiv = container.querySelector('[class*="fileContent"]')
|
||||
expect(contentDiv).toBeInTheDocument()
|
||||
// Content is escaped by React, so HTML entities are displayed
|
||||
expect(contentDiv?.textContent).toContain('alert')
|
||||
@ -625,7 +630,7 @@ describe('FilePreview', () => {
|
||||
|
||||
// Assert - Content should be in the DOM
|
||||
await waitFor(() => {
|
||||
const contentDiv = container.querySelector('.fileContent')
|
||||
const contentDiv = container.querySelector('[class*="fileContent"]')
|
||||
expect(contentDiv).toBeInTheDocument()
|
||||
expect(contentDiv?.textContent).toContain('Line 1')
|
||||
expect(contentDiv?.textContent).toContain('Line 2')
|
||||
@ -658,14 +663,14 @@ describe('FilePreview', () => {
|
||||
|
||||
// Act
|
||||
const { rerender } = render(
|
||||
<FilePreview file={file1} hidePreview={jest.fn()} />,
|
||||
<FilePreview file={file1} hidePreview={vi.fn()} />,
|
||||
)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockFetchFilePreview).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
rerender(<FilePreview file={file2} hidePreview={jest.fn()} />)
|
||||
rerender(<FilePreview file={file2} hidePreview={vi.fn()} />)
|
||||
|
||||
// Assert
|
||||
await waitFor(() => {
|
||||
@ -676,8 +681,8 @@ describe('FilePreview', () => {
|
||||
it('should not trigger effect when hidePreview changes', async () => {
|
||||
// Arrange
|
||||
const file = createMockFile()
|
||||
const hidePreview1 = jest.fn()
|
||||
const hidePreview2 = jest.fn()
|
||||
const hidePreview1 = vi.fn()
|
||||
const hidePreview2 = vi.fn()
|
||||
|
||||
// Act
|
||||
const { rerender } = render(
|
||||
@ -705,12 +710,12 @@ describe('FilePreview', () => {
|
||||
|
||||
// Act
|
||||
const { rerender } = render(
|
||||
<FilePreview file={files[0]} hidePreview={jest.fn()} />,
|
||||
<FilePreview file={files[0]} hidePreview={vi.fn()} />,
|
||||
)
|
||||
|
||||
// Rapidly change files
|
||||
for (let i = 1; i < files.length; i++)
|
||||
rerender(<FilePreview file={files[i]} hidePreview={jest.fn()} />)
|
||||
rerender(<FilePreview file={files[i]} hidePreview={vi.fn()} />)
|
||||
|
||||
// Assert - Should have called API for each file
|
||||
await waitFor(() => {
|
||||
@ -740,14 +745,14 @@ describe('FilePreview', () => {
|
||||
|
||||
// Act
|
||||
const { rerender, container } = render(
|
||||
<FilePreview file={file} hidePreview={jest.fn()} />,
|
||||
<FilePreview file={file} hidePreview={vi.fn()} />,
|
||||
)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockFetchFilePreview).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
rerender(<FilePreview file={undefined} hidePreview={jest.fn()} />)
|
||||
rerender(<FilePreview file={undefined} hidePreview={vi.fn()} />)
|
||||
|
||||
// Assert - Should not crash, API should not be called again
|
||||
expect(container.firstChild).toBeInTheDocument()
|
||||
@ -789,7 +794,7 @@ describe('FilePreview', () => {
|
||||
const { container } = renderFilePreview({ file })
|
||||
|
||||
// Assert - slice(0, -1) on single element array returns empty
|
||||
const fileNameElement = container.querySelector('.fileName')
|
||||
const fileNameElement = container.querySelector('[class*="fileName"]')
|
||||
const firstSpan = fileNameElement?.querySelector('span:first-child')
|
||||
expect(firstSpan?.textContent).toBe('')
|
||||
})
|
||||
|
||||
@ -18,23 +18,23 @@ const IndexingTypeValues = {
|
||||
// Mock External Dependencies
|
||||
// ==========================================
|
||||
|
||||
// Mock react-i18next (handled by __mocks__/react-i18next.ts but we override for custom messages)
|
||||
jest.mock('react-i18next', () => ({
|
||||
// Mock react-i18next (handled by global mock in web/vitest.setup.ts but we override for custom messages)
|
||||
vi.mock('react-i18next', () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}))
|
||||
|
||||
// Mock next/link
|
||||
jest.mock('next/link', () => {
|
||||
vi.mock('next/link', () => {
|
||||
return function MockLink({ children, href }: { children: React.ReactNode; href: string }) {
|
||||
return <a href={href}>{children}</a>
|
||||
}
|
||||
})
|
||||
|
||||
// Mock modal context
|
||||
const mockSetShowAccountSettingModal = jest.fn()
|
||||
jest.mock('@/context/modal-context', () => ({
|
||||
const mockSetShowAccountSettingModal = vi.fn()
|
||||
vi.mock('@/context/modal-context', () => ({
|
||||
useModalContextSelector: (selector: (state: any) => any) => {
|
||||
const state = {
|
||||
setShowAccountSettingModal: mockSetShowAccountSettingModal,
|
||||
@ -45,7 +45,7 @@ jest.mock('@/context/modal-context', () => ({
|
||||
|
||||
// Mock dataset detail context
|
||||
let mockDatasetDetail: DataSet | undefined
|
||||
jest.mock('@/context/dataset-detail', () => ({
|
||||
vi.mock('@/context/dataset-detail', () => ({
|
||||
useDatasetDetailContextWithSelector: (selector: (state: any) => any) => {
|
||||
const state = {
|
||||
dataset: mockDatasetDetail,
|
||||
@ -56,10 +56,10 @@ jest.mock('@/context/dataset-detail', () => ({
|
||||
|
||||
// Mock useDefaultModel hook
|
||||
let mockEmbeddingsDefaultModel: { model: string; provider: string } | undefined
|
||||
jest.mock('@/app/components/header/account-setting/model-provider-page/hooks', () => ({
|
||||
vi.mock('@/app/components/header/account-setting/model-provider-page/hooks', () => ({
|
||||
useDefaultModel: () => ({
|
||||
data: mockEmbeddingsDefaultModel,
|
||||
mutate: jest.fn(),
|
||||
mutate: vi.fn(),
|
||||
isLoading: false,
|
||||
}),
|
||||
}))
|
||||
@ -68,7 +68,7 @@ jest.mock('@/app/components/header/account-setting/model-provider-page/hooks', (
|
||||
let mockDataSourceList: { result: DataSourceAuth[] } | undefined
|
||||
let mockIsLoadingDataSourceList = false
|
||||
let mockFetchingError = false
|
||||
jest.mock('@/service/use-datasource', () => ({
|
||||
vi.mock('@/service/use-datasource', () => ({
|
||||
useGetDefaultDataSourceListAuth: () => ({
|
||||
data: mockDataSourceList,
|
||||
isLoading: mockIsLoadingDataSourceList,
|
||||
@ -87,7 +87,7 @@ let stepThreeProps: Record<string, any> = {}
|
||||
// _topBarProps is assigned but not directly used in assertions - values checked via data-testid
|
||||
let _topBarProps: Record<string, any> = {}
|
||||
|
||||
jest.mock('./step-one', () => ({
|
||||
vi.mock('./step-one', () => ({
|
||||
__esModule: true,
|
||||
default: (props: Record<string, any>) => {
|
||||
stepOneProps = props
|
||||
@ -161,7 +161,7 @@ jest.mock('./step-one', () => ({
|
||||
},
|
||||
}))
|
||||
|
||||
jest.mock('./step-two', () => ({
|
||||
vi.mock('./step-two', () => ({
|
||||
__esModule: true,
|
||||
default: (props: Record<string, any>) => {
|
||||
stepTwoProps = props
|
||||
@ -196,7 +196,7 @@ jest.mock('./step-two', () => ({
|
||||
},
|
||||
}))
|
||||
|
||||
jest.mock('./step-three', () => ({
|
||||
vi.mock('./step-three', () => ({
|
||||
__esModule: true,
|
||||
default: (props: Record<string, any>) => {
|
||||
stepThreeProps = props
|
||||
@ -211,7 +211,7 @@ jest.mock('./step-three', () => ({
|
||||
},
|
||||
}))
|
||||
|
||||
jest.mock('./top-bar', () => ({
|
||||
vi.mock('./top-bar', () => ({
|
||||
TopBar: (props: Record<string, any>) => {
|
||||
_topBarProps = props
|
||||
return (
|
||||
@ -300,7 +300,7 @@ const createMockDataSourceAuth = (overrides?: Partial<DataSourceAuth>): DataSour
|
||||
|
||||
describe('DatasetUpdateForm', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
// Reset mock state
|
||||
mockDatasetDetail = undefined
|
||||
mockEmbeddingsDefaultModel = { model: 'text-embedding-ada-002', provider: 'openai' }
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
import type { MockedFunction } from 'vitest'
|
||||
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import NotionPagePreview from './index'
|
||||
import type { NotionPage } from '@/models/common'
|
||||
import { fetchNotionPagePreview } from '@/service/datasets'
|
||||
|
||||
// Mock the fetchNotionPagePreview service
|
||||
jest.mock('@/service/datasets', () => ({
|
||||
fetchNotionPagePreview: jest.fn(),
|
||||
vi.mock('@/service/datasets', () => ({
|
||||
fetchNotionPagePreview: vi.fn(),
|
||||
}))
|
||||
|
||||
const mockFetchNotionPagePreview = fetchNotionPagePreview as jest.MockedFunction<typeof fetchNotionPagePreview>
|
||||
const mockFetchNotionPagePreview = fetchNotionPagePreview as MockedFunction<typeof fetchNotionPagePreview>
|
||||
|
||||
// Factory function to create mock NotionPage objects
|
||||
const createMockNotionPage = (overrides: Partial<NotionPage> = {}): NotionPage => {
|
||||
@ -60,7 +61,7 @@ const renderNotionPagePreview = async (
|
||||
const defaultProps = {
|
||||
currentPage: createMockNotionPage(),
|
||||
notionCredentialId: 'credential-123',
|
||||
hidePreview: jest.fn(),
|
||||
hidePreview: vi.fn(),
|
||||
...props,
|
||||
}
|
||||
const result = render(<NotionPagePreview {...defaultProps} />)
|
||||
@ -93,7 +94,7 @@ const findLoadingSpinner = (container: HTMLElement) => {
|
||||
// ============================================================================
|
||||
describe('NotionPagePreview', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
// Default successful API response
|
||||
mockFetchNotionPagePreview.mockResolvedValue({ content: 'Preview content here' })
|
||||
})
|
||||
@ -256,7 +257,7 @@ describe('NotionPagePreview', () => {
|
||||
|
||||
// Act - Initial render
|
||||
const { rerender, container } = render(
|
||||
<NotionPagePreview currentPage={page1} notionCredentialId="cred-123" hidePreview={jest.fn()} />,
|
||||
<NotionPagePreview currentPage={page1} notionCredentialId="cred-123" hidePreview={vi.fn()} />,
|
||||
)
|
||||
|
||||
// First page loading - spinner should be visible
|
||||
@ -272,7 +273,7 @@ describe('NotionPagePreview', () => {
|
||||
})
|
||||
|
||||
// Rerender with new page
|
||||
rerender(<NotionPagePreview currentPage={page2} notionCredentialId="cred-123" hidePreview={jest.fn()} />)
|
||||
rerender(<NotionPagePreview currentPage={page2} notionCredentialId="cred-123" hidePreview={vi.fn()} />)
|
||||
|
||||
// Should show loading again
|
||||
await waitFor(() => {
|
||||
@ -330,7 +331,7 @@ describe('NotionPagePreview', () => {
|
||||
|
||||
// Act
|
||||
const { rerender } = render(
|
||||
<NotionPagePreview currentPage={page1} notionCredentialId="cred-123" hidePreview={jest.fn()} />,
|
||||
<NotionPagePreview currentPage={page1} notionCredentialId="cred-123" hidePreview={vi.fn()} />,
|
||||
)
|
||||
|
||||
await waitFor(() => {
|
||||
@ -342,7 +343,7 @@ describe('NotionPagePreview', () => {
|
||||
})
|
||||
|
||||
await act(async () => {
|
||||
rerender(<NotionPagePreview currentPage={page2} notionCredentialId="cred-123" hidePreview={jest.fn()} />)
|
||||
rerender(<NotionPagePreview currentPage={page2} notionCredentialId="cred-123" hidePreview={vi.fn()} />)
|
||||
})
|
||||
|
||||
// Assert
|
||||
@ -401,7 +402,7 @@ describe('NotionPagePreview', () => {
|
||||
describe('User Interactions', () => {
|
||||
it('should call hidePreview when close button is clicked', async () => {
|
||||
// Arrange
|
||||
const hidePreview = jest.fn()
|
||||
const hidePreview = vi.fn()
|
||||
const { container } = await renderNotionPagePreview({ hidePreview })
|
||||
|
||||
// Act
|
||||
@ -414,7 +415,7 @@ describe('NotionPagePreview', () => {
|
||||
|
||||
it('should handle multiple clicks on close button', async () => {
|
||||
// Arrange
|
||||
const hidePreview = jest.fn()
|
||||
const hidePreview = vi.fn()
|
||||
const { container } = await renderNotionPagePreview({ hidePreview })
|
||||
|
||||
// Act
|
||||
@ -466,7 +467,7 @@ describe('NotionPagePreview', () => {
|
||||
|
||||
// Act
|
||||
const { rerender, container } = render(
|
||||
<NotionPagePreview currentPage={page1} notionCredentialId="cred-123" hidePreview={jest.fn()} />,
|
||||
<NotionPagePreview currentPage={page1} notionCredentialId="cred-123" hidePreview={vi.fn()} />,
|
||||
)
|
||||
|
||||
await waitFor(() => {
|
||||
@ -475,7 +476,7 @@ describe('NotionPagePreview', () => {
|
||||
|
||||
// Change page
|
||||
await act(async () => {
|
||||
rerender(<NotionPagePreview currentPage={page2} notionCredentialId="cred-123" hidePreview={jest.fn()} />)
|
||||
rerender(<NotionPagePreview currentPage={page2} notionCredentialId="cred-123" hidePreview={vi.fn()} />)
|
||||
})
|
||||
|
||||
// Assert - Loading should be shown again
|
||||
@ -498,7 +499,7 @@ describe('NotionPagePreview', () => {
|
||||
|
||||
// Act
|
||||
const { rerender } = render(
|
||||
<NotionPagePreview currentPage={page1} notionCredentialId="cred-123" hidePreview={jest.fn()} />,
|
||||
<NotionPagePreview currentPage={page1} notionCredentialId="cred-123" hidePreview={vi.fn()} />,
|
||||
)
|
||||
|
||||
await waitFor(() => {
|
||||
@ -507,7 +508,7 @@ describe('NotionPagePreview', () => {
|
||||
|
||||
// Change page
|
||||
await act(async () => {
|
||||
rerender(<NotionPagePreview currentPage={page2} notionCredentialId="cred-123" hidePreview={jest.fn()} />)
|
||||
rerender(<NotionPagePreview currentPage={page2} notionCredentialId="cred-123" hidePreview={vi.fn()} />)
|
||||
})
|
||||
|
||||
// Resolve second fetch
|
||||
@ -613,7 +614,7 @@ describe('NotionPagePreview', () => {
|
||||
describe('hidePreview prop', () => {
|
||||
it('should accept hidePreview callback', async () => {
|
||||
// Arrange
|
||||
const hidePreview = jest.fn()
|
||||
const hidePreview = vi.fn()
|
||||
|
||||
// Act
|
||||
await renderNotionPagePreview({ hidePreview })
|
||||
@ -673,7 +674,7 @@ describe('NotionPagePreview', () => {
|
||||
const { container } = await renderNotionPagePreview()
|
||||
|
||||
// Assert - Should render as text, not execute scripts
|
||||
const contentDiv = container.querySelector('.fileContent')
|
||||
const contentDiv = container.querySelector('[class*="fileContent"]')
|
||||
expect(contentDiv).toBeInTheDocument()
|
||||
expect(contentDiv?.textContent).toContain('alert')
|
||||
})
|
||||
@ -699,7 +700,7 @@ describe('NotionPagePreview', () => {
|
||||
const { container } = await renderNotionPagePreview()
|
||||
|
||||
// Assert
|
||||
const contentDiv = container.querySelector('.fileContent')
|
||||
const contentDiv = container.querySelector('[class*="fileContent"]')
|
||||
expect(contentDiv).toBeInTheDocument()
|
||||
expect(contentDiv?.textContent).toContain('Line 1')
|
||||
expect(contentDiv?.textContent).toContain('Line 2')
|
||||
@ -742,7 +743,7 @@ describe('NotionPagePreview', () => {
|
||||
|
||||
// Act
|
||||
const { rerender } = render(
|
||||
<NotionPagePreview currentPage={page1} notionCredentialId="cred-123" hidePreview={jest.fn()} />,
|
||||
<NotionPagePreview currentPage={page1} notionCredentialId="cred-123" hidePreview={vi.fn()} />,
|
||||
)
|
||||
|
||||
await waitFor(() => {
|
||||
@ -750,7 +751,7 @@ describe('NotionPagePreview', () => {
|
||||
})
|
||||
|
||||
await act(async () => {
|
||||
rerender(<NotionPagePreview currentPage={page2} notionCredentialId="cred-123" hidePreview={jest.fn()} />)
|
||||
rerender(<NotionPagePreview currentPage={page2} notionCredentialId="cred-123" hidePreview={vi.fn()} />)
|
||||
})
|
||||
|
||||
// Assert
|
||||
@ -762,8 +763,8 @@ describe('NotionPagePreview', () => {
|
||||
it('should not trigger effect when hidePreview changes', async () => {
|
||||
// Arrange
|
||||
const page = createMockNotionPage()
|
||||
const hidePreview1 = jest.fn()
|
||||
const hidePreview2 = jest.fn()
|
||||
const hidePreview1 = vi.fn()
|
||||
const hidePreview2 = vi.fn()
|
||||
|
||||
// Act
|
||||
const { rerender } = render(
|
||||
@ -789,7 +790,7 @@ describe('NotionPagePreview', () => {
|
||||
|
||||
// Act
|
||||
const { rerender } = render(
|
||||
<NotionPagePreview currentPage={page} notionCredentialId="cred-1" hidePreview={jest.fn()} />,
|
||||
<NotionPagePreview currentPage={page} notionCredentialId="cred-1" hidePreview={vi.fn()} />,
|
||||
)
|
||||
|
||||
await waitFor(() => {
|
||||
@ -797,7 +798,7 @@ describe('NotionPagePreview', () => {
|
||||
})
|
||||
|
||||
await act(async () => {
|
||||
rerender(<NotionPagePreview currentPage={page} notionCredentialId="cred-2" hidePreview={jest.fn()} />)
|
||||
rerender(<NotionPagePreview currentPage={page} notionCredentialId="cred-2" hidePreview={vi.fn()} />)
|
||||
})
|
||||
|
||||
// Assert - Should not call API again (only currentPage is in dependency array)
|
||||
@ -812,13 +813,13 @@ describe('NotionPagePreview', () => {
|
||||
|
||||
// Act
|
||||
const { rerender } = render(
|
||||
<NotionPagePreview currentPage={pages[0]} notionCredentialId="cred-123" hidePreview={jest.fn()} />,
|
||||
<NotionPagePreview currentPage={pages[0]} notionCredentialId="cred-123" hidePreview={vi.fn()} />,
|
||||
)
|
||||
|
||||
// Rapidly change pages
|
||||
for (let i = 1; i < pages.length; i++) {
|
||||
await act(async () => {
|
||||
rerender(<NotionPagePreview currentPage={pages[i]} notionCredentialId="cred-123" hidePreview={jest.fn()} />)
|
||||
rerender(<NotionPagePreview currentPage={pages[i]} notionCredentialId="cred-123" hidePreview={vi.fn()} />)
|
||||
})
|
||||
}
|
||||
|
||||
@ -850,7 +851,7 @@ describe('NotionPagePreview', () => {
|
||||
|
||||
// Act
|
||||
const { rerender, container } = render(
|
||||
<NotionPagePreview currentPage={page} notionCredentialId="cred-123" hidePreview={jest.fn()} />,
|
||||
<NotionPagePreview currentPage={page} notionCredentialId="cred-123" hidePreview={vi.fn()} />,
|
||||
)
|
||||
|
||||
await waitFor(() => {
|
||||
@ -858,7 +859,7 @@ describe('NotionPagePreview', () => {
|
||||
})
|
||||
|
||||
await act(async () => {
|
||||
rerender(<NotionPagePreview currentPage={undefined} notionCredentialId="cred-123" hidePreview={jest.fn()} />)
|
||||
rerender(<NotionPagePreview currentPage={undefined} notionCredentialId="cred-123" hidePreview={vi.fn()} />)
|
||||
})
|
||||
|
||||
// Assert - Should not crash, API should not be called again
|
||||
@ -1075,7 +1076,7 @@ describe('NotionPagePreview', () => {
|
||||
it('should handle page with icon object having empty url', async () => {
|
||||
// Arrange
|
||||
// Suppress console.error for this test as we're intentionally testing empty src edge case
|
||||
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(jest.fn())
|
||||
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(vi.fn())
|
||||
|
||||
const page = createMockNotionPage({
|
||||
page_icon: {
|
||||
@ -1112,7 +1113,7 @@ describe('NotionPagePreview', () => {
|
||||
const { container } = await renderNotionPagePreview()
|
||||
|
||||
// Assert
|
||||
const contentDiv = container.querySelector('.fileContent')
|
||||
const contentDiv = container.querySelector('[class*="fileContent"]')
|
||||
expect(contentDiv).toBeInTheDocument()
|
||||
expect(contentDiv).toHaveTextContent('Test content')
|
||||
})
|
||||
@ -1126,7 +1127,7 @@ describe('NotionPagePreview', () => {
|
||||
const { container } = await renderNotionPagePreview()
|
||||
|
||||
// Assert
|
||||
const contentDiv = container.querySelector('.fileContent')
|
||||
const contentDiv = container.querySelector('[class*="fileContent"]')
|
||||
expect(contentDiv).toBeInTheDocument()
|
||||
// The CSS class has white-space: pre-line
|
||||
expect(contentDiv?.textContent).toContain('indented content')
|
||||
@ -1142,7 +1143,7 @@ describe('NotionPagePreview', () => {
|
||||
// Assert
|
||||
const loadingElement = findLoadingSpinner(container)
|
||||
expect(loadingElement).not.toBeInTheDocument()
|
||||
const contentDiv = container.querySelector('.fileContent')
|
||||
const contentDiv = container.querySelector('[class*="fileContent"]')
|
||||
expect(contentDiv).toBeInTheDocument()
|
||||
expect(contentDiv?.textContent).toBe('')
|
||||
})
|
||||
|
||||
@ -3,9 +3,9 @@ import StepThree from './index'
|
||||
import type { FullDocumentDetail, IconInfo, createDocumentResponse } from '@/models/datasets'
|
||||
|
||||
// Mock the EmbeddingProcess component since it has complex async logic
|
||||
jest.mock('../embedding-process', () => ({
|
||||
vi.mock('../embedding-process', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(({ datasetId, batchId, documents, indexingType, retrievalMethod }) => (
|
||||
default: vi.fn(({ datasetId, batchId, documents, indexingType, retrievalMethod }) => (
|
||||
<div data-testid="embedding-process">
|
||||
<span data-testid="ep-dataset-id">{datasetId}</span>
|
||||
<span data-testid="ep-batch-id">{batchId}</span>
|
||||
@ -18,18 +18,18 @@ jest.mock('../embedding-process', () => ({
|
||||
|
||||
// Mock useBreakpoints hook
|
||||
let mockMediaType = 'pc'
|
||||
jest.mock('@/hooks/use-breakpoints', () => ({
|
||||
vi.mock('@/hooks/use-breakpoints', () => ({
|
||||
__esModule: true,
|
||||
MediaType: {
|
||||
mobile: 'mobile',
|
||||
tablet: 'tablet',
|
||||
pc: 'pc',
|
||||
},
|
||||
default: jest.fn(() => mockMediaType),
|
||||
default: vi.fn(() => mockMediaType),
|
||||
}))
|
||||
|
||||
// Mock useDocLink hook
|
||||
jest.mock('@/context/i18n', () => ({
|
||||
vi.mock('@/context/i18n', () => ({
|
||||
useDocLink: () => (path?: string) => `https://docs.dify.ai/en-US${path || ''}`,
|
||||
}))
|
||||
|
||||
@ -104,7 +104,7 @@ const renderStepThree = (props: Partial<Parameters<typeof StepThree>[0]> = {}) =
|
||||
// ============================================================================
|
||||
describe('StepThree', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
mockMediaType = 'pc'
|
||||
})
|
||||
|
||||
|
||||
@ -10,14 +10,14 @@ const supportedLanguages = languages.filter(lang => lang.supported)
|
||||
// Test data builder for props
|
||||
const createDefaultProps = (overrides?: Partial<ILanguageSelectProps>): ILanguageSelectProps => ({
|
||||
currentLanguage: 'English',
|
||||
onSelect: jest.fn(),
|
||||
onSelect: vi.fn(),
|
||||
disabled: false,
|
||||
...overrides,
|
||||
})
|
||||
|
||||
describe('LanguageSelect', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
// ==========================================
|
||||
@ -189,7 +189,7 @@ describe('LanguageSelect', () => {
|
||||
|
||||
describe('onSelect prop', () => {
|
||||
it('should be callable as a function', () => {
|
||||
const mockOnSelect = jest.fn()
|
||||
const mockOnSelect = vi.fn()
|
||||
const props = createDefaultProps({ onSelect: mockOnSelect })
|
||||
render(<LanguageSelect {...props} />)
|
||||
|
||||
@ -224,7 +224,7 @@ describe('LanguageSelect', () => {
|
||||
|
||||
it('should call onSelect when a language option is clicked', () => {
|
||||
// Arrange
|
||||
const mockOnSelect = jest.fn()
|
||||
const mockOnSelect = vi.fn()
|
||||
const props = createDefaultProps({ onSelect: mockOnSelect })
|
||||
render(<LanguageSelect {...props} />)
|
||||
|
||||
@ -241,7 +241,7 @@ describe('LanguageSelect', () => {
|
||||
|
||||
it('should call onSelect with correct language when selecting different languages', () => {
|
||||
// Arrange
|
||||
const mockOnSelect = jest.fn()
|
||||
const mockOnSelect = vi.fn()
|
||||
const props = createDefaultProps({ onSelect: mockOnSelect })
|
||||
render(<LanguageSelect {...props} />)
|
||||
|
||||
@ -274,7 +274,7 @@ describe('LanguageSelect', () => {
|
||||
|
||||
it('should not call onSelect when component is disabled', () => {
|
||||
// Arrange
|
||||
const mockOnSelect = jest.fn()
|
||||
const mockOnSelect = vi.fn()
|
||||
const props = createDefaultProps({ onSelect: mockOnSelect, disabled: true })
|
||||
render(<LanguageSelect {...props} />)
|
||||
|
||||
@ -288,7 +288,7 @@ describe('LanguageSelect', () => {
|
||||
|
||||
it('should handle rapid consecutive clicks', () => {
|
||||
// Arrange
|
||||
const mockOnSelect = jest.fn()
|
||||
const mockOnSelect = vi.fn()
|
||||
const props = createDefaultProps({ onSelect: mockOnSelect })
|
||||
render(<LanguageSelect {...props} />)
|
||||
|
||||
@ -314,9 +314,9 @@ describe('LanguageSelect', () => {
|
||||
|
||||
it('should not re-render when props remain the same', () => {
|
||||
// Arrange
|
||||
const mockOnSelect = jest.fn()
|
||||
const mockOnSelect = vi.fn()
|
||||
const props = createDefaultProps({ onSelect: mockOnSelect })
|
||||
const renderSpy = jest.fn()
|
||||
const renderSpy = vi.fn()
|
||||
|
||||
// Create a wrapper component to track renders
|
||||
const TrackedLanguageSelect: React.FC<ILanguageSelectProps> = (trackedProps) => {
|
||||
@ -515,7 +515,7 @@ describe('LanguageSelect', () => {
|
||||
describe('Popover Integration', () => {
|
||||
it('should use manualClose prop on Popover', () => {
|
||||
// Arrange
|
||||
const mockOnSelect = jest.fn()
|
||||
const mockOnSelect = vi.fn()
|
||||
const props = createDefaultProps({ onSelect: mockOnSelect })
|
||||
|
||||
// Act
|
||||
|
||||
@ -23,7 +23,7 @@ const createQAProps = (overrides?: Partial<IPreviewItemProps>): IPreviewItemProp
|
||||
|
||||
describe('PreviewItem', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
// ==========================================
|
||||
@ -346,7 +346,7 @@ describe('PreviewItem', () => {
|
||||
it('should not re-render when props remain the same', () => {
|
||||
// Arrange
|
||||
const props = createDefaultProps()
|
||||
const renderSpy = jest.fn()
|
||||
const renderSpy = vi.fn()
|
||||
|
||||
// Create a wrapper component to track renders
|
||||
const TrackedPreviewItem: React.FC<IPreviewItemProps> = (trackedProps) => {
|
||||
|
||||
@ -37,7 +37,7 @@ const renderStepperStep = (props: Partial<StepperStepProps> = {}) => {
|
||||
// ============================================================================
|
||||
describe('Stepper', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
@ -332,7 +332,7 @@ describe('Stepper', () => {
|
||||
// ============================================================================
|
||||
describe('StepperStep', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
@ -671,7 +671,7 @@ describe('StepperStep', () => {
|
||||
// ============================================================================
|
||||
describe('Stepper Integration', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('should pass correct props to each StepperStep', () => {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import type { MockInstance } from 'vitest'
|
||||
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import StopEmbeddingModal from './index'
|
||||
|
||||
@ -12,8 +13,8 @@ type StopEmbeddingModalProps = {
|
||||
const renderStopEmbeddingModal = (props: Partial<StopEmbeddingModalProps> = {}) => {
|
||||
const defaultProps: StopEmbeddingModalProps = {
|
||||
show: true,
|
||||
onConfirm: jest.fn(),
|
||||
onHide: jest.fn(),
|
||||
onConfirm: vi.fn(),
|
||||
onHide: vi.fn(),
|
||||
...props,
|
||||
}
|
||||
return {
|
||||
@ -28,12 +29,12 @@ const renderStopEmbeddingModal = (props: Partial<StopEmbeddingModalProps> = {})
|
||||
describe('StopEmbeddingModal', () => {
|
||||
// Suppress Headless UI warnings in tests
|
||||
// These warnings are from the library's internal behavior, not our code
|
||||
let consoleWarnSpy: jest.SpyInstance
|
||||
let consoleErrorSpy: jest.SpyInstance
|
||||
let consoleWarnSpy: MockInstance
|
||||
let consoleErrorSpy: MockInstance
|
||||
|
||||
beforeAll(() => {
|
||||
consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(jest.fn())
|
||||
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(jest.fn())
|
||||
consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(vi.fn())
|
||||
consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(vi.fn())
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
@ -42,7 +43,7 @@ describe('StopEmbeddingModal', () => {
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
@ -159,8 +160,8 @@ describe('StopEmbeddingModal', () => {
|
||||
|
||||
it('should use default value false when show is not provided', () => {
|
||||
// Arrange & Act
|
||||
const onConfirm = jest.fn()
|
||||
const onHide = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onHide = vi.fn()
|
||||
render(<StopEmbeddingModal onConfirm={onConfirm} onHide={onHide} show={false} />)
|
||||
|
||||
// Assert
|
||||
@ -169,8 +170,8 @@ describe('StopEmbeddingModal', () => {
|
||||
|
||||
it('should toggle visibility when show prop changes to true', async () => {
|
||||
// Arrange
|
||||
const onConfirm = jest.fn()
|
||||
const onHide = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onHide = vi.fn()
|
||||
|
||||
// Act - Initially hidden
|
||||
const { rerender } = render(
|
||||
@ -193,7 +194,7 @@ describe('StopEmbeddingModal', () => {
|
||||
describe('onConfirm prop', () => {
|
||||
it('should accept onConfirm callback function', () => {
|
||||
// Arrange
|
||||
const onConfirm = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
|
||||
// Act
|
||||
renderStopEmbeddingModal({ onConfirm })
|
||||
@ -206,7 +207,7 @@ describe('StopEmbeddingModal', () => {
|
||||
describe('onHide prop', () => {
|
||||
it('should accept onHide callback function', () => {
|
||||
// Arrange
|
||||
const onHide = jest.fn()
|
||||
const onHide = vi.fn()
|
||||
|
||||
// Act
|
||||
renderStopEmbeddingModal({ onHide })
|
||||
@ -224,8 +225,8 @@ describe('StopEmbeddingModal', () => {
|
||||
describe('Confirm Button', () => {
|
||||
it('should call onConfirm when confirm button is clicked', async () => {
|
||||
// Arrange
|
||||
const onConfirm = jest.fn()
|
||||
const onHide = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onHide = vi.fn()
|
||||
renderStopEmbeddingModal({ onConfirm, onHide })
|
||||
|
||||
// Act
|
||||
@ -240,8 +241,8 @@ describe('StopEmbeddingModal', () => {
|
||||
|
||||
it('should call onHide when confirm button is clicked', async () => {
|
||||
// Arrange
|
||||
const onConfirm = jest.fn()
|
||||
const onHide = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onHide = vi.fn()
|
||||
renderStopEmbeddingModal({ onConfirm, onHide })
|
||||
|
||||
// Act
|
||||
@ -257,8 +258,8 @@ describe('StopEmbeddingModal', () => {
|
||||
it('should call both onConfirm and onHide in correct order when confirm button is clicked', async () => {
|
||||
// Arrange
|
||||
const callOrder: string[] = []
|
||||
const onConfirm = jest.fn(() => callOrder.push('confirm'))
|
||||
const onHide = jest.fn(() => callOrder.push('hide'))
|
||||
const onConfirm = vi.fn(() => callOrder.push('confirm'))
|
||||
const onHide = vi.fn(() => callOrder.push('hide'))
|
||||
renderStopEmbeddingModal({ onConfirm, onHide })
|
||||
|
||||
// Act
|
||||
@ -273,8 +274,8 @@ describe('StopEmbeddingModal', () => {
|
||||
|
||||
it('should handle multiple clicks on confirm button', async () => {
|
||||
// Arrange
|
||||
const onConfirm = jest.fn()
|
||||
const onHide = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onHide = vi.fn()
|
||||
renderStopEmbeddingModal({ onConfirm, onHide })
|
||||
|
||||
// Act
|
||||
@ -294,8 +295,8 @@ describe('StopEmbeddingModal', () => {
|
||||
describe('Cancel Button', () => {
|
||||
it('should call onHide when cancel button is clicked', async () => {
|
||||
// Arrange
|
||||
const onConfirm = jest.fn()
|
||||
const onHide = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onHide = vi.fn()
|
||||
renderStopEmbeddingModal({ onConfirm, onHide })
|
||||
|
||||
// Act
|
||||
@ -310,8 +311,8 @@ describe('StopEmbeddingModal', () => {
|
||||
|
||||
it('should not call onConfirm when cancel button is clicked', async () => {
|
||||
// Arrange
|
||||
const onConfirm = jest.fn()
|
||||
const onHide = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onHide = vi.fn()
|
||||
renderStopEmbeddingModal({ onConfirm, onHide })
|
||||
|
||||
// Act
|
||||
@ -326,8 +327,8 @@ describe('StopEmbeddingModal', () => {
|
||||
|
||||
it('should handle multiple clicks on cancel button', async () => {
|
||||
// Arrange
|
||||
const onConfirm = jest.fn()
|
||||
const onHide = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onHide = vi.fn()
|
||||
renderStopEmbeddingModal({ onConfirm, onHide })
|
||||
|
||||
// Act
|
||||
@ -346,8 +347,8 @@ describe('StopEmbeddingModal', () => {
|
||||
describe('Close Icon', () => {
|
||||
it('should call onHide when close span is clicked', async () => {
|
||||
// Arrange
|
||||
const onConfirm = jest.fn()
|
||||
const onHide = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onHide = vi.fn()
|
||||
const { container } = renderStopEmbeddingModal({ onConfirm, onHide })
|
||||
|
||||
// Act - Find the close span (it should be the span with onClick handler)
|
||||
@ -372,8 +373,8 @@ describe('StopEmbeddingModal', () => {
|
||||
|
||||
it('should not call onConfirm when close span is clicked', async () => {
|
||||
// Arrange
|
||||
const onConfirm = jest.fn()
|
||||
const onHide = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onHide = vi.fn()
|
||||
const { container } = renderStopEmbeddingModal({ onConfirm, onHide })
|
||||
|
||||
// Act
|
||||
@ -396,8 +397,8 @@ describe('StopEmbeddingModal', () => {
|
||||
describe('Different Close Methods', () => {
|
||||
it('should distinguish between confirm and cancel actions', async () => {
|
||||
// Arrange
|
||||
const onConfirm = jest.fn()
|
||||
const onHide = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onHide = vi.fn()
|
||||
renderStopEmbeddingModal({ onConfirm, onHide })
|
||||
|
||||
// Act - Click cancel
|
||||
@ -411,7 +412,7 @@ describe('StopEmbeddingModal', () => {
|
||||
expect(onHide).toHaveBeenCalledTimes(1)
|
||||
|
||||
// Reset
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
|
||||
// Act - Click confirm
|
||||
const confirmButton = screen.getByText('datasetCreation.stepThree.modelButtonConfirm')
|
||||
@ -432,8 +433,8 @@ describe('StopEmbeddingModal', () => {
|
||||
describe('Edge Cases', () => {
|
||||
it('should handle rapid confirm button clicks', async () => {
|
||||
// Arrange
|
||||
const onConfirm = jest.fn()
|
||||
const onHide = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onHide = vi.fn()
|
||||
renderStopEmbeddingModal({ onConfirm, onHide })
|
||||
|
||||
// Act - Rapid clicks
|
||||
@ -450,8 +451,8 @@ describe('StopEmbeddingModal', () => {
|
||||
|
||||
it('should handle rapid cancel button clicks', async () => {
|
||||
// Arrange
|
||||
const onConfirm = jest.fn()
|
||||
const onHide = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onHide = vi.fn()
|
||||
renderStopEmbeddingModal({ onConfirm, onHide })
|
||||
|
||||
// Act - Rapid clicks
|
||||
@ -468,10 +469,10 @@ describe('StopEmbeddingModal', () => {
|
||||
|
||||
it('should handle callbacks being replaced', async () => {
|
||||
// Arrange
|
||||
const onConfirm1 = jest.fn()
|
||||
const onHide1 = jest.fn()
|
||||
const onConfirm2 = jest.fn()
|
||||
const onHide2 = jest.fn()
|
||||
const onConfirm1 = vi.fn()
|
||||
const onHide1 = vi.fn()
|
||||
const onConfirm2 = vi.fn()
|
||||
const onHide2 = vi.fn()
|
||||
|
||||
// Act
|
||||
const { rerender } = render(
|
||||
@ -501,8 +502,8 @@ describe('StopEmbeddingModal', () => {
|
||||
render(
|
||||
<StopEmbeddingModal
|
||||
show={true}
|
||||
onConfirm={jest.fn()}
|
||||
onHide={jest.fn()}
|
||||
onConfirm={vi.fn()}
|
||||
onHide={vi.fn()}
|
||||
/>,
|
||||
)
|
||||
|
||||
@ -553,10 +554,10 @@ describe('StopEmbeddingModal', () => {
|
||||
let confirmTime = 0
|
||||
let hideTime = 0
|
||||
let counter = 0
|
||||
const onConfirm = jest.fn(() => {
|
||||
const onConfirm = vi.fn(() => {
|
||||
confirmTime = ++counter
|
||||
})
|
||||
const onHide = jest.fn(() => {
|
||||
const onHide = vi.fn(() => {
|
||||
hideTime = ++counter
|
||||
})
|
||||
renderStopEmbeddingModal({ onConfirm, onHide })
|
||||
@ -574,8 +575,8 @@ describe('StopEmbeddingModal', () => {
|
||||
|
||||
it('should call both callbacks exactly once per click', async () => {
|
||||
// Arrange
|
||||
const onConfirm = jest.fn()
|
||||
const onHide = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onHide = vi.fn()
|
||||
renderStopEmbeddingModal({ onConfirm, onHide })
|
||||
|
||||
// Act
|
||||
@ -591,8 +592,8 @@ describe('StopEmbeddingModal', () => {
|
||||
|
||||
it('should pass no arguments to onConfirm', async () => {
|
||||
// Arrange
|
||||
const onConfirm = jest.fn()
|
||||
const onHide = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onHide = vi.fn()
|
||||
renderStopEmbeddingModal({ onConfirm, onHide })
|
||||
|
||||
// Act
|
||||
@ -607,8 +608,8 @@ describe('StopEmbeddingModal', () => {
|
||||
|
||||
it('should pass no arguments to onHide when called from submit', async () => {
|
||||
// Arrange
|
||||
const onConfirm = jest.fn()
|
||||
const onHide = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onHide = vi.fn()
|
||||
renderStopEmbeddingModal({ onConfirm, onHide })
|
||||
|
||||
// Act
|
||||
@ -629,7 +630,7 @@ describe('StopEmbeddingModal', () => {
|
||||
it('should pass show prop to Modal as isShow', async () => {
|
||||
// Arrange & Act
|
||||
const { rerender } = render(
|
||||
<StopEmbeddingModal show={true} onConfirm={jest.fn()} onHide={jest.fn()} />,
|
||||
<StopEmbeddingModal show={true} onConfirm={vi.fn()} onHide={vi.fn()} />,
|
||||
)
|
||||
|
||||
// Assert - Modal should be visible
|
||||
@ -637,7 +638,7 @@ describe('StopEmbeddingModal', () => {
|
||||
|
||||
// Act - Hide modal
|
||||
await act(async () => {
|
||||
rerender(<StopEmbeddingModal show={false} onConfirm={jest.fn()} onHide={jest.fn()} />)
|
||||
rerender(<StopEmbeddingModal show={false} onConfirm={vi.fn()} onHide={vi.fn()} />)
|
||||
})
|
||||
|
||||
// Assert - Modal should transition to hidden (wait for transition)
|
||||
@ -689,8 +690,8 @@ describe('StopEmbeddingModal', () => {
|
||||
describe('Component Lifecycle', () => {
|
||||
it('should unmount cleanly', () => {
|
||||
// Arrange
|
||||
const onConfirm = jest.fn()
|
||||
const onHide = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onHide = vi.fn()
|
||||
const { unmount } = renderStopEmbeddingModal({ onConfirm, onHide })
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
@ -699,8 +700,8 @@ describe('StopEmbeddingModal', () => {
|
||||
|
||||
it('should not call callbacks after unmount', () => {
|
||||
// Arrange
|
||||
const onConfirm = jest.fn()
|
||||
const onHide = jest.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const onHide = vi.fn()
|
||||
const { unmount } = renderStopEmbeddingModal({ onConfirm, onHide })
|
||||
|
||||
// Act
|
||||
@ -713,10 +714,10 @@ describe('StopEmbeddingModal', () => {
|
||||
|
||||
it('should re-render correctly when props update', async () => {
|
||||
// Arrange
|
||||
const onConfirm1 = jest.fn()
|
||||
const onHide1 = jest.fn()
|
||||
const onConfirm2 = jest.fn()
|
||||
const onHide2 = jest.fn()
|
||||
const onConfirm1 = vi.fn()
|
||||
const onHide1 = vi.fn()
|
||||
const onConfirm2 = vi.fn()
|
||||
const onHide2 = vi.fn()
|
||||
|
||||
// Act - Initial render
|
||||
const { rerender } = render(
|
||||
|
||||
@ -2,13 +2,13 @@ import { render, screen } from '@testing-library/react'
|
||||
import { TopBar, type TopBarProps } from './index'
|
||||
|
||||
// Mock next/link to capture href values
|
||||
jest.mock('next/link', () => {
|
||||
return ({ children, href, replace, className }: { children: React.ReactNode; href: string; replace?: boolean; className?: string }) => (
|
||||
vi.mock('next/link', () => ({
|
||||
default: ({ children, href, replace, className }: { children: React.ReactNode; href: string; replace?: boolean; className?: string }) => (
|
||||
<a href={href} data-replace={replace} className={className} data-testid="back-link">
|
||||
{children}
|
||||
</a>
|
||||
)
|
||||
})
|
||||
),
|
||||
}))
|
||||
|
||||
// Helper to render TopBar with default props
|
||||
const renderTopBar = (props: Partial<TopBarProps> = {}) => {
|
||||
@ -27,7 +27,7 @@ const renderTopBar = (props: Partial<TopBarProps> = {}) => {
|
||||
// ============================================================================
|
||||
describe('TopBar', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
@ -24,12 +24,12 @@ const createCrawlResultItem = (overrides: Partial<CrawlResultItem> = {}): CrawlR
|
||||
|
||||
describe('Input', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
const createInputProps = (overrides: Partial<Parameters<typeof Input>[0]> = {}) => ({
|
||||
value: '',
|
||||
onChange: jest.fn(),
|
||||
onChange: vi.fn(),
|
||||
...overrides,
|
||||
})
|
||||
|
||||
@ -70,7 +70,7 @@ describe('Input', () => {
|
||||
|
||||
describe('Text Input Behavior', () => {
|
||||
it('should call onChange with string value for text input', async () => {
|
||||
const onChange = jest.fn()
|
||||
const onChange = vi.fn()
|
||||
const props = createInputProps({ onChange })
|
||||
|
||||
render(<Input {...props} />)
|
||||
@ -88,7 +88,7 @@ describe('Input', () => {
|
||||
|
||||
describe('Number Input Behavior', () => {
|
||||
it('should call onChange with parsed integer for number input', () => {
|
||||
const onChange = jest.fn()
|
||||
const onChange = vi.fn()
|
||||
const props = createInputProps({ isNumber: true, onChange, value: 0 })
|
||||
|
||||
render(<Input {...props} />)
|
||||
@ -100,7 +100,7 @@ describe('Input', () => {
|
||||
})
|
||||
|
||||
it('should call onChange with empty string when input is NaN', () => {
|
||||
const onChange = jest.fn()
|
||||
const onChange = vi.fn()
|
||||
const props = createInputProps({ isNumber: true, onChange, value: 0 })
|
||||
|
||||
render(<Input {...props} />)
|
||||
@ -112,7 +112,7 @@ describe('Input', () => {
|
||||
})
|
||||
|
||||
it('should call onChange with empty string when input is empty', () => {
|
||||
const onChange = jest.fn()
|
||||
const onChange = vi.fn()
|
||||
const props = createInputProps({ isNumber: true, onChange, value: 5 })
|
||||
|
||||
render(<Input {...props} />)
|
||||
@ -124,7 +124,7 @@ describe('Input', () => {
|
||||
})
|
||||
|
||||
it('should clamp negative values to MIN_VALUE (0)', () => {
|
||||
const onChange = jest.fn()
|
||||
const onChange = vi.fn()
|
||||
const props = createInputProps({ isNumber: true, onChange, value: 0 })
|
||||
|
||||
render(<Input {...props} />)
|
||||
@ -136,7 +136,7 @@ describe('Input', () => {
|
||||
})
|
||||
|
||||
it('should handle decimal input by parsing as integer', () => {
|
||||
const onChange = jest.fn()
|
||||
const onChange = vi.fn()
|
||||
const props = createInputProps({ isNumber: true, onChange, value: 0 })
|
||||
|
||||
render(<Input {...props} />)
|
||||
@ -237,7 +237,7 @@ describe('Header', () => {
|
||||
|
||||
describe('User Interactions', () => {
|
||||
it('should call onClickConfiguration when button is clicked', async () => {
|
||||
const onClickConfiguration = jest.fn()
|
||||
const onClickConfiguration = vi.fn()
|
||||
const props = createHeaderProps({ onClickConfiguration })
|
||||
|
||||
render(<Header {...props} />)
|
||||
@ -263,8 +263,8 @@ describe('CrawledResultItem', () => {
|
||||
payload: createCrawlResultItem(),
|
||||
isChecked: false,
|
||||
isPreview: false,
|
||||
onCheckChange: jest.fn(),
|
||||
onPreview: jest.fn(),
|
||||
onCheckChange: vi.fn(),
|
||||
onPreview: vi.fn(),
|
||||
testId: 'test-item',
|
||||
...overrides,
|
||||
})
|
||||
@ -302,7 +302,7 @@ describe('CrawledResultItem', () => {
|
||||
|
||||
describe('Checkbox Behavior', () => {
|
||||
it('should call onCheckChange with true when unchecked item is clicked', async () => {
|
||||
const onCheckChange = jest.fn()
|
||||
const onCheckChange = vi.fn()
|
||||
const props = createItemProps({ isChecked: false, onCheckChange })
|
||||
|
||||
render(<CrawledResultItem {...props} />)
|
||||
@ -313,7 +313,7 @@ describe('CrawledResultItem', () => {
|
||||
})
|
||||
|
||||
it('should call onCheckChange with false when checked item is clicked', async () => {
|
||||
const onCheckChange = jest.fn()
|
||||
const onCheckChange = vi.fn()
|
||||
const props = createItemProps({ isChecked: true, onCheckChange })
|
||||
|
||||
render(<CrawledResultItem {...props} />)
|
||||
@ -326,7 +326,7 @@ describe('CrawledResultItem', () => {
|
||||
|
||||
describe('Preview Behavior', () => {
|
||||
it('should call onPreview when preview button is clicked', async () => {
|
||||
const onPreview = jest.fn()
|
||||
const onPreview = vi.fn()
|
||||
const props = createItemProps({ onPreview })
|
||||
|
||||
render(<CrawledResultItem {...props} />)
|
||||
@ -371,8 +371,8 @@ describe('CrawledResult', () => {
|
||||
createCrawlResultItem({ source_url: 'https://page3.com', title: 'Page 3' }),
|
||||
],
|
||||
checkedList: [],
|
||||
onSelectedChange: jest.fn(),
|
||||
onPreview: jest.fn(),
|
||||
onSelectedChange: vi.fn(),
|
||||
onPreview: vi.fn(),
|
||||
usedTime: 2.5,
|
||||
...overrides,
|
||||
})
|
||||
@ -420,7 +420,7 @@ describe('CrawledResult', () => {
|
||||
|
||||
describe('Select All / Deselect All', () => {
|
||||
it('should call onSelectedChange with all items when select all is clicked', async () => {
|
||||
const onSelectedChange = jest.fn()
|
||||
const onSelectedChange = vi.fn()
|
||||
const list = [
|
||||
createCrawlResultItem({ source_url: 'https://page1.com' }),
|
||||
createCrawlResultItem({ source_url: 'https://page2.com' }),
|
||||
@ -434,7 +434,7 @@ describe('CrawledResult', () => {
|
||||
})
|
||||
|
||||
it('should call onSelectedChange with empty array when reset all is clicked', async () => {
|
||||
const onSelectedChange = jest.fn()
|
||||
const onSelectedChange = vi.fn()
|
||||
const list = [
|
||||
createCrawlResultItem({ source_url: 'https://page1.com' }),
|
||||
createCrawlResultItem({ source_url: 'https://page2.com' }),
|
||||
@ -450,7 +450,7 @@ describe('CrawledResult', () => {
|
||||
|
||||
describe('Individual Item Selection', () => {
|
||||
it('should add item to checkedList when unchecked item is checked', async () => {
|
||||
const onSelectedChange = jest.fn()
|
||||
const onSelectedChange = vi.fn()
|
||||
const list = [
|
||||
createCrawlResultItem({ source_url: 'https://page1.com', title: 'Page 1' }),
|
||||
createCrawlResultItem({ source_url: 'https://page2.com', title: 'Page 2' }),
|
||||
@ -464,7 +464,7 @@ describe('CrawledResult', () => {
|
||||
})
|
||||
|
||||
it('should remove item from checkedList when checked item is unchecked', async () => {
|
||||
const onSelectedChange = jest.fn()
|
||||
const onSelectedChange = vi.fn()
|
||||
const list = [
|
||||
createCrawlResultItem({ source_url: 'https://page1.com', title: 'Page 1' }),
|
||||
createCrawlResultItem({ source_url: 'https://page2.com', title: 'Page 2' }),
|
||||
@ -478,7 +478,7 @@ describe('CrawledResult', () => {
|
||||
})
|
||||
|
||||
it('should preserve other checked items when unchecking one item', async () => {
|
||||
const onSelectedChange = jest.fn()
|
||||
const onSelectedChange = vi.fn()
|
||||
const list = [
|
||||
createCrawlResultItem({ source_url: 'https://page1.com', title: 'Page 1' }),
|
||||
createCrawlResultItem({ source_url: 'https://page2.com', title: 'Page 2' }),
|
||||
@ -496,7 +496,7 @@ describe('CrawledResult', () => {
|
||||
|
||||
describe('Preview Behavior', () => {
|
||||
it('should call onPreview with correct item when preview is clicked', async () => {
|
||||
const onPreview = jest.fn()
|
||||
const onPreview = vi.fn()
|
||||
const list = [
|
||||
createCrawlResultItem({ source_url: 'https://page1.com', title: 'Page 1' }),
|
||||
createCrawlResultItem({ source_url: 'https://page2.com', title: 'Page 2' }),
|
||||
@ -513,7 +513,7 @@ describe('CrawledResult', () => {
|
||||
})
|
||||
|
||||
it('should track preview index correctly', async () => {
|
||||
const onPreview = jest.fn()
|
||||
const onPreview = vi.fn()
|
||||
const list = [
|
||||
createCrawlResultItem({ source_url: 'https://page1.com', title: 'Page 1' }),
|
||||
createCrawlResultItem({ source_url: 'https://page2.com', title: 'Page 2' }),
|
||||
|
||||
@ -3,7 +3,7 @@ import userEvent from '@testing-library/user-event'
|
||||
import UrlInput from './base/url-input'
|
||||
|
||||
// Mock doc link context
|
||||
jest.mock('@/context/i18n', () => ({
|
||||
vi.mock('@/context/i18n', () => ({
|
||||
useDocLink: () => () => 'https://docs.example.com',
|
||||
}))
|
||||
|
||||
@ -13,13 +13,13 @@ jest.mock('@/context/i18n', () => ({
|
||||
|
||||
describe('UrlInput', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
// Helper to create default props for UrlInput
|
||||
const createUrlInputProps = (overrides: Partial<Parameters<typeof UrlInput>[0]> = {}) => ({
|
||||
isRunning: false,
|
||||
onRun: jest.fn(),
|
||||
onRun: vi.fn(),
|
||||
...overrides,
|
||||
})
|
||||
|
||||
@ -78,7 +78,7 @@ describe('UrlInput', () => {
|
||||
|
||||
it('should show loading state on button when running', () => {
|
||||
// Arrange
|
||||
const onRun = jest.fn()
|
||||
const onRun = vi.fn()
|
||||
const props = createUrlInputProps({ isRunning: true, onRun })
|
||||
|
||||
// Act
|
||||
@ -148,7 +148,7 @@ describe('UrlInput', () => {
|
||||
describe('Button Click', () => {
|
||||
it('should call onRun with URL when button is clicked', async () => {
|
||||
// Arrange
|
||||
const onRun = jest.fn()
|
||||
const onRun = vi.fn()
|
||||
const props = createUrlInputProps({ onRun })
|
||||
|
||||
// Act
|
||||
@ -164,7 +164,7 @@ describe('UrlInput', () => {
|
||||
|
||||
it('should call onRun with empty string if no URL entered', async () => {
|
||||
// Arrange
|
||||
const onRun = jest.fn()
|
||||
const onRun = vi.fn()
|
||||
const props = createUrlInputProps({ onRun })
|
||||
|
||||
// Act
|
||||
@ -177,7 +177,7 @@ describe('UrlInput', () => {
|
||||
|
||||
it('should not call onRun when isRunning is true', async () => {
|
||||
// Arrange
|
||||
const onRun = jest.fn()
|
||||
const onRun = vi.fn()
|
||||
const props = createUrlInputProps({ onRun, isRunning: true })
|
||||
|
||||
// Act
|
||||
@ -191,7 +191,7 @@ describe('UrlInput', () => {
|
||||
|
||||
it('should not call onRun when already running', async () => {
|
||||
// Arrange
|
||||
const onRun = jest.fn()
|
||||
const onRun = vi.fn()
|
||||
|
||||
// First render with isRunning=false, type URL, then rerender with isRunning=true
|
||||
const { rerender } = render(<UrlInput isRunning={false} onRun={onRun} />)
|
||||
@ -211,7 +211,7 @@ describe('UrlInput', () => {
|
||||
|
||||
it('should prevent multiple clicks when already running', async () => {
|
||||
// Arrange
|
||||
const onRun = jest.fn()
|
||||
const onRun = vi.fn()
|
||||
const props = createUrlInputProps({ onRun, isRunning: true })
|
||||
|
||||
// Act
|
||||
@ -250,8 +250,8 @@ describe('UrlInput', () => {
|
||||
|
||||
it('should call updated onRun callback after prop change', async () => {
|
||||
// Arrange
|
||||
const onRun1 = jest.fn()
|
||||
const onRun2 = jest.fn()
|
||||
const onRun1 = vi.fn()
|
||||
const onRun2 = vi.fn()
|
||||
|
||||
// Act
|
||||
const { rerender } = render(<UrlInput isRunning={false} onRun={onRun1} />)
|
||||
@ -363,7 +363,7 @@ describe('UrlInput', () => {
|
||||
|
||||
it('should handle keyboard enter to trigger run', async () => {
|
||||
// Arrange - Note: This tests if the button can be activated via keyboard
|
||||
const onRun = jest.fn()
|
||||
const onRun = vi.fn()
|
||||
const props = createUrlInputProps({ onRun })
|
||||
|
||||
// Act
|
||||
@ -382,7 +382,7 @@ describe('UrlInput', () => {
|
||||
|
||||
it('should handle empty URL submission', async () => {
|
||||
// Arrange
|
||||
const onRun = jest.fn()
|
||||
const onRun = vi.fn()
|
||||
const props = createUrlInputProps({ onRun })
|
||||
|
||||
// Act
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import type { Mock } from 'vitest'
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import JinaReader from './index'
|
||||
@ -6,25 +7,25 @@ import { checkJinaReaderTaskStatus, createJinaReaderTask } from '@/service/datas
|
||||
import { sleep } from '@/utils'
|
||||
|
||||
// Mock external dependencies
|
||||
jest.mock('@/service/datasets', () => ({
|
||||
createJinaReaderTask: jest.fn(),
|
||||
checkJinaReaderTaskStatus: jest.fn(),
|
||||
vi.mock('@/service/datasets', () => ({
|
||||
createJinaReaderTask: vi.fn(),
|
||||
checkJinaReaderTaskStatus: vi.fn(),
|
||||
}))
|
||||
|
||||
jest.mock('@/utils', () => ({
|
||||
sleep: jest.fn(() => Promise.resolve()),
|
||||
vi.mock('@/utils', () => ({
|
||||
sleep: vi.fn(() => Promise.resolve()),
|
||||
}))
|
||||
|
||||
// Mock modal context
|
||||
const mockSetShowAccountSettingModal = jest.fn()
|
||||
jest.mock('@/context/modal-context', () => ({
|
||||
const mockSetShowAccountSettingModal = vi.fn()
|
||||
vi.mock('@/context/modal-context', () => ({
|
||||
useModalContext: () => ({
|
||||
setShowAccountSettingModal: mockSetShowAccountSettingModal,
|
||||
}),
|
||||
}))
|
||||
|
||||
// Mock doc link context
|
||||
jest.mock('@/context/i18n', () => ({
|
||||
vi.mock('@/context/i18n', () => ({
|
||||
useDocLink: () => () => 'https://docs.example.com',
|
||||
}))
|
||||
|
||||
@ -54,12 +55,12 @@ const createCrawlResultItem = (overrides: Partial<CrawlResultItem> = {}): CrawlR
|
||||
})
|
||||
|
||||
const createDefaultProps = (overrides: Partial<Parameters<typeof JinaReader>[0]> = {}) => ({
|
||||
onPreview: jest.fn(),
|
||||
onPreview: vi.fn(),
|
||||
checkedCrawlResult: [] as CrawlResultItem[],
|
||||
onCheckedCrawlResultChange: jest.fn(),
|
||||
onJobIdChange: jest.fn(),
|
||||
onCheckedCrawlResultChange: vi.fn(),
|
||||
onJobIdChange: vi.fn(),
|
||||
crawlOptions: createDefaultCrawlOptions(),
|
||||
onCrawlOptionsChange: jest.fn(),
|
||||
onCrawlOptionsChange: vi.fn(),
|
||||
...overrides,
|
||||
})
|
||||
|
||||
@ -68,7 +69,7 @@ const createDefaultProps = (overrides: Partial<Parameters<typeof JinaReader>[0]>
|
||||
// ============================================================================
|
||||
describe('JinaReader', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('Rendering', () => {
|
||||
@ -158,7 +159,7 @@ describe('JinaReader', () => {
|
||||
it('should call onCrawlOptionsChange when options change', async () => {
|
||||
// Arrange
|
||||
const user = userEvent.setup()
|
||||
const onCrawlOptionsChange = jest.fn()
|
||||
const onCrawlOptionsChange = vi.fn()
|
||||
const props = createDefaultProps({ onCrawlOptionsChange })
|
||||
|
||||
// Act
|
||||
@ -188,7 +189,7 @@ describe('JinaReader', () => {
|
||||
it('should execute crawl task when checkedCrawlResult is provided', async () => {
|
||||
// Arrange
|
||||
const checkedItem = createCrawlResultItem({ source_url: 'https://checked.com' })
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
mockCreateTask.mockResolvedValueOnce({
|
||||
data: {
|
||||
title: 'Test',
|
||||
@ -234,7 +235,7 @@ describe('JinaReader', () => {
|
||||
describe('State Management', () => {
|
||||
it('should transition from init to running state when run is clicked', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
let resolvePromise: () => void
|
||||
mockCreateTask.mockImplementation(() => new Promise((resolve) => {
|
||||
resolvePromise = () => resolve({ data: { title: 'T', content: 'C', description: 'D', url: 'https://example.com' } })
|
||||
@ -262,7 +263,7 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should transition to finished state after successful crawl', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
mockCreateTask.mockResolvedValueOnce({
|
||||
data: {
|
||||
title: 'Test Page',
|
||||
@ -288,8 +289,8 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should update crawl result state during polling', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'test-job-123' })
|
||||
mockCheckStatus
|
||||
@ -310,8 +311,8 @@ describe('JinaReader', () => {
|
||||
],
|
||||
})
|
||||
|
||||
const onCheckedCrawlResultChange = jest.fn()
|
||||
const onJobIdChange = jest.fn()
|
||||
const onCheckedCrawlResultChange = vi.fn()
|
||||
const onJobIdChange = vi.fn()
|
||||
const props = createDefaultProps({ onCheckedCrawlResultChange, onJobIdChange })
|
||||
|
||||
// Act
|
||||
@ -332,7 +333,7 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should fold options when step changes from init', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
mockCreateTask.mockResolvedValueOnce({
|
||||
data: {
|
||||
title: 'Test',
|
||||
@ -367,9 +368,9 @@ describe('JinaReader', () => {
|
||||
describe('Side Effects and Cleanup', () => {
|
||||
it('should call sleep during polling', async () => {
|
||||
// Arrange
|
||||
const mockSleep = sleep as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as jest.Mock
|
||||
const mockSleep = sleep as Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'test-job' })
|
||||
mockCheckStatus
|
||||
@ -392,7 +393,7 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should update controlFoldOptions when step changes', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
mockCreateTask.mockImplementation(() => new Promise((_resolve) => { /* pending */ }))
|
||||
|
||||
const props = createDefaultProps()
|
||||
@ -439,7 +440,7 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should memoize checkValid callback based on crawlOptions', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
mockCreateTask.mockResolvedValue({ data: { title: 'T', content: 'C', description: 'D', url: 'https://a.com' } })
|
||||
|
||||
const props = createDefaultProps()
|
||||
@ -483,7 +484,7 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should handle URL input and run button click', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
mockCreateTask.mockResolvedValueOnce({
|
||||
data: {
|
||||
title: 'Test',
|
||||
@ -512,8 +513,8 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should handle preview action on crawled result', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const onPreview = jest.fn()
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
const onPreview = vi.fn()
|
||||
const crawlResultData = {
|
||||
title: 'Preview Test',
|
||||
content: '# Content',
|
||||
@ -545,7 +546,7 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should handle checkbox changes in options', async () => {
|
||||
// Arrange
|
||||
const onCrawlOptionsChange = jest.fn()
|
||||
const onCrawlOptionsChange = vi.fn()
|
||||
const props = createDefaultProps({
|
||||
onCrawlOptionsChange,
|
||||
crawlOptions: createDefaultCrawlOptions({ crawl_sub_pages: false }),
|
||||
@ -593,7 +594,7 @@ describe('JinaReader', () => {
|
||||
describe('API Calls', () => {
|
||||
it('should call createJinaReaderTask with correct parameters', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
mockCreateTask.mockResolvedValueOnce({
|
||||
data: { title: 'T', content: 'C', description: 'D', url: 'https://api-test.com' },
|
||||
})
|
||||
@ -618,8 +619,8 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should handle direct data response from API', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const onCheckedCrawlResultChange = jest.fn()
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
const onCheckedCrawlResultChange = vi.fn()
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({
|
||||
data: {
|
||||
@ -651,9 +652,9 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should handle job_id response and poll for status', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as jest.Mock
|
||||
const onJobIdChange = jest.fn()
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as Mock
|
||||
const onJobIdChange = vi.fn()
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'poll-job-123' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -686,8 +687,8 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should handle failed status from polling', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'fail-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -713,8 +714,8 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should handle API error during status check', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'error-job' })
|
||||
mockCheckStatus.mockRejectedValueOnce({
|
||||
@ -737,9 +738,9 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should limit total to crawlOptions.limit', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as jest.Mock
|
||||
const onCheckedCrawlResultChange = jest.fn()
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as Mock
|
||||
const onCheckedCrawlResultChange = vi.fn()
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'limit-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -832,7 +833,7 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should accept URL with http:// protocol', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
mockCreateTask.mockResolvedValueOnce({
|
||||
data: { title: 'T', content: 'C', description: 'D', url: 'http://example.com' },
|
||||
})
|
||||
@ -907,10 +908,10 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should handle API throwing an exception', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
mockCreateTask.mockRejectedValueOnce(new Error('Network error'))
|
||||
// Suppress console output during test to avoid noisy logs
|
||||
const consoleSpy = jest.spyOn(console, 'log').mockImplementation(jest.fn())
|
||||
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(vi.fn())
|
||||
|
||||
const props = createDefaultProps()
|
||||
|
||||
@ -930,8 +931,8 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should handle status response without status field', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'no-status-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -955,8 +956,8 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should show unknown error when error message is empty', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'empty-error-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -980,9 +981,9 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should handle empty data array from API', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as jest.Mock
|
||||
const onCheckedCrawlResultChange = jest.fn()
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as Mock
|
||||
const onCheckedCrawlResultChange = vi.fn()
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'empty-data-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -1008,9 +1009,9 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should handle null data from running status', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as jest.Mock
|
||||
const onCheckedCrawlResultChange = jest.fn()
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as Mock
|
||||
const onCheckedCrawlResultChange = vi.fn()
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'null-data-job' })
|
||||
mockCheckStatus
|
||||
@ -1043,9 +1044,9 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should return empty array when completed job has undefined data', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as jest.Mock
|
||||
const onCheckedCrawlResultChange = jest.fn()
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as Mock
|
||||
const onCheckedCrawlResultChange = vi.fn()
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'undefined-data-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -1071,8 +1072,8 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should show zero current progress when crawlResult is not yet available', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'zero-current-job' })
|
||||
mockCheckStatus.mockImplementation(() => new Promise(() => { /* never resolves */ }))
|
||||
@ -1095,8 +1096,8 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should show 0/0 progress when limit is zero string', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'zero-total-job' })
|
||||
mockCheckStatus.mockImplementation(() => new Promise(() => { /* never resolves */ }))
|
||||
@ -1119,9 +1120,9 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should complete successfully when result data is undefined', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as jest.Mock
|
||||
const onCheckedCrawlResultChange = jest.fn()
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as Mock
|
||||
const onCheckedCrawlResultChange = vi.fn()
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'undefined-result-data-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -1148,8 +1149,8 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should use limit as total when crawlResult total is not available', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'no-total-job' })
|
||||
mockCheckStatus.mockImplementation(() => new Promise(() => { /* never resolves */ }))
|
||||
@ -1172,8 +1173,8 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should fallback to limit when crawlResult has zero total', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'both-zero-job' })
|
||||
mockCheckStatus
|
||||
@ -1203,8 +1204,8 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should construct result item from direct data response', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const onCheckedCrawlResultChange = jest.fn()
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
const onCheckedCrawlResultChange = vi.fn()
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({
|
||||
data: {
|
||||
@ -1241,7 +1242,7 @@ describe('JinaReader', () => {
|
||||
describe('Prop Variations', () => {
|
||||
it('should handle different limit values in crawlOptions', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
mockCreateTask.mockResolvedValueOnce({
|
||||
data: { title: 'T', content: 'C', description: 'D', url: 'https://limit.com' },
|
||||
})
|
||||
@ -1268,7 +1269,7 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should handle different max_depth values', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
mockCreateTask.mockResolvedValueOnce({
|
||||
data: { title: 'T', content: 'C', description: 'D', url: 'https://depth.com' },
|
||||
})
|
||||
@ -1295,7 +1296,7 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should handle crawl_sub_pages disabled', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
mockCreateTask.mockResolvedValueOnce({
|
||||
data: { title: 'T', content: 'C', description: 'D', url: 'https://nosub.com' },
|
||||
})
|
||||
@ -1322,7 +1323,7 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should handle use_sitemap enabled', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
mockCreateTask.mockResolvedValueOnce({
|
||||
data: { title: 'T', content: 'C', description: 'D', url: 'https://sitemap.com' },
|
||||
})
|
||||
@ -1349,7 +1350,7 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should handle includes and excludes patterns', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
mockCreateTask.mockResolvedValueOnce({
|
||||
data: { title: 'T', content: 'C', description: 'D', url: 'https://patterns.com' },
|
||||
})
|
||||
@ -1382,7 +1383,7 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should handle pre-selected crawl results', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
const existingResult = createCrawlResultItem({ source_url: 'https://existing.com' })
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({
|
||||
@ -1407,7 +1408,7 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should handle string type limit value', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
mockCreateTask.mockResolvedValueOnce({
|
||||
data: { title: 'T', content: 'C', description: 'D', url: 'https://string-limit.com' },
|
||||
})
|
||||
@ -1435,8 +1436,8 @@ describe('JinaReader', () => {
|
||||
describe('Display and UI States', () => {
|
||||
it('should show crawling progress during running state', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'progress-job' })
|
||||
mockCheckStatus.mockImplementation(() => new Promise((_resolve) => { /* pending */ })) // Never resolves
|
||||
@ -1459,7 +1460,7 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should display time consumed after crawl completion', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({
|
||||
data: { title: 'T', content: 'C', description: 'D', url: 'https://time.com' },
|
||||
@ -1481,7 +1482,7 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should display crawled results list after completion', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({
|
||||
data: {
|
||||
@ -1508,11 +1509,11 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should show error message component when crawl fails', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
|
||||
mockCreateTask.mockRejectedValueOnce(new Error('Failed'))
|
||||
// Suppress console output during test to avoid noisy logs
|
||||
jest.spyOn(console, 'log').mockImplementation(jest.fn())
|
||||
vi.spyOn(console, 'log').mockImplementation(vi.fn())
|
||||
|
||||
const props = createDefaultProps()
|
||||
|
||||
@ -1535,11 +1536,11 @@ describe('JinaReader', () => {
|
||||
describe('Integration', () => {
|
||||
it('should complete full crawl workflow with job polling', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as jest.Mock
|
||||
const onCheckedCrawlResultChange = jest.fn()
|
||||
const onJobIdChange = jest.fn()
|
||||
const onPreview = jest.fn()
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
const mockCheckStatus = checkJinaReaderTaskStatus as Mock
|
||||
const onCheckedCrawlResultChange = vi.fn()
|
||||
const onJobIdChange = vi.fn()
|
||||
const onPreview = vi.fn()
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'full-workflow-job' })
|
||||
mockCheckStatus
|
||||
@ -1600,8 +1601,8 @@ describe('JinaReader', () => {
|
||||
|
||||
it('should handle select all and deselect all in results', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createJinaReaderTask as jest.Mock
|
||||
const onCheckedCrawlResultChange = jest.fn()
|
||||
const mockCreateTask = createJinaReaderTask as Mock
|
||||
const onCheckedCrawlResultChange = vi.fn()
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({
|
||||
data: { title: 'Single', content: 'C', description: 'D', url: 'https://single.com' },
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import type { Mock } from 'vitest'
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import WaterCrawl from './index'
|
||||
@ -6,18 +7,18 @@ import { checkWatercrawlTaskStatus, createWatercrawlTask } from '@/service/datas
|
||||
import { sleep } from '@/utils'
|
||||
|
||||
// Mock external dependencies
|
||||
jest.mock('@/service/datasets', () => ({
|
||||
createWatercrawlTask: jest.fn(),
|
||||
checkWatercrawlTaskStatus: jest.fn(),
|
||||
vi.mock('@/service/datasets', () => ({
|
||||
createWatercrawlTask: vi.fn(),
|
||||
checkWatercrawlTaskStatus: vi.fn(),
|
||||
}))
|
||||
|
||||
jest.mock('@/utils', () => ({
|
||||
sleep: jest.fn(() => Promise.resolve()),
|
||||
vi.mock('@/utils', () => ({
|
||||
sleep: vi.fn(() => Promise.resolve()),
|
||||
}))
|
||||
|
||||
// Mock modal context
|
||||
const mockSetShowAccountSettingModal = jest.fn()
|
||||
jest.mock('@/context/modal-context', () => ({
|
||||
const mockSetShowAccountSettingModal = vi.fn()
|
||||
vi.mock('@/context/modal-context', () => ({
|
||||
useModalContext: () => ({
|
||||
setShowAccountSettingModal: mockSetShowAccountSettingModal,
|
||||
}),
|
||||
@ -49,12 +50,12 @@ const createCrawlResultItem = (overrides: Partial<CrawlResultItem> = {}): CrawlR
|
||||
})
|
||||
|
||||
const createDefaultProps = (overrides: Partial<Parameters<typeof WaterCrawl>[0]> = {}) => ({
|
||||
onPreview: jest.fn(),
|
||||
onPreview: vi.fn(),
|
||||
checkedCrawlResult: [] as CrawlResultItem[],
|
||||
onCheckedCrawlResultChange: jest.fn(),
|
||||
onJobIdChange: jest.fn(),
|
||||
onCheckedCrawlResultChange: vi.fn(),
|
||||
onJobIdChange: vi.fn(),
|
||||
crawlOptions: createDefaultCrawlOptions(),
|
||||
onCrawlOptionsChange: jest.fn(),
|
||||
onCrawlOptionsChange: vi.fn(),
|
||||
...overrides,
|
||||
})
|
||||
|
||||
@ -63,7 +64,7 @@ const createDefaultProps = (overrides: Partial<Parameters<typeof WaterCrawl>[0]>
|
||||
// ============================================================================
|
||||
describe('WaterCrawl', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
// Tests for initial component rendering
|
||||
@ -154,7 +155,7 @@ describe('WaterCrawl', () => {
|
||||
it('should call onCrawlOptionsChange when options change', async () => {
|
||||
// Arrange
|
||||
const user = userEvent.setup()
|
||||
const onCrawlOptionsChange = jest.fn()
|
||||
const onCrawlOptionsChange = vi.fn()
|
||||
const props = createDefaultProps({ onCrawlOptionsChange })
|
||||
|
||||
// Act
|
||||
@ -184,10 +185,10 @@ describe('WaterCrawl', () => {
|
||||
it('should execute crawl task when checkedCrawlResult is provided', async () => {
|
||||
// Arrange
|
||||
const checkedItem = createCrawlResultItem({ source_url: 'https://checked.com' })
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'test-job' })
|
||||
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
status: 'completed',
|
||||
current: 1,
|
||||
@ -231,7 +232,7 @@ describe('WaterCrawl', () => {
|
||||
describe('State Management', () => {
|
||||
it('should transition from init to running state when run is clicked', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
let resolvePromise: () => void
|
||||
mockCreateTask.mockImplementation(() => new Promise((resolve) => {
|
||||
resolvePromise = () => resolve({ job_id: 'test-job' })
|
||||
@ -259,8 +260,8 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should transition to finished state after successful crawl', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'test-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -286,8 +287,8 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should update crawl result state during polling', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'test-job-123' })
|
||||
mockCheckStatus
|
||||
@ -308,8 +309,8 @@ describe('WaterCrawl', () => {
|
||||
],
|
||||
})
|
||||
|
||||
const onCheckedCrawlResultChange = jest.fn()
|
||||
const onJobIdChange = jest.fn()
|
||||
const onCheckedCrawlResultChange = vi.fn()
|
||||
const onJobIdChange = vi.fn()
|
||||
const props = createDefaultProps({ onCheckedCrawlResultChange, onJobIdChange })
|
||||
|
||||
// Act
|
||||
@ -330,8 +331,8 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should fold options when step changes from init', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'test-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -366,9 +367,9 @@ describe('WaterCrawl', () => {
|
||||
describe('Side Effects and Cleanup', () => {
|
||||
it('should call sleep during polling', async () => {
|
||||
// Arrange
|
||||
const mockSleep = sleep as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockSleep = sleep as Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'test-job' })
|
||||
mockCheckStatus
|
||||
@ -391,7 +392,7 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should update controlFoldOptions when step changes', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
mockCreateTask.mockImplementation(() => new Promise(() => { /* pending */ }))
|
||||
|
||||
const props = createDefaultProps()
|
||||
@ -438,8 +439,8 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should memoize checkValid callback based on crawlOptions', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValue({ job_id: 'test-job' })
|
||||
mockCheckStatus.mockResolvedValue({
|
||||
@ -490,8 +491,8 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should handle URL input and run button click', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'test-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -520,9 +521,9 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should handle preview action on crawled result', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const onPreview = jest.fn()
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
const onPreview = vi.fn()
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'test-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -554,7 +555,7 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should handle checkbox changes in options', async () => {
|
||||
// Arrange
|
||||
const onCrawlOptionsChange = jest.fn()
|
||||
const onCrawlOptionsChange = vi.fn()
|
||||
const props = createDefaultProps({
|
||||
onCrawlOptionsChange,
|
||||
crawlOptions: createDefaultCrawlOptions({ crawl_sub_pages: false }),
|
||||
@ -602,8 +603,8 @@ describe('WaterCrawl', () => {
|
||||
describe('API Calls', () => {
|
||||
it('should call createWatercrawlTask with correct parameters', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'api-test-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -633,8 +634,8 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should delete max_depth from options when it is empty string', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'test-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -662,9 +663,9 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should poll for status with job_id', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const onJobIdChange = jest.fn()
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
const onJobIdChange = vi.fn()
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'poll-job-123' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -697,8 +698,8 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should handle error status from polling', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'fail-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -724,8 +725,8 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should handle API error during status check', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'error-job' })
|
||||
mockCheckStatus.mockRejectedValueOnce({
|
||||
@ -748,9 +749,9 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should limit total to crawlOptions.limit', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const onCheckedCrawlResultChange = jest.fn()
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
const onCheckedCrawlResultChange = vi.fn()
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'limit-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -781,8 +782,8 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should handle response without status field as error', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'no-status-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -868,8 +869,8 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should accept URL with http:// protocol', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'http-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -949,10 +950,10 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should handle API throwing an exception', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
mockCreateTask.mockRejectedValueOnce(new Error('Network error'))
|
||||
// Suppress console output during test to avoid noisy logs
|
||||
const consoleSpy = jest.spyOn(console, 'log').mockImplementation(jest.fn())
|
||||
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(vi.fn())
|
||||
|
||||
const props = createDefaultProps()
|
||||
|
||||
@ -972,8 +973,8 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should show unknown error when error message is empty', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'empty-error-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -997,9 +998,9 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should handle empty data array from API', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const onCheckedCrawlResultChange = jest.fn()
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
const onCheckedCrawlResultChange = vi.fn()
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'empty-data-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -1025,9 +1026,9 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should handle null data from running status', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const onCheckedCrawlResultChange = jest.fn()
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
const onCheckedCrawlResultChange = vi.fn()
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'null-data-job' })
|
||||
mockCheckStatus
|
||||
@ -1060,9 +1061,9 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should handle undefined data from completed job polling', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const onCheckedCrawlResultChange = jest.fn()
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
const onCheckedCrawlResultChange = vi.fn()
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'undefined-data-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -1088,8 +1089,8 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should handle crawlResult with zero current value', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'zero-current-job' })
|
||||
mockCheckStatus.mockImplementation(() => new Promise(() => { /* never resolves */ }))
|
||||
@ -1112,8 +1113,8 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should handle crawlResult with zero total and empty limit', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'zero-total-job' })
|
||||
mockCheckStatus.mockImplementation(() => new Promise(() => { /* never resolves */ }))
|
||||
@ -1136,9 +1137,9 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should handle undefined crawlResult data in finished state', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const onCheckedCrawlResultChange = jest.fn()
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
const onCheckedCrawlResultChange = vi.fn()
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'undefined-result-data-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -1165,8 +1166,8 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should use parseFloat fallback when crawlResult.total is undefined', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'no-total-job' })
|
||||
mockCheckStatus.mockImplementation(() => new Promise(() => { /* never resolves */ }))
|
||||
@ -1189,8 +1190,8 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should handle crawlResult with current=0 and total=0 during running', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'both-zero-job' })
|
||||
mockCheckStatus
|
||||
@ -1225,8 +1226,8 @@ describe('WaterCrawl', () => {
|
||||
describe('Prop Variations', () => {
|
||||
it('should handle different limit values in crawlOptions', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'limit-var-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -1258,8 +1259,8 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should handle different max_depth values', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'depth-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -1291,8 +1292,8 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should handle crawl_sub_pages disabled', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'nosub-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -1324,8 +1325,8 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should handle use_sitemap enabled', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'sitemap-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -1357,8 +1358,8 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should handle includes and excludes patterns', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'patterns-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -1396,8 +1397,8 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should handle pre-selected crawl results', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
const existingResult = createCrawlResultItem({ source_url: 'https://existing.com' })
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'preselect-job' })
|
||||
@ -1426,8 +1427,8 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should handle string type limit value', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'string-limit-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -1455,8 +1456,8 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should handle only_main_content option', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'main-content-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -1493,8 +1494,8 @@ describe('WaterCrawl', () => {
|
||||
describe('Display and UI States', () => {
|
||||
it('should show crawling progress during running state', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'progress-job' })
|
||||
mockCheckStatus.mockImplementation(() => new Promise(() => { /* pending */ }))
|
||||
@ -1517,8 +1518,8 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should display time consumed after crawl completion', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'time-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -1545,8 +1546,8 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should display crawled results list after completion', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'result-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -1572,11 +1573,11 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should show error message component when crawl fails', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
|
||||
mockCreateTask.mockRejectedValueOnce(new Error('Failed'))
|
||||
// Suppress console output during test to avoid noisy logs
|
||||
jest.spyOn(console, 'log').mockImplementation(jest.fn())
|
||||
vi.spyOn(console, 'log').mockImplementation(vi.fn())
|
||||
|
||||
const props = createDefaultProps()
|
||||
|
||||
@ -1594,9 +1595,9 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should update progress during multiple polling iterations', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const onCheckedCrawlResultChange = jest.fn()
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
const onCheckedCrawlResultChange = vi.fn()
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'multi-poll-job' })
|
||||
mockCheckStatus
|
||||
@ -1659,11 +1660,11 @@ describe('WaterCrawl', () => {
|
||||
describe('Integration', () => {
|
||||
it('should complete full crawl workflow with job polling', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const onCheckedCrawlResultChange = jest.fn()
|
||||
const onJobIdChange = jest.fn()
|
||||
const onPreview = jest.fn()
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
const onCheckedCrawlResultChange = vi.fn()
|
||||
const onJobIdChange = vi.fn()
|
||||
const onPreview = vi.fn()
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'full-workflow-job' })
|
||||
mockCheckStatus
|
||||
@ -1724,9 +1725,9 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should handle select all and deselect all in results', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const onCheckedCrawlResultChange = jest.fn()
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
const onCheckedCrawlResultChange = vi.fn()
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'select-all-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
@ -1759,11 +1760,11 @@ describe('WaterCrawl', () => {
|
||||
|
||||
it('should handle complete workflow from input to preview', async () => {
|
||||
// Arrange
|
||||
const mockCreateTask = createWatercrawlTask as jest.Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as jest.Mock
|
||||
const onPreview = jest.fn()
|
||||
const onCheckedCrawlResultChange = jest.fn()
|
||||
const onJobIdChange = jest.fn()
|
||||
const mockCreateTask = createWatercrawlTask as Mock
|
||||
const mockCheckStatus = checkWatercrawlTaskStatus as Mock
|
||||
const onPreview = vi.fn()
|
||||
const onCheckedCrawlResultChange = vi.fn()
|
||||
const onJobIdChange = vi.fn()
|
||||
|
||||
mockCreateTask.mockResolvedValueOnce({ job_id: 'preview-workflow-job' })
|
||||
mockCheckStatus.mockResolvedValueOnce({
|
||||
|
||||
Reference in New Issue
Block a user