mirror of
https://github.com/lordmathis/lemma.git
synced 2025-11-05 23:44:22 +00:00
413 lines
12 KiB
TypeScript
413 lines
12 KiB
TypeScript
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
import { renderHook, act } from '@testing-library/react';
|
|
import { useGitOperations } from './useGitOperations';
|
|
import * as gitApi from '@/api/git';
|
|
|
|
// Mock dependencies
|
|
vi.mock('@/api/git');
|
|
vi.mock('@mantine/notifications', () => ({
|
|
notifications: {
|
|
show: vi.fn(),
|
|
},
|
|
}));
|
|
|
|
// Mock the workspace context
|
|
const mockWorkspaceData: {
|
|
currentWorkspace: { id: number; name: string; gitEnabled: boolean } | null;
|
|
} = {
|
|
currentWorkspace: {
|
|
id: 1,
|
|
name: 'test-workspace',
|
|
gitEnabled: true,
|
|
},
|
|
};
|
|
|
|
vi.mock('../contexts/WorkspaceDataContext', () => ({
|
|
useWorkspaceData: () => mockWorkspaceData,
|
|
}));
|
|
|
|
// Import notifications for assertions
|
|
import { notifications } from '@mantine/notifications';
|
|
|
|
describe('useGitOperations', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
// Reset workspace data to defaults
|
|
mockWorkspaceData.currentWorkspace = {
|
|
id: 1,
|
|
name: 'test-workspace',
|
|
gitEnabled: true,
|
|
};
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
describe('handlePull', () => {
|
|
it('pulls changes successfully and shows success notification', async () => {
|
|
const mockPullChanges = vi.mocked(gitApi.pullChanges);
|
|
mockPullChanges.mockResolvedValue('Successfully pulled latest changes');
|
|
|
|
const { result } = renderHook(() => useGitOperations());
|
|
|
|
let pullResult: boolean | undefined;
|
|
await act(async () => {
|
|
pullResult = await result.current.handlePull();
|
|
});
|
|
|
|
expect(pullResult).toBe(true);
|
|
expect(mockPullChanges).toHaveBeenCalledWith('test-workspace');
|
|
expect(notifications.show).toHaveBeenCalledWith({
|
|
title: 'Success',
|
|
message: 'Successfully pulled latest changes',
|
|
color: 'green',
|
|
});
|
|
});
|
|
|
|
it('handles pull errors and shows error notification', async () => {
|
|
const mockPullChanges = vi.mocked(gitApi.pullChanges);
|
|
const consoleSpy = vi
|
|
.spyOn(console, 'error')
|
|
.mockImplementation(() => {});
|
|
|
|
mockPullChanges.mockRejectedValue(new Error('Pull failed'));
|
|
|
|
const { result } = renderHook(() => useGitOperations());
|
|
|
|
let pullResult: boolean | undefined;
|
|
await act(async () => {
|
|
pullResult = await result.current.handlePull();
|
|
});
|
|
|
|
expect(pullResult).toBe(false);
|
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
'Failed to pull latest changes:',
|
|
expect.any(Error)
|
|
);
|
|
expect(notifications.show).toHaveBeenCalledWith({
|
|
title: 'Error',
|
|
message: 'Failed to pull latest changes',
|
|
color: 'red',
|
|
});
|
|
|
|
consoleSpy.mockRestore();
|
|
});
|
|
|
|
it('returns false when no workspace is available', async () => {
|
|
mockWorkspaceData.currentWorkspace = null;
|
|
|
|
const { result } = renderHook(() => useGitOperations());
|
|
|
|
let pullResult: boolean | undefined;
|
|
await act(async () => {
|
|
pullResult = await result.current.handlePull();
|
|
});
|
|
|
|
expect(pullResult).toBe(false);
|
|
expect(gitApi.pullChanges).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('returns false when git is disabled', async () => {
|
|
mockWorkspaceData.currentWorkspace!.gitEnabled = false;
|
|
|
|
const { result } = renderHook(() => useGitOperations());
|
|
|
|
let pullResult: boolean | undefined;
|
|
await act(async () => {
|
|
pullResult = await result.current.handlePull();
|
|
});
|
|
|
|
expect(pullResult).toBe(false);
|
|
expect(gitApi.pullChanges).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('handles pull with different response messages', async () => {
|
|
const mockPullChanges = vi.mocked(gitApi.pullChanges);
|
|
mockPullChanges.mockResolvedValue('Already up to date');
|
|
|
|
const { result } = renderHook(() => useGitOperations());
|
|
|
|
await act(async () => {
|
|
await result.current.handlePull();
|
|
});
|
|
|
|
expect(notifications.show).toHaveBeenCalledWith({
|
|
title: 'Success',
|
|
message: 'Already up to date',
|
|
color: 'green',
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('handleCommitAndPush', () => {
|
|
it('commits and pushes successfully with commit hash', async () => {
|
|
const mockCommitAndPush = vi.mocked(gitApi.commitAndPush);
|
|
mockCommitAndPush.mockResolvedValue('abc123def456');
|
|
|
|
const { result } = renderHook(() => useGitOperations());
|
|
|
|
await act(async () => {
|
|
await result.current.handleCommitAndPush('Add new feature');
|
|
});
|
|
|
|
expect(mockCommitAndPush).toHaveBeenCalledWith(
|
|
'test-workspace',
|
|
'Add new feature'
|
|
);
|
|
expect(notifications.show).toHaveBeenCalledWith({
|
|
title: 'Success',
|
|
message: 'Successfully committed and pushed changes abc123def456',
|
|
color: 'green',
|
|
});
|
|
});
|
|
|
|
it('handles commit errors and shows error notification', async () => {
|
|
const mockCommitAndPush = vi.mocked(gitApi.commitAndPush);
|
|
const consoleSpy = vi
|
|
.spyOn(console, 'error')
|
|
.mockImplementation(() => {});
|
|
|
|
mockCommitAndPush.mockRejectedValue(new Error('Commit failed'));
|
|
|
|
const { result } = renderHook(() => useGitOperations());
|
|
|
|
await act(async () => {
|
|
await result.current.handleCommitAndPush('Failed commit');
|
|
});
|
|
|
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
'Failed to commit and push changes:',
|
|
expect.any(Error)
|
|
);
|
|
expect(notifications.show).toHaveBeenCalledWith({
|
|
title: 'Error',
|
|
message: 'Failed to commit and push changes',
|
|
color: 'red',
|
|
});
|
|
|
|
consoleSpy.mockRestore();
|
|
});
|
|
|
|
it('does nothing when no workspace is available', async () => {
|
|
mockWorkspaceData.currentWorkspace = null;
|
|
|
|
const { result } = renderHook(() => useGitOperations());
|
|
|
|
await act(async () => {
|
|
await result.current.handleCommitAndPush('Test commit');
|
|
});
|
|
|
|
expect(gitApi.commitAndPush).not.toHaveBeenCalled();
|
|
expect(notifications.show).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('does nothing when git is disabled', async () => {
|
|
mockWorkspaceData.currentWorkspace!.gitEnabled = false;
|
|
|
|
const { result } = renderHook(() => useGitOperations());
|
|
|
|
await act(async () => {
|
|
await result.current.handleCommitAndPush('Test commit');
|
|
});
|
|
|
|
expect(gitApi.commitAndPush).not.toHaveBeenCalled();
|
|
expect(notifications.show).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('handles empty commit messages', async () => {
|
|
const mockCommitAndPush = vi.mocked(gitApi.commitAndPush);
|
|
mockCommitAndPush.mockResolvedValue('xyz789abc123');
|
|
|
|
const { result } = renderHook(() => useGitOperations());
|
|
|
|
await act(async () => {
|
|
await result.current.handleCommitAndPush('');
|
|
});
|
|
|
|
expect(mockCommitAndPush).toHaveBeenCalledWith('test-workspace', '');
|
|
expect(notifications.show).toHaveBeenCalledWith({
|
|
title: 'Success',
|
|
message: 'Successfully committed and pushed changes xyz789abc123',
|
|
color: 'green',
|
|
});
|
|
});
|
|
|
|
it('handles long commit messages', async () => {
|
|
const mockCommitAndPush = vi.mocked(gitApi.commitAndPush);
|
|
mockCommitAndPush.mockResolvedValue('longcommithash123456789');
|
|
|
|
const longMessage =
|
|
'This is a very long commit message that describes in detail all the changes that were made to the codebase including bug fixes, new features, and documentation updates';
|
|
|
|
const { result } = renderHook(() => useGitOperations());
|
|
|
|
await act(async () => {
|
|
await result.current.handleCommitAndPush(longMessage);
|
|
});
|
|
|
|
expect(mockCommitAndPush).toHaveBeenCalledWith(
|
|
'test-workspace',
|
|
longMessage
|
|
);
|
|
expect(notifications.show).toHaveBeenCalledWith({
|
|
title: 'Success',
|
|
message:
|
|
'Successfully committed and pushed changes longcommithash123456789',
|
|
color: 'green',
|
|
});
|
|
});
|
|
|
|
it('handles commit with special characters in message', async () => {
|
|
const mockCommitAndPush = vi.mocked(gitApi.commitAndPush);
|
|
mockCommitAndPush.mockResolvedValue('special123hash');
|
|
|
|
const specialMessage =
|
|
'Fix: update file with special chars àáâãäå & symbols!@#$%';
|
|
|
|
const { result } = renderHook(() => useGitOperations());
|
|
|
|
await act(async () => {
|
|
await result.current.handleCommitAndPush(specialMessage);
|
|
});
|
|
|
|
expect(mockCommitAndPush).toHaveBeenCalledWith(
|
|
'test-workspace',
|
|
specialMessage
|
|
);
|
|
expect(notifications.show).toHaveBeenCalledWith({
|
|
title: 'Success',
|
|
message: 'Successfully committed and pushed changes special123hash',
|
|
color: 'green',
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('workspace and settings dependencies', () => {
|
|
it('handles workspace changes correctly', async () => {
|
|
const mockPullChanges = vi.mocked(gitApi.pullChanges);
|
|
mockPullChanges.mockResolvedValue('Success');
|
|
|
|
const { result, rerender } = renderHook(() => useGitOperations());
|
|
|
|
// Test with initial workspace
|
|
await act(async () => {
|
|
await result.current.handlePull();
|
|
});
|
|
|
|
expect(mockPullChanges).toHaveBeenCalledWith('test-workspace');
|
|
|
|
// Change workspace
|
|
mockWorkspaceData.currentWorkspace = {
|
|
id: 2,
|
|
name: 'different-workspace',
|
|
gitEnabled: true,
|
|
};
|
|
|
|
rerender();
|
|
|
|
await act(async () => {
|
|
await result.current.handlePull();
|
|
});
|
|
|
|
expect(mockPullChanges).toHaveBeenCalledWith('different-workspace');
|
|
});
|
|
|
|
it('handles git settings changes correctly', async () => {
|
|
const { result, rerender } = renderHook(() => useGitOperations());
|
|
|
|
// Initially git is enabled
|
|
expect(mockWorkspaceData.currentWorkspace!.gitEnabled).toBe(true);
|
|
|
|
// Disable git
|
|
mockWorkspaceData.currentWorkspace!.gitEnabled = false;
|
|
rerender();
|
|
|
|
let pullResult: boolean | undefined;
|
|
await act(async () => {
|
|
pullResult = await result.current.handlePull();
|
|
});
|
|
|
|
expect(pullResult).toBe(false);
|
|
expect(gitApi.pullChanges).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('hook interface', () => {
|
|
it('returns correct function interface', () => {
|
|
const { result } = renderHook(() => useGitOperations());
|
|
|
|
expect(typeof result.current.handlePull).toBe('function');
|
|
expect(typeof result.current.handleCommitAndPush).toBe('function');
|
|
});
|
|
|
|
it('functions are stable across re-renders', () => {
|
|
const { result, rerender } = renderHook(() => useGitOperations());
|
|
|
|
const initialHandlers = {
|
|
handlePull: result.current.handlePull,
|
|
handleCommitAndPush: result.current.handleCommitAndPush,
|
|
};
|
|
|
|
rerender();
|
|
|
|
expect(result.current.handlePull).toBe(initialHandlers.handlePull);
|
|
expect(result.current.handleCommitAndPush).toBe(
|
|
initialHandlers.handleCommitAndPush
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('edge cases', () => {
|
|
it('handles null workspace gracefully', async () => {
|
|
mockWorkspaceData.currentWorkspace = null;
|
|
|
|
const { result } = renderHook(() => useGitOperations());
|
|
|
|
let pullResult: boolean | undefined;
|
|
await act(async () => {
|
|
pullResult = await result.current.handlePull();
|
|
});
|
|
|
|
expect(pullResult).toBe(false);
|
|
expect(gitApi.pullChanges).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('handles undefined workspace name gracefully', async () => {
|
|
mockWorkspaceData.currentWorkspace = {
|
|
id: 1,
|
|
name: undefined!,
|
|
gitEnabled: true,
|
|
};
|
|
|
|
const { result } = renderHook(() => useGitOperations());
|
|
|
|
let pullResult: boolean | undefined;
|
|
await act(async () => {
|
|
pullResult = await result.current.handlePull();
|
|
});
|
|
|
|
expect(pullResult).toBe(false);
|
|
expect(gitApi.pullChanges).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('handles missing settings gracefully', async () => {
|
|
mockWorkspaceData.currentWorkspace = {
|
|
id: 1,
|
|
name: 'test-workspace',
|
|
gitEnabled: undefined!,
|
|
};
|
|
|
|
const { result } = renderHook(() => useGitOperations());
|
|
|
|
let pullResult: boolean | undefined;
|
|
await act(async () => {
|
|
pullResult = await result.current.handlePull();
|
|
});
|
|
|
|
expect(pullResult).toBe(false);
|
|
expect(gitApi.pullChanges).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|