import { describe, it, expect, vi, beforeEach } from 'vitest'; import { fireEvent, waitFor } from '@testing-library/react'; import { render } from '../../test/utils'; import FileTree from './FileTree'; import type { FileNode } from '../../types/models'; import { ModalProvider } from '../../contexts/ModalContext'; import { ThemeProvider } from '../../contexts/ThemeContext'; import { WorkspaceDataProvider } from '../../contexts/WorkspaceDataContext'; // Mock react-arborist vi.mock('react-arborist', () => ({ Tree: ({ children, data, onActivate, }: { children: (props: { node: { data: FileNode; isLeaf: boolean; isInternal: boolean; isOpen: boolean; level: number; toggle: () => void; }; style: Record; onNodeClick: (node: { isInternal: boolean }) => void; }) => React.ReactNode; data: FileNode[]; onActivate: (node: { isInternal: boolean; data: FileNode }) => void; }) => (
{data.map((file) => { const mockNode = { data: file, isLeaf: !file.children || file.children.length === 0, isInternal: !!(file.children && file.children.length > 0), isOpen: false, level: 0, toggle: vi.fn(), }; return (
{ // Simulate the Tree's onActivate behavior if (!mockNode.isInternal) { onActivate({ isInternal: mockNode.isInternal, data: file }); } }} > {children({ node: mockNode, style: {}, onNodeClick: (node: { isInternal: boolean }) => { if (!node.isInternal) { onActivate({ isInternal: node.isInternal, data: file }); } }, })}
); })}
), })); // Mock resize observer hook vi.mock('@react-hook/resize-observer', () => ({ default: vi.fn(), })); // Mock contexts vi.mock('../../contexts/ThemeContext', () => ({ useTheme: () => ({ colorScheme: 'light', updateColorScheme: vi.fn(), }), ThemeProvider: ({ children }: { children: React.ReactNode }) => (
{children}
), })); vi.mock('../../contexts/WorkspaceDataContext', () => ({ useWorkspaceData: () => ({ currentWorkspace: { name: 'test-workspace', path: '/test' }, workspaces: [], settings: {}, loading: false, loadWorkspaces: vi.fn(), loadWorkspaceData: vi.fn(), setCurrentWorkspace: vi.fn(), }), WorkspaceDataProvider: ({ children }: { children: React.ReactNode }) => (
{children}
), })); vi.mock('../../contexts/ModalContext', () => ({ useModalContext: () => ({ newFileModalVisible: false, setNewFileModalVisible: vi.fn(), deleteFileModalVisible: false, setDeleteFileModalVisible: vi.fn(), renameFileModalVisible: false, setRenameFileModalVisible: vi.fn(), commitMessageModalVisible: false, setCommitMessageModalVisible: vi.fn(), settingsModalVisible: false, setSettingsModalVisible: vi.fn(), switchWorkspaceModalVisible: false, setSwitchWorkspaceModalVisible: vi.fn(), createWorkspaceModalVisible: false, setCreateWorkspaceModalVisible: vi.fn(), }), ModalProvider: ({ children }: { children: React.ReactNode }) => (
{children}
), })); vi.mock('../../hooks/useFileOperations', () => ({ useFileOperations: () => ({ handleSave: vi.fn(), handleCreate: vi.fn(), handleDelete: vi.fn(), handleUpload: vi.fn(), handleMove: vi.fn(), handleRename: vi.fn(), }), })); const TestWrapper = ({ children }: { children: React.ReactNode }) => ( {children} ); describe('FileTree', () => { const mockHandleFileSelect = vi.fn(); const mockLoadFileList = vi.fn(); const mockFiles: FileNode[] = [ { id: '1', name: 'README.md', path: 'README.md', }, { id: '2', name: 'docs', path: 'docs', children: [ { id: '3', name: 'guide.md', path: 'docs/guide.md', }, ], }, { id: '4', name: '.hidden', path: '.hidden', }, ]; beforeEach(() => { vi.clearAllMocks(); }); it('renders file tree with files', () => { const { getByTestId } = render( ); expect(getByTestId('file-tree')).toBeInTheDocument(); expect(getByTestId('file-node-1')).toBeInTheDocument(); expect(getByTestId('file-node-2')).toBeInTheDocument(); }); it('calls handleFileSelect when file is clicked', async () => { const { getByTestId } = render( ); const fileNode = getByTestId('file-node-1'); fireEvent.click(fileNode); await waitFor(() => { expect(mockHandleFileSelect).toHaveBeenCalledWith('README.md'); }); }); it('filters out hidden files when showHiddenFiles is false', () => { const { getByTestId, queryByTestId } = render( ); // Should show regular files expect(getByTestId('file-node-1')).toBeInTheDocument(); expect(getByTestId('file-node-2')).toBeInTheDocument(); // Should not show hidden file expect(queryByTestId('file-node-4')).not.toBeInTheDocument(); }); it('shows hidden files when showHiddenFiles is true', () => { const { getByTestId } = render( ); // Should show all files including hidden expect(getByTestId('file-node-1')).toBeInTheDocument(); expect(getByTestId('file-node-2')).toBeInTheDocument(); expect(getByTestId('file-node-4')).toBeInTheDocument(); }); it('renders empty tree when no files provided', () => { const { getByTestId } = render( ); const tree = getByTestId('file-tree'); expect(tree).toBeInTheDocument(); expect(tree.children).toHaveLength(0); }); it('does not call handleFileSelect for folder clicks', async () => { const { getByTestId } = render( ); // Click on folder (has children) const folderNode = getByTestId('file-node-2'); fireEvent.click(folderNode); // Should not call handleFileSelect for folders await waitFor(() => { expect(mockHandleFileSelect).not.toHaveBeenCalled(); }); }); });