From d0015f276116f07625670ea443f13c239ed4e537 Mon Sep 17 00:00:00 2001 From: Alena Khineika Date: Thu, 12 Aug 2021 21:56:29 +0200 Subject: [PATCH] VSCODE-247: Replace code lenses with code actions (#318) * feat: replace code lenses for partial execution with code actions VSCODE-247 * test: add code action tests * build: patch node gyp on windows * build: equal windows * build: revert * test: add integration test for partial playground execution * test: move timeout to suite * build: patch node gyp on windows with powershell * test: use command from code action --- .github/workflows/test-and-build.yaml | 7 + package-lock.json | 33 +--- package.json | 2 +- src/editors/codeActionProvider.ts | 29 +++ src/editors/editorsController.ts | 19 +- .../partialExecutionCodeLensProvider.ts | 92 ---------- src/editors/playgroundController.ts | 82 +-------- src/mdbExtensionController.ts | 9 +- src/test/fixture/.vscode/settings.json | 3 +- .../suite/editors/codeActionProvider.test.ts | 117 ++++++++++++ .../partialExecutionCodeLensProvider.test.ts | 38 ---- .../editors/playgroundController.test.ts | 169 ++---------------- .../language/languageServerController.test.ts | 37 ++-- 13 files changed, 215 insertions(+), 422 deletions(-) create mode 100644 src/editors/codeActionProvider.ts delete mode 100644 src/editors/partialExecutionCodeLensProvider.ts create mode 100644 src/test/suite/editors/codeActionProvider.test.ts delete mode 100644 src/test/suite/editors/partialExecutionCodeLensProvider.test.ts diff --git a/.github/workflows/test-and-build.yaml b/.github/workflows/test-and-build.yaml index 3211491fe..11dcf6fb7 100644 --- a/.github/workflows/test-and-build.yaml +++ b/.github/workflows/test-and-build.yaml @@ -45,6 +45,13 @@ jobs: - name: Install npm@7 run: npm install -g npm@7 + - name: Patch node gyp on Windows + if: ${{ runner.os == 'Windows' }} + shell: powershell + run: | + npm install --global node-gyp@latest + npm prefix -g | % {npm config set node_gyp "$_\node_modules\node-gyp\bin\node-gyp.js"} + - name: Install Dependencies run: | npm ci diff --git a/package-lock.json b/package-lock.json index e2a60f4f9..b42870c09 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "@mongosh/i18n": "^1.0.4", "@mongosh/service-provider-server": "^1.0.4", "@mongosh/shell-api": "^1.0.4", - "analytics-node": "^3.5.0", + "analytics-node": "^5.0.0", "bson": "^4.4.1", "classnames": "^2.3.1", "debug": "^4.3.2", @@ -3221,9 +3221,9 @@ } }, "node_modules/analytics-node": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/analytics-node/-/analytics-node-3.5.0.tgz", - "integrity": "sha512-XgQq6ejZHCehUSnZS4V7QJPLIP7S9OAWwQDYl4WTLtsRvc5fCxIwzK/yihzmIW51v9PnyBmrl9dMcqvwfOE8WA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/analytics-node/-/analytics-node-5.0.0.tgz", + "integrity": "sha512-eU/lPK6V3hmmv7hnJ8762zExL7WvcpVeBL6RgGZ8yQZQewk08VbbHk0M5GAlhruyh0ZT+/IBPW3Q/SYVhjexNQ==", "dependencies": { "@segment/loosely-validate-event": "^2.0.0", "axios": "^0.21.1", @@ -3232,20 +3232,12 @@ "md5": "^2.2.1", "ms": "^2.0.0", "remove-trailing-slash": "^0.1.0", - "uuid": "^3.2.1" + "uuid": "^8.3.2" }, "engines": { "node": ">=4" } }, - "node_modules/analytics-node/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "bin": { - "uuid": "bin/uuid" - } - }, "node_modules/ansi": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.1.tgz", @@ -26971,9 +26963,9 @@ } }, "analytics-node": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/analytics-node/-/analytics-node-3.5.0.tgz", - "integrity": "sha512-XgQq6ejZHCehUSnZS4V7QJPLIP7S9OAWwQDYl4WTLtsRvc5fCxIwzK/yihzmIW51v9PnyBmrl9dMcqvwfOE8WA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/analytics-node/-/analytics-node-5.0.0.tgz", + "integrity": "sha512-eU/lPK6V3hmmv7hnJ8762zExL7WvcpVeBL6RgGZ8yQZQewk08VbbHk0M5GAlhruyh0ZT+/IBPW3Q/SYVhjexNQ==", "requires": { "@segment/loosely-validate-event": "^2.0.0", "axios": "^0.21.1", @@ -26982,14 +26974,7 @@ "md5": "^2.2.1", "ms": "^2.0.0", "remove-trailing-slash": "^0.1.0", - "uuid": "^3.2.1" - }, - "dependencies": { - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - } + "uuid": "^8.3.2" } }, "ansi": { diff --git a/package.json b/package.json index 1b255fdd0..ac7d1890c 100644 --- a/package.json +++ b/package.json @@ -873,7 +873,7 @@ "@mongosh/i18n": "^1.0.4", "@mongosh/service-provider-server": "^1.0.4", "@mongosh/shell-api": "^1.0.4", - "analytics-node": "^3.5.0", + "analytics-node": "^5.0.0", "bson": "^4.4.1", "classnames": "^2.3.1", "debug": "^4.3.2", diff --git a/src/editors/codeActionProvider.ts b/src/editors/codeActionProvider.ts new file mode 100644 index 000000000..b209ddbce --- /dev/null +++ b/src/editors/codeActionProvider.ts @@ -0,0 +1,29 @@ +import * as vscode from 'vscode'; +import EXTENSION_COMMANDS from '../commands'; +import PlaygroundController from './playgroundController'; + +export default class CodeActionProvider implements vscode.CodeActionProvider { + _playgroundController: PlaygroundController; + + static readonly providedCodeActionKinds = [vscode.CodeActionKind.QuickFix]; + + constructor(playgroundController: PlaygroundController) { + this._playgroundController = playgroundController; + } + + provideCodeActions(): vscode.CodeAction[] | undefined { + if (!this._playgroundController._selectedText) { + return; + } + + const commandAction = new vscode.CodeAction('Run selected playground blocks', vscode.CodeActionKind.Empty); + + commandAction.command = { + command: EXTENSION_COMMANDS.MDB_RUN_SELECTED_PLAYGROUND_BLOCKS, + title: 'Run selected playground blocks', + tooltip: 'Run selected playground blocks' + }; + + return [commandAction]; + } +} diff --git a/src/editors/editorsController.ts b/src/editors/editorsController.ts index 5ed2ea3e6..408e0c0fe 100644 --- a/src/editors/editorsController.ts +++ b/src/editors/editorsController.ts @@ -2,6 +2,7 @@ import * as vscode from 'vscode'; import { EJSON } from 'bson'; import ActiveConnectionCodeLensProvider from './activeConnectionCodeLensProvider'; +import CodeActionProvider from './codeActionProvider'; import ConnectionController from '../connectionController'; import CollectionDocumentsCodeLensProvider from './collectionDocumentsCodeLensProvider'; import CollectionDocumentsOperationsStore from './collectionDocumentsOperationsStore'; @@ -22,7 +23,6 @@ import MongoDBDocumentService, { DOCUMENT_SOURCE_URI_IDENTIFIER, VIEW_DOCUMENT_SCHEME } from './mongoDBDocumentService'; -import PartialExecutionCodeLensProvider from './partialExecutionCodeLensProvider'; import PlaygroundController from './playgroundController'; import PlaygroundResultProvider, { PLAYGROUND_RESULT_SCHEME @@ -37,6 +37,7 @@ const log = createLogger('editors controller'); * new editors and the data they need. It also manages active editors. */ export default class EditorsController { + _codeActionProvider: CodeActionProvider; _connectionController: ConnectionController; _playgroundController: PlaygroundController; _collectionDocumentsOperationsStore = new CollectionDocumentsOperationsStore(); @@ -49,7 +50,6 @@ export default class EditorsController { _telemetryService: TelemetryService; _playgroundResultViewProvider: PlaygroundResultProvider; _activeConnectionCodeLensProvider: ActiveConnectionCodeLensProvider; - _partialExecutionCodeLensProvider: PartialExecutionCodeLensProvider; _editDocumentCodeLensProvider: EditDocumentCodeLensProvider; _collectionDocumentsCodeLensProvider: CollectionDocumentsCodeLensProvider; @@ -61,7 +61,7 @@ export default class EditorsController { telemetryService: TelemetryService, playgroundResultViewProvider: PlaygroundResultProvider, activeConnectionCodeLensProvider: ActiveConnectionCodeLensProvider, - partialExecutionCodeLensProvider: PartialExecutionCodeLensProvider, + codeActionProvider: CodeActionProvider, editDocumentCodeLensProvider: EditDocumentCodeLensProvider ) { log.info('activating...'); @@ -90,10 +90,10 @@ export default class EditorsController { ); this._playgroundResultViewProvider = playgroundResultViewProvider; this._activeConnectionCodeLensProvider = activeConnectionCodeLensProvider; - this._partialExecutionCodeLensProvider = partialExecutionCodeLensProvider; this._collectionDocumentsCodeLensProvider = new CollectionDocumentsCodeLensProvider( this._collectionDocumentsOperationsStore ); + this._codeActionProvider = codeActionProvider; vscode.workspace.onDidCloseTextDocument((e) => { const uriParams = new URLSearchParams(e.uri.query); @@ -372,12 +372,6 @@ export default class EditorsController { this._activeConnectionCodeLensProvider ) ); - this._context.subscriptions.push( - vscode.languages.registerCodeLensProvider( - { language: 'mongodb' }, - this._partialExecutionCodeLensProvider - ) - ); this._context.subscriptions.push( vscode.languages.registerCodeLensProvider( { @@ -396,6 +390,11 @@ export default class EditorsController { this._editDocumentCodeLensProvider ) ); + this._context.subscriptions.push( + vscode.languages.registerCodeActionsProvider('mongodb', this._codeActionProvider, { + providedCodeActionKinds: CodeActionProvider.providedCodeActionKinds + }) + ); } deactivate(): void { diff --git a/src/editors/partialExecutionCodeLensProvider.ts b/src/editors/partialExecutionCodeLensProvider.ts deleted file mode 100644 index d7f8529c8..000000000 --- a/src/editors/partialExecutionCodeLensProvider.ts +++ /dev/null @@ -1,92 +0,0 @@ -import * as vscode from 'vscode'; -import EXTENSION_COMMANDS from '../commands'; - -// Returns a boolean if the selection is one that is valid to show a -// code lens for (isn't a partial line etc.). -export function isSelectionValidForCodeLens( - selections: vscode.Selection[], - textDocument: vscode.TextDocument -): boolean { - if (selections.length > 1) { - // Show codelens when it's multi cursor. - return true; - } - - if (!selections[0].isSingleLine) { - // Show codelens when it's a multi-line selection. - return true; - } - - const lineContent = (textDocument.lineAt(selections[0].end.line).text || '').trim(); - const selectionContent = (textDocument.getText(selections[0]) || '').trim(); - - // Show codelens when it contains the whole line. - return lineContent === selectionContent; -} - -export function getCodeLensLineOffsetForSelection( - selections: vscode.Selection[], - editor: vscode.TextEditor -): number { - const lastSelection = selections[selections.length - 1]; - const lastSelectedLineNumber = lastSelection.end.line; - const lastSelectedLineContent = editor.document.lineAt(lastSelectedLineNumber).text || ''; - - // Show a code lens after the selected line, unless the - // contents of the selection in the last line is empty. - const lastSelectedLineContentRange = new vscode.Range( - new vscode.Position( - lastSelection.end.line, - 0 - ), - new vscode.Position( - lastSelection.end.line, - lastSelectedLineContent.length - ) - ); - const interectedSelection = lastSelection.intersection(lastSelectedLineContentRange); - if (!interectedSelection || interectedSelection.isEmpty) { - return 0; - } - - return lastSelectedLineContent.trim().length > 0 ? 1 : 0; -} - -export default class PartialExecutionCodeLensProvider -implements vscode.CodeLensProvider { - _selection?: vscode.Range; - _onDidChangeCodeLenses: vscode.EventEmitter< - void - > = new vscode.EventEmitter(); - - readonly onDidChangeCodeLenses: vscode.Event = this - ._onDidChangeCodeLenses.event; - - constructor() { - vscode.workspace.onDidChangeConfiguration(() => { - this._onDidChangeCodeLenses.fire(); - }); - } - - refresh(selection?: vscode.Range): void { - this._selection = selection; - this._onDidChangeCodeLenses.fire(); - } - - provideCodeLenses(): vscode.CodeLens[] { - if (!this._selection) { - return []; - } - - const message = '► Run Selected Lines From Playground'; - const codeLens = new vscode.CodeLens(this._selection); - - codeLens.command = { - title: message, - command: EXTENSION_COMMANDS.MDB_RUN_SELECTED_PLAYGROUND_BLOCKS, - arguments: [message] - }; - - return [codeLens]; - } -} diff --git a/src/editors/playgroundController.ts b/src/editors/playgroundController.ts index 2c6664b17..d5d4bef82 100644 --- a/src/editors/playgroundController.ts +++ b/src/editors/playgroundController.ts @@ -9,10 +9,6 @@ import { createLogger } from '../logging'; import { ExplorerController, ConnectionTreeItem, DatabaseTreeItem } from '../explorer'; import { LanguageServerController } from '../language'; import { OutputChannel, ProgressLocation, TextEditor } from 'vscode'; -import PartialExecutionCodeLensProvider, { - isSelectionValidForCodeLens, - getCodeLensLineOffsetForSelection -} from './partialExecutionCodeLensProvider'; import playgroundCreateIndexTemplate from '../templates/playgroundCreateIndexTemplate'; import playgroundCreateCollectionTemplate from '../templates/playgroundCreateCollectionTemplate'; import playgroundCreateCollectionWithTSTemplate from '../templates/playgroundCreateCollectionWithTSTemplate'; @@ -59,7 +55,6 @@ export default class PlaygroundController { _languageServerController: LanguageServerController; _telemetryService: TelemetryService; _activeConnectionCodeLensProvider: ActiveConnectionCodeLensProvider; - _partialExecutionCodeLensProvider: PartialExecutionCodeLensProvider; _outputChannel: OutputChannel; _connectionString?: string; _selectedText?: string; @@ -79,7 +74,6 @@ export default class PlaygroundController { statusView: StatusView, playgroundResultViewProvider: PlaygroundResultProvider, activeConnectionCodeLensProvider: ActiveConnectionCodeLensProvider, - partialExecutionCodeLensProvider: PartialExecutionCodeLensProvider, explorerController: ExplorerController ) { this._context = context; @@ -93,7 +87,6 @@ export default class PlaygroundController { 'Playground output' ); this._activeConnectionCodeLensProvider = activeConnectionCodeLensProvider; - this._partialExecutionCodeLensProvider = partialExecutionCodeLensProvider; this._explorerController = explorerController; this._connectionController.addEventListener( @@ -130,66 +123,22 @@ export default class PlaygroundController { .sort((a, b) => (a.start.line > b.start.line ? 1 : -1)); this._selectedText = sortedSelections - .map((selection) => this._getSelectedText(selection)) + .map((item) => this._getSelectedText(item)) .join('\n'); - - void this._showCodeLensForSelection( - sortedSelections, - changeEvent.textEditor - ); } } ); } - _showCodeLensForSelection( - selections: vscode.Selection[], - editor: vscode.TextEditor - ): void { - if (!this._selectedText || this._selectedText.trim().length === 0) { - this._partialExecutionCodeLensProvider.refresh(); - return; - } - - if (!isSelectionValidForCodeLens(selections, editor.document)) { - this._partialExecutionCodeLensProvider.refresh(); - return; - } - - const lastSelection = selections[selections.length - 1]; - const lastSelectedLineNumber = lastSelection.end.line; - const lastSelectedLineContent = editor.document.lineAt(lastSelectedLineNumber).text || ''; - // Add an empty line to the end of the file when the selection includes - // the last line and it is not empty. - // We do this so that we can show the code lens after the line's contents. - if ( - lastSelection.end.line === editor.document.lineCount - 1 && - lastSelectedLineContent.trim().length > 0 - ) { - void editor.edit(edit => { - edit.insert( - new vscode.Position(lastSelection.end.line + 1, 0), - '\n' - ); - }); - } - - const codeLensLineOffset = getCodeLensLineOffsetForSelection(selections, editor); - this._partialExecutionCodeLensProvider.refresh( - new vscode.Range( - lastSelectedLineNumber + codeLensLineOffset, 0, - lastSelectedLineNumber + codeLensLineOffset, 0 - ) - ); - } - async _connectToServiceProvider(): Promise { + // Disconnect if already connected. await this._languageServerController.disconnectFromServiceProvider(); const dataService = this._connectionController.getActiveDataService(); const connectionId = this._connectionController.getActiveConnectionId(); const connectionModel = this._connectionController .getActiveConnectionModel(); + if (!dataService || !connectionId || !connectionModel) { this._activeConnectionCodeLensProvider.refresh(); @@ -505,34 +454,17 @@ export default class PlaygroundController { } runSelectedPlaygroundBlocks(): Promise { - if ( - !this._activeTextEditor || - this._activeTextEditor.document.languageId !== 'mongodb' - ) { - void vscode.window.showErrorMessage( - "Please open a '.mongodb' playground file before running it." - ); - - return Promise.resolve(false); - } - - const selections = this._activeTextEditor.selections; - - if ( - !selections || - !Array.isArray(selections) || - (selections.length === 1 && this._getSelectedText(selections[0]) === '') - ) { + if (!this._selectedText) { void vscode.window.showInformationMessage( 'Please select one or more lines in the playground.' ); return Promise.resolve(true); - } else if (this._selectedText) { - this._isPartialRun = true; - this._codeToEvaluate = this._selectedText; } + this._isPartialRun = true; + this._codeToEvaluate = this._selectedText; + return this._evaluatePlayground(); } diff --git a/src/mdbExtensionController.ts b/src/mdbExtensionController.ts index fd5a173ad..e7858c098 100644 --- a/src/mdbExtensionController.ts +++ b/src/mdbExtensionController.ts @@ -22,6 +22,7 @@ import { HelpExplorer, CollectionTreeItem } from './explorer'; +import CodeActionProvider from './editors/codeActionProvider'; import EXTENSION_COMMANDS from './commands'; import FieldTreeItem from './explorer/fieldTreeItem'; import IndexListTreeItem from './explorer/indexListTreeItem'; @@ -31,7 +32,6 @@ import SchemaTreeItem from './explorer/schemaTreeItem'; import { StatusView } from './views'; import { StorageController, StorageVariables } from './storage'; import TelemetryService from './telemetry/telemetryService'; -import PartialExecutionCodeLensProvider from './editors/partialExecutionCodeLensProvider'; import PlaygroundsTreeItem from './explorer/playgroundsTreeItem'; import PlaygroundResultProvider from './editors/playgroundResultProvider'; import WebviewController from './views/webviewController'; @@ -41,6 +41,7 @@ const log = createLogger('commands'); // This class is the top-level controller for our extension. // Commands which the extensions handles are defined in the function `activate`. export default class MDBExtensionController implements vscode.Disposable { + _codeActionProvider: CodeActionProvider; _connectionController: ConnectionController; _context: vscode.ExtensionContext; _editorsController: EditorsController; @@ -55,7 +56,6 @@ export default class MDBExtensionController implements vscode.Disposable { _webviewController: WebviewController; _playgroundResultViewProvider: PlaygroundResultProvider; _activeConnectionCodeLensProvider: ActiveConnectionCodeLensProvider; - _partialExecutionCodeLensProvider: PartialExecutionCodeLensProvider; _editDocumentCodeLensProvider: EditDocumentCodeLensProvider; constructor( @@ -91,7 +91,6 @@ export default class MDBExtensionController implements vscode.Disposable { this._activeConnectionCodeLensProvider = new ActiveConnectionCodeLensProvider( this._connectionController ); - this._partialExecutionCodeLensProvider = new PartialExecutionCodeLensProvider(); this._playgroundController = new PlaygroundController( context, this._connectionController, @@ -100,9 +99,9 @@ export default class MDBExtensionController implements vscode.Disposable { this._statusView, this._playgroundResultViewProvider, this._activeConnectionCodeLensProvider, - this._partialExecutionCodeLensProvider, this._explorerController ); + this._codeActionProvider = new CodeActionProvider(this._playgroundController); this._editorsController = new EditorsController( context, this._connectionController, @@ -111,7 +110,7 @@ export default class MDBExtensionController implements vscode.Disposable { this._telemetryService, this._playgroundResultViewProvider, this._activeConnectionCodeLensProvider, - this._partialExecutionCodeLensProvider, + this._codeActionProvider, this._editDocumentCodeLensProvider ); this._webviewController = new WebviewController( diff --git a/src/test/fixture/.vscode/settings.json b/src/test/fixture/.vscode/settings.json index a2dd2c536..be8348c0e 100644 --- a/src/test/fixture/.vscode/settings.json +++ b/src/test/fixture/.vscode/settings.json @@ -18,5 +18,6 @@ "other": true, "comments": false, "strings": true - } + }, + "security.workspace.trust.enabled": false } diff --git a/src/test/suite/editors/codeActionProvider.test.ts b/src/test/suite/editors/codeActionProvider.test.ts new file mode 100644 index 000000000..5c1154c55 --- /dev/null +++ b/src/test/suite/editors/codeActionProvider.test.ts @@ -0,0 +1,117 @@ +import * as vscode from 'vscode'; +import { beforeEach, afterEach } from 'mocha'; +import chai from 'chai'; +import sinon from 'sinon'; + +import ActiveDBCodeLensProvider from '../../../editors/activeConnectionCodeLensProvider'; +import CodeActionProvider from '../../../editors/codeActionProvider'; +import { ExplorerController } from '../../../explorer'; +import { LanguageServerController } from '../../../language'; +import { PlaygroundController } from '../../../editors'; + +import { mdbTestExtension } from '../stubbableMdbExtension'; +import { TestExtensionContext } from '../stubs'; + +const expect = chai.expect; + +import { TEST_DATABASE_URI } from '../dbTestHelper'; + +suite('Code Action Provider Test Suite', function () { + this.timeout(5000); + + const testExtensionContext = new TestExtensionContext(); + testExtensionContext.extensionPath = '../../'; + + beforeEach(async () => { + sinon.replace( + mdbTestExtension.testExtensionController, + '_languageServerController', + new LanguageServerController( + testExtensionContext + ) + ); + sinon.replace( + vscode.window, + 'showInformationMessage', + sinon.fake.resolves(true) + ); + + await mdbTestExtension.testExtensionController._connectionController.addNewConnectionStringAndConnect( + TEST_DATABASE_URI + ); + + const testActiveDBCodeLensProvider = new ActiveDBCodeLensProvider( + mdbTestExtension.testExtensionController._connectionController + ); + const testExplorerController = new ExplorerController( + mdbTestExtension.testExtensionController._connectionController + ); + + mdbTestExtension.testExtensionController._playgroundController = new PlaygroundController( + testExtensionContext, + mdbTestExtension.testExtensionController._connectionController, + mdbTestExtension.testExtensionController._languageServerController, + mdbTestExtension.testExtensionController._telemetryService, + mdbTestExtension.testExtensionController._statusView, + mdbTestExtension.testExtensionController._playgroundResultViewProvider, + testActiveDBCodeLensProvider, + testExplorerController + ); + + const mockOpenPlaygroundResult: any = sinon.fake(); + sinon.replace( + mdbTestExtension.testExtensionController._playgroundController, + '_openPlaygroundResult', + mockOpenPlaygroundResult + ); + + await vscode.workspace + .getConfiguration('mdb') + .update('confirmRunAll', false); + + await mdbTestExtension.testExtensionController._languageServerController.startLanguageServer(); + await mdbTestExtension.testExtensionController._playgroundController._connectToServiceProvider(); + }); + + afterEach(async () => { + await vscode.workspace + .getConfiguration('mdb') + .update('confirmRunAll', true); + await mdbTestExtension.testExtensionController._connectionController.disconnect(); + mdbTestExtension.testExtensionController._connectionController.clearAllConnections(); + sinon.restore(); + }); + + test('expected provideCodeActions to return undefined when text is not selected', () => { + const testCodeActionProvider = new CodeActionProvider(mdbTestExtension.testExtensionController._playgroundController); + const codeActions = testCodeActionProvider.provideCodeActions(); + + expect(codeActions).to.be.undefined; + }); + + test('expected provideCodeActions to return a run selected playground blocks action', async () => { + mdbTestExtension.testExtensionController._playgroundController._selectedText = '123'; + + const testCodeActionProvider = new CodeActionProvider(mdbTestExtension.testExtensionController._playgroundController); + const codeActions = testCodeActionProvider.provideCodeActions(); + + expect(codeActions).to.exist; + + if (codeActions) { + expect(codeActions.length).to.be.equal(1); + const actionCommand = codeActions[0].command; + expect(codeActions).to.exist; + + if (actionCommand) { + expect(actionCommand.command).to.be.equal('mdb.runSelectedPlaygroundBlocks'); + expect(actionCommand.title).to.be.equal('Run selected playground blocks'); + + await vscode.commands.executeCommand(actionCommand.command); + + const expectedResult = { namespace: null, type: 'number', content: 123 }; + expect(mdbTestExtension.testExtensionController._playgroundController._playgroundResult).to.be.deep.equal(expectedResult); + expect(mdbTestExtension.testExtensionController._playgroundController._isPartialRun).to.be.equal(true); + } + } + }); +}); diff --git a/src/test/suite/editors/partialExecutionCodeLensProvider.test.ts b/src/test/suite/editors/partialExecutionCodeLensProvider.test.ts deleted file mode 100644 index 239b75609..000000000 --- a/src/test/suite/editors/partialExecutionCodeLensProvider.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import assert from 'assert'; -import * as vscode from 'vscode'; -import PartialExecutionCodeLensProvider from '../../../editors/partialExecutionCodeLensProvider'; - -suite('Partial Execution Code Lens Provider Test Suite', () => { - test('expected provideCodeLenses to return empty array when text is not selected', () => { - const testCodeLensProvider = new PartialExecutionCodeLensProvider(); - - testCodeLensProvider.refresh(); - - const codeLens = testCodeLensProvider.provideCodeLenses(); - - assert(!!codeLens); - assert(codeLens.length === 0); - }); - - test('expected provideCodeLenses to return a code lens when text is selected', () => { - const testCodeLensProvider = new PartialExecutionCodeLensProvider(); - - testCodeLensProvider.refresh(new vscode.Range(4, 5, 5, 30)); - - const codeLens = testCodeLensProvider.provideCodeLenses(); - - assert(!!codeLens); - assert(codeLens.length === 1); - const range = codeLens[0].range; - const expectedStartLine = 4; - assert( - range.start.line === expectedStartLine, - `Expected a codeLens position to be at line ${expectedStartLine}, found ${range.start.line}` - ); - const expectedEnd = 5; - assert( - range.end.line === expectedEnd, - `Expected a codeLens position to be at line ${expectedEnd}, found ${range.end.line}` - ); - }); -}); diff --git a/src/test/suite/editors/playgroundController.test.ts b/src/test/suite/editors/playgroundController.test.ts index 53150b63e..f42cf4dbc 100644 --- a/src/test/suite/editors/playgroundController.test.ts +++ b/src/test/suite/editors/playgroundController.test.ts @@ -1,22 +1,22 @@ import * as vscode from 'vscode'; -import { PlaygroundController } from '../../../editors'; -import { LanguageServerController } from '../../../language'; -import ConnectionController from '../../../connectionController'; -import { StatusView } from '../../../views'; -import { StorageController } from '../../../storage'; -import { TestExtensionContext, MockLanguageServerController } from '../stubs'; import { before, beforeEach, afterEach } from 'mocha'; -import TelemetryService from '../../../telemetry/telemetryService'; -import PlaygroundResultProvider from '../../../editors/playgroundResultProvider'; +import chai from 'chai'; +import sinon from 'sinon'; + import ActiveDBCodeLensProvider from '../../../editors/activeConnectionCodeLensProvider'; -import PartialExecutionCodeLensProvider from '../../../editors/partialExecutionCodeLensProvider'; +import ConnectionController from '../../../connectionController'; +import { ConnectionModel } from '../../../types/connectionModelType'; import EditDocumentCodeLensProvider from '../../../editors/editDocumentCodeLensProvider'; import { ExplorerController } from '../../../explorer'; - -import sinon from 'sinon'; -import chai from 'chai'; +import { LanguageServerController } from '../../../language'; +import { PlaygroundController } from '../../../editors'; +import PlaygroundResultProvider from '../../../editors/playgroundResultProvider'; +import { StatusView } from '../../../views'; +import { StorageController } from '../../../storage'; +import TelemetryService from '../../../telemetry/telemetryService'; import { TEST_DATABASE_URI } from '../dbTestHelper'; -import { ConnectionModel } from '../../../types/connectionModelType'; +import { TestExtensionContext, MockLanguageServerController } from '../stubs'; + const expect = chai.expect; chai.use(require('chai-as-promised')); @@ -26,30 +26,6 @@ const CONNECTION = { driverOptions: {} }; -const mockPlayground = 'use(\'dbName\');\n a\n\n\n\ndb.find();'; - -const activeTestEditorWithSelectionMock = { - document: { - languageId: 'mongodb', - uri: { - path: 'test' - } as vscode.Uri, - getText: (range: vscode.Range) => mockPlayground.split('\n')[range.start.line].substr(range.start.character, range.end.character), - lineAt: (lineNumber: number) => ({ - text: mockPlayground.split('\n')[lineNumber] - }), - lineCount: mockPlayground.split('\n').length - }, - selections: [ - new vscode.Selection( - new vscode.Position(0, 5), - new vscode.Position(0, 11) - ) - ], - edit: () => null -} as unknown as vscode.TextEditor; - - suite('Playground Controller Test Suite', function () { this.timeout(5000); @@ -82,7 +58,6 @@ suite('Playground Controller Test Suite', function () { const testActiveDBCodeLensProvider = new ActiveDBCodeLensProvider( testConnectionController ); - const testPartialExecutionCodeLensProvider = new PartialExecutionCodeLensProvider(); const testExplorerController = new ExplorerController( testConnectionController ); @@ -94,7 +69,6 @@ suite('Playground Controller Test Suite', function () { testStatusView, testPlaygroundResultProvider, testActiveDBCodeLensProvider, - testPartialExecutionCodeLensProvider, testExplorerController ); const sandbox = sinon.createSandbox(); @@ -467,122 +441,6 @@ suite('Playground Controller Test Suite', function () { }); }); - test('do not show code lens if a part of a line with content is selected', () => { - testPlaygroundController._selectedText = 'db'; - testPlaygroundController._activeTextEditor = activeTestEditorWithSelectionMock; - - testPlaygroundController._showCodeLensForSelection( - [new vscode.Selection( - new vscode.Position(0, 5), - new vscode.Position(0, 11) - )], - activeTestEditorWithSelectionMock - ); - - const codeLens = testPlaygroundController._partialExecutionCodeLensProvider?.provideCodeLenses(); - - expect(codeLens.length).to.be.equal(0); - }); - - test('do not show code lens when it has no content (multi-line)', () => { - testPlaygroundController._selectedText = ' \n\n '; - testPlaygroundController._activeTextEditor = activeTestEditorWithSelectionMock; - - testPlaygroundController._showCodeLensForSelection( - [new vscode.Selection( - new vscode.Position(2, 0), - new vscode.Position(4, 0) - )], - activeTestEditorWithSelectionMock - ); - - const codeLens = testPlaygroundController._partialExecutionCodeLensProvider?.provideCodeLenses(); - - expect(codeLens.length).to.be.equal(0); - }); - - test('show code lens if whole line is selected', () => { - testPlaygroundController._selectedText = 'use(\'dbName\');'; - testPlaygroundController._showCodeLensForSelection( - [new vscode.Selection( - new vscode.Position(0, 0), - new vscode.Position(0, 15) - )], - activeTestEditorWithSelectionMock - ); - - const codeLens = testPlaygroundController._partialExecutionCodeLensProvider.provideCodeLenses(); - - expect(codeLens.length).to.be.equal(1); - expect(codeLens[0].range.end.line).to.be.equal(1); - }); - - test('has the correct line number for code lens when the selection includes the last line', () => { - testPlaygroundController._selectedText = 'use(\'dbName\');\n\na'; - const fakeEdit = sinon.fake.returns(() => ({ insert: sinon.fake() })); - sandbox.replace( - activeTestEditorWithSelectionMock, - 'edit', - fakeEdit - ); - testPlaygroundController._showCodeLensForSelection( - [new vscode.Selection( - new vscode.Position(0, 0), - new vscode.Position(5, 5) - )], - activeTestEditorWithSelectionMock - ); - - const codeLens = testPlaygroundController._partialExecutionCodeLensProvider.provideCodeLenses(); - - expect(codeLens.length).to.be.equal(1); - expect(codeLens[0].range.start.line).to.be.equal(6); - expect(codeLens[0].range.end.line).to.be.equal(6); - expect(fakeEdit).to.be.called; - }); - - test('it calls to edit the document to add an empty line if the selection includes the last line with content', (done) => { - testPlaygroundController._selectedText = 'use(\'dbName\');\n\na'; - sandbox.replace( - activeTestEditorWithSelectionMock, - 'edit', - (cb) => { - cb({ - insert: (position: vscode.Position, toInsert: string) => { - expect(position.line).to.equal(6); - expect(toInsert).to.equal('\n'); - done(); - } - } as unknown as vscode.TextEditorEdit); - return new Promise((resolve) => resolve(true)); - } - ); - testPlaygroundController._showCodeLensForSelection( - [new vscode.Selection( - new vscode.Position(0, 0), - new vscode.Position(5, 5) - )], - activeTestEditorWithSelectionMock - ); - }); - - test('shows the codelens on the line above the last selected line when the last selected line is empty', () => { - testPlaygroundController._selectedText = 'use(\'dbName\');\n\n'; - testPlaygroundController._showCodeLensForSelection( - [new vscode.Selection( - new vscode.Position(0, 0), - new vscode.Position(1, 3) - )], - activeTestEditorWithSelectionMock - ); - - const codeLens = testPlaygroundController._partialExecutionCodeLensProvider.provideCodeLenses(); - - expect(codeLens.length).to.be.equal(1); - expect(codeLens[0].range.start.line).to.be.equal(2); - expect(codeLens[0].range.end.line).to.be.equal(2); - }); - test('playground controller loads the active editor on start', () => { sandbox.replaceGetter( vscode.window, @@ -601,7 +459,6 @@ suite('Playground Controller Test Suite', function () { testStatusView, testPlaygroundResultProvider, testActiveDBCodeLensProvider, - testPartialExecutionCodeLensProvider, testExplorerController ); diff --git a/src/test/suite/language/languageServerController.test.ts b/src/test/suite/language/languageServerController.test.ts index 99c7e5533..bf2333a13 100644 --- a/src/test/suite/language/languageServerController.test.ts +++ b/src/test/suite/language/languageServerController.test.ts @@ -1,29 +1,28 @@ import { before, after } from 'mocha'; -import TelemetryService from '../../../telemetry/telemetryService'; -import { ExplorerController } from '../../../explorer'; - -import path from 'path'; +import chai from 'chai'; import fs from 'fs'; +import path from 'path'; import sinon from 'sinon'; -import chai from 'chai'; -const expect = chai.expect; -chai.use(require('chai-as-promised')); - -import { PlaygroundController } from '../../../editors'; -import { LanguageServerController } from '../../../language'; +import ActiveDBCodeLensProvider from '../../../editors/activeConnectionCodeLensProvider'; import ConnectionController from '../../../connectionController'; -import { StatusView } from '../../../views'; -import { StorageController } from '../../../storage'; -import { TestExtensionContext } from '../stubs'; +import { DataServiceType } from '../../../types/dataServiceType'; +import EditDocumentCodeLensProvider from '../../../editors/editDocumentCodeLensProvider'; +import { ExplorerController } from '../../../explorer'; +import { LanguageServerController } from '../../../language'; import { mdbTestExtension } from '../stubbableMdbExtension'; +import { PlaygroundController } from '../../../editors'; import PlaygroundResultProvider from '../../../editors/playgroundResultProvider'; -import ActiveDBCodeLensProvider from '../../../editors/activeConnectionCodeLensProvider'; -import PartialExecutionCodeLensProvider from '../../../editors/partialExecutionCodeLensProvider'; -import EditDocumentCodeLensProvider from '../../../editors/editDocumentCodeLensProvider'; -import { TEST_DATABASE_URI } from '../dbTestHelper'; import READ_PREFERENCES from '../../../views/webview-app/connection-model/constants/read-preferences'; -import { DataServiceType } from '../../../types/dataServiceType'; +import { StatusView } from '../../../views'; +import { StorageController } from '../../../storage'; +import { TEST_DATABASE_URI } from '../dbTestHelper'; +import TelemetryService from '../../../telemetry/telemetryService'; +import { TestExtensionContext } from '../stubs'; + +const expect = chai.expect; + +chai.use(require('chai-as-promised')); suite('Language Server Controller Test Suite', () => { const mockExtensionContext = new TestExtensionContext(); @@ -54,7 +53,6 @@ suite('Language Server Controller Test Suite', () => { const testActiveDBCodeLensProvider = new ActiveDBCodeLensProvider( testConnectionController ); - const testPartialExecutionCodeLensProvider = new PartialExecutionCodeLensProvider(); const testExplorerController = new ExplorerController( testConnectionController ); @@ -66,7 +64,6 @@ suite('Language Server Controller Test Suite', () => { testStatusView, testPlaygroundResultProvider, testActiveDBCodeLensProvider, - testPartialExecutionCodeLensProvider, testExplorerController );