From cac5bb2a11ae2f0bbf28d36481d04e5f955a0da9 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Thu, 19 Sep 2024 10:44:27 -0700 Subject: [PATCH] Add a command copilot calls back to ensure testing is set up (#24128) See https://github.com/microsoft/vscode-copilot/wiki/Package.json-Copilot-Contributions --- package.json | 6 +++++ src/client/common/application/commands.ts | 1 + src/client/common/constants.ts | 1 + src/client/common/utils/localize.ts | 1 + src/client/testing/common/types.ts | 1 + src/client/testing/configuration/index.ts | 5 ++++ src/client/testing/main.ts | 27 ++++++++++++++++++++- src/test/testing/configuration.unit.test.ts | 9 +++++++ 8 files changed, 50 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index af945dc2754e..3b1518f40121 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "onDebugInitialConfigurations", "onLanguage:python", "onDebugResolve:python", + "onCommand:python.copilotSetupTests", "workspaceContains:mspythonconfig.json", "workspaceContains:pyproject.toml", "workspaceContains:Pipfile", @@ -1517,6 +1518,11 @@ } ] }, + "copilot": { + "tests": { + "getSetupConfirmation": "python.copilotSetupTests" + } + }, "scripts": { "package": "gulp clean && gulp prePublishBundle && vsce package -o ms-python-insiders.vsix", "prePublish": "gulp clean && gulp prePublishNonBundle", diff --git a/src/client/common/application/commands.ts b/src/client/common/application/commands.ts index 4580a91a78d1..36f8c6d2ac17 100644 --- a/src/client/common/application/commands.ts +++ b/src/client/common/application/commands.ts @@ -103,6 +103,7 @@ export interface ICommandNameArgumentTypeMapping extends ICommandNameWithoutArgu [Commands.Exec_In_Terminal_Icon]: [undefined, Uri]; [Commands.Debug_In_Terminal]: [Uri]; [Commands.Tests_Configure]: [undefined, undefined | CommandSource, undefined | Uri]; + [Commands.Tests_CopilotSetup]: [undefined | Uri]; [Commands.LaunchTensorBoard]: [TensorBoardEntrypoint, TensorBoardEntrypointTrigger]; ['workbench.view.testing.focus']: []; ['cursorMove']: [ diff --git a/src/client/common/constants.ts b/src/client/common/constants.ts index 23e9c131b25c..df585abe1653 100644 --- a/src/client/common/constants.ts +++ b/src/client/common/constants.ts @@ -64,6 +64,7 @@ export namespace Commands { export const Start_REPL = 'python.startREPL'; export const Start_Native_REPL = 'python.startNativeREPL'; export const Tests_Configure = 'python.configureTests'; + export const Tests_CopilotSetup = 'python.copilotSetupTests'; export const TriggerEnvironmentSelection = 'python.triggerEnvSelection'; export const ViewOutput = 'python.viewOutput'; } diff --git a/src/client/common/utils/localize.ts b/src/client/common/utils/localize.ts index beed5a8999ea..3e11b1ca177b 100644 --- a/src/client/common/utils/localize.ts +++ b/src/client/common/utils/localize.ts @@ -432,6 +432,7 @@ export namespace Testing { export const errorUnittestExecution = l10n.t('Unittest test execution error'); export const cancelPytestExecution = l10n.t('Canceled pytest test execution'); export const errorPytestExecution = l10n.t('pytest test execution error'); + export const copilotSetupMessage = l10n.t('Confirm your Python testing framework to enable test discovery.'); } export namespace OutdatedDebugger { diff --git a/src/client/testing/common/types.ts b/src/client/testing/common/types.ts index 29a6de7768cb..67a9a44a5706 100644 --- a/src/client/testing/common/types.ts +++ b/src/client/testing/common/types.ts @@ -61,6 +61,7 @@ export interface ITestsHelper { export const ITestConfigurationService = Symbol('ITestConfigurationService'); export interface ITestConfigurationService { + hasConfiguredTests(wkspace: Uri): boolean; displayTestFrameworkError(wkspace: Uri): Promise; selectTestRunner(placeHolderMessage: string): Promise; enableTest(wkspace: Uri, product: UnitTestProduct): Promise; diff --git a/src/client/testing/configuration/index.ts b/src/client/testing/configuration/index.ts index e85154e72738..4825e9aa4f6a 100644 --- a/src/client/testing/configuration/index.ts +++ b/src/client/testing/configuration/index.ts @@ -35,6 +35,11 @@ export class UnitTestConfigurationService implements ITestConfigurationService { this.workspaceService = serviceContainer.get(IWorkspaceService); } + public hasConfiguredTests(wkspace: Uri): boolean { + const settings = this.configurationService.getSettings(wkspace); + return settings.testing.pytestEnabled || settings.testing.unittestEnabled || false; + } + public async displayTestFrameworkError(wkspace: Uri): Promise { const settings = this.configurationService.getSettings(wkspace); let enabledCount = settings.testing.pytestEnabled ? 1 : 0; diff --git a/src/client/testing/main.ts b/src/client/testing/main.ts index deebf2b34c06..c2675ed4a72b 100644 --- a/src/client/testing/main.ts +++ b/src/client/testing/main.ts @@ -1,7 +1,7 @@ 'use strict'; import { inject, injectable } from 'inversify'; -import { ConfigurationChangeEvent, Disposable, Uri, tests, TestResultState, WorkspaceFolder } from 'vscode'; +import { ConfigurationChangeEvent, Disposable, Uri, tests, TestResultState, WorkspaceFolder, Command } from 'vscode'; import { IApplicationShell, ICommandManager, IContextKeyManager, IWorkspaceService } from '../common/application/types'; import * as constants from '../common/constants'; import '../common/extensions'; @@ -170,6 +170,31 @@ export class UnitTestManagementService implements IExtensionActivationService { this.testController?.refreshTestData(resource, { forceRefresh: true }); }, ), + commandManager.registerCommand(constants.Commands.Tests_CopilotSetup, (resource?: Uri): + | { message: string; command: Command } + | undefined => { + const wkspaceFolder = + this.workspaceService.getWorkspaceFolder(resource) || this.workspaceService.workspaceFolders?.at(0); + if (!wkspaceFolder) { + return undefined; + } + + const configurationService = this.serviceContainer.get( + ITestConfigurationService, + ); + if (configurationService.hasConfiguredTests(wkspaceFolder.uri)) { + return undefined; + } + + return { + message: Testing.copilotSetupMessage, + command: { + title: Testing.configureTests, + command: constants.Commands.Tests_Configure, + arguments: [undefined, constants.CommandSource.ui, resource], + }, + }; + }), ); } diff --git a/src/test/testing/configuration.unit.test.ts b/src/test/testing/configuration.unit.test.ts index abb57aac2309..6682abf019b8 100644 --- a/src/test/testing/configuration.unit.test.ts +++ b/src/test/testing/configuration.unit.test.ts @@ -250,6 +250,15 @@ suite('Unit Tests - ConfigurationService', () => { expect(selectedItem).to.be.equal(undefined, 'invalid value'); appShell.verifyAll(); }); + test('Correctly returns hasConfiguredTests', () => { + let enabled = false; + unitTestSettings.setup((u) => u.unittestEnabled).returns(() => false); + unitTestSettings.setup((u) => u.pytestEnabled).returns(() => enabled); + + expect(testConfigService.target.hasConfiguredTests(workspaceUri)).to.equal(false); + enabled = true; + expect(testConfigService.target.hasConfiguredTests(workspaceUri)).to.equal(true); + }); test('Prompt to enable a test if a test framework is not enabled', async () => { unitTestSettings.setup((u) => u.pytestEnabled).returns(() => false); unitTestSettings.setup((u) => u.unittestEnabled).returns(() => false);