diff --git a/.vscode/launch.json b/.vscode/launch.json index 152f2b5c..0536c79e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -14,6 +14,9 @@ "preLaunchTask": "${defaultBuildTask}" }, { + "name": "Unit Tests", + "type": "node", + "request": "launch", "args": [ "-u=tdd", "--timeout=180000", @@ -24,12 +27,9 @@ "./out/test/**/*.unit.test.js" ], "internalConsoleOptions": "openOnSessionStart", - "name": "Unit Tests", "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", - "request": "launch", "skipFiles": ["/**"], "outFiles": ["${workspaceFolder}/out/**/*.js", "!${workspaceFolder}/**/node_modules**/*"], - "type": "node", "preLaunchTask": "tasks: watch-tests" }, { diff --git a/package.json b/package.json index c8152753..5c73335d 100644 --- a/package.json +++ b/package.json @@ -278,11 +278,11 @@ { "command": "python-envs.create", "group": "inline", - "when": "view == env-managers && viewItem =~ /.*pythonEnvManager.*-create.*/" + "when": "view == env-managers && viewItem =~ /.*pythonEnvManager.*;create;.*/" }, { "command": "python-envs.remove", - "when": "view == env-managers && viewItem =~ /.*pythonEnvironment.*-remove.*/" + "when": "view == env-managers && viewItem =~ /.*pythonEnvironment.*;remove;.*/" }, { "command": "python-envs.setEnv", @@ -297,7 +297,7 @@ { "command": "python-envs.createTerminal", "group": "inline", - "when": "view == env-managers && viewItem =~ /.*pythonEnvironment.*activatable.*/" + "when": "view == env-managers && viewItem =~ /.*pythonEnvironment.*;activatable;.*/" }, { "command": "python-envs.refreshPackages", diff --git a/src/features/views/treeViewItems.ts b/src/features/views/treeViewItems.ts index 6c3901bd..b8b5d855 100644 --- a/src/features/views/treeViewItems.ts +++ b/src/features/views/treeViewItems.ts @@ -1,4 +1,4 @@ -import { TreeItem, TreeItemCollapsibleState, MarkdownString, Command, ThemeIcon, Uri } from 'vscode'; +import { TreeItem, TreeItemCollapsibleState, MarkdownString, Command, ThemeIcon } from 'vscode'; import { InternalEnvironmentManager, InternalPackageManager } from '../../internal.api'; import { PythonEnvironment, IconPath, Package, PythonProject } from '../../api'; import { removable } from './utils'; @@ -31,23 +31,14 @@ export class EnvManagerTreeItem implements EnvTreeItem { item.contextValue = this.getContextValue(); item.description = manager.description; item.tooltip = manager.tooltip; - this.setIcon(item); + item.iconPath = manager.iconPath; this.treeItem = item; } private getContextValue() { - const create = this.manager.supportsCreate ? '-create' : ''; - return `pythonEnvManager${create}`; - } - - private setIcon(item: TreeItem) { - const iconPath = this.manager.iconPath; - if (iconPath instanceof Uri && iconPath.fsPath.endsWith('__icon__.py')) { - item.resourceUri = iconPath; - item.iconPath = ThemeIcon.File; - } else { - item.iconPath = iconPath; - } + const create = this.manager.supportsCreate ? 'create' : ''; + const parts = ['pythonEnvManager', create, this.manager.id].filter(Boolean); + return parts.join(';') + ';'; } } @@ -55,28 +46,19 @@ export class PythonEnvTreeItem implements EnvTreeItem { public readonly kind = EnvTreeItemKind.environment; public readonly treeItem: TreeItem; constructor(public readonly environment: PythonEnvironment, public readonly parent: EnvManagerTreeItem) { - const item = new TreeItem(environment.displayName ?? environment.name, TreeItemCollapsibleState.Collapsed); + const item = new TreeItem(environment.displayName, TreeItemCollapsibleState.Collapsed); item.contextValue = this.getContextValue(); item.description = environment.description; item.tooltip = environment.tooltip; - this.setIcon(item); + item.iconPath = environment.iconPath; this.treeItem = item; } private getContextValue() { - const activatable = isActivatableEnvironment(this.environment) ? '-activatable' : ''; - const remove = this.parent.manager.supportsRemove ? '-remove' : ''; - return `pythonEnvironment${remove}${activatable}`; - } - - private setIcon(item: TreeItem) { - const iconPath = this.environment.iconPath; - if (iconPath instanceof Uri && iconPath.fsPath.endsWith('__icon__.py')) { - item.resourceUri = iconPath; - item.iconPath = ThemeIcon.File; - } else { - item.iconPath = iconPath; - } + const activatable = isActivatableEnvironment(this.environment) ? 'activatable' : ''; + const remove = this.parent.manager.supportsRemove ? 'remove' : ''; + const parts = ['pythonEnvironment', remove, activatable].filter(Boolean); + return parts.join(';') + ';'; } } @@ -244,7 +226,7 @@ export class ProjectEnvironment implements ProjectTreeItem { public readonly id: string; public readonly treeItem: TreeItem; constructor(public readonly parent: ProjectItem, public readonly environment: PythonEnvironment) { - this.id = ProjectEnvironment.getId(parent, environment); + this.id = this.getId(parent, environment); const item = new TreeItem( this.environment.displayName ?? this.environment.name, TreeItemCollapsibleState.Collapsed, @@ -252,23 +234,13 @@ export class ProjectEnvironment implements ProjectTreeItem { item.contextValue = 'python-env'; item.description = this.environment.description; item.tooltip = this.environment.tooltip; - this.setIcon(item); + item.iconPath = this.environment.iconPath; this.treeItem = item; } - static getId(workspace: ProjectItem, environment: PythonEnvironment): string { + getId(workspace: ProjectItem, environment: PythonEnvironment): string { return `${workspace.id}>>>${environment.envId}`; } - - private setIcon(item: TreeItem) { - const iconPath = this.environment.iconPath; - if (iconPath instanceof Uri && iconPath.fsPath.endsWith('__icon__.py')) { - item.resourceUri = iconPath; - item.iconPath = ThemeIcon.File; - } else { - item.iconPath = iconPath; - } - } } export class NoProjectEnvironment implements ProjectTreeItem { diff --git a/src/test/features/views/treeViewItems.unit.test.ts b/src/test/features/views/treeViewItems.unit.test.ts new file mode 100644 index 00000000..929f76f2 --- /dev/null +++ b/src/test/features/views/treeViewItems.unit.test.ts @@ -0,0 +1,241 @@ +import * as assert from 'assert'; +import { EnvManagerTreeItem, PythonEnvTreeItem } from '../../../features/views/treeViewItems'; +import { InternalEnvironmentManager, PythonEnvironmentImpl } from '../../../internal.api'; +import { Uri } from 'vscode'; + +suite('Test TreeView Items', () => { + suite('EnvManagerTreeItem', () => { + test('Context Value: no-create', () => { + const manager = new InternalEnvironmentManager('ms-python.python:test-manager', { + name: 'test', + description: 'test', + preferredPackageManagerId: 'pip', + refresh: () => Promise.resolve(), + getEnvironments: () => Promise.resolve([]), + resolve: () => Promise.resolve(undefined), + set: () => Promise.resolve(), + get: () => Promise.resolve(undefined), + }); + const item = new EnvManagerTreeItem(manager); + assert.equal(item.treeItem.contextValue, 'pythonEnvManager;ms-python.python:test-manager;'); + }); + + test('Context Value: with create', () => { + const manager = new InternalEnvironmentManager('ms-python.python:test-manager', { + name: 'test', + description: 'test', + preferredPackageManagerId: 'pip', + refresh: () => Promise.resolve(), + getEnvironments: () => Promise.resolve([]), + resolve: () => Promise.resolve(undefined), + set: () => Promise.resolve(), + get: () => Promise.resolve(undefined), + create: () => Promise.resolve(undefined), + }); + const item = new EnvManagerTreeItem(manager); + assert.equal(item.treeItem.contextValue, 'pythonEnvManager;create;ms-python.python:test-manager;'); + }); + + test('Name is used', () => { + const manager = new InternalEnvironmentManager('ms-python.python:test-manager', { + name: 'test', + description: 'test', + preferredPackageManagerId: 'pip', + refresh: () => Promise.resolve(), + getEnvironments: () => Promise.resolve([]), + resolve: () => Promise.resolve(undefined), + set: () => Promise.resolve(), + get: () => Promise.resolve(undefined), + }); + const item = new EnvManagerTreeItem(manager); + assert.equal(item.treeItem.label, manager.name); + }); + + test('DisplayName is used', () => { + const manager = new InternalEnvironmentManager('ms-python.python:test-manager', { + name: 'test', + displayName: 'Test', + description: 'test', + preferredPackageManagerId: 'pip', + refresh: () => Promise.resolve(), + getEnvironments: () => Promise.resolve([]), + resolve: () => Promise.resolve(undefined), + set: () => Promise.resolve(), + get: () => Promise.resolve(undefined), + }); + const item = new EnvManagerTreeItem(manager); + assert.equal(item.treeItem.label, manager.displayName); + }); + }); + + suite('PythonEnvTreeItem', () => { + const manager1 = new InternalEnvironmentManager('ms-python.python:test-manager', { + name: 'test', + displayName: 'Test', + description: 'test', + preferredPackageManagerId: 'pip', + refresh: () => Promise.resolve(), + getEnvironments: () => Promise.resolve([]), + resolve: () => Promise.resolve(undefined), + set: () => Promise.resolve(), + get: () => Promise.resolve(undefined), + }); + const managerItem1 = new EnvManagerTreeItem(manager1); + + const manager2 = new InternalEnvironmentManager('ms-python.python:test-manager', { + name: 'test', + displayName: 'Test', + description: 'test', + preferredPackageManagerId: 'pip', + refresh: () => Promise.resolve(), + getEnvironments: () => Promise.resolve([]), + resolve: () => Promise.resolve(undefined), + set: () => Promise.resolve(), + get: () => Promise.resolve(undefined), + create: () => Promise.resolve(undefined), + remove: () => Promise.resolve(), + }); + const managerItem2 = new EnvManagerTreeItem(manager2); + + test('Context Value: no-remove, no-activate', () => { + const env = new PythonEnvironmentImpl( + { + id: 'test-env', + managerId: manager1.id, + }, + { + name: 'test-env', + displayName: 'Test Env', + description: 'This is test environment', + displayPath: '/home/user/envs/.venv/bin/python', + version: '3.12.1', + environmentPath: Uri.file('/home/user/envs/.venv/bin/python'), + execInfo: { + run: { + executable: '/home/user/envs/.venv/bin/python', + }, + }, + sysPrefix: '/home/user/envs/.venv', + }, + ); + + const item = new PythonEnvTreeItem(env, managerItem1); + assert.equal(item.treeItem.contextValue, 'pythonEnvironment;'); + }); + + test('Context Value: no-remove, with activate', () => { + const env = new PythonEnvironmentImpl( + { + id: 'test-env', + managerId: manager1.id, + }, + { + name: 'test-env', + displayName: 'Test Env', + description: 'This is test environment', + displayPath: '/home/user/envs/.venv/bin/python', + version: '3.12.1', + environmentPath: Uri.file('/home/user/envs/.venv/bin/python'), + execInfo: { + run: { + executable: '/home/user/envs/.venv/bin/python', + }, + activation: [ + { + executable: '/home/user/envs/.venv/bin/activate', + }, + ], + }, + sysPrefix: '/home/user/envs/.venv', + }, + ); + + const item = new PythonEnvTreeItem(env, managerItem1); + assert.equal(item.treeItem.contextValue, 'pythonEnvironment;activatable;'); + }); + + test('Context Value: with remove, with activate', () => { + const env = new PythonEnvironmentImpl( + { + id: 'test-env', + managerId: manager2.id, + }, + { + name: 'test-env', + displayName: 'Test Env', + description: 'This is test environment', + displayPath: '/home/user/envs/.venv/bin/python', + version: '3.12.1', + environmentPath: Uri.file('/home/user/envs/.venv/bin/python'), + execInfo: { + run: { + executable: '/home/user/envs/.venv/bin/python', + }, + activation: [ + { + executable: '/home/user/envs/.venv/bin/activate', + }, + ], + }, + sysPrefix: '/home/user/envs/.venv', + }, + ); + + const item = new PythonEnvTreeItem(env, managerItem2); + assert.equal(item.treeItem.contextValue, 'pythonEnvironment;remove;activatable;'); + }); + + test('Context Value: with remove, no-activate', () => { + const env = new PythonEnvironmentImpl( + { + id: 'test-env', + managerId: manager2.id, + }, + { + name: 'test-env', + displayName: 'Test Env', + description: 'This is test environment', + displayPath: '/home/user/envs/.venv/bin/python', + version: '3.12.1', + environmentPath: Uri.file('/home/user/envs/.venv/bin/python'), + execInfo: { + run: { + executable: '/home/user/envs/.venv/bin/python', + }, + }, + sysPrefix: '/home/user/envs/.venv', + }, + ); + + const item = new PythonEnvTreeItem(env, managerItem2); + assert.equal(item.treeItem.contextValue, 'pythonEnvironment;remove;'); + }); + + test('Display Name is used', () => { + const env = new PythonEnvironmentImpl( + { + id: 'test-env', + managerId: manager1.id, + }, + { + name: 'test-env', + displayName: 'Test Env', + description: 'This is test environment', + displayPath: '/home/user/envs/.venv/bin/python', + version: '3.12.1', + environmentPath: Uri.file('/home/user/envs/.venv/bin/python'), + execInfo: { + run: { + executable: '/home/user/envs/.venv/bin/python', + }, + }, + sysPrefix: '/home/user/envs/.venv', + }, + ); + + const item = new PythonEnvTreeItem(env, managerItem1); + + assert.equal(item.treeItem.label, env.displayName); + }); + }); +});