feat: Redesign git changes to split stage/unstaged files. (#1359)
* feat: Redesign git changes to split stage/unstaged files. Signed-off-by: Paolo Insogna <paolo@cowtech.it> * fixup Signed-off-by: Paolo Insogna <paolo@cowtech.it> * fixup Signed-off-by: Paolo Insogna <paolo@cowtech.it> * refactor: streamline git changes panel * fix: label staged and working diff tabs * fix: isolate staged and working diff files * fix: scope staged and working diff updates * fix: scope git row revert to working changes --------- Signed-off-by: Paolo Insogna <paolo@cowtech.it> Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
This commit is contained in:
committed by
GitHub
co-authored by
Bohdan Triapitsyn
parent
9af0de0056
commit
e16097b05d
@@ -0,0 +1,112 @@
|
||||
import { describe, expect, test } from 'bun:test';
|
||||
import { stageGitFile, stageGitFiles, unstageGitFile, unstageGitFiles } from './gitApiHttp';
|
||||
|
||||
type FetchCall = {
|
||||
input: RequestInfo | URL;
|
||||
init?: RequestInit;
|
||||
};
|
||||
|
||||
const previousFetch = globalThis.fetch;
|
||||
const previousWindowDescriptor = Object.getOwnPropertyDescriptor(globalThis, 'window');
|
||||
|
||||
const installFetchMock = () => {
|
||||
const calls: FetchCall[] = [];
|
||||
globalThis.fetch = (async (input, init) => {
|
||||
calls.push({ input, init });
|
||||
return new Response(JSON.stringify({ success: true }), {
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}) as typeof fetch;
|
||||
return calls;
|
||||
};
|
||||
|
||||
const installWindowMock = () => {
|
||||
Object.defineProperty(globalThis, 'window', {
|
||||
configurable: true,
|
||||
value: {
|
||||
location: { origin: 'http://localhost:3000' },
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const restoreMocks = () => {
|
||||
globalThis.fetch = previousFetch;
|
||||
if (previousWindowDescriptor) {
|
||||
Object.defineProperty(globalThis, 'window', previousWindowDescriptor);
|
||||
} else {
|
||||
delete (globalThis as { window?: Window }).window;
|
||||
}
|
||||
};
|
||||
|
||||
const captureError = async (callback: () => Promise<void>): Promise<unknown> => {
|
||||
try {
|
||||
await callback();
|
||||
return null;
|
||||
} catch (error) {
|
||||
return error;
|
||||
}
|
||||
};
|
||||
|
||||
describe('gitApiHttp index mutations', () => {
|
||||
test('sends bulk stage payloads as paths', async () => {
|
||||
installWindowMock();
|
||||
const calls = installFetchMock();
|
||||
try {
|
||||
await stageGitFiles('/repo', ['a.ts', 'b.ts']);
|
||||
|
||||
expect(calls).toHaveLength(1);
|
||||
expect(String(calls[0].input)).toBe('http://localhost:3000/api/git/stage?directory=%2Frepo');
|
||||
expect(calls[0].init?.method).toBe('POST');
|
||||
expect(JSON.parse(String(calls[0].init?.body))).toEqual({ paths: ['a.ts', 'b.ts'] });
|
||||
} finally {
|
||||
restoreMocks();
|
||||
}
|
||||
});
|
||||
|
||||
test('sends bulk unstage payloads as paths', async () => {
|
||||
installWindowMock();
|
||||
const calls = installFetchMock();
|
||||
try {
|
||||
await unstageGitFiles('/repo', ['a.ts', 'b.ts']);
|
||||
|
||||
expect(calls).toHaveLength(1);
|
||||
expect(String(calls[0].input)).toBe('http://localhost:3000/api/git/unstage?directory=%2Frepo');
|
||||
expect(calls[0].init?.method).toBe('POST');
|
||||
expect(JSON.parse(String(calls[0].init?.body))).toEqual({ paths: ['a.ts', 'b.ts'] });
|
||||
} finally {
|
||||
restoreMocks();
|
||||
}
|
||||
});
|
||||
|
||||
test('single-file helpers use the bulk paths payload shape', async () => {
|
||||
installWindowMock();
|
||||
const calls = installFetchMock();
|
||||
try {
|
||||
await stageGitFile('/repo', 'a.ts');
|
||||
await unstageGitFile('/repo', 'b.ts');
|
||||
|
||||
expect(JSON.parse(String(calls[0].init?.body))).toEqual({ paths: ['a.ts'] });
|
||||
expect(JSON.parse(String(calls[1].init?.body))).toEqual({ paths: ['b.ts'] });
|
||||
} finally {
|
||||
restoreMocks();
|
||||
}
|
||||
});
|
||||
|
||||
test('rejects empty bulk path lists before fetching', async () => {
|
||||
installWindowMock();
|
||||
const calls = installFetchMock();
|
||||
try {
|
||||
const stageError = await captureError(() => stageGitFiles('/repo', [' ', '']));
|
||||
const unstageError = await captureError(() => unstageGitFiles('/repo', []));
|
||||
|
||||
expect(stageError).toBeInstanceOf(Error);
|
||||
expect((stageError as Error).message).toBe('path is required to stage git changes');
|
||||
expect(unstageError).toBeInstanceOf(Error);
|
||||
expect((unstageError as Error).message).toBe('path is required to unstage git changes');
|
||||
expect(calls).toHaveLength(0);
|
||||
} finally {
|
||||
restoreMocks();
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user