difit 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/cli/index.js +0 -0
- package/dist/client/assets/index-W2UC55JC.css +1 -0
- package/dist/client/assets/index-hiGBtmpa.js +142 -0
- package/dist/client/index.html +2 -2
- package/dist/server/comment-store.js +85 -8
- package/dist/server/git-diff.js +3 -1
- package/dist/server/server.js +10 -0
- package/package.json +4 -4
- package/dist/cli/index.d.ts +0 -2
- package/dist/cli/index.test.d.ts +0 -1
- package/dist/cli/index.test.js +0 -676
- package/dist/cli/utils.d.ts +0 -1
- package/dist/cli/utils.test.d.ts +0 -1
- package/dist/cli/utils.test.js +0 -214
- package/dist/client/assets/index-CSJzfcU-.css +0 -1
- package/dist/client/assets/index-IxkylgHX.js +0 -53
- package/dist/client/assets/prism-css-Bpx-unsJ.js +0 -1
- package/dist/client/assets/prism-json-xwnKirkR.js +0 -1
- package/dist/client/assets/prism-typescript-B2PMeEx1.js +0 -1
- package/dist/server/comment-store.d.ts +0 -13
- package/dist/server/git-diff-tui.d.ts +0 -2
- package/dist/server/git-diff-tui.js +0 -95
- package/dist/server/git-diff.d.ts +0 -10
- package/dist/server/git-diff.test.d.ts +0 -1
- package/dist/server/git-diff.test.js +0 -292
- package/dist/server/server.d.ts +0 -11
- package/dist/server/server.test.d.ts +0 -1
- package/dist/server/server.test.js +0 -382
- package/dist/tui/App.d.ts +0 -8
- package/dist/tui/App.js +0 -92
- package/dist/tui/App.test.d.ts +0 -1
- package/dist/tui/App.test.js +0 -31
- package/dist/tui/components/DiffViewer.d.ts +0 -9
- package/dist/tui/components/DiffViewer.js +0 -88
- package/dist/tui/components/FileList.d.ts +0 -8
- package/dist/tui/components/FileList.js +0 -48
- package/dist/tui/components/SideBySideDiffViewer.d.ts +0 -9
- package/dist/tui/components/SideBySideDiffViewer.js +0 -237
- package/dist/tui/components/StatusBar.d.ts +0 -8
- package/dist/tui/components/StatusBar.js +0 -23
- package/dist/tui/utils/parseDiff.d.ts +0 -2
- package/dist/tui/utils/parseDiff.js +0 -68
- package/dist/types/diff.d.ts +0 -33
- package/dist/utils/fileUtils.d.ts +0 -12
- package/dist/utils/fileUtils.js +0 -21
package/dist/cli/index.test.js
DELETED
|
@@ -1,676 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import React from 'react';
|
|
3
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
4
|
-
// Mock all external dependencies
|
|
5
|
-
vi.mock('simple-git');
|
|
6
|
-
vi.mock('../server/server.js');
|
|
7
|
-
vi.mock('./utils.js', async () => {
|
|
8
|
-
const actual = await vi.importActual('./utils.js');
|
|
9
|
-
return {
|
|
10
|
-
...actual,
|
|
11
|
-
promptUser: vi.fn(),
|
|
12
|
-
findUntrackedFiles: vi.fn(),
|
|
13
|
-
markFilesIntentToAdd: vi.fn(),
|
|
14
|
-
resolvePrCommits: vi.fn(),
|
|
15
|
-
};
|
|
16
|
-
});
|
|
17
|
-
const { simpleGit } = await import('simple-git');
|
|
18
|
-
const { startServer } = await import('../server/server.js');
|
|
19
|
-
const { promptUser, findUntrackedFiles, markFilesIntentToAdd, resolvePrCommits } = await import('./utils.js');
|
|
20
|
-
describe('CLI index.ts', () => {
|
|
21
|
-
let mockGit;
|
|
22
|
-
let mockStartServer;
|
|
23
|
-
let mockPromptUser;
|
|
24
|
-
let mockFindUntrackedFiles;
|
|
25
|
-
let mockMarkFilesIntentToAdd;
|
|
26
|
-
let mockResolvePrCommits;
|
|
27
|
-
// Store original console methods
|
|
28
|
-
let originalConsoleLog;
|
|
29
|
-
let originalConsoleError;
|
|
30
|
-
let originalProcessExit;
|
|
31
|
-
beforeEach(() => {
|
|
32
|
-
// Setup mocks
|
|
33
|
-
mockGit = {
|
|
34
|
-
status: vi.fn(),
|
|
35
|
-
add: vi.fn(),
|
|
36
|
-
};
|
|
37
|
-
vi.mocked(simpleGit).mockReturnValue(mockGit);
|
|
38
|
-
mockStartServer = vi.mocked(startServer);
|
|
39
|
-
mockStartServer.mockResolvedValue({
|
|
40
|
-
port: 3000,
|
|
41
|
-
url: 'http://localhost:3000',
|
|
42
|
-
isEmpty: false,
|
|
43
|
-
});
|
|
44
|
-
mockPromptUser = vi.mocked(promptUser);
|
|
45
|
-
mockFindUntrackedFiles = vi.mocked(findUntrackedFiles);
|
|
46
|
-
mockMarkFilesIntentToAdd = vi.mocked(markFilesIntentToAdd);
|
|
47
|
-
mockResolvePrCommits = vi.mocked(resolvePrCommits);
|
|
48
|
-
// Mock console and process.exit
|
|
49
|
-
originalConsoleLog = console.log;
|
|
50
|
-
originalConsoleError = console.error;
|
|
51
|
-
originalProcessExit = process.exit;
|
|
52
|
-
console.log = vi.fn();
|
|
53
|
-
console.error = vi.fn();
|
|
54
|
-
process.exit = vi.fn();
|
|
55
|
-
// Reset all mocks
|
|
56
|
-
vi.clearAllMocks();
|
|
57
|
-
});
|
|
58
|
-
afterEach(() => {
|
|
59
|
-
// Restore original methods
|
|
60
|
-
console.log = originalConsoleLog;
|
|
61
|
-
console.error = originalConsoleError;
|
|
62
|
-
process.exit = originalProcessExit;
|
|
63
|
-
});
|
|
64
|
-
describe('CLI argument processing', () => {
|
|
65
|
-
it.each([
|
|
66
|
-
{
|
|
67
|
-
name: 'default arguments',
|
|
68
|
-
args: [],
|
|
69
|
-
expectedTarget: 'HEAD',
|
|
70
|
-
expectedBase: 'HEAD^',
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
name: 'single commit argument',
|
|
74
|
-
args: ['main'],
|
|
75
|
-
expectedTarget: 'main',
|
|
76
|
-
expectedBase: 'main^',
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
name: 'two commit arguments',
|
|
80
|
-
args: ['main', 'develop'],
|
|
81
|
-
expectedTarget: 'main',
|
|
82
|
-
expectedBase: 'develop',
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
name: 'special: working',
|
|
86
|
-
args: ['working'],
|
|
87
|
-
expectedTarget: 'working',
|
|
88
|
-
expectedBase: 'staged',
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
name: 'special: staged',
|
|
92
|
-
args: ['staged'],
|
|
93
|
-
expectedTarget: 'staged',
|
|
94
|
-
expectedBase: 'HEAD',
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
name: 'special: dot',
|
|
98
|
-
args: ['.'],
|
|
99
|
-
expectedTarget: '.',
|
|
100
|
-
expectedBase: 'HEAD',
|
|
101
|
-
},
|
|
102
|
-
])('$name', async ({ args, expectedTarget, expectedBase }) => {
|
|
103
|
-
mockFindUntrackedFiles.mockResolvedValue([]);
|
|
104
|
-
const program = new Command();
|
|
105
|
-
// Simulate command execution
|
|
106
|
-
program
|
|
107
|
-
.argument('[commit-ish]', 'commit-ish', 'HEAD')
|
|
108
|
-
.argument('[compare-with]', 'compare-with')
|
|
109
|
-
.option('--port <port>', 'port', parseInt)
|
|
110
|
-
.option('--host <host>', 'host', '127.0.0.1')
|
|
111
|
-
.option('--no-open', 'no-open')
|
|
112
|
-
.option('--mode <mode>', 'mode', 'side-by-side')
|
|
113
|
-
.option('--tui', 'tui')
|
|
114
|
-
.option('--pr <url>', 'pr')
|
|
115
|
-
.action(async (commitish, _compareWith, options) => {
|
|
116
|
-
// Simulate the logic from index.ts
|
|
117
|
-
let targetCommitish = commitish;
|
|
118
|
-
let baseCommitish;
|
|
119
|
-
if (_compareWith) {
|
|
120
|
-
baseCommitish = _compareWith;
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
if (commitish === 'working') {
|
|
124
|
-
baseCommitish = 'staged';
|
|
125
|
-
}
|
|
126
|
-
else if (commitish === 'staged' || commitish === '.') {
|
|
127
|
-
baseCommitish = 'HEAD';
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
baseCommitish = commitish + '^';
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
if (commitish === 'working' || commitish === '.') {
|
|
134
|
-
const git = simpleGit();
|
|
135
|
-
await findUntrackedFiles(git);
|
|
136
|
-
// Skip prompt logic for test
|
|
137
|
-
}
|
|
138
|
-
await startServer({
|
|
139
|
-
targetCommitish,
|
|
140
|
-
baseCommitish,
|
|
141
|
-
preferredPort: options.port,
|
|
142
|
-
host: options.host,
|
|
143
|
-
openBrowser: options.open,
|
|
144
|
-
mode: options.mode,
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
await program.parseAsync([...args], { from: 'user' });
|
|
148
|
-
expect(mockStartServer).toHaveBeenCalledWith({
|
|
149
|
-
targetCommitish: expectedTarget,
|
|
150
|
-
baseCommitish: expectedBase,
|
|
151
|
-
preferredPort: undefined,
|
|
152
|
-
host: '127.0.0.1',
|
|
153
|
-
openBrowser: true,
|
|
154
|
-
mode: 'side-by-side',
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
});
|
|
158
|
-
describe('CLI options', () => {
|
|
159
|
-
it.each([
|
|
160
|
-
{
|
|
161
|
-
name: '--port option',
|
|
162
|
-
args: ['--port', '4000'],
|
|
163
|
-
expectedOptions: { port: 4000 },
|
|
164
|
-
},
|
|
165
|
-
{
|
|
166
|
-
name: '--host option',
|
|
167
|
-
args: ['--host', '0.0.0.0'],
|
|
168
|
-
expectedOptions: { host: '0.0.0.0' },
|
|
169
|
-
},
|
|
170
|
-
{
|
|
171
|
-
name: '--no-open option',
|
|
172
|
-
args: ['--no-open'],
|
|
173
|
-
expectedOptions: { open: false },
|
|
174
|
-
},
|
|
175
|
-
{
|
|
176
|
-
name: '--mode option',
|
|
177
|
-
args: ['--mode', 'inline'],
|
|
178
|
-
expectedOptions: { mode: 'inline' },
|
|
179
|
-
},
|
|
180
|
-
])('$name', async ({ args, expectedOptions }) => {
|
|
181
|
-
mockFindUntrackedFiles.mockResolvedValue([]);
|
|
182
|
-
const program = new Command();
|
|
183
|
-
program
|
|
184
|
-
.argument('[commit-ish]', 'commit-ish', 'HEAD')
|
|
185
|
-
.argument('[compare-with]', 'compare-with')
|
|
186
|
-
.option('--port <port>', 'port', parseInt)
|
|
187
|
-
.option('--host <host>', 'host', '127.0.0.1')
|
|
188
|
-
.option('--no-open', 'no-open')
|
|
189
|
-
.option('--mode <mode>', 'mode', 'side-by-side')
|
|
190
|
-
.option('--tui', 'tui')
|
|
191
|
-
.option('--pr <url>', 'pr')
|
|
192
|
-
.action(async (commitish, _compareWith, options) => {
|
|
193
|
-
let targetCommitish = commitish;
|
|
194
|
-
let baseCommitish = commitish + '^';
|
|
195
|
-
await startServer({
|
|
196
|
-
targetCommitish,
|
|
197
|
-
baseCommitish,
|
|
198
|
-
preferredPort: options.port,
|
|
199
|
-
host: options.host,
|
|
200
|
-
openBrowser: options.open,
|
|
201
|
-
mode: options.mode,
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
await program.parseAsync([...args], { from: 'user' });
|
|
205
|
-
const expectedCall = {
|
|
206
|
-
targetCommitish: 'HEAD',
|
|
207
|
-
baseCommitish: 'HEAD^',
|
|
208
|
-
preferredPort: expectedOptions.port,
|
|
209
|
-
host: expectedOptions.host || '127.0.0.1',
|
|
210
|
-
openBrowser: expectedOptions.open !== false,
|
|
211
|
-
mode: expectedOptions.mode || 'side-by-side',
|
|
212
|
-
};
|
|
213
|
-
expect(mockStartServer).toHaveBeenCalledWith(expectedCall);
|
|
214
|
-
});
|
|
215
|
-
});
|
|
216
|
-
describe('Git operations', () => {
|
|
217
|
-
it('handles untracked files for working directory', async () => {
|
|
218
|
-
const untrackedFiles = ['file1.js', 'file2.js'];
|
|
219
|
-
mockFindUntrackedFiles.mockResolvedValue(untrackedFiles);
|
|
220
|
-
mockPromptUser.mockResolvedValue(true);
|
|
221
|
-
mockMarkFilesIntentToAdd.mockResolvedValue(undefined);
|
|
222
|
-
const program = new Command();
|
|
223
|
-
program
|
|
224
|
-
.argument('[commit-ish]', 'commit-ish', 'HEAD')
|
|
225
|
-
.argument('[compare-with]', 'compare-with')
|
|
226
|
-
.option('--port <port>', 'port', parseInt)
|
|
227
|
-
.option('--host <host>', 'host', '127.0.0.1')
|
|
228
|
-
.option('--no-open', 'no-open')
|
|
229
|
-
.option('--mode <mode>', 'mode', 'side-by-side')
|
|
230
|
-
.option('--tui', 'tui')
|
|
231
|
-
.option('--pr <url>', 'pr')
|
|
232
|
-
.action(async (commitish, _compareWith, options) => {
|
|
233
|
-
if (commitish === 'working' || commitish === '.') {
|
|
234
|
-
const git = simpleGit();
|
|
235
|
-
await findUntrackedFiles(git);
|
|
236
|
-
// Skip prompt logic for test
|
|
237
|
-
}
|
|
238
|
-
await startServer({
|
|
239
|
-
targetCommitish: commitish,
|
|
240
|
-
baseCommitish: 'staged',
|
|
241
|
-
preferredPort: options.port,
|
|
242
|
-
host: options.host,
|
|
243
|
-
openBrowser: options.open,
|
|
244
|
-
mode: options.mode,
|
|
245
|
-
});
|
|
246
|
-
});
|
|
247
|
-
await program.parseAsync(['working'], { from: 'user' });
|
|
248
|
-
expect(mockFindUntrackedFiles).toHaveBeenCalledWith(mockGit);
|
|
249
|
-
// Note: The actual CLI uses promptUserToIncludeUntracked, not promptUser directly
|
|
250
|
-
// This test verifies the Git interaction pattern
|
|
251
|
-
});
|
|
252
|
-
it('skips untracked file handling for regular commits', async () => {
|
|
253
|
-
const program = new Command();
|
|
254
|
-
program
|
|
255
|
-
.argument('[commit-ish]', 'commit-ish', 'HEAD')
|
|
256
|
-
.argument('[compare-with]', 'compare-with')
|
|
257
|
-
.option('--port <port>', 'port', parseInt)
|
|
258
|
-
.option('--host <host>', 'host', '127.0.0.1')
|
|
259
|
-
.option('--no-open', 'no-open')
|
|
260
|
-
.option('--mode <mode>', 'mode', 'side-by-side')
|
|
261
|
-
.option('--tui', 'tui')
|
|
262
|
-
.option('--pr <url>', 'pr')
|
|
263
|
-
.action(async (commitish, _compareWith, options) => {
|
|
264
|
-
if (commitish === 'working' || commitish === '.') {
|
|
265
|
-
const git = simpleGit();
|
|
266
|
-
await findUntrackedFiles(git);
|
|
267
|
-
}
|
|
268
|
-
await startServer({
|
|
269
|
-
targetCommitish: commitish,
|
|
270
|
-
baseCommitish: commitish + '^',
|
|
271
|
-
preferredPort: options.port,
|
|
272
|
-
host: options.host,
|
|
273
|
-
openBrowser: options.open,
|
|
274
|
-
mode: options.mode,
|
|
275
|
-
});
|
|
276
|
-
});
|
|
277
|
-
await program.parseAsync(['HEAD'], { from: 'user' });
|
|
278
|
-
expect(mockFindUntrackedFiles).not.toHaveBeenCalled();
|
|
279
|
-
});
|
|
280
|
-
});
|
|
281
|
-
describe('GitHub PR integration', () => {
|
|
282
|
-
it('resolves PR commits correctly', async () => {
|
|
283
|
-
const prUrl = 'https://github.com/owner/repo/pull/123';
|
|
284
|
-
const prCommits = {
|
|
285
|
-
targetCommitish: 'abc123',
|
|
286
|
-
baseCommitish: 'def456',
|
|
287
|
-
};
|
|
288
|
-
mockResolvePrCommits.mockResolvedValue(prCommits);
|
|
289
|
-
const program = new Command();
|
|
290
|
-
program
|
|
291
|
-
.argument('[commit-ish]', 'commit-ish', 'HEAD')
|
|
292
|
-
.argument('[compare-with]', 'compare-with')
|
|
293
|
-
.option('--port <port>', 'port', parseInt)
|
|
294
|
-
.option('--host <host>', 'host', '127.0.0.1')
|
|
295
|
-
.option('--no-open', 'no-open')
|
|
296
|
-
.option('--mode <mode>', 'mode', 'side-by-side')
|
|
297
|
-
.option('--tui', 'tui')
|
|
298
|
-
.option('--pr <url>', 'pr')
|
|
299
|
-
.action(async (commitish, _compareWith, options) => {
|
|
300
|
-
let targetCommitish = commitish;
|
|
301
|
-
let baseCommitish;
|
|
302
|
-
if (options.pr) {
|
|
303
|
-
if (commitish !== 'HEAD' || _compareWith) {
|
|
304
|
-
console.error('Error: --pr option cannot be used with positional arguments');
|
|
305
|
-
process.exit(1);
|
|
306
|
-
}
|
|
307
|
-
const prCommits = await resolvePrCommits(options.pr);
|
|
308
|
-
targetCommitish = prCommits.targetCommitish;
|
|
309
|
-
baseCommitish = prCommits.baseCommitish;
|
|
310
|
-
}
|
|
311
|
-
else {
|
|
312
|
-
baseCommitish = commitish + '^';
|
|
313
|
-
}
|
|
314
|
-
await startServer({
|
|
315
|
-
targetCommitish,
|
|
316
|
-
baseCommitish,
|
|
317
|
-
preferredPort: options.port,
|
|
318
|
-
host: options.host,
|
|
319
|
-
openBrowser: options.open,
|
|
320
|
-
mode: options.mode,
|
|
321
|
-
});
|
|
322
|
-
});
|
|
323
|
-
await program.parseAsync(['--pr', prUrl], { from: 'user' });
|
|
324
|
-
expect(mockResolvePrCommits).toHaveBeenCalledWith(prUrl);
|
|
325
|
-
expect(mockStartServer).toHaveBeenCalledWith({
|
|
326
|
-
targetCommitish: 'abc123',
|
|
327
|
-
baseCommitish: 'def456',
|
|
328
|
-
preferredPort: undefined,
|
|
329
|
-
host: '127.0.0.1',
|
|
330
|
-
openBrowser: true,
|
|
331
|
-
mode: 'side-by-side',
|
|
332
|
-
});
|
|
333
|
-
});
|
|
334
|
-
it('rejects PR option with positional arguments', async () => {
|
|
335
|
-
const program = new Command();
|
|
336
|
-
program
|
|
337
|
-
.argument('[commit-ish]', 'commit-ish', 'HEAD')
|
|
338
|
-
.argument('[compare-with]', 'compare-with')
|
|
339
|
-
.option('--port <port>', 'port', parseInt)
|
|
340
|
-
.option('--host <host>', 'host', '127.0.0.1')
|
|
341
|
-
.option('--no-open', 'no-open')
|
|
342
|
-
.option('--mode <mode>', 'mode', 'side-by-side')
|
|
343
|
-
.option('--tui', 'tui')
|
|
344
|
-
.option('--pr <url>', 'pr')
|
|
345
|
-
.action(async (commitish, _compareWith, options) => {
|
|
346
|
-
if (options.pr) {
|
|
347
|
-
if (commitish !== 'HEAD' || _compareWith) {
|
|
348
|
-
console.error('Error: --pr option cannot be used with positional arguments');
|
|
349
|
-
process.exit(1);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
});
|
|
353
|
-
await program.parseAsync(['main', '--pr', 'https://github.com/owner/repo/pull/123'], {
|
|
354
|
-
from: 'user',
|
|
355
|
-
});
|
|
356
|
-
expect(console.error).toHaveBeenCalledWith('Error: --pr option cannot be used with positional arguments');
|
|
357
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
358
|
-
});
|
|
359
|
-
});
|
|
360
|
-
describe('Console output', () => {
|
|
361
|
-
it('displays server startup message with correct URL', async () => {
|
|
362
|
-
mockFindUntrackedFiles.mockResolvedValue([]);
|
|
363
|
-
mockStartServer.mockResolvedValue({
|
|
364
|
-
port: 3000,
|
|
365
|
-
url: 'http://localhost:3000',
|
|
366
|
-
isEmpty: false,
|
|
367
|
-
});
|
|
368
|
-
const program = new Command();
|
|
369
|
-
program
|
|
370
|
-
.argument('[commit-ish]', 'commit-ish', 'HEAD')
|
|
371
|
-
.argument('[compare-with]', 'compare-with')
|
|
372
|
-
.option('--port <port>', 'port', parseInt)
|
|
373
|
-
.option('--host <host>', 'host', '127.0.0.1')
|
|
374
|
-
.option('--no-open', 'no-open')
|
|
375
|
-
.option('--mode <mode>', 'mode', 'side-by-side')
|
|
376
|
-
.option('--tui', 'tui')
|
|
377
|
-
.option('--pr <url>', 'pr')
|
|
378
|
-
.action(async (commitish, _compareWith, options) => {
|
|
379
|
-
const { url, isEmpty } = await startServer({
|
|
380
|
-
targetCommitish: commitish,
|
|
381
|
-
baseCommitish: commitish + '^',
|
|
382
|
-
preferredPort: options.port,
|
|
383
|
-
host: options.host,
|
|
384
|
-
openBrowser: options.open,
|
|
385
|
-
mode: options.mode,
|
|
386
|
-
});
|
|
387
|
-
console.log(`\n🚀 ReviewIt server started on ${url}`);
|
|
388
|
-
console.log(`📋 Reviewing: ${commitish}`);
|
|
389
|
-
if (isEmpty) {
|
|
390
|
-
console.log('\n! No differences found. Browser will not open automatically.');
|
|
391
|
-
console.log(` Server is running at ${url} if you want to check manually.\n`);
|
|
392
|
-
}
|
|
393
|
-
else if (options.open) {
|
|
394
|
-
console.log('🌐 Opening browser...\n');
|
|
395
|
-
}
|
|
396
|
-
else {
|
|
397
|
-
console.log('💡 Use --open to automatically open browser\n');
|
|
398
|
-
}
|
|
399
|
-
});
|
|
400
|
-
await program.parseAsync([], { from: 'user' });
|
|
401
|
-
expect(console.log).toHaveBeenCalledWith('\n🚀 ReviewIt server started on http://localhost:3000');
|
|
402
|
-
expect(console.log).toHaveBeenCalledWith('📋 Reviewing: HEAD');
|
|
403
|
-
});
|
|
404
|
-
it('displays correct message when no differences found', async () => {
|
|
405
|
-
mockFindUntrackedFiles.mockResolvedValue([]);
|
|
406
|
-
mockStartServer.mockResolvedValue({
|
|
407
|
-
port: 3000,
|
|
408
|
-
url: 'http://localhost:3000',
|
|
409
|
-
isEmpty: true,
|
|
410
|
-
});
|
|
411
|
-
const program = new Command();
|
|
412
|
-
program
|
|
413
|
-
.argument('[commit-ish]', 'commit-ish', 'HEAD')
|
|
414
|
-
.argument('[compare-with]', 'compare-with')
|
|
415
|
-
.option('--port <port>', 'port', parseInt)
|
|
416
|
-
.option('--host <host>', 'host', '127.0.0.1')
|
|
417
|
-
.option('--no-open', 'no-open')
|
|
418
|
-
.option('--mode <mode>', 'mode', 'side-by-side')
|
|
419
|
-
.option('--tui', 'tui')
|
|
420
|
-
.option('--pr <url>', 'pr')
|
|
421
|
-
.action(async (commitish, _compareWith, options) => {
|
|
422
|
-
const { url, isEmpty } = await startServer({
|
|
423
|
-
targetCommitish: commitish,
|
|
424
|
-
baseCommitish: commitish + '^',
|
|
425
|
-
preferredPort: options.port,
|
|
426
|
-
host: options.host,
|
|
427
|
-
openBrowser: options.open,
|
|
428
|
-
mode: options.mode,
|
|
429
|
-
});
|
|
430
|
-
console.log(`\n🚀 ReviewIt server started on ${url}`);
|
|
431
|
-
console.log(`📋 Reviewing: ${commitish}`);
|
|
432
|
-
if (isEmpty) {
|
|
433
|
-
console.log('\n! No differences found. Browser will not open automatically.');
|
|
434
|
-
console.log(` Server is running at ${url} if you want to check manually.\n`);
|
|
435
|
-
}
|
|
436
|
-
});
|
|
437
|
-
await program.parseAsync([], { from: 'user' });
|
|
438
|
-
expect(console.log).toHaveBeenCalledWith('\n! No differences found. Browser will not open automatically.');
|
|
439
|
-
expect(console.log).toHaveBeenCalledWith(' Server is running at http://localhost:3000 if you want to check manually.\n');
|
|
440
|
-
});
|
|
441
|
-
});
|
|
442
|
-
describe('Server mode option handling', () => {
|
|
443
|
-
it('passes mode option to startServer', async () => {
|
|
444
|
-
mockFindUntrackedFiles.mockResolvedValue([]);
|
|
445
|
-
const program = new Command();
|
|
446
|
-
program
|
|
447
|
-
.argument('[commit-ish]', 'commit-ish', 'HEAD')
|
|
448
|
-
.argument('[compare-with]', 'compare-with')
|
|
449
|
-
.option('--port <port>', 'port', parseInt)
|
|
450
|
-
.option('--host <host>', 'host', '127.0.0.1')
|
|
451
|
-
.option('--no-open', 'no-open')
|
|
452
|
-
.option('--mode <mode>', 'mode', 'side-by-side')
|
|
453
|
-
.option('--tui', 'tui')
|
|
454
|
-
.option('--pr <url>', 'pr')
|
|
455
|
-
.action(async (commitish, _compareWith, options) => {
|
|
456
|
-
await startServer({
|
|
457
|
-
targetCommitish: commitish,
|
|
458
|
-
baseCommitish: commitish + '^',
|
|
459
|
-
preferredPort: options.port,
|
|
460
|
-
host: options.host,
|
|
461
|
-
openBrowser: options.open,
|
|
462
|
-
mode: options.mode,
|
|
463
|
-
});
|
|
464
|
-
});
|
|
465
|
-
await program.parseAsync(['--mode', 'inline'], { from: 'user' });
|
|
466
|
-
expect(mockStartServer).toHaveBeenCalledWith(expect.objectContaining({
|
|
467
|
-
mode: 'inline',
|
|
468
|
-
}));
|
|
469
|
-
});
|
|
470
|
-
it('uses default mode when not specified', async () => {
|
|
471
|
-
mockFindUntrackedFiles.mockResolvedValue([]);
|
|
472
|
-
const program = new Command();
|
|
473
|
-
program
|
|
474
|
-
.argument('[commit-ish]', 'commit-ish', 'HEAD')
|
|
475
|
-
.argument('[compare-with]', 'compare-with')
|
|
476
|
-
.option('--port <port>', 'port', parseInt)
|
|
477
|
-
.option('--host <host>', 'host', '127.0.0.1')
|
|
478
|
-
.option('--no-open', 'no-open')
|
|
479
|
-
.option('--mode <mode>', 'mode', 'side-by-side')
|
|
480
|
-
.option('--tui', 'tui')
|
|
481
|
-
.option('--pr <url>', 'pr')
|
|
482
|
-
.action(async (commitish, _compareWith, options) => {
|
|
483
|
-
await startServer({
|
|
484
|
-
targetCommitish: commitish,
|
|
485
|
-
baseCommitish: commitish + '^',
|
|
486
|
-
preferredPort: options.port,
|
|
487
|
-
host: options.host,
|
|
488
|
-
openBrowser: options.open,
|
|
489
|
-
mode: options.mode,
|
|
490
|
-
});
|
|
491
|
-
});
|
|
492
|
-
await program.parseAsync([], { from: 'user' });
|
|
493
|
-
expect(mockStartServer).toHaveBeenCalledWith(expect.objectContaining({
|
|
494
|
-
mode: 'side-by-side',
|
|
495
|
-
}));
|
|
496
|
-
});
|
|
497
|
-
});
|
|
498
|
-
describe('TUI mode', () => {
|
|
499
|
-
let mockRender;
|
|
500
|
-
let mockTuiApp;
|
|
501
|
-
beforeEach(async () => {
|
|
502
|
-
// Mock ink and TUI components
|
|
503
|
-
mockRender = vi.fn();
|
|
504
|
-
mockTuiApp = vi.fn();
|
|
505
|
-
vi.doMock('ink', async () => ({
|
|
506
|
-
render: mockRender,
|
|
507
|
-
}));
|
|
508
|
-
vi.doMock('../tui/App.js', async () => ({
|
|
509
|
-
default: mockTuiApp,
|
|
510
|
-
}));
|
|
511
|
-
// Mock React.createElement for testing
|
|
512
|
-
vi.spyOn(React, 'createElement').mockImplementation((component, props) => ({ component, props }));
|
|
513
|
-
// Mock process.stdin.isTTY
|
|
514
|
-
Object.defineProperty(process.stdin, 'isTTY', {
|
|
515
|
-
value: true,
|
|
516
|
-
configurable: true,
|
|
517
|
-
});
|
|
518
|
-
});
|
|
519
|
-
afterEach(() => {
|
|
520
|
-
vi.doUnmock('ink');
|
|
521
|
-
vi.doUnmock('../tui/App.js');
|
|
522
|
-
vi.restoreAllMocks();
|
|
523
|
-
});
|
|
524
|
-
it('passes arguments to TUI app correctly', async () => {
|
|
525
|
-
mockFindUntrackedFiles.mockResolvedValue([]);
|
|
526
|
-
const program = new Command();
|
|
527
|
-
program
|
|
528
|
-
.argument('[commit-ish]', 'commit-ish', 'HEAD')
|
|
529
|
-
.argument('[compare-with]', 'compare-with')
|
|
530
|
-
.option('--port <port>', 'port', parseInt)
|
|
531
|
-
.option('--host <host>', 'host', '127.0.0.1')
|
|
532
|
-
.option('--no-open', 'no-open')
|
|
533
|
-
.option('--mode <mode>', 'mode', 'side-by-side')
|
|
534
|
-
.option('--tui', 'tui')
|
|
535
|
-
.option('--pr <url>', 'pr')
|
|
536
|
-
.action(async (commitish, _compareWith, options) => {
|
|
537
|
-
if (options.tui) {
|
|
538
|
-
if (!process.stdin.isTTY) {
|
|
539
|
-
console.error('Error: TUI mode requires an interactive terminal (TTY).');
|
|
540
|
-
process.exit(1);
|
|
541
|
-
}
|
|
542
|
-
const { render } = await import('ink');
|
|
543
|
-
const { default: TuiApp } = await import('../tui/App.js');
|
|
544
|
-
render(React.createElement(TuiApp, {
|
|
545
|
-
targetCommitish: commitish,
|
|
546
|
-
baseCommitish: commitish + '^',
|
|
547
|
-
mode: options.mode,
|
|
548
|
-
}));
|
|
549
|
-
return;
|
|
550
|
-
}
|
|
551
|
-
});
|
|
552
|
-
await program.parseAsync(['main', '--tui'], { from: 'user' });
|
|
553
|
-
expect(mockRender).toHaveBeenCalledWith({
|
|
554
|
-
component: mockTuiApp,
|
|
555
|
-
props: {
|
|
556
|
-
targetCommitish: 'main',
|
|
557
|
-
baseCommitish: 'main^',
|
|
558
|
-
mode: 'side-by-side',
|
|
559
|
-
},
|
|
560
|
-
});
|
|
561
|
-
});
|
|
562
|
-
it('passes mode option to TUI app', async () => {
|
|
563
|
-
mockFindUntrackedFiles.mockResolvedValue([]);
|
|
564
|
-
const program = new Command();
|
|
565
|
-
program
|
|
566
|
-
.argument('[commit-ish]', 'commit-ish', 'HEAD')
|
|
567
|
-
.argument('[compare-with]', 'compare-with')
|
|
568
|
-
.option('--port <port>', 'port', parseInt)
|
|
569
|
-
.option('--host <host>', 'host', '127.0.0.1')
|
|
570
|
-
.option('--no-open', 'no-open')
|
|
571
|
-
.option('--mode <mode>', 'mode', 'side-by-side')
|
|
572
|
-
.option('--tui', 'tui')
|
|
573
|
-
.option('--pr <url>', 'pr')
|
|
574
|
-
.action(async (commitish, _compareWith, options) => {
|
|
575
|
-
if (options.tui) {
|
|
576
|
-
if (!process.stdin.isTTY) {
|
|
577
|
-
console.error('Error: TUI mode requires an interactive terminal (TTY).');
|
|
578
|
-
process.exit(1);
|
|
579
|
-
}
|
|
580
|
-
const { render } = await import('ink');
|
|
581
|
-
const { default: TuiApp } = await import('../tui/App.js');
|
|
582
|
-
render(React.createElement(TuiApp, {
|
|
583
|
-
targetCommitish: commitish,
|
|
584
|
-
baseCommitish: commitish + '^',
|
|
585
|
-
mode: options.mode,
|
|
586
|
-
}));
|
|
587
|
-
return;
|
|
588
|
-
}
|
|
589
|
-
});
|
|
590
|
-
await program.parseAsync(['--tui', '--mode', 'inline'], { from: 'user' });
|
|
591
|
-
expect(mockRender).toHaveBeenCalledWith({
|
|
592
|
-
component: mockTuiApp,
|
|
593
|
-
props: {
|
|
594
|
-
targetCommitish: 'HEAD',
|
|
595
|
-
baseCommitish: 'HEAD^',
|
|
596
|
-
mode: 'inline',
|
|
597
|
-
},
|
|
598
|
-
});
|
|
599
|
-
});
|
|
600
|
-
it('handles special arguments with TUI mode', async () => {
|
|
601
|
-
mockFindUntrackedFiles.mockResolvedValue([]);
|
|
602
|
-
const program = new Command();
|
|
603
|
-
program
|
|
604
|
-
.argument('[commit-ish]', 'commit-ish', 'HEAD')
|
|
605
|
-
.argument('[compare-with]', 'compare-with')
|
|
606
|
-
.option('--port <port>', 'port', parseInt)
|
|
607
|
-
.option('--host <host>', 'host', '127.0.0.1')
|
|
608
|
-
.option('--no-open', 'no-open')
|
|
609
|
-
.option('--mode <mode>', 'mode', 'side-by-side')
|
|
610
|
-
.option('--tui', 'tui')
|
|
611
|
-
.option('--pr <url>', 'pr')
|
|
612
|
-
.action(async (commitish, _compareWith, options) => {
|
|
613
|
-
if (options.tui) {
|
|
614
|
-
const { render } = await import('ink');
|
|
615
|
-
const { default: TuiApp } = await import('../tui/App.js');
|
|
616
|
-
let targetCommitish = commitish;
|
|
617
|
-
let baseCommitish;
|
|
618
|
-
if (commitish === 'working') {
|
|
619
|
-
baseCommitish = 'staged';
|
|
620
|
-
}
|
|
621
|
-
else if (commitish === 'staged' || commitish === '.') {
|
|
622
|
-
baseCommitish = 'HEAD';
|
|
623
|
-
}
|
|
624
|
-
else {
|
|
625
|
-
baseCommitish = commitish + '^';
|
|
626
|
-
}
|
|
627
|
-
render(React.createElement(TuiApp, {
|
|
628
|
-
targetCommitish,
|
|
629
|
-
baseCommitish,
|
|
630
|
-
mode: options.mode,
|
|
631
|
-
}));
|
|
632
|
-
return;
|
|
633
|
-
}
|
|
634
|
-
});
|
|
635
|
-
await program.parseAsync(['working', '--tui', '--mode', 'inline'], { from: 'user' });
|
|
636
|
-
expect(mockRender).toHaveBeenCalledWith({
|
|
637
|
-
component: mockTuiApp,
|
|
638
|
-
props: {
|
|
639
|
-
targetCommitish: 'working',
|
|
640
|
-
baseCommitish: 'staged',
|
|
641
|
-
mode: 'inline',
|
|
642
|
-
},
|
|
643
|
-
});
|
|
644
|
-
});
|
|
645
|
-
it('rejects TUI mode in non-TTY environment', async () => {
|
|
646
|
-
// Mock non-TTY environment
|
|
647
|
-
Object.defineProperty(process.stdin, 'isTTY', {
|
|
648
|
-
value: false,
|
|
649
|
-
configurable: true,
|
|
650
|
-
});
|
|
651
|
-
const program = new Command();
|
|
652
|
-
program
|
|
653
|
-
.argument('[commit-ish]', 'commit-ish', 'HEAD')
|
|
654
|
-
.argument('[compare-with]', 'compare-with')
|
|
655
|
-
.option('--port <port>', 'port', parseInt)
|
|
656
|
-
.option('--host <host>', 'host', '127.0.0.1')
|
|
657
|
-
.option('--no-open', 'no-open')
|
|
658
|
-
.option('--mode <mode>', 'mode', 'side-by-side')
|
|
659
|
-
.option('--tui', 'tui')
|
|
660
|
-
.option('--pr <url>', 'pr')
|
|
661
|
-
.action(async (_commitish, _compareWith, options) => {
|
|
662
|
-
if (options.tui) {
|
|
663
|
-
if (!process.stdin.isTTY) {
|
|
664
|
-
console.error('Error: TUI mode requires an interactive terminal (TTY).');
|
|
665
|
-
console.error('Try running the command directly in your terminal without piping.');
|
|
666
|
-
process.exit(1);
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
});
|
|
670
|
-
await program.parseAsync(['--tui'], { from: 'user' });
|
|
671
|
-
expect(console.error).toHaveBeenCalledWith('Error: TUI mode requires an interactive terminal (TTY).');
|
|
672
|
-
expect(console.error).toHaveBeenCalledWith('Try running the command directly in your terminal without piping.');
|
|
673
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
674
|
-
});
|
|
675
|
-
});
|
|
676
|
-
});
|
package/dist/cli/utils.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function validateCommitish(commitish: string): boolean;
|
package/dist/cli/utils.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|