fix(git): git-status IPC returns a flat array, not wrapped object

GitOperations.getStatus returns { files: [...] } but the renderer's
ipc.file.gitStatus type declares Array<...> and calls result.data.map().
The mismatch made GitStatusPanel.tsx throw 'n.map is not a function'
on mount, which blanked the entire React tree.

Unwrap result.files in the IPC handler so it returns the array the
renderer expects. Add tests covering the success, empty, and non-git
branches so this regression cannot recur.

Refs: blank-screen-on-md-open
This commit is contained in:
2026-07-23 09:34:57 +05:30
parent ec3d53ea7e
commit 772a791d9a
2 changed files with 74 additions and 1 deletions
+2 -1
View File
@@ -8,7 +8,8 @@ function register(currentFileRef) {
const dir =
rootPath ||
(currentFileRef.current ? require('path').dirname(currentFileRef.current) : process.cwd());
return GitOperations.getStatus(dir);
const result = GitOperations.getStatus(dir);
return Array.isArray(result?.files) ? result.files : [];
});
ipcMain.handle('git-stage', async (_event, { rootPath, files }) => {
+72
View File
@@ -0,0 +1,72 @@
/**
* Regression: src/main/files/git.js must return a flat array from the
* 'git-status' handler so that the renderer's `result.data.map(...)` works.
* Previously the handler returned `{ files: [...] }` and the renderer blew
* up with `n.map is not a function`, blanking the entire UI.
*/
const EventEmitter = require('events');
class FakeIpcMain extends EventEmitter {
constructor() {
super();
this.handlers = new Map();
}
handle(channel, handler) {
this.handlers.set(channel, handler);
}
}
describe('files/git IPC handler', () => {
let ipc;
let register;
let currentFileRef;
beforeEach(() => {
jest.resetModules();
ipc = new FakeIpcMain();
currentFileRef = { current: null };
jest.doMock('electron', () => ({ ipcMain: ipc }));
jest.doMock('../../../../src/main/GitOperations', () => ({
getStatus: () => ({ files: [{ filePath: 'a.md', status: 'modified' }] }),
}));
({ register } = require('../../../../src/main/files/git'));
});
test('git-status returns a flat array, not a wrapped object', async () => {
register(currentFileRef);
const handler = ipc.handlers.get('git-status');
const result = await handler({}, '/repo');
expect(Array.isArray(result)).toBe(true);
expect(result).toEqual([{ filePath: 'a.md', status: 'modified' }]);
});
test('git-status falls back to [] when GitOperations returns no files', async () => {
jest.resetModules();
ipc = new FakeIpcMain();
jest.doMock('electron', () => ({ ipcMain: ipc }));
jest.doMock('../../../../src/main/GitOperations', () => ({
getStatus: () => ({ files: undefined }),
}));
({ register } = require('../../../../src/main/files/git'));
register(currentFileRef);
const handler = ipc.handlers.get('git-status');
const result = await handler({}, '/repo');
expect(Array.isArray(result)).toBe(true);
expect(result).toEqual([]);
});
test('git-status returns [] for non-git directories (error path)', async () => {
jest.resetModules();
ipc = new FakeIpcMain();
jest.doMock('electron', () => ({ ipcMain: ipc }));
jest.doMock('../../../../src/main/GitOperations', () => ({
getStatus: () => ({ files: [], error: 'Not a git repository' }),
}));
({ register } = require('../../../../src/main/files/git'));
register(currentFileRef);
const handler = ipc.handlers.get('git-status');
const result = await handler({}, '/repo');
expect(Array.isArray(result)).toBe(true);
expect(result).toEqual([]);
});
});