Path: blob/main/extensions/copilot/src/extension/chatSessions/common/test/externalEditTracker.spec.ts
13405 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { describe, expect, it } from 'vitest';6import { SpyChatResponseStream } from '../../../../util/common/test/mockChatResponseStream';7import { CancellationToken } from '../../../../util/vs/base/common/cancellation';8import { URI } from '../../../../util/vs/base/common/uri';9import { ExternalEditTracker } from '../externalEditTracker';1011describe('ExternalEditTracker', () => {12describe('ignore directories', () => {13it('should filter out files in ignored directories', async () => {14const userHome = URI.file('/home/user');15const planDir = URI.joinPath(userHome, '.claude', 'plans');16const tracker = new ExternalEditTracker([planDir]);1718const planFile = URI.joinPath(userHome, '.claude', 'plans', 'test-plan.md');19const regularFile = URI.file('/workspace/src/test.ts');20const stream = new SpyChatResponseStream();2122await tracker.trackEdit('edit-1', [planFile, regularFile], stream, CancellationToken.None);2324// Only the regular file should be tracked25expect(stream.externalEditUris.length).toBe(1);26expect(stream.externalEditUris[0].toString()).toBe(regularFile.toString());27});2829it('should not filter files from other .claude subdirectories', async () => {30const userHome = URI.file('/home/user');31const planDir = URI.joinPath(userHome, '.claude', 'plans');32const tracker = new ExternalEditTracker([planDir]);3334const agentFile = URI.joinPath(userHome, '.claude', 'agents', 'my-agent.md');35const memoryFile = URI.joinPath(userHome, '.claude', 'CLAUDE.md');36const stream = new SpyChatResponseStream();3738await tracker.trackEdit('edit-2', [agentFile, memoryFile], stream, CancellationToken.None);3940// Both files should be tracked41expect(stream.externalEditUris.length).toBe(2);42});4344it('should handle multiple ignored directories', async () => {45const userHome = URI.file('/home/user');46const planDir = URI.joinPath(userHome, '.claude', 'plans');47const tempDir = URI.file('/tmp/claude-temp');48const tracker = new ExternalEditTracker([planDir, tempDir]);4950const planFile = URI.joinPath(userHome, '.claude', 'plans', 'plan.md');51const tempFile = URI.joinPath(tempDir, 'temp.txt');52const regularFile = URI.file('/workspace/src/test.ts');53const stream = new SpyChatResponseStream();5455await tracker.trackEdit('edit-3', [planFile, tempFile, regularFile], stream, CancellationToken.None);5657// Only the regular file should be tracked58expect(stream.externalEditUris.length).toBe(1);59expect(stream.externalEditUris[0].toString()).toBe(regularFile.toString());60});6162it('should handle nested files in ignored directories', async () => {63const userHome = URI.file('/home/user');64const planDir = URI.joinPath(userHome, '.claude', 'plans');65const tracker = new ExternalEditTracker([planDir]);6667const nestedPlanFile = URI.joinPath(userHome, '.claude', 'plans', 'subfolder', 'nested-plan.md');68const regularFile = URI.file('/workspace/src/test.ts');69const stream = new SpyChatResponseStream();7071await tracker.trackEdit('edit-4', [nestedPlanFile, regularFile], stream, CancellationToken.None);7273// Only the regular file should be tracked74expect(stream.externalEditUris.length).toBe(1);75expect(stream.externalEditUris[0].toString()).toBe(regularFile.toString());76});7778it('should not filter files with similar prefix outside ignored directory', async () => {79const userHome = URI.file('/home/user');80const planDir = URI.joinPath(userHome, '.claude', 'plans');81const tracker = new ExternalEditTracker([planDir]);8283const similarFile = URI.joinPath(userHome, '.claude', 'plans-backup', 'file.md');84const regularFile = URI.file('/workspace/src/test.ts');85const stream = new SpyChatResponseStream();8687await tracker.trackEdit('edit-5', [similarFile, regularFile], stream, CancellationToken.None);8889// Both should be tracked because plans-backup is not the plans directory90expect(stream.externalEditUris.length).toBe(2);91});9293it('should work when no ignore directories are provided', async () => {94const tracker = new ExternalEditTracker();95const file1 = URI.file('/workspace/src/file1.ts');96const file2 = URI.file('/workspace/src/file2.ts');97const stream = new SpyChatResponseStream();9899await tracker.trackEdit('edit-6', [file1, file2], stream, CancellationToken.None);100101// All files should be tracked102expect(stream.externalEditUris.length).toBe(2);103});104105it('should not call externalEdit when all files are filtered', async () => {106const userHome = URI.file('/home/user');107const planDir = URI.joinPath(userHome, '.claude', 'plans');108const tracker = new ExternalEditTracker([planDir]);109110const planFile1 = URI.joinPath(userHome, '.claude', 'plans', 'plan1.md');111const planFile2 = URI.joinPath(userHome, '.claude', 'plans', 'plan2.md');112const stream = new SpyChatResponseStream();113114await tracker.trackEdit('edit-7', [planFile1, planFile2], stream, CancellationToken.None);115116// No files should be tracked117expect(stream.externalEditUris.length).toBe(0);118});119});120});121122123