import type { AppContextValue } from '@/context/app-context' import type { CommonResponse } from '@/models/common' import { fireEvent, render, screen, waitFor } from '@testing-library/react' import { useAppContext } from '@/context/app-context' import { DataSourceProvider } from '@/models/common' import { fetchDataSources, removeDataSourceApiKeyBinding } from '@/service/datasets' import DataSourceWebsite from './index' /** * DataSourceWebsite Component Tests * Tests integration of multiple website scraping providers (Firecrawl, WaterCrawl, Jina Reader). */ type DataSourcesResponse = CommonResponse & { sources: Array<{ id: string, provider: DataSourceProvider }> } // Mock App Context vi.mock('@/context/app-context', () => ({ useAppContext: vi.fn(), })) // Mock Service calls vi.mock('@/service/datasets', () => ({ fetchDataSources: vi.fn(), removeDataSourceApiKeyBinding: vi.fn(), createDataSourceApiKeyBinding: vi.fn(), })) describe('DataSourceWebsite Component', () => { const mockSources = [ { id: '1', provider: DataSourceProvider.fireCrawl }, { id: '2', provider: DataSourceProvider.waterCrawl }, { id: '3', provider: DataSourceProvider.jinaReader }, ] beforeEach(() => { vi.clearAllMocks() vi.mocked(useAppContext).mockReturnValue({ isCurrentWorkspaceManager: true } as unknown as AppContextValue) vi.mocked(fetchDataSources).mockResolvedValue({ result: 'success', sources: [] } as DataSourcesResponse) }) // Helper to render and wait for initial fetch to complete const renderAndWait = async (provider: DataSourceProvider) => { const result = render() await waitFor(() => expect(fetchDataSources).toHaveBeenCalled()) return result } describe('Data Initialization', () => { it('should fetch data sources on mount and reflect configured status', async () => { // Arrange vi.mocked(fetchDataSources).mockResolvedValue({ result: 'success', sources: mockSources } as DataSourcesResponse) // Act await renderAndWait(DataSourceProvider.fireCrawl) // Assert expect(screen.getByText('common.dataSource.website.configuredCrawlers')).toBeInTheDocument() }) it('should pass readOnly status based on workspace manager permissions', async () => { // Arrange vi.mocked(useAppContext).mockReturnValue({ isCurrentWorkspaceManager: false } as unknown as AppContextValue) // Act await renderAndWait(DataSourceProvider.fireCrawl) // Assert expect(screen.getByText('common.dataSource.configure')).toHaveClass('cursor-default') }) }) describe('Provider Specific Rendering', () => { it('should render correct logo and name for Firecrawl', async () => { // Arrange vi.mocked(fetchDataSources).mockResolvedValue({ result: 'success', sources: [mockSources[0]] } as DataSourcesResponse) // Act await renderAndWait(DataSourceProvider.fireCrawl) // Assert expect(await screen.findByText('Firecrawl')).toBeInTheDocument() expect(screen.getByText('🔥')).toBeInTheDocument() }) it('should render correct logo and name for WaterCrawl', async () => { // Arrange vi.mocked(fetchDataSources).mockResolvedValue({ result: 'success', sources: [mockSources[1]] } as DataSourcesResponse) // Act await renderAndWait(DataSourceProvider.waterCrawl) // Assert const elements = await screen.findAllByText('WaterCrawl') expect(elements.length).toBeGreaterThanOrEqual(1) }) it('should render correct logo and name for Jina Reader', async () => { // Arrange vi.mocked(fetchDataSources).mockResolvedValue({ result: 'success', sources: [mockSources[2]] } as DataSourcesResponse) // Act await renderAndWait(DataSourceProvider.jinaReader) // Assert const elements = await screen.findAllByText('Jina Reader') expect(elements.length).toBeGreaterThanOrEqual(1) }) }) describe('Modal Interactions', () => { it('should manage opening and closing of configuration modals', async () => { // Arrange await renderAndWait(DataSourceProvider.fireCrawl) // Act (Open) fireEvent.click(screen.getByText('common.dataSource.configure')) // Assert expect(screen.getByText('datasetCreation.firecrawl.configFirecrawl')).toBeInTheDocument() // Act (Cancel) fireEvent.click(screen.getByRole('button', { name: /common\.operation\.cancel/i })) // Assert expect(screen.queryByText('datasetCreation.firecrawl.configFirecrawl')).not.toBeInTheDocument() }) it('should re-fetch sources after saving configuration (Watercrawl)', async () => { // Arrange await renderAndWait(DataSourceProvider.waterCrawl) fireEvent.click(screen.getByText('common.dataSource.configure')) vi.mocked(fetchDataSources).mockClear() // Act fireEvent.change(screen.getByPlaceholderText('datasetCreation.watercrawl.apiKeyPlaceholder'), { target: { value: 'test-key' } }) fireEvent.click(screen.getByRole('button', { name: /common\.operation\.save/i })) // Assert await waitFor(() => { expect(fetchDataSources).toHaveBeenCalled() expect(screen.queryByText('datasetCreation.watercrawl.configWatercrawl')).not.toBeInTheDocument() }) }) it('should re-fetch sources after saving configuration (Jina Reader)', async () => { // Arrange await renderAndWait(DataSourceProvider.jinaReader) fireEvent.click(screen.getByText('common.dataSource.configure')) vi.mocked(fetchDataSources).mockClear() // Act fireEvent.change(screen.getByPlaceholderText('datasetCreation.jinaReader.apiKeyPlaceholder'), { target: { value: 'test-key' } }) fireEvent.click(screen.getByRole('button', { name: /common\.operation\.save/i })) // Assert await waitFor(() => { expect(fetchDataSources).toHaveBeenCalled() expect(screen.queryByText('datasetCreation.jinaReader.configJinaReader')).not.toBeInTheDocument() }) }) }) describe('Management Actions', () => { it('should handle successful data source removal with toast notification', async () => { // Arrange vi.mocked(fetchDataSources).mockResolvedValue({ result: 'success', sources: [mockSources[0]] } as DataSourcesResponse) vi.mocked(removeDataSourceApiKeyBinding).mockResolvedValue({ result: 'success' } as CommonResponse) await renderAndWait(DataSourceProvider.fireCrawl) await waitFor(() => expect(screen.getByText('common.dataSource.website.configuredCrawlers')).toBeInTheDocument()) // Act const removeBtn = screen.getByText('Firecrawl').parentElement?.querySelector('svg')?.parentElement if (removeBtn) fireEvent.click(removeBtn) // Assert await waitFor(() => { expect(removeDataSourceApiKeyBinding).toHaveBeenCalledWith('1') expect(screen.getByText('common.api.remove')).toBeInTheDocument() }) expect(screen.queryByText('common.dataSource.website.configuredCrawlers')).not.toBeInTheDocument() }) it('should skip removal API call if no data source ID is present', async () => { // Arrange await renderAndWait(DataSourceProvider.fireCrawl) // Act const removeBtn = screen.queryByText('Firecrawl')?.parentElement?.querySelector('svg')?.parentElement if (removeBtn) fireEvent.click(removeBtn) // Assert expect(removeDataSourceApiKeyBinding).not.toHaveBeenCalled() }) }) })