fix: added gitignore handling
This commit is contained in:
@@ -11,14 +11,16 @@ const mockCommit = vi.fn();
|
||||
const mockGetRemotes = vi.fn();
|
||||
const mockAddRemote = vi.fn();
|
||||
const mockRemote = vi.fn();
|
||||
const { mockReadFile, mockStat } = vi.hoisted(() => ({
|
||||
const { mockReadFile, mockStat, mockWriteFile } = vi.hoisted(() => ({
|
||||
mockReadFile: vi.fn(),
|
||||
mockStat: vi.fn(),
|
||||
mockWriteFile: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('fs/promises', () => ({
|
||||
readFile: mockReadFile,
|
||||
stat: mockStat,
|
||||
writeFile: mockWriteFile,
|
||||
default: {},
|
||||
}));
|
||||
|
||||
@@ -47,6 +49,7 @@ describe('GitEngine', () => {
|
||||
vi.clearAllMocks();
|
||||
mockReadFile.mockRejectedValue(new Error('ENOENT'));
|
||||
mockStat.mockResolvedValue({});
|
||||
mockWriteFile.mockResolvedValue(undefined);
|
||||
mockCheckIsRepo.mockResolvedValue(false);
|
||||
gitEngine = new GitEngine();
|
||||
});
|
||||
@@ -136,6 +139,48 @@ describe('GitEngine', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('ensureGitignore', () => {
|
||||
it('should create .gitignore with default system metadata entries when missing', async () => {
|
||||
mockReadFile.mockRejectedValue(new Error('ENOENT'));
|
||||
|
||||
const result = await gitEngine.ensureGitignore('/tmp/project');
|
||||
|
||||
expect(result.updated).toBe(true);
|
||||
expect(result.created).toBe(true);
|
||||
expect(result.addedEntries.length).toBeGreaterThan(0);
|
||||
expect(mockWriteFile).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should append missing entries when .gitignore exists but is incomplete', async () => {
|
||||
mockReadFile.mockResolvedValue('node_modules/\n.DS_Store\n');
|
||||
|
||||
const result = await gitEngine.ensureGitignore('/tmp/project');
|
||||
|
||||
expect(result.updated).toBe(true);
|
||||
expect(result.created).toBe(false);
|
||||
expect(result.addedEntries).toContain('Thumbs.db');
|
||||
expect(mockWriteFile).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should not rewrite .gitignore when all default entries already exist', async () => {
|
||||
mockReadFile.mockResolvedValue([
|
||||
'.DS_Store',
|
||||
'Thumbs.db',
|
||||
'Desktop.ini',
|
||||
'$RECYCLE.BIN/',
|
||||
'.Spotlight-V100/',
|
||||
'.Trashes/',
|
||||
'._*',
|
||||
'.fseventsd',
|
||||
].join('\n'));
|
||||
|
||||
const result = await gitEngine.ensureGitignore('/tmp/project');
|
||||
|
||||
expect(result).toEqual({ updated: false, created: false, addedEntries: [] });
|
||||
expect(mockWriteFile).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('initializeRepo', () => {
|
||||
it('should emit detailed progress updates throughout initialization', async () => {
|
||||
mockVersion.mockResolvedValue({ major: 2, minor: 49, patch: 0, agent: 'git/version', installed: true });
|
||||
|
||||
@@ -145,6 +145,7 @@ const mockGitEngine = {
|
||||
getRepoState: vi.fn(),
|
||||
getStatus: vi.fn(),
|
||||
initializeRepo: vi.fn(),
|
||||
ensureGitignore: vi.fn(),
|
||||
};
|
||||
|
||||
const mockTaskManager = {
|
||||
@@ -359,6 +360,25 @@ describe('IPC Handlers', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('git:ensureGitignore', () => {
|
||||
it('should pass project path to GitEngine.ensureGitignore', async () => {
|
||||
mockGitEngine.ensureGitignore.mockResolvedValue({
|
||||
updated: true,
|
||||
created: false,
|
||||
addedEntries: ['Thumbs.db'],
|
||||
});
|
||||
|
||||
const result = await invokeHandler('git:ensureGitignore', '/repo');
|
||||
|
||||
expect(mockGitEngine.ensureGitignore).toHaveBeenCalledWith('/repo');
|
||||
expect(result).toEqual({
|
||||
updated: true,
|
||||
created: false,
|
||||
addedEntries: ['Thumbs.db'],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ============ Project Handlers ============
|
||||
|
||||
@@ -29,11 +29,20 @@ describe('GitSidebar', () => {
|
||||
checkAvailability: vi.fn().mockResolvedValue({ gitFound: true, version: '2.49.0' }),
|
||||
getRepoState: vi.fn().mockResolvedValue({ isRepo: false, hasRemote: false }),
|
||||
init: vi.fn().mockResolvedValue({ success: true }),
|
||||
ensureGitignore: vi.fn().mockResolvedValue({ updated: false, created: false, addedEntries: [] }),
|
||||
onInitProgress: vi.fn().mockImplementation(() => () => {}),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
it('checks gitignore defaults when sidebar loads', async () => {
|
||||
render(<GitSidebar />);
|
||||
|
||||
await screen.findByRole('button', { name: /initialize git/i });
|
||||
|
||||
expect((window as any).electronAPI.git.ensureGitignore).toHaveBeenCalledWith('/repo/path');
|
||||
});
|
||||
|
||||
it('shows Initialize Git button when active project is not a git repository', async () => {
|
||||
render(<GitSidebar />);
|
||||
|
||||
@@ -139,7 +148,7 @@ describe('GitSidebar', () => {
|
||||
subscription({ message: 'Staging project files...', progress: 75 });
|
||||
});
|
||||
|
||||
expect(screen.getByText(/75% — staging project files/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/staging project files\.\.\.\s*\(75%\)/i)).toBeInTheDocument();
|
||||
|
||||
await act(async () => {
|
||||
resolveInit?.({ success: true });
|
||||
@@ -170,7 +179,16 @@ describe('GitSidebar', () => {
|
||||
subscription({ message: 'Initializing repository...', progress: 15 });
|
||||
});
|
||||
|
||||
expect(screen.getByText(/initialization transcript/i)).toBeInTheDocument();
|
||||
const transcriptToggle = screen.getByRole('button', { name: /initialization transcript/i });
|
||||
expect(transcriptToggle).toBeInTheDocument();
|
||||
expect(transcriptToggle).toHaveAttribute('aria-expanded', 'false');
|
||||
expect(screen.queryByText(/5% — checking git availability/i)).not.toBeInTheDocument();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(transcriptToggle);
|
||||
});
|
||||
|
||||
expect(transcriptToggle).toHaveAttribute('aria-expanded', 'true');
|
||||
expect(screen.getByText(/5% — checking git availability/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/15% — initializing repository/i)).toBeInTheDocument();
|
||||
|
||||
@@ -178,4 +196,19 @@ describe('GitSidebar', () => {
|
||||
resolveInit?.({ success: true });
|
||||
});
|
||||
});
|
||||
|
||||
it('auto-expands transcript when a failed progress event is received', async () => {
|
||||
render(<GitSidebar />);
|
||||
|
||||
const onInitProgressMock = (window as any).electronAPI.git.onInitProgress as ReturnType<typeof vi.fn>;
|
||||
const subscription = onInitProgressMock.mock.calls[0][0] as (payload: { phase: string; message: string; progress: number }) => void;
|
||||
|
||||
await act(async () => {
|
||||
subscription({ phase: 'failed', message: 'Failed to configure remote repository.', progress: 100 });
|
||||
});
|
||||
|
||||
const transcriptToggle = screen.getByRole('button', { name: /initialization transcript/i });
|
||||
expect(transcriptToggle).toHaveAttribute('aria-expanded', 'true');
|
||||
expect(screen.getByText(/100% — failed to configure remote repository/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user