mirror of
https://github.com/lordmathis/lemma.git
synced 2025-12-23 01:54:28 +00:00
215 lines
7.2 KiB
TypeScript
215 lines
7.2 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import {
|
|
render as rtlRender,
|
|
screen,
|
|
fireEvent,
|
|
waitFor,
|
|
} from '@testing-library/react';
|
|
import React from 'react';
|
|
import { MantineProvider } from '@mantine/core';
|
|
import CommitMessageModal from './CommitMessageModal';
|
|
|
|
// Mock notifications
|
|
vi.mock('@mantine/notifications', () => ({
|
|
notifications: {
|
|
show: vi.fn(),
|
|
},
|
|
}));
|
|
|
|
// Mock ModalContext with modal always open
|
|
const mockModalContext = {
|
|
newFileModalVisible: false,
|
|
setNewFileModalVisible: vi.fn(),
|
|
deleteFileModalVisible: false,
|
|
setDeleteFileModalVisible: vi.fn(),
|
|
commitMessageModalVisible: true,
|
|
setCommitMessageModalVisible: vi.fn(),
|
|
settingsModalVisible: false,
|
|
setSettingsModalVisible: vi.fn(),
|
|
switchWorkspaceModalVisible: false,
|
|
setSwitchWorkspaceModalVisible: vi.fn(),
|
|
createWorkspaceModalVisible: false,
|
|
setCreateWorkspaceModalVisible: vi.fn(),
|
|
};
|
|
|
|
vi.mock('../../../contexts/ModalContext', () => ({
|
|
useModalContext: () => mockModalContext,
|
|
}));
|
|
|
|
// Helper wrapper component for testing
|
|
const TestWrapper = ({ children }: { children: React.ReactNode }) => (
|
|
<MantineProvider defaultColorScheme="light">{children}</MantineProvider>
|
|
);
|
|
|
|
// Custom render function
|
|
const render = (ui: React.ReactElement) => {
|
|
return rtlRender(ui, { wrapper: TestWrapper });
|
|
};
|
|
|
|
describe('CommitMessageModal', () => {
|
|
const mockOnCommitAndPush = vi.fn();
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
mockOnCommitAndPush.mockResolvedValue(undefined);
|
|
mockModalContext.setCommitMessageModalVisible.mockClear();
|
|
});
|
|
|
|
describe('Modal Visibility and Content', () => {
|
|
it('renders modal with correct content when opened', () => {
|
|
render(<CommitMessageModal onCommitAndPush={mockOnCommitAndPush} />);
|
|
|
|
expect(screen.getByText('Enter Commit Message')).toBeInTheDocument();
|
|
expect(screen.getByTestId('commit-message-input')).toBeInTheDocument();
|
|
expect(
|
|
screen.getByTestId('cancel-commit-message-button')
|
|
).toBeInTheDocument();
|
|
expect(
|
|
screen.getByTestId('confirm-commit-message-button')
|
|
).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
describe('User Actions', () => {
|
|
it('calls onClose when cancel button is clicked', () => {
|
|
render(<CommitMessageModal onCommitAndPush={mockOnCommitAndPush} />);
|
|
|
|
const cancelButton = screen.getByTestId('cancel-commit-message-button');
|
|
fireEvent.click(cancelButton);
|
|
|
|
expect(
|
|
mockModalContext.setCommitMessageModalVisible
|
|
).toHaveBeenCalledWith(false);
|
|
});
|
|
});
|
|
|
|
describe('Form Validation', () => {
|
|
it('updates input value when user types', () => {
|
|
render(<CommitMessageModal onCommitAndPush={mockOnCommitAndPush} />);
|
|
|
|
const messageInput = screen.getByTestId('commit-message-input');
|
|
fireEvent.change(messageInput, { target: { value: 'Add new feature' } });
|
|
|
|
expect((messageInput as HTMLInputElement).value).toBe('Add new feature');
|
|
});
|
|
|
|
it('disables commit button when input is empty', () => {
|
|
render(<CommitMessageModal onCommitAndPush={mockOnCommitAndPush} />);
|
|
|
|
const commitButton = screen.getByTestId('confirm-commit-message-button');
|
|
expect(commitButton).toBeDisabled();
|
|
});
|
|
|
|
it('enables commit button when input has content', () => {
|
|
render(<CommitMessageModal onCommitAndPush={mockOnCommitAndPush} />);
|
|
|
|
const messageInput = screen.getByTestId('commit-message-input');
|
|
const commitButton = screen.getByTestId('confirm-commit-message-button');
|
|
|
|
fireEvent.change(messageInput, { target: { value: 'Test commit' } });
|
|
|
|
expect(commitButton).not.toBeDisabled();
|
|
});
|
|
});
|
|
|
|
describe('Commit and Push Flow', () => {
|
|
it('calls onCommitAndPush with trimmed message', async () => {
|
|
render(<CommitMessageModal onCommitAndPush={mockOnCommitAndPush} />);
|
|
|
|
const messageInput = screen.getByTestId('commit-message-input');
|
|
const commitButton = screen.getByTestId('confirm-commit-message-button');
|
|
|
|
fireEvent.change(messageInput, {
|
|
target: { value: ' Update README ' },
|
|
});
|
|
fireEvent.click(commitButton);
|
|
|
|
await waitFor(() => {
|
|
expect(mockOnCommitAndPush).toHaveBeenCalledWith('Update README');
|
|
});
|
|
});
|
|
|
|
it('calls onCommitAndPush when commit button clicked', async () => {
|
|
render(<CommitMessageModal onCommitAndPush={mockOnCommitAndPush} />);
|
|
|
|
const messageInput = screen.getByTestId('commit-message-input');
|
|
const commitButton = screen.getByTestId('confirm-commit-message-button');
|
|
|
|
fireEvent.change(messageInput, {
|
|
target: { value: 'Fix bug in editor' },
|
|
});
|
|
fireEvent.click(commitButton);
|
|
|
|
await waitFor(() => {
|
|
expect(mockOnCommitAndPush).toHaveBeenCalledWith('Fix bug in editor');
|
|
});
|
|
});
|
|
|
|
it('submits form when Enter key is pressed', async () => {
|
|
render(<CommitMessageModal onCommitAndPush={mockOnCommitAndPush} />);
|
|
|
|
const messageInput = screen.getByTestId('commit-message-input');
|
|
|
|
fireEvent.change(messageInput, { target: { value: 'Enter key commit' } });
|
|
fireEvent.keyDown(messageInput, { key: 'Enter', code: 'Enter' });
|
|
|
|
await waitFor(() => {
|
|
expect(mockOnCommitAndPush).toHaveBeenCalledWith('Enter key commit');
|
|
});
|
|
});
|
|
|
|
it('does not submit when Enter pressed with empty message', () => {
|
|
render(<CommitMessageModal onCommitAndPush={mockOnCommitAndPush} />);
|
|
|
|
const messageInput = screen.getByTestId('commit-message-input');
|
|
fireEvent.keyDown(messageInput, { key: 'Enter', code: 'Enter' });
|
|
|
|
expect(mockOnCommitAndPush).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('closes modal and clears input after successful commit', async () => {
|
|
render(<CommitMessageModal onCommitAndPush={mockOnCommitAndPush} />);
|
|
|
|
const messageInput = screen.getByTestId('commit-message-input');
|
|
const commitButton = screen.getByTestId('confirm-commit-message-button');
|
|
|
|
fireEvent.change(messageInput, { target: { value: 'Initial commit' } });
|
|
fireEvent.click(commitButton);
|
|
|
|
await waitFor(() => {
|
|
expect(mockOnCommitAndPush).toHaveBeenCalledWith('Initial commit');
|
|
});
|
|
|
|
await waitFor(() => {
|
|
expect(
|
|
mockModalContext.setCommitMessageModalVisible
|
|
).toHaveBeenCalledWith(false);
|
|
expect((messageInput as HTMLInputElement).value).toBe('');
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Accessibility', () => {
|
|
it('has proper form structure with labeled input', () => {
|
|
render(<CommitMessageModal onCommitAndPush={mockOnCommitAndPush} />);
|
|
|
|
const messageInput = screen.getByTestId('commit-message-input');
|
|
|
|
expect(messageInput).toHaveAttribute('type', 'text');
|
|
expect(messageInput).toHaveAccessibleName();
|
|
expect(messageInput).not.toHaveAttribute('disabled');
|
|
});
|
|
|
|
it('has accessible buttons with proper roles', () => {
|
|
render(<CommitMessageModal onCommitAndPush={mockOnCommitAndPush} />);
|
|
|
|
const cancelButton = screen.getByTestId('cancel-commit-message-button');
|
|
const commitButton = screen.getByTestId('confirm-commit-message-button');
|
|
|
|
// Mantine buttons are semantic HTML buttons
|
|
expect(cancelButton.tagName).toBe('BUTTON');
|
|
expect(commitButton.tagName).toBe('BUTTON');
|
|
});
|
|
});
|
|
});
|