diff --git a/app/src/components/modals/file/CreateFileModal.test.tsx b/app/src/components/modals/file/CreateFileModal.test.tsx index d013ba4..098bcb2 100644 --- a/app/src/components/modals/file/CreateFileModal.test.tsx +++ b/app/src/components/modals/file/CreateFileModal.test.tsx @@ -9,13 +9,6 @@ import React from 'react'; import { MantineProvider } from '@mantine/core'; import CreateFileModal from './CreateFileModal'; -// Mock notifications -vi.mock('@mantine/notifications', () => ({ - notifications: { - show: vi.fn(), - }, -})); - // Mock ModalContext with modal always open const mockModalContext = { newFileModalVisible: true, @@ -51,14 +44,13 @@ describe('CreateFileModal', () => { beforeEach(() => { vi.clearAllMocks(); + mockOnCreateFile.mockReset(); mockOnCreateFile.mockResolvedValue(undefined); - - // Reset modal context mocks mockModalContext.setNewFileModalVisible.mockClear(); }); - describe('Modal Visibility', () => { - it('renders modal when open', () => { + describe('Basic functionality', () => { + it('renders modal with all essential elements', () => { render(); expect(screen.getByText('Create New File')).toBeInTheDocument(); @@ -67,19 +59,16 @@ describe('CreateFileModal', () => { expect(screen.getByTestId('confirm-create-button')).toBeInTheDocument(); }); - it('calls setNewFileModalVisible when modal is closed', () => { + it('closes modal when cancel button is clicked', () => { render(); - const cancelButton = screen.getByTestId('cancel-create-button'); - fireEvent.click(cancelButton); + fireEvent.click(screen.getByTestId('cancel-create-button')); expect(mockModalContext.setNewFileModalVisible).toHaveBeenCalledWith( false ); }); - }); - describe('Form Interaction', () => { it('updates file name input when typed', () => { render(); @@ -89,7 +78,27 @@ describe('CreateFileModal', () => { expect((fileNameInput as HTMLInputElement).value).toBe('test-file.md'); }); - it('handles form submission with valid file name', async () => { + it('has disabled create button when input is empty', () => { + render(); + + const createButton = screen.getByTestId('confirm-create-button'); + expect(createButton).toBeDisabled(); + }); + + it('enables create button when valid input is provided', () => { + render(); + + const fileNameInput = screen.getByTestId('file-name-input'); + const createButton = screen.getByTestId('confirm-create-button'); + + fireEvent.change(fileNameInput, { target: { value: 'test.md' } }); + + expect(createButton).not.toBeDisabled(); + }); + }); + + describe('File creation flow', () => { + it('creates file successfully with valid input', async () => { render(); const fileNameInput = screen.getByTestId('file-name-input'); @@ -101,155 +110,25 @@ describe('CreateFileModal', () => { await waitFor(() => { expect(mockOnCreateFile).toHaveBeenCalledWith('new-document.md'); }); - }); - - it('prevents submission with empty file name', () => { - render(); - - const createButton = screen.getByTestId('confirm-create-button'); - fireEvent.click(createButton); - - // Should not call the function with empty name - expect(mockOnCreateFile).not.toHaveBeenCalled(); - }); - - it('closes modal after successful file creation', async () => { - render(); - - const fileNameInput = screen.getByTestId('file-name-input'); - const createButton = screen.getByTestId('confirm-create-button'); - - fireEvent.change(fileNameInput, { target: { value: 'test.md' } }); - fireEvent.click(createButton); - - await waitFor(() => { - expect(mockOnCreateFile).toHaveBeenCalledWith('test.md'); - }); await waitFor(() => { expect(mockModalContext.setNewFileModalVisible).toHaveBeenCalledWith( false ); - }); - }); - - it('clears input after successful submission', async () => { - render(); - - const fileNameInput = screen.getByTestId('file-name-input'); - const createButton = screen.getByTestId('confirm-create-button'); - - fireEvent.change(fileNameInput, { target: { value: 'test.md' } }); - fireEvent.click(createButton); - - await waitFor(() => { - expect(mockOnCreateFile).toHaveBeenCalledWith('test.md'); - }); - - await waitFor(() => { expect((fileNameInput as HTMLInputElement).value).toBe(''); }); }); - }); - describe('Modal Actions', () => { - it('has cancel and create buttons', () => { - render(); - - const confirmButton = screen.getByTestId('confirm-create-button'); - const cancelButton = screen.getByTestId('cancel-create-button'); - - expect(confirmButton).toBeInTheDocument(); - expect(cancelButton).toBeInTheDocument(); - - expect(confirmButton).toHaveRole('button'); - expect(cancelButton).toHaveRole('button'); - }); - - it('closes modal when cancel button is clicked', () => { - render(); - - const cancelButton = screen.getByTestId('cancel-create-button'); - fireEvent.click(cancelButton); - - expect(mockModalContext.setNewFileModalVisible).toHaveBeenCalledWith( - false - ); - }); - - it('calls onCreateFile when create button is clicked with valid input', async () => { + it('creates file via Enter key press', async () => { render(); const fileNameInput = screen.getByTestId('file-name-input'); - const createButton = screen.getByTestId('confirm-create-button'); - fireEvent.change(fileNameInput, { target: { value: 'test.md' } }); - fireEvent.click(createButton); + fireEvent.change(fileNameInput, { target: { value: 'enter-test.md' } }); + fireEvent.keyDown(fileNameInput, { key: 'Enter', code: 'Enter' }); await waitFor(() => { - expect(mockOnCreateFile).toHaveBeenCalledTimes(1); - expect(mockOnCreateFile).toHaveBeenCalledWith('test.md'); - }); - }); - }); - - describe('File Name Validation', () => { - it('handles special characters in file names', async () => { - render(); - - const fileNameInput = screen.getByTestId('file-name-input'); - const createButton = screen.getByTestId('confirm-create-button'); - - const specialFileName = 'file-with_special.chars (1).md'; - fireEvent.change(fileNameInput, { target: { value: specialFileName } }); - fireEvent.click(createButton); - - await waitFor(() => { - expect(mockOnCreateFile).toHaveBeenCalledWith(specialFileName); - }); - }); - - it('handles long file names', async () => { - render(); - - const fileNameInput = screen.getByTestId('file-name-input'); - const createButton = screen.getByTestId('confirm-create-button'); - - const longFileName = 'a'.repeat(100) + '.md'; - fireEvent.change(fileNameInput, { target: { value: longFileName } }); - fireEvent.click(createButton); - - await waitFor(() => { - expect(mockOnCreateFile).toHaveBeenCalledWith(longFileName); - }); - }); - - it('handles file names without extensions', async () => { - render(); - - const fileNameInput = screen.getByTestId('file-name-input'); - const createButton = screen.getByTestId('confirm-create-button'); - - fireEvent.change(fileNameInput, { target: { value: 'README' } }); - fireEvent.click(createButton); - - await waitFor(() => { - expect(mockOnCreateFile).toHaveBeenCalledWith('README'); - }); - }); - - it('handles unicode characters in file names', async () => { - render(); - - const fileNameInput = screen.getByTestId('file-name-input'); - const createButton = screen.getByTestId('confirm-create-button'); - - const unicodeFileName = 'ファイル名.md'; - fireEvent.change(fileNameInput, { target: { value: unicodeFileName } }); - fireEvent.click(createButton); - - await waitFor(() => { - expect(mockOnCreateFile).toHaveBeenCalledWith(unicodeFileName); + expect(mockOnCreateFile).toHaveBeenCalledWith('enter-test.md'); }); }); @@ -268,154 +147,68 @@ describe('CreateFileModal', () => { expect(mockOnCreateFile).toHaveBeenCalledWith('spaced-file.md'); }); }); - }); - - describe('Error Handling', () => { - it('handles creation errors gracefully', async () => { - mockOnCreateFile.mockRejectedValue(new Error('File creation failed')); + it('does not submit when input is empty', () => { render(); const fileNameInput = screen.getByTestId('file-name-input'); - const createButton = screen.getByTestId('confirm-create-button'); + fireEvent.keyDown(fileNameInput, { key: 'Enter', code: 'Enter' }); - fireEvent.change(fileNameInput, { target: { value: 'test.md' } }); - fireEvent.click(createButton); - - await waitFor(() => { - expect(mockOnCreateFile).toHaveBeenCalledWith('test.md'); - }); - - // Modal should handle the error gracefully (not crash) - expect(screen.getByText('Create New File')).toBeInTheDocument(); + expect(mockOnCreateFile).not.toHaveBeenCalled(); }); - it('does not close modal when creation fails', async () => { - mockOnCreateFile.mockRejectedValue(new Error('File creation failed')); - + it('does not submit when input contains only whitespace', () => { render(); const fileNameInput = screen.getByTestId('file-name-input'); const createButton = screen.getByTestId('confirm-create-button'); - fireEvent.change(fileNameInput, { target: { value: 'test.md' } }); + fireEvent.change(fileNameInput, { target: { value: ' ' } }); + + expect(createButton).toBeDisabled(); + expect(mockOnCreateFile).not.toHaveBeenCalled(); + }); + }); + + describe('File name variations', () => { + it.each([ + ['file-with_special.chars (1).md', 'special characters'], + ['README', 'no extension'], + ['ファイル名.md', 'unicode characters'], + ['a'.repeat(100) + '.md', 'long file names'], + ])('handles %s (%s)', async (fileName, _description) => { + render(); + + const fileNameInput = screen.getByTestId('file-name-input'); + const createButton = screen.getByTestId('confirm-create-button'); + + fireEvent.change(fileNameInput, { target: { value: fileName } }); fireEvent.click(createButton); await waitFor(() => { - expect(mockOnCreateFile).toHaveBeenCalledWith('test.md'); + expect(mockOnCreateFile).toHaveBeenCalledWith(fileName); }); - - // Modal should remain open when creation fails - expect(mockModalContext.setNewFileModalVisible).not.toHaveBeenCalledWith( - false - ); }); }); describe('Accessibility', () => { - it('has proper form labels and structure', () => { + it('provides proper keyboard navigation and accessibility features', () => { render(); const fileNameInput = screen.getByTestId('file-name-input'); - expect(fileNameInput).toBeInTheDocument(); - expect(fileNameInput.tagName).toBe('INPUT'); + + // Input should be focusable and accessible + expect(fileNameInput).not.toHaveAttribute('disabled'); + expect(fileNameInput).not.toHaveAttribute('readonly'); expect(fileNameInput).toHaveAttribute('type', 'text'); - }); - - it('has proper button roles', () => { - render(); - - const buttons = screen.getAllByRole('button'); - expect(buttons.length).toBeGreaterThanOrEqual(2); // Cancel and Create buttons + expect(fileNameInput).toHaveAccessibleName(); + // Buttons should have proper roles const cancelButton = screen.getByRole('button', { name: /cancel/i }); const createButton = screen.getByRole('button', { name: /create/i }); expect(cancelButton).toBeInTheDocument(); expect(createButton).toBeInTheDocument(); }); - - it('supports keyboard navigation', () => { - render(); - - const fileNameInput = screen.getByTestId('file-name-input'); - - // Check that the input is focusable (not disabled or readonly) - expect(fileNameInput).not.toHaveAttribute('disabled'); - expect(fileNameInput).not.toHaveAttribute('readonly'); - - // Check that the input can receive keyboard events (more reliable than focus) - fireEvent.keyDown(fileNameInput, { key: 'a' }); - fireEvent.change(fileNameInput, { target: { value: 'test' } }); - - expect((fileNameInput as HTMLInputElement).value).toBe('test'); - - // Verify the input is accessible via keyboard navigation - expect(fileNameInput).toHaveAttribute('type', 'text'); - expect(fileNameInput).toHaveAccessibleName(); // Has proper label - }); - - it('has proper modal structure', () => { - render(); - - // Modal should have proper title - expect(screen.getByText('Create New File')).toBeInTheDocument(); - - // Should have form elements - expect(screen.getByTestId('file-name-input')).toBeInTheDocument(); - }); - }); - - describe('Component Props', () => { - it('accepts and uses onCreateFile prop correctly', async () => { - const customMockCreate = vi.fn().mockResolvedValue(undefined); - - render(); - - const fileNameInput = screen.getByTestId('file-name-input'); - const createButton = screen.getByTestId('confirm-create-button'); - - fireEvent.change(fileNameInput, { target: { value: 'custom-test.md' } }); - fireEvent.click(createButton); - - await waitFor(() => { - expect(customMockCreate).toHaveBeenCalledWith('custom-test.md'); - }); - }); - - it('handles function prop correctly', () => { - const testFunction = vi.fn(); - - expect(() => { - render(); - }).not.toThrow(); - - expect(screen.getByText('Create New File')).toBeInTheDocument(); - }); - }); - - describe('Form Submission Edge Cases', () => { - it('submits form via Enter key', async () => { - render(); - - const fileNameInput = screen.getByTestId('file-name-input'); - - fireEvent.change(fileNameInput, { target: { value: 'enter-test.md' } }); - fireEvent.keyDown(fileNameInput, { key: 'Enter', code: 'Enter' }); - - await waitFor(() => { - expect(mockOnCreateFile).toHaveBeenCalledWith('enter-test.md'); - }); - }); - - it('does not submit empty form via Enter key', () => { - render(); - - const fileNameInput = screen.getByTestId('file-name-input'); - fireEvent.keyDown(fileNameInput, { key: 'Enter', code: 'Enter' }); - - // Should not call the function - expect(mockOnCreateFile).not.toHaveBeenCalled(); - }); }); }); diff --git a/app/src/components/modals/file/DeleteFileModal.test.tsx b/app/src/components/modals/file/DeleteFileModal.test.tsx index 13098ac..9fe3bd2 100644 --- a/app/src/components/modals/file/DeleteFileModal.test.tsx +++ b/app/src/components/modals/file/DeleteFileModal.test.tsx @@ -9,13 +9,6 @@ import React from 'react'; import { MantineProvider } from '@mantine/core'; import DeleteFileModal from './DeleteFileModal'; -// Mock notifications -vi.mock('@mantine/notifications', () => ({ - notifications: { - show: vi.fn(), - }, -})); - // Mock ModalContext with modal always open const mockModalContext = { newFileModalVisible: false, @@ -51,14 +44,13 @@ describe('DeleteFileModal', () => { beforeEach(() => { vi.clearAllMocks(); + mockOnDeleteFile.mockReset(); mockOnDeleteFile.mockResolvedValue(undefined); - - // Reset modal context mocks mockModalContext.setDeleteFileModalVisible.mockClear(); }); - describe('Modal Visibility', () => { - it('renders modal when open with file selected', () => { + describe('Basic functionality', () => { + it('renders modal with file confirmation and action buttons', () => { render( { expect( screen.getByText(/Are you sure you want to delete "test-file.md"?/) ).toBeInTheDocument(); - const cancelButton = screen.getByTestId('cancel-delete-button'); - const deleteButton = screen.getByTestId('confirm-delete-button'); - - expect(cancelButton).toBeInTheDocument(); - expect(deleteButton).toBeInTheDocument(); - - expect(cancelButton).toHaveTextContent('Cancel'); - expect(deleteButton).toHaveTextContent('Delete'); - - expect(cancelButton).toHaveRole('button'); - expect(deleteButton).toHaveRole('button'); + expect(screen.getByTestId('cancel-delete-button')).toBeInTheDocument(); + expect(screen.getByTestId('confirm-delete-button')).toBeInTheDocument(); }); - it('renders modal when open with no file selected', () => { + it('renders modal with null file selection', () => { render( ); expect(screen.getByText('Delete File')).toBeInTheDocument(); - // Should still render the confirmation text with null file expect( screen.getByText(/Are you sure you want to delete/) ).toBeInTheDocument(); }); - it('calls setDeleteFileModalVisible when modal is closed', () => { - render( - - ); - - const cancelButton = screen.getByTestId('cancel-delete-button'); - fireEvent.click(cancelButton); - - expect(mockModalContext.setDeleteFileModalVisible).toHaveBeenCalledWith( - false - ); - }); - }); - - describe('File Deletion', () => { - it('handles file deletion with valid file', async () => { - render( - - ); - - const deleteButton = screen.getByTestId('confirm-delete-button'); - fireEvent.click(deleteButton); - - await waitFor(() => { - expect(mockOnDeleteFile).toHaveBeenCalledWith('document.md'); - }); - }); - - it('does not call onDeleteFile when no file is selected', () => { - render( - - ); - - const deleteButton = screen.getByTestId('confirm-delete-button'); - fireEvent.click(deleteButton); - - // Should not call the function when no file is selected - expect(mockOnDeleteFile).not.toHaveBeenCalled(); - }); - - it('closes modal after successful file deletion', async () => { - render( - - ); - - const deleteButton = screen.getByTestId('confirm-delete-button'); - fireEvent.click(deleteButton); - - await waitFor(() => { - expect(mockOnDeleteFile).toHaveBeenCalledWith('test.md'); - }); - - await waitFor(() => { - expect(mockModalContext.setDeleteFileModalVisible).toHaveBeenCalledWith( - false - ); - }); - }); - - it('handles deletion of files with special characters', async () => { - const specialFileName = 'file-with_special.chars (1).md'; - render( - - ); - - expect( - screen.getByText( - `Are you sure you want to delete "${specialFileName}"?` - ) - ).toBeInTheDocument(); - - const deleteButton = screen.getByTestId('confirm-delete-button'); - fireEvent.click(deleteButton); - - await waitFor(() => { - expect(mockOnDeleteFile).toHaveBeenCalledWith(specialFileName); - }); - }); - - it('handles deletion of files with unicode characters', async () => { - const unicodeFileName = 'ファイル名.md'; - render( - - ); - - expect( - screen.getByText( - `Are you sure you want to delete "${unicodeFileName}"?` - ) - ).toBeInTheDocument(); - - const deleteButton = screen.getByTestId('confirm-delete-button'); - fireEvent.click(deleteButton); - - await waitFor(() => { - expect(mockOnDeleteFile).toHaveBeenCalledWith(unicodeFileName); - }); - }); - - it('handles very long file names', async () => { - const longFileName = 'a'.repeat(100) + '.md'; - render( - - ); - - const deleteButton = screen.getByTestId('confirm-delete-button'); - fireEvent.click(deleteButton); - - await waitFor(() => { - expect(mockOnDeleteFile).toHaveBeenCalledWith(longFileName); - }); - }); - }); - - describe('Modal Actions', () => { - it('has cancel and delete buttons', () => { - render( - - ); - - const cancelButton = screen.getByTestId('cancel-delete-button'); - const deleteButton = screen.getByTestId('confirm-delete-button'); - - expect(cancelButton).toHaveRole('button'); - expect(deleteButton).toHaveRole('button'); - }); - it('closes modal when cancel button is clicked', () => { render( { /> ); - const cancelButton = screen.getByTestId('cancel-delete-button'); - fireEvent.click(cancelButton); + fireEvent.click(screen.getByTestId('cancel-delete-button')); expect(mockModalContext.setDeleteFileModalVisible).toHaveBeenCalledWith( false @@ -260,249 +93,21 @@ describe('DeleteFileModal', () => { }); }); - describe('Error Handling', () => { - it('handles deletion errors gracefully', async () => { - mockOnDeleteFile.mockRejectedValue(new Error('File deletion failed')); - + describe('File deletion flow', () => { + it('deletes file successfully when confirmed', async () => { render( ); - const deleteButton = screen.getByTestId('confirm-delete-button'); - fireEvent.click(deleteButton); + fireEvent.click(screen.getByTestId('confirm-delete-button')); await waitFor(() => { - expect(mockOnDeleteFile).toHaveBeenCalledWith('test.md'); + expect(mockOnDeleteFile).toHaveBeenCalledWith('document.md'); }); - // Modal should handle the error gracefully (not crash) - expect(screen.getByText('Delete File')).toBeInTheDocument(); - }); - - it('does not close modal when deletion fails', async () => { - mockOnDeleteFile.mockRejectedValue(new Error('File deletion failed')); - - render( - - ); - - const deleteButton = screen.getByTestId('confirm-delete-button'); - - fireEvent.click(deleteButton); - - await waitFor(() => { - expect(mockOnDeleteFile).toHaveBeenCalledWith('test.md'); - }); - - // Modal should remain open when deletion fails - expect( - mockModalContext.setDeleteFileModalVisible - ).not.toHaveBeenCalledWith(false); - }); - }); - - describe('Accessibility', () => { - it('has proper modal structure', () => { - render( - - ); - - // Modal should have proper title - expect(screen.getByText('Delete File')).toBeInTheDocument(); - - // Should have confirmation text - expect( - screen.getByText(/Are you sure you want to delete/) - ).toBeInTheDocument(); - }); - - it('has proper button roles', () => { - render( - - ); - - const buttons = screen.getAllByRole('button'); - expect(buttons.length).toBeGreaterThanOrEqual(2); // Cancel and Delete buttons - - const cancelButton = screen.getByRole('button', { name: /cancel/i }); - const deleteButton = screen.getByRole('button', { name: /delete/i }); - - expect(cancelButton).toBeInTheDocument(); - expect(deleteButton).toBeInTheDocument(); - }); - - it('has proper confirmation message structure', () => { - render( - - ); - - // Check that the file name is properly quoted in the message - expect( - screen.getByText(/Are you sure you want to delete "important-file.md"?/) - ).toBeInTheDocument(); - }); - - it('supports keyboard navigation', () => { - render( - - ); - - const cancelButton = screen.getByTestId('cancel-delete-button'); - const deleteButton = screen.getByTestId('confirm-delete-button'); - - // Buttons should be focusable - expect(cancelButton).not.toHaveAttribute('disabled'); - expect(deleteButton).not.toHaveAttribute('disabled'); - - // Should handle keyboard events - fireEvent.keyDown(deleteButton, { key: 'Enter', code: 'Enter' }); - fireEvent.keyDown(cancelButton, { key: 'Escape', code: 'Escape' }); - }); - }); - - describe('Component Props', () => { - it('accepts and uses onDeleteFile prop correctly', async () => { - const customMockDelete = vi.fn().mockResolvedValue(undefined); - - render( - - ); - - const deleteButton = screen.getByText('Delete'); - fireEvent.click(deleteButton); - - await waitFor(() => { - expect(customMockDelete).toHaveBeenCalledWith('custom-test.md'); - }); - }); - - it('handles different selectedFile prop values', () => { - const testCases = [ - 'simple.md', - 'folder/nested.md', - 'file with spaces.md', - 'UPPERCASE.MD', - null, - ]; - - testCases.forEach((fileName) => { - const { unmount } = render( - - ); - - expect(screen.getByText('Delete File')).toBeInTheDocument(); - unmount(); - }); - }); - - it('handles function prop correctly', () => { - const testFunction = vi.fn(); - - expect(() => { - render( - - ); - }).not.toThrow(); - - expect(screen.getByText('Delete File')).toBeInTheDocument(); - }); - }); - - describe('File Path Edge Cases', () => { - it('handles file paths with folders', async () => { - const nestedFilePath = 'folder/subfolder/deep-file.md'; - render( - - ); - - const deleteButton = screen.getByText('Delete'); - fireEvent.click(deleteButton); - - await waitFor(() => { - expect(mockOnDeleteFile).toHaveBeenCalledWith(nestedFilePath); - }); - }); - - it('handles files without extensions', async () => { - render( - - ); - - const deleteButton = screen.getByText('Delete'); - fireEvent.click(deleteButton); - - await waitFor(() => { - expect(mockOnDeleteFile).toHaveBeenCalledWith('README'); - }); - }); - - it('handles empty string as selectedFile', () => { - render( - - ); - - const deleteButton = screen.getByText('Delete'); - fireEvent.click(deleteButton); - - // Should not call the function with empty string - expect(mockOnDeleteFile).not.toHaveBeenCalled(); - }); - }); - - describe('User Interaction Flow', () => { - it('completes full deletion flow successfully', async () => { - render( - - ); - - // 1. Modal opens and shows file name - expect( - screen.getByText('Are you sure you want to delete "complete-test.md"?') - ).toBeInTheDocument(); - - // 2. User clicks delete - const deleteButton = screen.getByTestId('confirm-delete-button'); - fireEvent.click(deleteButton); - - // 3. Deletion function is called - await waitFor(() => { - expect(mockOnDeleteFile).toHaveBeenCalledWith('complete-test.md'); - }); - - // 4. Modal closes await waitFor(() => { expect(mockModalContext.setDeleteFileModalVisible).toHaveBeenCalledWith( false @@ -510,6 +115,26 @@ describe('DeleteFileModal', () => { }); }); + it('does not delete when no file is selected', () => { + render( + + ); + + fireEvent.click(screen.getByTestId('confirm-delete-button')); + + expect(mockOnDeleteFile).not.toHaveBeenCalled(); + }); + + it('does not delete when selectedFile is empty string', () => { + render( + + ); + + fireEvent.click(screen.getByTestId('confirm-delete-button')); + + expect(mockOnDeleteFile).not.toHaveBeenCalled(); + }); + it('allows user to cancel deletion', () => { render( { /> ); - // User clicks cancel instead of delete - const cancelButton = screen.getByTestId('cancel-delete-button'); - fireEvent.click(cancelButton); + fireEvent.click(screen.getByTestId('cancel-delete-button')); - // Should close modal without calling delete function expect(mockOnDeleteFile).not.toHaveBeenCalled(); expect(mockModalContext.setDeleteFileModalVisible).toHaveBeenCalledWith( false ); }); }); + + describe('File name variations', () => { + it.each([ + ['file-with_special.chars (1).md', 'special characters'], + ['ファイル名.md', 'unicode characters'], + ['folder/subfolder/deep-file.md', 'nested path'], + ['README', 'no extension'], + ['a'.repeat(100) + '.md', 'long file name'], + ])('handles %s (%s)', async (fileName, _description) => { + render( + + ); + + expect( + screen.getByText(`Are you sure you want to delete "${fileName}"?`) + ).toBeInTheDocument(); + + fireEvent.click(screen.getByTestId('confirm-delete-button')); + + await waitFor(() => { + expect(mockOnDeleteFile).toHaveBeenCalledWith(fileName); + }); + }); + }); + + describe('Accessibility', () => { + it('provides proper modal structure and button accessibility', () => { + render( + + ); + + // Modal structure + expect(screen.getByText('Delete File')).toBeInTheDocument(); + expect( + screen.getByText(/Are you sure you want to delete "test.md"?/) + ).toBeInTheDocument(); + + // Button accessibility + const cancelButton = screen.getByRole('button', { name: /cancel/i }); + const deleteButton = screen.getByRole('button', { name: /delete/i }); + + expect(cancelButton).toBeInTheDocument(); + expect(deleteButton).toBeInTheDocument(); + expect(cancelButton).not.toHaveAttribute('disabled'); + expect(deleteButton).not.toHaveAttribute('disabled'); + }); + }); });