import { act, fireEvent, render, screen, within } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' import { useAppContext } from '@/context/app-context' import { useParams, useRouter, useSelectedLayoutSegment, } from '@/next/navigation' import { useDatasetDetail, useDatasetList, } from '@/service/knowledge/use-dataset' import DatasetNav from '../index' vi.mock('@/next/navigation', () => ({ useParams: vi.fn(), useRouter: vi.fn(), useSelectedLayoutSegment: vi.fn(), })) vi.mock('@/service/knowledge/use-dataset', () => ({ useDatasetDetail: vi.fn(), useDatasetList: vi.fn(), })) vi.mock('@/context/app-context', () => ({ useAppContext: vi.fn(), })) vi.mock('@remixicon/react', () => ({ RiBook2Fill: () =>
, RiBook2Line: () =>
, RiArrowDownSLine: () =>
, RiArrowRightSLine: () =>
, RiAddLine: () =>
, })) vi.mock('@/app/components/base/loading', () => ({ default: () =>
, })) vi.mock('@/app/components/base/app-icon', () => ({ default: () =>
, })) vi.mock('@/app/components/app/type-selector', () => ({ AppTypeIcon: () =>
, })) vi.mock('@/app/components/base/icons/src/vender/line/arrows', () => ({ ArrowNarrowLeft: () =>
, })) vi.mock('@/app/components/base/icons/src/vender/line/files', () => ({ FileArrow01: () =>
, FilePlus01: () =>
, FilePlus02: () =>
, })) describe('DatasetNav', () => { const mockPush = vi.fn() const mockFetchNextPage = vi.fn() const mockDataset = { id: 'dataset-1', name: 'Test Dataset', runtime_mode: 'general', icon_info: { icon: 'book', icon_type: 'image', icon_background: '#fff', icon_url: '/url', }, provider: 'vendor', } const mockDatasetList = { pages: [ { data: [ mockDataset, { id: 'dataset-2', name: 'Pipeline Dataset', runtime_mode: 'rag_pipeline', is_published: false, icon_info: { icon: 'pipeline' }, provider: 'vendor', }, { id: 'dataset-3', name: 'External Dataset', runtime_mode: 'general', icon_info: { icon: 'external' }, provider: 'external', }, { id: 'dataset-4', name: 'Published Pipeline', runtime_mode: 'rag_pipeline', is_published: true, icon_info: { icon: 'pipeline' }, provider: 'vendor', }, ], }, ], } beforeEach(() => { vi.clearAllMocks() vi.mocked(useRouter).mockReturnValue({ push: mockPush, } as unknown as ReturnType) vi.mocked(useParams).mockReturnValue({ datasetId: 'dataset-1' }) vi.mocked(useSelectedLayoutSegment).mockReturnValue('datasets') vi.mocked(useDatasetDetail).mockReturnValue({ data: mockDataset, } as unknown as ReturnType) vi.mocked(useDatasetList).mockReturnValue({ data: mockDatasetList, fetchNextPage: mockFetchNextPage, hasNextPage: true, isFetchingNextPage: false, } as unknown as ReturnType) vi.mocked(useAppContext).mockReturnValue({ isCurrentWorkspaceEditor: true, } as unknown as ReturnType) }) describe('Rendering', () => { it('should render the navigation component', () => { render() expect(screen.getByText('common.menus.datasets')).toBeInTheDocument() }) it('should render without current dataset correctly', () => { vi.mocked(useDatasetDetail).mockReturnValue({ data: undefined, } as unknown as ReturnType) render() expect(screen.getByText('common.menus.datasets')).toBeInTheDocument() }) }) describe('Navigation Items logic', () => { it('should generate correct links for different dataset types', () => { render() const selector = screen.getByRole('button', { name: /Test Dataset/i }) fireEvent.click(selector) const menu = screen.getByRole('menu') expect(within(menu).getByText('Test Dataset')).toBeInTheDocument() expect(within(menu).getByText('Pipeline Dataset')).toBeInTheDocument() expect(within(menu).getByText('External Dataset')).toBeInTheDocument() }) it('should navigate to correct link when an item is clicked', () => { render() const selector = screen.getByRole('button', { name: /Test Dataset/i }) fireEvent.click(selector) const menu = screen.getByRole('menu') const pipelineItem = within(menu).getByText('Pipeline Dataset') fireEvent.click(pipelineItem) // dataset-2 is rag_pipeline and not published -> /datasets/dataset-2/pipeline expect(mockPush).toHaveBeenCalledWith('/datasets/dataset-2/pipeline') fireEvent.click(selector) const menu2 = screen.getByRole('menu') const externalItem = within(menu2).getByText('External Dataset') fireEvent.click(externalItem) // dataset-3 is provider external -> /datasets/dataset-3/hitTesting expect(mockPush).toHaveBeenCalledWith('/datasets/dataset-3/hitTesting') fireEvent.click(selector) const menu3 = screen.getByRole('menu') const publishedItem = within(menu3).getByText('Published Pipeline') fireEvent.click(publishedItem) // dataset-4 is rag_pipeline and published -> /datasets/dataset-4/documents expect(mockPush).toHaveBeenCalledWith('/datasets/dataset-4/documents') }) }) describe('User Interactions', () => { it('should call router.push with correct path when creating a general dataset', () => { render() const selector = screen.getByRole('button', { name: /Test Dataset/i }) fireEvent.click(selector) const menu = screen.getByRole('menu') const createBtn = within(menu).getByText('common.menus.newDataset') fireEvent.click(createBtn) expect(mockPush).toHaveBeenCalledWith('/datasets/create') }) it('should call router.push with correct path when creating a pipeline dataset', () => { vi.mocked(useDatasetDetail).mockReturnValue({ data: { ...mockDataset, runtime_mode: 'rag_pipeline' }, } as unknown as ReturnType) render() const selector = screen.getByRole('button', { name: /Test Dataset/i }) fireEvent.click(selector) const menu = screen.getByRole('menu') const createBtn = within(menu).getByText('common.menus.newDataset') fireEvent.click(createBtn) expect(mockPush).toHaveBeenCalledWith('/datasets/create-from-pipeline') }) it('should trigger fetchNextPage when loading more', () => { vi.useFakeTimers() render() const selector = screen.getByRole('button', { name: /Test Dataset/i }) fireEvent.click(selector) const menu = screen.getByRole('menu') const scrollContainer = menu.querySelector('.overflow-auto') if (scrollContainer) { Object.defineProperty(scrollContainer, 'scrollHeight', { value: 1000 }) Object.defineProperty(scrollContainer, 'clientHeight', { value: 500 }) Object.defineProperty(scrollContainer, 'scrollTop', { value: 500 }) fireEvent.scroll(scrollContainer) act(() => { vi.advanceTimersByTime(100) }) expect(mockFetchNextPage).toHaveBeenCalled() } vi.useRealTimers() }) it('should not trigger fetchNextPage if hasNextPage is false', () => { vi.useFakeTimers() vi.mocked(useDatasetList).mockReturnValue({ data: mockDatasetList, fetchNextPage: mockFetchNextPage, hasNextPage: false, isFetchingNextPage: false, } as unknown as ReturnType) render() const selector = screen.getByRole('button', { name: /Test Dataset/i }) fireEvent.click(selector) const menu = screen.getByRole('menu') const scrollContainer = menu.querySelector('.overflow-auto') if (scrollContainer) { Object.defineProperty(scrollContainer, 'scrollHeight', { value: 1000 }) Object.defineProperty(scrollContainer, 'clientHeight', { value: 500 }) Object.defineProperty(scrollContainer, 'scrollTop', { value: 500 }) fireEvent.scroll(scrollContainer) act(() => { vi.advanceTimersByTime(100) }) expect(mockFetchNextPage).not.toHaveBeenCalled() } vi.useRealTimers() }) }) })