summaryrefslogtreecommitdiff
path: root/packages/vscode-ide-companion/src/recent-files-manager.test.ts
diff options
context:
space:
mode:
authorchristine betts <[email protected]>2025-07-21 17:54:37 +0000
committerGitHub <[email protected]>2025-07-21 17:54:37 +0000
commit9bdcdf97d883d860a1552a5da5b890e8a7ec6b8a (patch)
tree1c40835626074929fd84652a619e8e2696eb5f07 /packages/vscode-ide-companion/src/recent-files-manager.test.ts
parent45b764943ae9dd2d5f18ece0aaa13f84eaa4ad5c (diff)
[ide-mode] Keep track of recently-opened files and send them to the CLI (#4463)
Diffstat (limited to 'packages/vscode-ide-companion/src/recent-files-manager.test.ts')
-rw-r--r--packages/vscode-ide-companion/src/recent-files-manager.test.ts211
1 files changed, 211 insertions, 0 deletions
diff --git a/packages/vscode-ide-companion/src/recent-files-manager.test.ts b/packages/vscode-ide-companion/src/recent-files-manager.test.ts
new file mode 100644
index 00000000..27742ed2
--- /dev/null
+++ b/packages/vscode-ide-companion/src/recent-files-manager.test.ts
@@ -0,0 +1,211 @@
+/**
+ * @license
+ * Copyright 2025 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
+import * as vscode from 'vscode';
+import {
+ RecentFilesManager,
+ MAX_FILES,
+ MAX_FILE_AGE_MINUTES,
+} from './recent-files-manager.js';
+
+vi.mock('vscode', () => ({
+ EventEmitter: vi.fn(() => ({
+ event: vi.fn(),
+ fire: vi.fn(),
+ dispose: vi.fn(),
+ })),
+ window: {
+ onDidChangeActiveTextEditor: vi.fn(),
+ },
+ workspace: {
+ onDidDeleteFiles: vi.fn(),
+ onDidCloseTextDocument: vi.fn(),
+ onDidRenameFiles: vi.fn(),
+ },
+ Uri: {
+ file: (path: string) => ({
+ fsPath: path,
+ }),
+ },
+}));
+
+describe('RecentFilesManager', () => {
+ let context: vscode.ExtensionContext;
+
+ beforeEach(() => {
+ context = {
+ subscriptions: [],
+ } as unknown as vscode.ExtensionContext;
+ });
+
+ afterEach(() => {
+ vi.restoreAllMocks();
+ });
+
+ it('adds a file to the list', () => {
+ const manager = new RecentFilesManager(context);
+ const uri = vscode.Uri.file('/test/file1.txt');
+ manager.add(uri);
+ expect(manager.recentFiles).toHaveLength(1);
+ expect(manager.recentFiles[0].filePath).toBe('/test/file1.txt');
+ });
+
+ it('moves an existing file to the top', () => {
+ const manager = new RecentFilesManager(context);
+ const uri1 = vscode.Uri.file('/test/file1.txt');
+ const uri2 = vscode.Uri.file('/test/file2.txt');
+ manager.add(uri1);
+ manager.add(uri2);
+ manager.add(uri1);
+ expect(manager.recentFiles).toHaveLength(2);
+ expect(manager.recentFiles[0].filePath).toBe('/test/file1.txt');
+ });
+
+ it('does not exceed the max number of files', () => {
+ const manager = new RecentFilesManager(context);
+ for (let i = 0; i < MAX_FILES + 5; i++) {
+ const uri = vscode.Uri.file(`/test/file${i}.txt`);
+ manager.add(uri);
+ }
+ expect(manager.recentFiles).toHaveLength(MAX_FILES);
+ expect(manager.recentFiles[0].filePath).toBe(
+ `/test/file${MAX_FILES + 4}.txt`,
+ );
+ expect(manager.recentFiles[MAX_FILES - 1].filePath).toBe(`/test/file5.txt`);
+ });
+
+ it('fires onDidChange when a file is added', () => {
+ const manager = new RecentFilesManager(context);
+ const spy = vi.spyOn(manager['onDidChangeEmitter'], 'fire');
+ const uri = vscode.Uri.file('/test/file1.txt');
+ manager.add(uri);
+ expect(spy).toHaveBeenCalled();
+ });
+
+ it('removes a file when it is closed', () => {
+ const manager = new RecentFilesManager(context);
+ const uri = vscode.Uri.file('/test/file1.txt');
+ manager.add(uri);
+ expect(manager.recentFiles).toHaveLength(1);
+
+ // Simulate closing the file
+ const closeHandler = vi.mocked(vscode.workspace.onDidCloseTextDocument).mock
+ .calls[0][0];
+ closeHandler({ uri } as vscode.TextDocument);
+
+ expect(manager.recentFiles).toHaveLength(0);
+ });
+
+ it('fires onDidChange when a file is removed', () => {
+ const manager = new RecentFilesManager(context);
+ const uri = vscode.Uri.file('/test/file1.txt');
+ manager.add(uri);
+
+ const spy = vi.spyOn(manager['onDidChangeEmitter'], 'fire');
+ const closeHandler = vi.mocked(vscode.workspace.onDidCloseTextDocument).mock
+ .calls[0][0];
+ closeHandler({ uri } as vscode.TextDocument);
+
+ expect(spy).toHaveBeenCalled();
+ });
+
+ it('removes a file when it is deleted', () => {
+ const manager = new RecentFilesManager(context);
+ const uri1 = vscode.Uri.file('/test/file1.txt');
+ const uri2 = vscode.Uri.file('/test/file2.txt');
+ manager.add(uri1);
+ manager.add(uri2);
+ expect(manager.recentFiles).toHaveLength(2);
+
+ // Simulate deleting a file
+ const deleteHandler = vi.mocked(vscode.workspace.onDidDeleteFiles).mock
+ .calls[0][0];
+ deleteHandler({ files: [uri1] });
+
+ expect(manager.recentFiles).toHaveLength(1);
+ expect(manager.recentFiles[0].filePath).toBe('/test/file2.txt');
+ });
+
+ it('fires onDidChange when a file is deleted', () => {
+ const manager = new RecentFilesManager(context);
+ const uri = vscode.Uri.file('/test/file1.txt');
+ manager.add(uri);
+
+ const spy = vi.spyOn(manager['onDidChangeEmitter'], 'fire');
+ const deleteHandler = vi.mocked(vscode.workspace.onDidDeleteFiles).mock
+ .calls[0][0];
+ deleteHandler({ files: [uri] });
+
+ expect(spy).toHaveBeenCalled();
+ });
+
+ it('removes multiple files when they are deleted', () => {
+ const manager = new RecentFilesManager(context);
+ const uri1 = vscode.Uri.file('/test/file1.txt');
+ const uri2 = vscode.Uri.file('/test/file2.txt');
+ const uri3 = vscode.Uri.file('/test/file3.txt');
+ manager.add(uri1);
+ manager.add(uri2);
+ manager.add(uri3);
+ expect(manager.recentFiles).toHaveLength(3);
+
+ // Simulate deleting multiple files
+ const deleteHandler = vi.mocked(vscode.workspace.onDidDeleteFiles).mock
+ .calls[0][0];
+ deleteHandler({ files: [uri1, uri3] });
+
+ expect(manager.recentFiles).toHaveLength(1);
+ expect(manager.recentFiles[0].filePath).toBe('/test/file2.txt');
+ });
+
+ it('prunes files older than the max age', () => {
+ vi.useFakeTimers();
+
+ const manager = new RecentFilesManager(context);
+ const uri1 = vscode.Uri.file('/test/file1.txt');
+ manager.add(uri1);
+
+ // Advance time by more than the max age
+ const twoMinutesMs = (MAX_FILE_AGE_MINUTES + 1) * 60 * 1000;
+ vi.advanceTimersByTime(twoMinutesMs);
+
+ const uri2 = vscode.Uri.file('/test/file2.txt');
+ manager.add(uri2);
+
+ expect(manager.recentFiles).toHaveLength(1);
+ expect(manager.recentFiles[0].filePath).toBe('/test/file2.txt');
+
+ vi.useRealTimers();
+ });
+
+ it('fires onDidChange only once when adding an existing file', () => {
+ const manager = new RecentFilesManager(context);
+ const uri = vscode.Uri.file('/test/file1.txt');
+ manager.add(uri);
+
+ const spy = vi.spyOn(manager['onDidChangeEmitter'], 'fire');
+ manager.add(uri);
+ expect(spy).toHaveBeenCalledTimes(1);
+ });
+
+ it('updates the file when it is renamed', () => {
+ const manager = new RecentFilesManager(context);
+ const oldUri = vscode.Uri.file('/test/file1.txt');
+ const newUri = vscode.Uri.file('/test/file2.txt');
+ manager.add(oldUri);
+ expect(manager.recentFiles).toHaveLength(1);
+ expect(manager.recentFiles[0].filePath).toBe('/test/file1.txt');
+
+ // Simulate renaming the file
+ const renameHandler = vi.mocked(vscode.workspace.onDidRenameFiles).mock
+ .calls[0][0];
+ renameHandler({ files: [{ oldUri, newUri }] });
+
+ expect(manager.recentFiles).toHaveLength(1);
+ expect(manager.recentFiles[0].filePath).toBe('/test/file2.txt');
+ });
+});