From 98649f9cd720287efbf3578ac20c4c17d9da4ce6 Mon Sep 17 00:00:00 2001 From: Mohit Sahoo Date: Thu, 21 Sep 2023 09:15:53 +0530 Subject: [PATCH] feat(deleteAppMaps.ts): add closeEditorByUri function after deleting appmaps feat(contextMenu.ts): update deleteAppMap command to handle absence of item object feat(deleteAppMap.test.ts): add new integration test for deleteAppMap fix(closeEditorByUri.test.ts): fix test to include input conditions feat(deleteAppMaps.ts): add clear method to AppMapCollection after closing editors feat(appmapCollection.ts): introduce clear method in AppMapCollection interface feat(appmapCollectionFile.ts): implement clear method for AppMapCollectionFile class --- src/lib/deleteAppMaps.ts | 9 ++++- src/services/appmapCollection.ts | 2 + src/services/appmapCollectionFile.ts | 6 +++ src/tree/contextMenu.ts | 17 +++++++- test/integration/command/deleteAppMap.test.ts | 40 +++++++++++++++++++ test/integration/lib/closeEditorByUri.test.ts | 7 ++-- 6 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 test/integration/command/deleteAppMap.test.ts diff --git a/src/lib/deleteAppMaps.ts b/src/lib/deleteAppMaps.ts index 42583e4b..a4052752 100644 --- a/src/lib/deleteAppMaps.ts +++ b/src/lib/deleteAppMaps.ts @@ -1,5 +1,6 @@ import * as vscode from 'vscode'; import deleteAppMap from './deleteAppMap'; +import closeEditorByUri from './closeEditorByUri'; import { promisify } from 'util'; import { glob } from 'glob'; import AppMapCollection from '../services/appmapCollection'; @@ -14,7 +15,13 @@ export default async function deleteAppMaps( vscode.Uri.file(path) ); - await Promise.all(appmapFiles.map((uri) => deleteAppMap(uri, appMapCollection))); + await Promise.all( + appmapFiles.map((uri) => { + deleteAppMap(uri, appMapCollection); + closeEditorByUri(uri); + }) + ); + appMapCollection.clear(); return appmapFiles.length; } diff --git a/src/services/appmapCollection.ts b/src/services/appmapCollection.ts index 721e9728..f04a3291 100644 --- a/src/services/appmapCollection.ts +++ b/src/services/appmapCollection.ts @@ -18,4 +18,6 @@ export default interface AppMapCollection { allAppMapsForWorkspaceFolder(workspaceFolder: WorkspaceFolder): AppMapLoader[]; has(uri: vscode.Uri): boolean; + + clear(): void; } diff --git a/src/services/appmapCollectionFile.ts b/src/services/appmapCollectionFile.ts index 1d89acdf..76a76d47 100644 --- a/src/services/appmapCollectionFile.ts +++ b/src/services/appmapCollectionFile.ts @@ -140,4 +140,10 @@ export default class AppMapCollectionFile implements AppMapCollection, AppMapsSe public has(uri: vscode.Uri): boolean { return this.loaders.has(uri.fsPath); } + + public clear(): void { + console.debug('Clearing AppMap collection from tree', this.loaders.size); + this.loaders.clear(); + this.emitUpdated(undefined); + } } diff --git a/src/tree/contextMenu.ts b/src/tree/contextMenu.ts index a2bd5e61..c360b8ae 100644 --- a/src/tree/contextMenu.ts +++ b/src/tree/contextMenu.ts @@ -65,8 +65,21 @@ export default class ContextMenu { ); context.subscriptions.push( vscode.commands.registerCommand('appmap.context.deleteAppMap', async (item: AppMapLoader) => { - await deleteAppMap(item.descriptor.resourceUri, appmaps); - await closeEditorByUri(item.descriptor.resourceUri); + let uri: vscode.Uri; + if (!item) { + const { activeTab } = vscode.window.tabGroups.activeTabGroup; + if (!activeTab) { + vscode.window.showErrorMessage('No active editor.'); + return; + } + + uri = (activeTab.input as vscode.TabInputCustom).uri; + } else { + uri = item.descriptor.resourceUri; + } + + await deleteAppMap(uri, appmaps); + await closeEditorByUri(uri); }) ); context.subscriptions.push( diff --git a/test/integration/command/deleteAppMap.test.ts b/test/integration/command/deleteAppMap.test.ts new file mode 100644 index 00000000..584ec364 --- /dev/null +++ b/test/integration/command/deleteAppMap.test.ts @@ -0,0 +1,40 @@ +import * as vscode from 'vscode'; +import { assert } from 'chai'; +import { join } from 'path'; +import * as sinon from 'sinon'; +import { initializeWorkspace, ProjectA, waitForExtension } from '../util'; + +describe('deleteAppMap test', function () { + let sandbox: sinon.SinonSandbox; + + const appmapFilePath = join( + ProjectA, + 'tmp/appmap/minitest', + 'Microposts_controller_can_get_microposts_as_JSON.appmap.json' + ); + + beforeEach(() => (sandbox = sinon.createSandbox())); + beforeEach(initializeWorkspace); + beforeEach(async () => await waitForExtension()); + + afterEach(initializeWorkspace); + afterEach(() => sandbox.restore()); + + it('deletes appmap with matching URI', async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const mockUri: vscode.Uri = { path: appmapFilePath } as any; + await vscode.workspace.openTextDocument(mockUri); + await vscode.window.showTextDocument(vscode.Uri.file(mockUri.path)); + await vscode.commands.executeCommand('appmap.context.deleteAppMap'); + + const tabs = vscode.window.tabGroups.all.map((tg) => tg.tabs).flat(); + const index = tabs.findIndex( + (tab) => + (tab.input instanceof vscode.TabInputCustom || + tab.input instanceof vscode.TabInputText || + tab.input instanceof vscode.TabInputNotebook) && + tab.input.uri.path === mockUri.path + ); + assert.isTrue(index === -1); + }); +}); diff --git a/test/integration/lib/closeEditorByUri.test.ts b/test/integration/lib/closeEditorByUri.test.ts index 39780abc..69b7539f 100644 --- a/test/integration/lib/closeEditorByUri.test.ts +++ b/test/integration/lib/closeEditorByUri.test.ts @@ -33,9 +33,10 @@ describe('closeEditorByUri Tests', function () { const tabs = vscode.window.tabGroups.all.map((tg) => tg.tabs).flat(); const index = tabs.findIndex( (tab) => - tab.input instanceof vscode.TabInputCustom || - tab.input instanceof vscode.TabInputText || - (tab.input instanceof vscode.TabInputNotebook && tab.input.uri.path === mockUri.path) + (tab.input instanceof vscode.TabInputCustom || + tab.input instanceof vscode.TabInputText || + tab.input instanceof vscode.TabInputNotebook) && + tab.input.uri.path === mockUri.path ); assert.isTrue(index === -1);