Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/chatSessions/common/test/externalEditTracker.spec.ts
13405 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { describe, expect, it } from 'vitest';
7
import { SpyChatResponseStream } from '../../../../util/common/test/mockChatResponseStream';
8
import { CancellationToken } from '../../../../util/vs/base/common/cancellation';
9
import { URI } from '../../../../util/vs/base/common/uri';
10
import { ExternalEditTracker } from '../externalEditTracker';
11
12
describe('ExternalEditTracker', () => {
13
describe('ignore directories', () => {
14
it('should filter out files in ignored directories', async () => {
15
const userHome = URI.file('/home/user');
16
const planDir = URI.joinPath(userHome, '.claude', 'plans');
17
const tracker = new ExternalEditTracker([planDir]);
18
19
const planFile = URI.joinPath(userHome, '.claude', 'plans', 'test-plan.md');
20
const regularFile = URI.file('/workspace/src/test.ts');
21
const stream = new SpyChatResponseStream();
22
23
await tracker.trackEdit('edit-1', [planFile, regularFile], stream, CancellationToken.None);
24
25
// Only the regular file should be tracked
26
expect(stream.externalEditUris.length).toBe(1);
27
expect(stream.externalEditUris[0].toString()).toBe(regularFile.toString());
28
});
29
30
it('should not filter files from other .claude subdirectories', async () => {
31
const userHome = URI.file('/home/user');
32
const planDir = URI.joinPath(userHome, '.claude', 'plans');
33
const tracker = new ExternalEditTracker([planDir]);
34
35
const agentFile = URI.joinPath(userHome, '.claude', 'agents', 'my-agent.md');
36
const memoryFile = URI.joinPath(userHome, '.claude', 'CLAUDE.md');
37
const stream = new SpyChatResponseStream();
38
39
await tracker.trackEdit('edit-2', [agentFile, memoryFile], stream, CancellationToken.None);
40
41
// Both files should be tracked
42
expect(stream.externalEditUris.length).toBe(2);
43
});
44
45
it('should handle multiple ignored directories', async () => {
46
const userHome = URI.file('/home/user');
47
const planDir = URI.joinPath(userHome, '.claude', 'plans');
48
const tempDir = URI.file('/tmp/claude-temp');
49
const tracker = new ExternalEditTracker([planDir, tempDir]);
50
51
const planFile = URI.joinPath(userHome, '.claude', 'plans', 'plan.md');
52
const tempFile = URI.joinPath(tempDir, 'temp.txt');
53
const regularFile = URI.file('/workspace/src/test.ts');
54
const stream = new SpyChatResponseStream();
55
56
await tracker.trackEdit('edit-3', [planFile, tempFile, regularFile], stream, CancellationToken.None);
57
58
// Only the regular file should be tracked
59
expect(stream.externalEditUris.length).toBe(1);
60
expect(stream.externalEditUris[0].toString()).toBe(regularFile.toString());
61
});
62
63
it('should handle nested files in ignored directories', async () => {
64
const userHome = URI.file('/home/user');
65
const planDir = URI.joinPath(userHome, '.claude', 'plans');
66
const tracker = new ExternalEditTracker([planDir]);
67
68
const nestedPlanFile = URI.joinPath(userHome, '.claude', 'plans', 'subfolder', 'nested-plan.md');
69
const regularFile = URI.file('/workspace/src/test.ts');
70
const stream = new SpyChatResponseStream();
71
72
await tracker.trackEdit('edit-4', [nestedPlanFile, regularFile], stream, CancellationToken.None);
73
74
// Only the regular file should be tracked
75
expect(stream.externalEditUris.length).toBe(1);
76
expect(stream.externalEditUris[0].toString()).toBe(regularFile.toString());
77
});
78
79
it('should not filter files with similar prefix outside ignored directory', async () => {
80
const userHome = URI.file('/home/user');
81
const planDir = URI.joinPath(userHome, '.claude', 'plans');
82
const tracker = new ExternalEditTracker([planDir]);
83
84
const similarFile = URI.joinPath(userHome, '.claude', 'plans-backup', 'file.md');
85
const regularFile = URI.file('/workspace/src/test.ts');
86
const stream = new SpyChatResponseStream();
87
88
await tracker.trackEdit('edit-5', [similarFile, regularFile], stream, CancellationToken.None);
89
90
// Both should be tracked because plans-backup is not the plans directory
91
expect(stream.externalEditUris.length).toBe(2);
92
});
93
94
it('should work when no ignore directories are provided', async () => {
95
const tracker = new ExternalEditTracker();
96
const file1 = URI.file('/workspace/src/file1.ts');
97
const file2 = URI.file('/workspace/src/file2.ts');
98
const stream = new SpyChatResponseStream();
99
100
await tracker.trackEdit('edit-6', [file1, file2], stream, CancellationToken.None);
101
102
// All files should be tracked
103
expect(stream.externalEditUris.length).toBe(2);
104
});
105
106
it('should not call externalEdit when all files are filtered', async () => {
107
const userHome = URI.file('/home/user');
108
const planDir = URI.joinPath(userHome, '.claude', 'plans');
109
const tracker = new ExternalEditTracker([planDir]);
110
111
const planFile1 = URI.joinPath(userHome, '.claude', 'plans', 'plan1.md');
112
const planFile2 = URI.joinPath(userHome, '.claude', 'plans', 'plan2.md');
113
const stream = new SpyChatResponseStream();
114
115
await tracker.trackEdit('edit-7', [planFile1, planFile2], stream, CancellationToken.None);
116
117
// No files should be tracked
118
expect(stream.externalEditUris.length).toBe(0);
119
});
120
});
121
});
122
123