diff --git a/web/app/components/datasets/common/__tests__/chunking-mode-label.spec.tsx b/web/app/components/datasets/common/__tests__/chunking-mode-label.spec.tsx
index 92ea3929dc6..8fd1ba43dc6 100644
--- a/web/app/components/datasets/common/__tests__/chunking-mode-label.spec.tsx
+++ b/web/app/components/datasets/common/__tests__/chunking-mode-label.spec.tsx
@@ -1,61 +1,26 @@
import { render, screen } from '@testing-library/react'
-import { describe, expect, it } from 'vitest'
import ChunkingModeLabel from '../chunking-mode-label'
describe('ChunkingModeLabel', () => {
- describe('Rendering', () => {
- it('should render without crashing', () => {
- render(
)
- expect(screen.getByText(/general/i)).toBeInTheDocument()
- })
+ it.each([
+ {
+ isGeneralMode: true,
+ isQAMode: false,
+ label: 'dataset.chunkingMode.general',
+ },
+ {
+ isGeneralMode: true,
+ isQAMode: true,
+ label: 'dataset.chunkingMode.general ยท QA',
+ },
+ {
+ isGeneralMode: false,
+ isQAMode: true,
+ label: 'dataset.chunkingMode.parentChild',
+ },
+ ])('displays $label for a supported chunking mode', ({ isGeneralMode, isQAMode, label }) => {
+ render(
)
- it('should render with Badge wrapper', () => {
- const { container } = render(
)
- // Badge component renders with specific styles
- expect(container.querySelector('.flex')).toBeInTheDocument()
- })
- })
-
- describe('Props', () => {
- it('should display general mode text when isGeneralMode is true', () => {
- render(
)
- expect(screen.getByText(/general/i)).toBeInTheDocument()
- })
-
- it('should display parent-child mode text when isGeneralMode is false', () => {
- render(
)
- expect(screen.getByText(/parentChild/i)).toBeInTheDocument()
- })
-
- it('should append QA suffix when isGeneralMode and isQAMode are both true', () => {
- render(
)
- expect(screen.getByText(/general.*QA/i)).toBeInTheDocument()
- })
-
- it('should not append QA suffix when isGeneralMode is true but isQAMode is false', () => {
- render(
)
- const text = screen.getByText(/general/i)
- expect(text.textContent).not.toContain('QA')
- })
-
- it('should not display QA suffix for parent-child mode even when isQAMode is true', () => {
- render(
)
- expect(screen.getByText(/parentChild/i)).toBeInTheDocument()
- expect(screen.queryByText(/QA/i)).not.toBeInTheDocument()
- })
- })
-
- describe('Edge Cases', () => {
- it('should render icon element', () => {
- const { container } = render(
)
- const iconElement = container.querySelector('svg')
- expect(iconElement).toBeInTheDocument()
- })
-
- it('should apply correct icon size classes', () => {
- const { container } = render(
)
- const iconElement = container.querySelector('svg')
- expect(iconElement).toHaveClass('size-3')
- })
+ expect(screen.getByText(label)).toBeInTheDocument()
})
})
diff --git a/web/app/components/datasets/common/__tests__/credential-icon.spec.tsx b/web/app/components/datasets/common/__tests__/credential-icon.spec.tsx
index e059f7c6226..5ef72058986 100644
--- a/web/app/components/datasets/common/__tests__/credential-icon.spec.tsx
+++ b/web/app/components/datasets/common/__tests__/credential-icon.spec.tsx
@@ -1,136 +1,25 @@
import { fireEvent, render, screen } from '@testing-library/react'
-import { describe, expect, it } from 'vitest'
import { CredentialIcon } from '../credential-icon'
describe('CredentialIcon', () => {
- describe('Rendering', () => {
- it('should render without crashing', () => {
- render(
)
- expect(screen.getByText('T')).toBeInTheDocument()
- })
+ it('shows the credential initial when there is no avatar', () => {
+ render(
)
- it('should render first letter when no avatar provided', () => {
- render(
)
- expect(screen.getByText('A')).toBeInTheDocument()
- })
-
- it('should render image when avatarUrl is provided', () => {
- render(
)
- const img = screen.getByRole('img')
- expect(img).toBeInTheDocument()
- expect(img).toHaveAttribute('src', 'https://example.com/avatar.png')
- })
+ expect(screen.getByText('A')).toBeInTheDocument()
})
- describe('Props', () => {
- it('should apply default size of 20px', () => {
- const { container } = render(
)
- const wrapper = container.firstChild as HTMLElement
- expect(wrapper).toHaveStyle({ width: '20px', height: '20px' })
- })
+ it('shows the credential avatar when one is configured', () => {
+ render(
)
- it('should apply custom size', () => {
- const { container } = render(
)
- const wrapper = container.firstChild as HTMLElement
- expect(wrapper).toHaveStyle({ width: '40px', height: '40px' })
- })
-
- it('should apply custom className', () => {
- const { container } = render(
)
- const wrapper = container.firstChild as HTMLElement
- expect(wrapper).toHaveClass('custom-class')
- })
-
- it('should uppercase the first letter', () => {
- render(
)
- expect(screen.getByText('B')).toBeInTheDocument()
- })
-
- it('should render fallback when avatarUrl is "default"', () => {
- render(
)
- expect(screen.getByText('T')).toBeInTheDocument()
- expect(screen.queryByRole('img')).not.toBeInTheDocument()
- })
+ expect(screen.getByRole('img')).toHaveAttribute('src', 'https://example.com/avatar.png')
})
- describe('User Interactions', () => {
- it('should fallback to letter when image fails to load', () => {
- render(
)
+ it('falls back to the credential initial when the avatar fails to load', () => {
+ render(
)
- // Initially shows image
- const img = screen.getByRole('img')
- expect(img).toBeInTheDocument()
+ fireEvent.error(screen.getByRole('img'))
- // Trigger error event
- fireEvent.error(img)
-
- // Should now show letter fallback
- expect(screen.getByText('T')).toBeInTheDocument()
- expect(screen.queryByRole('img')).not.toBeInTheDocument()
- })
- })
-
- describe('Edge Cases', () => {
- it('should handle single character name', () => {
- render(
)
- expect(screen.getByText('A')).toBeInTheDocument()
- })
-
- it('should handle name starting with number', () => {
- render(
)
- expect(screen.getByText('1')).toBeInTheDocument()
- })
-
- it('should handle name starting with special character', () => {
- render(
)
- expect(screen.getByText('@')).toBeInTheDocument()
- })
-
- it('should assign consistent background colors based on first letter', () => {
- // Same first letter should get same color
- const { container: container1 } = render(
)
- const { container: container2 } = render(
)
-
- const wrapper1 = container1.firstChild as HTMLElement
- const wrapper2 = container2.firstChild as HTMLElement
-
- // Both should have the same bg class since they start with 'A'
- const classes1 = wrapper1.className
- const classes2 = wrapper2.className
-
- const bgClass1 = /bg-components-icon-bg-\S+/.exec(classes1)?.[0]
- const bgClass2 = /bg-components-icon-bg-\S+/.exec(classes2)?.[0]
-
- expect(bgClass1).toBe(bgClass2)
- })
-
- it('should apply different background colors for different letters', () => {
- // 'A' (65) % 4 = 1 โ pink, 'B' (66) % 4 = 2 โ indigo
- const { container: container1 } = render(
)
- const { container: container2 } = render(
)
-
- const wrapper1 = container1.firstChild as HTMLElement
- const wrapper2 = container2.firstChild as HTMLElement
-
- const bgClass1 = /bg-components-icon-bg-\S+/.exec(wrapper1.className)?.[0]
- const bgClass2 = /bg-components-icon-bg-\S+/.exec(wrapper2.className)?.[0]
-
- expect(bgClass1).toBeDefined()
- expect(bgClass2).toBeDefined()
- expect(bgClass1).not.toBe(bgClass2)
- })
-
- it('should handle empty avatarUrl string', () => {
- render(
)
- expect(screen.getByText('T')).toBeInTheDocument()
- expect(screen.queryByRole('img')).not.toBeInTheDocument()
- })
-
- it('should render image with correct dimensions', () => {
- render(
)
- const img = screen.getByRole('img')
- expect(img).toHaveAttribute('width', '32')
- expect(img).toHaveAttribute('height', '32')
- })
+ expect(screen.queryByRole('img')).not.toBeInTheDocument()
+ expect(screen.getByText('A')).toBeInTheDocument()
})
})
diff --git a/web/app/components/datasets/common/__tests__/document-file-icon.spec.tsx b/web/app/components/datasets/common/__tests__/document-file-icon.spec.tsx
deleted file mode 100644
index 774e8e1da4e..00000000000
--- a/web/app/components/datasets/common/__tests__/document-file-icon.spec.tsx
+++ /dev/null
@@ -1,115 +0,0 @@
-import { render } from '@testing-library/react'
-import { describe, expect, it } from 'vitest'
-import DocumentFileIcon from '../document-file-icon'
-
-describe('DocumentFileIcon', () => {
- describe('Rendering', () => {
- it('should render without crashing', () => {
- const { container } = render(
)
- expect(container.firstChild).toBeInTheDocument()
- })
-
- it('should render FileTypeIcon component', () => {
- const { container } = render(
)
- // FileTypeIcon renders an svg or img element
- expect(container.querySelector('svg, img')).toBeInTheDocument()
- })
- })
-
- describe('Props', () => {
- it('should determine type from extension prop', () => {
- const { container } = render(
)
- expect(container.firstChild).toBeInTheDocument()
- })
-
- it('should determine type from name when extension not provided', () => {
- const { container } = render(
)
- expect(container.firstChild).toBeInTheDocument()
- })
-
- it('should handle uppercase extension', () => {
- const { container } = render(
)
- expect(container.firstChild).toBeInTheDocument()
- })
-
- it('should handle uppercase name extension', () => {
- const { container } = render(
)
- expect(container.firstChild).toBeInTheDocument()
- })
-
- it('should apply custom className', () => {
- const { container } = render(
)
- expect(container.querySelector('.custom-icon')).toBeInTheDocument()
- })
-
- it('should pass size prop to FileTypeIcon', () => {
- // Testing different size values
- const { container: smContainer } = render(
)
- const { container: lgContainer } = render(
)
-
- expect(smContainer.firstChild).toBeInTheDocument()
- expect(lgContainer.firstChild).toBeInTheDocument()
- })
- })
-
- describe('File Type Mapping', () => {
- const testCases = [
- { extension: 'pdf', description: 'PDF files' },
- { extension: 'json', description: 'JSON files' },
- { extension: 'html', description: 'HTML files' },
- { extension: 'txt', description: 'TXT files' },
- { extension: 'markdown', description: 'Markdown files' },
- { extension: 'md', description: 'MD files' },
- { extension: 'xlsx', description: 'XLSX files' },
- { extension: 'xls', description: 'XLS files' },
- { extension: 'csv', description: 'CSV files' },
- { extension: 'doc', description: 'DOC files' },
- { extension: 'docx', description: 'DOCX files' },
- ]
-
- testCases.forEach(({ extension, description }) => {
- it(`should handle ${description}`, () => {
- const { container } = render(
)
- expect(container.firstChild).toBeInTheDocument()
- })
- })
- })
-
- describe('Edge Cases', () => {
- it('should handle unknown extension with default document type', () => {
- const { container } = render(
)
- expect(container.firstChild).toBeInTheDocument()
- })
-
- it('should handle empty extension string', () => {
- const { container } = render(
)
- expect(container.firstChild).toBeInTheDocument()
- })
-
- it('should handle name without extension', () => {
- const { container } = render(
)
- expect(container.firstChild).toBeInTheDocument()
- })
-
- it('should handle name with multiple dots', () => {
- const { container } = render(
)
- expect(container.firstChild).toBeInTheDocument()
- })
-
- it('should prioritize extension over name', () => {
- // If both are provided, extension should take precedence
- const { container } = render(
)
- expect(container.firstChild).toBeInTheDocument()
- })
-
- it('should handle undefined extension and name', () => {
- const { container } = render(
)
- expect(container.firstChild).toBeInTheDocument()
- })
-
- it('should apply default size of md', () => {
- const { container } = render(
)
- expect(container.firstChild).toBeInTheDocument()
- })
- })
-})
diff --git a/web/app/components/datasets/common/document-picker/__tests__/document-list.spec.tsx b/web/app/components/datasets/common/document-picker/__tests__/document-list.spec.tsx
index 28017d606e5..55d161c9506 100644
--- a/web/app/components/datasets/common/document-picker/__tests__/document-list.spec.tsx
+++ b/web/app/components/datasets/common/document-picker/__tests__/document-list.spec.tsx
@@ -7,7 +7,7 @@ import DocumentList from '../document-list'
vi.mock('../../document-file-icon', () => ({
default: ({ name, extension }: { name?: string; extension?: string }) => (
-
+
{name}.{extension}
),
@@ -71,11 +71,7 @@ const renderDocumentList = (list: SimpleDocumentDetail[], onValueChange = vi.fn(
})
describe('DocumentList', () => {
- beforeEach(() => {
- vi.clearAllMocks()
- })
-
- it('should render documents as combobox options', () => {
+ it('renders documents as combobox options', () => {
renderDocumentList([
createDocument({ id: 'doc-1', name: 'report' }),
createDocument({ id: 'doc-2', name: 'data' }),
@@ -83,16 +79,9 @@ describe('DocumentList', () => {
expect(screen.getByRole('option', { name: /report/ })).toBeInTheDocument()
expect(screen.getByRole('option', { name: /data/ })).toBeInTheDocument()
- expect(screen.getAllByTestId('file-icon')).toHaveLength(2)
})
- it('should keep item spacing symmetric with the search field', () => {
- renderDocumentList([createDocument({ id: 'doc-1', name: 'report' })])
-
- expect(screen.getByRole('option', { name: /report/ })).toHaveClass('px-3')
- })
-
- it('should select a document through combobox value change', async () => {
+ it('selects a document through the combobox', async () => {
const user = userEvent.setup()
const selectedDocument = createDocument({ id: 'doc-1', name: 'report' })
const { onValueChange } = renderDocumentList([selectedDocument])
diff --git a/web/app/components/datasets/common/document-picker/__tests__/preview-document-picker.spec.tsx b/web/app/components/datasets/common/document-picker/__tests__/preview-document-picker.spec.tsx
index e5070ff9147..323b4a9ddd6 100644
--- a/web/app/components/datasets/common/document-picker/__tests__/preview-document-picker.spec.tsx
+++ b/web/app/components/datasets/common/document-picker/__tests__/preview-document-picker.spec.tsx
@@ -56,12 +56,6 @@ describe('PreviewDocumentPicker', () => {
// Tests for basic rendering
describe('Rendering', () => {
- it('should render without crashing', () => {
- renderComponent()
-
- expect(screen.getByTestId('popover')).toBeInTheDocument()
- })
-
it('should render document name from value prop', () => {
renderComponent({
value: createMockDocumentItem({ name: 'My Document' }),
@@ -116,20 +110,6 @@ describe('PreviewDocumentPicker', () => {
expect(screen.getByTestId('popover')).toBeInTheDocument()
})
- it('should apply className to trigger element', () => {
- renderComponent({ className: 'custom-class' })
-
- const trigger = screen.getByTestId('popover-trigger')
- expect(trigger).toHaveClass('custom-class')
- })
-
- it('should handle empty files array', () => {
- // Component should render without crashing with empty files
- renderComponent({ files: [] })
-
- expect(screen.getByTestId('popover')).toBeInTheDocument()
- })
-
it('should handle single file', () => {
// Component should accept single file
renderComponent({
@@ -228,10 +208,6 @@ describe('PreviewDocumentPicker', () => {
// Tests for component memoization
describe('Component Memoization', () => {
- it('should be wrapped with React.memo', () => {
- expect((PreviewDocumentPicker as unknown as { $$typeof: symbol }).$$typeof).toBeDefined()
- })
-
it('should not re-render when props are the same', () => {
const onChange = vi.fn()
const value = createMockDocumentItem()
@@ -320,13 +296,6 @@ describe('PreviewDocumentPicker', () => {
expect(screen.getByTestId('popover')).toBeInTheDocument()
})
- it('should handle empty files array', () => {
- renderComponent({ files: [] })
-
- // Component should render without crashing
- expect(screen.getByTestId('popover')).toBeInTheDocument()
- })
-
it('should handle very long document names', () => {
const longName = 'A'.repeat(500)
renderComponent({
@@ -345,18 +314,6 @@ describe('PreviewDocumentPicker', () => {
expect(screen.getByText(specialName)).toBeInTheDocument()
})
- it('should handle undefined files prop', () => {
- // Test edge case where files might be undefined at runtime
- const props = createDefaultProps()
- // @ts-expect-error - Testing runtime edge case
- props.files = undefined
-
- render()
-
- // Component should render without crashing
- expect(screen.getByTestId('popover')).toBeInTheDocument()
- })
-
it('should handle large number of files', () => {
const manyFiles = createMockDocumentList(100)
renderComponent({ files: manyFiles })
@@ -427,29 +384,6 @@ describe('PreviewDocumentPicker', () => {
})
})
- describe('className variations', () => {
- it('should apply custom className', () => {
- renderComponent({ className: 'my-custom-class' })
-
- const trigger = screen.getByTestId('popover-trigger')
- expect(trigger).toHaveClass('my-custom-class')
- })
-
- it('should work without className', () => {
- renderComponent({ className: undefined })
-
- expect(screen.getByTestId('popover-trigger')).toBeInTheDocument()
- })
-
- it('should handle multiple class names', () => {
- renderComponent({ className: 'class-one class-two' })
-
- const trigger = screen.getByTestId('popover-trigger')
- expect(trigger).toHaveClass('class-one')
- expect(trigger).toHaveClass('class-two')
- })
- })
-
describe('extension variations', () => {
const extensions = ['txt', 'pdf', 'docx', 'xlsx', 'md']
@@ -516,22 +450,6 @@ describe('PreviewDocumentPicker', () => {
// Tests for visual states
describe('Visual States', () => {
- it('should apply hover styles on trigger', () => {
- renderComponent()
-
- const trigger = screen.getByTestId('popover-trigger')
- expect(trigger).toHaveClass('hover:bg-state-base-hover')
- })
-
- it('should have truncate class for long names', () => {
- renderComponent({
- value: createMockDocumentItem({ name: 'Very Long Document Name' }),
- })
-
- const nameElement = screen.getByText('Very Long Document Name')
- expect(nameElement).toHaveClass('truncate')
- })
-
it('should have max-width on name element', () => {
renderComponent({
value: createMockDocumentItem({ name: 'Test' }),
diff --git a/web/app/components/datasets/common/document-status-with-action/__tests__/status-with-action.spec.tsx b/web/app/components/datasets/common/document-status-with-action/__tests__/status-with-action.spec.tsx
deleted file mode 100644
index 2ce3e5cb09f..00000000000
--- a/web/app/components/datasets/common/document-status-with-action/__tests__/status-with-action.spec.tsx
+++ /dev/null
@@ -1,149 +0,0 @@
-import { fireEvent, render, screen } from '@testing-library/react'
-import { describe, expect, it, vi } from 'vitest'
-import StatusWithAction from '../status-with-action'
-
-describe('StatusWithAction', () => {
- describe('Rendering', () => {
- it('should render without crashing', () => {
- render()
- expect(screen.getByText('Test description')).toBeInTheDocument()
- })
-
- it('should render description text', () => {
- render()
- expect(screen.getByText('This is a test message')).toBeInTheDocument()
- })
-
- it('should render icon based on type', () => {
- const { container } = render()
- expect(container.querySelector('svg')).toBeInTheDocument()
- })
- })
-
- describe('Props', () => {
- it('should default to info type when type is not provided', () => {
- const { container } = render()
- const icon = container.querySelector('svg')
- expect(icon).toHaveClass('text-text-accent')
- })
-
- it('should render success type with correct color', () => {
- const { container } = render()
- const icon = container.querySelector('svg')
- expect(icon).toHaveClass('text-text-success')
- })
-
- it('should render error type with correct color', () => {
- const { container } = render()
- const icon = container.querySelector('svg')
- expect(icon).toHaveClass('text-text-destructive')
- })
-
- it('should render warning type with correct color', () => {
- const { container } = render()
- const icon = container.querySelector('svg')
- expect(icon).toHaveClass('text-text-warning-secondary')
- })
-
- it('should render info type with correct color', () => {
- const { container } = render()
- const icon = container.querySelector('svg')
- expect(icon).toHaveClass('text-text-accent')
- })
-
- it('should render action button when actionText and onAction are provided', () => {
- const onAction = vi.fn()
- render()
- expect(screen.getByRole('button', { name: 'Click me' })).toBeInTheDocument()
- })
-
- it('should not render action button when onAction is not provided', () => {
- render()
- expect(screen.queryByText('Click me')).not.toBeInTheDocument()
- })
-
- it('should render divider when action is present', () => {
- const { container } = render(
- {}} />,
- )
- // Divider component renders a div with specific classes
- expect(container.querySelector('.bg-divider-regular')).toBeInTheDocument()
- })
- })
-
- describe('User Interactions', () => {
- it('should call onAction when action button is clicked', () => {
- const onAction = vi.fn()
- render()
-
- fireEvent.click(screen.getByRole('button', { name: 'Click me' }))
- expect(onAction).toHaveBeenCalledTimes(1)
- })
-
- it('should not call onAction when disabled', () => {
- const onAction = vi.fn()
- render(
- ,
- )
-
- fireEvent.click(screen.getByRole('button', { name: 'Click me' }))
- expect(onAction).not.toHaveBeenCalled()
- })
-
- it('should apply disabled styles when disabled prop is true', () => {
- render(
- {}} disabled />,
- )
-
- const actionButton = screen.getByRole('button', { name: 'Click me' })
- expect(actionButton).toHaveClass('cursor-not-allowed')
- expect(actionButton).toHaveClass('text-text-disabled')
- expect(actionButton).toBeDisabled()
- })
- })
-
- describe('Status Background Gradients', () => {
- it('should apply success gradient background', () => {
- const { container } = render()
- const gradientDiv = container.querySelector('.opacity-40')
- expect(gradientDiv?.className).toContain('rgba(23,178,106,0.25)')
- })
-
- it('should apply warning gradient background', () => {
- const { container } = render()
- const gradientDiv = container.querySelector('.opacity-40')
- expect(gradientDiv?.className).toContain('rgba(247,144,9,0.25)')
- })
-
- it('should apply error gradient background', () => {
- const { container } = render()
- const gradientDiv = container.querySelector('.opacity-40')
- expect(gradientDiv?.className).toContain('rgba(240,68,56,0.25)')
- })
-
- it('should apply info gradient background', () => {
- const { container } = render()
- const gradientDiv = container.querySelector('.opacity-40')
- expect(gradientDiv?.className).toContain('rgba(11,165,236,0.25)')
- })
- })
-
- describe('Edge Cases', () => {
- it('should handle empty description', () => {
- const { container } = render()
- expect(container.firstChild).toBeInTheDocument()
- })
-
- it('should handle long description text', () => {
- const longText = 'A'.repeat(500)
- render()
- expect(screen.getByText(longText)).toBeInTheDocument()
- })
-
- it('should handle undefined actionText when onAction is provided', () => {
- render( {}} />)
- // Should render without throwing
- expect(screen.getByText('Test')).toBeInTheDocument()
- })
- })
-})
diff --git a/web/app/components/datasets/common/economical-retrieval-method-config/__tests__/index.spec.tsx b/web/app/components/datasets/common/economical-retrieval-method-config/__tests__/index.spec.tsx
deleted file mode 100644
index 830b0054806..00000000000
--- a/web/app/components/datasets/common/economical-retrieval-method-config/__tests__/index.spec.tsx
+++ /dev/null
@@ -1,108 +0,0 @@
-import { fireEvent, render, screen } from '@testing-library/react'
-import { RETRIEVE_METHOD } from '@/types/app'
-import EconomicalRetrievalMethodConfig from '../index'
-
-vi.mock('../../../settings/option-card', () => ({
- default: ({
- children,
- title,
- description,
- disabled,
- id,
- }: {
- children?: React.ReactNode
- title?: string
- description?: React.ReactNode
- disabled?: boolean
- id?: string
- }) => (
-
-
{description}
- {children}
-
- ),
-}))
-
-vi.mock('../../retrieval-param-config', () => ({
- default: ({
- value,
- onChange,
- type,
- }: {
- value: Record
- onChange: (value: Record) => void
- type?: string
- }) => (
-
-
-
- ),
-}))
-
-vi.mock('@/app/components/base/icons/src/vender/knowledge', () => ({
- VectorSearch: () => ,
-}))
-
-describe('EconomicalRetrievalMethodConfig', () => {
- const mockOnChange = vi.fn()
- const defaultProps = {
- value: {
- search_method: RETRIEVE_METHOD.keywordSearch,
- reranking_enable: false,
- reranking_model: {
- reranking_provider_name: '',
- reranking_model_name: '',
- },
- top_k: 2,
- score_threshold_enabled: false,
- score_threshold: 0.5,
- },
- onChange: mockOnChange,
- }
-
- beforeEach(() => {
- vi.clearAllMocks()
- })
-
- it('should render correctly', () => {
- render()
-
- expect(screen.getByTestId('option-card')).toBeInTheDocument()
- expect(screen.getByTestId('retrieval-param-config')).toBeInTheDocument()
- // Check if title and description are rendered (mocked i18n returns key)
- expect(screen.getByText('dataset.retrieval.keyword_search.description')).toBeInTheDocument()
- })
-
- it('should pass correct props to OptionCard', () => {
- render()
-
- const card = screen.getByTestId('option-card')
- expect(card).toHaveAttribute('data-disabled', 'true')
- expect(card).toHaveAttribute('data-id', RETRIEVE_METHOD.keywordSearch)
- })
-
- it('should pass correct props to RetrievalParamConfig', () => {
- render()
-
- const config = screen.getByTestId('retrieval-param-config')
- expect(config).toHaveAttribute('data-type', RETRIEVE_METHOD.keywordSearch)
- })
-
- it('should handle onChange events', () => {
- render()
-
- fireEvent.click(screen.getByText('Change Value'))
-
- expect(mockOnChange).toHaveBeenCalledTimes(1)
- expect(mockOnChange).toHaveBeenCalledWith({
- ...defaultProps.value,
- newProp: 'changed',
- })
- })
-
- it('should default disabled prop to false', () => {
- render()
- const card = screen.getByTestId('option-card')
- expect(card).toHaveAttribute('data-disabled', 'false')
- })
-})
diff --git a/web/app/components/datasets/common/image-list/__tests__/index.spec.tsx b/web/app/components/datasets/common/image-list/__tests__/index.spec.tsx
index c9a670da54b..bd644904d5e 100644
--- a/web/app/components/datasets/common/image-list/__tests__/index.spec.tsx
+++ b/web/app/components/datasets/common/image-list/__tests__/index.spec.tsx
@@ -19,13 +19,14 @@ vi.mock('@/app/components/base/file-thumb', () => ({
// Capture the onClick for testing
capturedOnClick = onClick ?? null
return (
- onClick?.(file)}
>
{file.name}
-
+
)
},
}))
@@ -71,12 +72,6 @@ describe('ImageList', () => {
})
describe('Rendering', () => {
- it('should render without crashing', () => {
- const images = createMockImages(3)
- const { container } = render()
- expect(container.firstChild)!.toBeInTheDocument()
- })
-
it('should render all images when count is below limit', () => {
const images = createMockImages(5)
render()
@@ -95,12 +90,6 @@ describe('ImageList', () => {
})
describe('Props', () => {
- it('should apply custom className', () => {
- const images = createMockImages(3)
- const { container } = render()
- expect(container.firstChild)!.toHaveClass('custom-class')
- })
-
it('should use default limit of 9', () => {
const images = createMockImages(12)
render()
@@ -116,18 +105,6 @@ describe('ImageList', () => {
// Should show "+5" for remaining images
expect(screen.getByText(/\+5/))!.toBeInTheDocument()
})
-
- it('should handle size prop sm', () => {
- const images = createMockImages(2)
- const { container } = render()
- expect(container.firstChild)!.toBeInTheDocument()
- })
-
- it('should handle size prop md', () => {
- const images = createMockImages(2)
- const { container } = render()
- expect(container.firstChild)!.toBeInTheDocument()
- })
})
describe('User Interactions', () => {
@@ -238,11 +215,6 @@ describe('ImageList', () => {
})
describe('Edge Cases', () => {
- it('should handle empty images array', () => {
- const { container } = render()
- expect(container.firstChild)!.toBeInTheDocument()
- })
-
it('should not open preview when clicked image not found in list (index === -1)', () => {
const images = createMockImages(3)
const { rerender } = render()
@@ -318,12 +290,6 @@ describe('ImageList', () => {
expect(screen.queryByTestId('image-previewer')).not.toBeInTheDocument()
})
- it('should handle single image', () => {
- const images = createMockImages(1)
- const { container } = render()
- expect(container.firstChild)!.toBeInTheDocument()
- })
-
it('should not show More button when images count equals limit', () => {
const images = createMockImages(9)
render()
diff --git a/web/app/components/datasets/common/image-list/__tests__/more.spec.tsx b/web/app/components/datasets/common/image-list/__tests__/more.spec.tsx
deleted file mode 100644
index a192c7acc6d..00000000000
--- a/web/app/components/datasets/common/image-list/__tests__/more.spec.tsx
+++ /dev/null
@@ -1,144 +0,0 @@
-import { fireEvent, render, screen } from '@testing-library/react'
-import { describe, expect, it, vi } from 'vitest'
-import More from '../more'
-
-describe('More', () => {
- describe('Rendering', () => {
- it('should render without crashing', () => {
- render()
- expect(screen.getByText('+5')).toBeInTheDocument()
- })
-
- it('should display count with plus sign', () => {
- render()
- expect(screen.getByText('+10')).toBeInTheDocument()
- })
- })
-
- describe('Props', () => {
- it('should format count as-is when less than 1000', () => {
- render()
- expect(screen.getByText('+999')).toBeInTheDocument()
- })
-
- it('should format count with k suffix when 1000 or more', () => {
- render()
- expect(screen.getByText('+1.5k')).toBeInTheDocument()
- })
-
- it('should format count with M suffix when 1000000 or more', () => {
- render()
- expect(screen.getByText('+2.5M')).toBeInTheDocument()
- })
-
- it('should format 1000 as 1.0k', () => {
- render()
- expect(screen.getByText('+1.0k')).toBeInTheDocument()
- })
-
- it('should format 1000000 as 1.0M', () => {
- render()
- expect(screen.getByText('+1.0M')).toBeInTheDocument()
- })
- })
-
- describe('User Interactions', () => {
- it('should call onClick when clicked', () => {
- const onClick = vi.fn()
- render()
-
- fireEvent.click(screen.getByRole('button', { name: '+5' }))
- expect(onClick).toHaveBeenCalledTimes(1)
- })
-
- it('should not throw when clicked without onClick', () => {
- render()
-
- // Should not throw
- expect(() => {
- fireEvent.click(screen.getByRole('button', { name: '+5' }))
- }).not.toThrow()
- })
-
- it('should stop event propagation on click', () => {
- const parentClick = vi.fn()
- const childClick = vi.fn()
-
- render(
-
-
-
,
- )
-
- fireEvent.click(screen.getByRole('button', { name: '+5' }))
- expect(childClick).toHaveBeenCalled()
- expect(parentClick).not.toHaveBeenCalled()
- })
- })
-
- describe('Edge Cases', () => {
- it('should display +0 when count is 0', () => {
- render()
- expect(screen.getByText('+0')).toBeInTheDocument()
- })
-
- it('should handle count of 1', () => {
- render()
- expect(screen.getByText('+1')).toBeInTheDocument()
- })
-
- it('should handle boundary value 999', () => {
- render()
- expect(screen.getByText('+999')).toBeInTheDocument()
- })
-
- it('should handle boundary value 999999', () => {
- render()
- // 999999 / 1000 = 999.999 -> 1000.0k
- expect(screen.getByText('+1000.0k')).toBeInTheDocument()
- })
-
- it('should apply cursor-pointer class', () => {
- const { container } = render()
- expect(container.firstChild).toHaveClass('cursor-pointer')
- })
- })
-
- describe('formatNumber branches', () => {
- it('should return "0" when num equals 0', () => {
- // This covers line 11-12: if (num === 0) return '0'
- render()
- expect(screen.getByText('+0')).toBeInTheDocument()
- })
-
- it('should return num.toString() when num < 1000 and num > 0', () => {
- // This covers line 13-14: if (num < 1000) return num.toString()
- render()
- expect(screen.getByText('+500')).toBeInTheDocument()
- })
-
- it('should return k format when 1000 <= num < 1000000', () => {
- // This covers line 15-16
- const { rerender } = render()
- expect(screen.getByText('+5.0k')).toBeInTheDocument()
-
- rerender()
- expect(screen.getByText('+1000.0k')).toBeInTheDocument()
-
- rerender()
- expect(screen.getByText('+50.0k')).toBeInTheDocument()
- })
-
- it('should return M format when num >= 1000000', () => {
- // This covers line 17
- const { rerender } = render()
- expect(screen.getByText('+1.0M')).toBeInTheDocument()
-
- rerender()
- expect(screen.getByText('+5.0M')).toBeInTheDocument()
-
- rerender()
- expect(screen.getByText('+1000.0M')).toBeInTheDocument()
- })
- })
-})
diff --git a/web/app/components/datasets/common/image-previewer/__tests__/index.spec.tsx b/web/app/components/datasets/common/image-previewer/__tests__/index.spec.tsx
index cbc4249f144..f6f3041c01d 100644
--- a/web/app/components/datasets/common/image-previewer/__tests__/index.spec.tsx
+++ b/web/app/components/datasets/common/image-previewer/__tests__/index.spec.tsx
@@ -55,19 +55,6 @@ describe('ImagePreviewer', () => {
})
describe('Rendering', () => {
- it('should render without crashing', async () => {
- const onClose = vi.fn()
- const images = createMockImages()
-
- await act(async () => {
- render()
- })
-
- // Should render in portal
- // Should render in portal
- expect(document.body.querySelector('.image-previewer'))!.toBeInTheDocument()
- })
-
it('should render close button', async () => {
const onClose = vi.fn()
const images = createMockImages()
diff --git a/web/app/components/datasets/common/image-uploader/__tests__/constants.spec.ts b/web/app/components/datasets/common/image-uploader/__tests__/constants.spec.ts
deleted file mode 100644
index 925fa3af23a..00000000000
--- a/web/app/components/datasets/common/image-uploader/__tests__/constants.spec.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { describe, expect, it } from 'vitest'
-import {
- ACCEPT_TYPES,
- DEFAULT_IMAGE_FILE_BATCH_LIMIT,
- DEFAULT_IMAGE_FILE_SIZE_LIMIT,
- DEFAULT_SINGLE_CHUNK_ATTACHMENT_LIMIT,
-} from '../constants'
-
-describe('image-uploader constants', () => {
- // Verify accepted image types
- describe('ACCEPT_TYPES', () => {
- it('should include standard image formats', () => {
- expect(ACCEPT_TYPES).toContain('jpg')
- expect(ACCEPT_TYPES).toContain('jpeg')
- expect(ACCEPT_TYPES).toContain('png')
- expect(ACCEPT_TYPES).toContain('gif')
- })
-
- it('should have exactly 4 types', () => {
- expect(ACCEPT_TYPES).toHaveLength(4)
- })
- })
-
- // Verify numeric limits are positive
- describe('Limits', () => {
- it('should have a positive file size limit', () => {
- expect(DEFAULT_IMAGE_FILE_SIZE_LIMIT).toBeGreaterThan(0)
- expect(DEFAULT_IMAGE_FILE_SIZE_LIMIT).toBe(2)
- })
-
- it('should have a positive batch limit', () => {
- expect(DEFAULT_IMAGE_FILE_BATCH_LIMIT).toBeGreaterThan(0)
- expect(DEFAULT_IMAGE_FILE_BATCH_LIMIT).toBe(5)
- })
-
- it('should have a positive single chunk attachment limit', () => {
- expect(DEFAULT_SINGLE_CHUNK_ATTACHMENT_LIMIT).toBeGreaterThan(0)
- expect(DEFAULT_SINGLE_CHUNK_ATTACHMENT_LIMIT).toBe(10)
- })
- })
-})
diff --git a/web/app/components/datasets/common/image-uploader/hooks/__tests__/use-upload.spec.tsx b/web/app/components/datasets/common/image-uploader/hooks/__tests__/use-upload.spec.tsx
index 3669d428876..1c57e5f7a14 100644
--- a/web/app/components/datasets/common/image-uploader/hooks/__tests__/use-upload.spec.tsx
+++ b/web/app/components/datasets/common/image-uploader/hooks/__tests__/use-upload.spec.tsx
@@ -127,48 +127,6 @@ describe('useUpload hook', () => {
})
})
- describe('File Operations', () => {
- it('should expose selectHandle function', () => {
- const { result } = renderHook(() => useUpload(), {
- wrapper: createWrapper(),
- })
-
- expect(typeof result.current.selectHandle).toBe('function')
- })
-
- it('should expose fileChangeHandle function', () => {
- const { result } = renderHook(() => useUpload(), {
- wrapper: createWrapper(),
- })
-
- expect(typeof result.current.fileChangeHandle).toBe('function')
- })
-
- it('should expose handleRemoveFile function', () => {
- const { result } = renderHook(() => useUpload(), {
- wrapper: createWrapper(),
- })
-
- expect(typeof result.current.handleRemoveFile).toBe('function')
- })
-
- it('should expose handleReUploadFile function', () => {
- const { result } = renderHook(() => useUpload(), {
- wrapper: createWrapper(),
- })
-
- expect(typeof result.current.handleReUploadFile).toBe('function')
- })
-
- it('should expose handleLocalFileUpload function', () => {
- const { result } = renderHook(() => useUpload(), {
- wrapper: createWrapper(),
- })
-
- expect(typeof result.current.handleLocalFileUpload).toBe('function')
- })
- })
-
describe('File Validation', () => {
it('should show error toast for invalid file type', async () => {
const { result } = renderHook(() => useUpload(), {
@@ -270,25 +228,6 @@ describe('useUpload hook', () => {
expect(toast.error).not.toHaveBeenCalled()
})
- it('should handle null files', () => {
- const { result } = renderHook(() => useUpload(), {
- wrapper: createWrapper(),
- })
-
- const mockEvent = {
- target: {
- files: null,
- },
- } as unknown as React.ChangeEvent
-
- act(() => {
- result.current.fileChangeHandle(mockEvent)
- })
-
- // Should not throw
- expect(true).toBe(true)
- })
-
it('should respect batch limit from config', () => {
const { result } = renderHook(() => useUpload(), {
wrapper: createWrapper(),
diff --git a/web/app/components/datasets/common/image-uploader/image-uploader-in-chunk/__tests__/image-input.spec.tsx b/web/app/components/datasets/common/image-uploader/image-uploader-in-chunk/__tests__/image-input.spec.tsx
deleted file mode 100644
index 26e04ed2ba8..00000000000
--- a/web/app/components/datasets/common/image-uploader/image-uploader-in-chunk/__tests__/image-input.spec.tsx
+++ /dev/null
@@ -1,102 +0,0 @@
-import { fireEvent, render, screen } from '@testing-library/react'
-import { describe, expect, it, vi } from 'vitest'
-import { FileContextProvider } from '../../store'
-import ImageInput from '../image-input'
-
-vi.mock('@/service/use-common', () => ({
- useFileUploadConfig: vi.fn(() => ({
- data: {
- image_file_batch_limit: 10,
- single_chunk_attachment_limit: 20,
- attachment_image_file_size_limit: 15,
- },
- })),
-}))
-
-const renderWithProvider = (ui: React.ReactElement) => {
- return render({ui})
-}
-
-describe('ImageInput (image-uploader-in-chunk)', () => {
- describe('Rendering', () => {
- it('should render without crashing', () => {
- const { container } = renderWithProvider()
- expect(container.firstChild).toBeInTheDocument()
- })
-
- it('should render file input element', () => {
- renderWithProvider()
- const input = document.querySelector('input[type="file"]')
- expect(input).toBeInTheDocument()
- })
-
- it('should have hidden file input', () => {
- renderWithProvider()
- const input = document.querySelector('input[type="file"]')
- expect(input).toHaveClass('hidden')
- })
-
- it('should render upload icon', () => {
- const { container } = renderWithProvider()
- const icon = container.querySelector('svg')
- expect(icon).toBeInTheDocument()
- })
-
- it('should render browse text', () => {
- renderWithProvider()
- expect(screen.getByText(/browse/i)).toBeInTheDocument()
- })
- })
-
- describe('File Input Props', () => {
- it('should accept multiple files', () => {
- renderWithProvider()
- const input = document.querySelector('input[type="file"]')
- expect(input).toHaveAttribute('multiple')
- })
-
- it('should have accept attribute for images', () => {
- renderWithProvider()
- const input = document.querySelector('input[type="file"]')
- expect(input).toHaveAttribute('accept')
- })
- })
-
- describe('User Interactions', () => {
- it('should open file dialog when browse is clicked', () => {
- renderWithProvider()
-
- const browseText = screen.getByText(/browse/i)
- const input = document.querySelector('input[type="file"]') as HTMLInputElement
- const clickSpy = vi.spyOn(input, 'click')
-
- fireEvent.click(browseText)
-
- expect(clickSpy).toHaveBeenCalled()
- })
- })
-
- describe('Drag and Drop', () => {
- it('should have drop zone area', () => {
- const { container } = renderWithProvider()
- // The drop zone has dashed border styling
- expect(container.querySelector('.border-dashed')).toBeInTheDocument()
- })
-
- it('should apply accent styles when dragging', () => {
- // This would require simulating drag events
- // Just verify the base structure exists
- const { container } = renderWithProvider()
- expect(container.querySelector('.border-components-dropzone-border')).toBeInTheDocument()
- })
- })
-
- describe('Edge Cases', () => {
- it('should display file size limit from config', () => {
- renderWithProvider()
- // The tip text should contain the size limit (15 from mock)
- const tipText = document.querySelector('.system-xs-regular')
- expect(tipText).toBeInTheDocument()
- })
- })
-})
diff --git a/web/app/components/datasets/common/image-uploader/image-uploader-in-chunk/__tests__/image-item.spec.tsx b/web/app/components/datasets/common/image-uploader/image-uploader-in-chunk/__tests__/image-item.spec.tsx
index 5095ce4abd4..ae499195116 100644
--- a/web/app/components/datasets/common/image-uploader/image-uploader-in-chunk/__tests__/image-item.spec.tsx
+++ b/web/app/components/datasets/common/image-uploader/image-uploader-in-chunk/__tests__/image-item.spec.tsx
@@ -16,12 +16,6 @@ const createMockFile = (overrides: Partial = {}): FileEntity =>
describe('ImageItem (image-uploader-in-chunk)', () => {
describe('Rendering', () => {
- it('should render without crashing', () => {
- const file = createMockFile()
- const { container } = render()
- expect(container.firstChild).toBeInTheDocument()
- })
-
it('should render image preview', () => {
const file = createMockFile()
const { container } = render()
@@ -45,18 +39,6 @@ describe('ImageItem (image-uploader-in-chunk)', () => {
const deleteButton = container.querySelector('button')
expect(deleteButton).not.toBeInTheDocument()
})
-
- it('should use base64Url for image when available', () => {
- const file = createMockFile({ base64Url: 'data:image/png;base64,custom' })
- const { container } = render()
- expect(container.firstChild).toBeInTheDocument()
- })
-
- it('should fallback to sourceUrl when base64Url is not available', () => {
- const file = createMockFile({ base64Url: undefined })
- const { container } = render()
- expect(container.firstChild).toBeInTheDocument()
- })
})
describe('Progress States', () => {
diff --git a/web/app/components/datasets/common/image-uploader/image-uploader-in-chunk/__tests__/index.spec.tsx b/web/app/components/datasets/common/image-uploader/image-uploader-in-chunk/__tests__/index.spec.tsx
index c650503ccff..515c586cdd5 100644
--- a/web/app/components/datasets/common/image-uploader/image-uploader-in-chunk/__tests__/index.spec.tsx
+++ b/web/app/components/datasets/common/image-uploader/image-uploader-in-chunk/__tests__/index.spec.tsx
@@ -25,12 +25,6 @@ vi.mock('@/app/components/datasets/common/image-previewer', () => ({
describe('ImageUploaderInChunk', () => {
describe('Rendering', () => {
- it('should render without crashing', () => {
- const onChange = vi.fn()
- const { container } = render()
- expect(container.firstChild).toBeInTheDocument()
- })
-
it('should render ImageInput when not disabled', () => {
const onChange = vi.fn()
render()
@@ -47,14 +41,6 @@ describe('ImageUploaderInChunk', () => {
})
describe('Props', () => {
- it('should apply custom className', () => {
- const onChange = vi.fn()
- const { container } = render(
- ,
- )
- expect(container.firstChild).toHaveClass('custom-class')
- })
-
it('should render files when value is provided', () => {
const onChange = vi.fn()
const files: FileEntity[] = [
@@ -141,20 +127,4 @@ describe('ImageUploaderInChunk', () => {
}
})
})
-
- describe('Edge Cases', () => {
- it('should handle empty files array', () => {
- const onChange = vi.fn()
- const { container } = render()
- expect(container.firstChild).toBeInTheDocument()
- })
-
- it('should handle undefined value', () => {
- const onChange = vi.fn()
- const { container } = render(
- ,
- )
- expect(container.firstChild).toBeInTheDocument()
- })
- })
})
diff --git a/web/app/components/datasets/common/image-uploader/image-uploader-in-retrieval-testing/__tests__/image-input.spec.tsx b/web/app/components/datasets/common/image-uploader/image-uploader-in-retrieval-testing/__tests__/image-input.spec.tsx
deleted file mode 100644
index a8ab5482f47..00000000000
--- a/web/app/components/datasets/common/image-uploader/image-uploader-in-retrieval-testing/__tests__/image-input.spec.tsx
+++ /dev/null
@@ -1,119 +0,0 @@
-import type { FileEntity } from '../../types'
-import { fireEvent, render } from '@testing-library/react'
-import { describe, expect, it, vi } from 'vitest'
-import { FileContextProvider } from '../../store'
-import ImageInput from '../image-input'
-
-vi.mock('@/service/use-common', () => ({
- useFileUploadConfig: vi.fn(() => ({
- data: {
- image_file_batch_limit: 10,
- single_chunk_attachment_limit: 20,
- attachment_image_file_size_limit: 15,
- },
- })),
-}))
-
-const renderWithProvider = (ui: React.ReactElement, initialFiles: FileEntity[] = []) => {
- return render({ui})
-}
-
-describe('ImageInput (image-uploader-in-retrieval-testing)', () => {
- describe('Rendering', () => {
- it('should render without crashing', () => {
- const { container } = renderWithProvider()
- expect(container.firstChild).toBeInTheDocument()
- })
-
- it('should render file input element', () => {
- renderWithProvider()
- const input = document.querySelector('input[type="file"]')
- expect(input).toBeInTheDocument()
- })
-
- it('should have hidden file input', () => {
- renderWithProvider()
- const input = document.querySelector('input[type="file"]')
- expect(input).toHaveClass('hidden')
- })
-
- it('should render add image icon', () => {
- const { container } = renderWithProvider()
- const icon = container.querySelector('svg')
- expect(icon).toBeInTheDocument()
- })
-
- it('should show tip text when no files are uploaded', () => {
- renderWithProvider()
- // Tip text should be visible
- expect(document.querySelector('.system-sm-regular')).toBeInTheDocument()
- })
-
- it('should hide tip text when files exist', () => {
- const files: FileEntity[] = [
- {
- id: 'file1',
- name: 'test.png',
- extension: 'png',
- mimeType: 'image/png',
- size: 1024,
- progress: 100,
- uploadedId: 'uploaded-1',
- },
- ]
- renderWithProvider(, files)
- // Tip text should not be visible
- expect(document.querySelector('.text-text-quaternary')).not.toBeInTheDocument()
- })
- })
-
- describe('File Input Props', () => {
- it('should accept multiple files', () => {
- renderWithProvider()
- const input = document.querySelector('input[type="file"]')
- expect(input).toHaveAttribute('multiple')
- })
-
- it('should have accept attribute', () => {
- renderWithProvider()
- const input = document.querySelector('input[type="file"]')
- expect(input).toHaveAttribute('accept')
- })
- })
-
- describe('User Interactions', () => {
- it('should open file dialog when icon is clicked', () => {
- renderWithProvider()
-
- const clickableArea = document.querySelector('.cursor-pointer')
- const input = document.querySelector('input[type="file"]') as HTMLInputElement
- const clickSpy = vi.spyOn(input, 'click')
-
- if (clickableArea) fireEvent.click(clickableArea)
-
- expect(clickSpy).toHaveBeenCalled()
- })
- })
-
- describe('Tooltip', () => {
- it('should have tooltip component', () => {
- const { container } = renderWithProvider()
- // Tooltip wrapper should exist
- expect(container.firstChild).toBeInTheDocument()
- })
-
- it('should disable tooltip when no files exist', () => {
- // When files.length === 0, tooltip should be disabled
- renderWithProvider()
- // Component renders with tip text visible instead of tooltip
- expect(document.querySelector('.system-sm-regular')).toBeInTheDocument()
- })
- })
-
- describe('Edge Cases', () => {
- it('should render icon container with correct styling', () => {
- const { container } = renderWithProvider()
- expect(container.querySelector('.border-dashed')).toBeInTheDocument()
- })
- })
-})
diff --git a/web/app/components/datasets/common/image-uploader/image-uploader-in-retrieval-testing/__tests__/image-item.spec.tsx b/web/app/components/datasets/common/image-uploader/image-uploader-in-retrieval-testing/__tests__/image-item.spec.tsx
index 7a40afb2669..db2a89ef308 100644
--- a/web/app/components/datasets/common/image-uploader/image-uploader-in-retrieval-testing/__tests__/image-item.spec.tsx
+++ b/web/app/components/datasets/common/image-uploader/image-uploader-in-retrieval-testing/__tests__/image-item.spec.tsx
@@ -16,12 +16,6 @@ const createMockFile = (overrides: Partial = {}): FileEntity =>
describe('ImageItem (image-uploader-in-retrieval-testing)', () => {
describe('Rendering', () => {
- it('should render without crashing', () => {
- const file = createMockFile()
- const { container } = render()
- expect(container.firstChild).toBeInTheDocument()
- })
-
it('should render with size-20 class', () => {
const file = createMockFile()
const { container } = render()
@@ -129,17 +123,5 @@ describe('ImageItem (image-uploader-in-retrieval-testing)', () => {
if (imageContainer) fireEvent.click(imageContainer)
}).not.toThrow()
})
-
- it('should use base64Url when available', () => {
- const file = createMockFile({ base64Url: 'data:custom' })
- const { container } = render()
- expect(container.firstChild).toBeInTheDocument()
- })
-
- it('should fallback to sourceUrl', () => {
- const file = createMockFile({ base64Url: undefined })
- const { container } = render()
- expect(container.firstChild).toBeInTheDocument()
- })
})
})
diff --git a/web/app/components/datasets/common/image-uploader/image-uploader-in-retrieval-testing/__tests__/index.spec.tsx b/web/app/components/datasets/common/image-uploader/image-uploader-in-retrieval-testing/__tests__/index.spec.tsx
index daffb04c421..3d48681aa55 100644
--- a/web/app/components/datasets/common/image-uploader/image-uploader-in-retrieval-testing/__tests__/index.spec.tsx
+++ b/web/app/components/datasets/common/image-uploader/image-uploader-in-retrieval-testing/__tests__/index.spec.tsx
@@ -31,13 +31,6 @@ describe('ImageUploaderInRetrievalTesting', () => {
}
describe('Rendering', () => {
- it('should render without crashing', () => {
- const { container } = render(
- ,
- )
- expect(container.firstChild).toBeInTheDocument()
- })
-
it('should render textArea prop', () => {
render()
expect(screen.getByTestId('text-area')).toBeInTheDocument()
@@ -66,29 +59,6 @@ describe('ImageUploaderInRetrievalTesting', () => {
})
describe('Props', () => {
- it('should apply custom className', () => {
- const { container } = render(
- ,
- )
- expect(container.firstChild).toHaveClass('custom-class')
- })
-
- it('should apply actionAreaClassName', () => {
- const { container } = render(
- ,
- )
- // The action area should have the custom class
- expect(container.querySelector('.action-area-class')).toBeInTheDocument()
- })
-
it('should render file list when files are provided', () => {
const files: FileEntity[] = [
{
@@ -212,13 +182,6 @@ describe('ImageUploaderInRetrievalTesting', () => {
})
describe('Edge Cases', () => {
- it('should handle undefined value', () => {
- const { container } = render(
- ,
- )
- expect(container.firstChild).toBeInTheDocument()
- })
-
it('should handle multiple files', () => {
const files: FileEntity[] = Array.from({ length: 5 }, (_, i) => ({
id: `file${i}`,
diff --git a/web/app/components/datasets/common/retrieval-method-config/__tests__/index.spec.tsx b/web/app/components/datasets/common/retrieval-method-config/__tests__/index.spec.tsx
index 487cb1a9c04..973bde347bd 100644
--- a/web/app/components/datasets/common/retrieval-method-config/__tests__/index.spec.tsx
+++ b/web/app/components/datasets/common/retrieval-method-config/__tests__/index.spec.tsx
@@ -101,12 +101,6 @@ describe('RetrievalMethodConfig', () => {
// Tests for basic rendering
describe('Rendering', () => {
- it('should render without crashing', () => {
- renderComponent()
-
- expect(screen.getByText('dataset.retrieval.semantic_search.title')).toBeInTheDocument()
- })
-
it('should render all three retrieval methods when all are supported', () => {
renderComponent()
@@ -624,13 +618,6 @@ describe('RetrievalMethodConfig', () => {
// Tests for component memoization
describe('Component Memoization', () => {
- it('should be memoized with React.memo', () => {
- // Verify the component is wrapped with React.memo by checking its displayName or type
- expect(RetrievalMethodConfig).toBeDefined()
- // React.memo components have a $$typeof property
- expect((RetrievalMethodConfig as unknown as { $$typeof: symbol }).$$typeof).toBeDefined()
- })
-
it('should not re-render when props are the same', () => {
const onChange = vi.fn()
const value = createMockRetrievalConfig()
@@ -660,7 +647,6 @@ describe('RetrievalMethodConfig', () => {
onChange,
})
- // Should not crash
expect(screen.getByText('dataset.retrieval.semantic_search.title')).toBeInTheDocument()
})
@@ -826,14 +812,6 @@ describe('RetrievalMethodConfig', () => {
// Tests for all prop variations
describe('Prop Variations', () => {
- it('should render with minimum required props', () => {
- const { container } = render(
- ,
- )
-
- expect(container.firstChild).toBeInTheDocument()
- })
-
it('should render with all props set', () => {
renderComponent({
disabled: true,
diff --git a/web/app/components/datasets/common/retrieval-method-info/__tests__/index.spec.tsx b/web/app/components/datasets/common/retrieval-method-info/__tests__/index.spec.tsx
deleted file mode 100644
index 967339e8e28..00000000000
--- a/web/app/components/datasets/common/retrieval-method-info/__tests__/index.spec.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import { RETRIEVE_METHOD } from '@/types/app'
-import { retrievalIcon } from '../../../create/icons'
-import { getIcon } from '../index'
-
-// Mock icons
-vi.mock('../../../create/icons', () => ({
- retrievalIcon: {
- vector: 'vector-icon.png',
- fullText: 'fulltext-icon.png',
- hybrid: 'hybrid-icon.png',
- },
-}))
-
-describe('RetrievalMethodInfo', () => {
- beforeEach(() => {
- vi.clearAllMocks()
- })
-
- describe('getIcon utility', () => {
- it('should return correct icon for each type', () => {
- expect(getIcon(RETRIEVE_METHOD.semantic)).toBe(retrievalIcon.vector)
- expect(getIcon(RETRIEVE_METHOD.fullText)).toBe(retrievalIcon.fullText)
- expect(getIcon(RETRIEVE_METHOD.hybrid)).toBe(retrievalIcon.hybrid)
- expect(getIcon(RETRIEVE_METHOD.invertedIndex)).toBe(retrievalIcon.vector)
- expect(getIcon(RETRIEVE_METHOD.keywordSearch)).toBe(retrievalIcon.vector)
- })
-
- it('should return default vector icon for unknown type', () => {
- // Test fallback branch when type is not in the mapping
- const unknownType = 'unknown_method' as RETRIEVE_METHOD
- expect(getIcon(unknownType)).toBe(retrievalIcon.vector)
- })
- })
-})
diff --git a/web/app/components/datasets/create-from-pipeline/__tests__/footer.spec.tsx b/web/app/components/datasets/create-from-pipeline/__tests__/footer.spec.tsx
index cfb9002d273..8ced87ecedf 100644
--- a/web/app/components/datasets/create-from-pipeline/__tests__/footer.spec.tsx
+++ b/web/app/components/datasets/create-from-pipeline/__tests__/footer.spec.tsx
@@ -64,11 +64,6 @@ describe('Footer', () => {
})
describe('Rendering', () => {
- it('should render without crashing', () => {
- render()
- expect(screen.getByText(/importDSL/i)).toBeInTheDocument()
- })
-
it('should render import button with icon', () => {
const { container } = render()
const button = screen.getByRole('button')
@@ -125,12 +120,6 @@ describe('Footer', () => {
})
describe('Layout', () => {
- it('should have proper container classes', () => {
- const { container } = render()
- const footerDiv = container.firstChild as HTMLElement
- expect(footerDiv).toHaveClass('absolute', 'bottom-0', 'left-0', 'right-0', 'z-10')
- })
-
it('should have backdrop blur-sm effect', () => {
const { container } = render()
const footerDiv = container.firstChild as HTMLElement
@@ -138,14 +127,6 @@ describe('Footer', () => {
})
})
- describe('Memoization', () => {
- it('should be memoized with React.memo', () => {
- const { rerender } = render()
- rerender()
- expect(screen.getByText(/importDSL/i)).toBeInTheDocument()
- })
- })
-
// URL Parameter Tests (Branch Coverage)
describe('URL Parameter Handling', () => {
it('should set activeTab to FROM_URL when dslUrl is present', () => {
diff --git a/web/app/components/datasets/create-from-pipeline/__tests__/header.spec.tsx b/web/app/components/datasets/create-from-pipeline/__tests__/header.spec.tsx
index 54dbfb06d3b..2c7ccdb8c56 100644
--- a/web/app/components/datasets/create-from-pipeline/__tests__/header.spec.tsx
+++ b/web/app/components/datasets/create-from-pipeline/__tests__/header.spec.tsx
@@ -1,60 +1,28 @@
-import { render, screen } from '@testing-library/react'
-import { describe, expect, it } from 'vitest'
+import { render } from '@testing-library/react'
import Header from '../header'
-// Header Component Tests
+vi.mock('@/next/link', () => ({
+ default: ({
+ children,
+ href,
+ replace,
+ className,
+ }: {
+ children: React.ReactNode
+ href: string
+ replace?: boolean
+ className?: string
+ }) => (
+
+ {children}
+
+ ),
+}))
describe('Header', () => {
- describe('Rendering', () => {
- it('should render without crashing', () => {
- render()
- expect(screen.getByText(/backToKnowledge/i)).toBeInTheDocument()
- })
+ it('links back to the dataset list', () => {
+ const { container } = render()
- it('should render back button with link to datasets', () => {
- render()
- const link = screen.getByRole('link')
- expect(link).toHaveAttribute('href', '/datasets')
- })
-
- it('should render arrow icon in button', () => {
- const { container } = render()
- const icon = container.querySelector('svg')
- expect(icon).toBeInTheDocument()
- })
-
- it('should render button with correct styling', () => {
- render()
- const button = screen.getByRole('button')
- expect(button).toHaveClass('rounded-full')
- })
-
- it('should have replace attribute on link', () => {
- const { container } = render()
- const link = container.querySelector('a[href="/datasets"]')
- expect(link).toBeInTheDocument()
- })
- })
-
- describe('Layout', () => {
- it('should have proper container classes', () => {
- const { container } = render()
- const headerDiv = container.firstChild as HTMLElement
- expect(headerDiv).toHaveClass('relative', 'flex', 'px-16', 'pb-2', 'pt-5')
- })
-
- it('should position link absolutely at bottom left', () => {
- const { container } = render()
- const link = container.querySelector('a')
- expect(link).toHaveClass('absolute', 'bottom-0', 'left-5')
- })
- })
-
- describe('Memoization', () => {
- it('should be memoized with React.memo', () => {
- const { rerender } = render()
- rerender()
- expect(screen.getByText(/backToKnowledge/i)).toBeInTheDocument()
- })
+ expect(container.querySelector('a')).toHaveAttribute('href', '/datasets')
})
})
diff --git a/web/app/components/datasets/create-from-pipeline/__tests__/index.spec.tsx b/web/app/components/datasets/create-from-pipeline/__tests__/index.spec.tsx
deleted file mode 100644
index 28918949f97..00000000000
--- a/web/app/components/datasets/create-from-pipeline/__tests__/index.spec.tsx
+++ /dev/null
@@ -1,102 +0,0 @@
-import { render, screen } from '@testing-library/react'
-import { describe, expect, it, vi } from 'vitest'
-import CreateFromPipeline from '../index'
-
-// Mock child components to isolate testing
-vi.mock('../header', () => ({
- default: () => Header
,
-}))
-
-vi.mock('../list', () => ({
- default: () => List
,
-}))
-
-vi.mock('../footer', () => ({
- default: () => Footer
,
-}))
-
-vi.mock('../../../base/effect', () => ({
- default: ({ className }: { className?: string }) => (
-
- Effect
-
- ),
-}))
-
-// CreateFromPipeline Component Tests
-
-describe('CreateFromPipeline', () => {
- describe('Rendering', () => {
- it('should render without crashing', () => {
- render()
- expect(screen.getByTestId('mock-header')).toBeInTheDocument()
- })
-
- it('should render Header component', () => {
- render()
- expect(screen.getByTestId('mock-header')).toBeInTheDocument()
- })
-
- it('should render List component', () => {
- render()
- expect(screen.getByTestId('mock-list')).toBeInTheDocument()
- })
-
- it('should render Footer component', () => {
- render()
- expect(screen.getByTestId('mock-footer')).toBeInTheDocument()
- })
-
- it('should render Effect component', () => {
- render()
- expect(screen.getByTestId('mock-effect')).toBeInTheDocument()
- })
- })
-
- describe('Layout', () => {
- it('should have proper container classes', () => {
- const { container } = render()
- const mainDiv = container.firstChild as HTMLElement
- expect(mainDiv).toHaveClass(
- 'relative',
- 'flex',
- 'flex-col',
- 'overflow-hidden',
- 'rounded-t-2xl',
- )
- })
-
- it('should have correct height calculation', () => {
- const { container } = render()
- const mainDiv = container.firstChild as HTMLElement
- expect(mainDiv).toHaveClass('h-[calc(100vh-56px)]')
- })
-
- it('should have border and background styling', () => {
- const { container } = render()
- const mainDiv = container.firstChild as HTMLElement
- expect(mainDiv).toHaveClass(
- 'border-t',
- 'border-effects-highlight',
- 'bg-background-default-subtle',
- )
- })
-
- it('should position Effect component correctly', () => {
- render()
- const effect = screen.getByTestId('mock-effect')
- expect(effect).toHaveClass('left-8', 'top-[-34px]', 'opacity-20')
- })
- })
-
- // Component Order Tests
- describe('Component Order', () => {
- it('should render components in correct order', () => {
- const { container } = render()
- const children = Array.from(container.firstChild?.childNodes || [])
-
- // Effect, Header, List, Footer
- expect(children.length).toBe(4)
- })
- })
-})
diff --git a/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/__tests__/dsl-confirm-modal.spec.tsx b/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/__tests__/dsl-confirm-modal.spec.tsx
deleted file mode 100644
index 8c9980ddcba..00000000000
--- a/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/__tests__/dsl-confirm-modal.spec.tsx
+++ /dev/null
@@ -1,138 +0,0 @@
-import { fireEvent, render, screen } from '@testing-library/react'
-import { beforeEach, describe, expect, it, vi } from 'vitest'
-import DSLConfirmModal from '../dsl-confirm-modal'
-
-// DSLConfirmModal Component Tests
-
-describe('DSLConfirmModal', () => {
- const defaultProps = {
- onCancel: vi.fn(),
- onConfirm: vi.fn(),
- }
-
- beforeEach(() => {
- vi.clearAllMocks()
- })
-
- describe('Rendering', () => {
- it('should render without crashing', () => {
- render()
- expect(screen.getByText(/appCreateDSLErrorTitle/i)).toBeInTheDocument()
- })
-
- it('should render title', () => {
- render()
- expect(screen.getByText(/appCreateDSLErrorTitle/i)).toBeInTheDocument()
- })
-
- it('should render error message parts', () => {
- render()
- expect(screen.getByText(/appCreateDSLErrorPart1/i)).toBeInTheDocument()
- expect(screen.getByText(/appCreateDSLErrorPart2/i)).toBeInTheDocument()
- expect(screen.getByText(/appCreateDSLErrorPart3/i)).toBeInTheDocument()
- expect(screen.getByText(/appCreateDSLErrorPart4/i)).toBeInTheDocument()
- })
-
- it('should render cancel button', () => {
- render()
- expect(screen.getByText(/Cancel/i)).toBeInTheDocument()
- })
-
- it('should render confirm button', () => {
- render()
- expect(screen.getByText(/Confirm/i)).toBeInTheDocument()
- })
- })
-
- // Versions Display Tests
- describe('Versions Display', () => {
- it('should display imported version when provided', () => {
- render(
- ,
- )
- expect(screen.getByText('1.0.0')).toBeInTheDocument()
- })
-
- it('should display system version when provided', () => {
- render(
- ,
- )
- expect(screen.getByText('2.0.0')).toBeInTheDocument()
- })
-
- it('should use default empty versions when not provided', () => {
- render()
- // Should render without errors
- expect(screen.getByText(/appCreateDSLErrorTitle/i)).toBeInTheDocument()
- })
- })
-
- describe('User Interactions', () => {
- it('should call onCancel when cancel button is clicked', () => {
- render()
- const cancelButton = screen.getByText(/Cancel/i)
-
- fireEvent.click(cancelButton)
-
- expect(defaultProps.onCancel).toHaveBeenCalledTimes(1)
- })
-
- it('should call onConfirm when confirm button is clicked', () => {
- render()
- const confirmButton = screen.getByText(/Confirm/i)
-
- fireEvent.click(confirmButton)
-
- expect(defaultProps.onConfirm).toHaveBeenCalledTimes(1)
- })
-
- it('should call onCancel when modal is closed', () => {
- render()
- // Modal close is triggered by clicking backdrop or close button
- // The onClose prop is mapped to onCancel
- const cancelButton = screen.getByText(/Cancel/i)
- fireEvent.click(cancelButton)
-
- expect(defaultProps.onCancel).toHaveBeenCalled()
- })
- })
-
- // Button State Tests
- describe('Button State', () => {
- it('should enable confirm button by default', () => {
- render()
- const confirmButton = screen.getByText(/Confirm/i)
-
- expect(confirmButton).not.toBeDisabled()
- })
-
- it('should disable confirm button when confirmDisabled is true', () => {
- render()
- const confirmButton = screen.getByText(/Confirm/i)
-
- expect(confirmButton).toBeDisabled()
- })
-
- it('should enable confirm button when confirmDisabled is false', () => {
- render()
- const confirmButton = screen.getByText(/Confirm/i)
-
- expect(confirmButton).not.toBeDisabled()
- })
- })
-
- describe('Layout', () => {
- it('should have button container with proper styling', () => {
- render()
- const cancelButton = screen.getByText(/Cancel/i)
- const buttonContainer = cancelButton.parentElement
- expect(buttonContainer).toHaveClass('flex', 'items-start', 'justify-end', 'gap-2')
- })
- })
-})
diff --git a/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/__tests__/header.spec.tsx b/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/__tests__/header.spec.tsx
deleted file mode 100644
index f32532358c9..00000000000
--- a/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/__tests__/header.spec.tsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import { fireEvent, render, screen } from '@testing-library/react'
-import { beforeEach, describe, expect, it, vi } from 'vitest'
-import Header from '../header'
-
-// Header Component Tests
-
-describe('Header', () => {
- const defaultProps = {
- onClose: vi.fn(),
- }
-
- beforeEach(() => {
- vi.clearAllMocks()
- })
-
- describe('Rendering', () => {
- it('should render without crashing', () => {
- render()
- expect(screen.getByText(/importFromDSL/i)).toBeInTheDocument()
- })
-
- it('should render title', () => {
- render()
- expect(screen.getByText(/importFromDSL/i)).toBeInTheDocument()
- })
-
- it('should render close button', () => {
- const { container } = render()
- const closeButton = container.querySelector('[class*="cursor-pointer"]')
- expect(closeButton).toBeInTheDocument()
- })
-
- it('should render close icon', () => {
- const { container } = render()
- const icon = container.querySelector('svg')
- expect(icon).toBeInTheDocument()
- })
- })
-
- describe('User Interactions', () => {
- it('should call onClose when close button is clicked', () => {
- const { container } = render()
- const closeButton = container.querySelector('[class*="cursor-pointer"]')
-
- fireEvent.click(closeButton!)
-
- expect(defaultProps.onClose).toHaveBeenCalledTimes(1)
- })
- })
-
- describe('Layout', () => {
- it('should have proper container styling', () => {
- const { container } = render()
- const wrapper = container.firstChild as HTMLElement
- expect(wrapper).toHaveClass('title-2xl-semi-bold', 'relative', 'flex', 'items-center')
- })
-
- it('should have close button positioned absolutely', () => {
- const { container } = render()
- const closeButton = container.querySelector('[class*="absolute"]')
- expect(closeButton).toHaveClass('right-5', 'top-5')
- })
-
- it('should have padding classes', () => {
- const { container } = render()
- const wrapper = container.firstChild as HTMLElement
- expect(wrapper).toHaveClass('pb-3', 'pl-6', 'pr-14', 'pt-6')
- })
- })
-
- describe('Memoization', () => {
- it('should be memoized with React.memo', () => {
- const { rerender } = render()
- rerender()
- expect(screen.getByText(/importFromDSL/i)).toBeInTheDocument()
- })
- })
-})
diff --git a/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/__tests__/index.spec.tsx b/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/__tests__/index.spec.tsx
index a8ad1acf783..3982556c4de 100644
--- a/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/__tests__/index.spec.tsx
+++ b/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/__tests__/index.spec.tsx
@@ -113,12 +113,6 @@ describe('CreateFromDSLModal', () => {
})
describe('Rendering', () => {
- it('should render without crashing when show is true', () => {
- render(, { wrapper: createWrapper() })
-
- expect(screen.getByText('app.importFromDSL'))!.toBeInTheDocument()
- })
-
it('should not render modal content when show is false', () => {
render(, { wrapper: createWrapper() })
@@ -1294,14 +1288,6 @@ describe('Uploader', () => {
expect(screen.getByText('test.pipeline'))!.toBeInTheDocument()
expect(screen.getByText('PIPELINE'))!.toBeInTheDocument()
})
-
- it('should apply custom className', () => {
- const { container } = render(
- ,
- )
-
- expect(container.firstChild)!.toHaveClass('custom-class')
- })
})
describe('Event Handlers', () => {
@@ -1626,8 +1612,6 @@ describe('DSLConfirmModal', () => {
it('should render with default empty versions', () => {
render()
- // Should not crash with default empty strings
- // Should not crash with default empty strings
expect(screen.getByText('app.newApp.appCreateDSLErrorTitle'))!.toBeInTheDocument()
})
@@ -1687,14 +1671,6 @@ describe('DSLConfirmModal', () => {
})
describe('Props', () => {
- it('should use default versions when not provided', () => {
- render()
-
- // Component should render without crashing
- // Component should render without crashing
- expect(screen.getByText('app.newApp.appCreateDSLErrorTitle'))!.toBeInTheDocument()
- })
-
it('should use default confirmDisabled when not provided', () => {
render()
diff --git a/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/__tests__/uploader.spec.tsx b/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/__tests__/uploader.spec.tsx
index 09214a53b33..b11cf80669f 100644
--- a/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/__tests__/uploader.spec.tsx
+++ b/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/__tests__/uploader.spec.tsx
@@ -37,11 +37,6 @@ describe('Uploader', () => {
// Rendering Tests - No File
describe('Rendering - No File', () => {
- it('should render without crashing', () => {
- render()
- expect(screen.getByText(/dslUploader\.button/i)).toBeInTheDocument()
- })
-
it('should render upload prompt when no file', () => {
render()
expect(screen.getByText(/dslUploader\.button/i)).toBeInTheDocument()
@@ -140,27 +135,8 @@ describe('Uploader', () => {
})
// Custom className Tests
- describe('Custom className', () => {
- it('should apply custom className', () => {
- const { container } = render()
- const wrapper = container.firstChild as HTMLElement
- expect(wrapper).toHaveClass('custom-class')
- })
-
- it('should merge custom className with default', () => {
- const { container } = render()
- const wrapper = container.firstChild as HTMLElement
- expect(wrapper).toHaveClass('mt-6', 'custom-class')
- })
- })
describe('Layout', () => {
- it('should have proper container styling', () => {
- const { container } = render()
- const wrapper = container.firstChild as HTMLElement
- expect(wrapper).toHaveClass('mt-6')
- })
-
it('should have dropzone styling when no file', () => {
const { container } = render()
const dropzone = container.querySelector('[class*="border-dashed"]')
@@ -174,12 +150,4 @@ describe('Uploader', () => {
expect(fileCard).toBeInTheDocument()
})
})
-
- describe('Memoization', () => {
- it('should be memoized with React.memo', () => {
- const { rerender } = render()
- rerender()
- expect(screen.getByText(/dslUploader\.button/i)).toBeInTheDocument()
- })
- })
})
diff --git a/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/tab/__tests__/index.spec.tsx b/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/tab/__tests__/index.spec.tsx
deleted file mode 100644
index b4853c5ccbb..00000000000
--- a/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/tab/__tests__/index.spec.tsx
+++ /dev/null
@@ -1,107 +0,0 @@
-import { fireEvent, render, screen } from '@testing-library/react'
-import { beforeEach, describe, expect, it, vi } from 'vitest'
-import { CreateFromDSLModalTab } from '../../hooks/use-dsl-import'
-import Tab from '../index'
-
-// Tab Component Tests
-
-describe('Tab', () => {
- const defaultProps = {
- currentTab: CreateFromDSLModalTab.FROM_FILE,
- setCurrentTab: vi.fn(),
- }
-
- beforeEach(() => {
- vi.clearAllMocks()
- })
-
- describe('Rendering', () => {
- it('should render without crashing', () => {
- render()
- expect(screen.getByText(/importFromDSLFile/i)).toBeInTheDocument()
- })
-
- it('should render file tab', () => {
- render()
- expect(screen.getByText(/importFromDSLFile/i)).toBeInTheDocument()
- })
-
- it('should render URL tab', () => {
- render()
- expect(screen.getByText(/importFromDSLUrl/i)).toBeInTheDocument()
- })
-
- it('should render both tabs', () => {
- render()
- const tabs = screen.getAllByText(/importFromDSL/i)
- expect(tabs.length).toBe(2)
- })
- })
-
- // Active State Tests
- describe('Active State', () => {
- it('should mark file tab as active when currentTab is FROM_FILE', () => {
- const { container } = render(
- ,
- )
- const activeIndicators = container.querySelectorAll('[class*="bg-util-colors-blue-brand"]')
- expect(activeIndicators.length).toBe(1)
- })
-
- it('should mark URL tab as active when currentTab is FROM_URL', () => {
- const { container } = render(
- ,
- )
- const activeIndicators = container.querySelectorAll('[class*="bg-util-colors-blue-brand"]')
- expect(activeIndicators.length).toBe(1)
- })
- })
-
- describe('User Interactions', () => {
- it('should call setCurrentTab with FROM_FILE when file tab is clicked', () => {
- render()
- const fileTab = screen.getByText(/importFromDSLFile/i)
-
- fireEvent.click(fileTab)
-
- // bind() prepends the bound argument, so setCurrentTab is called with (FROM_FILE, event)
- expect(defaultProps.setCurrentTab).toHaveBeenCalledWith(
- CreateFromDSLModalTab.FROM_FILE,
- expect.anything(),
- )
- })
-
- it('should call setCurrentTab with FROM_URL when URL tab is clicked', () => {
- render()
- const urlTab = screen.getByText(/importFromDSLUrl/i)
-
- fireEvent.click(urlTab)
-
- // bind() prepends the bound argument, so setCurrentTab is called with (FROM_URL, event)
- expect(defaultProps.setCurrentTab).toHaveBeenCalledWith(
- CreateFromDSLModalTab.FROM_URL,
- expect.anything(),
- )
- })
- })
-
- describe('Layout', () => {
- it('should have proper container styling', () => {
- const { container } = render()
- const wrapper = container.firstChild as HTMLElement
- expect(wrapper).toHaveClass('system-md-semibold', 'flex', 'h-9', 'items-center', 'gap-x-6')
- })
-
- it('should have border bottom', () => {
- const { container } = render()
- const wrapper = container.firstChild as HTMLElement
- expect(wrapper).toHaveClass('border-b', 'border-divider-subtle')
- })
-
- it('should have padding', () => {
- const { container } = render()
- const wrapper = container.firstChild as HTMLElement
- expect(wrapper).toHaveClass('px-6')
- })
- })
-})
diff --git a/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/tab/__tests__/item.spec.tsx b/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/tab/__tests__/item.spec.tsx
deleted file mode 100644
index 10848061106..00000000000
--- a/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/tab/__tests__/item.spec.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-import { fireEvent, render, screen } from '@testing-library/react'
-import { beforeEach, describe, expect, it, vi } from 'vitest'
-import Item from '../item'
-
-// Item Component Tests
-
-describe('Item', () => {
- const defaultProps = {
- isActive: false,
- label: 'Tab Label',
- onClick: vi.fn(),
- }
-
- beforeEach(() => {
- vi.clearAllMocks()
- })
-
- describe('Rendering', () => {
- it('should render without crashing', () => {
- render( )
- expect(screen.getByText('Tab Label')).toBeInTheDocument()
- })
-
- it('should render label', () => {
- render( )
- expect(screen.getByText('Custom Label')).toBeInTheDocument()
- })
-
- it('should not render indicator when inactive', () => {
- const { container } = render( )
- const indicator = container.querySelector('[class*="bg-util-colors-blue-brand"]')
- expect(indicator).not.toBeInTheDocument()
- })
-
- it('should render indicator when active', () => {
- const { container } = render( )
- const indicator = container.querySelector('[class*="bg-util-colors-blue-brand"]')
- expect(indicator).toBeInTheDocument()
- })
- })
-
- // Active State Tests
- describe('Active State', () => {
- it('should have tertiary text color when inactive', () => {
- const { container } = render( )
- const item = container.firstChild as HTMLElement
- expect(item).toHaveClass('text-text-tertiary')
- })
-
- it('should have primary text color when active', () => {
- const { container } = render( )
- const item = container.firstChild as HTMLElement
- expect(item).toHaveClass('text-text-primary')
- })
-
- it('should show active indicator bar when active', () => {
- const { container } = render( )
- const indicator = container.querySelector('[class*="absolute"]')
- expect(indicator).toHaveClass('bottom-0', 'h-0.5', 'w-full')
- })
- })
-
- describe('User Interactions', () => {
- it('should call onClick when clicked', () => {
- render( )
- const item = screen.getByText('Tab Label')
-
- fireEvent.click(item)
-
- expect(defaultProps.onClick).toHaveBeenCalledTimes(1)
- })
-
- it('should have cursor pointer', () => {
- const { container } = render( )
- const item = container.firstChild as HTMLElement
- expect(item).toHaveClass('cursor-pointer')
- })
- })
-
- describe('Layout', () => {
- it('should have proper container styling', () => {
- const { container } = render( )
- const item = container.firstChild as HTMLElement
- expect(item).toHaveClass('system-md-semibold', 'relative', 'flex', 'h-full', 'items-center')
- })
- })
-
- describe('Memoization', () => {
- it('should be memoized with React.memo', () => {
- const { rerender } = render( )
- rerender( )
- expect(screen.getByText('Tab Label')).toBeInTheDocument()
- })
- })
-})
diff --git a/web/app/components/datasets/create-from-pipeline/list/__tests__/built-in-pipeline-list.spec.tsx b/web/app/components/datasets/create-from-pipeline/list/__tests__/built-in-pipeline-list.spec.tsx
index ce5ff2d2457..29498ead7fd 100644
--- a/web/app/components/datasets/create-from-pipeline/list/__tests__/built-in-pipeline-list.spec.tsx
+++ b/web/app/components/datasets/create-from-pipeline/list/__tests__/built-in-pipeline-list.spec.tsx
@@ -50,16 +50,6 @@ describe('BuiltInPipelineList', () => {
})
describe('Rendering', () => {
- it('should render without crashing', () => {
- mockUsePipelineTemplateList.mockReturnValue({
- data: { pipeline_templates: [] },
- isLoading: false,
- })
-
- render()
- expect(screen.getByTestId('create-card')).toBeInTheDocument()
- })
-
it('should always render CreateCard', () => {
mockUsePipelineTemplateList.mockReturnValue({
data: null,
diff --git a/web/app/components/datasets/create-from-pipeline/list/__tests__/create-card.spec.tsx b/web/app/components/datasets/create-from-pipeline/list/__tests__/create-card.spec.tsx
index 0bf08ecee2b..6309795bb61 100644
--- a/web/app/components/datasets/create-from-pipeline/list/__tests__/create-card.spec.tsx
+++ b/web/app/components/datasets/create-from-pipeline/list/__tests__/create-card.spec.tsx
@@ -52,11 +52,6 @@ describe('CreateCard', () => {
})
describe('Rendering', () => {
- it('should render without crashing', () => {
- render()
- expect(screen.getByText(/createFromScratch\.title/i)).toBeInTheDocument()
- })
-
it('should render title and description', () => {
render()
expect(screen.getByText(/createFromScratch\.title/i)).toBeInTheDocument()
@@ -166,12 +161,6 @@ describe('CreateCard', () => {
})
describe('Layout', () => {
- it('should have proper card styling', () => {
- const { container } = render()
- const card = container.firstChild as HTMLElement
- expect(card).toHaveClass('relative', 'flex', 'cursor-pointer', 'flex-col', 'rounded-xl')
- })
-
it('should have fixed height', () => {
const { container } = render()
const card = container.firstChild as HTMLElement
@@ -184,12 +173,4 @@ describe('CreateCard', () => {
expect(card).toHaveClass('border-[0.5px]', 'shadow-xs')
})
})
-
- describe('Memoization', () => {
- it('should be memoized with React.memo', () => {
- const { rerender } = render()
- rerender()
- expect(screen.getByText(/createFromScratch\.title/i)).toBeInTheDocument()
- })
- })
})
diff --git a/web/app/components/datasets/create-from-pipeline/list/__tests__/index.spec.tsx b/web/app/components/datasets/create-from-pipeline/list/__tests__/index.spec.tsx
deleted file mode 100644
index b7d50461643..00000000000
--- a/web/app/components/datasets/create-from-pipeline/list/__tests__/index.spec.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import { render, screen } from '@testing-library/react'
-import { describe, expect, it, vi } from 'vitest'
-import List from '../index'
-
-vi.mock('../built-in-pipeline-list', () => ({
- default: () => BuiltInPipelineList
,
-}))
-
-vi.mock('../customized-list', () => ({
- default: () => CustomizedList
,
-}))
-
-// List Component Tests
-
-describe('List', () => {
- describe('Rendering', () => {
- it('should render without crashing', () => {
- render(
)
- expect(screen.getByTestId('built-in-list')).toBeInTheDocument()
- })
-
- it('should render BuiltInPipelineList component', () => {
- render(
)
- expect(screen.getByTestId('built-in-list')).toBeInTheDocument()
- })
-
- it('should render CustomizedList component', () => {
- render(
)
- expect(screen.getByTestId('customized-list')).toBeInTheDocument()
- })
- })
-
- describe('Layout', () => {
- it('should have proper container classes', () => {
- const { container } = render(
)
- const listDiv = container.firstChild as HTMLElement
- expect(listDiv).toHaveClass('grow', 'overflow-y-auto', 'px-16', 'pb-[60px]', 'pt-1')
- })
-
- it('should have gap between items', () => {
- const { container } = render(
)
- const listDiv = container.firstChild as HTMLElement
- expect(listDiv).toHaveClass('gap-y-1')
- })
- })
-
- // Component Order Tests
- describe('Component Order', () => {
- it('should render BuiltInPipelineList before CustomizedList', () => {
- const { container } = render(
)
- const children = Array.from(container.firstChild?.childNodes || [])
-
- expect(children.length).toBe(2)
- expect((children[0] as HTMLElement).getAttribute('data-testid')).toBe('built-in-list')
- expect((children[1] as HTMLElement).getAttribute('data-testid')).toBe('customized-list')
- })
- })
-})
diff --git a/web/app/components/datasets/create-from-pipeline/list/template-card/__tests__/actions.spec.tsx b/web/app/components/datasets/create-from-pipeline/list/template-card/__tests__/actions.spec.tsx
deleted file mode 100644
index 41ae70a943f..00000000000
--- a/web/app/components/datasets/create-from-pipeline/list/template-card/__tests__/actions.spec.tsx
+++ /dev/null
@@ -1,128 +0,0 @@
-import { fireEvent, render, screen } from '@testing-library/react'
-import { beforeEach, describe, expect, it, vi } from 'vitest'
-import Actions from '../actions'
-
-// Actions Component Tests
-
-describe('Actions', () => {
- const defaultProps = {
- onApplyTemplate: vi.fn(),
- handleShowTemplateDetails: vi.fn(),
- showMoreOperations: true,
- openEditModal: vi.fn(),
- handleExportDSL: vi.fn(),
- handleDelete: vi.fn(),
- }
-
- beforeEach(() => {
- vi.clearAllMocks()
- })
-
- describe('Rendering', () => {
- it('should render without crashing', () => {
- render()
- expect(screen.getByText(/operations\.choose/i)).toBeInTheDocument()
- })
-
- it('should render choose button', () => {
- render()
- expect(screen.getByText(/operations\.choose/i)).toBeInTheDocument()
- })
-
- it('should render details button', () => {
- render()
- expect(screen.getByText(/operations\.details/i)).toBeInTheDocument()
- })
-
- it('should render add icon', () => {
- const { container } = render()
- expect(container.querySelector('.i-ri-add-line')).toBeInTheDocument()
- })
-
- it('should render arrow icon for details', () => {
- const { container } = render()
- expect(container.querySelector('.i-ri-arrow-right-up-line')).toBeInTheDocument()
- })
- })
-
- // More Operations Tests
- describe('More Operations', () => {
- it('should render more operations button when showMoreOperations is true', () => {
- const { container } = render()
- // CustomPopover should be rendered with more button
- const moreButton = container.querySelector('[class*="rounded-lg"]')
- expect(moreButton).toBeInTheDocument()
- })
-
- it('should not render more operations button when showMoreOperations is false', () => {
- render()
- // Should only have choose and details buttons
- const buttons = screen.getAllByRole('button')
- expect(buttons).toHaveLength(2)
- })
- })
-
- describe('User Interactions', () => {
- it('should call onApplyTemplate when choose button is clicked', () => {
- render()
-
- const chooseButton = screen.getByText(/operations\.choose/i).closest('button')
- fireEvent.click(chooseButton!)
-
- expect(defaultProps.onApplyTemplate).toHaveBeenCalledTimes(1)
- })
-
- it('should call handleShowTemplateDetails when details button is clicked', () => {
- render()
-
- const detailsButton = screen.getByText(/operations\.details/i).closest('button')
- fireEvent.click(detailsButton!)
-
- expect(defaultProps.handleShowTemplateDetails).toHaveBeenCalledTimes(1)
- })
-
- it('should open more operations menu and close it after selecting edit', async () => {
- render()
-
- fireEvent.click(screen.getByRole('button', { name: 'common.operation.more' }))
- const editButton = await screen.findByText(/operations\.editInfo/i)
- fireEvent.click(editButton)
-
- expect(defaultProps.openEditModal).toHaveBeenCalledTimes(1)
- })
- })
-
- describe('Layout', () => {
- it('should have absolute positioning', () => {
- const { container } = render()
- const wrapper = container.firstChild as HTMLElement
- expect(wrapper).toHaveClass('absolute', 'bottom-0', 'left-0')
- })
-
- it('should be hidden by default', () => {
- const { container } = render()
- const wrapper = container.firstChild as HTMLElement
- expect(wrapper).toHaveClass('hidden')
- })
-
- it('should show on group hover', () => {
- const { container } = render()
- const wrapper = container.firstChild as HTMLElement
- expect(wrapper).toHaveClass('group-hover:flex')
- })
-
- it('should have proper z-index', () => {
- const { container } = render()
- const wrapper = container.firstChild as HTMLElement
- expect(wrapper).toHaveClass('z-10')
- })
- })
-
- describe('Memoization', () => {
- it('should be memoized with React.memo', () => {
- const { rerender } = render()
- rerender()
- expect(screen.getByText(/operations\.choose/i)).toBeInTheDocument()
- })
- })
-})
diff --git a/web/app/components/datasets/create-from-pipeline/list/template-card/__tests__/content.spec.tsx b/web/app/components/datasets/create-from-pipeline/list/template-card/__tests__/content.spec.tsx
deleted file mode 100644
index 8e36cd62821..00000000000
--- a/web/app/components/datasets/create-from-pipeline/list/template-card/__tests__/content.spec.tsx
+++ /dev/null
@@ -1,176 +0,0 @@
-import type { IconInfo } from '@/models/datasets'
-import { render, screen } from '@testing-library/react'
-import { describe, expect, it } from 'vitest'
-import { ChunkingMode } from '@/models/datasets'
-import Content from '../content'
-
-const createIconInfo = (overrides: Partial = {}): IconInfo => ({
- icon_type: 'emoji',
- icon: '๐',
- icon_background: '#FFF4ED',
- icon_url: '',
- ...overrides,
-})
-
-const createImageIconInfo = (overrides: Partial = {}): IconInfo => ({
- icon_type: 'image',
- icon: 'file-id-123',
- icon_background: '',
- icon_url: 'https://example.com/icon.png',
- ...overrides,
-})
-
-// Content Component Tests
-
-describe('Content', () => {
- const defaultProps = {
- name: 'Test Pipeline',
- description: 'This is a test pipeline description',
- iconInfo: createIconInfo(),
- chunkStructure: 'text' as ChunkingMode,
- }
-
- describe('Rendering', () => {
- it('should render without crashing', () => {
- render()
- expect(screen.getByText('Test Pipeline')).toBeInTheDocument()
- })
-
- it('should render name', () => {
- render()
- expect(screen.getByText('Test Pipeline')).toBeInTheDocument()
- })
-
- it('should render description', () => {
- render()
- expect(screen.getByText('This is a test pipeline description')).toBeInTheDocument()
- })
-
- it('should render chunking mode text', () => {
- render()
- // The translation key should be rendered
- expect(screen.getByText(/chunkingMode/i)).toBeInTheDocument()
- })
-
- it('should have title attribute for truncation', () => {
- render()
- const nameElement = screen.getByText('Test Pipeline')
- expect(nameElement).toHaveAttribute('title', 'Test Pipeline')
- })
-
- it('should have title attribute on description', () => {
- render()
- const descElement = screen.getByText('This is a test pipeline description')
- expect(descElement).toHaveAttribute('title', 'This is a test pipeline description')
- })
- })
-
- // Icon Rendering Tests
- describe('Icon Rendering', () => {
- it('should render emoji icon correctly', () => {
- const { container } = render()
- // AppIcon component should be rendered
- const iconContainer = container.querySelector('[class*="shrink-0"]')
- expect(iconContainer).toBeInTheDocument()
- })
-
- it('should render image icon correctly', () => {
- const props = {
- ...defaultProps,
- iconInfo: createImageIconInfo(),
- }
- const { container } = render()
- const iconContainer = container.querySelector('[class*="shrink-0"]')
- expect(iconContainer).toBeInTheDocument()
- })
-
- it('should render chunk structure icon', () => {
- const { container } = render()
- // Icon should be rendered in the corner
- const icons = container.querySelectorAll('svg')
- expect(icons.length).toBeGreaterThan(0)
- })
- })
-
- // Chunk Structure Tests
- describe('Chunk Structure', () => {
- it('should handle text chunk structure', () => {
- render()
- expect(screen.getByText(/chunkingMode/i)).toBeInTheDocument()
- })
-
- it('should handle parent-child chunk structure', () => {
- render()
- expect(screen.getByText(/chunkingMode/i)).toBeInTheDocument()
- })
-
- it('should handle qa chunk structure', () => {
- render()
- expect(screen.getByText(/chunkingMode/i)).toBeInTheDocument()
- })
-
- it('should fallback to General icon for unknown chunk structure', () => {
- const { container } = render(
- ,
- )
- const icons = container.querySelectorAll('svg')
- expect(icons.length).toBeGreaterThan(0)
- })
- })
-
- describe('Layout', () => {
- it('should have proper header layout', () => {
- const { container } = render()
- const header = container.querySelector('[class*="gap-x-3"]')
- expect(header).toBeInTheDocument()
- })
-
- it('should have truncate class on name', () => {
- render()
- const nameElement = screen.getByText('Test Pipeline')
- expect(nameElement).toHaveClass('truncate')
- })
-
- it('should have line-clamp on description', () => {
- render()
- const descElement = screen.getByText('This is a test pipeline description')
- expect(descElement).toHaveClass('line-clamp-3')
- })
- })
-
- describe('Edge Cases', () => {
- it('should handle empty name', () => {
- render()
- const { container } = render()
- expect(container).toBeInTheDocument()
- })
-
- it('should handle empty description', () => {
- render()
- const { container } = render()
- expect(container).toBeInTheDocument()
- })
-
- it('should handle long name', () => {
- const longName = 'A'.repeat(100)
- render()
- const nameElement = screen.getByText(longName)
- expect(nameElement).toHaveClass('truncate')
- })
-
- it('should handle long description', () => {
- const longDesc = 'A'.repeat(500)
- render()
- const descElement = screen.getByText(longDesc)
- expect(descElement).toHaveClass('line-clamp-3')
- })
- })
-
- describe('Memoization', () => {
- it('should be memoized with React.memo', () => {
- const { rerender } = render()
- rerender()
- expect(screen.getByText('Test Pipeline')).toBeInTheDocument()
- })
- })
-})
diff --git a/web/app/components/datasets/create-from-pipeline/list/template-card/__tests__/edit-pipeline-info.spec.tsx b/web/app/components/datasets/create-from-pipeline/list/template-card/__tests__/edit-pipeline-info.spec.tsx
index 68d6f1e72ff..a2136144acb 100644
--- a/web/app/components/datasets/create-from-pipeline/list/template-card/__tests__/edit-pipeline-info.spec.tsx
+++ b/web/app/components/datasets/create-from-pipeline/list/template-card/__tests__/edit-pipeline-info.spec.tsx
@@ -72,11 +72,6 @@ describe('EditPipelineInfo', () => {
})
describe('Rendering', () => {
- it('should render without crashing', () => {
- render()
- expect(screen.getByText(/editPipelineInfo/i)).toBeInTheDocument()
- })
-
it('should render title', () => {
render()
expect(screen.getByText(/editPipelineInfo/i)).toBeInTheDocument()
@@ -573,24 +568,10 @@ describe('EditPipelineInfo', () => {
})
describe('Layout', () => {
- it('should have proper container styling', () => {
- const { container } = render()
- const wrapper = container.firstChild as HTMLElement
- expect(wrapper).toHaveClass('relative', 'flex', 'flex-col')
- })
-
it('should have close button in header', () => {
const { container } = render()
const closeButton = container.querySelector('button.absolute')
expect(closeButton).toHaveClass('right-5', 'top-5')
})
})
-
- describe('Memoization', () => {
- it('should be memoized with React.memo', () => {
- const { rerender } = render()
- rerender()
- expect(screen.getByText(/editPipelineInfo/i)).toBeInTheDocument()
- })
- })
})
diff --git a/web/app/components/datasets/create-from-pipeline/list/template-card/__tests__/index.spec.tsx b/web/app/components/datasets/create-from-pipeline/list/template-card/__tests__/index.spec.tsx
index e5627cd384c..fc4919f8ea9 100644
--- a/web/app/components/datasets/create-from-pipeline/list/template-card/__tests__/index.spec.tsx
+++ b/web/app/components/datasets/create-from-pipeline/list/template-card/__tests__/index.spec.tsx
@@ -202,11 +202,6 @@ describe('TemplateCard', () => {
})
describe('Rendering', () => {
- it('should render without crashing', () => {
- render()
- expect(screen.getByText('Test Pipeline')).toBeInTheDocument()
- })
-
it('should render pipeline name', () => {
render()
expect(screen.getByText('Test Pipeline')).toBeInTheDocument()
@@ -688,19 +683,6 @@ describe('TemplateCard', () => {
})
describe('Layout', () => {
- it('should have proper card styling', () => {
- const { container } = render()
- const card = container.firstChild as HTMLElement
- expect(card).toHaveClass(
- 'group',
- 'relative',
- 'flex',
- 'cursor-pointer',
- 'flex-col',
- 'rounded-xl',
- )
- })
-
it('should have fixed height', () => {
const { container } = render()
const card = container.firstChild as HTMLElement
@@ -713,12 +695,4 @@ describe('TemplateCard', () => {
expect(card).toHaveClass('border-[0.5px]', 'shadow-xs')
})
})
-
- describe('Memoization', () => {
- it('should be memoized with React.memo', () => {
- const { rerender } = render()
- rerender()
- expect(screen.getByText('Test Pipeline')).toBeInTheDocument()
- })
- })
})
diff --git a/web/app/components/datasets/create-from-pipeline/list/template-card/__tests__/operations.spec.tsx b/web/app/components/datasets/create-from-pipeline/list/template-card/__tests__/operations.spec.tsx
deleted file mode 100644
index c235567f15e..00000000000
--- a/web/app/components/datasets/create-from-pipeline/list/template-card/__tests__/operations.spec.tsx
+++ /dev/null
@@ -1,133 +0,0 @@
-import { fireEvent, render, screen } from '@testing-library/react'
-import { beforeEach, describe, expect, it, vi } from 'vitest'
-import Operations from '../operations'
-
-// Operations Component Tests
-
-describe('Operations', () => {
- const defaultProps = {
- openEditModal: vi.fn(),
- onDelete: vi.fn(),
- onExport: vi.fn(),
- }
-
- beforeEach(() => {
- vi.clearAllMocks()
- })
-
- describe('Rendering', () => {
- it('should render without crashing', () => {
- render()
- expect(screen.getByText(/editInfo/i)).toBeInTheDocument()
- })
-
- it('should render all operation buttons', () => {
- render()
- expect(screen.getByText(/editInfo/i)).toBeInTheDocument()
- expect(screen.getByText(/exportPipeline/i)).toBeInTheDocument()
- expect(screen.getByText(/delete/i)).toBeInTheDocument()
- })
-
- it('should have proper container styling', () => {
- const { container } = render()
- const wrapper = container.firstChild as HTMLElement
- expect(wrapper).toHaveClass('relative', 'flex', 'flex-col', 'rounded-xl')
- })
- })
-
- describe('User Interactions', () => {
- it('should call openEditModal when edit is clicked', () => {
- render()
-
- const editButton = screen.getByText(/editInfo/i).closest('div[class*="cursor-pointer"]')
- fireEvent.click(editButton!)
-
- expect(defaultProps.openEditModal).toHaveBeenCalledTimes(1)
- })
-
- it('should call onExport when export is clicked', () => {
- render()
-
- const exportButton = screen
- .getByText(/exportPipeline/i)
- .closest('div[class*="cursor-pointer"]')
- fireEvent.click(exportButton!)
-
- expect(defaultProps.onExport).toHaveBeenCalledTimes(1)
- })
-
- it('should call onDelete when delete is clicked', () => {
- render()
-
- const deleteButton = screen.getByText(/delete/i).closest('div[class*="cursor-pointer"]')
- fireEvent.click(deleteButton!)
-
- expect(defaultProps.onDelete).toHaveBeenCalledTimes(1)
- })
-
- it('should stop propagation on edit click', () => {
- const stopPropagation = vi.fn()
- const preventDefault = vi.fn()
-
- render()
-
- const editButton = screen.getByText(/editInfo/i).closest('div[class*="cursor-pointer"]')
- fireEvent.click(editButton!, {
- stopPropagation,
- preventDefault,
- })
-
- expect(defaultProps.openEditModal).toHaveBeenCalled()
- })
-
- it('should stop propagation on export click', () => {
- render()
-
- const exportButton = screen
- .getByText(/exportPipeline/i)
- .closest('div[class*="cursor-pointer"]')
- fireEvent.click(exportButton!)
-
- expect(defaultProps.onExport).toHaveBeenCalled()
- })
-
- it('should stop propagation on delete click', () => {
- render()
-
- const deleteButton = screen.getByText(/delete/i).closest('div[class*="cursor-pointer"]')
- fireEvent.click(deleteButton!)
-
- expect(defaultProps.onDelete).toHaveBeenCalled()
- })
- })
-
- describe('Layout', () => {
- it('should have divider between sections', () => {
- const { container } = render()
- const divider = container.querySelector('[class*="bg-divider"]')
- expect(divider).toBeInTheDocument()
- })
-
- it('should have hover states on buttons', () => {
- render()
-
- const editButton = screen.getByText(/editInfo/i).closest('div[class*="cursor-pointer"]')
- expect(editButton).toHaveClass('hover:bg-state-base-hover')
- })
-
- it('should have destructive hover state on delete button', () => {
- render()
-
- const deleteButton = screen.getByText(/delete/i).closest('div[class*="cursor-pointer"]')
- expect(deleteButton).toHaveClass('hover:bg-state-destructive-hover')
- })
- })
-
- describe('Memoization', () => {
- it('should be memoized with React.memo', () => {
- const { rerender } = render()
- rerender()
- expect(screen.getByText(/editInfo/i)).toBeInTheDocument()
- })
- })
-})
diff --git a/web/app/components/datasets/create-from-pipeline/list/template-card/details/__tests__/chunk-structure-card.spec.tsx b/web/app/components/datasets/create-from-pipeline/list/template-card/details/__tests__/chunk-structure-card.spec.tsx
deleted file mode 100644
index 2dd5acd2851..00000000000
--- a/web/app/components/datasets/create-from-pipeline/list/template-card/details/__tests__/chunk-structure-card.spec.tsx
+++ /dev/null
@@ -1,164 +0,0 @@
-import { render, screen } from '@testing-library/react'
-import { describe, expect, it } from 'vitest'
-import ChunkStructureCard from '../chunk-structure-card'
-import { EffectColor } from '../types'
-
-// ChunkStructureCard Component Tests
-
-describe('ChunkStructureCard', () => {
- const defaultProps = {
- icon: Icon,
- title: 'General',
- description: 'General chunk structure description',
- effectColor: EffectColor.indigo,
- }
-
- describe('Rendering', () => {
- it('should render without crashing', () => {
- render()
- expect(screen.getByText('General')).toBeInTheDocument()
- })
-
- it('should render title', () => {
- render()
- expect(screen.getByText('General')).toBeInTheDocument()
- })
-
- it('should render description', () => {
- render()
- expect(screen.getByText('General chunk structure description')).toBeInTheDocument()
- })
-
- it('should render icon', () => {
- render()
- expect(screen.getByTestId('test-icon')).toBeInTheDocument()
- })
-
- it('should not render description when empty', () => {
- render()
- expect(screen.getByText('General')).toBeInTheDocument()
- expect(screen.queryByText('General chunk structure description')).not.toBeInTheDocument()
- })
-
- it('should not render description when undefined', () => {
- const { description: _, ...propsWithoutDesc } = defaultProps
- render()
- expect(screen.getByText('General')).toBeInTheDocument()
- })
- })
-
- // Effect Colors Tests
- describe('Effect Colors', () => {
- it('should apply indigo effect color', () => {
- const { container } = render(
- ,
- )
- const effectElement = container.querySelector('[class*="blur-"]')
- expect(effectElement).toHaveClass('bg-util-colors-indigo-indigo-600')
- })
-
- it('should apply blueLight effect color', () => {
- const { container } = render(
- ,
- )
- const effectElement = container.querySelector('[class*="blur-"]')
- expect(effectElement).toHaveClass('bg-util-colors-blue-light-blue-light-500')
- })
-
- it('should apply green effect color', () => {
- const { container } = render(
- ,
- )
- const effectElement = container.querySelector('[class*="blur-"]')
- expect(effectElement).toHaveClass('bg-util-colors-teal-teal-600')
- })
-
- it('should handle none effect color', () => {
- const { container } = render(
- ,
- )
- const effectElement = container.querySelector('[class*="blur-"]')
- expect(effectElement).toBeInTheDocument()
- })
- })
-
- // Icon Background Tests
- describe('Icon Background', () => {
- it('should apply indigo icon background', () => {
- const { container } = render(
- ,
- )
- const iconBg = container.querySelector('[class*="bg-components-icon-bg"]')
- expect(iconBg).toHaveClass('bg-components-icon-bg-indigo-solid')
- })
-
- it('should apply blue light icon background', () => {
- const { container } = render(
- ,
- )
- const iconBg = container.querySelector('[class*="bg-components-icon-bg"]')
- expect(iconBg).toHaveClass('bg-components-icon-bg-blue-light-solid')
- })
-
- it('should apply green icon background', () => {
- const { container } = render(
- ,
- )
- const iconBg = container.querySelector('[class*="bg-components-icon-bg"]')
- expect(iconBg).toHaveClass('bg-components-icon-bg-teal-solid')
- })
- })
-
- // Custom className Tests
- describe('Custom className', () => {
- it('should apply custom className', () => {
- const { container } = render(
- ,
- )
- const wrapper = container.firstChild as HTMLElement
- expect(wrapper).toHaveClass('custom-class')
- })
-
- it('should merge custom className with default classes', () => {
- const { container } = render(
- ,
- )
- const wrapper = container.firstChild as HTMLElement
- expect(wrapper).toHaveClass('relative', 'flex', 'custom-class')
- })
- })
-
- describe('Layout', () => {
- it('should have proper card styling', () => {
- const { container } = render()
- const card = container.firstChild as HTMLElement
- expect(card).toHaveClass('relative', 'flex', 'overflow-hidden', 'rounded-xl')
- })
-
- it('should have border styling', () => {
- const { container } = render()
- const card = container.firstChild as HTMLElement
- expect(card).toHaveClass('border-[0.5px]', 'border-components-panel-border-subtle')
- })
-
- it('should have shadow styling', () => {
- const { container } = render()
- const card = container.firstChild as HTMLElement
- expect(card).toHaveClass('shadow-xs')
- })
-
- it('should have blur-sm effect element', () => {
- const { container } = render()
- const blurElement = container.querySelector('[class*="blur-"]')
- expect(blurElement).toHaveClass('absolute', '-left-1', '-top-1', 'size-14', 'rounded-full')
- })
- })
-
- describe('Memoization', () => {
- it('should be memoized with React.memo', () => {
- const { rerender } = render()
- rerender()
- expect(screen.getByText('General')).toBeInTheDocument()
- })
- })
-})
diff --git a/web/app/components/datasets/create-from-pipeline/list/template-card/details/__tests__/index.spec.tsx b/web/app/components/datasets/create-from-pipeline/list/template-card/details/__tests__/index.spec.tsx
index 9c547d850ad..b4bdb282809 100644
--- a/web/app/components/datasets/create-from-pipeline/list/template-card/details/__tests__/index.spec.tsx
+++ b/web/app/components/datasets/create-from-pipeline/list/template-card/details/__tests__/index.spec.tsx
@@ -74,15 +74,6 @@ describe('Details', () => {
})
describe('Rendering', () => {
- it('should render without crashing when data is available', () => {
- mockUsePipelineTemplateById.mockReturnValue({
- data: createPipelineTemplateInfo(),
- })
-
- render( )
- expect(screen.getByText('Test Pipeline')).toBeInTheDocument()
- })
-
it('should render pipeline name', () => {
mockUsePipelineTemplateById.mockReturnValue({
data: createPipelineTemplateInfo(),
@@ -213,16 +204,13 @@ describe('Details', () => {
expect(screen.getByText('Test Pipeline')).toBeInTheDocument()
})
- it('should have default icon when data is null', () => {
+ it('shows the loading state when template data is absent', () => {
mockUsePipelineTemplateById.mockReturnValue({
data: null,
})
- // When data is null, component shows loading state
- // The default icon is only used in useMemo when pipelineTemplateInfo is null
render( )
- // Should not crash and should render (loading state)
expect(screen.queryByText('Test Pipeline')).not.toBeInTheDocument()
})
})
@@ -287,16 +275,6 @@ describe('Details', () => {
})
describe('Layout', () => {
- it('should have proper container styling', () => {
- mockUsePipelineTemplateById.mockReturnValue({
- data: createPipelineTemplateInfo(),
- })
-
- const { container } = render( )
- const wrapper = container.firstChild as HTMLElement
- expect(wrapper).toHaveClass('flex', 'h-full')
- })
-
it('should have fixed width sidebar', () => {
mockUsePipelineTemplateById.mockReturnValue({
data: createPipelineTemplateInfo(),
@@ -317,16 +295,4 @@ describe('Details', () => {
expect(previewContainer).toBeInTheDocument()
})
})
-
- describe('Memoization', () => {
- it('should be memoized with React.memo', () => {
- mockUsePipelineTemplateById.mockReturnValue({
- data: createPipelineTemplateInfo(),
- })
-
- const { rerender } = render( )
- rerender( )
- expect(screen.getByText('Test Pipeline')).toBeInTheDocument()
- })
- })
})
diff --git a/web/app/components/datasets/create/__tests__/icons.spec.ts b/web/app/components/datasets/create/__tests__/icons.spec.ts
deleted file mode 100644
index 780c0bf4c09..00000000000
--- a/web/app/components/datasets/create/__tests__/icons.spec.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { describe, expect, it } from 'vitest'
-import { indexMethodIcon, retrievalIcon } from '../icons'
-
-describe('create/icons', () => {
- // Verify icon map exports have expected keys
- describe('indexMethodIcon', () => {
- it('should have high_quality and economical keys', () => {
- expect(indexMethodIcon).toHaveProperty('high_quality')
- expect(indexMethodIcon).toHaveProperty('economical')
- })
-
- it('should have truthy values for each key', () => {
- expect(indexMethodIcon.high_quality).toBeTruthy()
- expect(indexMethodIcon.economical).toBeTruthy()
- })
- })
-
- describe('retrievalIcon', () => {
- it('should have vector, fullText, and hybrid keys', () => {
- expect(retrievalIcon).toHaveProperty('vector')
- expect(retrievalIcon).toHaveProperty('fullText')
- expect(retrievalIcon).toHaveProperty('hybrid')
- })
-
- it('should have truthy values for each key', () => {
- expect(retrievalIcon.vector).toBeTruthy()
- expect(retrievalIcon.fullText).toBeTruthy()
- expect(retrievalIcon.hybrid).toBeTruthy()
- })
- })
-})
diff --git a/web/app/components/datasets/create/__tests__/index.spec.tsx b/web/app/components/datasets/create/__tests__/index.spec.tsx
index 2a2d1ac90ff..898a752b835 100644
--- a/web/app/components/datasets/create/__tests__/index.spec.tsx
+++ b/web/app/components/datasets/create/__tests__/index.spec.tsx
@@ -409,13 +409,6 @@ describe('DatasetUpdateForm', () => {
// Rendering Tests - Verify component renders correctly in different states
describe('Rendering', () => {
- it('should render without crashing', () => {
- render()
-
- expect(screen.getByTestId('top-bar'))!.toBeInTheDocument()
- expect(screen.getByTestId('step-one'))!.toBeInTheDocument()
- })
-
it('should render TopBar with correct active index for step 1', () => {
render()
diff --git a/web/app/components/datasets/create/embedding-process/__tests__/index.spec.tsx b/web/app/components/datasets/create/embedding-process/__tests__/index.spec.tsx
index cc337268a19..98f8d7b6ddd 100644
--- a/web/app/components/datasets/create/embedding-process/__tests__/index.spec.tsx
+++ b/web/app/components/datasets/create/embedding-process/__tests__/index.spec.tsx
@@ -1,54 +1,55 @@
-import type {
- FullDocumentDetail,
- IndexingStatusResponse,
- ProcessRuleResponse,
-} from '@/models/datasets'
-import { act, render, renderHook, screen } from '@testing-library/react'
-import { DataSourceType, ProcessMode } from '@/models/datasets'
-import { RETRIEVE_METHOD } from '@/types/app'
-import IndexingProgressItem from '../indexing-progress-item'
-import RuleDetail from '../rule-detail'
-import UpgradeBanner from '../upgrade-banner'
-import { useIndexingStatusPolling } from '../use-indexing-status-polling'
-import {
- createDocumentLookup,
- getFileType,
- getSourcePercent,
- isLegacyDataSourceInfo,
- isSourceEmbedding,
-} from '../utils'
+import type { IndexingStatusResponse } from '@/models/datasets'
+import { render, screen } from '@testing-library/react'
+import userEvent from '@testing-library/user-event'
+import EmbeddingProcess from '../index'
const mockPush = vi.fn()
-const mockRouter = { push: mockPush }
-vi.mock('@/next/navigation', () => ({
- useRouter: () => mockRouter,
-}))
-
-// Mock API service
-const mockFetchIndexingStatusBatch = vi.fn()
-vi.mock('@/service/datasets', () => ({
- fetchIndexingStatusBatch: (params: { datasetId: string; batchId: string }) =>
- mockFetchIndexingStatusBatch(params),
-}))
-
-const mockProcessRuleData: ProcessRuleResponse | undefined = undefined
-vi.mock('@/service/knowledge/use-dataset', () => ({
- useProcessRule: vi.fn(() => ({ data: mockProcessRuleData })),
-}))
-
const mockInvalidDocumentList = vi.fn()
+let mockEnableBilling = false
+let mockPlanType = 'sandbox'
+let mockPollingState: {
+ statusList: IndexingStatusResponse[]
+ isEmbedding: boolean
+ isEmbeddingCompleted: boolean
+} = {
+ statusList: [],
+ isEmbedding: false,
+ isEmbeddingCompleted: false,
+}
+
+vi.mock('@/next/navigation', () => ({
+ useRouter: () => ({ push: mockPush }),
+}))
+
+vi.mock('@/next/link', () => ({
+ default: ({
+ children,
+ href,
+ ...props
+ }: {
+ children: React.ReactNode
+ href: string
+ target?: string
+ rel?: string
+ }) => (
+
+ {children}
+
+ ),
+}))
+
+vi.mock('@/service/knowledge/use-dataset', () => ({
+ useProcessRule: () => ({ data: undefined }),
+}))
+
vi.mock('@/service/knowledge/use-document', () => ({
useInvalidDocumentList: () => mockInvalidDocumentList,
}))
-// Mock useDatasetApiAccessUrl hook
vi.mock('@/hooks/use-api-access-url', () => ({
useDatasetApiAccessUrl: () => 'https://api.example.com/docs',
}))
-// Mock provider context
-let mockEnableBilling = false
-let mockPlanType = 'sandbox'
vi.mock('@/context/provider-context', () => ({
useProviderContext: () => ({
enableBilling: mockEnableBilling,
@@ -56,1272 +57,74 @@ vi.mock('@/context/provider-context', () => ({
}),
}))
-// Mock icons
-vi.mock('../../icons', () => ({
- indexMethodIcon: {
- economical: '/icons/economical.svg',
- high_quality: '/icons/high-quality.svg',
- },
- retrievalIcon: {
- fullText: '/icons/full-text.svg',
- hybrid: '/icons/hybrid.svg',
- vector: '/icons/vector.svg',
- },
+vi.mock('../use-indexing-status-polling', () => ({
+ useIndexingStatusPolling: () => mockPollingState,
}))
-// Mock IndexingType enum from step-two
-vi.mock('../../step-two', () => ({
- IndexingType: {
- QUALIFIED: 'high_quality',
- ECONOMICAL: 'economy',
- },
+vi.mock('../indexing-progress-item', () => ({
+ default: () => progress item
,
}))
-// Factory Functions for Test Data
+vi.mock('../rule-detail', () => ({
+ default: () => rule detail
,
+}))
-/**
- * Create a mock IndexingStatusResponse
- */
-const createMockIndexingStatus = (
- overrides: Partial = {},
-): IndexingStatusResponse => ({
- id: 'doc-1',
- indexing_status: 'completed',
- processing_started_at: Date.now(),
- parsing_completed_at: Date.now(),
- cleaning_completed_at: Date.now(),
- splitting_completed_at: Date.now(),
- completed_at: Date.now(),
- paused_at: null,
- error: null,
- stopped_at: null,
- completed_segments: 10,
- total_segments: 10,
- ...overrides,
-})
-
-/**
- * Create a mock FullDocumentDetail
- */
-const createMockDocument = (overrides: Partial = {}): FullDocumentDetail =>
- ({
- id: 'doc-1',
- name: 'test-document.txt',
- data_source_type: DataSourceType.FILE,
- data_source_info: {
- upload_file: {
- id: 'file-1',
- name: 'test-document.txt',
- extension: 'txt',
- mime_type: 'text/plain',
- size: 1024,
- created_by: 'user-1',
- created_at: Date.now(),
- },
- },
- batch: 'batch-1',
- created_api_request_id: 'req-1',
- processing_started_at: Date.now(),
- parsing_completed_at: Date.now(),
- cleaning_completed_at: Date.now(),
- splitting_completed_at: Date.now(),
- tokens: 100,
- indexing_latency: 5000,
- completed_at: Date.now(),
- paused_by: '',
- paused_at: 0,
- stopped_at: 0,
- indexing_status: 'completed',
- disabled_at: 0,
- ...overrides,
- }) as FullDocumentDetail
-
-/**
- * Create a mock ProcessRuleResponse
- */
-const createMockProcessRule = (overrides: Partial = {}): ProcessRuleResponse =>
- ({
- mode: ProcessMode.general,
- rules: {
- segmentation: {
- separator: '\n',
- max_tokens: 500,
- chunk_overlap: 50,
- },
- pre_processing_rules: [
- { id: 'remove_extra_spaces', enabled: true },
- { id: 'remove_urls_emails', enabled: false },
- ],
- },
- ...overrides,
- }) as ProcessRuleResponse
-
-// Utils Tests
-
-describe('utils', () => {
- // Test utility functions for document handling
-
- describe('isLegacyDataSourceInfo', () => {
- it('should return true for legacy data source with upload_file object', () => {
- const info = {
- upload_file: { id: 'file-1', name: 'test.txt' },
- }
-
- expect(isLegacyDataSourceInfo(info as Parameters[0])).toBe(
- true,
- )
- })
-
- it('should return false for null', () => {
- expect(
- isLegacyDataSourceInfo(null as unknown as Parameters[0]),
- ).toBe(false)
- })
-
- it('should return false for undefined', () => {
- expect(
- isLegacyDataSourceInfo(
- undefined as unknown as Parameters[0],
- ),
- ).toBe(false)
- })
-
- it('should return false when upload_file is not an object', () => {
- const info = { upload_file: 'string-value' }
-
- expect(
- isLegacyDataSourceInfo(info as unknown as Parameters[0]),
- ).toBe(false)
- })
- })
-
- describe('isSourceEmbedding', () => {
- it.each([
- ['indexing', true],
- ['splitting', true],
- ['parsing', true],
- ['cleaning', true],
- ['waiting', true],
- ['completed', false],
- ['error', false],
- ['paused', false],
- ])('should return %s for status "%s"', (status, expected) => {
- const detail = createMockIndexingStatus({
- indexing_status: status as IndexingStatusResponse['indexing_status'],
- })
-
- expect(isSourceEmbedding(detail)).toBe(expected)
- })
- })
-
- describe('getSourcePercent', () => {
- it('should return 0 when total_segments is 0', () => {
- const detail = createMockIndexingStatus({
- completed_segments: 0,
- total_segments: 0,
- })
-
- expect(getSourcePercent(detail)).toBe(0)
- })
-
- it('should calculate correct percentage', () => {
- const detail = createMockIndexingStatus({
- completed_segments: 5,
- total_segments: 10,
- })
-
- expect(getSourcePercent(detail)).toBe(50)
- })
-
- it('should cap percentage at 100', () => {
- const detail = createMockIndexingStatus({
- completed_segments: 15,
- total_segments: 10,
- })
-
- expect(getSourcePercent(detail)).toBe(100)
- })
-
- it('should handle undefined values', () => {
- const detail = { indexing_status: 'indexing' } as IndexingStatusResponse
-
- expect(getSourcePercent(detail)).toBe(0)
- })
-
- it('should round to nearest integer', () => {
- const detail = createMockIndexingStatus({
- completed_segments: 1,
- total_segments: 3,
- })
-
- expect(getSourcePercent(detail)).toBe(33)
- })
- })
-
- describe('getFileType', () => {
- it('should extract extension from filename', () => {
- expect(getFileType('document.pdf')).toBe('pdf')
- expect(getFileType('file.name.txt')).toBe('txt')
- expect(getFileType('archive.tar.gz')).toBe('gz')
- })
-
- it('should return "txt" for undefined', () => {
- expect(getFileType(undefined)).toBe('txt')
- })
-
- it('should return filename without extension', () => {
- expect(getFileType('filename')).toBe('filename')
- })
- })
-
- describe('createDocumentLookup', () => {
- it('should create lookup functions for documents', () => {
- const documents = [
- createMockDocument({ id: 'doc-1', name: 'file1.txt' }),
- createMockDocument({
- id: 'doc-2',
- name: 'file2.pdf',
- data_source_type: DataSourceType.NOTION,
- }),
- ]
-
- const lookup = createDocumentLookup(documents)
-
- expect(lookup.getName('doc-1')).toBe('file1.txt')
- expect(lookup.getName('doc-2')).toBe('file2.pdf')
- expect(lookup.getName('non-existent')).toBeUndefined()
- })
-
- it('should return source type correctly', () => {
- const documents = [
- createMockDocument({ id: 'doc-1', data_source_type: DataSourceType.FILE }),
- createMockDocument({ id: 'doc-2', data_source_type: DataSourceType.NOTION }),
- ]
- const lookup = createDocumentLookup(documents)
-
- expect(lookup.getSourceType('doc-1')).toBe(DataSourceType.FILE)
- expect(lookup.getSourceType('doc-2')).toBe(DataSourceType.NOTION)
- })
-
- it('should return notion icon for legacy data source', () => {
- const documents = [
- createMockDocument({
- id: 'doc-1',
- data_source_info: {
- upload_file: { id: 'f1' },
- notion_page_icon: '๐',
- } as FullDocumentDetail['data_source_info'],
- }),
- ]
- const lookup = createDocumentLookup(documents)
-
- expect(lookup.getNotionIcon('doc-1')).toBe('๐')
- })
-
- it('should return undefined for non-legacy notion icon', () => {
- const documents = [
- createMockDocument({
- id: 'doc-1',
- data_source_info: {
- some_other_field: 'value',
- } as unknown as FullDocumentDetail['data_source_info'],
- }),
- ]
- const lookup = createDocumentLookup(documents)
-
- expect(lookup.getNotionIcon('doc-1')).toBeUndefined()
- })
-
- it('should memoize lookups with Map for performance', () => {
- const documents = Array.from({ length: 1000 }, (_, i) =>
- createMockDocument({ id: `doc-${i}`, name: `file${i}.txt` }),
- )
-
- const lookup = createDocumentLookup(documents)
- const startTime = performance.now()
- for (let i = 0; i < 1000; i++) lookup.getName(`doc-${i}`)
-
- const duration = performance.now() - startTime
-
- // Assert - should be very fast due to Map lookup
- expect(duration).toBeLessThan(50)
- })
- })
-})
-
-// useIndexingStatusPolling Hook Tests
-
-describe('useIndexingStatusPolling', () => {
- // Test the polling hook for indexing status
-
- beforeEach(() => {
- vi.clearAllMocks()
- vi.useFakeTimers()
- })
-
- afterEach(() => {
- vi.useRealTimers()
- })
-
- it('should fetch status on mount', async () => {
- const mockStatus = [createMockIndexingStatus({ indexing_status: 'completed' })]
- mockFetchIndexingStatusBatch.mockResolvedValue({ data: mockStatus })
-
- const { result } = renderHook(() =>
- useIndexingStatusPolling({ datasetId: 'ds-1', batchId: 'batch-1' }),
- )
-
- await act(async () => {
- await vi.runOnlyPendingTimersAsync()
- })
-
- expect(mockFetchIndexingStatusBatch).toHaveBeenCalledWith({
- datasetId: 'ds-1',
- batchId: 'batch-1',
- })
- expect(result.current.statusList).toEqual(mockStatus)
- })
-
- it('should stop polling when all statuses are completed', async () => {
- const mockStatus = [createMockIndexingStatus({ indexing_status: 'completed' })]
- mockFetchIndexingStatusBatch.mockResolvedValue({ data: mockStatus })
-
- renderHook(() => useIndexingStatusPolling({ datasetId: 'ds-1', batchId: 'batch-1' }))
-
- await act(async () => {
- await vi.runOnlyPendingTimersAsync()
- })
-
- // Assert - should only be called once since status is completed
- expect(mockFetchIndexingStatusBatch).toHaveBeenCalledTimes(1)
- })
-
- it('should continue polling when status is indexing', async () => {
- const indexingStatus = [createMockIndexingStatus({ indexing_status: 'indexing' })]
- const completedStatus = [createMockIndexingStatus({ indexing_status: 'completed' })]
-
- mockFetchIndexingStatusBatch
- .mockResolvedValueOnce({ data: indexingStatus })
- .mockResolvedValueOnce({ data: completedStatus })
-
- renderHook(() => useIndexingStatusPolling({ datasetId: 'ds-1', batchId: 'batch-1' }))
-
- // First poll
- await act(async () => {
- await vi.runOnlyPendingTimersAsync()
- })
-
- // Advance timer for next poll (2500ms)
- await act(async () => {
- await vi.advanceTimersByTimeAsync(2500)
- })
-
- expect(mockFetchIndexingStatusBatch).toHaveBeenCalledTimes(2)
- })
-
- it('should stop polling when status is error', async () => {
- const mockStatus = [createMockIndexingStatus({ indexing_status: 'error', error: 'Some error' })]
- mockFetchIndexingStatusBatch.mockResolvedValue({ data: mockStatus })
-
- const { result } = renderHook(() =>
- useIndexingStatusPolling({ datasetId: 'ds-1', batchId: 'batch-1' }),
- )
-
- await act(async () => {
- await vi.runOnlyPendingTimersAsync()
- })
-
- expect(result.current.isEmbeddingCompleted).toBe(true)
- expect(mockFetchIndexingStatusBatch).toHaveBeenCalledTimes(1)
- })
-
- it('should stop polling when status is paused', async () => {
- const mockStatus = [createMockIndexingStatus({ indexing_status: 'paused' })]
- mockFetchIndexingStatusBatch.mockResolvedValue({ data: mockStatus })
-
- const { result } = renderHook(() =>
- useIndexingStatusPolling({ datasetId: 'ds-1', batchId: 'batch-1' }),
- )
-
- await act(async () => {
- await vi.runOnlyPendingTimersAsync()
- })
-
- expect(result.current.isEmbeddingCompleted).toBe(true)
- })
-
- it('should continue polling on API error', async () => {
- mockFetchIndexingStatusBatch
- .mockRejectedValueOnce(new Error('Network error'))
- .mockResolvedValueOnce({ data: [createMockIndexingStatus({ indexing_status: 'completed' })] })
-
- renderHook(() => useIndexingStatusPolling({ datasetId: 'ds-1', batchId: 'batch-1' }))
-
- await act(async () => {
- await vi.runOnlyPendingTimersAsync()
- })
-
- await act(async () => {
- await vi.advanceTimersByTimeAsync(2500)
- })
-
- // Assert - should retry after error
- expect(mockFetchIndexingStatusBatch).toHaveBeenCalledTimes(2)
- })
-
- it('should return correct isEmbedding state', async () => {
- const mockStatus = [createMockIndexingStatus({ indexing_status: 'indexing' })]
- mockFetchIndexingStatusBatch.mockResolvedValue({ data: mockStatus })
-
- const { result } = renderHook(() =>
- useIndexingStatusPolling({ datasetId: 'ds-1', batchId: 'batch-1' }),
- )
-
- await act(async () => {
- await vi.runOnlyPendingTimersAsync()
- })
-
- expect(result.current.isEmbedding).toBe(true)
- expect(result.current.isEmbeddingCompleted).toBe(false)
- })
-
- it('should cleanup timeout on unmount', async () => {
- const mockStatus = [createMockIndexingStatus({ indexing_status: 'indexing' })]
- mockFetchIndexingStatusBatch.mockResolvedValue({ data: mockStatus })
-
- const { unmount } = renderHook(() =>
- useIndexingStatusPolling({ datasetId: 'ds-1', batchId: 'batch-1' }),
- )
-
- await act(async () => {
- await vi.runOnlyPendingTimersAsync()
- })
-
- const callCountBeforeUnmount = mockFetchIndexingStatusBatch.mock.calls.length
-
- unmount()
-
- // Advance timers - should not trigger more calls after unmount
- await act(async () => {
- await vi.advanceTimersByTimeAsync(5000)
- })
-
- // Assert - no additional calls after unmount
- expect(mockFetchIndexingStatusBatch).toHaveBeenCalledTimes(callCountBeforeUnmount)
- })
-
- it('should handle multiple documents with mixed statuses', async () => {
- const mockStatus = [
- createMockIndexingStatus({ id: 'doc-1', indexing_status: 'completed' }),
- createMockIndexingStatus({ id: 'doc-2', indexing_status: 'indexing' }),
- ]
- mockFetchIndexingStatusBatch.mockResolvedValue({ data: mockStatus })
-
- const { result } = renderHook(() =>
- useIndexingStatusPolling({ datasetId: 'ds-1', batchId: 'batch-1' }),
- )
-
- await act(async () => {
- await vi.runOnlyPendingTimersAsync()
- })
-
- expect(result.current.isEmbedding).toBe(true)
- expect(result.current.isEmbeddingCompleted).toBe(false)
- expect(result.current.statusList).toHaveLength(2)
- })
-
- it('should return empty statusList initially', () => {
- const { result } = renderHook(() =>
- useIndexingStatusPolling({ datasetId: 'ds-1', batchId: 'batch-1' }),
- )
-
- expect(result.current.statusList).toEqual([])
- expect(result.current.isEmbedding).toBe(false)
- expect(result.current.isEmbeddingCompleted).toBe(false)
- })
-})
-
-// UpgradeBanner Component Tests
-
-describe('UpgradeBanner', () => {
- // Test the upgrade banner component
-
- beforeEach(() => {
- vi.clearAllMocks()
- })
-
- it('should render upgrade message', () => {
- render()
-
- expect(
- screen.getByText(/billing\.plansCommon\.documentProcessingPriorityUpgrade/i),
- ).toBeInTheDocument()
- })
-
- it('should render ZapFast icon', () => {
- const { container } = render()
-
- expect(container.querySelector('svg')).toBeInTheDocument()
- })
-
- it('should render UpgradeBtn component', () => {
- render()
-
- // Assert - UpgradeBtn should be rendered
- const upgradeContainer = screen.getByText(
- /billing\.plansCommon\.documentProcessingPriorityUpgrade/i,
- ).parentElement
- expect(upgradeContainer).toBeInTheDocument()
- })
-})
-
-// IndexingProgressItem Component Tests
-
-describe('IndexingProgressItem', () => {
- // Test the progress item component for individual documents
-
- beforeEach(() => {
- vi.clearAllMocks()
- })
-
- describe('Rendering', () => {
- it('should render document name', () => {
- const detail = createMockIndexingStatus()
-
- render()
-
- expect(screen.getByText('test-document.txt')).toBeInTheDocument()
- })
-
- it('should render progress percentage when embedding', () => {
- const detail = createMockIndexingStatus({
- indexing_status: 'indexing',
- completed_segments: 5,
- total_segments: 10,
- })
-
- render()
-
- expect(screen.getByText('50%')).toBeInTheDocument()
- })
-
- it('should not render progress percentage when completed', () => {
- const detail = createMockIndexingStatus({ indexing_status: 'completed' })
-
- render()
-
- expect(screen.queryByText('%')).not.toBeInTheDocument()
- })
- })
-
- describe('Status Icons', () => {
- it('should render success icon for completed status', () => {
- const detail = createMockIndexingStatus({ indexing_status: 'completed' })
-
- const { container } = render()
-
- expect(container.querySelector('.text-text-success')).toBeInTheDocument()
- })
-
- it('should render error icon for error status', () => {
- const detail = createMockIndexingStatus({
- indexing_status: 'error',
- error: 'Processing failed',
- })
-
- const { container } = render()
-
- expect(container.querySelector('.text-text-destructive')).toBeInTheDocument()
- })
-
- it('should not render status icon for indexing status', () => {
- const detail = createMockIndexingStatus({ indexing_status: 'indexing' })
-
- const { container } = render()
-
- expect(container.querySelector('.text-text-success')).not.toBeInTheDocument()
- expect(container.querySelector('.text-text-destructive')).not.toBeInTheDocument()
- })
- })
-
- describe('Source Type Icons', () => {
- it('should render file icon for FILE source type', () => {
- const detail = createMockIndexingStatus()
-
- render(
- ,
- )
-
- // Assert - DocumentFileIcon should be rendered
- expect(screen.getByText('document.pdf')).toBeInTheDocument()
- })
-
- // DocumentFileIcon branch coverage: different file extensions
- describe('DocumentFileIcon file extensions', () => {
- it.each([
- ['document.pdf', 'pdf'],
- ['data.json', 'json'],
- ['page.html', 'html'],
- ['readme.txt', 'txt'],
- ['notes.markdown', 'markdown'],
- ['readme.md', 'md'],
- ['spreadsheet.xlsx', 'xlsx'],
- ['legacy.xls', 'xls'],
- ['data.csv', 'csv'],
- ['letter.doc', 'doc'],
- ['report.docx', 'docx'],
- ])('should render file icon for %s (%s extension)', (filename) => {
- const detail = createMockIndexingStatus()
-
- render(
- ,
- )
-
- expect(screen.getByText(filename)).toBeInTheDocument()
- })
-
- it('should handle unknown file extension with default icon', () => {
- const detail = createMockIndexingStatus()
-
- render(
- ,
- )
-
- // Assert - should still render with default document icon
- expect(screen.getByText('archive.zip')).toBeInTheDocument()
- })
-
- it('should handle uppercase extension', () => {
- const detail = createMockIndexingStatus()
-
- render(
- ,
- )
-
- expect(screen.getByText('REPORT.PDF')).toBeInTheDocument()
- })
-
- it('should handle mixed case extension', () => {
- const detail = createMockIndexingStatus()
-
- render(
- ,
- )
-
- expect(screen.getByText('Document.Docx')).toBeInTheDocument()
- })
-
- it('should handle filename with multiple dots', () => {
- const detail = createMockIndexingStatus()
-
- render(
- ,
- )
-
- // Assert - should extract "pdf" as extension
- expect(screen.getByText('my.file.name.pdf')).toBeInTheDocument()
- })
-
- it('should handle filename without extension', () => {
- const detail = createMockIndexingStatus()
-
- render(
- ,
- )
-
- // Assert - should use filename itself as fallback
- expect(screen.getByText('noextension')).toBeInTheDocument()
- })
- })
-
- it('should render notion icon for NOTION source type', () => {
- const detail = createMockIndexingStatus()
-
- render(
- ,
- )
-
- expect(screen.getByText('Notion Page')).toBeInTheDocument()
- })
- })
-
- describe('Progress Bar', () => {
- it('should render progress bar when embedding', () => {
- const detail = createMockIndexingStatus({
- indexing_status: 'indexing',
- completed_segments: 30,
- total_segments: 100,
- })
-
- const { container } = render()
-
- const progressBar = container.querySelector('[style*="width: 30%"]')
- expect(progressBar).toBeInTheDocument()
- })
-
- it('should not render progress bar when completed', () => {
- const detail = createMockIndexingStatus({ indexing_status: 'completed' })
-
- const { container } = render()
-
- const progressBar = container.querySelector('.bg-components-progress-bar-progress')
- expect(progressBar).not.toBeInTheDocument()
- })
-
- it('should apply error styling for error status', () => {
- const detail = createMockIndexingStatus({ indexing_status: 'error' })
-
- const { container } = render()
-
- expect(container.querySelector('.bg-state-destructive-hover-alt')).toBeInTheDocument()
- })
- })
-
- describe('Billing', () => {
- it('should render PriorityLabel when enableBilling is true', () => {
- const detail = createMockIndexingStatus()
-
- render()
-
- // Assert - PriorityLabel component should be in the DOM
- const container = screen.getByText('test.txt').parentElement
- expect(container).toBeInTheDocument()
- })
-
- it('should not render PriorityLabel when enableBilling is false', () => {
- const detail = createMockIndexingStatus()
-
- render()
-
- expect(screen.getByText('test.txt')).toBeInTheDocument()
- })
- })
-
- describe('Edge Cases', () => {
- it('should handle undefined name', () => {
- const detail = createMockIndexingStatus()
-
- render()
-
- // Assert - should not crash
- expect(document.body).toBeInTheDocument()
- })
-
- it('should handle undefined sourceType', () => {
- const detail = createMockIndexingStatus()
-
- render()
-
- // Assert - should render without source icon
- expect(screen.getByText('test.txt')).toBeInTheDocument()
- })
- })
-})
-
-// RuleDetail Component Tests
-
-describe('RuleDetail', () => {
- // Test the rule detail component for process configuration display
-
- beforeEach(() => {
- vi.clearAllMocks()
- })
-
- describe('Rendering', () => {
- it('should render without crashing', () => {
- render()
-
- expect(screen.getByText(/datasetDocuments\.embedding\.mode/i)).toBeInTheDocument()
- })
-
- it('should render all field labels', () => {
- render()
-
- expect(screen.getByText(/datasetDocuments\.embedding\.mode/i)).toBeInTheDocument()
- expect(screen.getByText(/datasetDocuments\.embedding\.segmentLength/i)).toBeInTheDocument()
- expect(screen.getByText(/datasetDocuments\.embedding\.textCleaning/i)).toBeInTheDocument()
- expect(screen.getByText(/datasetCreation\.stepTwo\.indexMode/i)).toBeInTheDocument()
- expect(
- screen.getByText(/datasetSettings\.form\.retrievalSetting\.title/i),
- ).toBeInTheDocument()
- })
- })
-
- describe('Mode Display', () => {
- it('should show "-" when sourceData is undefined', () => {
- render()
-
- expect(screen.getAllByText('-')).toHaveLength(3) // mode, segmentLength, textCleaning
- })
-
- it('should show "custom" for general process mode', () => {
- const sourceData = createMockProcessRule({ mode: ProcessMode.general })
-
- render()
-
- expect(screen.getByText(/datasetDocuments\.embedding\.custom/i)).toBeInTheDocument()
- })
-
- it('should show hierarchical mode with paragraph parent', () => {
- const sourceData = createMockProcessRule({
- mode: ProcessMode.parentChild,
- rules: {
- parent_mode: 'paragraph',
- segmentation: { max_tokens: 500 },
- },
- } as Partial)
-
- render()
-
- expect(screen.getByText(/datasetDocuments\.embedding\.hierarchical/i)).toBeInTheDocument()
- })
- })
-
- describe('Segment Length Display', () => {
- it('should show max_tokens for general mode', () => {
- const sourceData = createMockProcessRule({
- mode: ProcessMode.general,
- rules: {
- segmentation: { max_tokens: 500 },
- },
- } as Partial)
-
- render()
-
- expect(screen.getByText('500')).toBeInTheDocument()
- })
-
- it('should show parent and child tokens for hierarchical mode', () => {
- const sourceData = createMockProcessRule({
- mode: ProcessMode.parentChild,
- rules: {
- segmentation: { max_tokens: 1000 },
- subchunk_segmentation: { max_tokens: 200 },
- },
- } as Partial)
-
- render()
-
- expect(screen.getByText(/1000/)).toBeInTheDocument()
- expect(screen.getByText(/200/)).toBeInTheDocument()
- })
- })
-
- describe('Text Cleaning Rules', () => {
- it('should show enabled rule names', () => {
- const sourceData = createMockProcessRule({
- mode: ProcessMode.general,
- rules: {
- pre_processing_rules: [
- { id: 'remove_extra_spaces', enabled: true },
- { id: 'remove_urls_emails', enabled: true },
- { id: 'remove_stopwords', enabled: false },
- ],
- },
- } as Partial)
-
- render()
-
- expect(screen.getByText(/removeExtraSpaces/i)).toBeInTheDocument()
- expect(screen.getByText(/removeUrlEmails/i)).toBeInTheDocument()
- })
-
- it('should show "-" when no rules are enabled', () => {
- const sourceData = createMockProcessRule({
- mode: ProcessMode.general,
- rules: {
- pre_processing_rules: [{ id: 'remove_extra_spaces', enabled: false }],
- },
- } as Partial)
-
- render()
-
- // Assert - textCleaning should show "-"
- const dashElements = screen.getAllByText('-')
- expect(dashElements.length).toBeGreaterThan(0)
- })
- })
-
- describe('Indexing Type', () => {
- it('should show qualified for high_quality indexing', () => {
- render()
-
- expect(screen.getByText(/datasetCreation\.stepTwo\.qualified/i)).toBeInTheDocument()
- })
-
- it('should show economical for economy indexing', () => {
- render()
-
- expect(screen.getByText(/datasetCreation\.stepTwo\.economical/i)).toBeInTheDocument()
- })
-
- it('should render correct icon for indexing type', () => {
- const { container } = render()
-
- const images = container.querySelectorAll('img')
- expect(images.length).toBeGreaterThan(0)
- })
- })
-
- describe('Retrieval Method', () => {
- it('should show semantic search by default', () => {
- render()
-
- expect(screen.getByText(/dataset\.retrieval\.semantic_search\.title/i)).toBeInTheDocument()
- })
-
- it('should show keyword search for economical indexing', () => {
- render()
-
- expect(screen.getByText(/dataset\.retrieval\.keyword_search\.title/i)).toBeInTheDocument()
- })
-
- it.each([
- [RETRIEVE_METHOD.fullText, 'full_text_search'],
- [RETRIEVE_METHOD.hybrid, 'hybrid_search'],
- [RETRIEVE_METHOD.semantic, 'semantic_search'],
- ])('should show correct label for %s retrieval method', (method, expectedKey) => {
- render()
-
- expect(
- screen.getByText(new RegExp(`dataset\\.retrieval\\.${expectedKey}\\.title`, 'i')),
- ).toBeInTheDocument()
- })
- })
-})
-
-// EmbeddingProcess Integration Tests
+vi.mock('../upgrade-banner', () => ({
+ default: () => upgrade processing priority
,
+}))
describe('EmbeddingProcess', () => {
- // Integration tests for the main EmbeddingProcess component
-
- // Import the main component after mocks are set up
- let EmbeddingProcess: typeof import('../index').default
-
- beforeEach(async () => {
+ beforeEach(() => {
vi.clearAllMocks()
- vi.useFakeTimers()
mockEnableBilling = false
mockPlanType = 'sandbox'
-
- // Dynamically import to get fresh component with mocks
- const embeddingModule = await import('../index')
- EmbeddingProcess = embeddingModule.default
+ mockPollingState = {
+ statusList: [],
+ isEmbedding: false,
+ isEmbeddingCompleted: false,
+ }
})
- afterEach(() => {
- vi.useRealTimers()
+ it('shows that document indexing is in progress', () => {
+ mockPollingState.isEmbedding = true
+
+ render()
+
+ expect(screen.getByText('datasetDocuments.embedding.processing')).toBeInTheDocument()
})
- describe('Rendering', () => {
- it('should render without crashing', async () => {
- mockFetchIndexingStatusBatch.mockResolvedValue({ data: [] })
+ it('shows that document indexing has completed', () => {
+ mockPollingState.isEmbeddingCompleted = true
- render()
+ render()
- await act(async () => {
- await vi.runOnlyPendingTimersAsync()
- })
-
- expect(document.body).toBeInTheDocument()
- })
-
- it('should render status header', async () => {
- const mockStatus = [createMockIndexingStatus({ indexing_status: 'indexing' })]
- mockFetchIndexingStatusBatch.mockResolvedValue({ data: mockStatus })
-
- render()
-
- await act(async () => {
- await vi.runOnlyPendingTimersAsync()
- })
-
- expect(screen.getByText(/datasetDocuments\.embedding\.processing/i)).toBeInTheDocument()
- })
-
- it('should show completed status when all documents are done', async () => {
- const mockStatus = [createMockIndexingStatus({ indexing_status: 'completed' })]
- mockFetchIndexingStatusBatch.mockResolvedValue({ data: mockStatus })
-
- render()
-
- await act(async () => {
- await vi.runOnlyPendingTimersAsync()
- })
-
- expect(screen.getByText(/datasetDocuments\.embedding\.completed/i)).toBeInTheDocument()
- })
+ expect(screen.getByText('datasetDocuments.embedding.completed')).toBeInTheDocument()
})
- describe('Progress Items', () => {
- it('should render progress items for each document', async () => {
- const documents = [
- createMockDocument({ id: 'doc-1', name: 'file1.txt' }),
- createMockDocument({ id: 'doc-2', name: 'file2.pdf' }),
- ]
- const mockStatus = [
- createMockIndexingStatus({ id: 'doc-1' }),
- createMockIndexingStatus({ id: 'doc-2' }),
- ]
- mockFetchIndexingStatusBatch.mockResolvedValue({ data: mockStatus })
+ it('invalidates the document list before navigating to it', async () => {
+ const user = userEvent.setup()
+ render()
- render()
+ await user.click(screen.getByRole('button', { name: 'datasetCreation.stepThree.navTo' }))
- await act(async () => {
- await vi.runOnlyPendingTimersAsync()
- })
-
- expect(screen.getByText('file1.txt')).toBeInTheDocument()
- expect(screen.getByText('file2.pdf')).toBeInTheDocument()
- })
+ expect(mockInvalidDocumentList).toHaveBeenCalledOnce()
+ expect(mockPush).toHaveBeenCalledWith('/datasets/dataset-1/documents')
})
- describe('Upgrade Banner', () => {
- it('should show upgrade banner when billing is enabled and not team plan', async () => {
- mockEnableBilling = true
- mockPlanType = 'sandbox'
- mockFetchIndexingStatusBatch.mockResolvedValue({ data: [] })
+ it('links to the dataset API reference', () => {
+ render()
- // Re-import to get updated mock values
- const embeddingModule = await import('../index')
- EmbeddingProcess = embeddingModule.default
-
- render()
-
- await act(async () => {
- await vi.runOnlyPendingTimersAsync()
- })
-
- expect(
- screen.getByText(/billing\.plansCommon\.documentProcessingPriorityUpgrade/i),
- ).toBeInTheDocument()
- })
-
- it('should not show upgrade banner when billing is disabled', async () => {
- mockEnableBilling = false
- mockFetchIndexingStatusBatch.mockResolvedValue({ data: [] })
-
- render()
-
- await act(async () => {
- await vi.runOnlyPendingTimersAsync()
- })
-
- expect(
- screen.queryByText(/billing\.plansCommon\.documentProcessingPriorityUpgrade/i),
- ).not.toBeInTheDocument()
- })
-
- it('should not show upgrade banner for team plan', async () => {
- mockEnableBilling = true
- mockPlanType = 'team'
- mockFetchIndexingStatusBatch.mockResolvedValue({ data: [] })
-
- // Re-import to get updated mock values
- const embeddingModule = await import('../index')
- EmbeddingProcess = embeddingModule.default
-
- render()
-
- await act(async () => {
- await vi.runOnlyPendingTimersAsync()
- })
-
- expect(
- screen.queryByText(/billing\.plansCommon\.documentProcessingPriorityUpgrade/i),
- ).not.toBeInTheDocument()
- })
+ expect(screen.getByRole('link', { name: 'Access the API' })).toHaveAttribute(
+ 'href',
+ 'https://api.example.com/docs',
+ )
})
- describe('Action Buttons', () => {
- it('should render API access button with correct link', async () => {
- mockFetchIndexingStatusBatch.mockResolvedValue({ data: [] })
+ it('offers a processing-priority upgrade outside the team plan', () => {
+ mockEnableBilling = true
- render()
+ render()
- await act(async () => {
- await vi.runOnlyPendingTimersAsync()
- })
-
- const apiButton = screen.getByText('Access the API')
- expect(apiButton).toBeInTheDocument()
- expect(apiButton.closest('a')).toHaveAttribute('href', 'https://api.example.com/docs')
- })
-
- it('should render navigation button', async () => {
- mockFetchIndexingStatusBatch.mockResolvedValue({ data: [] })
-
- render()
-
- await act(async () => {
- await vi.runOnlyPendingTimersAsync()
- })
-
- expect(screen.getByText(/datasetCreation\.stepThree\.navTo/i)).toBeInTheDocument()
- })
-
- it('should navigate to documents list when nav button clicked', async () => {
- mockFetchIndexingStatusBatch.mockResolvedValue({ data: [] })
-
- render()
-
- await act(async () => {
- await vi.runOnlyPendingTimersAsync()
- })
-
- const navButton = screen.getByText(/datasetCreation\.stepThree\.navTo/i)
-
- await act(async () => {
- navButton.click()
- })
-
- expect(mockInvalidDocumentList).toHaveBeenCalled()
- expect(mockPush).toHaveBeenCalledWith('/datasets/ds-1/documents')
- })
- })
-
- describe('Rule Detail', () => {
- it('should render RuleDetail component', async () => {
- mockFetchIndexingStatusBatch.mockResolvedValue({ data: [] })
-
- render(
- ,
- )
-
- await act(async () => {
- await vi.runOnlyPendingTimersAsync()
- })
-
- expect(screen.getByText(/datasetDocuments\.embedding\.mode/i)).toBeInTheDocument()
- })
-
- it('should pass indexingType to RuleDetail', async () => {
- mockFetchIndexingStatusBatch.mockResolvedValue({ data: [] })
-
- render()
-
- await act(async () => {
- await vi.runOnlyPendingTimersAsync()
- })
-
- expect(screen.getByText(/datasetCreation\.stepTwo\.economical/i)).toBeInTheDocument()
- })
- })
-
- describe('Document Lookup Memoization', () => {
- it('should memoize document lookup based on documents array', async () => {
- const documents = [createMockDocument({ id: 'doc-1', name: 'test.txt' })]
- mockFetchIndexingStatusBatch.mockResolvedValue({
- data: [createMockIndexingStatus({ id: 'doc-1' })],
- })
-
- const { rerender } = render(
- ,
- )
-
- await act(async () => {
- await vi.runOnlyPendingTimersAsync()
- })
-
- // Rerender with same documents reference
- rerender()
-
- // Assert - component should render without issues
- expect(screen.getByText('test.txt')).toBeInTheDocument()
- })
- })
-
- describe('Edge Cases', () => {
- it('should handle empty documents array', async () => {
- mockFetchIndexingStatusBatch.mockResolvedValue({ data: [] })
-
- render()
-
- await act(async () => {
- await vi.runOnlyPendingTimersAsync()
- })
-
- // Assert - should render without crashing
- expect(document.body).toBeInTheDocument()
- })
-
- it('should handle undefined documents', async () => {
- mockFetchIndexingStatusBatch.mockResolvedValue({ data: [] })
-
- render()
-
- await act(async () => {
- await vi.runOnlyPendingTimersAsync()
- })
-
- // Assert - should render without crashing
- expect(document.body).toBeInTheDocument()
- })
-
- it('should handle status with missing document', async () => {
- const documents = [createMockDocument({ id: 'doc-1', name: 'test.txt' })]
- mockFetchIndexingStatusBatch.mockResolvedValue({
- data: [
- createMockIndexingStatus({ id: 'doc-1' }),
- createMockIndexingStatus({ id: 'doc-unknown' }), // No matching document
- ],
- })
-
- render()
-
- await act(async () => {
- await vi.runOnlyPendingTimersAsync()
- })
-
- // Assert - should render known document and handle unknown gracefully
- expect(screen.getByText('test.txt')).toBeInTheDocument()
- })
-
- it('should handle undefined retrievalMethod', async () => {
- mockFetchIndexingStatusBatch.mockResolvedValue({ data: [] })
-
- render()
-
- await act(async () => {
- await vi.runOnlyPendingTimersAsync()
- })
-
- // Assert - should use default semantic search
- expect(screen.getByText(/dataset\.retrieval\.semantic_search\.title/i)).toBeInTheDocument()
- })
+ expect(screen.getByText('upgrade processing priority')).toBeInTheDocument()
})
})
diff --git a/web/app/components/datasets/create/embedding-process/__tests__/indexing-progress-item.spec.tsx b/web/app/components/datasets/create/embedding-process/__tests__/indexing-progress-item.spec.tsx
index 4ad973c46be..c893046bd04 100644
--- a/web/app/components/datasets/create/embedding-process/__tests__/indexing-progress-item.spec.tsx
+++ b/web/app/components/datasets/create/embedding-process/__tests__/indexing-progress-item.spec.tsx
@@ -107,13 +107,4 @@ describe('IndexingProgressItem', () => {
expect(screen.queryByTestId('priority-label')).not.toBeInTheDocument()
})
-
- it('should apply error styling for error status', () => {
- const { container } = render(
- ,
- )
-
- const wrapper = container.firstChild as HTMLElement
- expect(wrapper.className).toContain('bg-state-destructive-hover-alt')
- })
})
diff --git a/web/app/components/datasets/create/embedding-process/__tests__/upgrade-banner.spec.tsx b/web/app/components/datasets/create/embedding-process/__tests__/upgrade-banner.spec.tsx
deleted file mode 100644
index ca4db9c9822..00000000000
--- a/web/app/components/datasets/create/embedding-process/__tests__/upgrade-banner.spec.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import { render, screen } from '@testing-library/react'
-import { beforeEach, describe, expect, it, vi } from 'vitest'
-import UpgradeBanner from '../upgrade-banner'
-
-vi.mock('@/app/components/base/icons/src/vender/solid/general', () => ({
- ZapFast: (props: React.SVGProps) => ,
-}))
-vi.mock('@/app/components/billing/upgrade-btn', () => ({
- default: ({ loc }: { loc: string }) => (
-
- ),
-}))
-
-describe('UpgradeBanner', () => {
- beforeEach(() => {
- vi.clearAllMocks()
- })
-
- it('should render the banner with icon, text, and upgrade button', () => {
- render()
-
- expect(screen.getByTestId('zap-icon')).toBeInTheDocument()
- expect(
- screen.getByText('billing.plansCommon.documentProcessingPriorityUpgrade'),
- ).toBeInTheDocument()
- expect(screen.getByTestId('upgrade-btn')).toBeInTheDocument()
- })
-
- it('should pass correct loc to UpgradeBtn', () => {
- render()
- expect(screen.getByTestId('upgrade-btn')).toHaveAttribute('data-loc', 'knowledge-speed-up')
- })
-})
diff --git a/web/app/components/datasets/create/empty-dataset-creation-modal/__tests__/index.spec.tsx b/web/app/components/datasets/create/empty-dataset-creation-modal/__tests__/index.spec.tsx
index 19dca097582..291ba7c32d5 100644
--- a/web/app/components/datasets/create/empty-dataset-creation-modal/__tests__/index.spec.tsx
+++ b/web/app/components/datasets/create/empty-dataset-creation-modal/__tests__/index.spec.tsx
@@ -66,15 +66,6 @@ describe('EmptyDatasetCreationModal', () => {
// Rendering Tests - Verify component renders correctly
describe('Rendering', () => {
- it('should render without crashing when show is true', () => {
- const props = createDefaultProps()
-
- render()
-
- // Assert - Check modal title is rendered
- expect(screen.getByText('datasetCreation.stepOne.modal.title')).toBeInTheDocument()
- })
-
it('should render modal with correct elements', () => {
const props = createDefaultProps()
diff --git a/web/app/components/datasets/create/file-preview/__tests__/index.spec.tsx b/web/app/components/datasets/create/file-preview/__tests__/index.spec.tsx
index 92701430c3e..d649ec56034 100644
--- a/web/app/components/datasets/create/file-preview/__tests__/index.spec.tsx
+++ b/web/app/components/datasets/create/file-preview/__tests__/index.spec.tsx
@@ -58,14 +58,6 @@ describe('FilePreview', () => {
// Rendering Tests - Verify component renders properly
describe('Rendering', () => {
- it('should render without crashing', async () => {
- renderFilePreview()
-
- await waitFor(() => {
- expect(screen.getByText('datasetCreation.stepOne.filePreview'))!.toBeInTheDocument()
- })
- })
-
it('should render file preview header', async () => {
renderFilePreview()
@@ -98,13 +90,6 @@ describe('FilePreview', () => {
expect(screen.getByText('.pdf'))!.toBeInTheDocument()
})
-
- it('should apply correct CSS classes to container', async () => {
- const { container } = renderFilePreview()
-
- const wrapper = container.firstChild as HTMLElement
- expect(wrapper)!.toHaveClass('h-full')
- })
})
// Loading State Tests
@@ -245,18 +230,14 @@ describe('FilePreview', () => {
})
})
- it('should handle API error gracefully', async () => {
+ it('should keep the preview header visible when loading fails', async () => {
mockFetchFilePreview.mockRejectedValue(new Error('Network error'))
- const { container } = renderFilePreview()
+ renderFilePreview()
- // Assert - Component should not crash, loading may persist
await waitFor(() => {
- expect(container.firstChild)!.toBeInTheDocument()
+ expect(screen.getByText('datasetCreation.stepOne.filePreview'))!.toBeInTheDocument()
})
- // No error thrown, component still rendered
- // No error thrown, component still rendered
- expect(screen.getByText('datasetCreation.stepOne.filePreview'))!.toBeInTheDocument()
})
it('should handle empty content response', async () => {
@@ -434,16 +415,6 @@ describe('FilePreview', () => {
const fileNameSpan = fileNameElement?.querySelector('span:first-child')
expect(fileNameSpan?.textContent).toBe('')
})
-
- it('should handle file with empty name', async () => {
- const file = createMockFile({ name: '' })
-
- const { container } = renderFilePreview({ file })
-
- // Assert - Should not crash
- // Assert - Should not crash
- expect(container.firstChild)!.toBeInTheDocument()
- })
})
describe('hidePreview prop', () => {
@@ -463,11 +434,9 @@ describe('FilePreview', () => {
it('should handle file with undefined id', async () => {
const file = createMockFile({ id: undefined })
- const { container } = renderFilePreview({ file })
+ renderFilePreview({ file })
- // Assert - Should not call API, remain in loading state
expect(mockFetchFilePreview).not.toHaveBeenCalled()
- expect(container.firstChild)!.toBeInTheDocument()
})
it('should handle file with empty string id', async () => {
@@ -548,17 +517,6 @@ describe('FilePreview', () => {
expect(contentDiv?.textContent).toContain('Line 3')
})
})
-
- it('should handle null content from API', async () => {
- mockFetchFilePreview.mockResolvedValue({ content: null as unknown as string })
-
- const { container } = renderFilePreview()
-
- // Assert - Should not crash
- await waitFor(() => {
- expect(container.firstChild)!.toBeInTheDocument()
- })
- })
})
// Side Effects and Cleanup Tests
@@ -615,24 +573,10 @@ describe('FilePreview', () => {
})
})
- it('should handle unmount during loading', async () => {
- mockFetchFilePreview.mockImplementation(
- () => new Promise((resolve) => setTimeout(() => resolve({ content: 'delayed' }), 1000)),
- )
-
- const { unmount } = renderFilePreview()
-
- // Unmount before API resolves
- unmount()
-
- // Assert - No errors should be thrown (React handles state updates on unmounted)
- expect(true).toBe(true)
- })
-
it('should handle file changing from defined to undefined', async () => {
const file = createMockFile()
- const { rerender, container } = render()
+ const { rerender } = render()
await waitFor(() => {
expect(mockFetchFilePreview).toHaveBeenCalledTimes(1)
@@ -640,9 +584,6 @@ describe('FilePreview', () => {
rerender()
- // Assert - Should not crash, API should not be called again
- // Assert - Should not crash, API should not be called again
- expect(container.firstChild)!.toBeInTheDocument()
expect(mockFetchFilePreview).toHaveBeenCalledTimes(1)
})
})
@@ -686,54 +627,4 @@ describe('FilePreview', () => {
expect(fileNameElement)!.toBeInTheDocument()
})
})
-
- describe('Accessibility', () => {
- it('should have clickable close button with visual indicator', async () => {
- const { container } = renderFilePreview()
-
- const closeButton = container.querySelector('.cursor-pointer')
- expect(closeButton)!.toBeInTheDocument()
- expect(closeButton)!.toHaveClass('cursor-pointer')
- })
-
- it('should have proper heading structure', async () => {
- renderFilePreview()
-
- expect(screen.getByText('datasetCreation.stepOne.filePreview'))!.toBeInTheDocument()
- })
- })
-
- // Error Handling Tests
- describe('Error Handling', () => {
- it('should not crash on API network error', async () => {
- mockFetchFilePreview.mockRejectedValue(new Error('Network Error'))
-
- const { container } = renderFilePreview()
-
- // Assert - Component should still render
- await waitFor(() => {
- expect(container.firstChild)!.toBeInTheDocument()
- })
- })
-
- it('should not crash on API timeout', async () => {
- mockFetchFilePreview.mockRejectedValue(new Error('Timeout'))
-
- const { container } = renderFilePreview()
-
- await waitFor(() => {
- expect(container.firstChild)!.toBeInTheDocument()
- })
- })
-
- it('should not crash on malformed API response', async () => {
- mockFetchFilePreview.mockResolvedValue({} as { content: string })
-
- const { container } = renderFilePreview()
-
- await waitFor(() => {
- expect(container.firstChild)!.toBeInTheDocument()
- })
- })
- })
})
diff --git a/web/app/components/datasets/create/file-uploader/__tests__/constants.spec.ts b/web/app/components/datasets/create/file-uploader/__tests__/constants.spec.ts
deleted file mode 100644
index 4582abc9ab7..00000000000
--- a/web/app/components/datasets/create/file-uploader/__tests__/constants.spec.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { describe, expect, it } from 'vitest'
-import { PROGRESS_COMPLETE, PROGRESS_ERROR, PROGRESS_NOT_STARTED } from '../constants'
-
-describe('file-uploader constants', () => {
- // Verify progress sentinel values
- describe('Progress Sentinels', () => {
- it('should define PROGRESS_NOT_STARTED as -1', () => {
- expect(PROGRESS_NOT_STARTED).toBe(-1)
- })
-
- it('should define PROGRESS_ERROR as -2', () => {
- expect(PROGRESS_ERROR).toBe(-2)
- })
-
- it('should define PROGRESS_COMPLETE as 100', () => {
- expect(PROGRESS_COMPLETE).toBe(100)
- })
-
- it('should have distinct values for all sentinels', () => {
- const values = [PROGRESS_NOT_STARTED, PROGRESS_ERROR, PROGRESS_COMPLETE]
- expect(new Set(values).size).toBe(values.length)
- })
-
- it('should have negative values for non-progress states', () => {
- expect(PROGRESS_NOT_STARTED).toBeLessThan(0)
- expect(PROGRESS_ERROR).toBeLessThan(0)
- })
- })
-})
diff --git a/web/app/components/datasets/create/file-uploader/__tests__/index.spec.tsx b/web/app/components/datasets/create/file-uploader/__tests__/index.spec.tsx
index 1921e7fd52a..2a7f7ef4d5e 100644
--- a/web/app/components/datasets/create/file-uploader/__tests__/index.spec.tsx
+++ b/web/app/components/datasets/create/file-uploader/__tests__/index.spec.tsx
@@ -109,12 +109,6 @@ describe('FileUploader', () => {
render()
expect(screen.getByText('datasetCreation.stepOne.uploader.browse')).toBeInTheDocument()
})
-
- it('should apply custom title className', () => {
- render()
- const title = screen.getByText('datasetCreation.stepOne.uploader.title')
- expect(title).toHaveClass('custom-class')
- })
})
describe('file list rendering', () => {
diff --git a/web/app/components/datasets/create/file-uploader/components/__tests__/file-list-item.spec.tsx b/web/app/components/datasets/create/file-uploader/components/__tests__/file-list-item.spec.tsx
index f96e254e0ef..267d68e39e0 100644
--- a/web/app/components/datasets/create/file-uploader/components/__tests__/file-list-item.spec.tsx
+++ b/web/app/components/datasets/create/file-uploader/components/__tests__/file-list-item.spec.tsx
@@ -104,7 +104,6 @@ describe('FileListItem', () => {
render()
const extensionSpan = screen.getByText('pdf')
expect(extensionSpan).toBeInTheDocument()
- expect(extensionSpan).toHaveClass('uppercase')
})
it('should render file size', () => {
@@ -328,18 +327,6 @@ describe('FileListItem', () => {
})
describe('styling', () => {
- it('should have proper shadow styling', () => {
- const { container } = render()
- const item = container.firstChild as HTMLElement
- expect(item).toHaveClass('shadow-xs')
- })
-
- it('should have proper border styling', () => {
- const { container } = render()
- const item = container.firstChild as HTMLElement
- expect(item).toHaveClass('border', 'border-components-panel-border')
- })
-
it('should truncate long file names', () => {
const longFileName = 'this-is-a-very-long-file-name-that-should-be-truncated.pdf'
const fileItem = createMockFileItem({
diff --git a/web/app/components/datasets/create/file-uploader/components/__tests__/upload-dropzone.spec.tsx b/web/app/components/datasets/create/file-uploader/components/__tests__/upload-dropzone.spec.tsx
index cb64248e531..df7b0705bf0 100644
--- a/web/app/components/datasets/create/file-uploader/components/__tests__/upload-dropzone.spec.tsx
+++ b/web/app/components/datasets/create/file-uploader/components/__tests__/upload-dropzone.spec.tsx
@@ -252,13 +252,6 @@ describe('UploadDropzone', () => {
})
describe('styling', () => {
- it('should have base dropzone styling', () => {
- const { container } = render()
- const dropzone = container.querySelector('[class*="border-dashed"]')
- expect(dropzone).toBeInTheDocument()
- expect(dropzone).toHaveClass('rounded-xl')
- })
-
it('should have cursor-pointer on browse label', () => {
render()
const browseLabel = screen.getByText('datasetCreation.stepOne.uploader.browse')
diff --git a/web/app/components/datasets/create/notion-page-preview/__tests__/index.spec.tsx b/web/app/components/datasets/create/notion-page-preview/__tests__/index.spec.tsx
index 5abd804415b..852d76ec30e 100644
--- a/web/app/components/datasets/create/notion-page-preview/__tests__/index.spec.tsx
+++ b/web/app/components/datasets/create/notion-page-preview/__tests__/index.spec.tsx
@@ -113,12 +113,6 @@ describe('NotionPagePreview', () => {
// Rendering Tests - Verify component renders properly
describe('Rendering', () => {
- it('should render without crashing', async () => {
- await renderNotionPagePreview()
-
- expect(screen.getByText('datasetCreation.stepOne.pagePreview')).toBeInTheDocument()
- })
-
it('should render page preview header', async () => {
await renderNotionPagePreview()
@@ -142,13 +136,6 @@ describe('NotionPagePreview', () => {
expect(screen.getByText('My Notion Page')).toBeInTheDocument()
})
- it('should apply correct CSS classes to container', async () => {
- const { container } = await renderNotionPagePreview()
-
- const wrapper = container.firstChild as HTMLElement
- expect(wrapper).toHaveClass('h-full')
- })
-
it('should render NotionIcon component', async () => {
const page = createMockNotionPage()
@@ -361,17 +348,14 @@ describe('NotionPagePreview', () => {
expect(screen.getByText('Notion page preview content from API')).toBeInTheDocument()
})
- it('should handle API error gracefully', async () => {
+ it('should keep the preview header visible when loading fails', async () => {
mockFetchNotionPagePreview.mockRejectedValue(new Error('Network error'))
- const { container } = await renderNotionPagePreview({}, false)
+ await renderNotionPagePreview({}, false)
- // Assert - Component should not crash
await waitFor(() => {
- expect(container.firstChild).toBeInTheDocument()
+ expect(screen.getByText('datasetCreation.stepOne.pagePreview')).toBeInTheDocument()
})
- // Header should still render
- expect(screen.getByText('datasetCreation.stepOne.pagePreview')).toBeInTheDocument()
})
it('should handle empty content response', async () => {
@@ -543,15 +527,6 @@ describe('NotionPagePreview', () => {
expect(screen.getByText('datasetCreation.stepOne.pagePreview')).toBeInTheDocument()
})
- it('should handle page with empty name', async () => {
- const page = createMockNotionPage({ page_name: '' })
-
- const { container } = await renderNotionPagePreview({ currentPage: page })
-
- // Assert - Should not crash
- expect(container.firstChild).toBeInTheDocument()
- })
-
it('should handle page with very long name', async () => {
const longName = 'a'.repeat(200)
const page = createMockNotionPage({ page_name: longName })
@@ -668,15 +643,6 @@ describe('NotionPagePreview', () => {
expect(contentDiv?.textContent).toContain('Line 3')
})
- it('should handle null content from API', async () => {
- mockFetchNotionPagePreview.mockResolvedValue({ content: null as unknown as string })
-
- const { container } = await renderNotionPagePreview()
-
- // Assert - Should not crash
- expect(container.firstChild).toBeInTheDocument()
- })
-
it('should handle different page types', async () => {
const databasePage = createMockNotionPage({ type: 'database' })
@@ -810,25 +776,10 @@ describe('NotionPagePreview', () => {
})
})
- it('should handle unmount during loading', async () => {
- mockFetchNotionPagePreview.mockImplementation(
- () => new Promise((resolve) => setTimeout(() => resolve({ content: 'delayed' }), 1000)),
- )
-
- // Act - Don't wait for content
- const { unmount } = await renderNotionPagePreview({}, false)
-
- // Unmount before API resolves
- unmount()
-
- // Assert - No errors should be thrown
- expect(true).toBe(true)
- })
-
it('should handle page changing from defined to undefined', async () => {
const page = createMockNotionPage()
- const { rerender, container } = render(
+ const { rerender } = render(
{
)
})
- // Assert - Should not crash, API should not be called again
- expect(container.firstChild).toBeInTheDocument()
expect(mockFetchNotionPagePreview).toHaveBeenCalledTimes(1)
})
})
- describe('Accessibility', () => {
- it('should have clickable close button with visual indicator', async () => {
- const { container } = await renderNotionPagePreview()
-
- const closeButton = container.querySelector('.cursor-pointer')
- expect(closeButton).toBeInTheDocument()
- expect(closeButton).toHaveClass('cursor-pointer')
- })
-
- it('should have proper heading structure', async () => {
- await renderNotionPagePreview()
-
- expect(screen.getByText('datasetCreation.stepOne.pagePreview')).toBeInTheDocument()
- })
- })
-
- // Error Handling Tests
- describe('Error Handling', () => {
- it('should not crash on API network error', async () => {
- mockFetchNotionPagePreview.mockRejectedValue(new Error('Network Error'))
-
- const { container } = await renderNotionPagePreview({}, false)
-
- // Assert - Component should still render
- await waitFor(() => {
- expect(container.firstChild).toBeInTheDocument()
- })
- })
-
- it('should not crash on API timeout', async () => {
- mockFetchNotionPagePreview.mockRejectedValue(new Error('Timeout'))
-
- const { container } = await renderNotionPagePreview({}, false)
-
- await waitFor(() => {
- expect(container.firstChild).toBeInTheDocument()
- })
- })
-
- it('should not crash on malformed API response', async () => {
- mockFetchNotionPagePreview.mockResolvedValue({} as { content: string })
-
- const { container } = await renderNotionPagePreview()
-
- expect(container.firstChild).toBeInTheDocument()
- })
-
- it('should handle 404 error gracefully', async () => {
- mockFetchNotionPagePreview.mockRejectedValue(new Error('404 Not Found'))
-
- const { container } = await renderNotionPagePreview({}, false)
-
- await waitFor(() => {
- expect(container.firstChild).toBeInTheDocument()
- })
- })
-
- it('should handle 500 error gracefully', async () => {
- mockFetchNotionPagePreview.mockRejectedValue(new Error('500 Internal Server Error'))
-
- const { container } = await renderNotionPagePreview({}, false)
-
- await waitFor(() => {
- expect(container.firstChild).toBeInTheDocument()
- })
- })
-
- it('should handle authorization error gracefully', async () => {
- mockFetchNotionPagePreview.mockRejectedValue(new Error('401 Unauthorized'))
-
- const { container } = await renderNotionPagePreview({}, false)
-
- await waitFor(() => {
- expect(container.firstChild).toBeInTheDocument()
- })
- })
- })
-
// Page Type Variations Tests
describe('Page Type Variations', () => {
it('should handle page type', async () => {
@@ -996,59 +867,10 @@ describe('NotionPagePreview', () => {
expect(img).toBeInTheDocument()
expect(img).toHaveAttribute('src', 'https://example.com/custom-icon.png')
})
-
- it('should handle page with icon object having null values', async () => {
- const page = createMockNotionPage({
- page_icon: {
- type: null,
- url: null,
- emoji: null,
- },
- })
-
- const { container } = await renderNotionPagePreview({ currentPage: page })
-
- // Assert - Should render, likely with default/fallback
- expect(container.firstChild).toBeInTheDocument()
- })
-
- it('should handle page with icon object having empty url', async () => {
- // Suppress console.error for this test as we're intentionally testing empty src edge case
- const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(vi.fn())
-
- const page = createMockNotionPage({
- page_icon: {
- type: 'url',
- url: '',
- emoji: null,
- },
- })
-
- const { container } = await renderNotionPagePreview({ currentPage: page })
-
- // Assert - Component should not crash, may render img or fallback
- expect(container.firstChild).toBeInTheDocument()
- // NotionIcon renders img when type is 'url'
- const img = container.querySelector('img[alt="page icon"]')
- if (img) expect(img).toBeInTheDocument()
-
- // Restore console.error
- consoleErrorSpy.mockRestore()
- })
})
// Content Display Tests
describe('Content Display', () => {
- it('should display content in fileContent div with correct class', async () => {
- mockFetchNotionPagePreview.mockResolvedValue({ content: 'Test content' })
-
- const { container } = await renderNotionPagePreview()
-
- const contentDiv = container.querySelector('[class*="fileContent"]')
- expect(contentDiv).toBeInTheDocument()
- expect(contentDiv).toHaveTextContent('Test content')
- })
-
it('should preserve whitespace in content', async () => {
const contentWithWhitespace = ' indented content\n more indent'
mockFetchNotionPagePreview.mockResolvedValue({ content: contentWithWhitespace })
diff --git a/web/app/components/datasets/create/step-one/__tests__/index.spec.tsx b/web/app/components/datasets/create/step-one/__tests__/index.spec.tsx
index ad9c8ba46b1..99aa4a875c6 100644
--- a/web/app/components/datasets/create/step-one/__tests__/index.spec.tsx
+++ b/web/app/components/datasets/create/step-one/__tests__/index.spec.tsx
@@ -234,12 +234,6 @@ describe('StepOne', () => {
})
describe('Rendering', () => {
- it('should render without crashing', () => {
- render()
-
- expect(screen.getByText('datasetCreation.steps.one')).toBeInTheDocument()
- })
-
it('should render DataSourceTypeSelector when not editing existing dataset', () => {
render()
diff --git a/web/app/components/datasets/create/step-one/__tests__/upgrade-card.spec.tsx b/web/app/components/datasets/create/step-one/__tests__/upgrade-card.spec.tsx
index 6e852dd61db..1bc8c27c9c8 100644
--- a/web/app/components/datasets/create/step-one/__tests__/upgrade-card.spec.tsx
+++ b/web/app/components/datasets/create/step-one/__tests__/upgrade-card.spec.tsx
@@ -1,97 +1,33 @@
-import { fireEvent, render, screen } from '@testing-library/react'
-import { beforeEach, describe, expect, it, vi } from 'vitest'
+import { render, screen } from '@testing-library/react'
+import userEvent from '@testing-library/user-event'
import UpgradeCard from '../upgrade-card'
const mockSetShowPricingModal = vi.fn()
-vi.mock('@/config', async (importOriginal) => {
- const actual = await importOriginal()
- return {
- ...actual,
- IS_CLOUD_EDITION: true,
- }
-})
+vi.mock('@/config', async (importOriginal) => ({
+ ...(await importOriginal()),
+ IS_CLOUD_EDITION: true,
+}))
vi.mock('@/context/modal-context', () => ({
- useModalContext: () => ({
- setShowPricingModal: mockSetShowPricingModal,
- }),
+ useModalContext: () => ({ setShowPricingModal: mockSetShowPricingModal }),
}))
vi.mock('@/app/components/billing/upgrade-btn', () => ({
- default: ({ onClick, className }: { onClick?: () => void; className?: string }) => (
-