From 5b30ae5622b584383589fc2ec14cc9e1bd91dea8 Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Tue, 9 May 2023 17:25:29 -0700 Subject: [PATCH 1/2] Add `createEnvironment.contentButton` setting --- package.json | 17 +- package.nls.json | 1 + src/client/common/vscodeApis/workspaceApis.ts | 8 +- src/client/extensionActivation.ts | 4 +- ...CreateEnv.ts => createEnvButtonContext.ts} | 33 +- .../createEnvButtonContext.unit.test.ts | 346 ++++++++++++++++++ .../pyprojectTomlCreateEnv.unit.test.ts | 216 ----------- 7 files changed, 395 insertions(+), 230 deletions(-) rename src/client/pythonEnvironments/creation/{pyprojectTomlCreateEnv.ts => createEnvButtonContext.ts} (50%) create mode 100644 src/test/pythonEnvironments/creation/createEnvButtonContext.unit.test.ts delete mode 100644 src/test/pythonEnvironments/creation/pyprojectTomlCreateEnv.unit.test.ts diff --git a/package.json b/package.json index 34d76ae4d9e8..fd92481c7f84 100644 --- a/package.json +++ b/package.json @@ -404,6 +404,19 @@ "type": "array", "uniqueItems": true }, + "python.createEnvironment.contentButton": { + "default": "show", + "markdownDescription": "%python.createEnvironment.contentButton.description%", + "scope": "machine-overridable", + "type": "string", + "enum": [ + "show", + "hide" + ], + "tags": [ + "experimental" + ] + }, "python.condaPath": { "default": "", "description": "%python.condaPath.description%", @@ -1707,12 +1720,12 @@ { "group": "Python", "command": "python.createEnvironment-button", - "when": "resourceLangId == pip-requirements && !virtualWorkspace && shellExecutionSupported && !inDiffEditor" + "when": "showCreateEnvButton && resourceLangId == pip-requirements && !virtualWorkspace && shellExecutionSupported && !inDiffEditor" }, { "group": "Python", "command": "python.createEnvironment-button", - "when": "resourceFilename == pyproject.toml && pipInstallableToml && !virtualWorkspace && shellExecutionSupported && !inDiffEditor" + "when": "showCreateEnvButton && resourceFilename == pyproject.toml && pipInstallableToml && !virtualWorkspace && shellExecutionSupported && !inDiffEditor" } ], "editor/context": [ diff --git a/package.nls.json b/package.nls.json index cfbbeb7d41d5..db772a70ee13 100644 --- a/package.nls.json +++ b/package.nls.json @@ -25,6 +25,7 @@ "python.command.python.analysis.restartLanguageServer.title": "Restart Language Server", "python.command.python.launchTensorBoard.title": "Launch TensorBoard", "python.command.python.refreshTensorBoard.title": "Refresh TensorBoard", + "python.createEnvironment.contentButton.description": "Show or hide Create Environment button in the editor for `requirements` or dependency files.", "python.menu.createNewFile.title": "Python File", "python.editor.context.submenu.runPython": "Run Python", "python.editor.context.submenu.runPythonInteractive": "Run in Interactive window", diff --git a/src/client/common/vscodeApis/workspaceApis.ts b/src/client/common/vscodeApis/workspaceApis.ts index 74200ba46924..20528c17ec11 100644 --- a/src/client/common/vscodeApis/workspaceApis.ts +++ b/src/client/common/vscodeApis/workspaceApis.ts @@ -48,10 +48,14 @@ export function getOpenTextDocuments(): readonly vscode.TextDocument[] { return vscode.workspace.textDocuments; } -export function onDidOpenTextDocument(handler: (doc: vscode.TextDocument) => void): vscode.Disposable { +export function onDidOpenTextDocument(handler: (doc: vscode.TextDocument) => unknown): vscode.Disposable { return vscode.workspace.onDidOpenTextDocument(handler); } -export function onDidChangeTextDocument(handler: (e: vscode.TextDocumentChangeEvent) => void): vscode.Disposable { +export function onDidChangeTextDocument(handler: (e: vscode.TextDocumentChangeEvent) => unknown): vscode.Disposable { return vscode.workspace.onDidChangeTextDocument(handler); } + +export function onDidChangeConfiguration(handler: (e: vscode.ConfigurationChangeEvent) => unknown): vscode.Disposable { + return vscode.workspace.onDidChangeConfiguration(handler); +} diff --git a/src/client/extensionActivation.ts b/src/client/extensionActivation.ts index ba7bdf61c8f2..542a6ccc3010 100644 --- a/src/client/extensionActivation.ts +++ b/src/client/extensionActivation.ts @@ -54,7 +54,7 @@ import { DynamicPythonDebugConfigurationService } from './debugger/extension/con import { registerCreateEnvironmentFeatures } from './pythonEnvironments/creation/createEnvApi'; import { IInterpreterQuickPick } from './interpreter/configuration/types'; import { registerInstallFormatterPrompt } from './providers/prompts/installFormatterPrompt'; -import { registerPyProjectTomlCreateEnvFeatures } from './pythonEnvironments/creation/pyprojectTomlCreateEnv'; +import { registerCreateEnvButtonFeatures } from './pythonEnvironments/creation/createEnvButtonContext'; export async function activateComponents( // `ext` is passed to any extra activation funcs. @@ -98,7 +98,7 @@ export function activateFeatures(ext: ExtensionState, _components: Components): ); const pathUtils = ext.legacyIOC.serviceContainer.get(IPathUtils); registerCreateEnvironmentFeatures(ext.disposables, interpreterQuickPick, interpreterPathService, pathUtils); - registerPyProjectTomlCreateEnvFeatures(ext.disposables); + registerCreateEnvButtonFeatures(ext.disposables); } /// ////////////////////////// diff --git a/src/client/pythonEnvironments/creation/pyprojectTomlCreateEnv.ts b/src/client/pythonEnvironments/creation/createEnvButtonContext.ts similarity index 50% rename from src/client/pythonEnvironments/creation/pyprojectTomlCreateEnv.ts rename to src/client/pythonEnvironments/creation/createEnvButtonContext.ts index 5ead37b80dc9..cd009bc6118a 100644 --- a/src/client/pythonEnvironments/creation/pyprojectTomlCreateEnv.ts +++ b/src/client/pythonEnvironments/creation/createEnvButtonContext.ts @@ -8,6 +8,8 @@ import { onDidOpenTextDocument, onDidChangeTextDocument, getOpenTextDocuments, + getConfiguration, + onDidChangeConfiguration, } from '../../common/vscodeApis/workspaceApis'; import { isPipInstallableToml } from './provider/venvUtils'; @@ -19,7 +21,13 @@ async function setPyProjectTomlContextKey(doc: TextDocument): Promise { } } -export function registerPyProjectTomlCreateEnvFeatures(disposables: IDisposableRegistry): void { +async function setShowCreateEnvButtonContextKey(): Promise { + const config = getConfiguration('python'); + const showCreateEnvButton = config.get('createEnvironment.contentButton', 'show') === 'show'; + await executeCommand('setContext', 'showCreateEnvButton', showCreateEnvButton); +} + +export function registerCreateEnvButtonFeatures(disposables: IDisposableRegistry): void { disposables.push( onDidOpenTextDocument(async (doc: TextDocument) => { if (doc.fileName.endsWith('pyproject.toml')) { @@ -27,15 +35,24 @@ export function registerPyProjectTomlCreateEnvFeatures(disposables: IDisposableR } }), onDidChangeTextDocument(async (e: TextDocumentChangeEvent) => { - if (e.document.fileName.endsWith('pyproject.toml')) { - await setPyProjectTomlContextKey(e.document); + const doc = e.document; + if (doc.fileName.endsWith('pyproject.toml')) { + await setPyProjectTomlContextKey(doc); } }), + onDidChangeConfiguration(async () => { + await setShowCreateEnvButtonContextKey(); + }), ); - getOpenTextDocuments().forEach(async (doc: TextDocument) => { - if (doc.fileName.endsWith('pyproject.toml')) { - await setPyProjectTomlContextKey(doc); - } - }); + setShowCreateEnvButtonContextKey(); + + const docs = getOpenTextDocuments().filter( + (doc) => doc.fileName.endsWith('pyproject.toml') && isPipInstallableToml(doc.getText()), + ); + if (docs.length > 0) { + executeCommand('setContext', 'pipInstallableToml', true); + } else { + executeCommand('setContext', 'pipInstallableToml', false); + } } diff --git a/src/test/pythonEnvironments/creation/createEnvButtonContext.unit.test.ts b/src/test/pythonEnvironments/creation/createEnvButtonContext.unit.test.ts new file mode 100644 index 000000000000..31842420dd59 --- /dev/null +++ b/src/test/pythonEnvironments/creation/createEnvButtonContext.unit.test.ts @@ -0,0 +1,346 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import * as chaiAsPromised from 'chai-as-promised'; +import * as sinon from 'sinon'; +import * as typemoq from 'typemoq'; +import { assert, use as chaiUse } from 'chai'; +import { TextDocument, TextDocumentChangeEvent, WorkspaceConfiguration } from 'vscode'; +import * as cmdApis from '../../../client/common/vscodeApis/commandApis'; +import * as workspaceApis from '../../../client/common/vscodeApis/workspaceApis'; +import { IDisposableRegistry } from '../../../client/common/types'; +import { registerCreateEnvButtonFeatures } from '../../../client/pythonEnvironments/creation/createEnvButtonContext'; + +chaiUse(chaiAsPromised); + +class FakeDisposable { + public dispose() { + // Do nothing + } +} + +function getInstallableToml(): typemoq.IMock { + const pyprojectTomlPath = 'pyproject.toml'; + const pyprojectToml = typemoq.Mock.ofType(); + pyprojectToml.setup((p) => p.fileName).returns(() => pyprojectTomlPath); + pyprojectToml + .setup((p) => p.getText(typemoq.It.isAny())) + .returns( + () => + '[project]\nname = "spam"\nversion = "2020.0.0"\n[build-system]\nrequires = ["setuptools ~= 58.0", "cython ~= 0.29.0"]\n[project.optional-dependencies]\ntest = ["pytest"]\ndoc = ["sphinx", "furo"]', + ); + return pyprojectToml; +} + +function getNonInstallableToml(): typemoq.IMock { + const pyprojectTomlPath = 'pyproject.toml'; + const pyprojectToml = typemoq.Mock.ofType(); + pyprojectToml.setup((p) => p.fileName).returns(() => pyprojectTomlPath); + pyprojectToml + .setup((p) => p.getText(typemoq.It.isAny())) + .returns(() => '[project]\nname = "spam"\nversion = "2020.0.0"\n'); + return pyprojectToml; +} + +function getSomeFile(): typemoq.IMock { + const someFilePath = 'something.py'; + const someFile = typemoq.Mock.ofType(); + someFile.setup((p) => p.fileName).returns(() => someFilePath); + someFile.setup((p) => p.getText(typemoq.It.isAny())).returns(() => 'print("Hello World")'); + return someFile; +} + +suite('PyProject.toml Create Env Features', () => { + let executeCommandStub: sinon.SinonStub; + const disposables: IDisposableRegistry = []; + let getOpenTextDocumentsStub: sinon.SinonStub; + let onDidOpenTextDocumentStub: sinon.SinonStub; + let onDidChangeTextDocumentStub: sinon.SinonStub; + let onDidChangeConfigurationStub: sinon.SinonStub; + let getConfigurationStub: sinon.SinonStub; + let configMock: typemoq.IMock; + + setup(() => { + executeCommandStub = sinon.stub(cmdApis, 'executeCommand'); + getOpenTextDocumentsStub = sinon.stub(workspaceApis, 'getOpenTextDocuments'); + onDidOpenTextDocumentStub = sinon.stub(workspaceApis, 'onDidOpenTextDocument'); + onDidChangeTextDocumentStub = sinon.stub(workspaceApis, 'onDidChangeTextDocument'); + getConfigurationStub = sinon.stub(workspaceApis, 'getConfiguration'); + onDidChangeConfigurationStub = sinon.stub(workspaceApis, 'onDidChangeConfiguration'); + + onDidOpenTextDocumentStub.returns(new FakeDisposable()); + onDidChangeTextDocumentStub.returns(new FakeDisposable()); + onDidChangeConfigurationStub.returns(new FakeDisposable()); + + configMock = typemoq.Mock.ofType(); + configMock.setup((c) => c.get(typemoq.It.isAny(), typemoq.It.isAny())).returns(() => 'show'); + getConfigurationStub.returns(configMock.object); + }); + + teardown(() => { + sinon.restore(); + disposables.forEach((d) => d.dispose()); + }); + + test('python.createEnvironment.contentButton setting is set to "show", no files open', async () => { + getOpenTextDocumentsStub.returns([]); + + registerCreateEnvButtonFeatures(disposables); + + assert.ok(executeCommandStub.calledWithExactly('setContext', 'showCreateEnvButton', true)); + assert.ok(executeCommandStub.calledWithExactly('setContext', 'pipInstallableToml', false)); + }); + + test('python.createEnvironment.contentButton setting is set to "hide", no files open', async () => { + configMock.reset(); + configMock.setup((c) => c.get(typemoq.It.isAny(), typemoq.It.isAny())).returns(() => 'hide'); + getOpenTextDocumentsStub.returns([]); + + registerCreateEnvButtonFeatures(disposables); + + assert.ok(executeCommandStub.calledWithExactly('setContext', 'showCreateEnvButton', false)); + assert.ok(executeCommandStub.calledWithExactly('setContext', 'pipInstallableToml', false)); + }); + + test('python.createEnvironment.contentButton setting changed from "hide" to "show"', async () => { + configMock.reset(); + configMock.setup((c) => c.get(typemoq.It.isAny(), typemoq.It.isAny())).returns(() => 'hide'); + getOpenTextDocumentsStub.returns([]); + + let handler: () => void = () => { + /* do nothing */ + }; + onDidChangeConfigurationStub.callsFake((callback) => { + handler = callback; + return new FakeDisposable(); + }); + + registerCreateEnvButtonFeatures(disposables); + assert.ok(executeCommandStub.calledWithExactly('setContext', 'showCreateEnvButton', false)); + assert.ok(executeCommandStub.calledWithExactly('setContext', 'pipInstallableToml', false)); + executeCommandStub.reset(); + + configMock.reset(); + configMock.setup((c) => c.get(typemoq.It.isAny(), typemoq.It.isAny())).returns(() => 'show'); + handler(); + + assert.ok(executeCommandStub.calledWithExactly('setContext', 'showCreateEnvButton', true)); + }); + + test('python.createEnvironment.contentButton setting changed from "show" to "hide"', async () => { + configMock.reset(); + configMock.setup((c) => c.get(typemoq.It.isAny(), typemoq.It.isAny())).returns(() => 'show'); + getOpenTextDocumentsStub.returns([]); + + let handler: () => void = () => { + /* do nothing */ + }; + onDidChangeConfigurationStub.callsFake((callback) => { + handler = callback; + return new FakeDisposable(); + }); + + registerCreateEnvButtonFeatures(disposables); + assert.ok(executeCommandStub.calledWithExactly('setContext', 'showCreateEnvButton', true)); + assert.ok(executeCommandStub.calledWithExactly('setContext', 'pipInstallableToml', false)); + executeCommandStub.reset(); + + configMock.reset(); + configMock.setup((c) => c.get(typemoq.It.isAny(), typemoq.It.isAny())).returns(() => 'hide'); + handler(); + + assert.ok(executeCommandStub.calledWithExactly('setContext', 'showCreateEnvButton', false)); + }); + + test('Installable pyproject.toml is already open in the editor on extension activate', async () => { + const pyprojectToml = getInstallableToml(); + getOpenTextDocumentsStub.returns([pyprojectToml.object]); + + registerCreateEnvButtonFeatures(disposables); + + assert.ok(executeCommandStub.calledWithExactly('setContext', 'pipInstallableToml', true)); + }); + + test('Non installable pyproject.toml is already open in the editor on extension activate', async () => { + const pyprojectToml = getNonInstallableToml(); + getOpenTextDocumentsStub.returns([pyprojectToml.object]); + + registerCreateEnvButtonFeatures(disposables); + + assert.ok(executeCommandStub.calledWithExactly('setContext', 'pipInstallableToml', false)); + }); + + test('Some random file open in the editor on extension activate', async () => { + const someFile = getSomeFile(); + getOpenTextDocumentsStub.returns([someFile.object]); + + registerCreateEnvButtonFeatures(disposables); + + assert.ok(executeCommandStub.calledWithExactly('setContext', 'pipInstallableToml', false)); + }); + + test('Installable pyproject.toml is opened in the editor', async () => { + getOpenTextDocumentsStub.returns([]); + + let handler: (doc: TextDocument) => void = () => { + /* do nothing */ + }; + onDidOpenTextDocumentStub.callsFake((callback) => { + handler = callback; + return new FakeDisposable(); + }); + + const pyprojectToml = getInstallableToml(); + + registerCreateEnvButtonFeatures(disposables); + assert.ok(executeCommandStub.neverCalledWith('setContext', 'pipInstallableToml', true)); + + handler(pyprojectToml.object); + assert.ok(executeCommandStub.calledWithExactly('setContext', 'pipInstallableToml', true)); + }); + + test('Non Installable pyproject.toml is opened in the editor', async () => { + getOpenTextDocumentsStub.returns([]); + + let handler: (doc: TextDocument) => void = () => { + /* do nothing */ + }; + onDidOpenTextDocumentStub.callsFake((callback) => { + handler = callback; + return new FakeDisposable(); + }); + + const pyprojectToml = getNonInstallableToml(); + + registerCreateEnvButtonFeatures(disposables); + assert.ok(executeCommandStub.calledWithExactly('setContext', 'pipInstallableToml', false)); + executeCommandStub.reset(); + + handler(pyprojectToml.object); + + assert.ok(executeCommandStub.calledWithExactly('setContext', 'pipInstallableToml', false)); + }); + + test('Some random file is opened in the editor', async () => { + getOpenTextDocumentsStub.returns([]); + + let handler: (doc: TextDocument) => void = () => { + /* do nothing */ + }; + onDidOpenTextDocumentStub.callsFake((callback) => { + handler = callback; + return new FakeDisposable(); + }); + + const someFile = getSomeFile(); + + registerCreateEnvButtonFeatures(disposables); + assert.ok(executeCommandStub.calledWithExactly('setContext', 'pipInstallableToml', false)); + executeCommandStub.reset(); + + handler(someFile.object); + + assert.ok(executeCommandStub.neverCalledWith('setContext', 'pipInstallableToml', false)); + }); + + test('Installable pyproject.toml is changed', async () => { + getOpenTextDocumentsStub.returns([]); + + let handler: (d: TextDocumentChangeEvent) => void = () => { + /* do nothing */ + }; + onDidChangeTextDocumentStub.callsFake((callback) => { + handler = callback; + return new FakeDisposable(); + }); + + const pyprojectToml = getInstallableToml(); + + registerCreateEnvButtonFeatures(disposables); + assert.ok(executeCommandStub.calledWithExactly('setContext', 'pipInstallableToml', false)); + + handler({ contentChanges: [], document: pyprojectToml.object, reason: undefined }); + + assert.ok(executeCommandStub.calledWithExactly('setContext', 'pipInstallableToml', true)); + }); + + test('Non Installable pyproject.toml is changed', async () => { + getOpenTextDocumentsStub.returns([]); + + let handler: (d: TextDocumentChangeEvent) => void = () => { + /* do nothing */ + }; + onDidChangeTextDocumentStub.callsFake((callback) => { + handler = callback; + return new FakeDisposable(); + }); + + const pyprojectToml = getNonInstallableToml(); + + registerCreateEnvButtonFeatures(disposables); + assert.ok(executeCommandStub.calledWithExactly('setContext', 'pipInstallableToml', false)); + executeCommandStub.reset(); + + handler({ contentChanges: [], document: pyprojectToml.object, reason: undefined }); + + assert.ok(executeCommandStub.calledOnceWithExactly('setContext', 'pipInstallableToml', false)); + }); + + test('Non Installable pyproject.toml is changed to Installable', async () => { + getOpenTextDocumentsStub.returns([]); + + let openHandler: (doc: TextDocument) => void = () => { + /* do nothing */ + }; + onDidOpenTextDocumentStub.callsFake((callback) => { + openHandler = callback; + return new FakeDisposable(); + }); + + let changeHandler: (d: TextDocumentChangeEvent) => void = () => { + /* do nothing */ + }; + onDidChangeTextDocumentStub.callsFake((callback) => { + changeHandler = callback; + return new FakeDisposable(); + }); + + const nonInatallablePyprojectToml = getNonInstallableToml(); + const installablePyprojectToml = getInstallableToml(); + + registerCreateEnvButtonFeatures(disposables); + assert.ok(executeCommandStub.calledWithExactly('setContext', 'pipInstallableToml', false)); + executeCommandStub.reset(); + + openHandler(nonInatallablePyprojectToml.object); + assert.ok(executeCommandStub.calledOnceWithExactly('setContext', 'pipInstallableToml', false)); + executeCommandStub.reset(); + + changeHandler({ contentChanges: [], document: installablePyprojectToml.object, reason: undefined }); + + assert.ok(executeCommandStub.calledOnceWithExactly('setContext', 'pipInstallableToml', true)); + }); + + test('Some random file is changed', async () => { + getOpenTextDocumentsStub.returns([]); + + let handler: (d: TextDocumentChangeEvent) => void = () => { + /* do nothing */ + }; + onDidChangeTextDocumentStub.callsFake((callback) => { + handler = callback; + return new FakeDisposable(); + }); + + const someFile = getSomeFile(); + + registerCreateEnvButtonFeatures(disposables); + assert.ok(executeCommandStub.calledWithExactly('setContext', 'pipInstallableToml', false)); + executeCommandStub.reset(); + + handler({ contentChanges: [], document: someFile.object, reason: undefined }); + + assert.ok(executeCommandStub.notCalled); + }); +}); diff --git a/src/test/pythonEnvironments/creation/pyprojectTomlCreateEnv.unit.test.ts b/src/test/pythonEnvironments/creation/pyprojectTomlCreateEnv.unit.test.ts deleted file mode 100644 index 3f19aa5775b3..000000000000 --- a/src/test/pythonEnvironments/creation/pyprojectTomlCreateEnv.unit.test.ts +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -/* eslint-disable @typescript-eslint/no-explicit-any */ - -import * as chaiAsPromised from 'chai-as-promised'; -import * as sinon from 'sinon'; -import * as typemoq from 'typemoq'; -import { assert, use as chaiUse } from 'chai'; -import { TextDocument, TextDocumentChangeEvent } from 'vscode'; -import * as cmdApis from '../../../client/common/vscodeApis/commandApis'; -import * as workspaceApis from '../../../client/common/vscodeApis/workspaceApis'; -import { IDisposableRegistry } from '../../../client/common/types'; -import { registerPyProjectTomlCreateEnvFeatures } from '../../../client/pythonEnvironments/creation/pyprojectTomlCreateEnv'; - -chaiUse(chaiAsPromised); - -class FakeDisposable { - public dispose() { - // Do nothing - } -} - -function getInstallableToml(): typemoq.IMock { - const pyprojectTomlPath = 'pyproject.toml'; - const pyprojectToml = typemoq.Mock.ofType(); - pyprojectToml.setup((p) => p.fileName).returns(() => pyprojectTomlPath); - pyprojectToml - .setup((p) => p.getText(typemoq.It.isAny())) - .returns( - () => - '[project]\nname = "spam"\nversion = "2020.0.0"\n[build-system]\nrequires = ["setuptools ~= 58.0", "cython ~= 0.29.0"]\n[project.optional-dependencies]\ntest = ["pytest"]\ndoc = ["sphinx", "furo"]', - ); - return pyprojectToml; -} - -function getNonInstallableToml(): typemoq.IMock { - const pyprojectTomlPath = 'pyproject.toml'; - const pyprojectToml = typemoq.Mock.ofType(); - pyprojectToml.setup((p) => p.fileName).returns(() => pyprojectTomlPath); - pyprojectToml - .setup((p) => p.getText(typemoq.It.isAny())) - .returns(() => '[project]\nname = "spam"\nversion = "2020.0.0"\n'); - return pyprojectToml; -} - -function getSomeFile(): typemoq.IMock { - const someFilePath = 'something.py'; - const someFile = typemoq.Mock.ofType(); - someFile.setup((p) => p.fileName).returns(() => someFilePath); - someFile.setup((p) => p.getText(typemoq.It.isAny())).returns(() => 'print("Hello World")'); - return someFile; -} - -suite('PyProject.toml Create Env Features', () => { - let executeCommandStub: sinon.SinonStub; - const disposables: IDisposableRegistry = []; - let getOpenTextDocumentsStub: sinon.SinonStub; - let onDidOpenTextDocumentStub: sinon.SinonStub; - let onDidChangeTextDocumentStub: sinon.SinonStub; - - setup(() => { - executeCommandStub = sinon.stub(cmdApis, 'executeCommand'); - getOpenTextDocumentsStub = sinon.stub(workspaceApis, 'getOpenTextDocuments'); - onDidOpenTextDocumentStub = sinon.stub(workspaceApis, 'onDidOpenTextDocument'); - onDidChangeTextDocumentStub = sinon.stub(workspaceApis, 'onDidChangeTextDocument'); - - onDidOpenTextDocumentStub.returns(new FakeDisposable()); - onDidChangeTextDocumentStub.returns(new FakeDisposable()); - }); - - teardown(() => { - sinon.restore(); - disposables.forEach((d) => d.dispose()); - }); - - test('Installable pyproject.toml is already open in the editor on extension activate', async () => { - const pyprojectToml = getInstallableToml(); - getOpenTextDocumentsStub.returns([pyprojectToml.object]); - - registerPyProjectTomlCreateEnvFeatures(disposables); - - assert.ok(executeCommandStub.calledOnceWithExactly('setContext', 'pipInstallableToml', true)); - }); - - test('Non installable pyproject.toml is already open in the editor on extension activate', async () => { - const pyprojectToml = getNonInstallableToml(); - getOpenTextDocumentsStub.returns([pyprojectToml.object]); - - registerPyProjectTomlCreateEnvFeatures(disposables); - - assert.ok(executeCommandStub.calledOnceWithExactly('setContext', 'pipInstallableToml', false)); - }); - - test('Some random file open in the editor on extension activate', async () => { - const someFile = getSomeFile(); - getOpenTextDocumentsStub.returns([someFile.object]); - - registerPyProjectTomlCreateEnvFeatures(disposables); - - assert.ok(executeCommandStub.notCalled); - }); - - test('Installable pyproject.toml is opened in the editor', async () => { - getOpenTextDocumentsStub.returns([]); - - let handler: (doc: TextDocument) => void = () => { - /* do nothing */ - }; - onDidOpenTextDocumentStub.callsFake((callback) => { - handler = callback; - return new FakeDisposable(); - }); - - const pyprojectToml = getInstallableToml(); - - registerPyProjectTomlCreateEnvFeatures(disposables); - handler(pyprojectToml.object); - - assert.ok(executeCommandStub.calledOnceWithExactly('setContext', 'pipInstallableToml', true)); - }); - - test('Non Installable pyproject.toml is opened in the editor', async () => { - getOpenTextDocumentsStub.returns([]); - - let handler: (doc: TextDocument) => void = () => { - /* do nothing */ - }; - onDidOpenTextDocumentStub.callsFake((callback) => { - handler = callback; - return new FakeDisposable(); - }); - - const pyprojectToml = getNonInstallableToml(); - - registerPyProjectTomlCreateEnvFeatures(disposables); - handler(pyprojectToml.object); - - assert.ok(executeCommandStub.calledOnceWithExactly('setContext', 'pipInstallableToml', false)); - }); - - test('Some random file is opened in the editor', async () => { - getOpenTextDocumentsStub.returns([]); - - let handler: (doc: TextDocument) => void = () => { - /* do nothing */ - }; - onDidOpenTextDocumentStub.callsFake((callback) => { - handler = callback; - return new FakeDisposable(); - }); - - const someFile = getSomeFile(); - - registerPyProjectTomlCreateEnvFeatures(disposables); - handler(someFile.object); - - assert.ok(executeCommandStub.notCalled); - }); - - test('Installable pyproject.toml is changed', async () => { - getOpenTextDocumentsStub.returns([]); - - let handler: (d: TextDocumentChangeEvent) => void = () => { - /* do nothing */ - }; - onDidChangeTextDocumentStub.callsFake((callback) => { - handler = callback; - return new FakeDisposable(); - }); - - const pyprojectToml = getInstallableToml(); - - registerPyProjectTomlCreateEnvFeatures(disposables); - handler({ contentChanges: [], document: pyprojectToml.object, reason: undefined }); - - assert.ok(executeCommandStub.calledOnceWithExactly('setContext', 'pipInstallableToml', true)); - }); - - test('Non Installable pyproject.toml is changed', async () => { - getOpenTextDocumentsStub.returns([]); - - let handler: (d: TextDocumentChangeEvent) => void = () => { - /* do nothing */ - }; - onDidChangeTextDocumentStub.callsFake((callback) => { - handler = callback; - return new FakeDisposable(); - }); - - const pyprojectToml = getNonInstallableToml(); - - registerPyProjectTomlCreateEnvFeatures(disposables); - handler({ contentChanges: [], document: pyprojectToml.object, reason: undefined }); - - assert.ok(executeCommandStub.calledOnceWithExactly('setContext', 'pipInstallableToml', false)); - }); - - test('Some random file is changed', async () => { - getOpenTextDocumentsStub.returns([]); - - let handler: (d: TextDocumentChangeEvent) => void = () => { - /* do nothing */ - }; - onDidChangeTextDocumentStub.callsFake((callback) => { - handler = callback; - return new FakeDisposable(); - }); - - const someFile = getSomeFile(); - - registerPyProjectTomlCreateEnvFeatures(disposables); - handler({ contentChanges: [], document: someFile.object, reason: undefined }); - - assert.ok(executeCommandStub.notCalled); - }); -}); From 7ae8061453539533f978b8f3e56fe1071d58758b Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Tue, 9 May 2023 18:24:02 -0700 Subject: [PATCH 2/2] Update package.nls.json Co-authored-by: Luciana Abud <45497113+luabud@users.noreply.github.com> --- package.nls.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.nls.json b/package.nls.json index db772a70ee13..187b0d832743 100644 --- a/package.nls.json +++ b/package.nls.json @@ -25,7 +25,7 @@ "python.command.python.analysis.restartLanguageServer.title": "Restart Language Server", "python.command.python.launchTensorBoard.title": "Launch TensorBoard", "python.command.python.refreshTensorBoard.title": "Refresh TensorBoard", - "python.createEnvironment.contentButton.description": "Show or hide Create Environment button in the editor for `requirements` or dependency files.", + "python.createEnvironment.contentButton.description": "Show or hide Create Environment button in the editor for `requirements.txt` or other dependency files.", "python.menu.createNewFile.title": "Python File", "python.editor.context.submenu.runPython": "Run Python", "python.editor.context.submenu.runPythonInteractive": "Run in Interactive window",