diff --git a/src/connectionController.ts b/src/connectionController.ts index 9520bc915..22f5a05a0 100644 --- a/src/connectionController.ts +++ b/src/connectionController.ts @@ -11,16 +11,11 @@ import { EventEmitter } from 'events'; import { StorageController, StorageVariables } from './storage'; import { SavedConnection, StorageScope } from './storage/storageController'; import { getNodeModule } from './utils/getNodeModule'; -import TelemetryController, { - TelemetryEventTypes -} from './telemetry/telemetryController'; -import { getCloudInfo } from 'mongodb-cloud-info'; +import TelemetryController from './telemetry/telemetryController'; const { name, version } = require('../package.json'); const log = createLogger('connection controller'); const MAX_CONNECTION_NAME_LENGTH = 512; -const ATLAS_REGEX = /mongodb.net[:/]/i; -const LOCALHOST_REGEX = /(localhost|127\.0\.0\.1)/i; type KeyTar = typeof keytarType; @@ -245,6 +240,17 @@ export default class ConnectionController { }); }; + public sendTelemetry( + newDataService: DataServiceType, + connectionType: ConnectionTypes + ) { + // Send metrics to Segment + this._telemetryController.trackNewConnection( + newDataService, + connectionType + ); + } + public parseNewConnectionAndConnect = ( newConnectionModel: ConnectionModelType ): Promise => { @@ -309,82 +315,13 @@ export default class ConnectionController { return resolve(false); } - resolve(true); + return resolve(true); }, reject ); }); }; - public async getCloudInfoFromDataService(firstServerHostname: string) { - const cloudInfo = await getCloudInfo(firstServerHostname); - let isPublicCloud = false; - let publicCloudName: string | null = null; - - if (cloudInfo.isAws) { - isPublicCloud = true; - publicCloudName = 'aws'; - } else if (cloudInfo.isGcp) { - isPublicCloud = true; - publicCloudName = 'gcp'; - } else if (cloudInfo.isAzure) { - isPublicCloud = true; - publicCloudName = 'azure'; - } - - return { isPublicCloud, publicCloudName }; - } - - private async sendTelemetry( - dataService: DataServiceType, - connectionType: ConnectionTypes - ): Promise { - dataService.instance({}, async (error: any, data: any) => { - if (error) { - log.error('TELEMETRY data service error', error); - } - - if (data) { - try { - const firstServerHostname = dataService.client.model.hosts[0].host; - const cloudInfo = await this.getCloudInfoFromDataService( - firstServerHostname - ); - const nonGenuineServerName = data.genuineMongoDB.isGenuine - ? null - : data.genuineMongoDB.dbType; - const telemetryData = { - is_atlas: !!data.client.s.url.match(ATLAS_REGEX), - is_localhost: !!data.client.s.url.match(LOCALHOST_REGEX), - is_data_lake: data.dataLake.isDataLake, - is_enterprise: data.build.enterprise_module, - is_public_cloud: cloudInfo.isPublicCloud, - public_cloud_name: cloudInfo.publicCloudName, - is_genuine: data.genuineMongoDB.isGenuine, - non_genuine_server_name: nonGenuineServerName, - server_version: data.build.version, - server_arch: data.build.raw.buildEnvironment.target_arch, - server_os: data.build.raw.buildEnvironment.target_os, - is_used_connect_screen: - connectionType === ConnectionTypes.CONNECTION_FORM, - is_used_command_palette: - connectionType === ConnectionTypes.CONNECTION_STRING, - is_used_saved_connection: - connectionType === ConnectionTypes.CONNECTION_ID - }; - - // Send metrics to Segment - this._telemetryController.track( - TelemetryEventTypes.NEW_CONNECTION, - telemetryData - ); - } catch (error) { - log.error('TELEMETRY cloud info error', error); - } - } - }); - } - public connect = async ( connectionId: string, connectionModel: ConnectionModelType, @@ -449,9 +386,8 @@ export default class ConnectionController { this.eventEmitter.emit(DataServiceEventTypes.CONNECTIONS_DID_CHANGE); this.eventEmitter.emit(DataServiceEventTypes.ACTIVE_CONNECTION_CHANGED); - if (this._telemetryController.needTelemetry()) { - this.sendTelemetry(newDataService, connectionType); - } + // Send metrics to Segment + this.sendTelemetry(newDataService, connectionType); return resolve(true); }); @@ -659,13 +595,13 @@ export default class ConnectionController { const connectionNameToRemove: | string | undefined = await vscode.window.showQuickPick( - connectionIds.map( - (id, index) => `${index + 1}: ${this._connections[id].name}` - ), - { - placeHolder: 'Choose a connection to remove...' - } - ); + connectionIds.map( + (id, index) => `${index + 1}: ${this._connections[id].name}` + ), + { + placeHolder: 'Choose a connection to remove...' + } + ); if (!connectionNameToRemove) { return Promise.resolve(false); diff --git a/src/editors/playgroundController.ts b/src/editors/playgroundController.ts index 740423d08..31533b832 100644 --- a/src/editors/playgroundController.ts +++ b/src/editors/playgroundController.ts @@ -1,13 +1,9 @@ import * as vscode from 'vscode'; - import ConnectionController, { DataServiceEventTypes } from '../connectionController'; import { LanguageServerController } from '../language'; -import TelemetryController, { - TelemetryEventTypes, - TelemetryEventProperties -} from '../telemetry/telemetryController'; +import TelemetryController from '../telemetry/telemetryController'; import ActiveConnectionCodeLensProvider from './activeConnectionCodeLensProvider'; import { OutputChannel, ProgressLocation, TextEditor } from 'vscode'; import playgroundTemplate from '../templates/playgroundTemplate'; @@ -137,43 +133,15 @@ export default class PlaygroundController { }); } - public prepareTelemetry(res: any): TelemetryEventProperties { - let type = 'other'; - - if (!res.shellApiType) { - return { type }; - } - - const shellApiType = res.shellApiType.toLocaleLowerCase(); - - // See: https://github.com/mongodb-js/mongosh/blob/master/packages/shell-api/src/shell-api.js - if (shellApiType.includes('insert')) { - type = 'insert'; - } else if (shellApiType.includes('update')) { - type = 'update'; - } else if (shellApiType.includes('delete')) { - type = 'delete'; - } else if (shellApiType.includes('aggregation')) { - type = 'aggregation'; - } else if (shellApiType.includes('cursor')) { - type = 'query'; - } - - return { type }; - } - public async evaluate(codeToEvaluate: string): Promise { - // Send a request to the language server to execute scripts from a playground + // Send a request to the language server to execute scripts from a playground. const result = await this._languageServerController.executeAll( codeToEvaluate ); - if (result && this._telemetryController.needTelemetry()) { - // Send metrics to Segment - this._telemetryController.track( - TelemetryEventTypes.PLAYGROUND_CODE_EXECUTED, - this.prepareTelemetry(result) - ); + // Send metrics to Segment. + if (result) { + this._telemetryController.trackPlaygroundCodeExecuted(result); } return Promise.resolve(result); @@ -196,7 +164,7 @@ export default class PlaygroundController { }, async (progress, token) => { token.onCancellationRequested(async () => { - // If a user clicked the cancel button terminate all playground scripts + // If a user clicked the cancel button terminate all playground scripts. this._languageServerController.cancelAll(); this._outputChannel.clear(); this._outputChannel.show(true); @@ -206,7 +174,7 @@ export default class PlaygroundController { const codeToEvaluate = this._activeTextEditor?.document.getText() || ''; - // Run all playground scripts + // Run all playground scripts. const result = await this.evaluate(codeToEvaluate); return resolve(result); diff --git a/src/explorer/explorerController.ts b/src/explorer/explorerController.ts index 3b1d08aa1..047dd6443 100644 --- a/src/explorer/explorerController.ts +++ b/src/explorer/explorerController.ts @@ -32,7 +32,6 @@ export default class ExplorerController { this._treeView = vscode.window.createTreeView('mongoDB', { treeDataProvider: this._treeController }); - this._treeController.activateTreeViewEventHandlers(this._treeView); } }; @@ -61,6 +60,7 @@ export default class ExplorerController { if (this._treeController) { return this._treeController.refresh(); } + return Promise.reject(new Error('No tree to refresh.')); } @@ -68,6 +68,7 @@ export default class ExplorerController { public getTreeView(): vscode.TreeView | undefined { return this._treeView; } + public getTreeController(): ExplorerTreeController { return this._treeController; } diff --git a/src/mdbExtensionController.ts b/src/mdbExtensionController.ts index 773ca9e85..e37350a1c 100644 --- a/src/mdbExtensionController.ts +++ b/src/mdbExtensionController.ts @@ -9,9 +9,7 @@ import ConnectionController from './connectionController'; import { EditorsController, PlaygroundController } from './editors'; import { ExplorerController, CollectionTreeItem } from './explorer'; import { LanguageServerController } from './language'; -import TelemetryController, { - TelemetryEventTypes -} from './telemetry/telemetryController'; +import TelemetryController from './telemetry/telemetryController'; import { StatusView } from './views'; import { createLogger } from './logging'; import { StorageController } from './storage'; @@ -42,7 +40,6 @@ export default class MDBExtensionController implements vscode.Disposable { connectionController?: ConnectionController ) { this._context = context; - this._statusView = new StatusView(context); this._storageController = new StorageController(context); this._telemetryController = new TelemetryController( @@ -136,13 +133,8 @@ export default class MDBExtensionController implements vscode.Disposable { commandHandler: (...args: any[]) => Promise ): void => { const commandHandlerWithTelemetry = (args: any[]) => { - // Send metrics to Segment - this._telemetryController.track( - TelemetryEventTypes.EXTENSION_COMMAND_RUN, - { - command - } - ); + // Send metrics to Segment. + this._telemetryController.trackCommandRun(command); return commandHandler(args); }; @@ -204,6 +196,7 @@ export default class MDBExtensionController implements vscode.Disposable { return new Promise((resolve, reject) => { vscode.env.clipboard.writeText(connectionString).then(() => { vscode.window.showInformationMessage('Copied to clipboard.'); + return resolve(true); }, reject); }); diff --git a/src/telemetry/telemetryController.ts b/src/telemetry/telemetryController.ts index 37da2bb2b..90cd234ab 100644 --- a/src/telemetry/telemetryController.ts +++ b/src/telemetry/telemetryController.ts @@ -4,14 +4,31 @@ import SegmentAnalytics = require('analytics-node'); import * as path from 'path'; import { config } from 'dotenv'; import { StorageController } from '../storage'; +import { ConnectionTypes } from '../connectionController'; +import { getCloudInfo } from 'mongodb-cloud-info'; +import { DataServiceType } from '../dataServiceType'; const log = createLogger('telemetry'); const fs = require('fs'); +const ATLAS_REGEX = /mongodb.net[:/]/i; +const LOCALHOST_REGEX = /(localhost|127\.0\.0\.1)/i; + type PlaygroundTelemetryEventProperties = { type: string; }; +type SegmentProperties = { + event: string; + userId?: string; + properties?: any; +}; + +type CloudInfo = { + isPublicCloud?: boolean; + publicCloudName?: string | null; +}; + type LinkClickedTelemetryEventProperties = { screen: string; link_id: string; @@ -26,8 +43,8 @@ type NewConnectionTelemetryEventProperties = { is_localhost: boolean; is_data_lake: boolean; is_enterprise: boolean; - is_public_cloud: boolean; - public_cloud_name: string | null; + is_public_cloud?: boolean; + public_cloud_name?: string | null; is_genuine: boolean; non_genuine_server_name: string | null; server_version: string; @@ -48,7 +65,9 @@ export enum TelemetryEventTypes { PLAYGROUND_CODE_EXECUTED = 'Playground Code Executed', EXTENSION_LINK_CLICKED = 'Link Clicked', EXTENSION_COMMAND_RUN = 'Command Run', - NEW_CONNECTION = 'New Connection' + NEW_CONNECTION = 'New Connection', + PLAYGROUND_SAVED = 'Playground Saved', + PLAYGROUND_LOADED = 'Playground Loaded' } /** @@ -79,6 +98,24 @@ export default class TelemetryController { } catch (error) { log.error('TELEMETRY key error', error); } + + vscode.workspace.onDidOpenTextDocument((document) => { + if ( + document && + document.languageId === 'mongodb' && + document.uri.scheme === 'file' + ) { + // Send metrics to Segment. + this.trackPlaygroundLoaded(); + } + }); + + vscode.workspace.onDidSaveTextDocument((document) => { + if (document && document.languageId === 'mongodb') { + // Send metrics to Segment. + this.trackPlaygroundSaved(); + } + }); } get segmentUserID(): string | undefined { @@ -116,29 +153,161 @@ export default class TelemetryController { public track( eventType: TelemetryEventTypes, - properties: TelemetryEventProperties + properties?: TelemetryEventProperties ): void { if (this.needTelemetry()) { - log.info('TELEMETRY track', { - eventType, - segmentUserID: this._segmentUserID, - properties - }); + const segmentProperties: SegmentProperties = { + event: eventType, + userId: this._segmentUserID + }; + + if (properties) { + segmentProperties.properties = properties; + } - this._segmentAnalytics?.track( - { - userId: this._segmentUserID, - event: eventType, - properties - }, - (error) => { - if (error) { - log.error('TELEMETRY track error', error); - } - - log.info('TELEMETRY track done'); + log.info('TELEMETRY track', segmentProperties); + + this._segmentAnalytics?.track(segmentProperties, (error: any) => { + if (error) { + log.error('TELEMETRY track error', error); } - ); + + log.info('TELEMETRY track done'); + }); + } + } + + private async getCloudInfoFromDataService( + firstServerHostname: string + ): Promise { + if (!this.needTelemetry()) { + return {}; + } + + try { + const cloudInfo = await getCloudInfo(firstServerHostname); + + if (cloudInfo.isAws) { + return { + isPublicCloud: true, + publicCloudName: 'aws' + }; + } + if (cloudInfo.isGcp) { + return { + isPublicCloud: true, + publicCloudName: 'gcp' + }; + } + if (cloudInfo.isAzure) { + return { + isPublicCloud: true, + publicCloudName: 'azure' + }; + } + + return { + isPublicCloud: false, + publicCloudName: null + }; + } catch (error) { + log.error('TELEMETRY cloud info error', error); + + return {}; + } + } + + public async trackNewConnection( + dataService: DataServiceType, + connectionType: ConnectionTypes + ): Promise { + dataService.instance({}, async (error: any, data: any) => { + if (error) { + log.error('TELEMETRY data service error', error); + } + + if (data) { + const firstServerHostname = dataService.client.model.hosts[0].host; + const cloudInfo = await this.getCloudInfoFromDataService( + firstServerHostname + ); + const nonGenuineServerName = data.genuineMongoDB.isGenuine + ? null + : data.genuineMongoDB.dbType; + const preparedProperties = { + is_atlas: !!data.client.s.url.match(ATLAS_REGEX), + is_localhost: !!data.client.s.url.match(LOCALHOST_REGEX), + is_data_lake: data.dataLake.isDataLake, + is_enterprise: data.build.enterprise_module, + is_public_cloud: cloudInfo.isPublicCloud, + public_cloud_name: cloudInfo.publicCloudName, + is_genuine: data.genuineMongoDB.isGenuine, + non_genuine_server_name: nonGenuineServerName, + server_version: data.build.version, + server_arch: data.build.raw.buildEnvironment.target_arch, + server_os: data.build.raw.buildEnvironment.target_os, + is_used_connect_screen: + connectionType === ConnectionTypes.CONNECTION_FORM, + is_used_command_palette: + connectionType === ConnectionTypes.CONNECTION_STRING, + is_used_saved_connection: + connectionType === ConnectionTypes.CONNECTION_ID + }; + + this.track(TelemetryEventTypes.NEW_CONNECTION, preparedProperties); + } + }); + } + + public async trackCommandRun(command: string): Promise { + this.track(TelemetryEventTypes.EXTENSION_COMMAND_RUN, { command }); + } + + public getPlaygroundResultType(res: any): string { + if (!res.shellApiType) { + return 'other'; + } + + const shellApiType = res.shellApiType.toLocaleLowerCase(); + + // See: https://github.com/mongodb-js/mongosh/blob/master/packages/shell-api/src/shell-api.js + if (shellApiType.includes('insert')) { + return 'insert'; + } + if (shellApiType.includes('update')) { + return 'update'; } + if (shellApiType.includes('delete')) { + return 'delete'; + } + if (shellApiType.includes('aggregation')) { + return 'aggregation'; + } + if (shellApiType.includes('cursor')) { + return 'query'; + } + + return 'other'; + } + + public async trackPlaygroundCodeExecuted(result: any): Promise { + this.track(TelemetryEventTypes.PLAYGROUND_CODE_EXECUTED, { + type: this.getPlaygroundResultType(result) + }); + } + + public async trackLinkClicked(screen: string, linkId: string): Promise { + this.track(TelemetryEventTypes.EXTENSION_LINK_CLICKED, { + screen, + link_id: linkId + }); + } + + public async trackPlaygroundLoaded(): Promise { + this.track(TelemetryEventTypes.PLAYGROUND_LOADED); + } + + public async trackPlaygroundSaved(): Promise { + this.track(TelemetryEventTypes.PLAYGROUND_SAVED); } } diff --git a/src/test/fixture/test.mongodb b/src/test/fixture/test.mongodb index fa14ae1f8..ea29d83a2 100644 --- a/src/test/fixture/test.mongodb +++ b/src/test/fixture/test.mongodb @@ -1,2 +1 @@ -use temp; -db.plantTempReadings.find(); +show dbs \ No newline at end of file diff --git a/src/test/suite/connectionController.test.ts b/src/test/suite/connectionController.test.ts index c0d7a2f4d..0babcdc73 100644 --- a/src/test/suite/connectionController.test.ts +++ b/src/test/suite/connectionController.test.ts @@ -13,7 +13,6 @@ import { DefaultSavingLocations } from '../../storage/storageController'; import { StatusView } from '../../views'; -import MDBExtensionController from '../../mdbExtensionController'; import { TestExtensionContext } from './stubs'; import { TEST_DATABASE_URI } from './dbTestHelper'; @@ -21,9 +20,24 @@ const testDatabaseInstanceId = 'localhost:27018'; const testDatabaseURI2WithTimeout = 'mongodb://shouldfail?connectTimeoutMS=1000&serverSelectionTimeoutMS=1000'; -suite('Connection Controller Test Suite', () => { +const sleep = (ms: number) => { + return new Promise((resolve) => setTimeout(resolve, ms)); +}; + +suite('Connection Controller Test Suite', function () { + this.timeout(5000); + const mockExtensionContext = new TestExtensionContext(); const mockStorageController = new StorageController(mockExtensionContext); + const testTelemetryController = new TelemetryController( + mockStorageController, + mockExtensionContext + ); + const testConnectionController = new ConnectionController( + new StatusView(mockExtensionContext), + mockStorageController, + testTelemetryController + ); beforeEach(() => { // Here we stub the showInformationMessage process because it is too much @@ -31,435 +45,259 @@ suite('Connection Controller Test Suite', () => { sinon.replace(vscode.window, 'showInformationMessage', sinon.stub()); }); - afterEach(() => { + afterEach(async () => { // Reset our mock extension's state. mockExtensionContext._workspaceState = {}; mockExtensionContext._globalState = {}; - sinon.restore(); - }); - test('it connects to mongodb', (done) => { - const testTelemetryController = new TelemetryController( - mockStorageController, - mockExtensionContext - ); - const testConnectionController = new ConnectionController( - new StatusView(mockExtensionContext), - mockStorageController, - testTelemetryController - ); + await testConnectionController.disconnect(); + testConnectionController.clearAllConnections(); - testConnectionController - .addNewConnectionStringAndConnect(TEST_DATABASE_URI) - .then((succesfullyConnected) => { - assert( - succesfullyConnected === true, - 'Expected a successful connection response.' - ); - assert( - testConnectionController.getSavedConnections().length === 1, - 'Expected there to be 1 connection in the connection list.' - ); - const connnectionId = - testConnectionController.getActiveConnectionId() || ''; - const name = testConnectionController._connections[connnectionId].name; - assert( - name === 'localhost:27018', - `Expected active connection to be 'localhost:27018' found ${name}` - ); - const connectionModel = testConnectionController.getActiveConnectionModel(); - assert(connectionModel !== null); - assert( - connectionModel?.getAttributes({ - derived: true - }).instanceId === 'localhost:27018' - ); - const dataService = testConnectionController.getActiveDataService(); - assert(dataService !== null); - assert( - testConnectionController._activeConnectionModel?.appname.startsWith( - 'mongodb-vscode' - ) - ); - assert(testConnectionController.isCurrentlyConnected()); - }) - .then(done, done); + sinon.restore(); }); - test('"disconnect()" disconnects from the active connection', (done) => { - const testTelemetryController = new TelemetryController( - mockStorageController, - mockExtensionContext - ); - const testConnectionController = new ConnectionController( - new StatusView(mockExtensionContext), - mockStorageController, - testTelemetryController - ); - - testConnectionController - .addNewConnectionStringAndConnect(TEST_DATABASE_URI) - .then((succesfullyConnected) => { - assert( - succesfullyConnected === true, - 'Expected a successful (true) connection response.' - ); - - testConnectionController - .disconnect() - .then((successfullyDisconnected) => { - assert( - successfullyDisconnected === true, - 'Expected a successful (true) disconnect response.' - ); - // Disconnecting should keep the connection contract, just disconnected. - const connectionsCount = testConnectionController.getSavedConnections() - .length; - assert( - connectionsCount === 1, - `Expected the amount of connections to be 1 found ${connectionsCount}.` - ); - const connnectionId = testConnectionController.getActiveConnectionId(); - assert( - connnectionId === null, - `Expected the active connection id to be null, found ${connnectionId}` - ); - const connectionModel = testConnectionController.getActiveConnectionModel(); - assert(connectionModel === null); - const dataService = testConnectionController.getActiveDataService(); - assert(dataService === null); - assert(!testConnectionController.isCurrentlyConnected()); - }) - .then(done, done); - }); + test('it connects to mongodb', async () => { + try { + const succesfullyConnected = await testConnectionController.addNewConnectionStringAndConnect( + TEST_DATABASE_URI + ); + const connnectionId = + testConnectionController.getActiveConnectionId() || ''; + const name = testConnectionController._connections[connnectionId].name; + const connectionModel = testConnectionController.getActiveConnectionModel(); + const dataService = testConnectionController.getActiveDataService(); + + assert( + succesfullyConnected === true, + 'Expected a successful connection response.' + ); + assert( + testConnectionController.getSavedConnections().length === 1, + 'Expected there to be 1 connection in the connection list.' + ); + assert( + name === 'localhost:27018', + `Expected active connection to be 'localhost:27018' found ${name}` + ); + assert(connectionModel !== null); + assert( + connectionModel?.getAttributes({ + derived: true + }).instanceId === 'localhost:27018' + ); + assert(dataService !== null); + assert( + testConnectionController._activeConnectionModel?.appname.startsWith( + 'mongodb-vscode' + ) + ); + assert(testConnectionController.isCurrentlyConnected()); + } catch (error) { + assert(false); + } }); - test('when the extension is deactivated, the active connection is disconnected', (done) => { - const testTelemetryController = new TelemetryController( - mockStorageController, - mockExtensionContext - ); - const testConnectionController = new ConnectionController( - new StatusView(mockExtensionContext), - mockStorageController, - testTelemetryController - ); - - testConnectionController - .addNewConnectionStringAndConnect(TEST_DATABASE_URI) - .then(async (succesfullyConnected) => { - assert( - succesfullyConnected === true, - 'Expected a successful (true) connection response.' - ); - assert( - testConnectionController.getActiveDataService() !== null, - 'Expected active data service to not be null.' - ); - assert( - testConnectionController.getActiveConnectionId() !== null, - 'Expected active connection id to not be null.' - ); + test('"disconnect()" disconnects from the active connection', async () => { + try { + const succesfullyConnected = await testConnectionController.addNewConnectionStringAndConnect( + TEST_DATABASE_URI + ); - await testConnectionController.disconnect(); + assert( + succesfullyConnected === true, + 'Expected a successful (true) connection response.' + ); - assert( - testConnectionController.getActiveDataService() === null, - 'Expected active data service to be null.' - ); - assert( - testConnectionController.getActiveConnectionId() === null, - 'Expected active connection id to be null.' - ); - }, done) - .then(done, done); - }); + const successfullyDisconnected = await testConnectionController.disconnect(); - test('"removeMongoDBConnection()" returns a reject promise when there is no active connection', (done) => { - const testTelemetryController = new TelemetryController( - mockStorageController, - mockExtensionContext - ); - const testConnectionController = new ConnectionController( - new StatusView(mockExtensionContext), - mockStorageController, - testTelemetryController - ); + // Disconnecting should keep the connection contract, just disconnected. + const connectionsCount = testConnectionController.getSavedConnections() + .length; + const connnectionId = testConnectionController.getActiveConnectionId(); + const connectionModel = testConnectionController.getActiveConnectionModel(); + const dataService = testConnectionController.getActiveDataService(); - testConnectionController - .onRemoveMongoDBConnection() - .then(null, (err) => { - assert(!!err, `Expected an error response, recieved ${err}.`); - }) - .then(done, done); + assert( + successfullyDisconnected === true, + 'Expected a successful (true) disconnect response.' + ); + assert( + connectionsCount === 1, + `Expected the amount of connections to be 1 found ${connectionsCount}.` + ); + assert( + connnectionId === null, + `Expected the active connection id to be null, found ${connnectionId}` + ); + assert(connectionModel === null); + assert(dataService === null); + assert(!testConnectionController.isCurrentlyConnected()); + } catch (error) { + assert(false); + } }); - test('"disconnect()" fails when there is no active connection', (done) => { - const testTelemetryController = new TelemetryController( - mockStorageController, - mockExtensionContext - ); - const testConnectionController = new ConnectionController( - new StatusView(mockExtensionContext), - mockStorageController, - testTelemetryController - ); - - testConnectionController - .disconnect() - .then(null, (err) => { - assert(!!err, 'Expected an error disconnect response.'); - }) - .then(done, done); + test('"removeMongoDBConnection()" returns a reject promise when there is no active connection', async () => { + try { + await testConnectionController.onRemoveMongoDBConnection(); + } catch (error) { + assert(!!error, `Expected an error response, recieved ${error}.`); + } }); - test('it errors when disconnecting with no active connection', (done) => { - const testTelemetryController = new TelemetryController( - mockStorageController, - mockExtensionContext - ); - const testConnectionController = new ConnectionController( - new StatusView(mockExtensionContext), - mockStorageController, - testTelemetryController - ); + test('"disconnect()" fails when there is no active connection', async () => { + const expectedMessage = 'Unable to disconnect: no active connection.'; const fakeVscodeErrorMessage = sinon.fake(); sinon.replace(vscode.window, 'showErrorMessage', fakeVscodeErrorMessage); - testConnectionController - .disconnect() - .then((disconnectSucceeded) => { - assert( - disconnectSucceeded === false, - 'Expected an false success disconnect response.' - ); - const expectedMessage = 'Unable to disconnect: no active connection.'; - assert( - fakeVscodeErrorMessage.firstArg === expectedMessage, - `Expected error message "${expectedMessage}" when disconnecting with no active connection, recieved "${fakeVscodeErrorMessage.firstArg}"` - ); - }) - .then(done, done); + try { + await testConnectionController.disconnect(); + + assert( + fakeVscodeErrorMessage.firstArg === expectedMessage, + `Expected error message "${expectedMessage}" when disconnecting with no active connection, recieved "${fakeVscodeErrorMessage.firstArg}"` + ); + } catch (error) { + assert(!!error, 'Expected an error disconnect response.'); + } }); - test('when adding a new connection it disconnects from the current connection', (done) => { - const testTelemetryController = new TelemetryController( - mockStorageController, - mockExtensionContext - ); - const testConnectionController = new ConnectionController( - new StatusView(mockExtensionContext), - mockStorageController, - testTelemetryController - ); + test('when adding a new connection it disconnects from the current connection', async () => { + try { + const succesfullyConnected = await testConnectionController.addNewConnectionStringAndConnect( + TEST_DATABASE_URI + ); - testConnectionController - .addNewConnectionStringAndConnect(TEST_DATABASE_URI) - .then((succesfullyConnected) => { - assert( - succesfullyConnected, - 'Expected a successful (true) connection response.' - ); + assert( + succesfullyConnected, + 'Expected a successful (true) connection response.' + ); + } catch (error) { + assert(false); + } - testConnectionController - .addNewConnectionStringAndConnect(testDatabaseURI2WithTimeout) - .then( - () => { - assert(false, 'Expected rejected promise, not resolved.'); - }, - (error) => { - const expectedError = 'Failed to connect'; - assert( - error.message.includes(expectedError), - `Expected error with message: ${expectedError}, got: ${error.message}` - ); - assert( - testConnectionController.getActiveDataService() === null, - 'Expected to current connection to be null (not connected).' - ); - assert( - testConnectionController.getActiveConnectionId() === null, - 'Expected to current connection instanceId to be null (not connected).' - ); - } - ) - .then(done, done); - }); - }); + try { + await testConnectionController.addNewConnectionStringAndConnect( + testDatabaseURI2WithTimeout + ); + } catch (error) { + const expectedError = 'Failed to connect'; - test('"connect()" failed when we are currently connecting', (done) => { - const testTelemetryController = new TelemetryController( - mockStorageController, - mockExtensionContext - ); - const testConnectionController = new ConnectionController( - new StatusView(mockExtensionContext), - mockStorageController, - testTelemetryController - ); + assert( + error.message.includes(expectedError), + `Expected error with message: ${expectedError}, got: ${error.message}` + ); + assert( + testConnectionController.getActiveDataService() === null, + 'Expected to current connection to be null (not connected).' + ); + assert( + testConnectionController.getActiveConnectionId() === null, + 'Expected to current connection instanceId to be null (not connected).' + ); + } + }); + test('"connect()" failed when we are currently connecting', async () => { testConnectionController.setConnnecting(true); - testConnectionController - .addNewConnectionStringAndConnect(TEST_DATABASE_URI) - .then( - () => { - assert(false, 'Expected rejected promise, not resolved.'); - }, - (error) => { - const expectedMessage = 'Unable to connect: already connecting.'; - assert( - error.message === expectedMessage, - `Expected "${expectedMessage}" when connecting when already connecting, recieved "${error.message}"` - ); - } - ) - .then(done, done); - }); + try { + await testConnectionController.addNewConnectionStringAndConnect( + TEST_DATABASE_URI + ); + } catch (error) { + const expectedMessage = 'Unable to connect: already connecting.'; - test('"connect()" failed when we are currently disconnecting', (done) => { - const testTelemetryController = new TelemetryController( - mockStorageController, - mockExtensionContext - ); - const testConnectionController = new ConnectionController( - new StatusView(mockExtensionContext), - mockStorageController, - testTelemetryController - ); + assert( + error.message === expectedMessage, + `Expected "${expectedMessage}" when connecting when already connecting, recieved "${error.message}"` + ); + } + }); + test('"connect()" failed when we are currently disconnecting', async () => { testConnectionController.setDisconnecting(true); - testConnectionController - .addNewConnectionStringAndConnect(TEST_DATABASE_URI) - .then( - () => { - assert(false, 'Expected rejected promise, not resolved.'); - }, - (error) => { - const expectedMessage = 'Unable to connect: currently disconnecting.'; - assert( - error.message === expectedMessage, - `Expected "${expectedMessage}" when connecting while disconnecting, recieved "${error.message}"` - ); - } - ) - .then(done, done); - }); - - test('"disconnect()" fails when we are currently connecting', (done) => { - const testTelemetryController = new TelemetryController( - mockStorageController, - mockExtensionContext - ); - const testConnectionController = new ConnectionController( - new StatusView(mockExtensionContext), - mockStorageController, - testTelemetryController - ); + try { + await testConnectionController.addNewConnectionStringAndConnect( + TEST_DATABASE_URI + ); + } catch (error) { + const expectedMessage = 'Unable to connect: currently disconnecting.'; - testConnectionController.setConnnecting(true); + assert( + error.message === expectedMessage, + `Expected "${expectedMessage}" when connecting while disconnecting, recieved "${error.message}"` + ); + } + }); + test('"disconnect()" fails when we are currently connecting', async () => { + const expectedMessage = + 'Unable to disconnect: currently connecting to an instance.'; const fakeVscodeErrorMessage = sinon.fake(); + testConnectionController.setConnnecting(true); sinon.replace(vscode.window, 'showErrorMessage', fakeVscodeErrorMessage); - testConnectionController - .disconnect() - .then((disconnectSucceeded) => { - assert( - disconnectSucceeded === false, - 'Expected the disconnect to return a false succeeded response' - ); - const expectedMessage = - 'Unable to disconnect: currently connecting to an instance.'; - assert( - fakeVscodeErrorMessage.firstArg === expectedMessage, - `Expected "${expectedMessage}" when disconnecting while connecting, recieved "${fakeVscodeErrorMessage.firstArg}"` - ); - }) - .then(done, done); - }); - - test('"disconnect()" fails when we are currently disconnecting', (done) => { - const testTelemetryController = new TelemetryController( - mockStorageController, - mockExtensionContext - ); - const testConnectionController = new ConnectionController( - new StatusView(mockExtensionContext), - mockStorageController, - testTelemetryController - ); + try { + await testConnectionController.disconnect(); - testConnectionController.setDisconnecting(true); + assert( + fakeVscodeErrorMessage.firstArg === expectedMessage, + `Expected "${expectedMessage}" when disconnecting while connecting, recieved "${fakeVscodeErrorMessage.firstArg}"` + ); + } catch (error) { + assert(!!error, 'Expected an error disconnect response.'); + } + }); + test('"disconnect()" fails when we are currently disconnecting', async () => { + const expectedMessage = + 'Unable to disconnect: already disconnecting from an instance.'; const fakeVscodeErrorMessage = sinon.fake(); + testConnectionController.setDisconnecting(true); sinon.replace(vscode.window, 'showErrorMessage', fakeVscodeErrorMessage); - testConnectionController - .disconnect() - .then((disconnectSucceeded) => { - assert( - disconnectSucceeded === false, - 'Expected the disconnect to return a false succeeded response' - ); - const expectedMessage = - 'Unable to disconnect: already disconnecting from an instance.'; - assert( - fakeVscodeErrorMessage.firstArg === expectedMessage, - `Expected "${expectedMessage}" when disconnecting while already disconnecting, recieved "${fakeVscodeErrorMessage.firstArg}"` - ); - }) - .then(done, done); + try { + await testConnectionController.disconnect(); + + assert( + fakeVscodeErrorMessage.firstArg === expectedMessage, + `Expected "${expectedMessage}" when disconnecting while already disconnecting, recieved "${fakeVscodeErrorMessage.firstArg}"` + ); + } catch (error) { + assert(!!error, 'Expected an error disconnect response.'); + } }); - test('"connect()" should fire a CONNECTIONS_DID_CHANGE event', (done) => { - const testTelemetryController = new TelemetryController( - mockStorageController, - mockExtensionContext - ); - const testConnectionController = new ConnectionController( - new StatusView(mockExtensionContext), - mockStorageController, - testTelemetryController - ); - let didFireConnectionEvent = false; + test('"connect()" should fire a CONNECTIONS_DID_CHANGE event', async () => { + let isConnectionChanged: any = false; testConnectionController.addEventListener( DataServiceEventTypes.CONNECTIONS_DID_CHANGE, () => { - didFireConnectionEvent = true; + isConnectionChanged = true; } ); - testConnectionController - .addNewConnectionStringAndConnect(TEST_DATABASE_URI) - .then(() => { - setTimeout(() => { - assert( - didFireConnectionEvent === true, - 'Expected connection event to be fired.' - ); - done(); - }, 150); - }); + try { + await testConnectionController.addNewConnectionStringAndConnect( + TEST_DATABASE_URI + ); + await sleep(50); + + assert(isConnectionChanged === true); + } catch (error) { + assert(false); + } }); const expectedTimesToFire = 3; - test(`"connect()" then "disconnect()" should fire the connections did change event ${expectedTimesToFire} times`, (done) => { - const testTelemetryController = new TelemetryController( - mockStorageController, - mockExtensionContext - ); - const testConnectionController = new ConnectionController( - new StatusView(mockExtensionContext), - mockStorageController, - testTelemetryController - ); + test(`"connect()" then "disconnect()" should fire the connections did change event ${expectedTimesToFire} times`, async () => { let connectionEventFiredCount = 0; testConnectionController.addEventListener( @@ -469,201 +307,198 @@ suite('Connection Controller Test Suite', () => { } ); - testConnectionController - .addNewConnectionStringAndConnect(TEST_DATABASE_URI) - .then(() => { - testConnectionController.disconnect().then(() => { - setTimeout(() => { - assert( - connectionEventFiredCount === expectedTimesToFire, - `Expected connection event to be fired ${expectedTimesToFire} times, got ${connectionEventFiredCount}.` - ); - done(); - }, 150); - }); - }); + try { + await testConnectionController.addNewConnectionStringAndConnect( + TEST_DATABASE_URI + ); + await testConnectionController.disconnect(); + await sleep(500); + + assert( + connectionEventFiredCount === expectedTimesToFire, + `Expected connection event to be fired ${expectedTimesToFire} times, got ${connectionEventFiredCount}.` + ); + } catch (error) { + assert(false); + } }); test('when there are no existing connections in the store and the connection controller loads connections', async () => { - const testTelemetryController = new TelemetryController( - mockStorageController, - mockExtensionContext - ); - const testConnectionController = new ConnectionController( - new StatusView(mockExtensionContext), - mockStorageController, - testTelemetryController - ); + try { + await testConnectionController.loadSavedConnections(); - await testConnectionController.loadSavedConnections(); + const connectionsCount = testConnectionController.getSavedConnections() + .length; - const connectionsCount = testConnectionController.getSavedConnections() - .length; - - assert( - connectionsCount === 0, - `Expected connections to be 0 found ${connectionsCount}` - ); + assert( + connectionsCount === 0, + `Expected connections to be 0 found ${connectionsCount}` + ); + } catch (error) { + assert(false); + } }); - test('The connection model loads both global and workspace stored connection models', async () => { - const testTelemetryController = new TelemetryController( - mockStorageController, - mockExtensionContext - ); - const testConnectionController = new ConnectionController( - new StatusView(mockExtensionContext), - mockStorageController, - testTelemetryController - ); - - await vscode.workspace - .getConfiguration('mdb.connectionSaving') - .update('defaultConnectionSavingLocation', DefaultSavingLocations.Global); - await testConnectionController.addNewConnectionStringAndConnect( - TEST_DATABASE_URI - ); - await testConnectionController.addNewConnectionStringAndConnect( - TEST_DATABASE_URI - ); - await vscode.workspace - .getConfiguration('mdb.connectionSaving') - .update( - 'defaultConnectionSavingLocation', - DefaultSavingLocations.Workspace - ); - await testConnectionController.addNewConnectionStringAndConnect( - TEST_DATABASE_URI - ); - await testConnectionController.addNewConnectionStringAndConnect( - TEST_DATABASE_URI - ); - await testConnectionController.disconnect(); - testConnectionController.clearAllConnections(); - await testConnectionController.loadSavedConnections(); - const connections = testConnectionController._connections; - assert( - Object.keys(connections).length === 4, - `Expected 4 connection configurations found ${ - Object.keys(connections).length - }` - ); - assert( - connections[Object.keys(connections)[0]].name === 'localhost:27018', - "Expected loaded connection to include name 'localhost:27018'" - ); + test('the connection model loads both global and workspace stored connection models', async () => { const expectedDriverUri = 'mongodb://localhost:27018/?readPreference=primary&ssl=false'; - assert( - connections[Object.keys(connections)[2]].driverUrl === expectedDriverUri, - `Expected loaded connection to include driver url '${expectedDriverUri}' found '${ - connections[Object.keys(connections)[2]].driverUrl - }'` - ); + + try { + await vscode.workspace + .getConfiguration('mdb.connectionSaving') + .update( + 'defaultConnectionSavingLocation', + DefaultSavingLocations.Global + ); + await testConnectionController.addNewConnectionStringAndConnect( + TEST_DATABASE_URI + ); + await testConnectionController.addNewConnectionStringAndConnect( + TEST_DATABASE_URI + ); + await vscode.workspace + .getConfiguration('mdb.connectionSaving') + .update( + 'defaultConnectionSavingLocation', + DefaultSavingLocations.Workspace + ); + await testConnectionController.addNewConnectionStringAndConnect( + TEST_DATABASE_URI + ); + await testConnectionController.addNewConnectionStringAndConnect( + TEST_DATABASE_URI + ); + await testConnectionController.disconnect(); + testConnectionController.clearAllConnections(); + await testConnectionController.loadSavedConnections(); + + const connections = testConnectionController._connections; + + assert( + Object.keys(connections).length === 4, + `Expected 4 connection configurations found ${ + Object.keys(connections).length + }` + ); + assert( + connections[Object.keys(connections)[0]].name === 'localhost:27018', + "Expected loaded connection to include name 'localhost:27018'" + ); + assert( + connections[Object.keys(connections)[2]].driverUrl === + expectedDriverUri, + `Expected loaded connection to include driver url '${expectedDriverUri}' found '${ + connections[Object.keys(connections)[2]].driverUrl + }'` + ); + } catch (error) { + assert(false); + } }); - test('When a connection is added it is saved to the global store', async () => { - const testTelemetryController = new TelemetryController( - mockStorageController, - mockExtensionContext - ); - const testConnectionController = new ConnectionController( - new StatusView(mockExtensionContext), - mockStorageController, - testTelemetryController - ); + test('when a connection is added it is saved to the global store', async () => { + try { + await testConnectionController.loadSavedConnections(); + await vscode.workspace + .getConfiguration('mdb.connectionSaving') + .update( + 'defaultConnectionSavingLocation', + DefaultSavingLocations.Global + ); + await testConnectionController.addNewConnectionStringAndConnect( + TEST_DATABASE_URI + ); - await testConnectionController.loadSavedConnections(); - await vscode.workspace - .getConfiguration('mdb.connectionSaving') - .update('defaultConnectionSavingLocation', DefaultSavingLocations.Global); - await testConnectionController.addNewConnectionStringAndConnect( - TEST_DATABASE_URI - ); - const globalStoreConnections = mockStorageController.get( - StorageVariables.GLOBAL_SAVED_CONNECTIONS - ); - assert( - Object.keys(globalStoreConnections).length === 1, - `Expected global store connections to have 1 connection found ${ - Object.keys(globalStoreConnections).length - }` - ); - const id = Object.keys(globalStoreConnections)[0]; - assert( - globalStoreConnections[id].name === testDatabaseInstanceId, - `Expected global stored connection to have correct name '${testDatabaseInstanceId}' found ${globalStoreConnections[id].name}` - ); - const workspaceStoreConnections = mockStorageController.get( - StorageVariables.WORKSPACE_SAVED_CONNECTIONS - ); - assert( - workspaceStoreConnections === undefined, - `Expected workspace store connections to be 'undefined' found ${workspaceStoreConnections}` - ); + const globalStoreConnections = mockStorageController.get( + StorageVariables.GLOBAL_SAVED_CONNECTIONS + ); + + assert( + Object.keys(globalStoreConnections).length === 1, + `Expected global store connections to have 1 connection found ${ + Object.keys(globalStoreConnections).length + }` + ); + + const id = Object.keys(globalStoreConnections)[0]; + + assert( + globalStoreConnections[id].name === testDatabaseInstanceId, + `Expected global stored connection to have correct name '${testDatabaseInstanceId}' found ${globalStoreConnections[id].name}` + ); + + const workspaceStoreConnections = mockStorageController.get( + StorageVariables.WORKSPACE_SAVED_CONNECTIONS + ); + + assert( + workspaceStoreConnections === undefined, + `Expected workspace store connections to be 'undefined' found ${workspaceStoreConnections}` + ); + } catch (error) { + assert(false); + } }); - test('When a connection is added it is saved to the workspace store', async () => { - const testTelemetryController = new TelemetryController( - mockStorageController, - mockExtensionContext - ); - const testConnectionController = new ConnectionController( - new StatusView(mockExtensionContext), - mockStorageController, - testTelemetryController - ); + test('when a connection is added it is saved to the workspace store', async () => { + try { + await testConnectionController.loadSavedConnections(); + await vscode.workspace + .getConfiguration('mdb.connectionSaving') + .update( + 'defaultConnectionSavingLocation', + DefaultSavingLocations.Workspace + ); + await testConnectionController.addNewConnectionStringAndConnect( + TEST_DATABASE_URI + ); - testConnectionController.loadSavedConnections(); + const workspaceStoreConnections = mockStorageController.get( + StorageVariables.WORKSPACE_SAVED_CONNECTIONS, + StorageScope.WORKSPACE + ); - await vscode.workspace - .getConfiguration('mdb.connectionSaving') - .update( - 'defaultConnectionSavingLocation', - DefaultSavingLocations.Workspace + assert( + Object.keys(workspaceStoreConnections).length === 1, + `Expected workspace store connections to have 1 connection found ${ + Object.keys(workspaceStoreConnections).length + }` ); - await testConnectionController.addNewConnectionStringAndConnect( - TEST_DATABASE_URI - ); - const workspaceStoreConnections = mockStorageController.get( - StorageVariables.WORKSPACE_SAVED_CONNECTIONS, - StorageScope.WORKSPACE - ); - assert( - Object.keys(workspaceStoreConnections).length === 1, - `Expected workspace store connections to have 1 connection found ${ - Object.keys(workspaceStoreConnections).length - }` - ); - const id = Object.keys(workspaceStoreConnections)[0]; - assert( - workspaceStoreConnections[id].name === testDatabaseInstanceId, - `Expected workspace stored connection to have correct name '${testDatabaseInstanceId}' found ${workspaceStoreConnections[id].name}` - ); - const globalStoreConnections = mockStorageController.get( - StorageVariables.GLOBAL_SAVED_CONNECTIONS - ); - assert( - globalStoreConnections === undefined, - `Expected global store connections to be 'undefined' found ${globalStoreConnections}` - ); + + const id = Object.keys(workspaceStoreConnections)[0]; + + assert( + workspaceStoreConnections[id].name === testDatabaseInstanceId, + `Expected workspace stored connection to have correct name '${testDatabaseInstanceId}' found ${workspaceStoreConnections[id].name}` + ); + + const globalStoreConnections = mockStorageController.get( + StorageVariables.GLOBAL_SAVED_CONNECTIONS + ); + + assert( + globalStoreConnections === undefined, + `Expected global store connections to be 'undefined' found ${globalStoreConnections}` + ); + } catch (error) { + assert(false); + } }); - test('A connection can be connected to by id', (done) => { - const testTelemetryController = new TelemetryController( - mockStorageController, - mockExtensionContext - ); - const testConnectionController = new ConnectionController( - new StatusView(mockExtensionContext), - mockStorageController, - testTelemetryController - ); + test('a connection can be connected to by id', async () => { + const getConnection = (TEST_DATABASE_URI): Promise => + new Promise((resolve, reject) => { + Connection.from(TEST_DATABASE_URI, (err, connectionModel) => { + if (err) { + return reject(err); + } - Connection.from(TEST_DATABASE_URI, (err, connectionModel) => { - if (err) { - assert(false); - } + return resolve(connectionModel); + }); + }); + + try { + const connectionModel = await getConnection(TEST_DATABASE_URI); testConnectionController._connections = { '25': { @@ -675,375 +510,316 @@ suite('Connection Controller Test Suite', () => { } }; - testConnectionController - .connectWithConnectionId('25') - .then((successfulConnection) => { - assert(successfulConnection); - assert(testConnectionController.getActiveConnectionId() === '25'); - testConnectionController.disconnect(); + const successfulConnection = await testConnectionController.connectWithConnectionId( + '25' + ); - done(); - }); - }); + assert(successfulConnection); + assert(testConnectionController.getActiveConnectionId() === '25'); + } catch (error) { + assert(false); + } }); - test('A saved connection can be loaded and connected to', (done) => { - const testTelemetryController = new TelemetryController( - mockStorageController, - mockExtensionContext - ); - const testConnectionController = new ConnectionController( - new StatusView(mockExtensionContext), - mockStorageController, - testTelemetryController - ); - - testConnectionController.loadSavedConnections().then(() => { - vscode.workspace + test('a saved connection can be loaded and connected to', async () => { + try { + await testConnectionController.loadSavedConnections(); + await vscode.workspace .getConfiguration('mdb.connectionSaving') .update( 'defaultConnectionSavingLocation', DefaultSavingLocations.Workspace - ) - .then(() => { - testConnectionController - .addNewConnectionStringAndConnect(TEST_DATABASE_URI) - .then(() => { - const workspaceStoreConnections = mockStorageController.get( - StorageVariables.WORKSPACE_SAVED_CONNECTIONS, - StorageScope.WORKSPACE - ); - assert( - Object.keys(workspaceStoreConnections).length === 1, - `Expected workspace store connections to have 1 connection found ${ - Object.keys(workspaceStoreConnections).length - }` - ); - - testConnectionController - .disconnect() - .then(() => { - testConnectionController.clearAllConnections(); - assert( - testConnectionController.getSavedConnections().length === 0, - 'Expected no connection configs.' - ); - - // Activate (which will load the past connection). - testConnectionController.loadSavedConnections().then(() => { - assert( - testConnectionController.getSavedConnections().length === - 1, - `Expected 1 connection config, found ${ - testConnectionController.getSavedConnections().length - }.` - ); - const id = testConnectionController.getSavedConnections()[0] - .id; - - testConnectionController - .connectWithConnectionId(id) - .then(() => { - const activeId = testConnectionController.getActiveConnectionId(); - const name = - testConnectionController._connections[activeId || ''] - .name; - assert( - activeId === id, - `Expected the active connection to be '${id}', found ${activeId}.` - ); - assert( - name === 'localhost:27018', - `Expected the active connection name to be 'localhost:27018', found ${name}.` - ); - const port = - testConnectionController._connections[activeId || ''] - .connectionModel.port; - assert( - port === 27018, - `Expected the active connection port to be '27018', found ${port}.` - ); - }, done) - .then(done, done); - }); - }) - .then(null, done); - }) - .then(null, done); - }); - }); + ); + await testConnectionController.addNewConnectionStringAndConnect( + TEST_DATABASE_URI + ); + + const workspaceStoreConnections = mockStorageController.get( + StorageVariables.WORKSPACE_SAVED_CONNECTIONS, + StorageScope.WORKSPACE + ); + + assert( + Object.keys(workspaceStoreConnections).length === 1, + `Expected workspace store connections to have 1 connection found ${ + Object.keys(workspaceStoreConnections).length + }` + ); + + await testConnectionController.disconnect(); + testConnectionController.clearAllConnections(); + + assert( + testConnectionController.getSavedConnections().length === 0, + 'Expected no connection configs.' + ); + + // Activate (which will load the past connection). + await testConnectionController.loadSavedConnections(); + + assert( + testConnectionController.getSavedConnections().length === 1, + `Expected 1 connection config, found ${ + testConnectionController.getSavedConnections().length + }.` + ); + + const id = testConnectionController.getSavedConnections()[0].id; + + await testConnectionController.connectWithConnectionId(id); + + const activeId = testConnectionController.getActiveConnectionId(); + const name = testConnectionController._connections[activeId || ''].name; + + assert( + activeId === id, + `Expected the active connection to be '${id}', found ${activeId}.` + ); + assert( + name === 'localhost:27018', + `Expected the active connection name to be 'localhost:27018', found ${name}.` + ); + + const port = + testConnectionController._connections[activeId || ''].connectionModel + .port; + + assert( + port === 27018, + `Expected the active connection port to be '27018', found ${port}.` + ); + } catch (error) { + assert(false); + } }); - test('"getConnectionStringFromConnectionId" returns the driver uri of a connection', (done) => { - const testTelemetryController = new TelemetryController( - mockStorageController, - mockExtensionContext - ); - const testConnectionController = new ConnectionController( - new StatusView(mockExtensionContext), - mockStorageController, - testTelemetryController - ); + test('"getConnectionStringFromConnectionId" returns the driver uri of a connection', async () => { const expectedDriverUri = 'mongodb://localhost:27018/?readPreference=primary&ssl=false'; - testConnectionController.loadSavedConnections().then(() => { - testConnectionController - .addNewConnectionStringAndConnect(TEST_DATABASE_URI) - .then(() => { - const activeConnectionId = testConnectionController.getActiveConnectionId(); - assert( - activeConnectionId !== null, - 'Expected active connection to not be null' - ); - const testDriverUri = testConnectionController.getConnectionStringFromConnectionId( - activeConnectionId || '' - ); - assert( - testDriverUri === expectedDriverUri, - `Expected to be returned the driver uri "${expectedDriverUri}" found ${testDriverUri}` - ); - }) - .then(done, done); - }); + try { + await testConnectionController.loadSavedConnections(); + await testConnectionController.addNewConnectionStringAndConnect( + TEST_DATABASE_URI + ); + + const activeConnectionId = testConnectionController.getActiveConnectionId(); + + assert( + activeConnectionId !== null, + 'Expected active connection to not be null' + ); + + const testDriverUri = testConnectionController.getConnectionStringFromConnectionId( + activeConnectionId || '' + ); + + assert( + testDriverUri === expectedDriverUri, + `Expected to be returned the driver uri "${expectedDriverUri}" found ${testDriverUri}` + ); + } catch (error) { + assert(false); + } }); - test('When a connection is added and the user has set it to not save on default it is not saved', (done) => { - const testTelemetryController = new TelemetryController( - mockStorageController, - mockExtensionContext - ); - const testConnectionController = new ConnectionController( - new StatusView(mockExtensionContext), - mockStorageController, - testTelemetryController - ); + test('when a connection is added and the user has set it to not save on default it is not saved', async () => { + try { + await testConnectionController.loadSavedConnections(); - testConnectionController.loadSavedConnections().then(() => { // Don't save connections on default. - vscode.workspace + await vscode.workspace .getConfiguration('mdb.connectionSaving') .update( 'defaultConnectionSavingLocation', DefaultSavingLocations['Session Only'] - ) - .then(() => { - // Updating a setting sometimes takes a bit on vscode, and it doesnt - // return a usable promise. Timeout to ensure it sets. - setTimeout(() => { - testConnectionController - .addNewConnectionStringAndConnect(TEST_DATABASE_URI) - .then(() => { - const objectString = JSON.stringify(undefined); - const globalStoreConnections = mockStorageController.get( - StorageVariables.GLOBAL_SAVED_CONNECTIONS - ); - assert( - JSON.stringify(globalStoreConnections) === objectString, - `Expected global store connections to be an empty object found ${globalStoreConnections}` - ); - const workspaceStoreConnections = mockStorageController.get( - StorageVariables.WORKSPACE_SAVED_CONNECTIONS, - StorageScope.WORKSPACE - ); - assert( - JSON.stringify(workspaceStoreConnections) === objectString, - `Expected workspace store connections to be an empty object found ${JSON.stringify( - workspaceStoreConnections - )}` - ); - }) - .then(done) - .catch(done); - }, 50); - }, done); - }); - }); + ); + await testConnectionController.addNewConnectionStringAndConnect( + TEST_DATABASE_URI + ); - test('When a connection is removed it is also removed from workspace storage', (done) => { - const testTelemetryController = new TelemetryController( - mockStorageController, - mockExtensionContext - ); - const testConnectionController = new ConnectionController( - new StatusView(mockExtensionContext), - mockStorageController, - testTelemetryController - ); + const objectString = JSON.stringify(undefined); + const globalStoreConnections = mockStorageController.get( + StorageVariables.GLOBAL_SAVED_CONNECTIONS + ); + + assert( + JSON.stringify(globalStoreConnections) === objectString, + `Expected global store connections to be an empty object found ${globalStoreConnections}` + ); + + const workspaceStoreConnections = mockStorageController.get( + StorageVariables.WORKSPACE_SAVED_CONNECTIONS, + StorageScope.WORKSPACE + ); - testConnectionController.loadSavedConnections().then(() => { - vscode.workspace + assert( + JSON.stringify(workspaceStoreConnections) === objectString, + `Expected workspace store connections to be an empty object found ${JSON.stringify( + workspaceStoreConnections + )}` + ); + } catch (error) { + assert(false); + } + }); + + test('when a connection is removed it is also removed from workspace storage', async () => { + try { + await testConnectionController.loadSavedConnections(); + await vscode.workspace .getConfiguration('mdb.connectionSaving') .update( 'defaultConnectionSavingLocation', DefaultSavingLocations.Workspace - ) - .then(() => { - // Updating a setting sometimes takes a bit on vscode, and it doesnt - // return a usable promise. Timeout to ensure it sets. - setTimeout(() => { - testConnectionController - .addNewConnectionStringAndConnect(TEST_DATABASE_URI) - .then(async () => { - const workspaceStoreConnections = mockStorageController.get( - StorageVariables.WORKSPACE_SAVED_CONNECTIONS, - StorageScope.WORKSPACE - ); - assert( - Object.keys(workspaceStoreConnections).length === 1, - `Expected workspace store connections to have 1 connection found ${ - Object.keys(workspaceStoreConnections).length - }` - ); - const connectionId = - testConnectionController.getActiveConnectionId() || 'a'; - testConnectionController.disconnect(); - await testConnectionController.removeSavedConnection( - connectionId - ); - const postWorkspaceStoreConnections = mockStorageController.get( - StorageVariables.WORKSPACE_SAVED_CONNECTIONS, - StorageScope.WORKSPACE - ); - assert( - Object.keys(postWorkspaceStoreConnections).length === 0, - `Expected workspace store connections to have 0 connections found ${ - Object.keys(postWorkspaceStoreConnections).length - }` - ); - }) - .then(done, done); - }, 50); - }); - }); - }); + ); + await testConnectionController.addNewConnectionStringAndConnect( + TEST_DATABASE_URI + ); - test('When a connection is removed it is also removed from global storage', async () => { - const testTelemetryController = new TelemetryController( - mockStorageController, - mockExtensionContext - ); - const testConnectionController = new ConnectionController( - new StatusView(mockExtensionContext), - mockStorageController, - testTelemetryController - ); + const workspaceStoreConnections = mockStorageController.get( + StorageVariables.WORKSPACE_SAVED_CONNECTIONS, + StorageScope.WORKSPACE + ); - await testConnectionController.loadSavedConnections(); - await vscode.workspace - .getConfiguration('mdb.connectionSaving') - .update('defaultConnectionSavingLocation', DefaultSavingLocations.Global); - await testConnectionController.addNewConnectionStringAndConnect( - TEST_DATABASE_URI - ); - const globalStoreConnections = mockStorageController.get( - StorageVariables.GLOBAL_SAVED_CONNECTIONS - ); - assert( - Object.keys(globalStoreConnections).length === 1, - `Expected workspace store connections to have 1 connection found ${ - Object.keys(globalStoreConnections).length - }` - ); - const connectionId = - testConnectionController.getActiveConnectionId() || 'a'; - await testConnectionController.removeSavedConnection(connectionId); - const postGlobalStoreConnections = mockStorageController.get( - StorageVariables.GLOBAL_SAVED_CONNECTIONS - ); - assert( - Object.keys(postGlobalStoreConnections).length === 0, - `Expected global store connections to have 0 connections found ${ - Object.keys(postGlobalStoreConnections).length - }` - ); + assert( + Object.keys(workspaceStoreConnections).length === 1, + `Expected workspace store connections to have 1 connection found ${ + Object.keys(workspaceStoreConnections).length + }` + ); + + const connectionId = + testConnectionController.getActiveConnectionId() || 'a'; + + await testConnectionController.disconnect(); + await testConnectionController.removeSavedConnection(connectionId); + + const postWorkspaceStoreConnections = mockStorageController.get( + StorageVariables.WORKSPACE_SAVED_CONNECTIONS, + StorageScope.WORKSPACE + ); + + assert( + Object.keys(postWorkspaceStoreConnections).length === 0, + `Expected workspace store connections to have 0 connections found ${ + Object.keys(postWorkspaceStoreConnections).length + }` + ); + } catch (error) { + assert(false); + } }); - test('A saved connection can be renamed and loaded', (done) => { - const testTelemetryController = new TelemetryController( - mockStorageController, - mockExtensionContext - ); - const testConnectionController = new ConnectionController( - new StatusView(mockExtensionContext), - mockStorageController, - testTelemetryController - ); + test('when a connection is removed it is also removed from global storage', async () => { + try { + await testConnectionController.loadSavedConnections(); + await vscode.workspace + .getConfiguration('mdb.connectionSaving') + .update( + 'defaultConnectionSavingLocation', + DefaultSavingLocations.Global + ); + await testConnectionController.addNewConnectionStringAndConnect( + TEST_DATABASE_URI + ); + + const globalStoreConnections = mockStorageController.get( + StorageVariables.GLOBAL_SAVED_CONNECTIONS + ); + + assert( + Object.keys(globalStoreConnections).length === 1, + `Expected workspace store connections to have 1 connection found ${ + Object.keys(globalStoreConnections).length + }` + ); - testConnectionController.loadSavedConnections().then(() => { - vscode.workspace + const connectionId = + testConnectionController.getActiveConnectionId() || 'a'; + await testConnectionController.removeSavedConnection(connectionId); + + const postGlobalStoreConnections = mockStorageController.get( + StorageVariables.GLOBAL_SAVED_CONNECTIONS + ); + + assert( + Object.keys(postGlobalStoreConnections).length === 0, + `Expected global store connections to have 0 connections found ${ + Object.keys(postGlobalStoreConnections).length + }` + ); + } catch (error) { + assert(false); + } + }); + + test('a saved connection can be renamed and loaded', async () => { + try { + await testConnectionController.loadSavedConnections(); + await vscode.workspace .getConfiguration('mdb.connectionSaving') .update( 'defaultConnectionSavingLocation', DefaultSavingLocations.Workspace - ) - .then(() => { - // Updating a setting sometimes takes a bit on vscode, and it doesnt - // return a usable promise. Timeout to ensure it sets. - setTimeout(() => { - testConnectionController - .addNewConnectionStringAndConnect(TEST_DATABASE_URI) - .then(() => { - const workspaceStoreConnections = mockStorageController.get( - StorageVariables.WORKSPACE_SAVED_CONNECTIONS, - StorageScope.WORKSPACE - ); - assert( - Object.keys(workspaceStoreConnections).length === 1, - `Expected workspace store connections to have 1 connection found ${ - Object.keys(workspaceStoreConnections).length - }` - ); - const connectionId = - testConnectionController.getActiveConnectionId() || 'zz'; - const mockInputBoxResolves = sinon.stub(); - mockInputBoxResolves.onCall(0).resolves('new connection name'); - sinon.replace( - vscode.window, - 'showInputBox', - mockInputBoxResolves - ); - testConnectionController - .renameConnection(connectionId) - .then((renameSuccess) => { - assert(renameSuccess); - - testConnectionController.disconnect().then(() => { - testConnectionController.clearAllConnections(); - assert( - testConnectionController.getSavedConnections() - .length === 0, - 'Expected no saved connection.' - ); - - // Activate (which will load the past connection). - testConnectionController - .loadSavedConnections() - .then(() => { - assert( - testConnectionController.getSavedConnections() - .length === 1, - `Expected 1 connection config, found ${ - testConnectionController.getSavedConnections() - .length - }.` - ); - const id = testConnectionController.getSavedConnections()[0] - .id; - const name = - testConnectionController._connections[id || 'x'] - .name; - assert( - name === 'new connection name', - `Expected the active connection name to be 'new connection name', found '${name}'.` - ); - }) - .then(done, done); - }, done); - }, done); - }, done); - }, 50); - }, done); - }); + ); + await testConnectionController.addNewConnectionStringAndConnect( + TEST_DATABASE_URI + ); + + const workspaceStoreConnections = mockStorageController.get( + StorageVariables.WORKSPACE_SAVED_CONNECTIONS, + StorageScope.WORKSPACE + ); + + assert( + Object.keys(workspaceStoreConnections).length === 1, + `Expected workspace store connections to have 1 connection found ${ + Object.keys(workspaceStoreConnections).length + }` + ); + + const connectionId = + testConnectionController.getActiveConnectionId() || 'zz'; + const mockInputBoxResolves = sinon.stub(); + + mockInputBoxResolves.onCall(0).resolves('new connection name'); + sinon.replace(vscode.window, 'showInputBox', mockInputBoxResolves); + + const renameSuccess = await testConnectionController.renameConnection( + connectionId + ); + + assert(renameSuccess); + + await testConnectionController.disconnect(); + + testConnectionController.clearAllConnections(); + + assert( + testConnectionController.getSavedConnections().length === 0, + 'Expected no saved connection.' + ); + + // Activate (which will load the past connection). + await testConnectionController.loadSavedConnections(); + + assert( + testConnectionController.getSavedConnections().length === 1, + `Expected 1 connection config, found ${ + testConnectionController.getSavedConnections().length + }.` + ); + + const id = testConnectionController.getSavedConnections()[0].id; + const name = testConnectionController._connections[id || 'x'].name; + + assert( + name === 'new connection name', + `Expected the active connection name to be 'new connection name', found '${name}'.` + ); + } catch (error) { + assert(false); + } }); }); diff --git a/src/test/suite/editorTestHelper.ts b/src/test/suite/editorTestHelper.ts new file mode 100644 index 000000000..33c2504f7 --- /dev/null +++ b/src/test/suite/editorTestHelper.ts @@ -0,0 +1,26 @@ +import * as vscode from 'vscode'; +import * as path from 'path'; + +// Get a test document from fixtures. +export const getDocUri = (docName: string): vscode.Uri => { + const docPath = path.resolve(__dirname, '../../../src/test/fixture', docName); + + return vscode.Uri.file(docPath); +}; + +// Open the MongoDB playground file, make a changes, and save to disc. +export async function loadAndSavePlayground(docUri: vscode.Uri): Promise { + try { + return vscode.workspace.openTextDocument(docUri).then((doc) => + vscode.window.showTextDocument(doc, 1, false).then((editor) => + editor + .edit((editBuilder) => { + editBuilder.replace(new vscode.Range(0, 0, 1, 0), 'show dbs'); + }) + .then(() => doc.save()) + ) + ); + } catch (error) { + return Promise.reject(error); + } +} diff --git a/src/test/suite/editors/collectionDocumentsCodeLensProvider.test.ts b/src/test/suite/editors/collectionDocumentsCodeLensProvider.test.ts index dc8a49d5a..4451a7fe5 100644 --- a/src/test/suite/editors/collectionDocumentsCodeLensProvider.test.ts +++ b/src/test/suite/editors/collectionDocumentsCodeLensProvider.test.ts @@ -8,7 +8,9 @@ import { mockVSCodeTextDocument } from '../stubs'; suite('Collection Documents Provider Test Suite', () => { test('expected provideCodeLenses to return a code lens with positions at the end of the document', () => { const testQueryStore = new CollectionDocumentsOperationsStore(); - const testCodeLensProvider = new CollectionDocumentsCodeLensProvider(testQueryStore); + const testCodeLensProvider = new CollectionDocumentsCodeLensProvider( + testQueryStore + ); const operationId = testQueryStore.createNewOperation(); @@ -27,14 +29,22 @@ suite('Collection Documents Provider Test Suite', () => { assert(codeLens.length === 1); const range = codeLens[0].range; const expectedStartLine = 49; - assert(range.start.line === expectedStartLine, `Expected a codeLens position to be at line ${expectedStartLine}, found ${range.start.line}`); + assert( + range.start.line === expectedStartLine, + `Expected a codeLens position to be at line ${expectedStartLine}, found ${range.start.line}` + ); const expectedEnd = 50; - assert(range.end.line === expectedEnd, `Expected a codeLens position to be at line ${expectedEnd}, found ${range.end.line}`); + assert( + range.end.line === expectedEnd, + `Expected a codeLens position to be at line ${expectedEnd}, found ${range.end.line}` + ); }); test('expected provideCodeLenses to not return a code lens when there are no more documents to show', () => { const testQueryStore = new CollectionDocumentsOperationsStore(); - const testCodeLensProvider = new CollectionDocumentsCodeLensProvider(testQueryStore); + const testCodeLensProvider = new CollectionDocumentsCodeLensProvider( + testQueryStore + ); const operationId = testQueryStore.createNewOperation(); testQueryStore.operations[operationId].hasMoreDocumentsToShow = false; diff --git a/src/test/suite/editors/collectionDocumentsOperationsStore.test.ts b/src/test/suite/editors/collectionDocumentsOperationsStore.test.ts index ede45af3b..99740b1b4 100644 --- a/src/test/suite/editors/collectionDocumentsOperationsStore.test.ts +++ b/src/test/suite/editors/collectionDocumentsOperationsStore.test.ts @@ -9,15 +9,24 @@ suite('Collection Documents Operations Store Test Suite', () => { const opId = testOpsStore.createNewOperation(); - assert(!!testOpsStore.operations[opId], `Expected operation with id ${opId}`); - assert(Object.keys(testOpsStore.operations).length === 1, `Expected an operation to be in the operations store, found ${testOpsStore.operations.length}`); + assert( + !!testOpsStore.operations[opId], + `Expected operation with id ${opId}` + ); + assert( + Object.keys(testOpsStore.operations).length === 1, + `Expected an operation to be in the operations store, found ${testOpsStore.operations.length}` + ); const operation = testOpsStore.operations[opId]; - const expectedLimit = vscode.workspace.getConfiguration( - 'mdb' - ).get('defaultLimit'); + const expectedLimit = vscode.workspace + .getConfiguration('mdb') + .get('defaultLimit'); - assert(operation.currentLimit === expectedLimit, `Expected limit to be ${expectedLimit} found ${operation.currentLimit}`); + assert( + operation.currentLimit === expectedLimit, + `Expected limit to be ${expectedLimit} found ${operation.currentLimit}` + ); }); test('expected increaseOperationDocumentLimit createNewOperation to increase limit by config setting', () => { @@ -26,14 +35,20 @@ suite('Collection Documents Operations Store Test Suite', () => { const opId = testOpsStore.createNewOperation(); const operation = testOpsStore.operations[opId]; - const expectedLimit = Number(vscode.workspace.getConfiguration( - 'mdb' - ).get('defaultLimit')); + const expectedLimit = Number( + vscode.workspace.getConfiguration('mdb').get('defaultLimit') + ); - assert(operation.currentLimit === expectedLimit, `Expected limit to be ${expectedLimit} found ${operation.currentLimit}`); + assert( + operation.currentLimit === expectedLimit, + `Expected limit to be ${expectedLimit} found ${operation.currentLimit}` + ); testOpsStore.increaseOperationDocumentLimit(opId); - assert(operation.currentLimit === expectedLimit * 2, `Expected limit to be ${expectedLimit} found ${operation.currentLimit}`); + assert( + operation.currentLimit === expectedLimit * 2, + `Expected limit to be ${expectedLimit} found ${operation.currentLimit}` + ); }); }); diff --git a/src/test/suite/editors/playgroundController.test.ts b/src/test/suite/editors/playgroundController.test.ts index cee37fdca..a7dfe968d 100644 --- a/src/test/suite/editors/playgroundController.test.ts +++ b/src/test/suite/editors/playgroundController.test.ts @@ -87,9 +87,11 @@ suite('Playground Controller Test Suite', () => { fakeShowErrorMessage.resolves(errorMessage); - await testPlaygroundController.runAllPlaygroundBlocks(); - - sinon.assert.calledWith(fakeShowErrorMessage, errorMessage); + try { + await testPlaygroundController.runAllPlaygroundBlocks(); + } catch (error) { + sinon.assert.calledWith(fakeShowErrorMessage, errorMessage); + } }); }); @@ -122,141 +124,65 @@ suite('Playground Controller Test Suite', () => { }); test('show a confirmation message if mdb.confirmRunAll is true', async () => { + let result: any; + fakeShowInformationMessage.resolves('Yes'); - const result = await testPlaygroundController.runAllPlaygroundBlocks(); + try { + result = await testPlaygroundController.runAllPlaygroundBlocks(); + } catch (error) { + // No action. + } expect(result).to.be.true; }); test('do not show a confirmation message if mdb.confirmRunAll is false', async () => { + let result: any; + await vscode.workspace .getConfiguration('mdb') .update('confirmRunAll', false); - const result = await testPlaygroundController.runAllPlaygroundBlocks(); + try { + result = await testPlaygroundController.runAllPlaygroundBlocks(); + } catch (error) { + // No action. + } expect(result).to.be.true; }); test('do not run a playground if user selected No in the confirmation message', async () => { + let result: any; + await vscode.workspace .getConfiguration('mdb') .update('confirmRunAll', true); fakeShowInformationMessage.resolves('No'); - const result = await testPlaygroundController.runAllPlaygroundBlocks(); + try { + result = await testPlaygroundController.runAllPlaygroundBlocks(); + } catch (error) { + // No action. + } expect(result).to.be.false; }); test('close cancelation modal when a playground is canceled', async () => { - sinon.replace(testPlaygroundController, 'evaluate', sinon.fake.rejects()); - - const result = await testPlaygroundController.evaluateWithCancelModal(); - - expect(result).to.be.equal(null); - }); - }); - - suite('prepare telemetry types', () => { - test('convert AggregationCursor shellApiType to aggregation telemetry type', () => { - const res = { shellApiType: 'AggregationCursor' }; - const type = testPlaygroundController.prepareTelemetry(res); - - expect(type).to.deep.equal({ type: 'aggregation' }); - }); - - test('convert BulkWriteResult shellApiType to other telemetry type', () => { - const res = { shellApiType: 'BulkWriteResult' }; - const type = testPlaygroundController.prepareTelemetry(res); - - expect(type).to.deep.equal({ type: 'other' }); - }); - - test('convert Collection shellApiType to other telemetry type', () => { - const res = { shellApiType: 'Collection' }; - const type = testPlaygroundController.prepareTelemetry(res); - - expect(type).to.deep.equal({ type: 'other' }); - }); - - test('convert Cursor shellApiType to other telemetry type', () => { - const res = { shellApiType: 'Cursor' }; - const type = testPlaygroundController.prepareTelemetry(res); - - expect(type).to.deep.equal({ type: 'query' }); - }); - - test('convert Database shellApiType to other telemetry type', () => { - const res = { shellApiType: 'Database' }; - const type = testPlaygroundController.prepareTelemetry(res); - - expect(type).to.deep.equal({ type: 'other' }); - }); + let result: any; - test('convert DeleteResult shellApiType to other telemetry type', () => { - const res = { shellApiType: 'DeleteResult' }; - const type = testPlaygroundController.prepareTelemetry(res); - - expect(type).to.deep.equal({ type: 'delete' }); - }); - - test('convert InsertManyResult shellApiType to other telemetry type', () => { - const res = { shellApiType: 'InsertManyResult' }; - const type = testPlaygroundController.prepareTelemetry(res); - - expect(type).to.deep.equal({ type: 'insert' }); - }); - - test('convert InsertOneResult shellApiType to other telemetry type', () => { - const res = { shellApiType: 'InsertOneResult' }; - const type = testPlaygroundController.prepareTelemetry(res); - - expect(type).to.deep.equal({ type: 'insert' }); - }); - - test('convert ReplicaSet shellApiType to other telemetry type', () => { - const res = { shellApiType: 'ReplicaSet' }; - const type = testPlaygroundController.prepareTelemetry(res); - - expect(type).to.deep.equal({ type: 'other' }); - }); - - test('convert Shard shellApiType to other telemetry type', () => { - const res = { shellApiType: 'Shard' }; - const type = testPlaygroundController.prepareTelemetry(res); - - expect(type).to.deep.equal({ type: 'other' }); - }); - - test('convert ShellApi shellApiType to other telemetry type', () => { - const res = { shellApiType: 'ShellApi' }; - const type = testPlaygroundController.prepareTelemetry(res); - - expect(type).to.deep.equal({ type: 'other' }); - }); - - test('convert UpdateResult shellApiType to other telemetry type', () => { - const res = { shellApiType: 'UpdateResult' }; - const type = testPlaygroundController.prepareTelemetry(res); - - expect(type).to.deep.equal({ type: 'update' }); - }); - - test('convert UpdateResult shellApiType to other telemetry type', () => { - const res = { shellApiType: 'UpdateResult' }; - const type = testPlaygroundController.prepareTelemetry(res); - - expect(type).to.deep.equal({ type: 'update' }); - }); + sinon.replace(testPlaygroundController, 'evaluate', sinon.fake.rejects()); - test('return other telemetry type if evaluation returns a string', () => { - const res = '2'; - const type = testPlaygroundController.prepareTelemetry(res); + try { + result = await testPlaygroundController.evaluateWithCancelModal(); + } catch (error) { + // No action. + } - expect(type).to.deep.equal({ type: 'other' }); + expect(result).to.be.null; }); }); }); diff --git a/src/test/suite/explorer/explorerController.test.ts b/src/test/suite/explorer/explorerController.test.ts index ee8d17bc4..e3ae451cf 100644 --- a/src/test/suite/explorer/explorerController.test.ts +++ b/src/test/suite/explorer/explorerController.test.ts @@ -3,19 +3,19 @@ import * as vscode from 'vscode'; import { beforeEach, afterEach } from 'mocha'; import Connection = require('mongodb-connection-model/lib/model'); import * as sinon from 'sinon'; - import { DefaultSavingLocations, StorageScope } from '../../../storage/storageController'; - import { TEST_DATABASE_URI } from '../dbTestHelper'; import { mdbTestExtension } from '../stubbableMdbExtension'; const testDatabaseURI2WithTimeout = 'mongodb://shouldfail?connectTimeoutMS=500&serverSelectionTimeoutMS=500'; -suite('Explorer Controller Test Suite', () => { +suite('Explorer Controller Test Suite', function () { + this.timeout(5000); + beforeEach(async () => { // Don't save connections on default. await vscode.workspace @@ -35,32 +35,35 @@ suite('Explorer Controller Test Suite', () => { DefaultSavingLocations.Workspace ); // Reset our connections. + await mdbTestExtension.testExtensionController._connectionController.disconnect(); mdbTestExtension.testExtensionController._connectionController.clearAllConnections(); sinon.restore(); }); - test('should have a connections root', (done) => { + test('should have a connections root', async () => { const testExplorerController = mdbTestExtension.testExtensionController._explorerController; const treeController = testExplorerController.getTreeController(); assert(!!treeController, 'Tree controller should not be undefined'); - treeController - .getChildren() - .then((treeControllerChildren) => { - assert( - treeControllerChildren.length === 1, - `Tree controller should have 1 child, found ${treeControllerChildren.length}` - ); - assert( - treeControllerChildren[0].label === 'Connections', - 'Tree controller should have a "Connections" child' - ); - }) - .then(done, done); + + try { + const treeControllerChildren = await treeController.getChildren(); + + assert( + treeControllerChildren.length === 1, + `Tree controller should have 1 child, found ${treeControllerChildren.length}` + ); + assert( + treeControllerChildren[0].label === 'Connections', + 'Tree controller should have a "Connections" child' + ); + } catch (error) { + assert(false); + } }); - test('it updates the connections to account for a change in the connection controller', (done) => { + test('it updates the connections to account for a change in the connection controller', async () => { const testConnectionController = mdbTestExtension.testExtensionController._connectionController; const testExplorerController = @@ -68,257 +71,264 @@ suite('Explorer Controller Test Suite', () => { const treeController = testExplorerController.getTreeController(); const mockConnectionId = 'testConnectionId'; - testConnectionController._connections = { - testConnectionId: { - id: 'testConnectionId', - connectionModel: new Connection(), - name: 'testConnectionName', - driverUrl: 'url', - storageLocation: StorageScope.NONE - } - }; - testConnectionController.setConnnectingConnectionId(mockConnectionId); - testConnectionController.setConnnecting(true); - - treeController.getChildren().then((treeControllerChildren) => { - treeControllerChildren[0] - .getChildren() - .then((connectionsItems) => { - assert( - connectionsItems.length === 1, - `Expected there to be 1 connection tree item, found ${connectionsItems.length}` - ); - assert( - connectionsItems[0].label === 'testConnectionName', - 'There should be a connection tree item with the label "testConnectionName"' - ); - testExplorerController.deactivate(); - }) - .then(done, done); - }); + try { + testConnectionController._connections = { + testConnectionId: { + id: 'testConnectionId', + connectionModel: new Connection(), + name: 'testConnectionName', + driverUrl: 'url', + storageLocation: StorageScope.NONE + } + }; + testConnectionController.setConnnectingConnectionId(mockConnectionId); + testConnectionController.setConnnecting(true); + + const treeControllerChildren = await treeController.getChildren(); + + assert( + treeControllerChildren.length === 1, + `Tree controller should have 1 child, found ${treeControllerChildren.length}` + ); + assert( + treeControllerChildren[0].label === 'Connections', + 'Tree controller should have a "Connections" child' + ); + + const connectionsItems = await treeControllerChildren[0].getChildren(); + + assert( + connectionsItems.length === 1, + `Expected there to be 1 connection tree item, found ${connectionsItems.length}` + ); + assert( + connectionsItems[0].label === 'testConnectionName', + 'There should be a connection tree item with the label "testConnectionName"' + ); + + testExplorerController.deactivate(); + } catch (error) { + assert(false); + } }); - test('when a connection is added and connected it is added to the tree and expanded', (done) => { + test('when a connection is added and connected it is added to the tree and expanded', async () => { const testConnectionController = mdbTestExtension.testExtensionController._connectionController; const testExplorerController = mdbTestExtension.testExtensionController._explorerController; const treeController = testExplorerController.getTreeController(); - testConnectionController - .addNewConnectionStringAndConnect(TEST_DATABASE_URI) - .then((succesfullyConnected) => { - assert( - succesfullyConnected === true, - 'Expected a successful connection response.' - ); - assert( - Object.keys(testConnectionController._connections).length === 1, - 'Expected there to be 1 connection in the connection list.' - ); - const activeId = testConnectionController.getActiveConnectionId(); - assert( - activeId === Object.keys(testConnectionController._connections)[0], - `Expected active connection to be '${ - Object.keys(testConnectionController._connections)[0] - }' found ${activeId}` - ); - - treeController.getChildren().then((treeControllerChildren) => { - treeControllerChildren[0] - .getChildren() - .then((connectionsItems) => { - assert( - connectionsItems.length === 1, - `Expected there be 1 connection tree item, found ${connectionsItems.length}` - ); - assert( - connectionsItems[0].label === 'localhost:27018', - 'There should be a connection tree item with the label "localhost:27018"' - ); - assert( - connectionsItems[0].description === 'connected', - 'There should be a connection tree item with the description "connected"' - ); - assert( - connectionsItems[0].isExpanded, - 'Expected the connection tree item to be expanded' - ); - - testExplorerController.deactivate(); - }) - .then(done, done); - }); - }); + try { + const succesfullyConnected = await testConnectionController.addNewConnectionStringAndConnect( + TEST_DATABASE_URI + ); + + assert( + succesfullyConnected === true, + 'Expected a successful connection response.' + ); + assert( + Object.keys(testConnectionController._connections).length === 1, + 'Expected there to be 1 connection in the connection list.' + ); + + const activeId = testConnectionController.getActiveConnectionId(); + + assert( + activeId === Object.keys(testConnectionController._connections)[0], + `Expected active connection to be '${ + Object.keys(testConnectionController._connections)[0] + }' found ${activeId}` + ); + + const treeControllerChildren = await treeController.getChildren(); + const connectionsItems = await treeControllerChildren[0].getChildren(); + + assert( + connectionsItems.length === 1, + `Expected there be 1 connection tree item, found ${connectionsItems.length}` + ); + assert( + connectionsItems[0].label === 'localhost:27018', + 'There should be a connection tree item with the label "localhost:27018"' + ); + assert( + connectionsItems[0].description === 'connected', + 'There should be a connection tree item with the description "connected"' + ); + assert( + connectionsItems[0].isExpanded, + 'Expected the connection tree item to be expanded' + ); + } catch (error) { + assert(false); + } }); - test('only the active connection is displayed as connected in the tree', (done) => { + test('only the active connection is displayed as connected in the tree', async () => { const testConnectionController = mdbTestExtension.testExtensionController._connectionController; const testExplorerController = mdbTestExtension.testExtensionController._explorerController; const treeController = testExplorerController.getTreeController(); - testConnectionController - .addNewConnectionStringAndConnect(TEST_DATABASE_URI) - .then((succesfullyConnected) => { - assert( - succesfullyConnected === true, - 'Expected a successful connection response.' - ); - assert( - Object.keys(testConnectionController._connections).length === 1, - 'Expected there to be 1 connection in the connection list.' - ); - const connectionId = - testConnectionController.getActiveConnectionId() || ''; - const connectionName = - testConnectionController._connections[connectionId].name; - assert( - connectionName === 'localhost:27018', - `Expected active connection name to be 'localhost:27018' found ${connectionName}` - ); - - // This will timeout in 500ms, which is enough time for us to just check. - testConnectionController - .addNewConnectionStringAndConnect(testDatabaseURI2WithTimeout) - .then( - () => {}, - () => {} /* Silent fail (should fail) */ - ); - - setTimeout(() => { - treeController.getChildren().then((treeControllerChildren) => { - treeControllerChildren[0] - .getChildren() - .then((connectionsItems) => { - assert( - connectionsItems.length === 2, - `Expected there be 2 connection tree item, found ${connectionsItems.length}` - ); - assert( - connectionsItems[0].label === 'localhost:27018', - `First connection tree item should have label "localhost:27018" found ${connectionsItems[0].label}` - ); - assert( - connectionsItems[0].description === '', - `Expected the first connection to have no description, found ${connectionsItems[0].description}.` - ); - assert( - connectionsItems[0].isExpanded === false, - 'Expected the first connection tree item to not be expanded' - ); - assert( - connectionsItems[1].label === 'shouldfail:27017', - 'Second connection tree item should have label "shouldfail:27017"' - ); - assert( - connectionsItems[1].description === 'connecting...', - 'The second connection should have a connecting description.' - ); - - testExplorerController.deactivate(); - }) - .then(done, done); - }); - }, 500); - }); + try { + const succesfullyConnected = await testConnectionController.addNewConnectionStringAndConnect( + TEST_DATABASE_URI + ); + + assert( + succesfullyConnected === true, + 'Expected a successful connection response.' + ); + assert( + Object.keys(testConnectionController._connections).length === 1, + 'Expected there to be 1 connection in the connection list.' + ); + + const connectionId = + testConnectionController.getActiveConnectionId() || ''; + const connectionName = + testConnectionController._connections[connectionId].name; + + assert( + connectionName === 'localhost:27018', + `Expected active connection name to be 'localhost:27018' found ${connectionName}` + ); + } catch (error) { + assert(false); + } + + try { + await testConnectionController.addNewConnectionStringAndConnect( + testDatabaseURI2WithTimeout + ); + } catch (error) { + /* Silent fail (should fail) */ + } + + try { + const treeControllerChildren = await treeController.getChildren(); + const connectionsItems = await treeControllerChildren[0].getChildren(); + + assert( + connectionsItems.length === 2, + `Expected there be 2 connection tree item, found ${connectionsItems.length}` + ); + assert( + connectionsItems[0].label === 'localhost:27018', + `First connection tree item should have label "localhost:27018" found ${connectionsItems[0].label}` + ); + assert( + connectionsItems[0].isExpanded === false, + 'Expected the first connection tree item to not be expanded' + ); + assert( + connectionsItems[1].label === 'shouldfail:27017', + 'Second connection tree item should have label "shouldfail:27017"' + ); + + testExplorerController.deactivate(); + } catch (error) { + assert(false); + } }); - test('shows the databases of connected connection in tree', (done) => { + test('shows the databases of connected connection in tree', async () => { const testConnectionController = mdbTestExtension.testExtensionController._connectionController; const testExplorerController = mdbTestExtension.testExtensionController._explorerController; const treeController = testExplorerController.getTreeController(); - testConnectionController - .addNewConnectionStringAndConnect(TEST_DATABASE_URI) - .then(() => { - treeController.getChildren().then((treeControllerChildren) => { - treeControllerChildren[0].getChildren().then((connectionsItems) => { - // Expand the connection. - treeControllerChildren[0].onDidExpand(); - - connectionsItems[0] - .getChildren() - .then((databaseItems) => { - assert( - databaseItems.length >= 3, - `Expected there be 3 or more database tree items, found ${databaseItems.length}` - ); - assert( - databaseItems[0].label === 'admin', - `First database tree item should have label "admin" found ${connectionsItems[0].label}.` - ); - - testExplorerController.deactivate(); - }) - .then(done, done); - }); - }); - }); + try { + await testConnectionController.addNewConnectionStringAndConnect( + TEST_DATABASE_URI + ); + + const treeControllerChildren = await treeController.getChildren(); + const connectionsItems = await treeControllerChildren[0].getChildren(); + + // Expand the connection. + treeControllerChildren[0].onDidExpand(); + + const databaseItems = await connectionsItems[0].getChildren(); + + assert( + databaseItems.length >= 3, + `Expected there be 3 or more database tree items, found ${databaseItems.length}` + ); + assert( + databaseItems[0].label === 'admin', + `First database tree item should have label "admin" found ${connectionsItems[0].label}.` + ); + + testExplorerController.deactivate(); + } catch (error) { + assert(false); + } }); - test('caches the expanded state of databases in the tree when a connection is expanded or collapsed', (done) => { + test('caches the expanded state of databases in the tree when a connection is expanded or collapsed', async () => { const testConnectionController = mdbTestExtension.testExtensionController._connectionController; const testExplorerController = mdbTestExtension.testExtensionController._explorerController; const treeController = testExplorerController.getTreeController(); - testConnectionController - .addNewConnectionStringAndConnect(TEST_DATABASE_URI) - .then(() => { - treeController.getChildren().then((rootTreeItem) => { - const connectionsTreeItem = rootTreeItem[0]; - connectionsTreeItem.getChildren().then((connectionsItems) => { - // Expand the connection. - const testConnectionTreeItem = connectionsItems[0]; - testConnectionTreeItem.onDidExpand().then(() => { - testConnectionTreeItem.getChildren().then((databaseItems) => { - assert( - databaseItems[1].isExpanded === false, - 'Expected database tree item not to be expanded on default.' - ); - - // Expand the first database item. - databaseItems[1].onDidExpand().then(() => { - assert( - databaseItems[1].isExpanded === true, - 'Expected database tree item be expanded.' - ); - - // Collapse the connection. - testConnectionTreeItem.onDidCollapse(); - - testConnectionTreeItem - .getChildren() - .then((databaseTreeItems) => { - assert( - databaseTreeItems.length === 0, - `Expected the connection tree to return no children when collapsed, found ${databaseTreeItems.length}` - ); - - testConnectionTreeItem.onDidExpand(); - testConnectionTreeItem - .getChildren() - .then((newDatabaseItems) => { - assert( - newDatabaseItems[1].isExpanded === true, - 'Expected database tree to be expanded from cache.' - ); - - testExplorerController.deactivate(); - }) - .then(done, done); - }); - }); - }); - }); - }); - }); - }); + try { + await testConnectionController.addNewConnectionStringAndConnect( + TEST_DATABASE_URI + ); + + const rootTreeItem = await treeController.getChildren(); + const connectionsTreeItem = rootTreeItem[0]; + const connectionsItems = await connectionsTreeItem.getChildren(); + + // Expand the connection. + const testConnectionTreeItem = connectionsItems[0]; + + await testConnectionTreeItem.onDidExpand(); + + const databaseItems = await testConnectionTreeItem.getChildren(); + + assert( + databaseItems[1].isExpanded === false, + 'Expected database tree item not to be expanded on default.' + ); + + // Expand the first database item. + await databaseItems[1].onDidExpand(); + + assert( + databaseItems[1].isExpanded === true, + 'Expected database tree item be expanded.' + ); + + // Collapse the connection. + testConnectionTreeItem.onDidCollapse(); + + const databaseTreeItems = await testConnectionTreeItem.getChildren(); + + assert( + databaseTreeItems.length === 0, + `Expected the connection tree to return no children when collapsed, found ${databaseTreeItems.length}` + ); + + testConnectionTreeItem.onDidExpand(); + + const newDatabaseItems = await testConnectionTreeItem.getChildren(); + + assert( + newDatabaseItems[1].isExpanded === true, + 'Expected database tree to be expanded from cache.' + ); + + testExplorerController.deactivate(); + } catch (error) { + assert(false); + } }); test('tree view should be not created by default (shows welcome view)', () => { @@ -328,13 +338,16 @@ suite('Explorer Controller Test Suite', () => { assert(testExplorerController.getTreeView() === undefined); }); - test('tree view should call create tree view after a "CONNECTIONS_DID_CHANGE" event', (done) => { + test('tree view should call create tree view after a "CONNECTIONS_DID_CHANGE" event', async () => { + const testConnectionController = + mdbTestExtension.testExtensionController._connectionController; const testExplorerController = mdbTestExtension.testExtensionController._explorerController; testExplorerController.activateTreeView(); const treeControllerStub = sinon.stub().returns(); + sinon.replace( testExplorerController.getTreeController(), 'activateTreeViewEventHandlers', @@ -342,14 +355,18 @@ suite('Explorer Controller Test Suite', () => { ); const vscodeCreateTreeViewStub = sinon.stub().returns(''); + sinon.replace(vscode.window, 'createTreeView', vscodeCreateTreeViewStub); - mdbTestExtension.testExtensionController._connectionController - .addNewConnectionStringAndConnect(TEST_DATABASE_URI) - .then(() => { - mdbTestExtension.testExtensionController._connectionController.disconnect(); - assert(vscodeCreateTreeViewStub.called); - }) - .then(done); + try { + await testConnectionController.addNewConnectionStringAndConnect( + TEST_DATABASE_URI + ); + await testConnectionController.disconnect(); + + assert(vscodeCreateTreeViewStub.called); + } catch (error) { + assert(false); + } }); }); diff --git a/src/test/suite/extension.test.ts b/src/test/suite/extension.test.ts index db1a5f4b2..d95bf934d 100644 --- a/src/test/suite/extension.test.ts +++ b/src/test/suite/extension.test.ts @@ -42,118 +42,112 @@ suite('Extension Test Suite', () => { sandbox.restore(); }); - test('commands are registered in vscode', (done) => { - vscode.commands - .getCommands() - .then((registeredCommands) => { - const expectedCommands = [ - // General / connection commands. - 'mdb.connect', - 'mdb.connectWithURI', - 'mdb.disconnect', - 'mdb.removeConnection', - 'mdb.openMongoDBShell', - 'mdb.createPlayground', - 'mdb.createNewPlaygroundFromViewAction', - - // Tree view commands. - 'mdb.addConnection', - 'mdb.addConnectionWithURI', - 'mdb.copyConnectionString', - 'mdb.treeItemRemoveConnection', - 'mdb.addDatabase', - 'mdb.refreshConnection', - 'mdb.copyDatabaseName', - 'mdb.refreshDatabase', - 'mdb.addCollection', - 'mdb.viewCollectionDocuments', - 'mdb.copyCollectionName', - 'mdb.refreshCollection', - 'mdb.refreshSchema', - - // Editor commands. - 'mdb.codeLens.showMoreDocumentsClicked' - ]; - - for (let i = 0; i < expectedCommands.length; i++) { - try { - assert.notEqual( - registeredCommands.indexOf(expectedCommands[i]), - -1, - `command ${expectedCommands[i]} not registered and was expected` - ); - } catch (e) { - done(e); - return; - } - } - }) - .then(() => done(), done); + test('commands are registered in vscode', async () => { + const registeredCommands = await vscode.commands.getCommands(); + + const expectedCommands = [ + // General / connection commands. + 'mdb.connect', + 'mdb.connectWithURI', + 'mdb.disconnect', + 'mdb.removeConnection', + 'mdb.openMongoDBShell', + 'mdb.createPlayground', + 'mdb.createNewPlaygroundFromViewAction', + + // Tree view commands. + 'mdb.addConnection', + 'mdb.addConnectionWithURI', + 'mdb.copyConnectionString', + 'mdb.treeItemRemoveConnection', + 'mdb.addDatabase', + 'mdb.refreshConnection', + 'mdb.copyDatabaseName', + 'mdb.refreshDatabase', + 'mdb.addCollection', + 'mdb.viewCollectionDocuments', + 'mdb.copyCollectionName', + 'mdb.refreshCollection', + 'mdb.refreshSchema', + + // Editor commands. + 'mdb.codeLens.showMoreDocumentsClicked' + ]; + + for (let i = 0; i < expectedCommands.length; i++) { + try { + assert.notEqual( + registeredCommands.indexOf(expectedCommands[i]), + -1, + `command ${expectedCommands[i]} not registered and was expected` + ); + } catch (e) { + assert(false); + return; + } + } }); - test('openMongoDBShell should display an error message when not connected', (done) => { + test('openMongoDBShell should display an error message when not connected', async () => { const errorMessage = 'You need to be connected before launching the MongoDB Shell.'; fakeShowErrorMessage.resolves(errorMessage); - mockMDBExtension - .openMongoDBShell() - .then((didOpenShell) => { - assert(didOpenShell === false); - sinon.assert.calledWith(fakeShowErrorMessage, errorMessage); - }) - .then(done, done); + try { + await mockMDBExtension.openMongoDBShell(); + } catch (error) { + sinon.assert.calledWith(fakeShowErrorMessage, errorMessage); + } }); - test('openMongoDBShell should open a terminal with the active connection driver url', (done) => { - testConnectionController - .addNewConnectionStringAndConnect(TEST_DATABASE_URI) - .then((succesfullyConnected) => { - assert( - succesfullyConnected === true, - 'Expected a successful connection response.' - ); + test('openMongoDBShell should open a terminal with the active connection driver url', async () => { + try { + const succesfullyConnected = await testConnectionController.addNewConnectionStringAndConnect( + TEST_DATABASE_URI + ); + + assert( + succesfullyConnected === true, + 'Expected a successful connection response.' + ); + + await mockMDBExtension.openMongoDBShell(); + + const spyActiveConnectionDriverUrl = sinon.spy( + testConnectionController, + 'getActiveConnectionDriverUrl' + ); + const createTerminalSpy = sinon.spy(vscode.window, 'createTerminal'); + + const checkResult = async () => { + await testConnectionController.disconnect(); + + try { + assert(spyActiveConnectionDriverUrl.called); + assert(createTerminalSpy.called); + + const expectedUri = + 'mongodb://localhost:27018/?readPreference=primary&ssl=false'; + + assert( + createTerminalSpy.getCall(0).args[0].env.MDB_CONNECTION_STRING === + expectedUri, + `Expected open terminal to set env var 'MDB_CONNECTION_STRING' to ${expectedUri} found ${ + createTerminalSpy.getCall(0).args[0].env.MDB_CONNECTION_STRING + }` + ); + + testConnectionController.clearAllConnections(); + } catch (e) { + assert(false); + return; + } + }; - mockMDBExtension - .openMongoDBShell() - .then(() => { - const spyActiveConnectionDriverUrl = sinon.spy( - testConnectionController, - 'getActiveConnectionDriverUrl' - ); - const createTerminalSpy = sinon.spy( - vscode.window, - 'createTerminal' - ); - - disposables.push( - vscode.window.onDidOpenTerminal(() => { - testConnectionController.disconnect(); - - try { - assert(spyActiveConnectionDriverUrl.called); - assert(createTerminalSpy.called); - const expectedUri = - 'mongodb://localhost:27018/?readPreference=primary&ssl=false'; - assert( - createTerminalSpy.getCall(0).args[0].env - .MDB_CONNECTION_STRING === expectedUri, - `Expected open terminal to set env var 'MDB_CONNECTION_STRING' to ${expectedUri} found ${ - createTerminalSpy.getCall(0).args[0].env - .MDB_CONNECTION_STRING - }` - ); - } catch (e) { - done(e); - return; - } - - done(); - }) - ); - }) - .then(done, done); - }); + disposables.push(vscode.window.onDidOpenTerminal(checkResult)); + } catch (error) { + assert(false); + } }); }); diff --git a/src/test/suite/mdbExtensionController.test.ts b/src/test/suite/mdbExtensionController.test.ts index 7035bf1f4..b89c12ecc 100644 --- a/src/test/suite/mdbExtensionController.test.ts +++ b/src/test/suite/mdbExtensionController.test.ts @@ -799,7 +799,7 @@ suite('MDBExtensionController Test Suite', () => { vscode.commands .executeCommand('mdb.dropCollection', testCollectionTreeItem) - .then((successfullyDropped) => { + .then(async (successfullyDropped) => { assert( successfullyDropped === false, 'Expected the drop collection command handler to return a false succeeded response' @@ -809,6 +809,9 @@ suite('MDBExtensionController Test Suite', () => { fakeVscodeErrorMessage.firstArg === expectedMessage, `Expected "${expectedMessage}" when dropping a collection that doesn't exist, recieved "${fakeVscodeErrorMessage.firstArg}"` ); + + await testConnectionController.disconnect(); + testConnectionController.clearAllConnections(); }) .then(done, done); }); @@ -1009,10 +1012,7 @@ suite('MDBExtensionController Test Suite', () => { ); mdbTestExtension.testExtensionController._connectionController.clearAllConnections(); }) - .then(done, () => { - mdbTestExtension.testExtensionController._connectionController.clearAllConnections(); - done(); - }); + .then(done, done); }); test('mdb.renameConnection updates the name of a connection', (done) => { @@ -1048,10 +1048,7 @@ suite('MDBExtensionController Test Suite', () => { ); mdbTestExtension.testExtensionController._connectionController.clearAllConnections(); }) - .then(done, () => { - mdbTestExtension.testExtensionController._connectionController.clearAllConnections(); - done(); - }); + .then(done, done); }); test('mdb.viewDocument opens an editor with the document using its id', (done) => { diff --git a/src/test/suite/stubs.ts b/src/test/suite/stubs.ts index 1fa13fe14..0b7c12245 100644 --- a/src/test/suite/stubs.ts +++ b/src/test/suite/stubs.ts @@ -176,8 +176,8 @@ class MockLanguageServerController { deactivate(): void {} - executeAll(codeToEvaluate): Promise { - return Promise.resolve(true); + executeAll(codeToEvaluate: string): Promise { + return Promise.resolve('Result'); } connectToServiceProvider(params: { diff --git a/src/test/suite/telemetry/telemetryController.test.ts b/src/test/suite/telemetry/telemetryController.test.ts index 58094a5d8..9b8fd2367 100644 --- a/src/test/suite/telemetry/telemetryController.test.ts +++ b/src/test/suite/telemetry/telemetryController.test.ts @@ -3,15 +3,16 @@ import { StorageController } from '../../../storage'; import { TestExtensionContext } from '../stubs'; import { resolve } from 'path'; import { config } from 'dotenv'; -import TelemetryController, { - TelemetryEventTypes -} from '../../../telemetry/telemetryController'; +import TelemetryController from '../../../telemetry/telemetryController'; import { mdbTestExtension } from '../stubbableMdbExtension'; import { afterEach, beforeEach } from 'mocha'; import { TEST_DATABASE_URI } from './../dbTestHelper'; import Connection = require('mongodb-connection-model/lib/model'); import { StorageScope } from '../../../storage/storageController'; import { ConnectionTypes } from '../../../connectionController'; +import { getDocUri, loadAndSavePlayground } from '../editorTestHelper'; +import DataService = require('mongodb-data-service'); + const sinon = require('sinon'); const chai = require('chai'); const sinonChai = require('sinon-chai'); @@ -24,57 +25,66 @@ config({ path: resolve(__dirname, '../../../../.env') }); suite('Telemetry Controller Test Suite', () => { vscode.window.showInformationMessage('Starting tests...'); + const dataService = new DataService( + new Connection({ + hostname: 'localhost', + port: 27018 + }) + ); const mockExtensionContext = new TestExtensionContext(); const mockStorageController = new StorageController(mockExtensionContext); const testTelemetryController = new TelemetryController( mockStorageController, mockExtensionContext ); - let mockTelemetryTrackMethod: void; - let mockExecuteAllMethod: Promise; - let mockGetCloudInfoFromDataService: Promise; - let mockConnect: Promise; - - beforeEach(async () => { - await vscode.workspace - .getConfiguration('mdb') - .update('sendTelemetry', true); - mockTelemetryTrackMethod = sinon.fake(); - mockExecuteAllMethod = sinon.fake.resolves({ - shellApiType: 'TEST' - }); - mockGetCloudInfoFromDataService = sinon.fake.resolves({ - isPublicCloud: false, - publicCloudName: null - }); - mockConnect = sinon.fake.resolves(true); + let mockTrackNewConnection: Promise; + let mockTrackCommandRun: Promise; + let mockTrackPlaygroundCodeExecuted: Promise; + let mockTrackPlaygroundLoadedMethod: Promise; + let mockTrackPlaygroundSavedMethod: Promise; + + beforeEach(() => { + mockTrackNewConnection = sinon.fake.resolves(true); + mockTrackCommandRun = sinon.fake.resolves(); + mockTrackPlaygroundCodeExecuted = sinon.fake.resolves(); + mockTrackPlaygroundLoadedMethod = sinon.fake.resolves(); + mockTrackPlaygroundSavedMethod = sinon.fake.resolves(); sinon.replace( mdbTestExtension.testExtensionController._telemetryController, - 'track', - mockTelemetryTrackMethod + 'trackNewConnection', + mockTrackNewConnection ); sinon.replace( - mdbTestExtension.testExtensionController._languageServerController, - 'executeAll', - mockExecuteAllMethod + mdbTestExtension.testExtensionController._telemetryController, + 'trackCommandRun', + mockTrackCommandRun ); sinon.replace( - mdbTestExtension.testExtensionController._connectionController, - 'getCloudInfoFromDataService', - mockGetCloudInfoFromDataService + mdbTestExtension.testExtensionController._telemetryController, + 'trackPlaygroundCodeExecuted', + mockTrackPlaygroundCodeExecuted ); sinon.replace( - mdbTestExtension.testExtensionController._connectionController, - 'connect', - mockConnect + mdbTestExtension.testExtensionController._telemetryController, + 'trackPlaygroundLoaded', + mockTrackPlaygroundLoadedMethod + ); + sinon.replace( + mdbTestExtension.testExtensionController._telemetryController, + 'trackPlaygroundSaved', + mockTrackPlaygroundSavedMethod + ); + sinon.replace( + mdbTestExtension.testExtensionController._languageServerController, + 'executeAll', + sinon.fake.resolves({ + shellApiType: 'TEST' + }) ); }); - afterEach(async () => { - await vscode.workspace - .getConfiguration('mdb') - .update('sendTelemetry', false); + afterEach(() => { sinon.restore(); }); @@ -98,95 +108,174 @@ suite('Telemetry Controller Test Suite', () => { .executeCommand('mdb.showActiveConnectionInPlayground', 'Test') .then(() => { sinon.assert.calledWith( - mockTelemetryTrackMethod, - TelemetryEventTypes.EXTENSION_COMMAND_RUN, - { - command: 'mdb.showActiveConnectionInPlayground' - } + mockTrackCommandRun, + 'mdb.showActiveConnectionInPlayground' ); }) .then(done, done); }); - test('track playground code executed event', async () => { - const mockPlaygroundController = - mdbTestExtension.testExtensionController._playgroundController; + test('track new connection event when connecting via connection string', () => { + const mockConnectionController = + mdbTestExtension.testExtensionController._connectionController; - await mockPlaygroundController.evaluate('show dbs'); + mockConnectionController.sendTelemetry( + dataService, + ConnectionTypes.CONNECTION_STRING + ); sinon.assert.calledWith( - mockTelemetryTrackMethod, - TelemetryEventTypes.PLAYGROUND_CODE_EXECUTED, - { - type: 'other' - } + mockTrackNewConnection, + sinon.match.any, + sinon.match(ConnectionTypes.CONNECTION_STRING) ); }); - test('test new connection event when connecting via connection string', (done) => { + test('track new connection event when connecting via connection form', () => { const mockConnectionController = mdbTestExtension.testExtensionController._connectionController; - mockConnectionController - .addNewConnectionStringAndConnect(TEST_DATABASE_URI) - .then(() => { - sinon.assert.calledWith( - mockConnect, - sinon.match.any, - sinon.match.any, - sinon.match(ConnectionTypes.CONNECTION_STRING) - ); - done(); - }); + mockConnectionController.sendTelemetry( + dataService, + ConnectionTypes.CONNECTION_FORM + ); + + sinon.assert.calledWith( + mockTrackNewConnection, + sinon.match.any, + sinon.match(ConnectionTypes.CONNECTION_FORM) + ); }); - test('test new connection event when connecting via connection form', (done) => { + test('track new connection event when connecting via saved connection', () => { const mockConnectionController = mdbTestExtension.testExtensionController._connectionController; - mockConnectionController - .parseNewConnectionAndConnect( - new Connection({ - hostname: 'localhost', - port: 27018 - }) - ) - .then(() => { - sinon.assert.calledWith( - mockConnect, - sinon.match.any, - sinon.match.any, - sinon.match(ConnectionTypes.CONNECTION_FORM) - ); - done(); - }); + mockConnectionController.sendTelemetry( + dataService, + ConnectionTypes.CONNECTION_ID + ); + + sinon.assert.calledWith( + mockTrackNewConnection, + sinon.match.any, + sinon.match(ConnectionTypes.CONNECTION_ID) + ); }); - test('test new connection event when connecting via saved connection', (done) => { - const mockConnectionController = - mdbTestExtension.testExtensionController._connectionController; + test('track playground code executed event', async () => { + const mockPlaygroundController = + mdbTestExtension.testExtensionController._playgroundController; + + await mockPlaygroundController.evaluate('show dbs'); + + sinon.assert.called(mockTrackPlaygroundCodeExecuted); + }); + + test('track playground loaded and saved events', async () => { + await loadAndSavePlayground(getDocUri('test.mongodb')); + + sinon.assert.called(mockTrackPlaygroundLoadedMethod); + sinon.assert.called(mockTrackPlaygroundSavedMethod); + }); + + suite('prepare playground result types', () => { + test('convert AggregationCursor shellApiType to aggregation telemetry type', () => { + const res = { shellApiType: 'AggregationCursor' }; + const type = testTelemetryController.getPlaygroundResultType(res); + + expect(type).to.deep.equal('aggregation'); + }); + + test('convert BulkWriteResult shellApiType to other telemetry type', () => { + const res = { shellApiType: 'BulkWriteResult' }; + const type = testTelemetryController.getPlaygroundResultType(res); + + expect(type).to.deep.equal('other'); + }); + + test('convert Collection shellApiType to other telemetry type', () => { + const res = { shellApiType: 'Collection' }; + const type = testTelemetryController.getPlaygroundResultType(res); + + expect(type).to.deep.equal('other'); + }); + + test('convert Cursor shellApiType to other telemetry type', () => { + const res = { shellApiType: 'Cursor' }; + const type = testTelemetryController.getPlaygroundResultType(res); + + expect(type).to.deep.equal('query'); + }); + + test('convert Database shellApiType to other telemetry type', () => { + const res = { shellApiType: 'Database' }; + const type = testTelemetryController.getPlaygroundResultType(res); + + expect(type).to.deep.equal('other'); + }); + + test('convert DeleteResult shellApiType to other telemetry type', () => { + const res = { shellApiType: 'DeleteResult' }; + const type = testTelemetryController.getPlaygroundResultType(res); + + expect(type).to.deep.equal('delete'); + }); + + test('convert InsertManyResult shellApiType to other telemetry type', () => { + const res = { shellApiType: 'InsertManyResult' }; + const type = testTelemetryController.getPlaygroundResultType(res); + + expect(type).to.deep.equal('insert'); + }); + + test('convert InsertOneResult shellApiType to other telemetry type', () => { + const res = { shellApiType: 'InsertOneResult' }; + const type = testTelemetryController.getPlaygroundResultType(res); + + expect(type).to.deep.equal('insert'); + }); + + test('convert ReplicaSet shellApiType to other telemetry type', () => { + const res = { shellApiType: 'ReplicaSet' }; + const type = testTelemetryController.getPlaygroundResultType(res); + + expect(type).to.deep.equal('other'); + }); + + test('convert Shard shellApiType to other telemetry type', () => { + const res = { shellApiType: 'Shard' }; + const type = testTelemetryController.getPlaygroundResultType(res); + + expect(type).to.deep.equal('other'); + }); + + test('convert ShellApi shellApiType to other telemetry type', () => { + const res = { shellApiType: 'ShellApi' }; + const type = testTelemetryController.getPlaygroundResultType(res); + + expect(type).to.deep.equal('other'); + }); + + test('convert UpdateResult shellApiType to other telemetry type', () => { + const res = { shellApiType: 'UpdateResult' }; + const type = testTelemetryController.getPlaygroundResultType(res); + + expect(type).to.deep.equal('update'); + }); + + test('convert UpdateResult shellApiType to other telemetry type', () => { + const res = { shellApiType: 'UpdateResult' }; + const type = testTelemetryController.getPlaygroundResultType(res); + + expect(type).to.deep.equal('update'); + }); + + test('return other telemetry type if evaluation returns a string', () => { + const res = '2'; + const type = testTelemetryController.getPlaygroundResultType(res); - mockConnectionController._connections = { - '25': { - id: '25', - driverUrl: TEST_DATABASE_URI, - name: 'tester', - connectionModel: new Connection({ - hostname: 'localhost', - port: 27018 - }), - storageLocation: StorageScope.NONE - } - }; - - mockConnectionController.connectWithConnectionId('25').then(() => { - sinon.assert.calledWith( - mockConnect, - sinon.match.any, - sinon.match.any, - sinon.match(ConnectionTypes.CONNECTION_ID) - ); - done(); + expect(type).to.deep.equal('other'); }); }); }); diff --git a/src/views/webviewController.ts b/src/views/webviewController.ts index e660d4de2..de814fceb 100644 --- a/src/views/webviewController.ts +++ b/src/views/webviewController.ts @@ -1,10 +1,6 @@ import * as vscode from 'vscode'; -const path = require('path'); - import ConnectionController from '../connectionController'; -import TelemetryController, { - TelemetryEventTypes -} from '../telemetry/telemetryController'; +import TelemetryController from '../telemetry/telemetryController'; import { MESSAGE_TYPES, ConnectMessage, @@ -14,6 +10,7 @@ import { } from './webview-app/extension-app-message-constants'; import { createLogger } from '../logging'; +const path = require('path'); const log = createLogger('webviewController'); const openFileOptions = { @@ -124,12 +121,9 @@ export default class WebviewController { return; case MESSAGE_TYPES.EXTENSION_LINK_CLICKED: - this._telemetryController.track( - TelemetryEventTypes.EXTENSION_LINK_CLICKED, - { - screen: message.screen, - link_id: message.linkId - } + this._telemetryController.trackLinkClicked( + message.screen, + message.linkId ); return;