import { fireEvent, render, screen, waitFor } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' import { ChunkingMode } from '@/models/datasets' import { IndexingType } from '../../create/step-two' import NewSegmentModal from './new-segment' // Mock next/navigation vi.mock('next/navigation', () => ({ useParams: () => ({ datasetId: 'test-dataset-id', documentId: 'test-document-id', }), })) // Mock ToastContext const mockNotify = vi.fn() vi.mock('use-context-selector', async (importOriginal) => { const actual = await importOriginal() as Record return { ...actual, useContext: () => ({ notify: mockNotify }), } }) // Mock dataset detail context let mockIndexingTechnique = IndexingType.QUALIFIED vi.mock('@/context/dataset-detail', () => ({ useDatasetDetailContextWithSelector: (selector: (state: { dataset: { indexing_technique: string } }) => unknown) => { return selector({ dataset: { indexing_technique: mockIndexingTechnique } }) }, })) // Mock segment list context let mockFullScreen = false const mockToggleFullScreen = vi.fn() vi.mock('./completed', () => ({ useSegmentListContext: (selector: (state: { fullScreen: boolean, toggleFullScreen: () => void }) => unknown) => { const state = { fullScreen: mockFullScreen, toggleFullScreen: mockToggleFullScreen, } return selector(state) }, })) // Mock useAddSegment const mockAddSegment = vi.fn() vi.mock('@/service/knowledge/use-segment', () => ({ useAddSegment: () => ({ mutateAsync: mockAddSegment, }), })) // Mock app store vi.mock('@/app/components/app/store', () => ({ useStore: () => ({ appSidebarExpand: 'expand' }), })) // Mock child components vi.mock('./completed/common/action-buttons', () => ({ default: ({ handleCancel, handleSave, loading, actionType }: { handleCancel: () => void, handleSave: () => void, loading: boolean, actionType: string }) => (
{actionType}
), })) vi.mock('./completed/common/add-another', () => ({ default: ({ isChecked, onCheck, className }: { isChecked: boolean, onCheck: () => void, className?: string }) => (
), })) vi.mock('./completed/common/chunk-content', () => ({ default: ({ docForm, question, answer, onQuestionChange, onAnswerChange, isEditMode }: { docForm: string, question: string, answer: string, onQuestionChange: (v: string) => void, onAnswerChange: (v: string) => void, isEditMode: boolean }) => (
onQuestionChange(e.target.value)} placeholder={docForm === ChunkingMode.qa ? 'Question' : 'Content'} /> {docForm === ChunkingMode.qa && ( onAnswerChange(e.target.value)} placeholder="Answer" /> )} {isEditMode ? 'editing' : 'viewing'}
), })) vi.mock('./completed/common/dot', () => ({ default: () => , })) vi.mock('./completed/common/keywords', () => ({ default: ({ keywords, onKeywordsChange, _isEditMode, _actionType }: { keywords: string[], onKeywordsChange: (v: string[]) => void, _isEditMode?: boolean, _actionType?: string }) => (
onKeywordsChange(e.target.value.split(',').filter(Boolean))} />
), })) vi.mock('./completed/common/segment-index-tag', () => ({ SegmentIndexTag: ({ label }: { label: string }) => {label}, })) vi.mock('@/app/components/datasets/common/image-uploader/image-uploader-in-chunk', () => ({ default: ({ onChange }: { value?: unknown[], onChange: (v: { uploadedId: string }[]) => void }) => (
), })) describe('NewSegmentModal', () => { beforeEach(() => { vi.clearAllMocks() mockFullScreen = false mockIndexingTechnique = IndexingType.QUALIFIED }) const defaultProps = { onCancel: vi.fn(), docForm: ChunkingMode.text, onSave: vi.fn(), viewNewlyAddedChunk: vi.fn(), } // Rendering tests describe('Rendering', () => { it('should render without crashing', () => { // Arrange & Act const { container } = render() // Assert expect(container.firstChild).toBeInTheDocument() }) it('should render title text', () => { // Arrange & Act render() // Assert expect(screen.getByText(/segment\.addChunk/i)).toBeInTheDocument() }) it('should render chunk content component', () => { // Arrange & Act render() // Assert expect(screen.getByTestId('chunk-content')).toBeInTheDocument() }) it('should render image uploader', () => { // Arrange & Act render() // Assert expect(screen.getByTestId('image-uploader')).toBeInTheDocument() }) it('should render segment index tag', () => { // Arrange & Act render() // Assert expect(screen.getByTestId('segment-index-tag')).toBeInTheDocument() }) it('should render dot separator', () => { // Arrange & Act render() // Assert expect(screen.getByTestId('dot')).toBeInTheDocument() }) }) // Keywords display describe('Keywords', () => { it('should show keywords component when indexing is ECONOMICAL', () => { // Arrange mockIndexingTechnique = IndexingType.ECONOMICAL // Act render() // Assert expect(screen.getByTestId('keywords')).toBeInTheDocument() }) it('should not show keywords when indexing is QUALIFIED', () => { // Arrange mockIndexingTechnique = IndexingType.QUALIFIED // Act render() // Assert expect(screen.queryByTestId('keywords')).not.toBeInTheDocument() }) }) // User Interactions describe('User Interactions', () => { it('should call onCancel when close button is clicked', () => { // Arrange const mockOnCancel = vi.fn() const { container } = render() // Act - find and click close button (RiCloseLine icon wrapper) const closeButtons = container.querySelectorAll('.cursor-pointer') // The close button is the second cursor-pointer element if (closeButtons.length > 1) fireEvent.click(closeButtons[1]) // Assert expect(mockOnCancel).toHaveBeenCalled() }) it('should update question when typing', () => { // Arrange render() const questionInput = screen.getByTestId('question-input') // Act fireEvent.change(questionInput, { target: { value: 'New question content' } }) // Assert expect(questionInput).toHaveValue('New question content') }) it('should update answer when docForm is QA and typing', () => { // Arrange render() const answerInput = screen.getByTestId('answer-input') // Act fireEvent.change(answerInput, { target: { value: 'New answer content' } }) // Assert expect(answerInput).toHaveValue('New answer content') }) it('should toggle add another checkbox', () => { // Arrange render() const checkbox = screen.getByTestId('add-another-checkbox') // Act fireEvent.click(checkbox) // Assert - checkbox state should toggle expect(checkbox).toBeInTheDocument() }) }) // Save validation describe('Save Validation', () => { it('should show error when content is empty for text mode', async () => { // Arrange render() // Act fireEvent.click(screen.getByTestId('save-btn')) // Assert await waitFor(() => { expect(mockNotify).toHaveBeenCalledWith( expect.objectContaining({ type: 'error', }), ) }) }) it('should show error when question is empty for QA mode', async () => { // Arrange render() // Act fireEvent.click(screen.getByTestId('save-btn')) // Assert await waitFor(() => { expect(mockNotify).toHaveBeenCalledWith( expect.objectContaining({ type: 'error', }), ) }) }) it('should show error when answer is empty for QA mode', async () => { // Arrange render() fireEvent.change(screen.getByTestId('question-input'), { target: { value: 'Question' } }) // Act fireEvent.click(screen.getByTestId('save-btn')) // Assert await waitFor(() => { expect(mockNotify).toHaveBeenCalledWith( expect.objectContaining({ type: 'error', }), ) }) }) }) // Successful save describe('Successful Save', () => { it('should call addSegment when valid content is provided for text mode', async () => { // Arrange mockAddSegment.mockImplementation((_params, options) => { options.onSuccess() options.onSettled() return Promise.resolve() }) render() fireEvent.change(screen.getByTestId('question-input'), { target: { value: 'Valid content' } }) // Act fireEvent.click(screen.getByTestId('save-btn')) // Assert await waitFor(() => { expect(mockAddSegment).toHaveBeenCalledWith( expect.objectContaining({ datasetId: 'test-dataset-id', documentId: 'test-document-id', body: expect.objectContaining({ content: 'Valid content', }), }), expect.any(Object), ) }) }) it('should show success notification after save', async () => { // Arrange mockAddSegment.mockImplementation((_params, options) => { options.onSuccess() options.onSettled() return Promise.resolve() }) render() fireEvent.change(screen.getByTestId('question-input'), { target: { value: 'Valid content' } }) // Act fireEvent.click(screen.getByTestId('save-btn')) // Assert await waitFor(() => { expect(mockNotify).toHaveBeenCalledWith( expect.objectContaining({ type: 'success', }), ) }) }) }) // Full screen mode describe('Full Screen Mode', () => { it('should apply full screen styling when fullScreen is true', () => { // Arrange mockFullScreen = true // Act const { container } = render() // Assert const header = container.querySelector('.border-divider-subtle') expect(header).toBeInTheDocument() }) it('should show action buttons in header when fullScreen', () => { // Arrange mockFullScreen = true // Act render() // Assert expect(screen.getByTestId('action-buttons')).toBeInTheDocument() }) it('should show add another in header when fullScreen', () => { // Arrange mockFullScreen = true // Act render() // Assert expect(screen.getByTestId('add-another')).toBeInTheDocument() }) it('should call toggleFullScreen when expand button is clicked', () => { // Arrange const { container } = render() // Act - click the expand button (first cursor-pointer) const expandButtons = container.querySelectorAll('.cursor-pointer') if (expandButtons.length > 0) fireEvent.click(expandButtons[0]) // Assert expect(mockToggleFullScreen).toHaveBeenCalled() }) }) // Props describe('Props', () => { it('should pass actionType add to ActionButtons', () => { // Arrange & Act render() // Assert expect(screen.getByTestId('action-type')).toHaveTextContent('add') }) it('should pass isEditMode true to ChunkContent', () => { // Arrange & Act render() // Assert expect(screen.getByTestId('edit-mode')).toHaveTextContent('editing') }) }) // Edge cases describe('Edge Cases', () => { it('should handle keyword changes for ECONOMICAL indexing', () => { // Arrange mockIndexingTechnique = IndexingType.ECONOMICAL render() // Act fireEvent.change(screen.getByTestId('keywords-input'), { target: { value: 'keyword1,keyword2' }, }) // Assert expect(screen.getByTestId('keywords-input')).toHaveValue('keyword1,keyword2') }) it('should handle image upload', () => { // Arrange render() // Act fireEvent.click(screen.getByTestId('upload-image-btn')) // Assert - image uploader should be rendered expect(screen.getByTestId('image-uploader')).toBeInTheDocument() }) it('should maintain structure when rerendered with different docForm', () => { // Arrange const { rerender } = render() // Act rerender() // Assert expect(screen.getByTestId('answer-input')).toBeInTheDocument() }) }) })