import type { CustomCollectionBackend } from '../types' import { fireEvent, render, screen, waitFor } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' import { AuthType } from '../types' import CustomCreateCard from './custom-create-card' // Mock workspace manager state let mockIsWorkspaceManager = true // Mock useAppContext vi.mock('@/context/app-context', () => ({ useAppContext: () => ({ isCurrentWorkspaceManager: mockIsWorkspaceManager, }), })) // Mock useLocale and useDocLink vi.mock('@/context/i18n', () => ({ useLocale: () => 'en-US', useDocLink: () => (path: string) => `https://docs.dify.ai/en/${path?.startsWith('/') ? path.slice(1) : path}`, })) // Mock getLanguage vi.mock('@/i18n-config/language', () => ({ getLanguage: () => 'en-US', })) // Mock createCustomCollection service const mockCreateCustomCollection = vi.fn() vi.mock('@/service/tools', () => ({ createCustomCollection: (data: CustomCollectionBackend) => mockCreateCustomCollection(data), })) // Track modal state let mockModalVisible = false // Mock EditCustomToolModal - complex component vi.mock('@/app/components/tools/edit-custom-collection-modal', () => ({ default: ({ payload, onHide, onAdd }: { payload: null onHide: () => void onAdd: (data: CustomCollectionBackend) => void }) => { mockModalVisible = true void onAdd // Keep reference to avoid lint warning about unused param return (
{payload === null ? 'null' : 'not-null'}
) }, })) // Mock Toast const mockToastNotify = vi.fn() vi.mock('@/app/components/base/toast', () => ({ default: { notify: (options: { type: string, message: string }) => mockToastNotify(options), }, })) describe('CustomCreateCard', () => { const mockOnRefreshData = vi.fn() beforeEach(() => { vi.clearAllMocks() mockIsWorkspaceManager = true mockModalVisible = false mockCreateCustomCollection.mockResolvedValue({}) }) // Tests for conditional rendering based on workspace manager status describe('Workspace Manager Conditional Rendering', () => { it('should render card when user is workspace manager', () => { mockIsWorkspaceManager = true render() // Card should be visible with create text expect(screen.getByText(/createCustomTool/i)).toBeInTheDocument() }) it('should not render anything when user is not workspace manager', () => { mockIsWorkspaceManager = false const { container } = render() // Container should be empty (firstChild is null when nothing renders) expect(container.firstChild).toBeNull() }) }) // Tests for card rendering and styling describe('Card Rendering', () => { it('should render without crashing', () => { render() expect(screen.getByText(/createCustomTool/i)).toBeInTheDocument() }) it('should render add icon', () => { render() // RiAddCircleFill icon should be present const iconContainer = document.querySelector('.h-10.w-10') expect(iconContainer).toBeInTheDocument() }) it('should have proper card styling', () => { render() const card = document.querySelector('.min-h-\\[135px\\]') expect(card).toBeInTheDocument() expect(card).toHaveClass('cursor-pointer') }) }) // Tests for modal interaction describe('Modal Interaction', () => { it('should open modal when card is clicked', () => { render() // Click on the card area (the group div) const cardClickArea = document.querySelector('.group.grow') fireEvent.click(cardClickArea!) expect(screen.getByTestId('edit-custom-collection-modal')).toBeInTheDocument() expect(mockModalVisible).toBe(true) }) it('should pass null payload to modal', () => { render() const cardClickArea = document.querySelector('.group.grow') fireEvent.click(cardClickArea!) expect(screen.getByTestId('modal-payload')).toHaveTextContent('null') }) it('should close modal when onHide is called', () => { render() // Open modal const cardClickArea = document.querySelector('.group.grow') fireEvent.click(cardClickArea!) expect(screen.getByTestId('edit-custom-collection-modal')).toBeInTheDocument() // Close modal fireEvent.click(screen.getByTestId('close-modal')) expect(screen.queryByTestId('edit-custom-collection-modal')).not.toBeInTheDocument() }) }) // Tests for custom collection creation describe('Custom Collection Creation', () => { it('should call createCustomCollection when form is submitted', async () => { render() // Open modal const cardClickArea = document.querySelector('.group.grow') fireEvent.click(cardClickArea!) // Submit form fireEvent.click(screen.getByTestId('submit-modal')) await waitFor(() => { expect(mockCreateCustomCollection).toHaveBeenCalledTimes(1) }) }) it('should show success toast after successful creation', async () => { render() // Open modal const cardClickArea = document.querySelector('.group.grow') fireEvent.click(cardClickArea!) // Submit form fireEvent.click(screen.getByTestId('submit-modal')) await waitFor(() => { expect(mockToastNotify).toHaveBeenCalledWith({ type: 'success', message: expect.any(String), }) }) }) it('should close modal after successful creation', async () => { render() // Open modal const cardClickArea = document.querySelector('.group.grow') fireEvent.click(cardClickArea!) expect(screen.getByTestId('edit-custom-collection-modal')).toBeInTheDocument() // Submit form fireEvent.click(screen.getByTestId('submit-modal')) await waitFor(() => { expect(screen.queryByTestId('edit-custom-collection-modal')).not.toBeInTheDocument() }) }) it('should call onRefreshData after successful creation', async () => { render() // Open modal const cardClickArea = document.querySelector('.group.grow') fireEvent.click(cardClickArea!) // Submit form fireEvent.click(screen.getByTestId('submit-modal')) await waitFor(() => { expect(mockOnRefreshData).toHaveBeenCalledTimes(1) }) }) it('should pass correct data to createCustomCollection', async () => { render() // Open modal const cardClickArea = document.querySelector('.group.grow') fireEvent.click(cardClickArea!) // Submit form fireEvent.click(screen.getByTestId('submit-modal')) await waitFor(() => { expect(mockCreateCustomCollection).toHaveBeenCalledWith( expect.objectContaining({ provider: 'test-provider', schema_type: 'json', }), ) }) }) }) // Tests for edge cases describe('Edge Cases', () => { it('should call createCustomCollection and handle successful response', async () => { mockCreateCustomCollection.mockResolvedValue({ success: true }) render() // Open modal const cardClickArea = document.querySelector('.group.grow') fireEvent.click(cardClickArea!) // Submit form fireEvent.click(screen.getByTestId('submit-modal')) // The API should be called await waitFor(() => { expect(mockCreateCustomCollection).toHaveBeenCalled() }) // And refresh should be triggered await waitFor(() => { expect(mockOnRefreshData).toHaveBeenCalled() }) }) it('should not call onRefreshData if modal is just closed without submitting', () => { render() // Open modal const cardClickArea = document.querySelector('.group.grow') fireEvent.click(cardClickArea!) // Close modal without submitting fireEvent.click(screen.getByTestId('close-modal')) expect(mockOnRefreshData).not.toHaveBeenCalled() }) it('should handle rapid open/close of modal', () => { render() const cardClickArea = document.querySelector('.group.grow') // Rapid open/close fireEvent.click(cardClickArea!) fireEvent.click(screen.getByTestId('close-modal')) fireEvent.click(cardClickArea!) expect(screen.getByTestId('edit-custom-collection-modal')).toBeInTheDocument() }) }) // Tests for hover styling describe('Hover Styling', () => { it('should have hover styles on card', () => { render() const card = document.querySelector('.transition-all.duration-200') expect(card).toBeInTheDocument() }) it('should have group hover styles on icon container', () => { render() const iconContainer = document.querySelector('.group-hover\\:border-state-accent-hover-alt') expect(iconContainer).toBeInTheDocument() }) }) })