codecritique 1.0.0 → 1.1.0
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 +82 -114
- package/package.json +10 -9
- package/src/content-retrieval.test.js +775 -0
- package/src/custom-documents.test.js +440 -0
- package/src/feedback-loader.test.js +529 -0
- package/src/llm.test.js +256 -0
- package/src/project-analyzer.test.js +747 -0
- package/src/rag-analyzer.js +12 -0
- package/src/rag-analyzer.test.js +1109 -0
- package/src/rag-review.test.js +317 -0
- package/src/setupTests.js +131 -0
- package/src/zero-shot-classifier-open.test.js +278 -0
- package/src/embeddings/cache-manager.js +0 -364
- package/src/embeddings/constants.js +0 -40
- package/src/embeddings/database.js +0 -921
- package/src/embeddings/errors.js +0 -208
- package/src/embeddings/factory.js +0 -447
- package/src/embeddings/file-processor.js +0 -851
- package/src/embeddings/model-manager.js +0 -337
- package/src/embeddings/similarity-calculator.js +0 -97
- package/src/embeddings/types.js +0 -113
- package/src/pr-history/analyzer.js +0 -579
- package/src/pr-history/bot-detector.js +0 -123
- package/src/pr-history/cli-utils.js +0 -204
- package/src/pr-history/comment-processor.js +0 -549
- package/src/pr-history/database.js +0 -819
- package/src/pr-history/github-client.js +0 -629
- package/src/technology-keywords.json +0 -753
- package/src/utils/command.js +0 -48
- package/src/utils/constants.js +0 -263
- package/src/utils/context-inference.js +0 -364
- package/src/utils/document-detection.js +0 -105
- package/src/utils/file-validation.js +0 -271
- package/src/utils/git.js +0 -232
- package/src/utils/language-detection.js +0 -170
- package/src/utils/logging.js +0 -24
- package/src/utils/markdown.js +0 -132
- package/src/utils/mobilebert-tokenizer.js +0 -141
- package/src/utils/pr-chunking.js +0 -276
- package/src/utils/string-utils.js +0 -28
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { runAnalysis, gatherUnifiedContextForPR } from './rag-analyzer.js';
|
|
2
|
+
import { reviewFile, reviewFiles, reviewPullRequest } from './rag-review.js';
|
|
3
|
+
import { shouldProcessFile } from './utils/file-validation.js';
|
|
4
|
+
import { getChangedLinesInfo, getFileContentFromGit } from './utils/git.js';
|
|
5
|
+
import { shouldChunkPR, chunkPRFiles, combineChunkResults } from './utils/pr-chunking.js';
|
|
6
|
+
|
|
7
|
+
vi.mock('./rag-analyzer.js', () => ({
|
|
8
|
+
runAnalysis: vi.fn(),
|
|
9
|
+
gatherUnifiedContextForPR: vi.fn().mockResolvedValue({
|
|
10
|
+
codeExamples: [],
|
|
11
|
+
guidelines: [],
|
|
12
|
+
prComments: [],
|
|
13
|
+
customDocChunks: [],
|
|
14
|
+
}),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
vi.mock('./utils/file-validation.js', () => ({
|
|
18
|
+
shouldProcessFile: vi.fn().mockReturnValue(true),
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
vi.mock('./utils/git.js', () => ({
|
|
22
|
+
findBaseBranch: vi.fn().mockReturnValue('main'),
|
|
23
|
+
getChangedLinesInfo: vi.fn().mockReturnValue({
|
|
24
|
+
hasChanges: true,
|
|
25
|
+
addedLines: [1, 2, 3],
|
|
26
|
+
removedLines: [4],
|
|
27
|
+
fullDiff: '+ new code\n- old code',
|
|
28
|
+
}),
|
|
29
|
+
getFileContentFromGit: vi.fn().mockReturnValue('const x = 1;'),
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
vi.mock('./utils/language-detection.js', () => ({
|
|
33
|
+
detectFileType: vi.fn().mockReturnValue({ isTest: false }),
|
|
34
|
+
detectLanguageFromExtension: vi.fn().mockReturnValue('javascript'),
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
vi.mock('./utils/pr-chunking.js', () => ({
|
|
38
|
+
shouldChunkPR: vi.fn().mockReturnValue({ shouldChunk: false, estimatedTokens: 1000 }),
|
|
39
|
+
chunkPRFiles: vi.fn(),
|
|
40
|
+
combineChunkResults: vi.fn(),
|
|
41
|
+
}));
|
|
42
|
+
|
|
43
|
+
describe('rag-review', () => {
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
mockConsole();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
afterEach(() => {
|
|
49
|
+
vi.restoreAllMocks();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('reviewFile', () => {
|
|
53
|
+
it('should review a file successfully', async () => {
|
|
54
|
+
runAnalysis.mockResolvedValue({
|
|
55
|
+
success: true,
|
|
56
|
+
filePath: '/test/file.js',
|
|
57
|
+
language: 'javascript',
|
|
58
|
+
results: { issues: [] },
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const result = await reviewFile('/test/file.js');
|
|
62
|
+
|
|
63
|
+
expect(result.success).toBe(true);
|
|
64
|
+
expect(runAnalysis).toHaveBeenCalledWith('/test/file.js', {});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should convert object results to array format', async () => {
|
|
68
|
+
runAnalysis.mockResolvedValue({
|
|
69
|
+
success: true,
|
|
70
|
+
filePath: '/test/file.js',
|
|
71
|
+
language: 'javascript',
|
|
72
|
+
results: { issues: [{ message: 'test' }] },
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const result = await reviewFile('/test/file.js');
|
|
76
|
+
|
|
77
|
+
expect(result.success).toBe(true);
|
|
78
|
+
expect(Array.isArray(result.results)).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should handle analysis errors', async () => {
|
|
82
|
+
runAnalysis.mockRejectedValue(new Error('Analysis failed'));
|
|
83
|
+
|
|
84
|
+
const result = await reviewFile('/test/file.js');
|
|
85
|
+
|
|
86
|
+
expect(result.success).toBe(false);
|
|
87
|
+
expect(result.error).toBe('Analysis failed');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should pass options to runAnalysis', async () => {
|
|
91
|
+
runAnalysis.mockResolvedValue({ success: true, results: [] });
|
|
92
|
+
|
|
93
|
+
await reviewFile('/test/file.js', { verbose: true, maxExamples: 10 });
|
|
94
|
+
|
|
95
|
+
expect(runAnalysis).toHaveBeenCalledWith('/test/file.js', { verbose: true, maxExamples: 10 });
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe('reviewFiles', () => {
|
|
100
|
+
it('should review multiple files', async () => {
|
|
101
|
+
runAnalysis.mockResolvedValue({ success: true, results: [] });
|
|
102
|
+
|
|
103
|
+
const result = await reviewFiles(['/test/file1.js', '/test/file2.js']);
|
|
104
|
+
|
|
105
|
+
expect(result.success).toBe(true);
|
|
106
|
+
expect(result.results.length).toBe(2);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should process files in batches based on concurrency', async () => {
|
|
110
|
+
runAnalysis.mockResolvedValue({ success: true, results: [] });
|
|
111
|
+
|
|
112
|
+
await reviewFiles(['/file1.js', '/file2.js', '/file3.js', '/file4.js', '/file5.js'], { concurrency: 2 });
|
|
113
|
+
|
|
114
|
+
expect(runAnalysis).toHaveBeenCalledTimes(5);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should count successes, skips, and errors', async () => {
|
|
118
|
+
runAnalysis
|
|
119
|
+
.mockResolvedValueOnce({ success: true, results: [] })
|
|
120
|
+
.mockResolvedValueOnce({ success: true, skipped: true, results: [] })
|
|
121
|
+
.mockResolvedValueOnce({ success: false, error: 'Error' });
|
|
122
|
+
|
|
123
|
+
const result = await reviewFiles(['/file1.js', '/file2.js', '/file3.js']);
|
|
124
|
+
|
|
125
|
+
expect(result.message).toContain('Success: 1');
|
|
126
|
+
expect(result.message).toContain('Skipped: 1');
|
|
127
|
+
expect(result.message).toContain('Errors: 1');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should handle overall errors gracefully', async () => {
|
|
131
|
+
runAnalysis.mockRejectedValue(new Error('Fatal error'));
|
|
132
|
+
|
|
133
|
+
const result = await reviewFiles(['/file.js']);
|
|
134
|
+
|
|
135
|
+
// Individual file errors are collected in results, not in a top-level error field
|
|
136
|
+
expect(result.success).toBe(false);
|
|
137
|
+
expect(result.results.length).toBe(1);
|
|
138
|
+
expect(result.results[0].success).toBe(false);
|
|
139
|
+
expect(result.results[0].error).toBe('Fatal error');
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe('reviewPullRequest', () => {
|
|
144
|
+
it('should review PR files', async () => {
|
|
145
|
+
runAnalysis.mockResolvedValue({
|
|
146
|
+
success: true,
|
|
147
|
+
results: { issues: [], crossFileIssues: [], summary: 'OK' },
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const result = await reviewPullRequest(['/src/file.js']);
|
|
151
|
+
|
|
152
|
+
expect(result.success).toBe(true);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('should return empty results when no processable files', async () => {
|
|
156
|
+
const result = await reviewPullRequest([]);
|
|
157
|
+
|
|
158
|
+
expect(result.success).toBe(true);
|
|
159
|
+
expect(result.results).toEqual([]);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('should skip files based on exclusion rules', async () => {
|
|
163
|
+
shouldProcessFile.mockReturnValue(false);
|
|
164
|
+
|
|
165
|
+
const result = await reviewPullRequest(['/file.js'], { verbose: true });
|
|
166
|
+
|
|
167
|
+
expect(result.success).toBe(true);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('should skip files with no changes', async () => {
|
|
171
|
+
getChangedLinesInfo.mockReturnValue({ hasChanges: false });
|
|
172
|
+
|
|
173
|
+
const result = await reviewPullRequest(['/file.js'], { verbose: true });
|
|
174
|
+
|
|
175
|
+
expect(result.success).toBe(true);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('should use chunked processing for large PRs', async () => {
|
|
179
|
+
// Ensure file processing succeeds first (files must pass filters to reach chunking logic)
|
|
180
|
+
shouldProcessFile.mockReturnValue(true);
|
|
181
|
+
getChangedLinesInfo.mockReturnValue({
|
|
182
|
+
hasChanges: true,
|
|
183
|
+
addedLines: [1, 2, 3],
|
|
184
|
+
removedLines: [],
|
|
185
|
+
fullDiff: '+ new code',
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// Setup: shouldChunkPR returns true to trigger chunked processing
|
|
189
|
+
shouldChunkPR.mockReturnValue({ shouldChunk: true, estimatedTokens: 100000, recommendedChunks: 2 });
|
|
190
|
+
|
|
191
|
+
// Setup chunks that will be processed
|
|
192
|
+
chunkPRFiles.mockReturnValue([
|
|
193
|
+
{ files: [{ filePath: '/file1.js' }], totalTokens: 30000 },
|
|
194
|
+
{ files: [{ filePath: '/file2.js' }], totalTokens: 30000 },
|
|
195
|
+
]);
|
|
196
|
+
|
|
197
|
+
// Setup combined results
|
|
198
|
+
combineChunkResults.mockReturnValue({
|
|
199
|
+
success: true,
|
|
200
|
+
results: [
|
|
201
|
+
{ filePath: '/file1.js', success: true },
|
|
202
|
+
{ filePath: '/file2.js', success: true },
|
|
203
|
+
],
|
|
204
|
+
prContext: { totalFiles: 2 },
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// Setup analysis for the recursive chunk calls - needs to return valid holistic result
|
|
208
|
+
runAnalysis.mockResolvedValue({
|
|
209
|
+
success: true,
|
|
210
|
+
results: { fileSpecificIssues: {}, crossFileIssues: [], summary: 'OK' },
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
const result = await reviewPullRequest(['/file1.js', '/file2.js'], { verbose: true });
|
|
214
|
+
|
|
215
|
+
// Verify chunking flow was triggered
|
|
216
|
+
expect(shouldChunkPR).toHaveBeenCalled();
|
|
217
|
+
expect(chunkPRFiles).toHaveBeenCalled();
|
|
218
|
+
expect(combineChunkResults).toHaveBeenCalled();
|
|
219
|
+
expect(result.success).toBe(true);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('should gather unified context for PR files', async () => {
|
|
223
|
+
// Setup analysis to return valid holistic result
|
|
224
|
+
runAnalysis.mockResolvedValue({
|
|
225
|
+
success: true,
|
|
226
|
+
results: { fileSpecificIssues: {}, crossFileIssues: [], summary: 'OK' },
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// Ensure file processing succeeds (these are already mocked at top level)
|
|
230
|
+
shouldProcessFile.mockReturnValue(true);
|
|
231
|
+
getChangedLinesInfo.mockReturnValue({
|
|
232
|
+
hasChanges: true,
|
|
233
|
+
addedLines: [1, 2, 3],
|
|
234
|
+
removedLines: [],
|
|
235
|
+
fullDiff: '+ new code',
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
await reviewPullRequest(['/src/file.js'], { verbose: true });
|
|
239
|
+
|
|
240
|
+
// gatherUnifiedContextForPR is called for regular (non-chunked) PRs
|
|
241
|
+
expect(gatherUnifiedContextForPR).toHaveBeenCalled();
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('should handle errors in file processing gracefully', async () => {
|
|
245
|
+
getFileContentFromGit.mockImplementation(() => {
|
|
246
|
+
throw new Error('File not found');
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
const result = await reviewPullRequest(['/missing.js'], { verbose: true });
|
|
250
|
+
|
|
251
|
+
// When file processing fails, the file is skipped, not failing the whole PR review
|
|
252
|
+
expect(result.success).toBe(true);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('should use holisticReview mode when enabled', async () => {
|
|
256
|
+
runAnalysis.mockResolvedValue({
|
|
257
|
+
success: true,
|
|
258
|
+
results: { fileSpecificIssues: {}, crossFileIssues: [], summary: 'Holistic review' },
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
shouldProcessFile.mockReturnValue(true);
|
|
262
|
+
getChangedLinesInfo.mockReturnValue({
|
|
263
|
+
hasChanges: true,
|
|
264
|
+
addedLines: [1],
|
|
265
|
+
removedLines: [],
|
|
266
|
+
fullDiff: '+ code',
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
const result = await reviewPullRequest(['/src/file.js'], { holisticReview: true });
|
|
270
|
+
|
|
271
|
+
expect(result.success).toBe(true);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('should include PR summary in results', async () => {
|
|
275
|
+
runAnalysis.mockResolvedValue({
|
|
276
|
+
success: true,
|
|
277
|
+
results: { fileSpecificIssues: {}, crossFileIssues: [], summary: 'Summary text' },
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
shouldProcessFile.mockReturnValue(true);
|
|
281
|
+
getChangedLinesInfo.mockReturnValue({
|
|
282
|
+
hasChanges: true,
|
|
283
|
+
addedLines: [1],
|
|
284
|
+
removedLines: [],
|
|
285
|
+
fullDiff: '+ code',
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
const result = await reviewPullRequest(['/src/file.js']);
|
|
289
|
+
|
|
290
|
+
expect(result.success).toBe(true);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('should handle multiple files with different statuses', async () => {
|
|
294
|
+
runAnalysis
|
|
295
|
+
.mockResolvedValueOnce({
|
|
296
|
+
success: true,
|
|
297
|
+
results: { issues: [], summary: 'OK' },
|
|
298
|
+
})
|
|
299
|
+
.mockResolvedValueOnce({
|
|
300
|
+
success: true,
|
|
301
|
+
skipped: true,
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
shouldProcessFile.mockReturnValue(true);
|
|
305
|
+
getChangedLinesInfo.mockReturnValue({
|
|
306
|
+
hasChanges: true,
|
|
307
|
+
addedLines: [1],
|
|
308
|
+
removedLines: [],
|
|
309
|
+
fullDiff: '+ code',
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
const result = await reviewPullRequest(['/file1.js', '/file2.js']);
|
|
313
|
+
|
|
314
|
+
expect(result.success).toBe(true);
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
});
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/* eslint-disable vitest/no-commented-out-tests */
|
|
2
|
+
import { setupConsoleSpies, suppressConsole, restoreConsole, CONSOLE_METHODS } from './test-utils/console-suppression.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Global test setup for Vitest
|
|
6
|
+
*
|
|
7
|
+
* Console Suppression:
|
|
8
|
+
* By default, console output from code under test is suppressed to keep test output clean.
|
|
9
|
+
* However, you can enable console output for debugging in several ways:
|
|
10
|
+
*
|
|
11
|
+
* 1. Environment Variable (for all tests):
|
|
12
|
+
* DEBUG_CONSOLE=true npm test
|
|
13
|
+
* or
|
|
14
|
+
* SHOW_CONSOLE=true npm test
|
|
15
|
+
*
|
|
16
|
+
* 2. Per-test helper functions:
|
|
17
|
+
* enableConsole() - Enable console output for the current test
|
|
18
|
+
* disableConsole() - Disable console output again
|
|
19
|
+
*
|
|
20
|
+
* Example:
|
|
21
|
+
* it('debug test', () => {
|
|
22
|
+
* enableConsole();
|
|
23
|
+
* console.log('This will be visible');
|
|
24
|
+
* // ... test code ...
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* 3. Restore console in specific tests:
|
|
28
|
+
* vi.restoreAllMocks() - Restores all mocks including console
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
// ============================================================================
|
|
32
|
+
// Global Mocks - Available in all test files without explicit vi.mock() calls
|
|
33
|
+
// ============================================================================
|
|
34
|
+
|
|
35
|
+
// Mock chalk - commonly used across all test files for console output
|
|
36
|
+
// This mock makes all chalk methods pass-through (return input unchanged)
|
|
37
|
+
vi.mock('chalk', () => ({
|
|
38
|
+
default: {
|
|
39
|
+
blue: vi.fn((s) => s),
|
|
40
|
+
green: vi.fn((s) => s),
|
|
41
|
+
yellow: vi.fn((s) => s),
|
|
42
|
+
red: vi.fn((s) => s),
|
|
43
|
+
cyan: vi.fn((s) => s),
|
|
44
|
+
gray: vi.fn((s) => s),
|
|
45
|
+
magenta: vi.fn((s) => s),
|
|
46
|
+
white: vi.fn((s) => s),
|
|
47
|
+
bold: vi.fn((s) => s),
|
|
48
|
+
},
|
|
49
|
+
}));
|
|
50
|
+
|
|
51
|
+
// Mock dotenv - prevent loading .env files during tests
|
|
52
|
+
vi.mock('dotenv', () => ({
|
|
53
|
+
default: { config: vi.fn() },
|
|
54
|
+
}));
|
|
55
|
+
|
|
56
|
+
// ============================================================================
|
|
57
|
+
// Console Suppression - Mock console methods to prevent output from code under test
|
|
58
|
+
// Can be disabled via DEBUG_CONSOLE environment variable for debugging
|
|
59
|
+
// ============================================================================
|
|
60
|
+
|
|
61
|
+
// Setup console spies immediately (before any modules are imported)
|
|
62
|
+
// This ensures spies exist for tests that assert on console calls
|
|
63
|
+
// Output suppression depends on enableConsoleOutput flag
|
|
64
|
+
setupConsoleSpies();
|
|
65
|
+
|
|
66
|
+
// ============================================================================
|
|
67
|
+
// Test Lifecycle Hooks
|
|
68
|
+
// ============================================================================
|
|
69
|
+
|
|
70
|
+
// Ensure console spies exist before each test
|
|
71
|
+
// Respects DEBUG_CONSOLE/SHOW_CONSOLE environment variable for output suppression
|
|
72
|
+
beforeEach(() => {
|
|
73
|
+
// Always ensure spies exist (they may have been restored in a previous test)
|
|
74
|
+
setupConsoleSpies();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Clean up after each test
|
|
78
|
+
afterEach(() => {
|
|
79
|
+
vi.clearAllMocks();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// ============================================================================
|
|
83
|
+
// Global Helper Functions
|
|
84
|
+
// ============================================================================
|
|
85
|
+
|
|
86
|
+
// Helper to create mock embedding vectors (384 dimensions for BGE-Small)
|
|
87
|
+
globalThis.createMockEmbedding = (dim = 384, value = 0.1) => new Array(dim).fill(value);
|
|
88
|
+
|
|
89
|
+
// Helper to create a mock file stats object
|
|
90
|
+
globalThis.createMockStats = (overrides = {}) => ({
|
|
91
|
+
size: 1000,
|
|
92
|
+
isFile: () => true,
|
|
93
|
+
isDirectory: () => false,
|
|
94
|
+
mtime: new Date(),
|
|
95
|
+
...overrides,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Helper to mock all console methods - suppresses output during tests
|
|
99
|
+
// Usage: Call in beforeEach() to suppress console output
|
|
100
|
+
// NOTE: Console is already suppressed by default. Use this only if you need
|
|
101
|
+
// to re-suppress after calling enableConsole()
|
|
102
|
+
globalThis.mockConsole = () => {
|
|
103
|
+
suppressConsole();
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// Helper to restore console output for debugging
|
|
107
|
+
// Usage: enableConsole() in a test to see console.log statements
|
|
108
|
+
// Example:
|
|
109
|
+
// it('debug test', () => {
|
|
110
|
+
// enableConsole();
|
|
111
|
+
// console.log('This will be visible');
|
|
112
|
+
// });
|
|
113
|
+
globalThis.enableConsole = () => {
|
|
114
|
+
restoreConsole();
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// Helper to disable console output again after enabling it
|
|
118
|
+
// Usage: disableConsole() to re-suppress console output
|
|
119
|
+
globalThis.disableConsole = () => {
|
|
120
|
+
suppressConsole();
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// Helper to mock only specific console methods
|
|
124
|
+
// Usage: mockConsoleSelective('log', 'error')
|
|
125
|
+
globalThis.mockConsoleSelective = (...methods) => {
|
|
126
|
+
methods.forEach((method) => {
|
|
127
|
+
if (CONSOLE_METHODS.includes(method)) {
|
|
128
|
+
vi.spyOn(console, method).mockImplementation(() => {});
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
};
|