mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-01 01:25:38 -05:00
Inital platform relayering (#6385)
* moving test files and inital refactoring * relayer extension host code * fix imports * make insights work * relayer dashboard * relayer notebooks * moveing more code around * formatting * accept angular as browser * fix serializer * add missing files * remove declarations from extensions * fix build errors * more relayering * change urls to relative to help code relayering * remove layering to prep for merge * fix hygiene errors * fix hygiene errors * fix tests
This commit is contained in:
@@ -32,7 +32,7 @@ import { AddAccountAction } from 'sql/platform/accounts/common/accountActions';
|
||||
import { AccountListRenderer, AccountListDelegate } from 'sql/platform/accounts/browser/accountListRenderer';
|
||||
import { AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/platform/accounts/common/eventTypes';
|
||||
import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/telemetryKeys';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import { Modal } from 'sql/workbench/browser/modal/modal';
|
||||
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
|
||||
import { attachModalDialogStyler, attachButtonStyler } from 'sql/platform/theme/common/styler';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/telemetryKeys';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
|
||||
@@ -26,7 +26,7 @@ import { FirewallRuleViewModel } from 'sql/platform/accounts/common/firewallRule
|
||||
import { attachModalDialogStyler, attachButtonStyler } from 'sql/platform/theme/common/styler';
|
||||
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
|
||||
import { IAccountPickerService } from 'sql/platform/accounts/common/accountPicker';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/telemetryKeys';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { AccountDialog } from 'sql/platform/accounts/browser/accountDialog';
|
||||
import { AccountDialogController } from 'sql/platform/accounts/browser/accountDialogController';
|
||||
import { AccountViewModel } from 'sql/platform/accounts/common/accountViewModel';
|
||||
import { TestAccountManagementService } from 'sql/platform/accounts/test/common/testAccountManagementService';
|
||||
import { TestErrorMessageService } from 'sql/platform/errorMessage/test/common/testErrorMessageService';
|
||||
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||
import { AccountListRenderer } from 'sql/platform/accounts/browser/accountListRenderer';
|
||||
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
|
||||
|
||||
// TESTS ///////////////////////////////////////////////////////////////////
|
||||
suite('Account Management Dialog Controller Tests', () => {
|
||||
test('Open Account Dialog - Dialog Doesn\'t Exist', () => {
|
||||
// Setup: Create instance of the controller
|
||||
let instantiationService = createInstantiationService();
|
||||
let controller = new AccountDialogController(instantiationService, undefined);
|
||||
assert.strictEqual(controller.accountDialog, undefined);
|
||||
|
||||
// If: I open the account dialog when one hasn't been opened
|
||||
controller.openAccountDialog();
|
||||
|
||||
// Then:
|
||||
// ... The account dialog should be defined
|
||||
assert.notStrictEqual(controller.accountDialog, undefined);
|
||||
});
|
||||
|
||||
test('Open Account Dialog - Dialog Exists', () => {
|
||||
// Setup: Create instance of the controller with an account dialog already loaded
|
||||
let instantiationService = createInstantiationService();
|
||||
let controller = new AccountDialogController(instantiationService, undefined);
|
||||
controller.openAccountDialog();
|
||||
let accountDialog = controller.accountDialog;
|
||||
|
||||
// If: I open the account dialog when one has already been opened
|
||||
controller.openAccountDialog();
|
||||
|
||||
// Then: It should be the same dialog that already existed
|
||||
assert.equal(controller.accountDialog, accountDialog);
|
||||
});
|
||||
|
||||
test('Add Account Failure - Error Message Shown', () => {
|
||||
// Setup:
|
||||
// ... Create instantiation service that returns mock emitter for account dialog
|
||||
let mockEventEmitter = new Emitter<string>();
|
||||
let instantiationService = createInstantiationService(mockEventEmitter);
|
||||
|
||||
// ... Create a mock instance of the error message service
|
||||
let errorMessageServiceStub = new TestErrorMessageService();
|
||||
let mockErrorMessageService = TypeMoq.Mock.ofInstance(errorMessageServiceStub);
|
||||
mockErrorMessageService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()));
|
||||
|
||||
// ... Create instance of the controller with an opened dialog
|
||||
let controller = new AccountDialogController(instantiationService, mockErrorMessageService.object);
|
||||
controller.openAccountDialog();
|
||||
|
||||
// If: The account dialog reports a failure adding an account
|
||||
mockEventEmitter.fire('Error message');
|
||||
|
||||
// Then: An error dialog should have been opened
|
||||
mockErrorMessageService.verify(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
});
|
||||
});
|
||||
|
||||
function createInstantiationService(addAccountFailureEmitter?: Emitter<string>): InstantiationService {
|
||||
// Create a mock account dialog view model
|
||||
let accountViewModel = new AccountViewModel(new TestAccountManagementService());
|
||||
let mockAccountViewModel = TypeMoq.Mock.ofInstance(accountViewModel);
|
||||
let mockEvent = new Emitter<any>();
|
||||
mockAccountViewModel.setup(x => x.addProviderEvent).returns(() => mockEvent.event);
|
||||
mockAccountViewModel.setup(x => x.removeProviderEvent).returns(() => mockEvent.event);
|
||||
mockAccountViewModel.setup(x => x.updateAccountListEvent).returns(() => mockEvent.event);
|
||||
mockAccountViewModel.setup(x => x.initialize()).returns(() => Promise.resolve([]));
|
||||
|
||||
// Create a mocked out instantiation service
|
||||
let instantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Strict);
|
||||
instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(AccountViewModel)))
|
||||
.returns(() => mockAccountViewModel.object);
|
||||
instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(AccountListRenderer)))
|
||||
.returns(() => undefined);
|
||||
|
||||
// Create a mock account dialog
|
||||
let accountDialog = new AccountDialog(null, null, instantiationService.object, null, null, null, null, new MockContextKeyService(), null, undefined);
|
||||
let mockAccountDialog = TypeMoq.Mock.ofInstance(accountDialog);
|
||||
mockAccountDialog.setup(x => x.onAddAccountErrorEvent)
|
||||
.returns(() => { return addAccountFailureEmitter ? addAccountFailureEmitter.event : mockEvent.event; });
|
||||
mockAccountDialog.setup(x => x.onCloseEvent)
|
||||
.returns(() => mockEvent.event);
|
||||
mockAccountDialog.setup(x => x.render())
|
||||
.returns(() => undefined);
|
||||
mockAccountDialog.setup(x => x.open())
|
||||
.returns(() => undefined);
|
||||
instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(AccountDialog)))
|
||||
.returns(() => mockAccountDialog.object);
|
||||
|
||||
return instantiationService.object;
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as assert from 'assert';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import { EventVerifierSingle } from 'sqltest/utils/eventVerifier';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { AccountPicker } from 'sql/platform/accounts/browser/accountPicker';
|
||||
import { AccountPickerService } from 'sql/platform/accounts/browser/accountPickerService';
|
||||
import { AccountPickerViewModel } from 'sql/platform/accounts/common/accountPickerViewModel';
|
||||
import { TestAccountManagementService } from 'sql/platform/accounts/test/common/testAccountManagementService';
|
||||
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||
|
||||
// SUITE STATE /////////////////////////////////////////////////////////////
|
||||
let mockAddAccountCompleteEmitter: Emitter<void>;
|
||||
let mockAddAccountErrorEmitter: Emitter<string>;
|
||||
let mockAddAccountStartEmitter: Emitter<void>;
|
||||
let mockOnAccountSelectionChangeEvent: Emitter<azdata.Account>;
|
||||
|
||||
// TESTS ///////////////////////////////////////////////////////////////////
|
||||
suite('Account picker service tests', () => {
|
||||
setup(() => {
|
||||
// Setup event mocks for the account picker service
|
||||
mockAddAccountCompleteEmitter = new Emitter<void>();
|
||||
mockAddAccountErrorEmitter = new Emitter<string>();
|
||||
mockAddAccountStartEmitter = new Emitter<void>();
|
||||
mockOnAccountSelectionChangeEvent = new Emitter<azdata.Account>();
|
||||
});
|
||||
|
||||
test('Construction - Events are properly defined', () => {
|
||||
// Setup:
|
||||
// ... Create instantiation service
|
||||
let instantiationService = createInstantiationService();
|
||||
|
||||
// ... Create instance of the service and reder account picker
|
||||
let service = new AccountPickerService(instantiationService);
|
||||
service.renderAccountPicker(TypeMoq.It.isAny());
|
||||
|
||||
// Then:
|
||||
// ... All the events for the view models should be properly initialized
|
||||
assert.notEqual(service.addAccountCompleteEvent, undefined);
|
||||
assert.notEqual(service.addAccountErrorEvent, undefined);
|
||||
assert.notEqual(service.addAccountStartEvent, undefined);
|
||||
assert.notEqual(service.onAccountSelectionChangeEvent, undefined);
|
||||
|
||||
|
||||
// ... All the events should properly fire
|
||||
let evAddAccountCompleteEvent = new EventVerifierSingle<void>();
|
||||
service.addAccountCompleteEvent(evAddAccountCompleteEvent.eventHandler);
|
||||
mockAddAccountCompleteEmitter.fire();
|
||||
evAddAccountCompleteEvent.assertFired();
|
||||
|
||||
let errorMsg = 'Error';
|
||||
let evAddAccountErrorEvent = new EventVerifierSingle<string>();
|
||||
service.addAccountErrorEvent(evAddAccountErrorEvent.eventHandler);
|
||||
mockAddAccountErrorEmitter.fire(errorMsg);
|
||||
evAddAccountErrorEvent.assertFired(errorMsg);
|
||||
|
||||
let evAddAccountStartEvent = new EventVerifierSingle<void>();
|
||||
service.addAccountStartEvent(evAddAccountStartEvent.eventHandler);
|
||||
mockAddAccountStartEmitter.fire();
|
||||
evAddAccountStartEvent.assertFired();
|
||||
|
||||
let account = {
|
||||
key: { providerId: 'azure', accountId: 'account1' },
|
||||
name: 'Account 1',
|
||||
displayInfo: {
|
||||
contextualDisplayName: 'Microsoft Account',
|
||||
accountType: 'microsoft',
|
||||
displayName: 'Account 1',
|
||||
userId: 'user@email.com'
|
||||
},
|
||||
properties: [],
|
||||
isStale: false
|
||||
};
|
||||
let evOnAccountSelectionChangeEvent = new EventVerifierSingle<azdata.Account>();
|
||||
service.onAccountSelectionChangeEvent(evOnAccountSelectionChangeEvent.eventHandler);
|
||||
mockOnAccountSelectionChangeEvent.fire(account);
|
||||
evOnAccountSelectionChangeEvent.assertFired(account);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function createInstantiationService(): InstantiationService {
|
||||
// Create a mock account picker view model
|
||||
let providerId = 'azure';
|
||||
let accountPickerViewModel = new AccountPickerViewModel(providerId, new TestAccountManagementService());
|
||||
let mockAccountViewModel = TypeMoq.Mock.ofInstance(accountPickerViewModel);
|
||||
let mockEvent = new Emitter<any>();
|
||||
mockAccountViewModel.setup(x => x.updateAccountListEvent).returns(() => mockEvent.event);
|
||||
|
||||
// Create a mocked out instantiation service
|
||||
let instantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Strict);
|
||||
instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(AccountPickerViewModel), TypeMoq.It.isAny()))
|
||||
.returns(() => mockAccountViewModel.object);
|
||||
|
||||
// Create a mock account picker
|
||||
let accountPicker = new AccountPicker(null, null, instantiationService.object, null);
|
||||
let mockAccountDialog = TypeMoq.Mock.ofInstance(accountPicker);
|
||||
|
||||
mockAccountDialog.setup(x => x.addAccountCompleteEvent)
|
||||
.returns(() => mockAddAccountCompleteEmitter.event);
|
||||
mockAccountDialog.setup(x => x.addAccountErrorEvent)
|
||||
.returns((msg) => mockAddAccountErrorEmitter.event);
|
||||
mockAccountDialog.setup(x => x.addAccountStartEvent)
|
||||
.returns(() => mockAddAccountStartEmitter.event);
|
||||
mockAccountDialog.setup(x => x.onAccountSelectionChangeEvent)
|
||||
.returns((account) => mockOnAccountSelectionChangeEvent.event);
|
||||
mockAccountDialog.setup(x => x.render(TypeMoq.It.isAny()))
|
||||
.returns((container) => undefined);
|
||||
mockAccountDialog.setup(x => x.createAccountPickerComponent());
|
||||
|
||||
instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(AccountPicker), TypeMoq.It.isAny()))
|
||||
.returns(() => mockAccountDialog.object);
|
||||
|
||||
return instantiationService.object;
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||
|
||||
import { AutoOAuthDialog } from 'sql/platform/accounts/browser/autoOAuthDialog';
|
||||
import { AutoOAuthDialogController } from 'sql/platform/accounts/browser/autoOAuthDialogController';
|
||||
import { TestAccountManagementService } from 'sql/platform/accounts/test/common/testAccountManagementService';
|
||||
import { TestErrorMessageService } from 'sql/platform/errorMessage/test/common/testErrorMessageService';
|
||||
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
|
||||
|
||||
// TESTS ///////////////////////////////////////////////////////////////////
|
||||
suite('auto OAuth dialog controller tests', () => {
|
||||
let instantiationService: TypeMoq.Mock<InstantiationService>;
|
||||
let mockAutoOAuthDialog: TypeMoq.Mock<AutoOAuthDialog>;
|
||||
let mockAccountManagementService: TypeMoq.Mock<TestAccountManagementService>;
|
||||
let mockErrorMessageService: TypeMoq.Mock<TestErrorMessageService>;
|
||||
let autoOAuthDialogController: AutoOAuthDialogController;
|
||||
|
||||
let mockOnCancelEvent: Emitter<void>;
|
||||
let mockOnAddAccountEvent: Emitter<void>;
|
||||
let mockOnCloseEvent: Emitter<void>;
|
||||
|
||||
let providerId = 'azure';
|
||||
let title = 'Add Account';
|
||||
let message = 'This is the dialog description';
|
||||
let userCode = 'abcde';
|
||||
let uri = 'uri';
|
||||
|
||||
setup(() => {
|
||||
mockOnCancelEvent = new Emitter<void>();
|
||||
mockOnAddAccountEvent = new Emitter<void>();
|
||||
mockOnCloseEvent = new Emitter<void>();
|
||||
|
||||
// Create a mock auto OAuth dialog
|
||||
let autoOAuthDialog = new AutoOAuthDialog(null, null, null, null, new MockContextKeyService(), null, undefined);
|
||||
mockAutoOAuthDialog = TypeMoq.Mock.ofInstance(autoOAuthDialog);
|
||||
|
||||
mockAutoOAuthDialog.setup(x => x.onCancel).returns(() => mockOnCancelEvent.event);
|
||||
mockAutoOAuthDialog.setup(x => x.onHandleAddAccount).returns(() => mockOnAddAccountEvent.event);
|
||||
mockAutoOAuthDialog.setup(x => x.onCloseEvent).returns(() => mockOnCloseEvent.event);
|
||||
mockAutoOAuthDialog.setup(x => x.render());
|
||||
mockAutoOAuthDialog.setup(x => x.open(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()));
|
||||
mockAutoOAuthDialog.setup(x => x.close()).callback(() => {
|
||||
mockOnCloseEvent.fire();
|
||||
});
|
||||
|
||||
// Create a mocked out instantiation service
|
||||
instantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Strict);
|
||||
instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(AutoOAuthDialog)))
|
||||
.returns(() => mockAutoOAuthDialog.object);
|
||||
|
||||
|
||||
// Create a mocked account management service
|
||||
let accountManagementTestService = new TestAccountManagementService();
|
||||
mockAccountManagementService = TypeMoq.Mock.ofInstance(accountManagementTestService);
|
||||
mockAccountManagementService.setup(x => x.copyUserCodeAndOpenBrowser(TypeMoq.It.isAny(), TypeMoq.It.isAny()));
|
||||
|
||||
// Create a mocked error message service
|
||||
let errorMessageServiceStub = new TestErrorMessageService();
|
||||
mockErrorMessageService = TypeMoq.Mock.ofInstance(errorMessageServiceStub);
|
||||
mockErrorMessageService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()));
|
||||
|
||||
// Create a mocked auto OAuth dialog controller
|
||||
autoOAuthDialogController = new AutoOAuthDialogController(instantiationService.object, mockAccountManagementService.object, mockErrorMessageService.object);
|
||||
|
||||
});
|
||||
|
||||
test('Open auto OAuth when the flyout is already open, return an error', (done) => {
|
||||
|
||||
// If: Open auto OAuth dialog first time
|
||||
autoOAuthDialogController.openAutoOAuthDialog(providerId, title, message, userCode, uri);
|
||||
|
||||
// Then: It should open the flyout successfully
|
||||
mockAutoOAuthDialog.verify(x => x.open(title, message, userCode, uri), TypeMoq.Times.once());
|
||||
mockErrorMessageService.verify(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.never());
|
||||
|
||||
// If: a oauth flyout is already open
|
||||
autoOAuthDialogController.openAutoOAuthDialog(providerId, title, message, userCode, uri)
|
||||
.then(success => done('Failure: Expected error on 2nd dialog open'), error => done());
|
||||
|
||||
// Then: An error dialog should have been opened
|
||||
mockErrorMessageService.verify(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
});
|
||||
|
||||
test('Close auto OAuth dialog successfully', () => {
|
||||
let title = 'Add Account';
|
||||
let message = 'This is the dialog description';
|
||||
let userCode = 'abcde';
|
||||
let uri = 'uri';
|
||||
|
||||
autoOAuthDialogController.openAutoOAuthDialog(providerId, title, message, userCode, uri);
|
||||
|
||||
// If: closeAutoOAuthDialog is called
|
||||
autoOAuthDialogController.closeAutoOAuthDialog();
|
||||
|
||||
// Then: it should close the dialog
|
||||
mockAutoOAuthDialog.verify(x => x.close(), TypeMoq.Times.once());
|
||||
});
|
||||
|
||||
test('Open and close auto OAuth dialog multiple times should work properly', () => {
|
||||
let title = 'Add Account';
|
||||
let message = 'This is the dialog description';
|
||||
let userCode = 'abcde';
|
||||
let uri = 'uri';
|
||||
|
||||
autoOAuthDialogController.openAutoOAuthDialog(providerId, title, message, userCode, uri);
|
||||
autoOAuthDialogController.closeAutoOAuthDialog();
|
||||
|
||||
// If: Open the flyout second time
|
||||
autoOAuthDialogController.openAutoOAuthDialog(providerId, title, message, userCode, uri);
|
||||
|
||||
// Then: It should open the flyout twice successfully
|
||||
mockAutoOAuthDialog.verify(x => x.open(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.exactly(2));
|
||||
mockErrorMessageService.verify(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.never());
|
||||
});
|
||||
|
||||
test('Copy and open button in auto OAuth dialog should work properly', () => {
|
||||
let title = 'Add Account';
|
||||
let message = 'This is the dialog description';
|
||||
let userCode = 'abcde';
|
||||
let uri = 'uri';
|
||||
|
||||
autoOAuthDialogController.openAutoOAuthDialog(providerId, title, message, userCode, uri);
|
||||
|
||||
// If: the 'copy & open' button in auto Oauth dialog is selected
|
||||
mockOnAddAccountEvent.fire();
|
||||
|
||||
// Then: copyUserCodeAndOpenBrowser should get called
|
||||
mockAccountManagementService.verify(x => x.copyUserCodeAndOpenBrowser(userCode, uri), TypeMoq.Times.once());
|
||||
});
|
||||
|
||||
// TODO: Test for cancel button
|
||||
|
||||
});
|
||||
@@ -0,0 +1,256 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { FirewallRuleDialog } from 'sql/platform/accounts/browser/firewallRuleDialog';
|
||||
import { FirewallRuleViewModel } from 'sql/platform/accounts/common/firewallRuleViewModel';
|
||||
import { FirewallRuleDialogController } from 'sql/platform/accounts/browser/firewallRuleDialogController';
|
||||
import { TestAccountManagementService } from 'sql/platform/accounts/test/common/testAccountManagementService';
|
||||
import { TestResourceProvider } from 'sql/workbench/services/resourceProvider/test/common/testResourceProviderService';
|
||||
import { TestErrorMessageService } from 'sql/platform/errorMessage/test/common/testErrorMessageService';
|
||||
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||
import { Deferred } from 'sql/base/common/promise';
|
||||
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
|
||||
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
|
||||
|
||||
// TESTS ///////////////////////////////////////////////////////////////////
|
||||
suite('Firewall rule dialog controller tests', () => {
|
||||
let connectionProfile: IConnectionProfile;
|
||||
let account: azdata.Account;
|
||||
let IPAddress = '250.222.155.198';
|
||||
let mockOnAddAccountErrorEvent: Emitter<string>;
|
||||
let mockOnCreateFirewallRule: Emitter<void>;
|
||||
|
||||
let instantiationService: TypeMoq.Mock<InstantiationService>;
|
||||
let mockFirewallRuleViewModel: TypeMoq.Mock<FirewallRuleViewModel>;
|
||||
let mockFirewallRuleDialog: TypeMoq.Mock<FirewallRuleDialog>;
|
||||
|
||||
setup(() => {
|
||||
account = {
|
||||
key: { providerId: 'azure', accountId: 'account1' },
|
||||
displayInfo: {
|
||||
contextualDisplayName: 'Microsoft Account',
|
||||
accountType: 'microsoft',
|
||||
displayName: 'Account 1',
|
||||
userId: 'user@email.com'
|
||||
},
|
||||
properties: [],
|
||||
isStale: false
|
||||
};
|
||||
|
||||
mockOnAddAccountErrorEvent = new Emitter<string>();
|
||||
mockOnCreateFirewallRule = new Emitter<void>();
|
||||
|
||||
// Create a mock firewall rule view model
|
||||
let firewallRuleViewModel = new FirewallRuleViewModel();
|
||||
mockFirewallRuleViewModel = TypeMoq.Mock.ofInstance(firewallRuleViewModel);
|
||||
mockFirewallRuleViewModel.setup(x => x.updateDefaultValues(TypeMoq.It.isAny()))
|
||||
.returns((ipAddress) => undefined);
|
||||
mockFirewallRuleViewModel.object.selectedAccount = account;
|
||||
mockFirewallRuleViewModel.object.isIPAddressSelected = true;
|
||||
|
||||
// Create a mocked out instantiation service
|
||||
instantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Strict);
|
||||
instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(FirewallRuleViewModel)))
|
||||
.returns(() => mockFirewallRuleViewModel.object);
|
||||
|
||||
// Create a mock account picker
|
||||
let firewallRuleDialog = new FirewallRuleDialog(null, null, null, instantiationService.object, null, null, new MockContextKeyService(), null, null, undefined);
|
||||
mockFirewallRuleDialog = TypeMoq.Mock.ofInstance(firewallRuleDialog);
|
||||
|
||||
let mockEvent = new Emitter<any>();
|
||||
mockFirewallRuleDialog.setup(x => x.onCancel)
|
||||
.returns(() => mockEvent.event);
|
||||
mockFirewallRuleDialog.setup(x => x.onCreateFirewallRule)
|
||||
.returns(() => mockOnCreateFirewallRule.event);
|
||||
mockFirewallRuleDialog.setup(x => x.onAddAccountErrorEvent)
|
||||
.returns((msg) => mockOnAddAccountErrorEvent.event);
|
||||
mockFirewallRuleDialog.setup(x => x.render());
|
||||
mockFirewallRuleDialog.setup(x => x.open());
|
||||
mockFirewallRuleDialog.setup(x => x.close());
|
||||
|
||||
instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(FirewallRuleDialog)))
|
||||
.returns(() => mockFirewallRuleDialog.object);
|
||||
|
||||
connectionProfile = {
|
||||
connectionName: 'new name',
|
||||
serverName: 'new server',
|
||||
databaseName: 'database',
|
||||
userName: 'user',
|
||||
password: 'password',
|
||||
authenticationType: '',
|
||||
savePassword: true,
|
||||
groupFullName: 'g2/g2-2',
|
||||
groupId: 'group id',
|
||||
getOptionsKey: undefined,
|
||||
matches: undefined,
|
||||
providerName: mssqlProviderName,
|
||||
options: {},
|
||||
saveProfile: true,
|
||||
id: undefined
|
||||
};
|
||||
});
|
||||
|
||||
test('Add Account Failure - Error Message Shown', () => {
|
||||
// ... Create a mock instance of the error message service
|
||||
let errorMessageServiceStub = new TestErrorMessageService();
|
||||
let mockErrorMessageService = TypeMoq.Mock.ofInstance(errorMessageServiceStub);
|
||||
mockErrorMessageService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()));
|
||||
|
||||
// ... Create instance of the controller with an opened dialog
|
||||
let controller = new FirewallRuleDialogController(instantiationService.object, null, null, mockErrorMessageService.object);
|
||||
controller.openFirewallRuleDialog(connectionProfile, IPAddress, 'resourceID');
|
||||
|
||||
// If: The firewall rule dialog reports a failure
|
||||
|
||||
mockOnAddAccountErrorEvent.fire('Error message');
|
||||
|
||||
// Then: An error dialog should have been opened
|
||||
mockErrorMessageService.verify(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
});
|
||||
|
||||
test('create firewall rule success', (done) => {
|
||||
let deferredPromise = new Deferred();
|
||||
|
||||
mockFirewallRuleDialog.setup(x => x.onServiceComplete())
|
||||
.callback(() => {
|
||||
deferredPromise.resolve(true);
|
||||
});
|
||||
|
||||
// ... Create a mock instance of the account management test service
|
||||
let mockAccountManagementService = getMockAccountManagementService(true);
|
||||
|
||||
// ... Create a mock instance of the resource provider
|
||||
let mockResourceProvider = getMockResourceProvider(true, { result: true, errorMessage: '' });
|
||||
|
||||
// ... Create instance of the controller with an opened dialog
|
||||
let controller = new FirewallRuleDialogController(instantiationService.object, mockResourceProvider.object, mockAccountManagementService.object, null);
|
||||
controller.openFirewallRuleDialog(connectionProfile, IPAddress, 'resourceID');
|
||||
|
||||
// If: The firewall rule dialog's create firewall rule get fired
|
||||
mockOnCreateFirewallRule.fire();
|
||||
|
||||
// Then: it should get security token from account management service and call create firewall rule in resource provider
|
||||
deferredPromise.promise.then(() => {
|
||||
mockAccountManagementService.verify(x => x.getSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
mockResourceProvider.verify(x => x.createFirewallRule(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
mockFirewallRuleDialog.verify(x => x.close(), TypeMoq.Times.once());
|
||||
mockFirewallRuleDialog.verify(x => x.onServiceComplete(), TypeMoq.Times.once());
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('create firewall rule fails during getSecurity', (done) => {
|
||||
let deferredPromise = new Deferred();
|
||||
|
||||
// ... Create a mock instance of the error message service
|
||||
let mockErrorMessageService = getMockErrorMessageService(deferredPromise);
|
||||
|
||||
// ... Create a mock instance of the account management test service
|
||||
let mockAccountManagementService = getMockAccountManagementService(false);
|
||||
|
||||
// ... Create a mock instance of the resource provider
|
||||
let mockResourceProvider = getMockResourceProvider(true, { result: true, errorMessage: '' });
|
||||
|
||||
// ... Create instance of the controller with an opened dialog
|
||||
let controller = new FirewallRuleDialogController(instantiationService.object, mockResourceProvider.object, mockAccountManagementService.object, mockErrorMessageService.object);
|
||||
controller.openFirewallRuleDialog(connectionProfile, IPAddress, 'resourceID');
|
||||
|
||||
// If: The firewall rule dialog's create firewall rule get fired
|
||||
mockOnCreateFirewallRule.fire();
|
||||
|
||||
// Then: it should get security token from account management service and an error dialog should have been opened
|
||||
deferredPromise.promise.then(() => {
|
||||
mockAccountManagementService.verify(x => x.getSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
mockErrorMessageService.verify(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
mockResourceProvider.verify(x => x.createFirewallRule(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.never());
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('create firewall rule fails during createFirewallRule in ResourceProvider - result is false', (done) => {
|
||||
let deferredPromise = new Deferred();
|
||||
|
||||
// ... Create a mock instance of the error message service
|
||||
let mockErrorMessageService = getMockErrorMessageService(deferredPromise);
|
||||
|
||||
// ... Create a mock instance of the account management test service
|
||||
let mockAccountManagementService = getMockAccountManagementService(true);
|
||||
|
||||
// ... Create a mock instance of the resource provider
|
||||
let mockResourceProvider = getMockResourceProvider(true, { result: false, errorMessage: '' });
|
||||
|
||||
// ... Create instance of the controller with an opened dialog
|
||||
let controller = new FirewallRuleDialogController(instantiationService.object, mockResourceProvider.object, mockAccountManagementService.object, mockErrorMessageService.object);
|
||||
controller.openFirewallRuleDialog(connectionProfile, IPAddress, 'resourceID');
|
||||
|
||||
// If: The firewall rule dialog's create firewall rule get fired
|
||||
mockOnCreateFirewallRule.fire();
|
||||
|
||||
// Then: it should get security token from account management service and an error dialog should have been opened
|
||||
deferredPromise.promise.then(() => {
|
||||
mockAccountManagementService.verify(x => x.getSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
mockResourceProvider.verify(x => x.createFirewallRule(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
mockErrorMessageService.verify(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('create firewall rule fails during createFirewallRule in ResourceProvider - reject promise', (done) => {
|
||||
let deferredPromise = new Deferred();
|
||||
|
||||
// ... Create a mock instance of the error message service
|
||||
let mockErrorMessageService = getMockErrorMessageService(deferredPromise);
|
||||
|
||||
// ... Create a mock instance of the account management test service
|
||||
let mockAccountManagementService = getMockAccountManagementService(true);
|
||||
|
||||
// ... Create a mock instance of the resource provider
|
||||
let mockResourceProvider = getMockResourceProvider(false);
|
||||
|
||||
// ... Create instance of the controller with an opened dialog
|
||||
let controller = new FirewallRuleDialogController(instantiationService.object, mockResourceProvider.object, mockAccountManagementService.object, mockErrorMessageService.object);
|
||||
controller.openFirewallRuleDialog(connectionProfile, IPAddress, 'resourceID');
|
||||
|
||||
// If: The firewall rule dialog's create firewall rule get fired
|
||||
mockOnCreateFirewallRule.fire();
|
||||
|
||||
// Then: it should get security token from account management service and an error dialog should have been opened
|
||||
deferredPromise.promise.then(() => {
|
||||
mockAccountManagementService.verify(x => x.getSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
mockResourceProvider.verify(x => x.createFirewallRule(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
mockErrorMessageService.verify(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function getMockAccountManagementService(resolveSecurityToken: boolean): TypeMoq.Mock<TestAccountManagementService> {
|
||||
let accountManagementTestService = new TestAccountManagementService();
|
||||
let mockAccountManagementService = TypeMoq.Mock.ofInstance(accountManagementTestService);
|
||||
mockAccountManagementService.setup(x => x.getSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny()))
|
||||
.returns(() => resolveSecurityToken ? Promise.resolve({}) : Promise.reject(null).then());
|
||||
return mockAccountManagementService;
|
||||
}
|
||||
|
||||
function getMockResourceProvider(resolveCreateFirewallRule: boolean, response?: azdata.CreateFirewallRuleResponse): TypeMoq.Mock<TestResourceProvider> {
|
||||
let resourceProviderStub = new TestResourceProvider();
|
||||
let mockResourceProvider = TypeMoq.Mock.ofInstance(resourceProviderStub);
|
||||
mockResourceProvider.setup(x => x.createFirewallRule(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()))
|
||||
.returns(() => resolveCreateFirewallRule ? Promise.resolve(response) : Promise.reject(null).then());
|
||||
return mockResourceProvider;
|
||||
}
|
||||
|
||||
function getMockErrorMessageService(deferredPromise: Deferred<{}>): TypeMoq.Mock<TestErrorMessageService> {
|
||||
let errorMessageServiceStub = new TestErrorMessageService();
|
||||
let mockErrorMessageService = TypeMoq.Mock.ofInstance(errorMessageServiceStub);
|
||||
mockErrorMessageService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).callback(() => {
|
||||
deferredPromise.resolve(true);
|
||||
});
|
||||
return mockErrorMessageService;
|
||||
}
|
||||
190
src/sql/platform/accounts/test/common/accountActions.test.ts
Normal file
190
src/sql/platform/accounts/test/common/accountActions.test.ts
Normal file
@@ -0,0 +1,190 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as azdata from 'azdata';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import { AddAccountAction, RemoveAccountAction } from 'sql/platform/accounts/common/accountActions';
|
||||
import { TestAccountManagementService } from 'sql/platform/accounts/test/common/testAccountManagementService';
|
||||
// import { MessageServiceStub } from 'sqltest/stubs/messageServiceStub';
|
||||
import { TestErrorMessageService } from 'sql/platform/errorMessage/test/common/testErrorMessageService';
|
||||
|
||||
let testAccount = <azdata.Account>{
|
||||
key: {
|
||||
providerId: 'azure',
|
||||
accountId: 'testAccount'
|
||||
},
|
||||
displayInfo: {
|
||||
accountType: 'test',
|
||||
displayName: 'Test Account',
|
||||
contextualDisplayName: 'Azure Account'
|
||||
},
|
||||
isStale: false
|
||||
};
|
||||
|
||||
suite('Account Management Dialog Actions Tests', () => {
|
||||
test('AddAccount - Success', (done) => {
|
||||
done();
|
||||
// // Setup: Create an AddAccountAction object
|
||||
// let param = 'azure';
|
||||
// let mocks = createAddAccountAction(true, true, param);
|
||||
|
||||
// // If: I run the action when it will resolve
|
||||
// mocks.action.run()
|
||||
// .then(result => {
|
||||
// // Then:
|
||||
// // ... I should have gotten true back
|
||||
// assert.ok(result);
|
||||
|
||||
// // ... The account management service should have gotten a add account request
|
||||
// mocks.accountMock.verify(x => x.addAccount(param), TypeMoq.Times.once());
|
||||
// })
|
||||
// .then(
|
||||
// () => done(),
|
||||
// err => done(err)
|
||||
// );
|
||||
});
|
||||
|
||||
// test('AddAccount - Failure', (done) => {
|
||||
// // // Setup: Create an AddAccountAction object
|
||||
// // let param = 'azure';
|
||||
// // let mocks = createAddAccountAction(false, true, param);
|
||||
|
||||
// // // If: I run the action when it will reject
|
||||
// // mocks.action.run().then(result => {
|
||||
// // // Then:
|
||||
// // // ... The result should be false since the operation failed
|
||||
// // assert.ok(!result);
|
||||
// // // ... The account management service should have gotten a add account request
|
||||
// // mocks.accountMock.verify(x => x.addAccount(param), TypeMoq.Times.once());
|
||||
// // done();
|
||||
// // }, error => {
|
||||
// // // Should fail as rejected actions cause the debugger to crash
|
||||
// // done(error);
|
||||
// // });
|
||||
// });
|
||||
|
||||
// test('RemoveAccount - Confirm Success', (done) => {
|
||||
// // // Setup: Create an AddAccountAction object
|
||||
// // let ams = getMockAccountManagementService(true);
|
||||
// // let ms = getMockMessageService(true);
|
||||
// // let es = getMockErrorMessageService();
|
||||
// // let action = new RemoveAccountAction(testAccount, ms.object, es.object, ams.object);
|
||||
|
||||
// // // If: I run the action when it will resolve
|
||||
// // action.run()
|
||||
// // .then(result => {
|
||||
// // // Then:
|
||||
// // // ... I should have gotten true back
|
||||
// // assert.ok(result);
|
||||
|
||||
// // // ... A confirmation dialog should have opened
|
||||
// // ms.verify(x => x.confirm(TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
|
||||
// // // ... The account management service should have gotten a remove account request
|
||||
// // ams.verify(x => x.removeAccount(TypeMoq.It.isValue(testAccount.key)), TypeMoq.Times.once());
|
||||
// // })
|
||||
// // .then(
|
||||
// // () => done(),
|
||||
// // err => done(err)
|
||||
// // );
|
||||
// });
|
||||
|
||||
// test('RemoveAccount - Declined Success', (done) => {
|
||||
// // // Setup: Create an AddAccountAction object
|
||||
// // let ams = getMockAccountManagementService(true);
|
||||
// // let ms = getMockMessageService(false);
|
||||
// // let es = getMockErrorMessageService();
|
||||
// // let action = new RemoveAccountAction(testAccount, ms.object, es.object, ams.object);
|
||||
|
||||
// // // If: I run the action when it will resolve
|
||||
// // action.run()
|
||||
// // .then(result => {
|
||||
// // try {
|
||||
// // // Then:
|
||||
// // // ... I should have gotten false back
|
||||
// // assert.ok(!result);
|
||||
|
||||
// // // ... A confirmation dialog should have opened
|
||||
// // ms.verify(x => x.confirm(TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
|
||||
// // // ... The account management service should not have gotten a remove account request
|
||||
// // ams.verify(x => x.removeAccount(TypeMoq.It.isAny()), TypeMoq.Times.never());
|
||||
|
||||
// // done();
|
||||
// // } catch (e) {
|
||||
// // done(e);
|
||||
// // }
|
||||
// // });
|
||||
// });
|
||||
|
||||
// test('RemoveAccount - Failure', (done) => {
|
||||
// // // Setup: Create an AddAccountAction object
|
||||
// // let ams = getMockAccountManagementService(false);
|
||||
// // let ms = getMockMessageService(true);
|
||||
// // let es = getMockErrorMessageService();
|
||||
// // let action = new RemoveAccountAction(testAccount, ms.object, es.object, ams.object);
|
||||
|
||||
// // // If: I run the action when it will reject
|
||||
// // action.run().then(result => {
|
||||
// // // Then:
|
||||
// // // ... The result should be false since the operation failed
|
||||
// // assert.ok(!result);
|
||||
// // // ... The account management service should have gotten a remove account request
|
||||
// // ams.verify(x => x.removeAccount(TypeMoq.It.isValue(testAccount.key)), TypeMoq.Times.once());
|
||||
// // done();
|
||||
// // }, error => {
|
||||
// // // Should fail as rejected actions cause the debugger to crash
|
||||
// // done(error);
|
||||
// // });
|
||||
// });
|
||||
});
|
||||
|
||||
// function createAddAccountAction(resolve: boolean, confirm: boolean, param: string): IAddActionMocks {
|
||||
// let ams = getMockAccountManagementService(resolve);
|
||||
// let mockMessageService = getMockMessageService(confirm);
|
||||
// let mockErrorMessageService = getMockErrorMessageService();
|
||||
// return {
|
||||
// accountMock: ams,
|
||||
// messageMock: mockMessageService,
|
||||
// errorMessageMock: mockErrorMessageService,
|
||||
// action: new AddAccountAction(param, mockMessageService.object,
|
||||
// mockErrorMessageService.object, ams.object)
|
||||
// };
|
||||
// }
|
||||
|
||||
// function getMockAccountManagementService(resolve: boolean): TypeMoq.Mock<AccountManagementTestService> {
|
||||
// let mockAccountManagementService = TypeMoq.Mock.ofType(AccountManagementTestService);
|
||||
|
||||
// mockAccountManagementService.setup(x => x.addAccount(TypeMoq.It.isAnyString()))
|
||||
// .returns(resolve ? () => Promise.resolve(null) : () => Promise.reject(null));
|
||||
// mockAccountManagementService.setup(x => x.removeAccount(TypeMoq.It.isAny()))
|
||||
// .returns(resolve ? () => Promise.resolve(true) : () => Promise.reject(null).then());
|
||||
|
||||
// return mockAccountManagementService;
|
||||
// }
|
||||
|
||||
// function getMockMessageService(confirm: boolean): TypeMoq.Mock<MessageServiceStub> {
|
||||
// let mockMessageService = TypeMoq.Mock.ofType(MessageServiceStub);
|
||||
|
||||
// mockMessageService.setup(x => x.confirm(TypeMoq.It.isAny()))
|
||||
// .returns(() => undefined);
|
||||
|
||||
// return mockMessageService;
|
||||
// }
|
||||
|
||||
// function getMockErrorMessageService(): TypeMoq.Mock<ErrorMessageServiceStub> {
|
||||
// let mockMessageService = TypeMoq.Mock.ofType(ErrorMessageServiceStub);
|
||||
// mockMessageService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()));
|
||||
// return mockMessageService;
|
||||
// }
|
||||
|
||||
// interface IAddActionMocks
|
||||
// {
|
||||
// accountMock: TypeMoq.Mock<AccountManagementTestService>;
|
||||
// messageMock: TypeMoq.Mock<MessageServiceStub>;
|
||||
// errorMessageMock: TypeMoq.Mock<ErrorMessageServiceStub>;
|
||||
// action: AddAccountAction;
|
||||
// }
|
||||
@@ -0,0 +1,149 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as assert from 'assert';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import { EventVerifierSingle } from 'sqltest/utils/eventVerifier';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { AccountPickerViewModel } from 'sql/platform/accounts/common/accountPickerViewModel';
|
||||
import { UpdateAccountListEventParams } from 'sql/platform/accounts/common/eventTypes';
|
||||
import { TestAccountManagementService } from 'sql/platform/accounts/test/common/testAccountManagementService';
|
||||
|
||||
// SUITE STATE /////////////////////////////////////////////////////////////
|
||||
let mockUpdateAccountEmitter: Emitter<UpdateAccountListEventParams>;
|
||||
|
||||
let providers: azdata.AccountProviderMetadata[];
|
||||
let accounts: azdata.Account[];
|
||||
suite('Account picker view model tests', () => {
|
||||
setup(() => {
|
||||
providers = [{
|
||||
id: 'azure',
|
||||
displayName: 'Azure'
|
||||
}];
|
||||
|
||||
let account1 = {
|
||||
key: { providerId: 'azure', accountId: 'account1' },
|
||||
name: 'Account 1',
|
||||
displayInfo: {
|
||||
contextualDisplayName: 'Microsoft Account',
|
||||
accountType: 'microsoft',
|
||||
displayName: 'Account 1',
|
||||
userId: 'user@email.com'
|
||||
},
|
||||
properties: [],
|
||||
isStale: false
|
||||
};
|
||||
let account2 = {
|
||||
key: { providerId: 'azure', accountId: 'account2' },
|
||||
name: 'Account 2',
|
||||
displayInfo: {
|
||||
contextualDisplayName: 'Work/School Account',
|
||||
accountType: 'microsoft',
|
||||
displayName: 'Account 2',
|
||||
userId: 'user@email.com'
|
||||
},
|
||||
properties: [],
|
||||
isStale: true
|
||||
};
|
||||
accounts = [account1, account2];
|
||||
|
||||
// Setup event mocks
|
||||
mockUpdateAccountEmitter = new Emitter<UpdateAccountListEventParams>();
|
||||
});
|
||||
|
||||
test('Construction - Events are properly defined', () => {
|
||||
// If: I create an account picker viewmodel
|
||||
let mockAccountManagementService = getMockAccountManagementService(false, false);
|
||||
let vm = new AccountPickerViewModel('azure', mockAccountManagementService.object);
|
||||
|
||||
// Then:
|
||||
// ... The event for the view models should be properly initialized
|
||||
assert.notEqual(vm.updateAccountListEvent, undefined);
|
||||
|
||||
// ... The event should properly fire
|
||||
let argUpdateAccounts: UpdateAccountListEventParams = { providerId: providers[0].id, accountList: accounts };
|
||||
let evUpdateAccounts = new EventVerifierSingle<UpdateAccountListEventParams>();
|
||||
vm.updateAccountListEvent(evUpdateAccounts.eventHandler);
|
||||
mockUpdateAccountEmitter.fire(argUpdateAccounts);
|
||||
evUpdateAccounts.assertFired(argUpdateAccounts);
|
||||
});
|
||||
|
||||
test('Initialize - Success', done => {
|
||||
// Setup: Create a viewmodel with event handlers
|
||||
let mockAccountManagementService = getMockAccountManagementService(true, true);
|
||||
let evUpdateAccounts = new EventVerifierSingle<UpdateAccountListEventParams>();
|
||||
let vm = getViewModel(mockAccountManagementService.object, evUpdateAccounts);
|
||||
|
||||
// If: I initialize the view model
|
||||
vm.initialize()
|
||||
.then(results => {
|
||||
// Then:
|
||||
// ... None of the events should have fired
|
||||
evUpdateAccounts.assertNotFired();
|
||||
|
||||
// ... The account management service should have been called
|
||||
mockAccountManagementService.verify(x => x.getAccountsForProvider(TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
|
||||
// ... The results that were returned should be an array of account
|
||||
assert.ok(Array.isArray(results));
|
||||
assert.equal(results.length, 2);
|
||||
assert.equal(results, accounts);
|
||||
}).then(
|
||||
() => done(),
|
||||
err => done(err)
|
||||
);
|
||||
});
|
||||
|
||||
test('Initialize - Get accounts fails expects empty array', done => {
|
||||
// Setup: Create a mock account management service that rejects the promise
|
||||
let mockAccountManagementService = getMockAccountManagementService(true, false);
|
||||
let evUpdateAccounts = new EventVerifierSingle<UpdateAccountListEventParams>();
|
||||
let vm = getViewModel(mockAccountManagementService.object, evUpdateAccounts);
|
||||
|
||||
// If: I initialize the view model
|
||||
vm.initialize()
|
||||
.then(result => {
|
||||
// Then:
|
||||
// ... None of the events should have fired
|
||||
evUpdateAccounts.assertNotFired();
|
||||
|
||||
// ... The account management service should have been called
|
||||
mockAccountManagementService.verify(x => x.getAccountsForProvider(TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
|
||||
// ... The results should be an empty array
|
||||
assert.ok(Array.isArray(result));
|
||||
assert.equal(result.length, 0);
|
||||
assert.equal(result, []);
|
||||
}).then(
|
||||
() => done(),
|
||||
err => done()
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
function getMockAccountManagementService(resolveProviders: boolean, resolveAccounts: boolean): TypeMoq.Mock<TestAccountManagementService> {
|
||||
let mockAccountManagementService = TypeMoq.Mock.ofType(TestAccountManagementService);
|
||||
|
||||
mockAccountManagementService.setup(x => x.getAccountProviderMetadata())
|
||||
.returns(() => resolveProviders ? Promise.resolve(providers) : Promise.reject(null).then());
|
||||
mockAccountManagementService.setup(x => x.getAccountsForProvider(TypeMoq.It.isAny()))
|
||||
.returns(() => resolveAccounts ? Promise.resolve(accounts) : Promise.reject(null).then());
|
||||
|
||||
mockAccountManagementService.setup(x => x.updateAccountListEvent)
|
||||
.returns(() => mockUpdateAccountEmitter.event);
|
||||
|
||||
return mockAccountManagementService;
|
||||
}
|
||||
|
||||
function getViewModel(
|
||||
ams: TestAccountManagementService,
|
||||
evUpdate: EventVerifierSingle<UpdateAccountListEventParams>
|
||||
): AccountPickerViewModel {
|
||||
let vm = new AccountPickerViewModel('azure', ams);
|
||||
vm.updateAccountListEvent(evUpdate.eventHandler);
|
||||
|
||||
return vm;
|
||||
}
|
||||
440
src/sql/platform/accounts/test/common/accountStore.test.ts
Normal file
440
src/sql/platform/accounts/test/common/accountStore.test.ts
Normal file
@@ -0,0 +1,440 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as azdata from 'azdata';
|
||||
import AccountStore from 'sql/platform/accounts/common/accountStore';
|
||||
import { EventVerifierSingle } from 'sqltest/utils/eventVerifier';
|
||||
|
||||
suite('Account Store Tests', () => {
|
||||
test('AddOrUpdate - Uninitialized memento', done => {
|
||||
// Setup: Create account store w/o initialized memento
|
||||
let memento = {};
|
||||
let as = new AccountStore(memento);
|
||||
|
||||
// If: I add an account to the store
|
||||
as.addOrUpdate(account1)
|
||||
.then(result => {
|
||||
// Then:
|
||||
// ... I should have gotten back a result indicating the account was added
|
||||
assert.ok(result.accountAdded);
|
||||
assert.ok(!result.accountModified);
|
||||
assertAccountEqual(result.changedAccount, account1);
|
||||
|
||||
// ... The memento should have been initialized and account added
|
||||
assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY]));
|
||||
assert.equal(memento[AccountStore.MEMENTO_KEY].length, 1);
|
||||
assertAccountEqual(memento[AccountStore.MEMENTO_KEY][0], account1);
|
||||
})
|
||||
.then(
|
||||
() => done(),
|
||||
e => done(e)
|
||||
);
|
||||
});
|
||||
|
||||
test('AddOrUpdate - Adds to accounts', done => {
|
||||
// Setup: Create account store with initialized memento with accounts
|
||||
let memento = {};
|
||||
memento[AccountStore.MEMENTO_KEY] = [];
|
||||
let as = new AccountStore(memento);
|
||||
|
||||
// If: I add an account to the store
|
||||
as.addOrUpdate(account1)
|
||||
.then(result => {
|
||||
// Then:
|
||||
// ... I should have gotten back a result indicating the account was added
|
||||
assert.ok(result.accountAdded);
|
||||
assert.ok(!result.accountModified);
|
||||
assertAccountEqual(result.changedAccount, account1);
|
||||
|
||||
// ... The memento should have the account added
|
||||
assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY]));
|
||||
assert.equal(memento[AccountStore.MEMENTO_KEY].length, 1);
|
||||
assertAccountEqual(memento[AccountStore.MEMENTO_KEY][0], account1);
|
||||
})
|
||||
.then(
|
||||
() => done(),
|
||||
e => done(e)
|
||||
);
|
||||
});
|
||||
|
||||
test('AddOrUpdate - Updates account', done => {
|
||||
// Setup: Create account store with initialized memento with accounts
|
||||
let memento = getTestMemento();
|
||||
let as = new AccountStore(memento);
|
||||
|
||||
// If: I add an account to the store that already exists
|
||||
let param = <azdata.Account>{
|
||||
key: account2.key,
|
||||
displayInfo: account1.displayInfo,
|
||||
isStale: account1.isStale
|
||||
};
|
||||
as.addOrUpdate(param)
|
||||
.then(result => {
|
||||
// Then:
|
||||
// ... I should have gotten back a result indicating the account was updated
|
||||
assert.ok(result.accountModified);
|
||||
assert.ok(!result.accountAdded);
|
||||
assertAccountEqual(result.changedAccount, param);
|
||||
|
||||
// ... The memento should have been initialized and account updated
|
||||
assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY]));
|
||||
assert.equal(memento[AccountStore.MEMENTO_KEY].length, 2);
|
||||
assertAccountEqual(memento[AccountStore.MEMENTO_KEY][0], account1);
|
||||
assertAccountEqual(memento[AccountStore.MEMENTO_KEY][1], param);
|
||||
})
|
||||
.then(
|
||||
() => done(),
|
||||
e => done(e)
|
||||
);
|
||||
});
|
||||
|
||||
test('GetAccountsByProvider - Uninitialized memento', done => {
|
||||
// Setup: Create account store w/o initialized memento
|
||||
let memento = {};
|
||||
let as = new AccountStore(memento);
|
||||
|
||||
// If: I get accounts by provider
|
||||
as.getAccountsByProvider('azure')
|
||||
.then(result => {
|
||||
// Then:
|
||||
// ... I should get back an empty array
|
||||
assert.ok(Array.isArray(result));
|
||||
assert.equal(result.length, 0);
|
||||
|
||||
// ... Memento should not have been written
|
||||
assert.equal(Object.keys(memento).length, 0);
|
||||
})
|
||||
.then(
|
||||
() => done(),
|
||||
e => done(e)
|
||||
);
|
||||
});
|
||||
|
||||
test('GetAccountsByProvider - No accounts', done => {
|
||||
// Setup: Create account store with initialized memento with accounts
|
||||
let memento = {};
|
||||
memento[AccountStore.MEMENTO_KEY] = [];
|
||||
let as = new AccountStore(memento);
|
||||
|
||||
// If: I get accounts when there aren't any accounts
|
||||
as.getAccountsByProvider('azure')
|
||||
.then(result => {
|
||||
// Then: I should get back an empty array
|
||||
assert.ok(Array.isArray(result));
|
||||
assert.equal(result.length, 0);
|
||||
})
|
||||
.then(
|
||||
() => done(),
|
||||
e => done(e)
|
||||
);
|
||||
});
|
||||
|
||||
test('GetAccountsByProvider - Accounts, but no accounts for provider', done => {
|
||||
// Setup: Create account store with initialized memento with accounts
|
||||
let memento = getTestMemento();
|
||||
let as = new AccountStore(memento);
|
||||
|
||||
// If: I get accounts by provider that doesn't have accounts
|
||||
as.getAccountsByProvider('cloudycloud')
|
||||
.then(result => {
|
||||
// Then: I should get back an empty array
|
||||
assert.ok(Array.isArray(result));
|
||||
assert.equal(result.length, 0);
|
||||
})
|
||||
.then(
|
||||
() => done(),
|
||||
e => done(e)
|
||||
);
|
||||
});
|
||||
|
||||
test('GetAccountsByProvider - Accounts for provider', done => {
|
||||
// Setup: Create account store with initialized memento with accounts
|
||||
let memento = getTestMemento();
|
||||
let as = new AccountStore(memento);
|
||||
|
||||
// If: I get accounts by provider that has accounts
|
||||
as.getAccountsByProvider('azure')
|
||||
.then(result => {
|
||||
// Then: I should get the accounts
|
||||
assert.ok(Array.isArray(result));
|
||||
assert.equal(result.length, 2);
|
||||
assertAccountEqual(result[0], memento[AccountStore.MEMENTO_KEY][0]);
|
||||
assertAccountEqual(result[1], memento[AccountStore.MEMENTO_KEY][1]);
|
||||
})
|
||||
.then(
|
||||
() => done(),
|
||||
e => done(e)
|
||||
);
|
||||
});
|
||||
|
||||
test('GetAllAccounts - Uninitialized memento', done => {
|
||||
// Setup: Create account store w/o initialized memento
|
||||
let memento = {};
|
||||
let as = new AccountStore(memento);
|
||||
|
||||
// If: I get accounts
|
||||
as.getAllAccounts()
|
||||
.then(result => {
|
||||
// Then:
|
||||
// ... I should get back an empty array
|
||||
assert.ok(Array.isArray(result));
|
||||
assert.equal(result.length, 0);
|
||||
|
||||
// ... Memento should not have been written
|
||||
assert.equal(Object.keys(memento).length, 0);
|
||||
})
|
||||
.then(
|
||||
() => done(),
|
||||
e => done(e)
|
||||
);
|
||||
});
|
||||
|
||||
test('GetAllAccounts - No accounts', done => {
|
||||
// Setup: Create account store with initialized memento with accounts
|
||||
let memento = {};
|
||||
memento[AccountStore.MEMENTO_KEY] = [];
|
||||
let as = new AccountStore(memento);
|
||||
|
||||
// If: I get accounts when there aren't any accounts
|
||||
as.getAllAccounts()
|
||||
.then(result => {
|
||||
// Then: I should get back an empty array
|
||||
assert.ok(Array.isArray(result));
|
||||
assert.equal(result.length, 0);
|
||||
})
|
||||
.then(
|
||||
() => done(),
|
||||
e => done(e)
|
||||
);
|
||||
});
|
||||
|
||||
test('GetAllAccounts - Accounts', done => {
|
||||
// Setup: Create account store with initialized memento with accounts
|
||||
let memento = getTestMemento();
|
||||
let as = new AccountStore(memento);
|
||||
|
||||
// If: I get accounts
|
||||
as.getAllAccounts()
|
||||
.then(result => {
|
||||
// Then: I should get the accounts
|
||||
assert.ok(Array.isArray(result));
|
||||
assert.equal(result.length, 2);
|
||||
assertAccountEqual(result[0], memento[AccountStore.MEMENTO_KEY][0]);
|
||||
assertAccountEqual(result[1], memento[AccountStore.MEMENTO_KEY][1]);
|
||||
})
|
||||
.then(
|
||||
() => done(),
|
||||
e => done(e)
|
||||
);
|
||||
});
|
||||
|
||||
test('Remove - Uninitialized menento', done => {
|
||||
// Setup: Create account store w/o initialized memento
|
||||
let memento = {};
|
||||
let as = new AccountStore(memento);
|
||||
|
||||
// If: I remove an account when there's an uninitialized memento
|
||||
as.remove(account1.key)
|
||||
.then(result => {
|
||||
// Then:
|
||||
// ... I should get back false (no account removed)
|
||||
assert.ok(!result);
|
||||
|
||||
// ... The memento should have been initialized
|
||||
assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY]));
|
||||
assert.equal(memento[AccountStore.MEMENTO_KEY].length, 0);
|
||||
})
|
||||
.then(
|
||||
() => done(),
|
||||
e => done(e)
|
||||
);
|
||||
});
|
||||
|
||||
test('Remove - Account does not exist', done => {
|
||||
// Setup: Create account store with initialized memento with accounts
|
||||
let memento = {};
|
||||
memento[AccountStore.MEMENTO_KEY] = [];
|
||||
let as = new AccountStore(memento);
|
||||
|
||||
// If: I remove an account that doesn't exist
|
||||
as.remove({ providerId: 'cloudyCloud', accountId: 'testyTest' })
|
||||
.then(result => {
|
||||
// Then:
|
||||
// ... I should get back false (no account removed)
|
||||
assert.ok(!result);
|
||||
|
||||
// ... The memento should still be empty
|
||||
assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY]));
|
||||
assert.equal(memento[AccountStore.MEMENTO_KEY].length, 0);
|
||||
})
|
||||
.then(
|
||||
() => done(),
|
||||
e => done(e)
|
||||
);
|
||||
});
|
||||
|
||||
test('Remove - Account exists', done => {
|
||||
// Setup: Create account store with initialized memento with accounts
|
||||
let memento = getTestMemento();
|
||||
let as = new AccountStore(memento);
|
||||
|
||||
// If: I remove an account that does exist
|
||||
as.remove(account1.key)
|
||||
.then(result => {
|
||||
// Then:
|
||||
// ... I should get back true (account removed)
|
||||
assert.ok(result);
|
||||
|
||||
// ... The memento should have removed the first account
|
||||
assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY]));
|
||||
assert.equal(memento[AccountStore.MEMENTO_KEY].length, 1);
|
||||
assertAccountEqual(memento[AccountStore.MEMENTO_KEY][0], account2);
|
||||
})
|
||||
.then(
|
||||
() => done(),
|
||||
e => done(e)
|
||||
);
|
||||
});
|
||||
|
||||
test('Update - Uninitialized menento', done => {
|
||||
// Setup:
|
||||
// ... Create account store w/o initialized memento
|
||||
let memento = {};
|
||||
let as = new AccountStore(memento);
|
||||
|
||||
// ... Create a callback that we can verify was called
|
||||
let updateCallback = new EventVerifierSingle<azdata.Account>();
|
||||
|
||||
// If: I update an account
|
||||
as.update(account1.key, updateCallback.eventHandler)
|
||||
.then(result => {
|
||||
// Then:
|
||||
// ... I should get back false (account did not change)
|
||||
assert.ok(!result);
|
||||
|
||||
// ... The memento should have been initialized
|
||||
assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY]));
|
||||
assert.equal(memento[AccountStore.MEMENTO_KEY].length, 0);
|
||||
|
||||
// ... The callback shouldn't have been called
|
||||
updateCallback.assertNotFired();
|
||||
})
|
||||
.then(
|
||||
() => done(),
|
||||
e => done(e)
|
||||
);
|
||||
});
|
||||
|
||||
test('Update - Account does not exist', done => {
|
||||
// Setup: Create account store with initialized memento with accounts
|
||||
let memento = {};
|
||||
memento[AccountStore.MEMENTO_KEY] = [];
|
||||
let as = new AccountStore(memento);
|
||||
|
||||
// ... Create a callback that we can verify was called
|
||||
let updateCallback = new EventVerifierSingle<azdata.Account>();
|
||||
|
||||
// If: I update an account that doesn't exist
|
||||
as.update({ accountId: 'testyTest', providerId: 'cloudyCloud' }, updateCallback.eventHandler)
|
||||
.then(result => {
|
||||
// Then:
|
||||
// ... I should get back false (account did not change)
|
||||
assert.ok(!result);
|
||||
|
||||
// ... The memento should still be empty
|
||||
assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY]));
|
||||
assert.equal(memento[AccountStore.MEMENTO_KEY].length, 0);
|
||||
|
||||
// ... The callback shouldn't have been called
|
||||
updateCallback.assertNotFired();
|
||||
})
|
||||
.then(
|
||||
() => done(),
|
||||
e => done(e)
|
||||
);
|
||||
});
|
||||
|
||||
test('Update - Account exists', done => {
|
||||
// Setup: Create account store with initialized memento with accounts
|
||||
let memento = getTestMemento();
|
||||
let as = new AccountStore(memento);
|
||||
|
||||
// ... Create a callback to update the account
|
||||
let newDisplayName = 'Display Name Changed!';
|
||||
let updateCallback = (arg: azdata.Account) => {
|
||||
arg.displayInfo.displayName = newDisplayName;
|
||||
};
|
||||
|
||||
// If: I update an account that exists
|
||||
as.update(account1.key, updateCallback)
|
||||
.then(result => {
|
||||
// Then:
|
||||
// ... I should get back true (account did change)
|
||||
assert.ok(result);
|
||||
|
||||
// ... The memento still contains two accounts
|
||||
assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY]));
|
||||
assert.equal(memento[AccountStore.MEMENTO_KEY].length, 2);
|
||||
|
||||
// ... Account 1 should have been updated
|
||||
assert.equal(memento[AccountStore.MEMENTO_KEY][0].displayInfo.displayName, newDisplayName);
|
||||
|
||||
// ... Account 2 should have stayed the same
|
||||
assertAccountEqual(memento[AccountStore.MEMENTO_KEY][1], account2);
|
||||
})
|
||||
.then(
|
||||
() => done(),
|
||||
e => done(e)
|
||||
);
|
||||
});
|
||||
|
||||
// TODO: Test to make sure operations occur sequentially
|
||||
});
|
||||
|
||||
// TODO: Reinstate contextual logo once UI changes are checked in
|
||||
const account1 = <azdata.Account>{
|
||||
key: {
|
||||
providerId: 'azure',
|
||||
accountId: 'testAccount1'
|
||||
},
|
||||
displayInfo: {
|
||||
displayName: 'Test Account 1',
|
||||
accountType: 'test',
|
||||
contextualDisplayName: 'Azure Account'
|
||||
},
|
||||
isStale: false
|
||||
};
|
||||
|
||||
const account2 = <azdata.Account>{
|
||||
key: {
|
||||
providerId: 'azure',
|
||||
accountId: 'testAccount2'
|
||||
},
|
||||
displayInfo: {
|
||||
displayName: 'Test Account 2',
|
||||
accountType: 'test',
|
||||
contextualDisplayName: 'Azure Account'
|
||||
},
|
||||
isStale: false
|
||||
};
|
||||
|
||||
function getTestMemento() {
|
||||
let memento = {};
|
||||
memento[AccountStore.MEMENTO_KEY] = [account1, account2];
|
||||
|
||||
return memento;
|
||||
}
|
||||
|
||||
function assertAccountEqual(a: azdata.Account, b: azdata.Account) {
|
||||
assert.equal(a.key.providerId, b.key.providerId);
|
||||
assert.equal(a.key.accountId, b.key.accountId);
|
||||
|
||||
assert.equal(a.displayInfo.contextualDisplayName, b.displayInfo.contextualDisplayName);
|
||||
assert.equal(a.displayInfo.accountType, b.displayInfo.accountType);
|
||||
assert.equal(a.displayInfo.displayName, b.displayInfo.displayName);
|
||||
|
||||
assert.equal(a.isStale, b.isStale);
|
||||
}
|
||||
224
src/sql/platform/accounts/test/common/accountViewModel.test.ts
Normal file
224
src/sql/platform/accounts/test/common/accountViewModel.test.ts
Normal file
@@ -0,0 +1,224 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as azdata from 'azdata';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import { EventVerifierSingle } from 'sqltest/utils/eventVerifier';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { AccountViewModel } from 'sql/platform/accounts/common/accountViewModel';
|
||||
import { AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/platform/accounts/common/eventTypes';
|
||||
import { TestAccountManagementService } from 'sql/platform/accounts/test/common/testAccountManagementService';
|
||||
|
||||
// SUITE STATE /////////////////////////////////////////////////////////////
|
||||
let mockAddProviderEmitter: Emitter<AccountProviderAddedEventParams>;
|
||||
let mockRemoveProviderEmitter: Emitter<azdata.AccountProviderMetadata>;
|
||||
let mockUpdateAccountEmitter: Emitter<UpdateAccountListEventParams>;
|
||||
|
||||
let providers: azdata.AccountProviderMetadata[];
|
||||
let accounts: azdata.Account[];
|
||||
|
||||
suite('Account Management Dialog ViewModel Tests', () => {
|
||||
|
||||
suiteSetup(() => {
|
||||
providers = [{
|
||||
id: 'azure',
|
||||
displayName: 'Azure'
|
||||
}];
|
||||
|
||||
let account1 = {
|
||||
key: { providerId: 'azure', accountId: 'account1' },
|
||||
name: 'Account 1',
|
||||
displayInfo: {
|
||||
contextualDisplayName: 'Microsoft Account',
|
||||
accountType: 'microsoft',
|
||||
displayName: 'Account 1',
|
||||
userId: 'user@email.com'
|
||||
},
|
||||
properties: [],
|
||||
isStale: false
|
||||
};
|
||||
let account2 = {
|
||||
key: { providerId: 'azure', accountId: 'account2' },
|
||||
name: 'Account 2',
|
||||
displayInfo: {
|
||||
contextualDisplayName: 'Work/School Account',
|
||||
accountType: 'work_school',
|
||||
displayName: 'Account 2',
|
||||
userId: 'user@email.com'
|
||||
},
|
||||
properties: [],
|
||||
isStale: true
|
||||
};
|
||||
accounts = [account1, account2];
|
||||
|
||||
// Setup event mocks for the account management service
|
||||
mockAddProviderEmitter = new Emitter<AccountProviderAddedEventParams>();
|
||||
mockRemoveProviderEmitter = new Emitter<azdata.AccountProviderMetadata>();
|
||||
mockUpdateAccountEmitter = new Emitter<UpdateAccountListEventParams>();
|
||||
});
|
||||
|
||||
test('Construction - Events are properly defined', () => {
|
||||
// If: I create an account viewmodel
|
||||
let mockAccountManagementService = getMockAccountManagementService(false, false);
|
||||
let vm = new AccountViewModel(mockAccountManagementService.object);
|
||||
|
||||
// Then:
|
||||
// ... All the events for the view models should be properly initialized
|
||||
assert.notEqual(vm.addProviderEvent, undefined);
|
||||
assert.notEqual(vm.removeProviderEvent, undefined);
|
||||
assert.notEqual(vm.updateAccountListEvent, undefined);
|
||||
|
||||
// ... All the events should properly fire
|
||||
let argAddProvider: AccountProviderAddedEventParams = { addedProvider: providers[0], initialAccounts: [] };
|
||||
let evAddProvider = new EventVerifierSingle<AccountProviderAddedEventParams>();
|
||||
vm.addProviderEvent(evAddProvider.eventHandler);
|
||||
mockAddProviderEmitter.fire(argAddProvider);
|
||||
evAddProvider.assertFired(argAddProvider);
|
||||
|
||||
let argRemoveProvider = providers[0];
|
||||
let evRemoveProvider = new EventVerifierSingle<azdata.AccountProviderMetadata>();
|
||||
vm.removeProviderEvent(evRemoveProvider.eventHandler);
|
||||
mockRemoveProviderEmitter.fire(argRemoveProvider);
|
||||
evRemoveProvider.assertFired(argRemoveProvider);
|
||||
|
||||
let argUpdateAccounts: UpdateAccountListEventParams = { providerId: providers[0].id, accountList: accounts };
|
||||
let evUpdateAccounts = new EventVerifierSingle<UpdateAccountListEventParams>();
|
||||
vm.updateAccountListEvent(evUpdateAccounts.eventHandler);
|
||||
mockUpdateAccountEmitter.fire(argUpdateAccounts);
|
||||
evUpdateAccounts.assertFired(argUpdateAccounts);
|
||||
});
|
||||
|
||||
test('Initialize - Success', done => {
|
||||
// Setup: Create a viewmodel with event handlers
|
||||
let mockAccountManagementService = getMockAccountManagementService(true, true);
|
||||
let evAddProvider = new EventVerifierSingle<AccountProviderAddedEventParams>();
|
||||
let evRemoveProvider = new EventVerifierSingle<azdata.AccountProviderMetadata>();
|
||||
let evUpdateAccounts = new EventVerifierSingle<UpdateAccountListEventParams>();
|
||||
let vm = getViewModel(mockAccountManagementService.object, evAddProvider, evRemoveProvider, evUpdateAccounts);
|
||||
|
||||
// If: I initialize the view model
|
||||
vm.initialize()
|
||||
.then(results => {
|
||||
// Then:
|
||||
// ... None of the events should have fired
|
||||
assertNoEventsFired(evAddProvider, evRemoveProvider, evUpdateAccounts);
|
||||
|
||||
// ... The account management service should have been called
|
||||
mockAccountManagementService.verify(x => x.getAccountProviderMetadata(), TypeMoq.Times.once());
|
||||
mockAccountManagementService.verify(x => x.getAccountsForProvider(TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
|
||||
// ... The results that were returned should be an array of account provider added event params
|
||||
assert.ok(Array.isArray(results));
|
||||
assert.equal(results.length, 1);
|
||||
assert.equal(results[0].addedProvider, providers[0]);
|
||||
assert.equal(results[0].initialAccounts, accounts);
|
||||
}).then(
|
||||
() => done(),
|
||||
err => done(err)
|
||||
);
|
||||
});
|
||||
|
||||
test('Initialize - Get providers fails', done => {
|
||||
// Setup: Create a mock account management service that rejects looking up providers
|
||||
let mockAccountManagementService = getMockAccountManagementService(false, true);
|
||||
let evAddProvider = new EventVerifierSingle<AccountProviderAddedEventParams>();
|
||||
let evRemoveProvider = new EventVerifierSingle<azdata.AccountProviderMetadata>();
|
||||
let evUpdateAccounts = new EventVerifierSingle<UpdateAccountListEventParams>();
|
||||
let vm = getViewModel(mockAccountManagementService.object, evAddProvider, evRemoveProvider, evUpdateAccounts);
|
||||
|
||||
// If: I initialize the view model
|
||||
vm.initialize()
|
||||
.then(results => {
|
||||
// Then
|
||||
// ... None of the events should have fired
|
||||
assertNoEventsFired(evAddProvider, evRemoveProvider, evUpdateAccounts);
|
||||
|
||||
// ... The account management service should have been called for providers, but not accounts
|
||||
mockAccountManagementService.verify(x => x.getAccountProviderMetadata(), TypeMoq.Times.once());
|
||||
mockAccountManagementService.verify(x => x.getAccountsForProvider(TypeMoq.It.isAny()), TypeMoq.Times.never());
|
||||
|
||||
// ... The results that were returned should be an empty array
|
||||
assert.ok(Array.isArray(results));
|
||||
assert.equal(results.length, 0);
|
||||
})
|
||||
.then(
|
||||
() => done(),
|
||||
err => done(err)
|
||||
);
|
||||
});
|
||||
|
||||
test('Initialize - Get accounts fails', done => {
|
||||
// Setup: Create a mock account management service that rejects the promise
|
||||
let mockAccountManagementService = getMockAccountManagementService(true, false);
|
||||
let evAddProvider = new EventVerifierSingle<AccountProviderAddedEventParams>();
|
||||
let evRemoveProvider = new EventVerifierSingle<azdata.AccountProviderMetadata>();
|
||||
let evUpdateAccounts = new EventVerifierSingle<UpdateAccountListEventParams>();
|
||||
let vm = getViewModel(mockAccountManagementService.object, evAddProvider, evRemoveProvider, evUpdateAccounts);
|
||||
|
||||
// If: I initialize the view model
|
||||
vm.initialize()
|
||||
.then(result => {
|
||||
// Then:
|
||||
// ... None of the events should have fired
|
||||
assertNoEventsFired(evAddProvider, evRemoveProvider, evUpdateAccounts);
|
||||
|
||||
// ... The account management service should have been called
|
||||
mockAccountManagementService.verify(x => x.getAccountProviderMetadata(), TypeMoq.Times.once());
|
||||
mockAccountManagementService.verify(x => x.getAccountsForProvider(TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
|
||||
// ... The results should include the provider
|
||||
assert.ok(Array.isArray(result));
|
||||
assert.equal(result.length, 1);
|
||||
assert.equal(result[0].addedProvider, providers[0]);
|
||||
assert.equal(result[0].initialAccounts, accounts);
|
||||
}).then(
|
||||
() => done(),
|
||||
err => done()
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
function getMockAccountManagementService(resolveProviders: boolean, resolveAccounts: boolean): TypeMoq.Mock<TestAccountManagementService> {
|
||||
let mockAccountManagementService = TypeMoq.Mock.ofType(TestAccountManagementService);
|
||||
|
||||
mockAccountManagementService.setup(x => x.getAccountProviderMetadata())
|
||||
.returns(() => resolveProviders ? Promise.resolve(providers) : Promise.reject(null).then());
|
||||
mockAccountManagementService.setup(x => x.getAccountsForProvider(TypeMoq.It.isAny()))
|
||||
.returns(() => resolveAccounts ? Promise.resolve(accounts) : Promise.reject(null).then());
|
||||
|
||||
mockAccountManagementService.setup(x => x.addAccountProviderEvent)
|
||||
.returns(() => mockAddProviderEmitter.event);
|
||||
mockAccountManagementService.setup(x => x.removeAccountProviderEvent)
|
||||
.returns(() => mockRemoveProviderEmitter.event);
|
||||
mockAccountManagementService.setup(x => x.updateAccountListEvent)
|
||||
.returns(() => mockUpdateAccountEmitter.event);
|
||||
|
||||
return mockAccountManagementService;
|
||||
}
|
||||
|
||||
function getViewModel(
|
||||
ams: TestAccountManagementService,
|
||||
evAdd: EventVerifierSingle<AccountProviderAddedEventParams>,
|
||||
evRemove: EventVerifierSingle<azdata.AccountProviderMetadata>,
|
||||
evUpdate: EventVerifierSingle<UpdateAccountListEventParams>
|
||||
): AccountViewModel {
|
||||
let vm = new AccountViewModel(ams);
|
||||
vm.addProviderEvent(evAdd.eventHandler);
|
||||
vm.removeProviderEvent(evRemove.eventHandler);
|
||||
vm.updateAccountListEvent(evUpdate.eventHandler);
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
function assertNoEventsFired(
|
||||
evAdd: EventVerifierSingle<AccountProviderAddedEventParams>,
|
||||
evRemove: EventVerifierSingle<azdata.AccountProviderMetadata>,
|
||||
evUpdate: EventVerifierSingle<UpdateAccountListEventParams>
|
||||
): void {
|
||||
evAdd.assertNotFired();
|
||||
evRemove.assertNotFired();
|
||||
evUpdate.assertNotFired();
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { FirewallRuleViewModel } from 'sql/platform/accounts/common/firewallRuleViewModel';
|
||||
|
||||
suite('Firewall rule view model tests', () => {
|
||||
let viewModel: FirewallRuleViewModel;
|
||||
|
||||
setup(() => {
|
||||
viewModel = new FirewallRuleViewModel();
|
||||
});
|
||||
|
||||
test('update default values to 250.222.155.198 should calculate the correct default subnet IP range', () => {
|
||||
let IPAddress = '250.222.155.198';
|
||||
viewModel.updateDefaultValues(IPAddress);
|
||||
assert.equal(IPAddress, viewModel.defaultIPAddress);
|
||||
assert.equal('250.222.155.0', viewModel.defaultFromSubnetIPRange);
|
||||
assert.equal('250.222.155.255', viewModel.defaultToSubnetIPRange);
|
||||
});
|
||||
|
||||
test('update default values to 250.222.155.0 should calculate the correct default subnet IP range', () => {
|
||||
let IPAddress = '250.222.155.2';
|
||||
viewModel.updateDefaultValues(IPAddress);
|
||||
assert.equal(IPAddress, viewModel.defaultIPAddress);
|
||||
assert.equal('250.222.155.0', viewModel.defaultFromSubnetIPRange);
|
||||
assert.equal('250.222.155.255', viewModel.defaultToSubnetIPRange);
|
||||
});
|
||||
|
||||
test('subnet IP range should return the correct values', () => {
|
||||
let IPAddress = '250.222.155.198';
|
||||
viewModel.updateDefaultValues(IPAddress);
|
||||
assert.equal('250.222.155.0', viewModel.fromSubnetIPRange);
|
||||
assert.equal('250.222.155.255', viewModel.toSubnetIPRange);
|
||||
|
||||
viewModel.fromSubnetIPRange = '250.222.155.100';
|
||||
viewModel.toSubnetIPRange = '250.222.155.220';
|
||||
assert.equal('250.222.155.100', viewModel.fromSubnetIPRange);
|
||||
assert.equal('250.222.155.220', viewModel.toSubnetIPRange);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,103 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IAccountManagementService } from 'sql/platform/accounts/common/interfaces';
|
||||
import { AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/platform/accounts/common/eventTypes';
|
||||
|
||||
export class TestAccountManagementService implements IAccountManagementService {
|
||||
_serviceBrand: any;
|
||||
|
||||
public get addAccountProviderEvent(): Event<AccountProviderAddedEventParams> { return () => { return undefined; }; }
|
||||
public get removeAccountProviderEvent(): Event<azdata.AccountProviderMetadata> { return () => { return undefined; }; }
|
||||
public get updateAccountListEvent(): Event<UpdateAccountListEventParams> { return () => { return undefined; }; }
|
||||
|
||||
accountUpdated(account: azdata.Account): Thenable<void> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
addAccount(providerId: string): Thenable<void> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
beginAutoOAuthDeviceCode(title: string, message: string, userCode: string, uri: string): Thenable<void> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
cancelAutoOAuthDeviceCode(providerId: string): void {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
endAutoOAuthDeviceCode(): void {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
copyUserCodeAndOpenBrowser(userCode: string, uri: string): void {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getAccountProviderMetadata(): Thenable<azdata.AccountProviderMetadata[]> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getAccountsForProvider(providerId: string): Thenable<azdata.Account[]> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getSecurityToken(account: azdata.Account, resource: azdata.AzureResource): Thenable<{}> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
removeAccount(accountKey: azdata.AccountKey): Thenable<boolean> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
refreshAccount(account: azdata.Account): Thenable<azdata.Account> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
openAccountListDialog(): Promise<any> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
registerProvider(providerMetadata: azdata.AccountProviderMetadata, provider: azdata.AccountProvider): void {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
shutdown(): void {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
unregisterProvider(providerMetadata: azdata.AccountProviderMetadata): void {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export class AccountProviderStub implements azdata.AccountProvider {
|
||||
autoOAuthCancelled(): Thenable<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
clear(account: azdata.AccountKey): Thenable<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
getSecurityToken(account: azdata.Account, resource: azdata.AzureResource): Thenable<{}> {
|
||||
return Promise.resolve({});
|
||||
}
|
||||
|
||||
initialize(storedAccounts: azdata.Account[]): Thenable<azdata.Account[]> {
|
||||
return Promise.resolve(storedAccounts);
|
||||
}
|
||||
|
||||
prompt(): Thenable<azdata.Account> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
refresh(account: azdata.Account): Thenable<azdata.Account> {
|
||||
return Promise.resolve(account);
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import * as azdata from 'azdata';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/telemetryKeys';
|
||||
import * as TelemetryUtils from 'sql/platform/telemetry/telemetryUtilities';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import * as TelemetryUtils from 'sql/platform/telemetry/common/telemetryUtilities';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IBackupService, TaskExecutionMode } from 'sql/platform/backup/common/backupService';
|
||||
import { invalidProvider } from 'sql/base/common/errors';
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { NgModuleRef, enableProdMode, InjectionToken, Type, PlatformRef, Provider } from '@angular/core';
|
||||
import { NgModuleRef, enableProdMode, PlatformRef, Provider } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import { IInstantiationService, _util } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IEditorInput } from 'vs/workbench/common/editor';
|
||||
import { Trace } from 'vs/platform/instantiation/common/instantiationService';
|
||||
import { values } from 'vs/base/common/map';
|
||||
import { IModuleFactory, IBootstrapParams } from 'sql/platform/bootstrap/common/bootstrapParams';
|
||||
|
||||
const selectorCounter = new Map<string, number>();
|
||||
|
||||
@@ -24,14 +25,6 @@ export function providerIterator(service: IInstantiationService): Provider[] {
|
||||
});
|
||||
}
|
||||
|
||||
export const ISelector = new InjectionToken<string>('selector');
|
||||
|
||||
export const IBootstrapParams = new InjectionToken<IBootstrapParams>('bootstrap_params');
|
||||
export interface IBootstrapParams {
|
||||
}
|
||||
|
||||
export type IModuleFactory<T> = (params: IBootstrapParams, selector: string, service: IInstantiationService) => Type<T>;
|
||||
|
||||
function createUniqueSelector(selector: string): string {
|
||||
let num: number;
|
||||
if (selectorCounter.has(selector)) {
|
||||
@@ -8,15 +8,14 @@ import { Injectable, Inject } from '@angular/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
/* SQL imports */
|
||||
import { IDefaultComponentParams } from 'sql/platform/bootstrap/node/bootstrapParams';
|
||||
import { IBootstrapParams } from 'sql/platform/bootstrap/node/bootstrapService';
|
||||
import { IDefaultComponentParams, IBootstrapParams } from 'sql/platform/bootstrap/common/bootstrapParams';
|
||||
import { IMetadataService } from 'sql/platform/metadata/common/metadataService';
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { ConnectionManagementInfo } from 'sql/platform/connection/common/connectionManagementInfo';
|
||||
import { IAdminService } from 'sql/workbench/services/admin/common/adminService';
|
||||
import { IQueryManagementService } from 'sql/platform/query/common/queryManagement';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { AngularDisposable } from 'sql/base/node/lifecycle';
|
||||
import { AngularDisposable } from 'sql/base/browser/lifecycle';
|
||||
import { ConnectionContextKey } from 'sql/workbench/parts/connection/common/connectionContextKey';
|
||||
|
||||
import { ProviderMetadata, DatabaseInfo, SimpleExecuteResult } from 'azdata';
|
||||
@@ -7,8 +7,8 @@ import { DataService } from 'sql/workbench/parts/grid/services/dataService';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ConnectionContextKey } from 'sql/workbench/parts/connection/common/connectionContextKey';
|
||||
import { IBootstrapParams } from 'sql/platform/bootstrap/node/bootstrapService';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export interface IQueryComponentParams extends IBootstrapParams {
|
||||
dataService: DataService;
|
||||
@@ -39,3 +39,15 @@ export interface ITaskDialogComponentParams extends IBootstrapParams {
|
||||
export interface IQueryPlanParams extends IBootstrapParams {
|
||||
planXml: string;
|
||||
}
|
||||
|
||||
export const ISelector = 'selector';
|
||||
|
||||
export const IBootstrapParams = 'bootstrap_params';
|
||||
export interface IBootstrapParams {
|
||||
}
|
||||
|
||||
export interface Type<T> {
|
||||
new(...args: any[]): T;
|
||||
}
|
||||
|
||||
export type IModuleFactory<T> = (params: IBootstrapParams, selector: string, service: IInstantiationService) => Type<T>;
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
} from '@angular/core';
|
||||
|
||||
import { Dropdown, IDropdownOptions } from 'sql/base/parts/editableDropdown/browser/dropdown';
|
||||
import { AngularDisposable } from 'sql/base/node/lifecycle';
|
||||
import { AngularDisposable } from 'sql/base/browser/lifecycle';
|
||||
import { attachEditableDropdownStyler } from 'sql/platform/theme/common/styler';
|
||||
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
83
src/sql/platform/browser/inputbox/inputBox.component.ts
Normal file
83
src/sql/platform/browser/inputbox/inputBox.component.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import {
|
||||
Component, Inject, forwardRef, ElementRef, OnInit, Input,
|
||||
Output, OnChanges, SimpleChanges, EventEmitter
|
||||
} from '@angular/core';
|
||||
|
||||
import { InputBox as vsInputBox } from 'sql/base/browser/ui/inputBox/inputBox';
|
||||
import { AngularDisposable } from 'sql/base/browser/lifecycle';
|
||||
|
||||
import { attachInputBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
|
||||
@Component({
|
||||
selector: 'input-box',
|
||||
template: ''
|
||||
})
|
||||
export class InputBox extends AngularDisposable implements OnInit, OnChanges {
|
||||
private _inputbox: vsInputBox;
|
||||
|
||||
@Input() min: string;
|
||||
@Input() max: string;
|
||||
@Input() type: string;
|
||||
@Input() placeholder: string;
|
||||
@Input('aria-label') ariaLabel: string;
|
||||
@Input() value: string;
|
||||
|
||||
@Output() onDidChange = new EventEmitter<string | number>();
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
|
||||
@Inject(IThemeService) private themeService: IThemeService,
|
||||
@Inject(IContextViewService) private contextViewService: IContextViewService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this._inputbox = new vsInputBox(this._el.nativeElement, this.contextViewService, {
|
||||
min: this.min,
|
||||
max: this.max,
|
||||
type: this.type,
|
||||
placeholder: this.placeholder,
|
||||
ariaLabel: this.ariaLabel
|
||||
});
|
||||
if (this.value) {
|
||||
this._inputbox.value = this.value;
|
||||
}
|
||||
this._inputbox.onDidChange(e => {
|
||||
switch (this.type) {
|
||||
case 'number':
|
||||
if (e) {
|
||||
this.onDidChange.emit(Number(e));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
this.onDidChange.emit(e);
|
||||
}
|
||||
});
|
||||
this._register(attachInputBoxStyler(this._inputbox, this.themeService));
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (this._inputbox) {
|
||||
if (changes['min']) {
|
||||
this._inputbox.inputElement.min = this.min;
|
||||
}
|
||||
if (changes['max']) {
|
||||
this._inputbox.inputElement.max = this.max;
|
||||
}
|
||||
if (changes['type']) {
|
||||
this._inputbox.inputElement.type = this.type;
|
||||
}
|
||||
if (changes['placeholder']) {
|
||||
this._inputbox.inputElement.placeholder = this.placeholder;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
} from '@angular/core';
|
||||
|
||||
import { SelectBox as vsSelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
|
||||
import { AngularDisposable } from 'sql/base/node/lifecycle';
|
||||
import { AngularDisposable } from 'sql/base/browser/lifecycle';
|
||||
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { ISelectData } from 'vs/base/browser/ui/selectBox/selectBox';
|
||||
@@ -0,0 +1,145 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import { ConnectionManagementInfo } from 'sql/platform/connection/common/connectionManagementInfo';
|
||||
import { ICapabilitiesService, ProviderFeatures } from 'sql/platform/capabilities/common/capabilitiesService';
|
||||
import { ConnectionOptionSpecialType, ServiceOptionType } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
|
||||
|
||||
export class TestCapabilitiesService implements ICapabilitiesService {
|
||||
|
||||
public _serviceBrand: any;
|
||||
|
||||
public capabilities: { [id: string]: ProviderFeatures } = {};
|
||||
|
||||
constructor() {
|
||||
|
||||
let connectionProvider: azdata.ConnectionOption[] = [
|
||||
{
|
||||
name: 'connectionName',
|
||||
displayName: undefined,
|
||||
description: undefined,
|
||||
groupName: undefined,
|
||||
categoryValues: undefined,
|
||||
defaultValue: undefined,
|
||||
isIdentity: true,
|
||||
isRequired: true,
|
||||
specialValueType: ConnectionOptionSpecialType.connectionName,
|
||||
valueType: ServiceOptionType.string
|
||||
},
|
||||
{
|
||||
name: 'serverName',
|
||||
displayName: undefined,
|
||||
description: undefined,
|
||||
groupName: undefined,
|
||||
categoryValues: undefined,
|
||||
defaultValue: undefined,
|
||||
isIdentity: true,
|
||||
isRequired: true,
|
||||
specialValueType: ConnectionOptionSpecialType.serverName,
|
||||
valueType: ServiceOptionType.string
|
||||
},
|
||||
{
|
||||
name: 'databaseName',
|
||||
displayName: undefined,
|
||||
description: undefined,
|
||||
groupName: undefined,
|
||||
categoryValues: undefined,
|
||||
defaultValue: undefined,
|
||||
isIdentity: true,
|
||||
isRequired: true,
|
||||
specialValueType: ConnectionOptionSpecialType.databaseName,
|
||||
valueType: ServiceOptionType.string
|
||||
},
|
||||
{
|
||||
name: 'userName',
|
||||
displayName: undefined,
|
||||
description: undefined,
|
||||
groupName: undefined,
|
||||
categoryValues: undefined,
|
||||
defaultValue: undefined,
|
||||
isIdentity: true,
|
||||
isRequired: true,
|
||||
specialValueType: ConnectionOptionSpecialType.userName,
|
||||
valueType: ServiceOptionType.string
|
||||
},
|
||||
{
|
||||
name: 'authenticationType',
|
||||
displayName: undefined,
|
||||
description: undefined,
|
||||
groupName: undefined,
|
||||
categoryValues: undefined,
|
||||
defaultValue: undefined,
|
||||
isIdentity: true,
|
||||
isRequired: true,
|
||||
specialValueType: ConnectionOptionSpecialType.authType,
|
||||
valueType: ServiceOptionType.string
|
||||
},
|
||||
{
|
||||
name: 'password',
|
||||
displayName: undefined,
|
||||
description: undefined,
|
||||
groupName: undefined,
|
||||
categoryValues: undefined,
|
||||
defaultValue: undefined,
|
||||
isIdentity: true,
|
||||
isRequired: true,
|
||||
specialValueType: ConnectionOptionSpecialType.password,
|
||||
valueType: ServiceOptionType.string
|
||||
}
|
||||
];
|
||||
let msSQLCapabilities = {
|
||||
providerId: mssqlProviderName,
|
||||
displayName: 'MSSQL',
|
||||
connectionOptions: connectionProvider,
|
||||
};
|
||||
this.capabilities[mssqlProviderName] = { connection: msSQLCapabilities };
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a list of registered server capabilities
|
||||
*/
|
||||
public getCapabilities(provider: string): ProviderFeatures {
|
||||
return this.capabilities[provider];
|
||||
}
|
||||
|
||||
public getLegacyCapabilities(provider: string): azdata.DataProtocolServerCapabilities {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
public get providers(): { [id: string]: ProviderFeatures } {
|
||||
return this.capabilities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the capabilities provider and query the provider for its capabilities
|
||||
*/
|
||||
public registerProvider(provider: azdata.CapabilitiesProvider): void {
|
||||
}
|
||||
|
||||
// Event Emitters
|
||||
public get onProviderRegisteredEvent(): Event<azdata.DataProtocolServerCapabilities> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public isFeatureAvailable(featureName: Action, connectionManagementInfo: ConnectionManagementInfo): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
public onCapabilitiesReady(): Promise<void> {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
public fireCapabilitiesRegistered(providerFeatures: ProviderFeatures): void {
|
||||
this._onCapabilitiesRegistered.fire(providerFeatures);
|
||||
}
|
||||
|
||||
private _onCapabilitiesRegistered = new Emitter<ProviderFeatures>();
|
||||
public readonly onCapabilitiesRegistered = this._onCapabilitiesRegistered.event;
|
||||
}
|
||||
@@ -18,10 +18,10 @@ import * as Constants from 'sql/platform/connection/common/constants';
|
||||
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
|
||||
import * as ConnectionContracts from 'sql/workbench/parts/connection/common/connection';
|
||||
import { ConnectionStatusManager } from 'sql/platform/connection/common/connectionStatusManager';
|
||||
import { DashboardInput } from 'sql/workbench/parts/dashboard/dashboardInput';
|
||||
import { DashboardInput } from 'sql/workbench/parts/dashboard/common/dashboardInput';
|
||||
import { ConnectionGlobalStatus } from 'sql/workbench/parts/connection/common/connectionGlobalStatus';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/telemetryKeys';
|
||||
import * as TelemetryUtils from 'sql/platform/telemetry/telemetryUtilities';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import * as TelemetryUtils from 'sql/platform/telemetry/common/telemetryUtilities';
|
||||
import { IResourceProviderService } from 'sql/workbench/services/resourceProvider/common/resourceProviderService';
|
||||
import { IAngularEventingService, AngularEventType } from 'sql/platform/angularEventing/common/angularEventingService';
|
||||
import * as QueryConstants from 'sql/workbench/parts/query/common/constants';
|
||||
|
||||
@@ -12,7 +12,7 @@ import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/platform/co
|
||||
import { IConnectionProfile, IConnectionProfileStore } from 'sql/platform/connection/common/interfaces';
|
||||
import { TestConfigurationService } from 'sql/platform/connection/test/common/testConfigurationService';
|
||||
import { ConnectionOptionSpecialType, ServiceOptionType } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { CapabilitiesTestService } from 'sqltest/stubs/capabilitiesTestService';
|
||||
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { deepClone, deepFreeze } from 'vs/base/common/objects';
|
||||
@@ -119,7 +119,7 @@ suite('ConnectionConfig', () => {
|
||||
]);
|
||||
|
||||
setup(() => {
|
||||
capabilitiesService = TypeMoq.Mock.ofType(CapabilitiesTestService);
|
||||
capabilitiesService = TypeMoq.Mock.ofType(TestCapabilitiesService);
|
||||
capabilities = [];
|
||||
let connectionProvider: azdata.ConnectionProviderOptions = {
|
||||
options: [
|
||||
|
||||
@@ -0,0 +1,994 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { TestConnectionDialogService } from 'sql/workbench/services/connection/test/common/testConnectionDialogService';
|
||||
import { ConnectionManagementService } from 'sql/platform/connection/common/connectionManagementService';
|
||||
import { ConnectionStatusManager } from 'sql/platform/connection/common/connectionStatusManager';
|
||||
import { ConnectionStore } from 'sql/platform/connection/common/connectionStore';
|
||||
import {
|
||||
INewConnectionParams, ConnectionType,
|
||||
IConnectionCompletionOptions, IConnectionResult,
|
||||
RunQueryOnConnectionMode
|
||||
} from 'sql/platform/connection/common/connectionManagement';
|
||||
import * as Constants from 'sql/platform/connection/common/constants';
|
||||
import * as Utils from 'sql/platform/connection/common/utils';
|
||||
import { IHandleFirewallRuleResult } from 'sql/workbench/services/resourceProvider/common/resourceProviderService';
|
||||
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
|
||||
import { TestConnectionProvider } from 'sql/platform/connection/test/common/testConnectionProvider';
|
||||
import { TestResourceProvider } from 'sql/workbench/services/resourceProvider/test/common/testResourceProviderService';
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import { IConnectionProfileGroup, ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
import { TestAccountManagementService } from 'sql/platform/accounts/test/common/testAccountManagementService';
|
||||
import { TestStorageService, TestEnvironmentService, TestEditorService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
|
||||
suite('SQL ConnectionManagementService tests', () => {
|
||||
|
||||
let capabilitiesService: TestCapabilitiesService;
|
||||
let connectionDialogService: TypeMoq.Mock<TestConnectionDialogService>;
|
||||
let connectionStore: TypeMoq.Mock<ConnectionStore>;
|
||||
let workbenchEditorService: TypeMoq.Mock<TestEditorService>;
|
||||
let connectionStatusManager: ConnectionStatusManager;
|
||||
let mssqlConnectionProvider: TypeMoq.Mock<TestConnectionProvider>;
|
||||
let workspaceConfigurationServiceMock: TypeMoq.Mock<TestConfigurationService>;
|
||||
let resourceProviderStubMock: TypeMoq.Mock<TestResourceProvider>;
|
||||
let accountManagementService: TypeMoq.Mock<TestAccountManagementService>;
|
||||
|
||||
let none: void;
|
||||
|
||||
let connectionProfile: IConnectionProfile = {
|
||||
connectionName: 'new name',
|
||||
serverName: 'new server',
|
||||
databaseName: 'database',
|
||||
userName: 'user',
|
||||
password: 'password',
|
||||
authenticationType: 'integrated',
|
||||
savePassword: true,
|
||||
groupFullName: 'g2/g2-2',
|
||||
groupId: 'group id',
|
||||
getOptionsKey: () => { return 'connectionId'; },
|
||||
matches: undefined,
|
||||
providerName: 'MSSQL',
|
||||
options: {},
|
||||
saveProfile: true,
|
||||
id: undefined
|
||||
};
|
||||
let connectionProfileWithEmptySavedPassword: IConnectionProfile =
|
||||
Object.assign({}, connectionProfile, { password: '', serverName: connectionProfile.serverName + 1 });
|
||||
let connectionProfileWithEmptyUnsavedPassword: IConnectionProfile =
|
||||
Object.assign({}, connectionProfile, { password: '', serverName: connectionProfile.serverName + 2, savePassword: false });
|
||||
|
||||
let connectionManagementService: ConnectionManagementService;
|
||||
let configResult: { [key: string]: any } = {};
|
||||
configResult['defaultEngine'] = 'MSSQL';
|
||||
let handleFirewallRuleResult: IHandleFirewallRuleResult;
|
||||
let resolveHandleFirewallRuleDialog: boolean;
|
||||
let isFirewallRuleAdded: boolean;
|
||||
|
||||
setup(() => {
|
||||
|
||||
capabilitiesService = new TestCapabilitiesService();
|
||||
connectionDialogService = TypeMoq.Mock.ofType(TestConnectionDialogService);
|
||||
connectionStore = TypeMoq.Mock.ofType(ConnectionStore, TypeMoq.MockBehavior.Loose, new TestStorageService());
|
||||
workbenchEditorService = TypeMoq.Mock.ofType(TestEditorService);
|
||||
connectionStatusManager = new ConnectionStatusManager(capabilitiesService, new NullLogService(), TestEnvironmentService, new TestNotificationService());
|
||||
mssqlConnectionProvider = TypeMoq.Mock.ofType(TestConnectionProvider);
|
||||
let resourceProviderStub = new TestResourceProvider();
|
||||
resourceProviderStubMock = TypeMoq.Mock.ofInstance(resourceProviderStub);
|
||||
accountManagementService = TypeMoq.Mock.ofType(TestAccountManagementService);
|
||||
let root = new ConnectionProfileGroup(ConnectionProfileGroup.RootGroupName, undefined, ConnectionProfileGroup.RootGroupName, undefined, undefined);
|
||||
root.connections = [ConnectionProfile.fromIConnectionProfile(capabilitiesService, connectionProfile)];
|
||||
|
||||
connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined, undefined)).returns(() => Promise.resolve(none));
|
||||
connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined, TypeMoq.It.isAny())).returns(() => Promise.resolve(none));
|
||||
connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined, undefined, TypeMoq.It.isAny())).returns(() => Promise.resolve(none));
|
||||
connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined, undefined, undefined)).returns(() => Promise.resolve(none));
|
||||
connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined)).returns(() => Promise.resolve(none));
|
||||
connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(none));
|
||||
|
||||
connectionStore.setup(x => x.addRecentConnection(TypeMoq.It.isAny())).returns(() => Promise.resolve());
|
||||
connectionStore.setup(x => x.saveProfile(TypeMoq.It.isAny())).returns(() => Promise.resolve(connectionProfile));
|
||||
workbenchEditorService.setup(x => x.openEditor(undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(undefined));
|
||||
connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is<IConnectionProfile>(
|
||||
c => c.serverName === connectionProfile.serverName))).returns(() => Promise.resolve({ profile: connectionProfile, savedCred: true }));
|
||||
connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is<IConnectionProfile>(
|
||||
c => c.serverName === connectionProfileWithEmptySavedPassword.serverName))).returns(
|
||||
() => Promise.resolve({ profile: connectionProfileWithEmptySavedPassword, savedCred: true }));
|
||||
connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is<IConnectionProfile>(
|
||||
c => c.serverName === connectionProfileWithEmptyUnsavedPassword.serverName))).returns(
|
||||
() => Promise.resolve({ profile: connectionProfileWithEmptyUnsavedPassword, savedCred: false }));
|
||||
connectionStore.setup(x => x.isPasswordRequired(TypeMoq.It.isAny())).returns(() => true);
|
||||
connectionStore.setup(x => x.getConnectionProfileGroups(false, undefined)).returns(() => [root]);
|
||||
connectionStore.setup(x => x.savePassword(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
|
||||
|
||||
mssqlConnectionProvider.setup(x => x.connect(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => undefined);
|
||||
|
||||
// Setup resource provider
|
||||
handleFirewallRuleResult = {
|
||||
canHandleFirewallRule: false,
|
||||
ipAddress: '123.123.123.123',
|
||||
resourceProviderId: 'Azure'
|
||||
};
|
||||
resourceProviderStubMock.setup(x => x.handleFirewallRule(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()))
|
||||
.returns(() => Promise.resolve(handleFirewallRuleResult));
|
||||
|
||||
resolveHandleFirewallRuleDialog = true;
|
||||
isFirewallRuleAdded = true;
|
||||
resourceProviderStubMock.setup(x => x.showFirewallRuleDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()))
|
||||
.returns(() => {
|
||||
if (resolveHandleFirewallRuleDialog) {
|
||||
return isFirewallRuleAdded ? Promise.resolve(true) : Promise.resolve(false);
|
||||
} else {
|
||||
return Promise.reject(null).then();
|
||||
}
|
||||
});
|
||||
|
||||
// Setup configuration to return a config that can be modified later.
|
||||
workspaceConfigurationServiceMock = TypeMoq.Mock.ofType(TestConfigurationService);
|
||||
workspaceConfigurationServiceMock.setup(x => x.getValue(Constants.sqlConfigSectionName))
|
||||
.returns(() => configResult);
|
||||
|
||||
connectionManagementService = createConnectionManagementService();
|
||||
|
||||
connectionManagementService.registerProvider('MSSQL', mssqlConnectionProvider.object);
|
||||
});
|
||||
|
||||
function createConnectionManagementService(): ConnectionManagementService {
|
||||
let connectionManagementService = new ConnectionManagementService(
|
||||
connectionStore.object,
|
||||
undefined,
|
||||
connectionDialogService.object,
|
||||
undefined, // IServerGroupController
|
||||
undefined, // IInstantiationService
|
||||
workbenchEditorService.object,
|
||||
undefined, // ITelemetryService
|
||||
workspaceConfigurationServiceMock.object,
|
||||
capabilitiesService,
|
||||
undefined, // IQuickInputService
|
||||
new TestNotificationService(),
|
||||
resourceProviderStubMock.object,
|
||||
undefined, // IAngularEventingService
|
||||
accountManagementService.object,
|
||||
new NullLogService(), // ILogService
|
||||
undefined, // IStorageService
|
||||
TestEnvironmentService
|
||||
);
|
||||
return connectionManagementService;
|
||||
}
|
||||
|
||||
function verifyShowConnectionDialog(connectionProfile: IConnectionProfile, connectionType: ConnectionType, uri: string, options: boolean, connectionResult?: IConnectionResult, didShow: boolean = true): void {
|
||||
if (connectionProfile) {
|
||||
connectionDialogService.verify(x => x.showDialog(
|
||||
TypeMoq.It.isAny(),
|
||||
TypeMoq.It.is<INewConnectionParams>(p => p.connectionType === connectionType && (uri === undefined || p.input.uri === uri)),
|
||||
TypeMoq.It.is<IConnectionProfile>(c => c !== undefined && c.serverName === connectionProfile.serverName),
|
||||
connectionResult ? TypeMoq.It.is<IConnectionResult>(r => r.errorMessage === connectionResult.errorMessage && r.callStack === connectionResult.callStack) : undefined,
|
||||
options ? TypeMoq.It.isAny() : undefined),
|
||||
didShow ? TypeMoq.Times.once() : TypeMoq.Times.never());
|
||||
|
||||
} else {
|
||||
connectionDialogService.verify(x => x.showDialog(
|
||||
TypeMoq.It.isAny(),
|
||||
TypeMoq.It.is<INewConnectionParams>(p => p.connectionType === connectionType && ((uri === undefined && p.input === undefined) || p.input.uri === uri)),
|
||||
undefined,
|
||||
connectionResult ? TypeMoq.It.is<IConnectionResult>(r => r.errorMessage === connectionResult.errorMessage && r.callStack === connectionResult.callStack) : undefined,
|
||||
options ? TypeMoq.It.isAny() : undefined),
|
||||
didShow ? TypeMoq.Times.once() : TypeMoq.Times.never());
|
||||
}
|
||||
}
|
||||
|
||||
function verifyShowFirewallRuleDialog(connectionProfile: IConnectionProfile, didShow: boolean = true): void {
|
||||
resourceProviderStubMock.verify(x => x.showFirewallRuleDialog(
|
||||
TypeMoq.It.is<IConnectionProfile>(c => c.serverName === connectionProfile.serverName),
|
||||
TypeMoq.It.isAny(),
|
||||
TypeMoq.It.isAny()),
|
||||
didShow ? TypeMoq.Times.once() : TypeMoq.Times.never());
|
||||
}
|
||||
|
||||
function verifyOptions(options?: IConnectionCompletionOptions, fromDialog?: boolean): void {
|
||||
|
||||
if (options) {
|
||||
if (options.saveTheConnection) {
|
||||
connectionStore.verify(x => x.saveProfile(TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
}
|
||||
if (options.showDashboard) {
|
||||
workbenchEditorService.verify(x => x.openEditor(undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
}
|
||||
}
|
||||
|
||||
if (fromDialog !== undefined && !fromDialog) {
|
||||
connectionStore.verify(x => x.addSavedPassword(TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function connect(uri: string, options?: IConnectionCompletionOptions, fromDialog?: boolean, connection?: IConnectionProfile, error?: string, errorCode?: number, errorCallStack?: string): Promise<IConnectionResult> {
|
||||
let connectionToUse = connection ? connection : connectionProfile;
|
||||
return new Promise<IConnectionResult>((resolve, reject) => {
|
||||
let id = connectionToUse.getOptionsKey();
|
||||
let defaultUri = 'connection://' + (id ? id : connectionToUse.serverName + ':' + connectionToUse.databaseName);
|
||||
connectionManagementService.onConnectionRequestSent(() => {
|
||||
let info: azdata.ConnectionInfoSummary = {
|
||||
connectionId: error ? undefined : 'id',
|
||||
connectionSummary: {
|
||||
databaseName: connectionToUse.databaseName,
|
||||
serverName: connectionToUse.serverName,
|
||||
userName: connectionToUse.userName
|
||||
},
|
||||
errorMessage: error,
|
||||
errorNumber: errorCode,
|
||||
messages: errorCallStack,
|
||||
ownerUri: uri ? uri : defaultUri,
|
||||
serverInfo: undefined
|
||||
};
|
||||
connectionManagementService.onConnectionComplete(0, info);
|
||||
});
|
||||
connectionManagementService.cancelConnectionForUri(uri).then(() => {
|
||||
if (fromDialog) {
|
||||
resolve(connectionManagementService.connectAndSaveProfile(connectionToUse, uri, options));
|
||||
} else {
|
||||
resolve(connectionManagementService.connect(connectionToUse, uri, options));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
test('showConnectionDialog should open the dialog with default type given no parameters', done => {
|
||||
connectionManagementService.showConnectionDialog().then(() => {
|
||||
verifyShowConnectionDialog(undefined, ConnectionType.default, undefined, false);
|
||||
done();
|
||||
}).catch(err => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('showConnectionDialog should open the dialog with given type given valid input', done => {
|
||||
let params: INewConnectionParams = {
|
||||
connectionType: ConnectionType.editor,
|
||||
input: {
|
||||
onConnectReject: undefined,
|
||||
onConnectStart: undefined,
|
||||
onDisconnect: undefined,
|
||||
onConnectSuccess: undefined,
|
||||
onConnectCanceled: undefined,
|
||||
uri: 'Editor Uri'
|
||||
},
|
||||
runQueryOnCompletion: RunQueryOnConnectionMode.executeQuery
|
||||
};
|
||||
connectionManagementService.showConnectionDialog(params).then(() => {
|
||||
verifyShowConnectionDialog(undefined, params.connectionType, params.input.uri, false);
|
||||
done();
|
||||
}).catch(err => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('showConnectionDialog should pass the model to the dialog if there is a model assigned to the uri', done => {
|
||||
let params: INewConnectionParams = {
|
||||
connectionType: ConnectionType.editor,
|
||||
input: {
|
||||
onConnectReject: undefined,
|
||||
onConnectStart: undefined,
|
||||
onDisconnect: undefined,
|
||||
onConnectSuccess: undefined,
|
||||
onConnectCanceled: undefined,
|
||||
uri: 'Editor Uri'
|
||||
},
|
||||
runQueryOnCompletion: RunQueryOnConnectionMode.executeQuery
|
||||
};
|
||||
|
||||
connect(params.input.uri).then(() => {
|
||||
let saveConnection = connectionManagementService.getConnectionProfile(params.input.uri);
|
||||
|
||||
assert.notEqual(saveConnection, undefined, `profile was not added to the connections`);
|
||||
assert.equal(saveConnection.serverName, connectionProfile.serverName, `Server names are different`);
|
||||
connectionManagementService.showConnectionDialog(params).then(() => {
|
||||
verifyShowConnectionDialog(connectionProfile, params.connectionType, params.input.uri, false);
|
||||
done();
|
||||
}).catch(err => {
|
||||
done(err);
|
||||
});
|
||||
}, err => done(err));
|
||||
});
|
||||
|
||||
test('connect should save profile given options with saveProfile set to true', done => {
|
||||
let uri: string = 'Editor Uri';
|
||||
let options: IConnectionCompletionOptions = {
|
||||
params: undefined,
|
||||
saveTheConnection: true,
|
||||
showDashboard: false,
|
||||
showConnectionDialogOnError: false,
|
||||
showFirewallRuleOnError: true
|
||||
};
|
||||
|
||||
connect(uri, options).then(() => {
|
||||
verifyOptions(options);
|
||||
done();
|
||||
}).catch(err => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('getDefaultProviderId is MSSQL', done => {
|
||||
let defaultProvider = connectionManagementService.getDefaultProviderId();
|
||||
assert.equal(defaultProvider, 'MSSQL', `Default provider is not equal to MSSQL`);
|
||||
done();
|
||||
});
|
||||
|
||||
/* Andresse 10/5/17 commented this test out since it was only working before my changes by the chance of how Promises work
|
||||
If we want to continue to test this, the connection logic needs to be rewritten to actually wait for everything to be done before it resolves */
|
||||
// test('connect should show dashboard given options with showDashboard set to true', done => {
|
||||
// let uri: string = 'Editor Uri';
|
||||
// let options: IConnectionCompletionOptions = {
|
||||
// params: undefined,
|
||||
// saveTheConnection: false,
|
||||
// showDashboard: true,
|
||||
// showConnectionDialogOnError: false
|
||||
// };
|
||||
|
||||
// connect(uri, options).then(() => {
|
||||
// verifyOptions(options);
|
||||
// done();
|
||||
// }).catch(err => {
|
||||
// done(err);
|
||||
// });
|
||||
// });
|
||||
|
||||
test('connect should pass the params in options to onConnectSuccess callback', done => {
|
||||
let uri: string = 'Editor Uri';
|
||||
let paramsInOnConnectSuccess: INewConnectionParams;
|
||||
let options: IConnectionCompletionOptions = {
|
||||
params: {
|
||||
connectionType: ConnectionType.editor,
|
||||
input: {
|
||||
onConnectSuccess: (params?: INewConnectionParams) => {
|
||||
paramsInOnConnectSuccess = params;
|
||||
},
|
||||
onConnectReject: undefined,
|
||||
onConnectStart: undefined,
|
||||
onDisconnect: undefined,
|
||||
onConnectCanceled: undefined,
|
||||
uri: uri
|
||||
},
|
||||
querySelection: undefined,
|
||||
runQueryOnCompletion: RunQueryOnConnectionMode.none
|
||||
},
|
||||
saveTheConnection: true,
|
||||
showDashboard: false,
|
||||
showConnectionDialogOnError: true,
|
||||
showFirewallRuleOnError: true
|
||||
};
|
||||
|
||||
connect(uri, options).then(() => {
|
||||
verifyOptions(options);
|
||||
assert.notEqual(paramsInOnConnectSuccess, undefined);
|
||||
assert.equal(paramsInOnConnectSuccess.connectionType, options.params.connectionType);
|
||||
done();
|
||||
}).catch(err => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('connectAndSaveProfile should show not load the password', done => {
|
||||
let uri: string = 'Editor Uri';
|
||||
let options: IConnectionCompletionOptions = undefined;
|
||||
|
||||
connect(uri, options, true).then(() => {
|
||||
verifyOptions(options, true);
|
||||
done();
|
||||
}).catch(err => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('connect with undefined uri and options should connect using the default uri', done => {
|
||||
let uri = undefined;
|
||||
let options: IConnectionCompletionOptions = undefined;
|
||||
|
||||
connect(uri, options).then(() => {
|
||||
assert.equal(connectionManagementService.isProfileConnected(connectionProfile), true);
|
||||
done();
|
||||
}).catch(err => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('failed connection should open the dialog if connection fails', done => {
|
||||
let uri = undefined;
|
||||
let error: string = 'error';
|
||||
let errorCode: number = 111;
|
||||
let errorCallStack: string = 'error call stack';
|
||||
let expectedConnection: boolean = false;
|
||||
let options: IConnectionCompletionOptions = {
|
||||
params: undefined,
|
||||
saveTheConnection: false,
|
||||
showDashboard: false,
|
||||
showConnectionDialogOnError: true,
|
||||
showFirewallRuleOnError: true
|
||||
};
|
||||
|
||||
let connectionResult: IConnectionResult = {
|
||||
connected: expectedConnection,
|
||||
errorMessage: error,
|
||||
errorCode: errorCode,
|
||||
callStack: errorCallStack
|
||||
};
|
||||
|
||||
connect(uri, options, false, connectionProfile, error, errorCode, errorCallStack).then(result => {
|
||||
assert.equal(result.connected, expectedConnection);
|
||||
assert.equal(result.errorMessage, connectionResult.errorMessage);
|
||||
verifyShowFirewallRuleDialog(connectionProfile, false);
|
||||
verifyShowConnectionDialog(connectionProfile, ConnectionType.default, uri, true, connectionResult);
|
||||
done();
|
||||
}).catch(err => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('failed connection should not open the dialog if the option is set to false even if connection fails', done => {
|
||||
let uri = undefined;
|
||||
let error: string = 'error when options set to false';
|
||||
let errorCode: number = 111;
|
||||
let errorCallStack: string = 'error call stack';
|
||||
let expectedConnection: boolean = false;
|
||||
let options: IConnectionCompletionOptions = {
|
||||
params: undefined,
|
||||
saveTheConnection: false,
|
||||
showDashboard: false,
|
||||
showConnectionDialogOnError: false,
|
||||
showFirewallRuleOnError: true
|
||||
};
|
||||
|
||||
let connectionResult: IConnectionResult = {
|
||||
connected: expectedConnection,
|
||||
errorMessage: error,
|
||||
errorCode: errorCode,
|
||||
callStack: errorCallStack
|
||||
};
|
||||
|
||||
connect(uri, options, false, connectionProfile, error, errorCode, errorCallStack).then(result => {
|
||||
assert.equal(result.connected, expectedConnection);
|
||||
assert.equal(result.errorMessage, connectionResult.errorMessage);
|
||||
verifyShowFirewallRuleDialog(connectionProfile, false);
|
||||
verifyShowConnectionDialog(connectionProfile, ConnectionType.default, uri, true, connectionResult, false);
|
||||
done();
|
||||
}).catch(err => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('failed firewall rule should open the firewall rule dialog', done => {
|
||||
handleFirewallRuleResult.canHandleFirewallRule = true;
|
||||
resolveHandleFirewallRuleDialog = true;
|
||||
isFirewallRuleAdded = true;
|
||||
|
||||
let uri = undefined;
|
||||
let error: string = 'error';
|
||||
let errorCode: number = 111;
|
||||
let expectedConnection: boolean = false;
|
||||
let expectedError: string = error;
|
||||
let options: IConnectionCompletionOptions = {
|
||||
params: undefined,
|
||||
saveTheConnection: false,
|
||||
showDashboard: false,
|
||||
showConnectionDialogOnError: true,
|
||||
showFirewallRuleOnError: true
|
||||
};
|
||||
|
||||
connect(uri, options, false, connectionProfile, error, errorCode).then(result => {
|
||||
assert.equal(result.connected, expectedConnection);
|
||||
assert.equal(result.errorMessage, expectedError);
|
||||
verifyShowFirewallRuleDialog(connectionProfile, true);
|
||||
done();
|
||||
}).catch(err => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('failed firewall rule connection should not open the firewall rule dialog if the option is set to false even if connection fails', done => {
|
||||
handleFirewallRuleResult.canHandleFirewallRule = true;
|
||||
resolveHandleFirewallRuleDialog = true;
|
||||
isFirewallRuleAdded = true;
|
||||
|
||||
let uri = undefined;
|
||||
let error: string = 'error when options set to false';
|
||||
let errorCallStack: string = 'error call stack';
|
||||
let errorCode: number = 111;
|
||||
let expectedConnection: boolean = false;
|
||||
let options: IConnectionCompletionOptions = {
|
||||
params: undefined,
|
||||
saveTheConnection: false,
|
||||
showDashboard: false,
|
||||
showConnectionDialogOnError: false,
|
||||
showFirewallRuleOnError: false
|
||||
};
|
||||
|
||||
let connectionResult: IConnectionResult = {
|
||||
connected: expectedConnection,
|
||||
errorMessage: error,
|
||||
errorCode: errorCode,
|
||||
callStack: errorCallStack
|
||||
};
|
||||
|
||||
connect(uri, options, false, connectionProfile, error, errorCode, errorCallStack).then(result => {
|
||||
assert.equal(result.connected, expectedConnection);
|
||||
assert.equal(result.errorMessage, connectionResult.errorMessage);
|
||||
verifyShowFirewallRuleDialog(connectionProfile, false);
|
||||
verifyShowConnectionDialog(connectionProfile, ConnectionType.default, uri, true, connectionResult, false);
|
||||
done();
|
||||
}).catch(err => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('failed firewall rule connection and failed during open firewall rule should open the firewall rule dialog and connection dialog with error', done => {
|
||||
handleFirewallRuleResult.canHandleFirewallRule = true;
|
||||
resolveHandleFirewallRuleDialog = false;
|
||||
isFirewallRuleAdded = true;
|
||||
|
||||
let uri = undefined;
|
||||
let error: string = 'error when options set to false';
|
||||
let errorCode: number = 111;
|
||||
let errorCallStack: string = 'error call stack';
|
||||
let expectedConnection: boolean = false;
|
||||
let options: IConnectionCompletionOptions = {
|
||||
params: undefined,
|
||||
saveTheConnection: false,
|
||||
showDashboard: false,
|
||||
showConnectionDialogOnError: true,
|
||||
showFirewallRuleOnError: true
|
||||
};
|
||||
|
||||
let connectionResult: IConnectionResult = {
|
||||
connected: expectedConnection,
|
||||
errorMessage: error,
|
||||
errorCode: errorCode,
|
||||
callStack: errorCallStack
|
||||
};
|
||||
|
||||
connect(uri, options, false, connectionProfile, error, errorCode, errorCallStack).then(result => {
|
||||
assert.equal(result.connected, expectedConnection);
|
||||
assert.equal(result.errorMessage, connectionResult.errorMessage);
|
||||
verifyShowFirewallRuleDialog(connectionProfile, true);
|
||||
verifyShowConnectionDialog(connectionProfile, ConnectionType.default, uri, true, connectionResult, true);
|
||||
done();
|
||||
}).catch(err => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('failed firewall rule connection should open the firewall rule dialog. Then canceled firewall rule dialog should not open connection dialog', done => {
|
||||
handleFirewallRuleResult.canHandleFirewallRule = true;
|
||||
resolveHandleFirewallRuleDialog = true;
|
||||
isFirewallRuleAdded = false;
|
||||
|
||||
let uri = undefined;
|
||||
let error: string = 'error when options set to false';
|
||||
let errorCallStack: string = 'error call stack';
|
||||
let errorCode: number = 111;
|
||||
let expectedConnection: boolean = false;
|
||||
let options: IConnectionCompletionOptions = {
|
||||
params: undefined,
|
||||
saveTheConnection: false,
|
||||
showDashboard: false,
|
||||
showConnectionDialogOnError: true,
|
||||
showFirewallRuleOnError: true
|
||||
};
|
||||
|
||||
let connectionResult: IConnectionResult = {
|
||||
connected: expectedConnection,
|
||||
errorMessage: error,
|
||||
errorCode: errorCode,
|
||||
callStack: errorCallStack
|
||||
};
|
||||
|
||||
connect(uri, options, false, connectionProfile, error, errorCode, errorCallStack).then(result => {
|
||||
assert.equal(result.connected, expectedConnection);
|
||||
assert.equal(result.errorMessage, connectionResult.errorMessage);
|
||||
verifyShowFirewallRuleDialog(connectionProfile, true);
|
||||
verifyShowConnectionDialog(connectionProfile, ConnectionType.default, uri, true, connectionResult, false);
|
||||
done();
|
||||
}).catch(err => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('connect when password is empty and unsaved should open the dialog', done => {
|
||||
let uri = undefined;
|
||||
let expectedConnection: boolean = false;
|
||||
let options: IConnectionCompletionOptions = {
|
||||
params: undefined,
|
||||
saveTheConnection: false,
|
||||
showDashboard: false,
|
||||
showConnectionDialogOnError: true,
|
||||
showFirewallRuleOnError: true
|
||||
};
|
||||
|
||||
let connectionResult: IConnectionResult = {
|
||||
connected: expectedConnection,
|
||||
errorMessage: undefined,
|
||||
errorCode: undefined,
|
||||
callStack: undefined
|
||||
};
|
||||
|
||||
connect(uri, options, false, connectionProfileWithEmptyUnsavedPassword).then(result => {
|
||||
assert.equal(result.connected, expectedConnection);
|
||||
assert.equal(result.errorMessage, connectionResult.errorMessage);
|
||||
verifyShowConnectionDialog(connectionProfileWithEmptyUnsavedPassword, ConnectionType.default, uri, true, connectionResult);
|
||||
verifyShowFirewallRuleDialog(connectionProfile, false);
|
||||
done();
|
||||
}).catch(err => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('connect when password is empty and saved should not open the dialog', done => {
|
||||
let uri = undefined;
|
||||
let expectedConnection: boolean = true;
|
||||
let options: IConnectionCompletionOptions = {
|
||||
params: undefined,
|
||||
saveTheConnection: false,
|
||||
showDashboard: false,
|
||||
showConnectionDialogOnError: true,
|
||||
showFirewallRuleOnError: true
|
||||
};
|
||||
|
||||
let connectionResult: IConnectionResult = {
|
||||
connected: expectedConnection,
|
||||
errorMessage: undefined,
|
||||
errorCode: undefined,
|
||||
callStack: undefined
|
||||
};
|
||||
|
||||
connect(uri, options, false, connectionProfileWithEmptySavedPassword).then(result => {
|
||||
assert.equal(result.connected, expectedConnection);
|
||||
assert.equal(result.errorMessage, connectionResult.errorMessage);
|
||||
verifyShowConnectionDialog(connectionProfileWithEmptySavedPassword, ConnectionType.default, uri, true, connectionResult, false);
|
||||
done();
|
||||
}).catch(err => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('connect from editor when empty password when it is required and saved should not open the dialog', done => {
|
||||
let uri = 'editor 3';
|
||||
let expectedConnection: boolean = true;
|
||||
let options: IConnectionCompletionOptions = {
|
||||
params: {
|
||||
connectionType: ConnectionType.editor,
|
||||
input: {
|
||||
onConnectSuccess: undefined,
|
||||
onConnectReject: undefined,
|
||||
onConnectStart: undefined,
|
||||
onDisconnect: undefined,
|
||||
onConnectCanceled: undefined,
|
||||
uri: uri
|
||||
},
|
||||
querySelection: undefined,
|
||||
runQueryOnCompletion: RunQueryOnConnectionMode.none
|
||||
},
|
||||
saveTheConnection: true,
|
||||
showDashboard: false,
|
||||
showConnectionDialogOnError: true,
|
||||
showFirewallRuleOnError: true
|
||||
};
|
||||
|
||||
let connectionResult: IConnectionResult = {
|
||||
connected: expectedConnection,
|
||||
errorMessage: undefined,
|
||||
errorCode: undefined,
|
||||
callStack: undefined
|
||||
};
|
||||
|
||||
connect(uri, options, false, connectionProfileWithEmptySavedPassword).then(result => {
|
||||
assert.equal(result.connected, expectedConnection);
|
||||
assert.equal(result.errorMessage, connectionResult.errorMessage);
|
||||
verifyShowConnectionDialog(connectionProfileWithEmptySavedPassword, ConnectionType.editor, uri, true, connectionResult, false);
|
||||
done();
|
||||
}).catch(err => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('doChangeLanguageFlavor should throw on unknown provider', done => {
|
||||
// given a provider that will never exist
|
||||
let invalidProvider = 'notaprovider';
|
||||
// when I call doChangeLanguageFlavor
|
||||
// Then I expect it to throw
|
||||
assert.throws(() => connectionManagementService.doChangeLanguageFlavor('file://my.sql', 'sql', invalidProvider));
|
||||
done();
|
||||
});
|
||||
|
||||
test('doChangeLanguageFlavor should send event for known provider', done => {
|
||||
// given a provider that is registered
|
||||
let uri = 'file://my.sql';
|
||||
let language = 'sql';
|
||||
let flavor = 'MSSQL';
|
||||
// when I call doChangeLanguageFlavor
|
||||
try {
|
||||
let called = false;
|
||||
connectionManagementService.onLanguageFlavorChanged((changeParams: azdata.DidChangeLanguageFlavorParams) => {
|
||||
called = true;
|
||||
assert.equal(changeParams.uri, uri);
|
||||
assert.equal(changeParams.language, language);
|
||||
assert.equal(changeParams.flavor, flavor);
|
||||
});
|
||||
connectionManagementService.doChangeLanguageFlavor(uri, language, flavor);
|
||||
assert.ok(called, 'expected onLanguageFlavorChanged event to be sent');
|
||||
done();
|
||||
} catch (error) {
|
||||
done(error);
|
||||
}
|
||||
});
|
||||
|
||||
test('ensureDefaultLanguageFlavor should not send event if uri is connected', done => {
|
||||
let uri: string = 'Editor Uri';
|
||||
let options: IConnectionCompletionOptions = {
|
||||
params: undefined,
|
||||
saveTheConnection: false,
|
||||
showDashboard: false,
|
||||
showConnectionDialogOnError: false,
|
||||
showFirewallRuleOnError: true
|
||||
};
|
||||
let connectionManagementService = createConnectionManagementService();
|
||||
let called = false;
|
||||
connectionManagementService.onLanguageFlavorChanged((changeParams: azdata.DidChangeLanguageFlavorParams) => {
|
||||
called = true;
|
||||
});
|
||||
connect(uri, options).then(() => {
|
||||
connectionManagementService.ensureDefaultLanguageFlavor(uri);
|
||||
assert.equal(called, false, 'do not expect flavor change to be called');
|
||||
done();
|
||||
}).catch(err => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('getConnectionId returns the URI associated with a connection that has had its database filled in', done => {
|
||||
// Set up the connection management service with a connection corresponding to a default database
|
||||
let dbName = 'master';
|
||||
let serverName = 'test_server';
|
||||
let userName = 'test_user';
|
||||
let connectionProfileWithoutDb: IConnectionProfile = Object.assign(connectionProfile,
|
||||
{ serverName: serverName, databaseName: '', userName: userName, getOptionsKey: () => undefined });
|
||||
let connectionProfileWithDb: IConnectionProfile = Object.assign(connectionProfileWithoutDb, { databaseName: dbName });
|
||||
// Save the database with a URI that has the database name filled in, to mirror Carbon's behavior
|
||||
let ownerUri = Utils.generateUri(connectionProfileWithDb);
|
||||
connect(ownerUri, undefined, false, connectionProfileWithoutDb).then(() => {
|
||||
try {
|
||||
// If I get the URI for the connection with or without a database from the connection management service
|
||||
let actualUriWithDb = connectionManagementService.getConnectionUri(connectionProfileWithDb);
|
||||
let actualUriWithoutDb = connectionManagementService.getConnectionUri(connectionProfileWithoutDb);
|
||||
|
||||
// Then the retrieved URIs should match the one on the connection
|
||||
let expectedUri = Utils.generateUri(connectionProfileWithoutDb);
|
||||
assert.equal(actualUriWithDb, expectedUri);
|
||||
assert.equal(actualUriWithoutDb, expectedUri);
|
||||
done();
|
||||
} catch (err) {
|
||||
done(err);
|
||||
}
|
||||
}, err => done(err));
|
||||
});
|
||||
|
||||
test('getTabColorForUri returns undefined when there is no connection for the given URI', () => {
|
||||
let connectionManagementService = createConnectionManagementService();
|
||||
let color = connectionManagementService.getTabColorForUri('invalidUri');
|
||||
assert.equal(color, undefined);
|
||||
});
|
||||
|
||||
test('getTabColorForUri returns the group color corresponding to the connection for a URI', done => {
|
||||
// Set up the connection store to give back a group for the expected connection profile
|
||||
configResult['tabColorMode'] = 'border';
|
||||
let expectedColor = 'red';
|
||||
connectionStore.setup(x => x.getGroupFromId(connectionProfile.groupId)).returns(() => <IConnectionProfileGroup>{
|
||||
color: expectedColor
|
||||
});
|
||||
let uri = 'testUri';
|
||||
connect(uri).then(() => {
|
||||
try {
|
||||
let tabColor = connectionManagementService.getTabColorForUri(uri);
|
||||
assert.equal(tabColor, expectedColor);
|
||||
done();
|
||||
} catch (e) {
|
||||
done(e);
|
||||
}
|
||||
}, err => done(err));
|
||||
});
|
||||
|
||||
test('getActiveConnectionCredentials returns the credentials dictionary for a connection profile', () => {
|
||||
let profile = Object.assign({}, connectionProfile);
|
||||
profile.options = { password: profile.password };
|
||||
profile.id = 'test_id';
|
||||
connectionStatusManager.addConnection(profile, 'test_uri');
|
||||
(connectionManagementService as any)._connectionStatusManager = connectionStatusManager;
|
||||
let credentials = connectionManagementService.getActiveConnectionCredentials(profile.id);
|
||||
assert.equal(credentials['password'], profile.options['password']);
|
||||
});
|
||||
|
||||
test('getConnectionUriFromId returns a URI of an active connection with the given id', () => {
|
||||
let profile = Object.assign({}, connectionProfile);
|
||||
profile.options = { password: profile.password };
|
||||
profile.id = 'test_id';
|
||||
let uri = 'test_initial_uri';
|
||||
connectionStatusManager.addConnection(profile, uri);
|
||||
(connectionManagementService as any)._connectionStatusManager = connectionStatusManager;
|
||||
|
||||
// If I call getConnectionUriFromId on the given connection
|
||||
let foundUri = connectionManagementService.getConnectionUriFromId(profile.id);
|
||||
|
||||
// Then the returned URI matches the connection's original URI
|
||||
assert.equal(foundUri, uri);
|
||||
});
|
||||
|
||||
test('getConectionUriFromId returns undefined if the given connection is not active', () => {
|
||||
let profile = Object.assign({}, connectionProfile);
|
||||
profile.options = { password: profile.password };
|
||||
profile.id = 'test_id';
|
||||
connectionStatusManager.addConnection(profile, Utils.generateUri(profile));
|
||||
(connectionManagementService as any)._connectionStatusManager = connectionStatusManager;
|
||||
|
||||
// If I call getConnectionUriFromId with a different URI than the connection's
|
||||
let foundUri = connectionManagementService.getConnectionUriFromId('different_id');
|
||||
|
||||
// Then undefined is returned
|
||||
assert.equal(foundUri, undefined);
|
||||
});
|
||||
|
||||
test('addSavedPassword fills in Azure access tokens for Azure accounts', async () => {
|
||||
// Set up a connection profile that uses Azure
|
||||
let azureConnectionProfile = ConnectionProfile.fromIConnectionProfile(capabilitiesService, connectionProfile);
|
||||
azureConnectionProfile.authenticationType = 'AzureMFA';
|
||||
let username = 'testuser@microsoft.com';
|
||||
azureConnectionProfile.userName = username;
|
||||
let servername = 'test-database.database.windows.net';
|
||||
azureConnectionProfile.serverName = servername;
|
||||
|
||||
// Set up the account management service to return a token for the given user
|
||||
accountManagementService.setup(x => x.getAccountsForProvider(TypeMoq.It.isAny())).returns(providerId => Promise.resolve<azdata.Account[]>([
|
||||
{
|
||||
key: {
|
||||
accountId: username,
|
||||
providerId: providerId
|
||||
},
|
||||
displayInfo: undefined,
|
||||
isStale: false,
|
||||
properties: undefined
|
||||
}
|
||||
]));
|
||||
let testToken = 'testToken';
|
||||
accountManagementService.setup(x => x.getSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve({
|
||||
azurePublicCloud: {
|
||||
token: testToken
|
||||
}
|
||||
}));
|
||||
connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is(profile => profile.authenticationType === 'AzureMFA'))).returns(profile => Promise.resolve({
|
||||
profile: profile,
|
||||
savedCred: false
|
||||
}));
|
||||
|
||||
// If I call addSavedPassword
|
||||
let profileWithCredentials = await connectionManagementService.addSavedPassword(azureConnectionProfile);
|
||||
|
||||
// Then the returned profile has the account token set
|
||||
assert.equal(profileWithCredentials.userName, username);
|
||||
assert.equal(profileWithCredentials.options['azureAccountToken'], testToken);
|
||||
});
|
||||
|
||||
test('addSavedPassword fills in Azure access token for selected tenant', async () => {
|
||||
// Set up a connection profile that uses Azure
|
||||
let azureConnectionProfile = ConnectionProfile.fromIConnectionProfile(capabilitiesService, connectionProfile);
|
||||
azureConnectionProfile.authenticationType = 'AzureMFA';
|
||||
let username = 'testuser@microsoft.com';
|
||||
azureConnectionProfile.userName = username;
|
||||
let servername = 'test-database.database.windows.net';
|
||||
azureConnectionProfile.serverName = servername;
|
||||
let azureTenantId = 'testTenant';
|
||||
azureConnectionProfile.azureTenantId = azureTenantId;
|
||||
|
||||
// Set up the account management service to return a token for the given user
|
||||
accountManagementService.setup(x => x.getAccountsForProvider(TypeMoq.It.isAny())).returns(providerId => Promise.resolve<azdata.Account[]>([
|
||||
{
|
||||
key: {
|
||||
accountId: username,
|
||||
providerId: providerId
|
||||
},
|
||||
displayInfo: undefined,
|
||||
isStale: false,
|
||||
properties: undefined
|
||||
}
|
||||
]));
|
||||
let testToken = 'testToken';
|
||||
let returnedTokens = {};
|
||||
returnedTokens['azurePublicCloud'] = { token: 'badToken' };
|
||||
returnedTokens[azureTenantId] = { token: testToken };
|
||||
accountManagementService.setup(x => x.getSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(returnedTokens));
|
||||
connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is(profile => profile.authenticationType === 'AzureMFA'))).returns(profile => Promise.resolve({
|
||||
profile: profile,
|
||||
savedCred: false
|
||||
}));
|
||||
|
||||
// If I call addSavedPassword
|
||||
let profileWithCredentials = await connectionManagementService.addSavedPassword(azureConnectionProfile);
|
||||
|
||||
// Then the returned profile has the account token set corresponding to the requested tenant
|
||||
assert.equal(profileWithCredentials.userName, username);
|
||||
assert.equal(profileWithCredentials.options['azureAccountToken'], testToken);
|
||||
});
|
||||
|
||||
test('getConnections test', () => {
|
||||
const connectionStatusManagerMock = TypeMoq.Mock.ofType(ConnectionStatusManager, TypeMoq.MockBehavior.Loose);
|
||||
const connectionStoreMock = TypeMoq.Mock.ofType(ConnectionStore, TypeMoq.MockBehavior.Loose, new TestStorageService());
|
||||
|
||||
connectionStatusManagerMock.setup(x => x.getActiveConnectionProfiles(undefined)).returns(() => {
|
||||
return [createConnectionProfile('1'), createConnectionProfile('2')];
|
||||
});
|
||||
connectionStoreMock.setup(x => x.getRecentlyUsedConnections(undefined)).returns(() => {
|
||||
return [createConnectionProfile('1'), createConnectionProfile('3')];
|
||||
});
|
||||
|
||||
const group1 = createConnectionGroup('group1');
|
||||
const group2 = createConnectionGroup('group2');
|
||||
group1.connections = [createConnectionProfile('1'), createConnectionProfile('4')];
|
||||
group1.children = [group2];
|
||||
group2.connections = [createConnectionProfile('5'), createConnectionProfile('6')];
|
||||
connectionStoreMock.setup(x => x.getConnectionProfileGroups(TypeMoq.It.isAny(), undefined)).returns(() => {
|
||||
return [group1];
|
||||
});
|
||||
const connectionManagementService = new ConnectionManagementService(connectionStoreMock.object, connectionStatusManagerMock.object, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
|
||||
|
||||
// dupe connections have been seeded the numbers below already reflected the de-duped results
|
||||
|
||||
const verifyConnections = (actualConnections: ConnectionProfile[], expectedConnectionIds: string[], scenario: string) => {
|
||||
assert.equal(actualConnections.length, expectedConnectionIds.length, 'incorrect number of connections returned, ' + scenario);
|
||||
assert.deepEqual(actualConnections.map(conn => conn.id).sort(), expectedConnectionIds.sort(), 'connections do not match expectation, ' + scenario);
|
||||
};
|
||||
|
||||
// no parameter - default to false
|
||||
let connections = connectionManagementService.getConnections();
|
||||
verifyConnections(connections, ['1', '2', '3', '4', '5', '6'], 'no parameter provided');
|
||||
|
||||
// explicitly set to false
|
||||
connections = connectionManagementService.getConnections(false);
|
||||
verifyConnections(connections, ['1', '2', '3', '4', '5', '6'], 'parameter is false');
|
||||
|
||||
// active connections only
|
||||
connections = connectionManagementService.getConnections(true);
|
||||
verifyConnections(connections, ['1', '2'], 'parameter is true');
|
||||
});
|
||||
});
|
||||
|
||||
function createConnectionProfile(id: string): ConnectionProfile {
|
||||
|
||||
const capabilitiesService = new TestCapabilitiesService();
|
||||
return new ConnectionProfile(capabilitiesService, {
|
||||
connectionName: 'newName',
|
||||
savePassword: false,
|
||||
groupFullName: 'testGroup',
|
||||
serverName: 'testServerName',
|
||||
databaseName: 'testDatabaseName',
|
||||
authenticationType: Constants.integrated,
|
||||
password: 'test',
|
||||
userName: 'testUsername',
|
||||
groupId: undefined,
|
||||
providerName: Constants.mssqlProviderName,
|
||||
options: {},
|
||||
saveProfile: true,
|
||||
id: id
|
||||
});
|
||||
}
|
||||
|
||||
function createConnectionGroup(id: string): ConnectionProfileGroup {
|
||||
return new ConnectionProfileGroup(id, undefined, id, undefined, undefined);
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
import { IConnectionProfile, IConnectionProfileStore } from 'sql/platform/connection/common/interfaces';
|
||||
import * as azdata from 'azdata';
|
||||
import * as assert from 'assert';
|
||||
import { ConnectionOptionSpecialType, ServiceOptionType } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
|
||||
import { ConnectionProviderProperties } from 'sql/workbench/parts/connection/common/connectionProviderExtension';
|
||||
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
|
||||
|
||||
suite('SQL ConnectionProfileInfo tests', () => {
|
||||
let msSQLCapabilities: ConnectionProviderProperties;
|
||||
let capabilitiesService: TestCapabilitiesService;
|
||||
|
||||
let connectionProfile: IConnectionProfile = {
|
||||
connectionName: 'new name',
|
||||
serverName: 'new server',
|
||||
databaseName: 'database',
|
||||
userName: 'user',
|
||||
password: 'password',
|
||||
authenticationType: '',
|
||||
savePassword: true,
|
||||
groupFullName: 'g2/g2-2',
|
||||
groupId: 'group id',
|
||||
getOptionsKey: undefined,
|
||||
matches: undefined,
|
||||
providerName: mssqlProviderName,
|
||||
options: {},
|
||||
saveProfile: true,
|
||||
id: undefined
|
||||
};
|
||||
|
||||
let storedProfile: IConnectionProfileStore = {
|
||||
groupId: 'groupId',
|
||||
id: 'id',
|
||||
options: {
|
||||
connectionName: 'new name',
|
||||
serverName: 'new server',
|
||||
databaseName: 'database',
|
||||
userName: 'user',
|
||||
password: 'password',
|
||||
authenticationType: ''
|
||||
},
|
||||
providerName: mssqlProviderName,
|
||||
savePassword: true
|
||||
};
|
||||
|
||||
setup(() => {
|
||||
let connectionProvider: azdata.ConnectionOption[] = [
|
||||
{
|
||||
name: 'connectionName',
|
||||
displayName: undefined,
|
||||
description: undefined,
|
||||
groupName: undefined,
|
||||
categoryValues: undefined,
|
||||
defaultValue: undefined,
|
||||
isIdentity: true,
|
||||
isRequired: true,
|
||||
specialValueType: ConnectionOptionSpecialType.connectionName,
|
||||
valueType: ServiceOptionType.string
|
||||
},
|
||||
{
|
||||
name: 'serverName',
|
||||
displayName: undefined,
|
||||
description: undefined,
|
||||
groupName: undefined,
|
||||
categoryValues: undefined,
|
||||
defaultValue: undefined,
|
||||
isIdentity: true,
|
||||
isRequired: true,
|
||||
specialValueType: ConnectionOptionSpecialType.serverName,
|
||||
valueType: ServiceOptionType.string
|
||||
},
|
||||
{
|
||||
name: 'databaseName',
|
||||
displayName: undefined,
|
||||
description: undefined,
|
||||
groupName: undefined,
|
||||
categoryValues: undefined,
|
||||
defaultValue: undefined,
|
||||
isIdentity: true,
|
||||
isRequired: true,
|
||||
specialValueType: ConnectionOptionSpecialType.databaseName,
|
||||
valueType: ServiceOptionType.string
|
||||
},
|
||||
{
|
||||
name: 'userName',
|
||||
displayName: undefined,
|
||||
description: undefined,
|
||||
groupName: undefined,
|
||||
categoryValues: undefined,
|
||||
defaultValue: undefined,
|
||||
isIdentity: true,
|
||||
isRequired: true,
|
||||
specialValueType: ConnectionOptionSpecialType.userName,
|
||||
valueType: ServiceOptionType.string
|
||||
},
|
||||
{
|
||||
name: 'authenticationType',
|
||||
displayName: undefined,
|
||||
description: undefined,
|
||||
groupName: undefined,
|
||||
categoryValues: undefined,
|
||||
defaultValue: undefined,
|
||||
isIdentity: true,
|
||||
isRequired: true,
|
||||
specialValueType: ConnectionOptionSpecialType.authType,
|
||||
valueType: ServiceOptionType.string
|
||||
},
|
||||
{
|
||||
name: 'password',
|
||||
displayName: undefined,
|
||||
description: undefined,
|
||||
groupName: undefined,
|
||||
categoryValues: undefined,
|
||||
defaultValue: undefined,
|
||||
isIdentity: true,
|
||||
isRequired: true,
|
||||
specialValueType: ConnectionOptionSpecialType.password,
|
||||
valueType: ServiceOptionType.string
|
||||
}
|
||||
];
|
||||
msSQLCapabilities = {
|
||||
providerId: mssqlProviderName,
|
||||
displayName: 'MSSQL',
|
||||
connectionOptions: connectionProvider
|
||||
};
|
||||
capabilitiesService = new TestCapabilitiesService();
|
||||
capabilitiesService.capabilities[mssqlProviderName] = { connection: msSQLCapabilities };
|
||||
});
|
||||
|
||||
test('set properties should set the values correctly', () => {
|
||||
let conn = new ConnectionProfile(capabilitiesService, undefined);
|
||||
assert.equal(conn.serverName, undefined);
|
||||
conn.connectionName = connectionProfile.connectionName;
|
||||
conn.serverName = connectionProfile.serverName;
|
||||
conn.databaseName = connectionProfile.databaseName;
|
||||
conn.authenticationType = connectionProfile.authenticationType;
|
||||
conn.password = connectionProfile.password;
|
||||
conn.userName = connectionProfile.userName;
|
||||
conn.groupId = connectionProfile.groupId;
|
||||
conn.groupFullName = connectionProfile.groupFullName;
|
||||
conn.savePassword = connectionProfile.savePassword;
|
||||
assert.equal(conn.connectionName, connectionProfile.connectionName);
|
||||
assert.equal(conn.serverName, connectionProfile.serverName);
|
||||
assert.equal(conn.databaseName, connectionProfile.databaseName);
|
||||
assert.equal(conn.authenticationType, connectionProfile.authenticationType);
|
||||
assert.equal(conn.password, connectionProfile.password);
|
||||
assert.equal(conn.userName, connectionProfile.userName);
|
||||
assert.equal(conn.groupId, connectionProfile.groupId);
|
||||
assert.equal(conn.groupFullName, connectionProfile.groupFullName);
|
||||
assert.equal(conn.savePassword, connectionProfile.savePassword);
|
||||
});
|
||||
|
||||
test('constructor should initialize the options given a valid model', () => {
|
||||
let conn = new ConnectionProfile(capabilitiesService, connectionProfile);
|
||||
|
||||
assert.equal(conn.connectionName, connectionProfile.connectionName);
|
||||
assert.equal(conn.serverName, connectionProfile.serverName);
|
||||
assert.equal(conn.databaseName, connectionProfile.databaseName);
|
||||
assert.equal(conn.authenticationType, connectionProfile.authenticationType);
|
||||
assert.equal(conn.password, connectionProfile.password);
|
||||
assert.equal(conn.userName, connectionProfile.userName);
|
||||
assert.equal(conn.groupId, connectionProfile.groupId);
|
||||
assert.equal(conn.groupFullName, connectionProfile.groupFullName);
|
||||
assert.equal(conn.savePassword, connectionProfile.savePassword);
|
||||
});
|
||||
|
||||
test('getOptionsKey should create a valid unique id', () => {
|
||||
let conn = new ConnectionProfile(capabilitiesService, connectionProfile);
|
||||
let expectedId = 'providerName:MSSQL|authenticationType:|databaseName:database|serverName:new server|userName:user|databaseDisplayName:database|group:group id';
|
||||
let id = conn.getOptionsKey();
|
||||
assert.equal(id, expectedId);
|
||||
});
|
||||
|
||||
test('createFromStoredProfile should create connection profile from stored profile', () => {
|
||||
let savedProfile = storedProfile;
|
||||
let connectionProfile = ConnectionProfile.createFromStoredProfile(savedProfile, capabilitiesService);
|
||||
assert.equal(savedProfile.groupId, connectionProfile.groupId);
|
||||
assert.deepEqual(savedProfile.providerName, connectionProfile.providerName);
|
||||
assert.deepEqual(savedProfile.savePassword, connectionProfile.savePassword);
|
||||
assert.deepEqual(savedProfile.id, connectionProfile.id);
|
||||
});
|
||||
|
||||
test('createFromStoredProfile should set the id to new guid if not set in stored profile', () => {
|
||||
let savedProfile = Object.assign({}, storedProfile, { id: undefined });
|
||||
let connectionProfile = ConnectionProfile.createFromStoredProfile(savedProfile, capabilitiesService);
|
||||
assert.equal(savedProfile.groupId, connectionProfile.groupId);
|
||||
assert.deepEqual(savedProfile.providerName, connectionProfile.providerName);
|
||||
assert.equal(savedProfile.savePassword, connectionProfile.savePassword);
|
||||
assert.notEqual(connectionProfile.id, undefined);
|
||||
assert.equal(savedProfile.id, undefined);
|
||||
});
|
||||
|
||||
test('withoutPassword should create a new instance without password', () => {
|
||||
let conn = new ConnectionProfile(capabilitiesService, connectionProfile);
|
||||
assert.notEqual(conn.password, '');
|
||||
let withoutPassword = conn.withoutPassword();
|
||||
assert.equal(withoutPassword.password, '');
|
||||
});
|
||||
|
||||
test('unique id should not include password', () => {
|
||||
let conn = new ConnectionProfile(capabilitiesService, connectionProfile);
|
||||
let withoutPassword = conn.withoutPassword();
|
||||
assert.equal(withoutPassword.getOptionsKey(), conn.getOptionsKey());
|
||||
});
|
||||
|
||||
test('cloneWithDatabase should create new profile with new id', () => {
|
||||
let conn = new ConnectionProfile(capabilitiesService, connectionProfile);
|
||||
let newProfile = conn.cloneWithDatabase('new db');
|
||||
assert.notEqual(newProfile.id, conn.id);
|
||||
assert.equal(newProfile.databaseName, 'new db');
|
||||
});
|
||||
|
||||
test('an empty connection profile does not cause issues', () => {
|
||||
assert.doesNotThrow(() => new ConnectionProfile(capabilitiesService, {} as IConnectionProfile));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,145 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
|
||||
import * as assert from 'assert';
|
||||
|
||||
suite('SQL ConnectionProfileGroup tests', () => {
|
||||
let root: ConnectionProfileGroup;
|
||||
let Groups1 = 'G1';
|
||||
let Groups11 = 'G11';
|
||||
let Groups2 = 'G2';
|
||||
let group1Node: ConnectionProfileGroup;
|
||||
let group11Node: ConnectionProfileGroup;
|
||||
let group2Node: ConnectionProfileGroup;
|
||||
setup(() => {
|
||||
root = new ConnectionProfileGroup(ConnectionProfileGroup.RootGroupName, undefined, ConnectionProfileGroup.RootGroupName, undefined, undefined);
|
||||
|
||||
group1Node = new ConnectionProfileGroup(Groups1, root, Groups1, undefined, undefined);
|
||||
group2Node = new ConnectionProfileGroup(Groups2, root, Groups2, undefined, undefined);
|
||||
group11Node = new ConnectionProfileGroup(Groups11, root, Groups11, undefined, undefined);
|
||||
root.addGroups([group1Node]);
|
||||
group1Node.addGroups([group11Node]);
|
||||
root.addGroups([group2Node]);
|
||||
});
|
||||
|
||||
test('Root name should be returned as empty string', () => {
|
||||
assert.equal(root.name, '');
|
||||
});
|
||||
|
||||
test('Fullname should return the group full name correctly', () => {
|
||||
assert.equal(group1Node.fullName, 'G1');
|
||||
assert.equal(group2Node.fullName, 'G2');
|
||||
assert.equal(group11Node.fullName, 'G1/G11');
|
||||
});
|
||||
|
||||
test('getGroupFullNameParts should return a list With ROOT in it given an empty string', () => {
|
||||
let groupFullName: string = '';
|
||||
let expected: string[] = [ConnectionProfileGroup.RootGroupName];
|
||||
let actual = ConnectionProfileGroup.getGroupFullNameParts(groupFullName);
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('getGroupFullNameParts should return a list With ROOT in it given null', () => {
|
||||
let groupFullName: string = undefined;
|
||||
let expected: string[] = [ConnectionProfileGroup.RootGroupName];
|
||||
let actual = ConnectionProfileGroup.getGroupFullNameParts(groupFullName);
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('getGroupFullNameParts should return a list With ROOT in it given /', () => {
|
||||
let groupFullName: string = '/';
|
||||
let expected: string[] = [ConnectionProfileGroup.RootGroupName];
|
||||
let actual = ConnectionProfileGroup.getGroupFullNameParts(groupFullName);
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('getGroupFullNameParts should add ROOT as first item if not added already and string starts with /', () => {
|
||||
let groupFullName: string = '/Groups/Group1';
|
||||
let expected: string[] = [ConnectionProfileGroup.RootGroupName, 'Groups', 'Group1'];
|
||||
let actual = ConnectionProfileGroup.getGroupFullNameParts(groupFullName);
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('getGroupFullNameParts should add ROOT as first item if not added already', () => {
|
||||
let groupFullName: string = 'Groups/Group1';
|
||||
let expected: string[] = [ConnectionProfileGroup.RootGroupName, 'Groups', 'Group1'];
|
||||
let actual = ConnectionProfileGroup.getGroupFullNameParts(groupFullName);
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('getGroupFullNameParts should not add ROOT if already added and string starts with /', () => {
|
||||
let groupFullName: string = '/ROOT/Groups/Group1';
|
||||
let expected: string[] = [ConnectionProfileGroup.RootGroupName, 'Groups', 'Group1'];
|
||||
let actual = ConnectionProfileGroup.getGroupFullNameParts(groupFullName);
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('getGroupFullNameParts should not add ROOT if already added', () => {
|
||||
let groupFullName: string = 'ROOT/Groups/Group1';
|
||||
let expected: string[] = [ConnectionProfileGroup.RootGroupName, 'Groups', 'Group1'];
|
||||
let actual = ConnectionProfileGroup.getGroupFullNameParts(groupFullName);
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('getGroupFullNameParts should not add ROOT if already added and it is not uppercase', () => {
|
||||
let groupFullName: string = 'rOOT/Groups/Group1';
|
||||
let expected: string[] = [ConnectionProfileGroup.RootGroupName, 'Groups', 'Group1'];
|
||||
let actual = ConnectionProfileGroup.getGroupFullNameParts(groupFullName);
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('isRoot should return true given empty string', () => {
|
||||
let name: string = '';
|
||||
let expected: boolean = true;
|
||||
let actual = ConnectionProfileGroup.isRoot(name);
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('isRoot should return true given null', () => {
|
||||
let name: string = undefined;
|
||||
let expected: boolean = true;
|
||||
let actual = ConnectionProfileGroup.isRoot(name);
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('isRoot should return true given /', () => {
|
||||
let name: string = '/';
|
||||
let expected: boolean = true;
|
||||
let actual = ConnectionProfileGroup.isRoot(name);
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('isRoot should return true given root', () => {
|
||||
let name: string = 'root';
|
||||
let expected: boolean = true;
|
||||
let actual = ConnectionProfileGroup.isRoot(name);
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('sameGroupName should return true given root', () => {
|
||||
let name1: string = '/';
|
||||
let name2: string = '';
|
||||
let expected: boolean = true;
|
||||
let actual = ConnectionProfileGroup.sameGroupName(name1, name2);
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('sameGroupName should return true given same group names', () => {
|
||||
let name1: string = '/group1';
|
||||
let name2: string = '/Group1';
|
||||
let expected: boolean = true;
|
||||
let actual = ConnectionProfileGroup.sameGroupName(name1, name2);
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('sameGroupName should return false given two different groups', () => {
|
||||
let name1: string = '/';
|
||||
let name2: string = '/Group1';
|
||||
let expected: boolean = false;
|
||||
let actual = ConnectionProfileGroup.sameGroupName(name1, name2);
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,258 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as azdata from 'azdata';
|
||||
import { ConnectionStatusManager } from 'sql/platform/connection/common/connectionStatusManager';
|
||||
import * as Utils from 'sql/platform/connection/common/utils';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
import { TestEnvironmentService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
|
||||
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
|
||||
let connections: ConnectionStatusManager;
|
||||
let capabilitiesService: TestCapabilitiesService;
|
||||
let connectionProfileObject: ConnectionProfile;
|
||||
let connectionProfile: IConnectionProfile = {
|
||||
connectionName: 'new name',
|
||||
serverName: 'new server',
|
||||
databaseName: 'database',
|
||||
userName: 'user',
|
||||
password: 'password',
|
||||
authenticationType: '',
|
||||
savePassword: true,
|
||||
groupFullName: 'g2/g2-2',
|
||||
groupId: 'group id',
|
||||
getOptionsKey: () => 'connection1',
|
||||
matches: undefined,
|
||||
providerName: mssqlProviderName,
|
||||
options: {},
|
||||
saveProfile: true,
|
||||
id: undefined
|
||||
};
|
||||
let editorConnectionProfile: IConnectionProfile = {
|
||||
connectionName: 'new name',
|
||||
serverName: 'new server',
|
||||
databaseName: 'database',
|
||||
userName: 'user',
|
||||
password: 'password',
|
||||
authenticationType: '',
|
||||
savePassword: true,
|
||||
groupFullName: 'g2/g2-2',
|
||||
groupId: 'group id',
|
||||
getOptionsKey: () => 'connection2',
|
||||
matches: undefined,
|
||||
providerName: mssqlProviderName,
|
||||
options: {},
|
||||
saveProfile: true,
|
||||
id: undefined
|
||||
};
|
||||
let connectionProfileWithoutDbName: IConnectionProfile = {
|
||||
connectionName: 'new name',
|
||||
serverName: 'new server',
|
||||
databaseName: '',
|
||||
userName: 'user',
|
||||
password: 'password',
|
||||
authenticationType: '',
|
||||
savePassword: true,
|
||||
groupFullName: 'g2/g2-2',
|
||||
groupId: 'group id',
|
||||
getOptionsKey: () => 'connection1',
|
||||
matches: undefined,
|
||||
providerName: mssqlProviderName,
|
||||
options: {},
|
||||
saveProfile: true,
|
||||
id: undefined
|
||||
};
|
||||
|
||||
let connection1Id: string;
|
||||
let connection2Id: string;
|
||||
let connection3Id: string;
|
||||
|
||||
suite('SQL ConnectionStatusManager tests', () => {
|
||||
setup(() => {
|
||||
capabilitiesService = new TestCapabilitiesService();
|
||||
connectionProfileObject = new ConnectionProfile(capabilitiesService, connectionProfile);
|
||||
connections = new ConnectionStatusManager(capabilitiesService, new NullLogService(), TestEnvironmentService, new TestNotificationService());
|
||||
connection1Id = Utils.generateUri(connectionProfile);
|
||||
connection2Id = 'connection2Id';
|
||||
connection3Id = 'connection3Id';
|
||||
connections.addConnection(connectionProfile, connection1Id);
|
||||
connections.addConnection(editorConnectionProfile, connection2Id);
|
||||
connections.addConnection(connectionProfileWithoutDbName, connection3Id);
|
||||
});
|
||||
|
||||
test('findConnection should return undefined given invalid id', () => {
|
||||
let id: string = 'invalid id';
|
||||
let expected = undefined;
|
||||
let actual = connections.findConnection(id);
|
||||
assert.equal(actual, expected);
|
||||
});
|
||||
|
||||
test('findConnection should return connection given valid id', () => {
|
||||
let id: string = connection1Id;
|
||||
let expected = connectionProfileObject;
|
||||
let actual = connections.findConnection(id);
|
||||
assert.equal(connectionProfileObject.matches(actual.connectionProfile), true);
|
||||
});
|
||||
|
||||
test('getConnectionProfile should return undefined given invalid id', () => {
|
||||
let id: string = 'invalid id';
|
||||
let expected = undefined;
|
||||
let actual = connections.getConnectionProfile(id);
|
||||
assert.equal(actual, expected);
|
||||
});
|
||||
|
||||
test('getConnectionProfile should return connection given valid id', () => {
|
||||
let id: string = connection1Id;
|
||||
let expected = connectionProfileObject;
|
||||
let actual = connections.getConnectionProfile(id);
|
||||
assert.equal(connectionProfileObject.matches(actual), true);
|
||||
});
|
||||
|
||||
test('hasConnection should return false given invalid id', () => {
|
||||
let id: string = 'invalid id';
|
||||
let expected = false;
|
||||
let actual = connections.hasConnection(id);
|
||||
assert.equal(actual, expected);
|
||||
});
|
||||
|
||||
test('hasConnection should return true given valid id', () => {
|
||||
let id: string = connection1Id;
|
||||
let expected = true;
|
||||
let actual = connections.hasConnection(id);
|
||||
assert.equal(actual, expected);
|
||||
});
|
||||
|
||||
test('addConnection should set connecting to true', () => {
|
||||
let expected = true;
|
||||
let summary: azdata.ConnectionInfoSummary = {
|
||||
ownerUri: connection1Id,
|
||||
connectionId: connection1Id,
|
||||
messages: undefined,
|
||||
errorMessage: undefined,
|
||||
errorNumber: undefined,
|
||||
serverInfo: undefined,
|
||||
connectionSummary: undefined
|
||||
};
|
||||
connections.onConnectionComplete(summary);
|
||||
let actual = connections.addConnection(connectionProfile, connection1Id).connecting;
|
||||
assert.equal(actual, expected);
|
||||
});
|
||||
|
||||
test('onConnectionComplete should set connecting to false', () => {
|
||||
let expected = false;
|
||||
let summary: azdata.ConnectionInfoSummary = {
|
||||
ownerUri: connection1Id,
|
||||
connectionId: connection1Id,
|
||||
messages: undefined,
|
||||
errorMessage: undefined,
|
||||
errorNumber: undefined,
|
||||
serverInfo: undefined,
|
||||
connectionSummary: undefined
|
||||
};
|
||||
connections.onConnectionComplete(summary);
|
||||
let actual = connections.findConnection(connection1Id).connecting;
|
||||
assert.equal(actual, expected);
|
||||
actual = connections.isConnecting(connection1Id);
|
||||
assert.equal(actual, expected);
|
||||
});
|
||||
|
||||
test('updateConnection should update the connection info', () => {
|
||||
let expected = connectionProfile.groupId + '1';
|
||||
let expectedConnectionId = 'new id';
|
||||
connections.addConnection(connectionProfile, connection1Id);
|
||||
|
||||
let updatedConnection = Object.assign({}, connectionProfile, { groupId: expected, getOptionsKey: () => connectionProfile.getOptionsKey() + expected, id: expectedConnectionId });
|
||||
let actualId = connections.updateConnectionProfile(updatedConnection, connection1Id);
|
||||
|
||||
let newId = Utils.generateUri(updatedConnection);
|
||||
let actual = connections.getConnectionProfile(newId).groupId;
|
||||
let actualConnectionId = connections.getConnectionProfile(newId).id;
|
||||
assert.equal(actual, expected);
|
||||
assert.equal(actualId, newId);
|
||||
assert.equal(actualConnectionId, expectedConnectionId);
|
||||
});
|
||||
|
||||
test('updateDatabaseName should update the database name in connection', () => {
|
||||
let dbName: string = 'db name';
|
||||
let summary: azdata.ConnectionInfoSummary = {
|
||||
connectionSummary: {
|
||||
databaseName: dbName,
|
||||
serverName: undefined,
|
||||
userName: undefined
|
||||
}
|
||||
, ownerUri: connection3Id,
|
||||
connectionId: 'connection id',
|
||||
errorMessage: undefined,
|
||||
errorNumber: undefined,
|
||||
messages: undefined,
|
||||
serverInfo: undefined
|
||||
};
|
||||
|
||||
//The original connection didn't have database name
|
||||
let connectionStatus = connections.findConnection(connection3Id);
|
||||
connectionStatus.connectionProfile.databaseName = '';
|
||||
|
||||
//Verify database name changed after connection is complete
|
||||
connections.updateDatabaseName(summary);
|
||||
connectionStatus = connections.findConnection(connection3Id);
|
||||
assert.equal(connectionStatus.connectionProfile.databaseName, dbName);
|
||||
});
|
||||
|
||||
test('getOriginalOwnerUri should return the original uri given uri with db name', () => {
|
||||
let dbName: string = 'db name';
|
||||
let summary: azdata.ConnectionInfoSummary = {
|
||||
connectionSummary: {
|
||||
databaseName: dbName,
|
||||
serverName: undefined,
|
||||
userName: undefined
|
||||
}
|
||||
, ownerUri: connection3Id,
|
||||
connectionId: 'connection id',
|
||||
errorMessage: undefined,
|
||||
errorNumber: undefined,
|
||||
messages: undefined,
|
||||
serverInfo: undefined
|
||||
};
|
||||
|
||||
//The original connection didn't have database name
|
||||
let connectionStatus = connections.findConnection(connection3Id);
|
||||
connectionStatus.connectionProfile.databaseName = '';
|
||||
|
||||
//Verify database name changed after connection is complete
|
||||
connections.updateDatabaseName(summary);
|
||||
connectionStatus = connections.findConnection(connection3Id);
|
||||
let ownerUriWithDbName = Utils.generateUriWithPrefix(connectionStatus.connectionProfile, 'connection://');
|
||||
|
||||
//The uri assigned to connection without db name should be the original one
|
||||
let connectionWitDbStatus = connections.getOriginalOwnerUri(ownerUriWithDbName);
|
||||
assert.equal(connectionWitDbStatus, connection3Id);
|
||||
});
|
||||
|
||||
test('getOriginalOwnerUri should return given uri if the original uri is the same as the given uri', () => {
|
||||
|
||||
let connectionStatus = connections.getOriginalOwnerUri(connection2Id);
|
||||
assert.equal(connectionStatus, connection2Id);
|
||||
});
|
||||
|
||||
test('getActiveConnectionProfiles should return a list of all the unique connections that the status manager knows about', () => {
|
||||
// Add duplicate connections
|
||||
let newConnection = Object.assign({}, connectionProfile);
|
||||
newConnection.id = 'test_id';
|
||||
newConnection.serverName = 'new_server_name';
|
||||
newConnection.options['databaseDisplayName'] = newConnection.databaseName;
|
||||
connections.addConnection(newConnection, 'test_uri_1');
|
||||
connections.addConnection(newConnection, 'test_uri_2');
|
||||
|
||||
// Get the connections and verify that the duplicate is only returned once
|
||||
let activeConnections = connections.getActiveConnectionProfiles();
|
||||
assert.equal(activeConnections.length, 4);
|
||||
assert.equal(activeConnections.filter(connection => connection.matches(newConnection)).length, 1, 'Did not find newConnection in active connections');
|
||||
});
|
||||
});
|
||||
@@ -13,7 +13,7 @@ import { TestConfigurationService } from 'sql/platform/connection/test/common/te
|
||||
import { TestCredentialsService } from 'sql/platform/credentials/test/common/testCredentialsService';
|
||||
import { ConnectionOptionSpecialType, ServiceOptionType } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { ConnectionProviderProperties } from 'sql/workbench/parts/connection/common/connectionProviderExtension';
|
||||
import { CapabilitiesTestService } from 'sqltest/stubs/capabilitiesTestService';
|
||||
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
|
||||
import { deepClone, deepFreeze } from 'vs/base/common/objects';
|
||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { TestStorageService } from 'vs/workbench/test/workbenchTestServices';
|
||||
@@ -37,7 +37,7 @@ suite('ConnectionStore', () => {
|
||||
saveProfile: true,
|
||||
id: undefined
|
||||
});
|
||||
let capabilitiesService: CapabilitiesTestService;
|
||||
let capabilitiesService: TestCapabilitiesService;
|
||||
let maxRecent = 5;
|
||||
let msSQLCapabilities: ConnectionProviderProperties;
|
||||
let provider2Capabilities: ConnectionProviderProperties;
|
||||
@@ -46,7 +46,7 @@ suite('ConnectionStore', () => {
|
||||
setup(() => {
|
||||
// setup configuration to return maxRecent for the #MRU items
|
||||
|
||||
capabilitiesService = new CapabilitiesTestService();
|
||||
capabilitiesService = new TestCapabilitiesService();
|
||||
let connectionProvider: azdata.ConnectionOption[] = [
|
||||
{
|
||||
name: 'connectionName',
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ProviderConnectionInfo } from 'sql/platform/connection/common/providerConnectionInfo';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import * as azdata from 'azdata';
|
||||
import * as assert from 'assert';
|
||||
import { ConnectionOptionSpecialType, ServiceOptionType } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
|
||||
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
|
||||
|
||||
suite('SQL ProviderConnectionInfo tests', () => {
|
||||
let msSQLCapabilities: any;
|
||||
let capabilitiesService: TestCapabilitiesService;
|
||||
|
||||
let connectionProfile: IConnectionProfile = {
|
||||
connectionName: 'name',
|
||||
serverName: 'new server',
|
||||
databaseName: 'database',
|
||||
userName: 'user',
|
||||
password: 'password',
|
||||
authenticationType: '',
|
||||
savePassword: true,
|
||||
groupFullName: 'g2/g2-2',
|
||||
groupId: undefined,
|
||||
getOptionsKey: undefined,
|
||||
matches: undefined,
|
||||
providerName: mssqlProviderName,
|
||||
options: undefined,
|
||||
saveProfile: true,
|
||||
id: undefined
|
||||
};
|
||||
|
||||
setup(() => {
|
||||
let capabilities: azdata.DataProtocolServerCapabilities[] = [];
|
||||
let connectionProvider: azdata.ConnectionOption[] = [
|
||||
{
|
||||
name: 'connectionName',
|
||||
displayName: undefined,
|
||||
description: undefined,
|
||||
groupName: undefined,
|
||||
categoryValues: undefined,
|
||||
defaultValue: undefined,
|
||||
isIdentity: true,
|
||||
isRequired: true,
|
||||
specialValueType: ConnectionOptionSpecialType.connectionName,
|
||||
valueType: ServiceOptionType.string
|
||||
},
|
||||
{
|
||||
name: 'serverName',
|
||||
displayName: undefined,
|
||||
description: undefined,
|
||||
groupName: undefined,
|
||||
categoryValues: undefined,
|
||||
defaultValue: undefined,
|
||||
isIdentity: true,
|
||||
isRequired: true,
|
||||
specialValueType: ConnectionOptionSpecialType.serverName,
|
||||
valueType: ServiceOptionType.string
|
||||
},
|
||||
{
|
||||
name: 'databaseName',
|
||||
displayName: undefined,
|
||||
description: undefined,
|
||||
groupName: undefined,
|
||||
categoryValues: undefined,
|
||||
defaultValue: undefined,
|
||||
isIdentity: true,
|
||||
isRequired: true,
|
||||
specialValueType: ConnectionOptionSpecialType.databaseName,
|
||||
valueType: ServiceOptionType.string
|
||||
},
|
||||
{
|
||||
name: 'userName',
|
||||
displayName: undefined,
|
||||
description: undefined,
|
||||
groupName: undefined,
|
||||
categoryValues: undefined,
|
||||
defaultValue: undefined,
|
||||
isIdentity: true,
|
||||
isRequired: true,
|
||||
specialValueType: ConnectionOptionSpecialType.userName,
|
||||
valueType: ServiceOptionType.string
|
||||
},
|
||||
{
|
||||
name: 'authenticationType',
|
||||
displayName: undefined,
|
||||
description: undefined,
|
||||
groupName: undefined,
|
||||
categoryValues: undefined,
|
||||
defaultValue: undefined,
|
||||
isIdentity: true,
|
||||
isRequired: true,
|
||||
specialValueType: ConnectionOptionSpecialType.authType,
|
||||
valueType: ServiceOptionType.string
|
||||
},
|
||||
{
|
||||
name: 'password',
|
||||
displayName: undefined,
|
||||
description: undefined,
|
||||
groupName: undefined,
|
||||
categoryValues: undefined,
|
||||
defaultValue: undefined,
|
||||
isIdentity: true,
|
||||
isRequired: true,
|
||||
specialValueType: ConnectionOptionSpecialType.password,
|
||||
valueType: ServiceOptionType.string
|
||||
},
|
||||
{
|
||||
name: 'encrypt',
|
||||
displayName: undefined,
|
||||
description: undefined,
|
||||
groupName: undefined,
|
||||
categoryValues: undefined,
|
||||
defaultValue: undefined,
|
||||
isIdentity: false,
|
||||
isRequired: false,
|
||||
specialValueType: undefined,
|
||||
valueType: ServiceOptionType.string
|
||||
}
|
||||
];
|
||||
msSQLCapabilities = {
|
||||
providerId: mssqlProviderName,
|
||||
displayName: 'MSSQL',
|
||||
connectionOptions: connectionProvider,
|
||||
};
|
||||
capabilities.push(msSQLCapabilities);
|
||||
capabilitiesService = new TestCapabilitiesService();
|
||||
capabilitiesService.capabilities[mssqlProviderName] = { connection: msSQLCapabilities };
|
||||
});
|
||||
|
||||
test('constructor should accept undefined parameters', () => {
|
||||
let conn = new ProviderConnectionInfo(undefined, undefined);
|
||||
assert.equal(conn.serverName, undefined);
|
||||
});
|
||||
|
||||
test('set properties should set the values correctly', () => {
|
||||
let conn = new ProviderConnectionInfo(capabilitiesService, mssqlProviderName);
|
||||
assert.equal(conn.serverName, undefined);
|
||||
conn.connectionName = connectionProfile.connectionName;
|
||||
conn.serverName = connectionProfile.serverName;
|
||||
conn.databaseName = connectionProfile.databaseName;
|
||||
conn.authenticationType = connectionProfile.authenticationType;
|
||||
conn.password = connectionProfile.password;
|
||||
conn.userName = connectionProfile.userName;
|
||||
assert.equal(conn.connectionName, connectionProfile.connectionName);
|
||||
assert.equal(conn.serverName, connectionProfile.serverName);
|
||||
assert.equal(conn.databaseName, connectionProfile.databaseName);
|
||||
assert.equal(conn.authenticationType, connectionProfile.authenticationType);
|
||||
assert.equal(conn.password, connectionProfile.password);
|
||||
assert.equal(conn.userName, connectionProfile.userName);
|
||||
});
|
||||
|
||||
test('set properties should store the values in the options', () => {
|
||||
let conn = new ProviderConnectionInfo(capabilitiesService, mssqlProviderName);
|
||||
assert.equal(conn.serverName, undefined);
|
||||
conn.serverName = connectionProfile.serverName;
|
||||
conn.databaseName = connectionProfile.databaseName;
|
||||
conn.authenticationType = connectionProfile.authenticationType;
|
||||
conn.password = connectionProfile.password;
|
||||
conn.userName = connectionProfile.userName;
|
||||
assert.equal(conn.getOptionValue('serverName'), connectionProfile.serverName);
|
||||
assert.equal(conn.getOptionValue('databaseName'), connectionProfile.databaseName);
|
||||
assert.equal(conn.getOptionValue('authenticationType'), connectionProfile.authenticationType);
|
||||
assert.equal(conn.getOptionValue('password'), connectionProfile.password);
|
||||
assert.equal(conn.getOptionValue('userName'), connectionProfile.userName);
|
||||
});
|
||||
|
||||
test('constructor should initialize the options given a valid model', () => {
|
||||
let conn = new ProviderConnectionInfo(capabilitiesService, connectionProfile);
|
||||
|
||||
assert.equal(conn.connectionName, connectionProfile.connectionName);
|
||||
assert.equal(conn.serverName, connectionProfile.serverName);
|
||||
assert.equal(conn.databaseName, connectionProfile.databaseName);
|
||||
assert.equal(conn.authenticationType, connectionProfile.authenticationType);
|
||||
assert.equal(conn.password, connectionProfile.password);
|
||||
assert.equal(conn.userName, connectionProfile.userName);
|
||||
});
|
||||
|
||||
test('clone should create a new instance that equals the old one', () => {
|
||||
let conn = new ProviderConnectionInfo(capabilitiesService, connectionProfile);
|
||||
|
||||
let conn2 = conn.clone();
|
||||
assert.equal(conn.connectionName, conn2.connectionName);
|
||||
assert.equal(conn.serverName, conn2.serverName);
|
||||
assert.equal(conn.databaseName, conn2.databaseName);
|
||||
assert.equal(conn.authenticationType, conn2.authenticationType);
|
||||
assert.equal(conn.password, conn2.password);
|
||||
assert.equal(conn.userName, conn2.userName);
|
||||
});
|
||||
|
||||
test('Changing the cloned object should not change the original one', () => {
|
||||
let conn = new ProviderConnectionInfo(capabilitiesService, connectionProfile);
|
||||
|
||||
let conn2 = conn.clone();
|
||||
conn2.serverName = conn.serverName + '1';
|
||||
assert.notEqual(conn.serverName, conn2.serverName);
|
||||
});
|
||||
|
||||
test('constructor should initialize the options given a valid model with options', () => {
|
||||
let options = {};
|
||||
options['encrypt'] = 'test value';
|
||||
let conn2 = Object.assign({}, connectionProfile, { options: options });
|
||||
let conn = new ProviderConnectionInfo(capabilitiesService, conn2);
|
||||
|
||||
assert.equal(conn.connectionName, conn2.connectionName);
|
||||
assert.equal(conn.serverName, conn2.serverName);
|
||||
assert.equal(conn.databaseName, conn2.databaseName);
|
||||
assert.equal(conn.authenticationType, conn2.authenticationType);
|
||||
assert.equal(conn.password, conn2.password);
|
||||
assert.equal(conn.userName, conn2.userName);
|
||||
assert.equal(conn.options['encrypt'], 'test value');
|
||||
});
|
||||
|
||||
test('getOptionsKey should create a valid unique id', () => {
|
||||
let conn = new ProviderConnectionInfo(capabilitiesService, connectionProfile);
|
||||
let expectedId = 'providerName:MSSQL|authenticationType:|databaseName:database|serverName:new server|userName:user';
|
||||
let id = conn.getOptionsKey();
|
||||
assert.equal(id, expectedId);
|
||||
});
|
||||
|
||||
test('getOptionsKey should create different id for different server names', () => {
|
||||
let conn = new ProviderConnectionInfo(capabilitiesService, connectionProfile);
|
||||
let conn2 = new ProviderConnectionInfo(capabilitiesService, Object.assign({}, connectionProfile, { serverName: connectionProfile.serverName + '1' }));
|
||||
|
||||
assert.notEqual(conn.getOptionsKey(), conn2.getOptionsKey());
|
||||
});
|
||||
|
||||
test('titleParts should return server, database and auth type as first items', () => {
|
||||
let conn = new ProviderConnectionInfo(capabilitiesService, connectionProfile);
|
||||
let titleParts = conn.titleParts;
|
||||
assert.equal(titleParts.length, 4);
|
||||
assert.equal(titleParts[0], connectionProfile.serverName);
|
||||
assert.equal(titleParts[1], connectionProfile.databaseName);
|
||||
assert.equal(titleParts[2], connectionProfile.authenticationType);
|
||||
assert.equal(titleParts[3], connectionProfile.userName);
|
||||
});
|
||||
|
||||
test('getProviderFromOptionsKey should return the provider name from the options key successfully', () => {
|
||||
let optionsKey = `providerName:${mssqlProviderName}|authenticationType:|databaseName:database|serverName:new server|userName:user`;
|
||||
let actual = ProviderConnectionInfo.getProviderFromOptionsKey(optionsKey);
|
||||
|
||||
assert.equal(mssqlProviderName, actual);
|
||||
});
|
||||
|
||||
test('getProviderFromOptionsKey should return empty string give null', () => {
|
||||
let optionsKey = undefined;
|
||||
let expectedProviderId: string = '';
|
||||
let actual = ProviderConnectionInfo.getProviderFromOptionsKey(optionsKey);
|
||||
|
||||
assert.equal(expectedProviderId, actual);
|
||||
});
|
||||
|
||||
test('getProviderFromOptionsKey should return empty string give key without provider name', () => {
|
||||
let optionsKey = 'providerName2:MSSQL|authenticationType:|databaseName:database|serverName:new server|userName:user';
|
||||
let expectedProviderId: string = '';
|
||||
let actual = ProviderConnectionInfo.getProviderFromOptionsKey(optionsKey);
|
||||
|
||||
assert.equal(expectedProviderId, actual);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,297 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IConnectionManagementService, IConnectableInput, IConnectionCompletionOptions, IConnectionCallbacks, IConnectionResult, INewConnectionParams }
|
||||
from 'sql/platform/connection/common/connectionManagement';
|
||||
import { IConnectionProfileGroup, ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
import { ConnectionManagementInfo } from 'sql/platform/connection/common/connectionManagementInfo';
|
||||
import * as azdata from 'azdata';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { ConnectionProviderProperties } from 'sql/workbench/parts/connection/common/connectionProviderExtension';
|
||||
|
||||
// Test stubs for commonly used objects
|
||||
|
||||
export class TestConnectionManagementService implements IConnectionManagementService {
|
||||
_serviceBrand: any;
|
||||
onAddConnectionProfile = undefined;
|
||||
onDeleteConnectionProfile = undefined;
|
||||
onConnectionChanged = undefined;
|
||||
onLanguageFlavorChanged = undefined;
|
||||
|
||||
public get onConnect(): Event<any> {
|
||||
let conEvent = new Emitter<any>();
|
||||
return conEvent.event;
|
||||
}
|
||||
|
||||
public get onDisconnect(): Event<any> {
|
||||
let conEvent = new Emitter<any>();
|
||||
return conEvent.event;
|
||||
}
|
||||
|
||||
registerProvider(providerId: string, provider: azdata.ConnectionProvider): void {
|
||||
|
||||
}
|
||||
|
||||
registerIconProvider(providerId: string, provider: azdata.IconProvider): void {
|
||||
|
||||
}
|
||||
|
||||
showConnectionDialog(params?: INewConnectionParams, options?: IConnectionCompletionOptions, model?: IConnectionProfile, connectionResult?: IConnectionResult): Promise<void> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
showCreateServerGroupDialog(): Promise<void> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
showEditServerGroupDialog(group: ConnectionProfileGroup): Promise<void> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
onConnectionComplete(handle: number, connectionInfoSummary: azdata.ConnectionInfoSummary): void {
|
||||
|
||||
}
|
||||
|
||||
onIntelliSenseCacheComplete(handle: number, connectionUri: string): void {
|
||||
|
||||
}
|
||||
|
||||
public onConnectionChangedNotification(handle: number, changedConnInfo: azdata.ChangedConnectionInfo): void {
|
||||
|
||||
}
|
||||
|
||||
getCurrentConnectionSummary(): azdata.ConnectionSummary {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getConnectionGroups(providers?: string[]): ConnectionProfileGroup[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
getActiveConnections(providers?: string[]): ConnectionProfile[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
saveProfileGroup(profile: IConnectionProfileGroup): Promise<string> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getRecentConnections(providers?: string[]): ConnectionProfile[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
public clearRecentConnectionsList(): void {
|
||||
return;
|
||||
}
|
||||
|
||||
public clearRecentConnection(connectionProfile: ConnectionProfile): void {
|
||||
return;
|
||||
}
|
||||
|
||||
getUnsavedConnections(): ConnectionProfile[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
changeGroupIdForConnectionGroup(source: IConnectionProfileGroup, target: IConnectionProfileGroup): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
changeGroupIdForConnection(source: ConnectionProfile, targetGroupId: string): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
deleteConnection(connection: ConnectionProfile): Promise<boolean> {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
resolve(true);
|
||||
});
|
||||
}
|
||||
|
||||
deleteConnectionGroup(group: ConnectionProfileGroup): Promise<boolean> {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
resolve(true);
|
||||
});
|
||||
}
|
||||
|
||||
getAdvancedProperties(): azdata.ConnectionOption[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
getConnectionUri(connectionProfile: ConnectionProfile): string {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getFormattedUri(uri: string, connectionProfile: ConnectionProfile): string {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getConnectionUriFromId(connectionId: string): string {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
isConnected(fileUri: string, connectionProfile?: ConnectionProfile): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
isRecent(connectionProfile: ConnectionProfile): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
isProfileConnected(connectionProfile: IConnectionProfile): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
isProfileConnecting(connectionProfile: IConnectionProfile): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
findExistingConnection(connection: IConnectionProfile, purpose?: 'dashboard' | 'insights' | 'connection'): ConnectionProfile {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
connect(connection: IConnectionProfile, uri: string, options?: IConnectionCompletionOptions, callbacks?: IConnectionCallbacks): Promise<IConnectionResult> {
|
||||
return new Promise<IConnectionResult>((resolve, reject) => {
|
||||
resolve({ connected: true, errorMessage: undefined, errorCode: undefined, callStack: undefined });
|
||||
});
|
||||
}
|
||||
|
||||
connectAndSaveProfile(connection: IConnectionProfile, uri: string, options?: IConnectionCompletionOptions, callbacks?: IConnectionCallbacks): Promise<IConnectionResult> {
|
||||
return new Promise<IConnectionResult>(() => true);
|
||||
}
|
||||
|
||||
disconnectEditor(owner: IConnectableInput): Promise<boolean> {
|
||||
return new Promise<boolean>(() => true);
|
||||
}
|
||||
|
||||
disconnect(connection: IConnectionProfile);
|
||||
disconnect(uri: string);
|
||||
disconnect(input: any): Promise<boolean> {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
resolve(true);
|
||||
});
|
||||
}
|
||||
|
||||
getConnectionProfile(fileUri: string): IConnectionProfile {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getConnectionInfo(fileUri: string): ConnectionManagementInfo {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
addSavedPassword(connectionProfile: IConnectionProfile): Promise<IConnectionProfile> {
|
||||
return new Promise<IConnectionProfile>(() => connectionProfile);
|
||||
}
|
||||
|
||||
public listDatabases(connectionUri: string): Thenable<azdata.ListDatabasesResult> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
cancelConnection(connection: IConnectionProfile): Thenable<boolean> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
cancelEditorConnection(owner: IConnectableInput): Thenable<boolean> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
showDashboard(connection: ConnectionProfile): Promise<boolean> {
|
||||
return new Promise(() => true);
|
||||
}
|
||||
|
||||
closeDashboard(uri: string): void {
|
||||
}
|
||||
|
||||
changeDatabase(connectionUri: string, databaseName: string): Thenable<boolean> {
|
||||
return new Promise(() => true);
|
||||
}
|
||||
|
||||
editGroup(group: ConnectionProfileGroup): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
getProviderIdFromUri(ownerUri: string): string {
|
||||
return undefined;
|
||||
}
|
||||
hasRegisteredServers(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
getCapabilities(providerName: string): azdata.DataProtocolServerCapabilities {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
canChangeConnectionConfig(profile: ConnectionProfile, newGroupID: string): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
doChangeLanguageFlavor(uri: string, language: string, flavor: string): void {
|
||||
|
||||
}
|
||||
ensureDefaultLanguageFlavor(uri: string): void {
|
||||
|
||||
}
|
||||
|
||||
public getProviderNames(): string[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
connectIfNotConnected(connection: IConnectionProfile, purpose?: 'dashboard' | 'insights' | 'connection', saveConnection: boolean = false): Promise<string> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
rebuildIntelliSenseCache(uri: string): Thenable<void> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getTabColorForUri(uri: string): string {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
removeConnectionProfileCredentials(profile: IConnectionProfile): IConnectionProfile {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getActiveConnectionCredentials(profileId: string): { [name: string]: string } {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getServerInfo(profileId: string): azdata.ServerInfo {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getConnectionString(connectionId: string): Thenable<string> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
buildConnectionInfo(connectionString: string, provider?: string): Thenable<azdata.ConnectionInfo> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
providerRegistered(providerId: string): boolean {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getConnectionProfileById(profileId: string): IConnectionProfile {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getProviderProperties(providerName: string): ConnectionProviderProperties {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getConnectionIconId(connectionId: string): string {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getDefaultProviderId(): string {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getConnections(activeConnectionsOnly?: boolean): ConnectionProfile[] {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
|
||||
|
||||
export class TestConnectionProvider implements azdata.ConnectionProvider {
|
||||
public readonly providerId = mssqlProviderName;
|
||||
|
||||
connect(connectionUri: string, connectionInfo: azdata.ConnectionInfo): Thenable<boolean> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
disconnect(connectionUri: string): Thenable<boolean> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
cancelConnect(connectionUri: string): Thenable<boolean> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
listDatabases(connectionUri: string): Thenable<azdata.ListDatabasesResult> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
changeDatabase(connectionUri: string, newDatabase: string): Thenable<boolean> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getConnectionString(connectionUri: string): Thenable<string> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
rebuildIntelliSenseCache(connectionUri: string): Thenable<void> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
registerOnConnectionComplete(handler: (connSummary: azdata.ConnectionInfoSummary) => any) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
registerOnIntelliSenseCacheComplete(handler: (connectionUri: string) => any) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
registerOnConnectionChanged(handler: (changedConnInfo: azdata.ChangedConnectionInfo) => any) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ICredentialsService, CredentialManagementEvents } from 'sql/platform/credentials/common/credentialsService';
|
||||
import { Credential } from 'azdata';
|
||||
import { Credential, CredentialProvider } from 'azdata';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
|
||||
@@ -40,3 +40,27 @@ export class TestCredentialsService implements ICredentialsService {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
|
||||
export class TestCredentialsProvider implements CredentialProvider {
|
||||
handle: number;
|
||||
|
||||
public storedCredentials: { [K: string]: Credential } = {};
|
||||
|
||||
saveCredential(credentialId: string, password: string): Thenable<boolean> {
|
||||
this.storedCredentials[credentialId] = {
|
||||
credentialId: credentialId,
|
||||
password: password
|
||||
};
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
readCredential(credentialId: string): Thenable<Credential> {
|
||||
return Promise.resolve(this.storedCredentials[credentialId]);
|
||||
}
|
||||
|
||||
deleteCredential(credentialId: string): Thenable<boolean> {
|
||||
let exists = this.storedCredentials[credentialId] !== undefined;
|
||||
delete this.storedCredentials[credentialId];
|
||||
return Promise.resolve(exists);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@ import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
|
||||
import * as nls from 'vs/nls';
|
||||
import { IExtensionPointUser, ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
||||
|
||||
import { ProviderProperties } from 'sql/workbench/parts/dashboard/widgets/properties/propertiesWidget.component';
|
||||
import { DATABASE_DASHBOARD_TABS } from 'sql/workbench/parts/dashboard/pages/databaseDashboardPage.contribution';
|
||||
import { SERVER_DASHBOARD_TABS, SERVER_DASHBOARD_PROPERTIES } from 'sql/workbench/parts/dashboard/pages/serverDashboardPage.contribution';
|
||||
import { DASHBOARD_CONFIG_ID, DASHBOARD_TABS_KEY_PROPERTY } from 'sql/workbench/parts/dashboard/pages/dashboardPageContribution';
|
||||
import { ProviderProperties } from 'sql/workbench/parts/dashboard/browser/widgets/properties/propertiesWidget.component';
|
||||
import { DATABASE_DASHBOARD_TABS } from 'sql/workbench/parts/dashboard/browser/pages/databaseDashboardPage.contribution';
|
||||
import { SERVER_DASHBOARD_TABS } from 'sql/workbench/parts/dashboard/browser/pages/serverDashboardPage.contribution';
|
||||
import { DASHBOARD_CONFIG_ID, DASHBOARD_TABS_KEY_PROPERTY } from 'sql/workbench/parts/dashboard/browser/pages/dashboardPageContribution';
|
||||
|
||||
export const Extensions = {
|
||||
DashboardContributions: 'dashboard.contributions'
|
||||
@@ -3,11 +3,11 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { Type } from '@angular/core';
|
||||
import { IInsightsConfig, IInsightsView } from 'sql/workbench/parts/dashboard/widgets/insights/interfaces';
|
||||
|
||||
import * as platform from 'vs/platform/registry/common/platform';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import * as nls from 'vs/nls';
|
||||
import { IInsightData } from 'sql/workbench/parts/charts/browser/interfaces';
|
||||
|
||||
export type InsightIdentifier = string;
|
||||
|
||||
@@ -15,6 +15,57 @@ export const Extensions = {
|
||||
InsightContribution: 'dashboard.contributions.insights'
|
||||
};
|
||||
|
||||
export interface IInsightsConfig {
|
||||
cacheId?: string;
|
||||
type: any;
|
||||
name?: string;
|
||||
when?: string;
|
||||
gridItemConfig?: ISize;
|
||||
query?: string | Array<string>;
|
||||
queryFile?: string;
|
||||
details?: IInsightsConfigDetails;
|
||||
autoRefreshInterval?: number;
|
||||
}
|
||||
|
||||
export interface IInsightsLabel {
|
||||
column: string;
|
||||
icon?: string;
|
||||
state?: Array<IStateCondition>;
|
||||
}
|
||||
|
||||
export interface IStateCondition {
|
||||
condition: {
|
||||
if: string,
|
||||
equals?: string
|
||||
};
|
||||
color?: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
export interface IInsightsConfigDetails {
|
||||
query?: string | Array<string>;
|
||||
queryFile?: string;
|
||||
label?: string | IInsightsLabel;
|
||||
value?: string;
|
||||
actions?: {
|
||||
types: Array<string>;
|
||||
database?: string;
|
||||
server?: string;
|
||||
user?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ISize {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export interface IInsightsView {
|
||||
data: IInsightData;
|
||||
setConfig?: (config: { [key: string]: any }) => void;
|
||||
init?: () => void;
|
||||
}
|
||||
|
||||
export interface IInsightRegistry {
|
||||
insightSchema: IJSONSchema;
|
||||
registerInsight(id: string, description: string, schema: IJSONSchema, ctor: Type<IInsightsView>): InsightIdentifier;
|
||||
@@ -6,7 +6,7 @@ import { Type } from '@angular/core';
|
||||
import { ModelComponentTypes } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
|
||||
import * as platform from 'vs/platform/registry/common/platform';
|
||||
import { IComponent } from 'sql/workbench/electron-browser/modelComponents/interfaces';
|
||||
import { IComponent } from 'sql/workbench/browser/modelComponents/interfaces';
|
||||
|
||||
export type ComponentIdentifier = string;
|
||||
|
||||
@@ -2,14 +2,11 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { IInsightsConfig } from 'sql/workbench/parts/dashboard/widgets/insights/interfaces';
|
||||
|
||||
import * as platform from 'vs/platform/registry/common/platform';
|
||||
import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
|
||||
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
|
||||
import * as nls from 'vs/nls';
|
||||
|
||||
const contributionRegistry = platform.Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
|
||||
import { IInsightsConfig } from 'sql/platform/dashboard/browser/insightRegistry';
|
||||
|
||||
export type WidgetIdentifier = string;
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
|
||||
import { Extensions as ConfigurationExtension } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { deepClone } from 'vs/base/common/objects';
|
||||
|
||||
import { WidgetConfig } from 'sql/workbench/parts/dashboard/common/dashboardWidget';
|
||||
|
||||
export const Extensions = {
|
||||
dashboardContainerContributions: 'dashboard.contributions.container'
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { DialogModal } from 'sql/platform/dialog/dialogModal';
|
||||
import { WizardModal } from 'sql/platform/dialog/wizardModal';
|
||||
import { Dialog, Wizard } from 'sql/platform/dialog/dialogTypes';
|
||||
import { DialogModal } from 'sql/platform/dialog/browser/dialogModal';
|
||||
import { WizardModal } from 'sql/platform/dialog/browser/wizardModal';
|
||||
import { Dialog, Wizard } from 'sql/platform/dialog/common/dialogTypes';
|
||||
import { IModalOptions } from 'sql/workbench/browser/modal/modal';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
@@ -10,21 +10,22 @@ import { FormsModule } from '@angular/forms';
|
||||
import { CommonModule, APP_BASE_HREF } from '@angular/common';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { DialogContainer } from 'sql/platform/dialog/dialogContainer.component';
|
||||
import { WizardNavigation } from 'sql/platform/dialog/wizardNavigation.component';
|
||||
import { Extensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
|
||||
import { ModelViewContent } from 'sql/workbench/electron-browser/modelComponents/modelViewContent.component';
|
||||
import { ModelComponentWrapper } from 'sql/workbench/electron-browser/modelComponents/modelComponentWrapper.component';
|
||||
import { ComponentHostDirective } from 'sql/workbench/parts/dashboard/common/componentHost.directive';
|
||||
import { IBootstrapParams, ISelector, providerIterator } from 'sql/platform/bootstrap/node/bootstrapService';
|
||||
import { CommonServiceInterface } from 'sql/platform/bootstrap/node/commonServiceInterface.service';
|
||||
import { EditableDropDown } from 'sql/platform/electron-browser/editableDropdown/editableDropdown.component';
|
||||
import { Checkbox } from 'sql/base/electron-browser/ui/checkbox/checkbox.component';
|
||||
import { SelectBox } from 'sql/platform/ui/electron-browser/selectBox/selectBox.component';
|
||||
import { InputBox } from 'sql/base/electron-browser/ui/inputBox/inputBox.component';
|
||||
import { DialogContainer } from 'sql/platform/dialog/browser/dialogContainer.component';
|
||||
import { WizardNavigation } from 'sql/platform/dialog/browser/wizardNavigation.component';
|
||||
import { Extensions, IComponentRegistry } from 'sql/platform/dashboard/browser/modelComponentRegistry';
|
||||
import { ModelViewContent } from 'sql/workbench/browser/modelComponents/modelViewContent.component';
|
||||
import { ModelComponentWrapper } from 'sql/workbench/browser/modelComponents/modelComponentWrapper.component';
|
||||
import { ComponentHostDirective } from 'sql/workbench/parts/dashboard/browser/core/componentHost.directive';
|
||||
import { providerIterator } from 'sql/platform/bootstrap/browser/bootstrapService';
|
||||
import { CommonServiceInterface } from 'sql/platform/bootstrap/browser/commonServiceInterface.service';
|
||||
import { EditableDropDown } from 'sql/platform/browser/editableDropdown/editableDropdown.component';
|
||||
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
|
||||
import { SelectBox } from 'sql/platform/browser/selectBox/selectBox.component';
|
||||
import { InputBox } from 'sql/platform/browser/inputbox/inputBox.component';
|
||||
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IBootstrapParams, ISelector } from 'sql/platform/bootstrap/common/bootstrapParams';
|
||||
|
||||
export const DialogModule = (params, selector: string, instantiationService: IInstantiationService): any => {
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
|
||||
import 'vs/css!./media/dialogModal';
|
||||
import { Component, ViewChild, Inject, forwardRef, ElementRef, AfterViewInit } from '@angular/core';
|
||||
import { ModelViewContent } from 'sql/workbench/electron-browser/modelComponents/modelViewContent.component';
|
||||
import { IBootstrapParams } from 'sql/platform/bootstrap/node/bootstrapService';
|
||||
import { DialogPane } from 'sql/platform/dialog/dialogPane';
|
||||
import { ComponentEventType } from 'sql/workbench/electron-browser/modelComponents/interfaces';
|
||||
import { ModelViewContent } from 'sql/workbench/browser/modelComponents/modelViewContent.component';
|
||||
import { DialogPane } from 'sql/platform/dialog/browser/dialogPane';
|
||||
import { ComponentEventType } from 'sql/workbench/browser/modelComponents/interfaces';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IBootstrapParams } from 'sql/platform/bootstrap/common/bootstrapParams';
|
||||
|
||||
export interface LayoutRequestParams {
|
||||
modelViewId?: string;
|
||||
@@ -6,8 +6,8 @@
|
||||
import 'vs/css!./media/dialogModal';
|
||||
import { Modal, IModalOptions } from 'sql/workbench/browser/modal/modal';
|
||||
import { attachModalDialogStyler } from 'sql/platform/theme/common/styler';
|
||||
import { Dialog, DialogButton } from 'sql/platform/dialog/dialogTypes';
|
||||
import { DialogPane } from 'sql/platform/dialog/dialogPane';
|
||||
import { Dialog, DialogButton } from 'sql/platform/dialog/common/dialogTypes';
|
||||
import { DialogPane } from 'sql/platform/dialog/browser/dialogPane';
|
||||
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
@@ -18,7 +18,7 @@ import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { DialogMessage } from '../../workbench/api/common/sqlExtHostTypes';
|
||||
import { DialogMessage } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { append, $ } from 'vs/base/browser/dom';
|
||||
@@ -8,11 +8,11 @@ import 'vs/css!./media/dialogModal';
|
||||
import { NgModuleRef } from '@angular/core';
|
||||
|
||||
import { IModalDialogStyles } from 'sql/workbench/browser/modal/modal';
|
||||
import { DialogTab } from 'sql/platform/dialog/dialogTypes';
|
||||
import { TabbedPanel, IPanelTab, IPanelView } from 'sql/base/browser/ui/panel/panel';
|
||||
import { bootstrapAngular } from 'sql/platform/bootstrap/node/bootstrapService';
|
||||
import { DialogModule } from 'sql/platform/dialog/dialog.module';
|
||||
import { DialogComponentParams, LayoutRequestParams } from 'sql/platform/dialog/dialogContainer.component';
|
||||
import { DialogTab } from 'sql/platform/dialog/common/dialogTypes';
|
||||
import { TabbedPanel } from 'sql/base/browser/ui/panel/panel';
|
||||
import { bootstrapAngular } from 'sql/platform/bootstrap/browser/bootstrapService';
|
||||
import { DialogModule } from 'sql/platform/dialog/browser/dialog.module';
|
||||
import { DialogComponentParams, LayoutRequestParams } from 'sql/platform/dialog/browser/dialogContainer.component';
|
||||
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { IThemable } from 'vs/platform/theme/common/styler';
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -6,11 +6,11 @@
|
||||
import 'vs/css!./media/dialogModal';
|
||||
import { Modal, IModalOptions } from 'sql/workbench/browser/modal/modal';
|
||||
import { attachModalDialogStyler } from 'sql/platform/theme/common/styler';
|
||||
import { Wizard, DialogButton, WizardPage } from 'sql/platform/dialog/dialogTypes';
|
||||
import { DialogPane } from 'sql/platform/dialog/dialogPane';
|
||||
import { bootstrapAngular } from 'sql/platform/bootstrap/node/bootstrapService';
|
||||
import { Wizard, DialogButton, WizardPage } from 'sql/platform/dialog/common/dialogTypes';
|
||||
import { DialogPane } from 'sql/platform/dialog/browser/dialogPane';
|
||||
import { bootstrapAngular } from 'sql/platform/bootstrap/browser/bootstrapService';
|
||||
import { DialogMessage } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { DialogModule } from 'sql/platform/dialog/dialog.module';
|
||||
import { DialogModule } from 'sql/platform/dialog/browser/dialog.module';
|
||||
import { Button } from 'vs/base/browser/ui/button/button';
|
||||
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
@@ -5,11 +5,11 @@
|
||||
|
||||
import 'vs/css!./media/wizardNavigation';
|
||||
import { Component, Inject, forwardRef, ElementRef, AfterViewInit, ChangeDetectorRef, ViewChild } from '@angular/core';
|
||||
import { IBootstrapParams } from 'sql/platform/bootstrap/node/bootstrapService';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Wizard } from './dialogTypes';
|
||||
import { Wizard } from '../common/dialogTypes';
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { IBootstrapParams } from 'sql/platform/bootstrap/common/bootstrapParams';
|
||||
|
||||
export class WizardNavigationParams implements IBootstrapParams {
|
||||
wizard: Wizard;
|
||||
105
src/sql/platform/dialog/test/electron-browser/dialogPane.test.ts
Normal file
105
src/sql/platform/dialog/test/electron-browser/dialogPane.test.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { Dialog, DialogTab } from 'sql/platform/dialog/common/dialogTypes';
|
||||
import { DialogPane } from 'sql/platform/dialog/browser/dialogPane';
|
||||
import { DialogComponentParams } from 'sql/platform/dialog/browser/dialogContainer.component';
|
||||
import { bootstrapAngular } from 'sql/platform/bootstrap/browser/bootstrapService';
|
||||
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
|
||||
|
||||
|
||||
interface BootstrapAngular {
|
||||
(collection, moduleType, container, selectorString, params: DialogComponentParams, input, callbackSetModule): void;
|
||||
}
|
||||
|
||||
suite('Dialog Pane Tests', () => {
|
||||
let dialog: Dialog;
|
||||
let container: HTMLElement;
|
||||
|
||||
let bootstrapSave: BootstrapAngular;
|
||||
|
||||
function setupBootstrap(fn: BootstrapAngular): void {
|
||||
(<any>bootstrapAngular) = fn;
|
||||
}
|
||||
|
||||
setup(() => {
|
||||
dialog = new Dialog('test_dialog');
|
||||
container = document.createElement('div');
|
||||
bootstrapSave = bootstrapAngular;
|
||||
});
|
||||
|
||||
test('Creating a pane from content without tabs initializes the model view content correctly', () => {
|
||||
// If I fill in a dialog's content with the ID of a specific model view provider and then render the dialog
|
||||
let modelViewId = 'test_content';
|
||||
let bootstrapCalls = 0;
|
||||
setupBootstrap((collection, moduleType, container, selectorString, params: DialogComponentParams, input, callbackSetModule) => {
|
||||
assert.equal(params.modelViewId, modelViewId);
|
||||
bootstrapCalls++;
|
||||
});
|
||||
dialog.content = modelViewId;
|
||||
const themeService = new TestThemeService();
|
||||
let dialogPane = new DialogPane(dialog.title, dialog.content, () => undefined, undefined, themeService, false);
|
||||
dialogPane.createBody(container);
|
||||
assert.equal(bootstrapCalls, 1);
|
||||
});
|
||||
|
||||
test('Creating a pane from content with a single tab initializes without showing tabs', () => {
|
||||
// If I fill in a dialog's content with a single tab and then render the dialog
|
||||
let modelViewId = 'test_content';
|
||||
let bootstrapCalls = 0;
|
||||
setupBootstrap((collection, moduleType, container, selectorString, params: DialogComponentParams, input, callbackSetModule) => {
|
||||
assert.equal(params.modelViewId, modelViewId);
|
||||
bootstrapCalls++;
|
||||
});
|
||||
dialog.content = [new DialogTab('', modelViewId)];
|
||||
const themeService = new TestThemeService();
|
||||
let dialogPane = new DialogPane(dialog.title, dialog.content, () => undefined, undefined, themeService, false);
|
||||
dialogPane.createBody(container);
|
||||
assert.equal(bootstrapCalls, 1);
|
||||
});
|
||||
|
||||
test('Dialog validation gets set based on the validity of the model view content', () => {
|
||||
// Set up the mock bootstrap service to intercept validation callbacks
|
||||
let validationCallbacks: ((valid: boolean) => void)[] = [];
|
||||
|
||||
setupBootstrap((collection, moduleType, container, selectorString, params: DialogComponentParams, input, callbackSetModule) => {
|
||||
validationCallbacks.push(params.validityChangedCallback);
|
||||
});
|
||||
|
||||
let modelViewId1 = 'test_content_1';
|
||||
let modelViewId2 = 'test_content_2';
|
||||
dialog.content = [new DialogTab('tab1', modelViewId1), new DialogTab('tab2', modelViewId2)];
|
||||
const themeService = new TestThemeService();
|
||||
let dialogPane = new DialogPane(dialog.title, dialog.content, valid => dialog.notifyValidityChanged(valid), undefined, themeService, false);
|
||||
dialogPane.createBody(container);
|
||||
|
||||
let validityChanges: boolean[] = [];
|
||||
|
||||
dialog.onValidityChanged(valid => validityChanges.push(valid));
|
||||
// If I set tab 2's validation to false
|
||||
validationCallbacks[1](false);
|
||||
// Then the whole dialog's validation is false
|
||||
assert.equal(dialog.valid, false);
|
||||
assert.equal(validityChanges.length, 1);
|
||||
assert.equal(validityChanges[0], false);
|
||||
// If I then set it back to true
|
||||
validationCallbacks[1](true);
|
||||
// Then the whole dialog's validation is true
|
||||
assert.equal(dialog.valid, true);
|
||||
assert.equal(validityChanges.length, 2);
|
||||
assert.equal(validityChanges[1], true);
|
||||
// If I set tab 1's validation to false
|
||||
validationCallbacks[0](false);
|
||||
// Then the whole dialog's validation is false
|
||||
assert.equal(dialog.valid, false);
|
||||
assert.equal(validityChanges.length, 3);
|
||||
assert.equal(validityChanges[2], false);
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
(<any>bootstrapAngular) = bootstrapSave;
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
|
||||
|
||||
export class TestErrorMessageService implements IErrorMessageService {
|
||||
_serviceBrand: any;
|
||||
showDialog(severity: Severity, headerTitle: string, message: string): void {
|
||||
}
|
||||
}
|
||||
@@ -8,18 +8,18 @@ import * as nls from 'vs/nls';
|
||||
import * as azdata from 'azdata';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { JobHistoryComponent } from 'sql/workbench/parts/jobManagement/electron-browser/jobHistory.component';
|
||||
import { JobHistoryComponent } from 'sql/workbench/parts/jobManagement/browser/jobHistory.component';
|
||||
import { IJobManagementService } from '../common/interfaces';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { JobsViewComponent } from 'sql/workbench/parts/jobManagement/electron-browser/jobsView.component';
|
||||
import { AlertsViewComponent } from 'sql/workbench/parts/jobManagement/electron-browser/alertsView.component';
|
||||
import { OperatorsViewComponent } from 'sql/workbench/parts/jobManagement/electron-browser/operatorsView.component';
|
||||
import { ProxiesViewComponent } from 'sql/workbench/parts/jobManagement/electron-browser/proxiesView.component';
|
||||
import { JobsViewComponent } from 'sql/workbench/parts/jobManagement/browser/jobsView.component';
|
||||
import { AlertsViewComponent } from 'sql/workbench/parts/jobManagement/browser/alertsView.component';
|
||||
import { OperatorsViewComponent } from 'sql/workbench/parts/jobManagement/browser/operatorsView.component';
|
||||
import { ProxiesViewComponent } from 'sql/workbench/parts/jobManagement/browser/proxiesView.component';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/telemetryKeys';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
|
||||
import { JobManagementView } from 'sql/workbench/parts/jobManagement/electron-browser/jobManagementView';
|
||||
import { JobManagementView } from 'sql/workbench/parts/jobManagement/browser/jobManagementView';
|
||||
|
||||
export const successLabel: string = nls.localize('jobaction.successLabel', 'Success');
|
||||
export const errorLabel: string = nls.localize('jobaction.faillabel', 'Error');
|
||||
@@ -0,0 +1,17 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { JobManagementService } from 'sql/platform/jobManagement/common/jobManagementService';
|
||||
|
||||
// TESTS ///////////////////////////////////////////////////////////////////
|
||||
suite('Job Management service tests', () => {
|
||||
setup(() => {
|
||||
});
|
||||
|
||||
test('Construction - Job Service Initialization', () => {
|
||||
// ... Create instance of the service and reder account picker
|
||||
let service = new JobManagementService(undefined);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,376 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import { JobsRefreshAction, NewJobAction, EditJobAction, RunJobAction, StopJobAction, DeleteJobAction, NewStepAction, DeleteStepAction, NewAlertAction, EditAlertAction, DeleteAlertAction, NewOperatorAction, EditOperatorAction, DeleteOperatorAction, NewProxyAction, EditProxyAction, DeleteProxyAction } from 'sql/platform/jobManagement/browser/jobActions';
|
||||
import { JobManagementService } from 'sql/platform/jobManagement/common/jobManagementService';
|
||||
|
||||
// Mock View Components
|
||||
let mockJobsViewComponent: TypeMoq.Mock<TestJobManagementView>;
|
||||
let mockAlertsViewComponent: TypeMoq.Mock<TestJobManagementView>;
|
||||
let mockOperatorsViewComponent: TypeMoq.Mock<TestJobManagementView>;
|
||||
let mockProxiesViewComponent: TypeMoq.Mock<TestJobManagementView>;
|
||||
let mockJobManagementService: TypeMoq.Mock<JobManagementService>;
|
||||
|
||||
// Mock Job Actions
|
||||
let mockRefreshAction: TypeMoq.Mock<JobsRefreshAction>;
|
||||
let mockNewJobAction: TypeMoq.Mock<NewJobAction>;
|
||||
let mockEditJobAction: TypeMoq.Mock<EditJobAction>;
|
||||
let mockRunJobAction: TypeMoq.Mock<RunJobAction>;
|
||||
let mockStopJobAction: TypeMoq.Mock<StopJobAction>;
|
||||
let mockDeleteJobAction: TypeMoq.Mock<DeleteJobAction>;
|
||||
|
||||
// Mock Step Actions
|
||||
let mockNewStepAction: TypeMoq.Mock<NewStepAction>;
|
||||
let mockDeleteStepAction: TypeMoq.Mock<DeleteStepAction>;
|
||||
|
||||
// Mock Alert Actions
|
||||
let mockNewAlertAction: TypeMoq.Mock<NewAlertAction>;
|
||||
let mockEditAlertAction: TypeMoq.Mock<EditAlertAction>;
|
||||
let mockDeleteAlertAction: TypeMoq.Mock<DeleteAlertAction>;
|
||||
|
||||
// Mock Operator Actions
|
||||
let mockNewOperatorAction: TypeMoq.Mock<NewOperatorAction>;
|
||||
let mockEditOperatorAction: TypeMoq.Mock<EditOperatorAction>;
|
||||
let mockDeleteOperatorAction: TypeMoq.Mock<DeleteOperatorAction>;
|
||||
|
||||
// Mock Proxy Actions
|
||||
let mockNewProxyAction: TypeMoq.Mock<NewProxyAction>;
|
||||
let mockEditProxyAction: TypeMoq.Mock<EditProxyAction>;
|
||||
let mockDeleteProxyAction: TypeMoq.Mock<DeleteProxyAction>;
|
||||
|
||||
/**
|
||||
* Class to test Job Management Views
|
||||
*/
|
||||
class TestJobManagementView {
|
||||
|
||||
refreshJobs() { return undefined; }
|
||||
|
||||
openCreateJobDialog() { return undefined; }
|
||||
|
||||
openCreateAlertDialog() { return undefined; }
|
||||
|
||||
openCreateOperatorDialog() { return undefined; }
|
||||
|
||||
openCreateProxyDialog() { return undefined; }
|
||||
}
|
||||
|
||||
// Tests
|
||||
suite('Job Management Actions', () => {
|
||||
|
||||
// Job Actions
|
||||
setup(() => {
|
||||
mockJobsViewComponent = TypeMoq.Mock.ofType<TestJobManagementView>(TestJobManagementView);
|
||||
mockAlertsViewComponent = TypeMoq.Mock.ofType<TestJobManagementView>(TestJobManagementView);
|
||||
mockOperatorsViewComponent = TypeMoq.Mock.ofType<TestJobManagementView>(TestJobManagementView);
|
||||
mockProxiesViewComponent = TypeMoq.Mock.ofType<TestJobManagementView>(TestJobManagementView);
|
||||
mockJobManagementService = TypeMoq.Mock.ofType<JobManagementService>(JobManagementService);
|
||||
let resultStatus: azdata.ResultStatus = {
|
||||
success: true,
|
||||
errorMessage: null
|
||||
};
|
||||
mockJobManagementService.setup(s => s.jobAction(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(resultStatus));
|
||||
});
|
||||
|
||||
test('Jobs Refresh Action', (done) => {
|
||||
mockRefreshAction = TypeMoq.Mock.ofType(JobsRefreshAction, TypeMoq.MockBehavior.Strict, JobsRefreshAction.ID, JobsRefreshAction.LABEL);
|
||||
mockRefreshAction.setup(s => s.run(TypeMoq.It.isAny())).returns(() => mockJobsViewComponent.object.refreshJobs());
|
||||
mockRefreshAction.setup(s => s.id).returns(() => JobsRefreshAction.ID);
|
||||
mockRefreshAction.setup(s => s.label).returns(() => JobsRefreshAction.LABEL);
|
||||
should(mockRefreshAction.object.id).equal(JobsRefreshAction.ID);
|
||||
should(mockRefreshAction.object.label).equal(JobsRefreshAction.LABEL);
|
||||
|
||||
// Job Refresh Action from Jobs View should refresh the component
|
||||
mockRefreshAction.object.run(null);
|
||||
mockJobsViewComponent.verify(c => c.refreshJobs(), TypeMoq.Times.once());
|
||||
done();
|
||||
});
|
||||
|
||||
test('New Job Action', (done) => {
|
||||
mockNewJobAction = TypeMoq.Mock.ofType(NewJobAction, TypeMoq.MockBehavior.Strict, NewJobAction.ID, NewJobAction.LABEL);
|
||||
mockNewJobAction.setup(s => s.run(TypeMoq.It.isAny())).returns(() => mockJobsViewComponent.object.openCreateJobDialog());
|
||||
mockNewJobAction.setup(s => s.id).returns(() => NewJobAction.ID);
|
||||
mockNewJobAction.setup(s => s.label).returns(() => NewJobAction.LABEL);
|
||||
should(mockNewJobAction.object.id).equal(NewJobAction.ID);
|
||||
should(mockNewJobAction.object.label).equal(NewJobAction.LABEL);
|
||||
|
||||
// New Job Action from Jobs View should open a dialog
|
||||
mockNewJobAction.object.run(null);
|
||||
mockJobsViewComponent.verify(c => c.openCreateJobDialog(), TypeMoq.Times.once());
|
||||
done();
|
||||
});
|
||||
|
||||
test('Edit Job Action', (done) => {
|
||||
mockEditJobAction = TypeMoq.Mock.ofType(EditJobAction, TypeMoq.MockBehavior.Strict, EditJobAction.ID, EditJobAction.LABEL);
|
||||
let commandServiceCalled: boolean = false;
|
||||
mockEditJobAction.setup(s => s.run(TypeMoq.It.isAny())).returns(() => {
|
||||
commandServiceCalled = true;
|
||||
return Promise.resolve(commandServiceCalled);
|
||||
});
|
||||
mockEditJobAction.setup(s => s.id).returns(() => EditJobAction.ID);
|
||||
mockEditJobAction.setup(s => s.label).returns(() => EditJobAction.LABEL);
|
||||
should(mockEditJobAction.object.id).equal(EditJobAction.ID);
|
||||
should(mockEditJobAction.object.label).equal(EditJobAction.LABEL);
|
||||
|
||||
// Edit Job Action from Jobs View should open a dialog
|
||||
mockEditJobAction.object.run(null);
|
||||
should(commandServiceCalled).equal(true);
|
||||
done();
|
||||
});
|
||||
|
||||
test('Run Job Action', (done) => {
|
||||
mockRunJobAction = TypeMoq.Mock.ofType(RunJobAction, TypeMoq.MockBehavior.Strict, RunJobAction.ID, RunJobAction.LABEL, null, null, mockJobManagementService);
|
||||
mockRunJobAction.setup(s => s.run(TypeMoq.It.isAny())).returns(async () => {
|
||||
let result = await mockJobManagementService.object.jobAction(null, null, null).then((result) => result.success);
|
||||
return result;
|
||||
});
|
||||
|
||||
mockRunJobAction.setup(s => s.id).returns(() => RunJobAction.ID);
|
||||
mockRunJobAction.setup(s => s.label).returns(() => RunJobAction.LABEL);
|
||||
should(mockRunJobAction.object.id).equal(RunJobAction.ID);
|
||||
should(mockRunJobAction.object.label).equal(RunJobAction.LABEL);
|
||||
|
||||
// Run Job Action should make the Job Management service call job action
|
||||
mockRunJobAction.object.run(null);
|
||||
mockJobManagementService.verify(s => s.jobAction(null, null, null), TypeMoq.Times.once());
|
||||
done();
|
||||
});
|
||||
|
||||
test('Stop Job Action', (done) => {
|
||||
mockStopJobAction = TypeMoq.Mock.ofType(StopJobAction, TypeMoq.MockBehavior.Strict, StopJobAction.ID, StopJobAction.LABEL, null, null, mockJobManagementService);
|
||||
mockStopJobAction.setup(s => s.run(TypeMoq.It.isAny())).returns(async () => {
|
||||
let result = await mockJobManagementService.object.jobAction(null, null, null).then((result) => result.success);
|
||||
return result;
|
||||
});
|
||||
|
||||
mockStopJobAction.setup(s => s.id).returns(() => RunJobAction.ID);
|
||||
mockStopJobAction.setup(s => s.label).returns(() => RunJobAction.LABEL);
|
||||
should(mockStopJobAction.object.id).equal(RunJobAction.ID);
|
||||
should(mockStopJobAction.object.label).equal(RunJobAction.LABEL);
|
||||
|
||||
// Run Job Action should make the Job Management service call job action
|
||||
mockStopJobAction.object.run(null);
|
||||
mockJobManagementService.verify(s => s.jobAction(null, null, null), TypeMoq.Times.once());
|
||||
done();
|
||||
});
|
||||
|
||||
test('Delete Job Action', (done) => {
|
||||
mockDeleteJobAction = TypeMoq.Mock.ofType(DeleteJobAction, TypeMoq.MockBehavior.Strict, DeleteJobAction.ID, DeleteJobAction.LABEL, null, null, mockJobManagementService);
|
||||
mockDeleteJobAction.setup(s => s.run(TypeMoq.It.isAny())).returns(async () => {
|
||||
let result = await mockJobManagementService.object.jobAction(null, null, null).then((result) => result.success);
|
||||
return result;
|
||||
});
|
||||
|
||||
mockDeleteJobAction.setup(s => s.id).returns(() => DeleteJobAction.ID);
|
||||
mockDeleteJobAction.setup(s => s.label).returns(() => DeleteJobAction.LABEL);
|
||||
should(mockDeleteJobAction.object.id).equal(DeleteJobAction.ID);
|
||||
should(mockDeleteJobAction.object.label).equal(DeleteJobAction.LABEL);
|
||||
|
||||
// Run Job Action should make the Job Management service call job action
|
||||
mockDeleteJobAction.object.run(null);
|
||||
mockJobManagementService.verify(s => s.jobAction(null, null, null), TypeMoq.Times.once());
|
||||
done();
|
||||
});
|
||||
|
||||
// Step Actions
|
||||
test('New Step Action', (done) => {
|
||||
mockNewStepAction = TypeMoq.Mock.ofType(NewStepAction, TypeMoq.MockBehavior.Strict, NewJobAction.ID, NewJobAction.LABEL);
|
||||
let commandServiceCalled = false;
|
||||
mockNewStepAction.setup(s => s.run(TypeMoq.It.isAny())).returns(() => {
|
||||
commandServiceCalled = true;
|
||||
return Promise.resolve(commandServiceCalled);
|
||||
});
|
||||
mockNewStepAction.setup(s => s.id).returns(() => NewJobAction.ID);
|
||||
mockNewStepAction.setup(s => s.label).returns(() => NewJobAction.LABEL);
|
||||
should(mockNewStepAction.object.id).equal(NewJobAction.ID);
|
||||
should(mockNewStepAction.object.label).equal(NewJobAction.LABEL);
|
||||
|
||||
// New Step Action should called command service
|
||||
mockNewStepAction.object.run(null);
|
||||
should(commandServiceCalled).equal(true);
|
||||
done();
|
||||
});
|
||||
|
||||
test('Delete Step Action', (done) => {
|
||||
mockDeleteStepAction = TypeMoq.Mock.ofType(DeleteStepAction, TypeMoq.MockBehavior.Strict, DeleteStepAction.ID, DeleteStepAction.LABEL);
|
||||
let commandServiceCalled = false;
|
||||
mockDeleteStepAction.setup(s => s.run(TypeMoq.It.isAny())).returns(async () => {
|
||||
commandServiceCalled = true;
|
||||
await mockJobManagementService.object.deleteJobStep(null, null).then((result) => result.success);
|
||||
return Promise.resolve(commandServiceCalled);
|
||||
});
|
||||
mockDeleteStepAction.setup(s => s.id).returns(() => DeleteStepAction.ID);
|
||||
mockDeleteStepAction.setup(s => s.label).returns(() => DeleteStepAction.LABEL);
|
||||
should(mockDeleteStepAction.object.id).equal(DeleteStepAction.ID);
|
||||
should(mockDeleteStepAction.object.label).equal(DeleteStepAction.LABEL);
|
||||
|
||||
// Delete Step Action should called command service
|
||||
mockDeleteStepAction.object.run(null);
|
||||
should(commandServiceCalled).equal(true);
|
||||
mockJobManagementService.verify(s => s.deleteJobStep(null, null), TypeMoq.Times.once());
|
||||
done();
|
||||
});
|
||||
|
||||
// Alert Actions
|
||||
test('New Alert Action', (done) => {
|
||||
mockNewAlertAction = TypeMoq.Mock.ofType(NewJobAction, TypeMoq.MockBehavior.Strict, NewJobAction.ID, NewJobAction.LABEL);
|
||||
mockNewAlertAction.setup(s => s.run(TypeMoq.It.isAny())).returns(() => mockAlertsViewComponent.object.openCreateAlertDialog());
|
||||
mockNewAlertAction.setup(s => s.id).returns(() => NewJobAction.ID);
|
||||
mockNewAlertAction.setup(s => s.label).returns(() => NewJobAction.LABEL);
|
||||
should(mockNewAlertAction.object.id).equal(NewJobAction.ID);
|
||||
should(mockNewAlertAction.object.label).equal(NewJobAction.LABEL);
|
||||
|
||||
// New Alert Action from Alerts View should open a dialog
|
||||
mockNewAlertAction.object.run(null);
|
||||
mockAlertsViewComponent.verify(c => c.openCreateAlertDialog(), TypeMoq.Times.once());
|
||||
done();
|
||||
});
|
||||
|
||||
test('Edit Alert Action', (done) => {
|
||||
mockEditAlertAction = TypeMoq.Mock.ofType(EditAlertAction, TypeMoq.MockBehavior.Strict, EditAlertAction.ID, EditAlertAction.LABEL);
|
||||
let commandServiceCalled: boolean = false;
|
||||
mockEditAlertAction.setup(s => s.run(TypeMoq.It.isAny())).returns(() => {
|
||||
commandServiceCalled = true;
|
||||
return Promise.resolve(commandServiceCalled);
|
||||
});
|
||||
mockEditAlertAction.setup(s => s.id).returns(() => EditAlertAction.ID);
|
||||
mockEditAlertAction.setup(s => s.label).returns(() => EditAlertAction.LABEL);
|
||||
should(mockEditAlertAction.object.id).equal(EditAlertAction.ID);
|
||||
should(mockEditAlertAction.object.label).equal(EditAlertAction.LABEL);
|
||||
|
||||
// Edit Alert Action from Jobs View should open a dialog
|
||||
mockEditAlertAction.object.run(null);
|
||||
should(commandServiceCalled).equal(true);
|
||||
done();
|
||||
});
|
||||
|
||||
test('Delete Alert Action', (done) => {
|
||||
mockDeleteAlertAction = TypeMoq.Mock.ofType(DeleteAlertAction, TypeMoq.MockBehavior.Strict, DeleteAlertAction.ID, DeleteAlertAction.LABEL, null, null, mockJobManagementService);
|
||||
let commandServiceCalled = false;
|
||||
mockDeleteAlertAction.setup(s => s.run(TypeMoq.It.isAny())).returns(async () => {
|
||||
commandServiceCalled = true;
|
||||
await mockJobManagementService.object.deleteAlert(null, null).then((result) => result.success);
|
||||
return commandServiceCalled;
|
||||
});
|
||||
mockDeleteAlertAction.setup(s => s.id).returns(() => DeleteAlertAction.ID);
|
||||
mockDeleteAlertAction.setup(s => s.label).returns(() => DeleteAlertAction.LABEL);
|
||||
should(mockDeleteAlertAction.object.id).equal(DeleteAlertAction.ID);
|
||||
should(mockDeleteAlertAction.object.label).equal(DeleteAlertAction.LABEL);
|
||||
|
||||
// Delete Alert Action should call job management service
|
||||
mockDeleteAlertAction.object.run(null);
|
||||
should(commandServiceCalled).equal(true);
|
||||
mockJobManagementService.verify(s => s.deleteAlert(null, null), TypeMoq.Times.once());
|
||||
done();
|
||||
});
|
||||
|
||||
// Operator Tests
|
||||
test('New Operator Action', (done) => {
|
||||
mockNewOperatorAction = TypeMoq.Mock.ofType(NewOperatorAction, TypeMoq.MockBehavior.Strict, NewOperatorAction.ID, NewOperatorAction.LABEL);
|
||||
mockNewOperatorAction.setup(s => s.run(TypeMoq.It.isAny())).returns(() => mockOperatorsViewComponent.object.openCreateOperatorDialog());
|
||||
mockNewOperatorAction.setup(s => s.id).returns(() => NewOperatorAction.ID);
|
||||
mockNewOperatorAction.setup(s => s.label).returns(() => NewOperatorAction.LABEL);
|
||||
should(mockNewOperatorAction.object.id).equal(NewOperatorAction.ID);
|
||||
should(mockNewOperatorAction.object.label).equal(NewOperatorAction.LABEL);
|
||||
|
||||
// New Operator Action from Operators View should open a dialog
|
||||
mockNewOperatorAction.object.run(null);
|
||||
mockOperatorsViewComponent.verify(c => c.openCreateOperatorDialog(), TypeMoq.Times.once());
|
||||
done();
|
||||
});
|
||||
|
||||
test('Edit Operator Action', (done) => {
|
||||
mockEditOperatorAction = TypeMoq.Mock.ofType(EditOperatorAction, TypeMoq.MockBehavior.Strict, EditOperatorAction.ID, EditOperatorAction.LABEL);
|
||||
let commandServiceCalled: boolean = false;
|
||||
mockEditOperatorAction.setup(s => s.run(TypeMoq.It.isAny())).returns(() => {
|
||||
commandServiceCalled = true;
|
||||
return Promise.resolve(commandServiceCalled);
|
||||
});
|
||||
mockEditOperatorAction.setup(s => s.id).returns(() => EditOperatorAction.ID);
|
||||
mockEditOperatorAction.setup(s => s.label).returns(() => EditOperatorAction.LABEL);
|
||||
should(mockEditOperatorAction.object.id).equal(EditOperatorAction.ID);
|
||||
should(mockEditOperatorAction.object.label).equal(EditOperatorAction.LABEL);
|
||||
|
||||
// Edit Operator Action from Jobs View should open a dialog
|
||||
mockEditOperatorAction.object.run(null);
|
||||
should(commandServiceCalled).equal(true);
|
||||
done();
|
||||
});
|
||||
|
||||
test('Delete Operator Action', (done) => {
|
||||
mockDeleteOperatorAction = TypeMoq.Mock.ofType(DeleteOperatorAction, TypeMoq.MockBehavior.Strict, DeleteOperatorAction.ID, DeleteOperatorAction.LABEL, null, null, mockJobManagementService);
|
||||
let commandServiceCalled = false;
|
||||
mockDeleteOperatorAction.setup(s => s.run(TypeMoq.It.isAny())).returns(async () => {
|
||||
commandServiceCalled = true;
|
||||
await mockJobManagementService.object.deleteOperator(null, null).then((result) => result.success);
|
||||
return commandServiceCalled;
|
||||
});
|
||||
mockDeleteOperatorAction.setup(s => s.id).returns(() => DeleteOperatorAction.ID);
|
||||
mockDeleteOperatorAction.setup(s => s.label).returns(() => DeleteOperatorAction.LABEL);
|
||||
should(mockDeleteOperatorAction.object.id).equal(DeleteOperatorAction.ID);
|
||||
should(mockDeleteOperatorAction.object.label).equal(DeleteOperatorAction.LABEL);
|
||||
|
||||
// Delete Operator Action should call job management service
|
||||
mockDeleteOperatorAction.object.run(null);
|
||||
should(commandServiceCalled).equal(true);
|
||||
mockJobManagementService.verify(s => s.deleteOperator(null, null), TypeMoq.Times.once());
|
||||
done();
|
||||
});
|
||||
|
||||
// Proxy Actions
|
||||
test('New Proxy Action', (done) => {
|
||||
mockNewProxyAction = TypeMoq.Mock.ofType(NewProxyAction, TypeMoq.MockBehavior.Strict, NewProxyAction.ID, NewProxyAction.LABEL);
|
||||
mockNewProxyAction.setup(s => s.run(TypeMoq.It.isAny())).returns(() => mockProxiesViewComponent.object.openCreateProxyDialog());
|
||||
mockNewProxyAction.setup(s => s.id).returns(() => NewProxyAction.ID);
|
||||
mockNewProxyAction.setup(s => s.label).returns(() => NewProxyAction.LABEL);
|
||||
should(mockNewProxyAction.object.id).equal(NewProxyAction.ID);
|
||||
should(mockNewProxyAction.object.label).equal(NewProxyAction.LABEL);
|
||||
|
||||
// New Proxy Action from Alerts View should open a dialog
|
||||
mockNewProxyAction.object.run(null);
|
||||
mockProxiesViewComponent.verify(c => c.openCreateProxyDialog(), TypeMoq.Times.once());
|
||||
done();
|
||||
});
|
||||
|
||||
test('Edit Proxy Action', (done) => {
|
||||
mockEditProxyAction = TypeMoq.Mock.ofType(EditProxyAction, TypeMoq.MockBehavior.Strict, EditProxyAction.ID, EditProxyAction.LABEL);
|
||||
let commandServiceCalled: boolean = false;
|
||||
mockEditProxyAction.setup(s => s.run(TypeMoq.It.isAny())).returns(() => {
|
||||
commandServiceCalled = true;
|
||||
return Promise.resolve(commandServiceCalled);
|
||||
});
|
||||
mockEditProxyAction.setup(s => s.id).returns(() => EditProxyAction.ID);
|
||||
mockEditProxyAction.setup(s => s.label).returns(() => EditProxyAction.LABEL);
|
||||
should(mockEditProxyAction.object.id).equal(EditProxyAction.ID);
|
||||
should(mockEditProxyAction.object.label).equal(EditProxyAction.LABEL);
|
||||
|
||||
// Edit Proxy Action from Proxies View should open a dialog
|
||||
mockEditProxyAction.object.run(null);
|
||||
should(commandServiceCalled).equal(true);
|
||||
done();
|
||||
});
|
||||
|
||||
test('Delete Proxy Action', (done) => {
|
||||
mockDeleteProxyAction = TypeMoq.Mock.ofType(DeleteProxyAction, TypeMoq.MockBehavior.Strict, DeleteProxyAction.ID, DeleteProxyAction.LABEL, null, null, mockJobManagementService);
|
||||
let commandServiceCalled = false;
|
||||
mockDeleteProxyAction.setup(s => s.run(TypeMoq.It.isAny())).returns(async () => {
|
||||
commandServiceCalled = true;
|
||||
await mockJobManagementService.object.deleteProxy(null, null).then((result) => result.success);
|
||||
return commandServiceCalled;
|
||||
});
|
||||
mockDeleteProxyAction.setup(s => s.id).returns(() => DeleteProxyAction.ID);
|
||||
mockDeleteProxyAction.setup(s => s.label).returns(() => DeleteProxyAction.LABEL);
|
||||
should(mockDeleteProxyAction.object.id).equal(DeleteProxyAction.ID);
|
||||
should(mockDeleteProxyAction.object.label).equal(DeleteProxyAction.LABEL);
|
||||
|
||||
// Delete Proxy Action should call job management service
|
||||
mockDeleteProxyAction.object.run(null);
|
||||
should(commandServiceCalled).equal(true);
|
||||
mockJobManagementService.verify(s => s.deleteProxy(null, null), TypeMoq.Times.once());
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import { IItemConfig, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { IComponentEventArgs } from 'sql/workbench/electron-browser/modelComponents/interfaces';
|
||||
import { IComponentEventArgs } from 'sql/workbench/browser/modelComponents/interfaces';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
|
||||
export interface IView {
|
||||
|
||||
@@ -8,8 +8,8 @@ import { IConnectionManagementService } from 'sql/platform/connection/common/con
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import * as azdata from 'azdata';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/telemetryKeys';
|
||||
import * as TelemetryUtils from 'sql/platform/telemetry/telemetryUtilities';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import * as TelemetryUtils from 'sql/platform/telemetry/common/telemetryUtilities';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { keys } from 'vs/base/common/map';
|
||||
|
||||
@@ -9,19 +9,16 @@ import QueryRunner from 'sql/platform/query/common/queryRunner';
|
||||
import { DataService } from 'sql/workbench/parts/grid/services/dataService';
|
||||
import { IQueryModelService, IQueryEvent } from 'sql/platform/query/common/queryModel';
|
||||
import { QueryInput } from 'sql/workbench/parts/query/common/queryInput';
|
||||
import { SqlFlavorStatusbarItem } from 'sql/workbench/parts/query/browser/flavorStatus';
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as platform from 'vs/platform/registry/common/platform';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { StatusbarAlignment } from 'vs/platform/statusbar/common/statusbar';
|
||||
|
||||
const selectionSnippetMaxLen = 100;
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ import { ITextResourcePropertiesService } from 'vs/editor/common/services/resour
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
|
||||
import { IGridDataProvider, getResultsString } from 'sql/platform/query/common/gridDataProvider';
|
||||
import { getErrorMessage } from 'sql/workbench/parts/notebook/notebookUtils';
|
||||
import { getErrorMessage } from 'vs/base/common/errors';
|
||||
|
||||
export interface IEditSessionReadyEvent {
|
||||
ownerUri: string;
|
||||
|
||||
@@ -21,8 +21,8 @@ import * as Utils from 'sql/platform/connection/common/utils';
|
||||
import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/common/objectExplorerService';
|
||||
import { ITaskService } from 'sql/platform/tasks/common/tasksService';
|
||||
import { TaskStatus, TaskNode } from 'sql/platform/tasks/common/tasksNode';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/telemetryKeys';
|
||||
import * as TelemetryUtils from 'sql/platform/telemetry/telemetryUtilities';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import * as TelemetryUtils from 'sql/platform/telemetry/common/telemetryUtilities';
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { invalidProvider } from 'sql/base/common/errors';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
115
src/sql/platform/tasks/browser/tasksRegistry.ts
Normal file
115
src/sql/platform/tasks/browser/tasksRegistry.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { MenuRegistry, ICommandAction } from 'vs/platform/actions/common/actions';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ITaskRegistry, ITaskHandler, ITask, ITaskHandlerDescription, ITaskOptions } from 'sql/platform/tasks/common/tasks';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { createCSSRule } from 'vs/base/browser/dom';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { IdGenerator } from 'vs/base/common/idGenerator';
|
||||
|
||||
const ids = new IdGenerator('task-icon-');
|
||||
|
||||
export const TaskRegistry: ITaskRegistry = new class implements ITaskRegistry {
|
||||
|
||||
private _tasks = new Array<string>();
|
||||
private _onTaskRegistered = new Emitter<string>();
|
||||
public readonly onTaskRegistered: Event<string> = this._onTaskRegistered.event;
|
||||
private taskIdToIconClassNameMap: Map<string /* task id */, string /* CSS rule */> = new Map<string, string>();
|
||||
|
||||
registerTask(idOrTask: string | ITask, handler?: ITaskHandler): IDisposable {
|
||||
let disposable: IDisposable;
|
||||
let id: string;
|
||||
if (types.isString(idOrTask)) {
|
||||
disposable = CommandsRegistry.registerCommand(idOrTask, handler);
|
||||
id = idOrTask;
|
||||
} else {
|
||||
if (idOrTask.iconClass) {
|
||||
this.taskIdToIconClassNameMap.set(idOrTask.id, idOrTask.iconClass);
|
||||
}
|
||||
disposable = CommandsRegistry.registerCommand(idOrTask);
|
||||
id = idOrTask.id;
|
||||
}
|
||||
|
||||
this._tasks.push(id);
|
||||
this._onTaskRegistered.fire(id);
|
||||
|
||||
return {
|
||||
dispose: () => {
|
||||
let index = this._tasks.indexOf(id);
|
||||
if (index >= 0) {
|
||||
this._tasks = this._tasks.splice(index, 1);
|
||||
}
|
||||
disposable.dispose();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
getOrCreateTaskIconClassName(item: ICommandAction): string {
|
||||
let iconClass = null;
|
||||
if (this.taskIdToIconClassNameMap.has(item.id)) {
|
||||
iconClass = this.taskIdToIconClassNameMap.get(item.id);
|
||||
} else if (item.iconLocation) {
|
||||
iconClass = ids.nextId();
|
||||
createCSSRule(`.icon.${iconClass}`, `background-image: url("${(item.iconLocation.light || item.iconLocation.dark).toString()}")`);
|
||||
createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: url("${(item.iconLocation.dark).toString()}")`);
|
||||
this.taskIdToIconClassNameMap.set(item.id, iconClass);
|
||||
}
|
||||
return iconClass;
|
||||
}
|
||||
|
||||
getTasks(): string[] {
|
||||
return this._tasks.slice(0);
|
||||
}
|
||||
};
|
||||
|
||||
export abstract class Task {
|
||||
public readonly id: string;
|
||||
public readonly title: string;
|
||||
public readonly iconPathDark: string;
|
||||
public readonly iconPath: { dark: URI; light?: URI; };
|
||||
private readonly _iconClass: string;
|
||||
private readonly _description: ITaskHandlerDescription;
|
||||
|
||||
constructor(private opts: ITaskOptions) {
|
||||
this.id = opts.id;
|
||||
this.title = opts.title;
|
||||
this.iconPath = {
|
||||
dark: opts.iconPath ? URI.parse(opts.iconPath.dark) : undefined,
|
||||
light: opts.iconPath ? URI.parse(opts.iconPath.light) : undefined,
|
||||
};
|
||||
this._iconClass = opts.iconClass;
|
||||
this._description = opts.description;
|
||||
}
|
||||
|
||||
private toITask(): ITask {
|
||||
return {
|
||||
id: this.id,
|
||||
handler: (accessor, profile, args) => this.runTask(accessor, profile, args),
|
||||
description: this._description,
|
||||
iconClass: this._iconClass
|
||||
};
|
||||
}
|
||||
|
||||
private toCommandAction(): ICommandAction {
|
||||
return {
|
||||
iconLocation: this.iconPath,
|
||||
id: this.id,
|
||||
title: this.title
|
||||
};
|
||||
}
|
||||
|
||||
public registerTask(): IDisposable {
|
||||
MenuRegistry.addCommand(this.toCommandAction());
|
||||
return TaskRegistry.registerTask(this.toITask());
|
||||
}
|
||||
|
||||
public abstract runTask(accessor: ServicesAccessor, profile: IConnectionProfile, args: any): void | Promise<void>;
|
||||
}
|
||||
@@ -6,14 +6,11 @@
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { ILocalizedString, MenuRegistry, ICommandAction } from 'vs/platform/actions/common/actions';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { ILocalizedString, ICommandAction } from 'vs/platform/actions/common/actions';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IdGenerator } from 'vs/base/common/idGenerator';
|
||||
import { createCSSRule } from 'vs/base/browser/dom';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export interface ITaskOptions {
|
||||
@@ -24,50 +21,6 @@ export interface ITaskOptions {
|
||||
iconClass?: string;
|
||||
}
|
||||
|
||||
export abstract class Task {
|
||||
public readonly id: string;
|
||||
public readonly title: string;
|
||||
public readonly iconPathDark: string;
|
||||
public readonly iconPath: { dark: URI; light?: URI; };
|
||||
private readonly _iconClass: string;
|
||||
private readonly _description: ITaskHandlerDescription;
|
||||
|
||||
constructor(private opts: ITaskOptions) {
|
||||
this.id = opts.id;
|
||||
this.title = opts.title;
|
||||
this.iconPath = {
|
||||
dark: opts.iconPath ? URI.parse(opts.iconPath.dark) : undefined,
|
||||
light: opts.iconPath ? URI.parse(opts.iconPath.light) : undefined,
|
||||
};
|
||||
this._iconClass = opts.iconClass;
|
||||
this._description = opts.description;
|
||||
}
|
||||
|
||||
private toITask(): ITask {
|
||||
return {
|
||||
id: this.id,
|
||||
handler: (accessor, profile, args) => this.runTask(accessor, profile, args),
|
||||
description: this._description,
|
||||
iconClass: this._iconClass
|
||||
};
|
||||
}
|
||||
|
||||
private toCommandAction(): ICommandAction {
|
||||
return {
|
||||
iconLocation: this.iconPath,
|
||||
id: this.id,
|
||||
title: this.title
|
||||
};
|
||||
}
|
||||
|
||||
public registerTask(): IDisposable {
|
||||
MenuRegistry.addCommand(this.toCommandAction());
|
||||
return TaskRegistry.registerTask(this.toITask());
|
||||
}
|
||||
|
||||
public abstract runTask(accessor: ServicesAccessor, profile: IConnectionProfile, args: any): void | Promise<void>;
|
||||
}
|
||||
|
||||
export interface ITaskHandlerDescription {
|
||||
description: string;
|
||||
args: { name: string; description?: string; constraint?: types.TypeConstraint; }[];
|
||||
@@ -105,58 +58,3 @@ export interface ITaskRegistry {
|
||||
getOrCreateTaskIconClassName(item: ICommandAction): string;
|
||||
onTaskRegistered: Event<string>;
|
||||
}
|
||||
|
||||
const ids = new IdGenerator('task-icon-');
|
||||
|
||||
export const TaskRegistry: ITaskRegistry = new class implements ITaskRegistry {
|
||||
|
||||
private _tasks = new Array<string>();
|
||||
private _onTaskRegistered = new Emitter<string>();
|
||||
public readonly onTaskRegistered: Event<string> = this._onTaskRegistered.event;
|
||||
private taskIdToIconClassNameMap: Map<string /* task id */, string /* CSS rule */> = new Map<string, string>();
|
||||
|
||||
registerTask(idOrTask: string | ITask, handler?: ITaskHandler): IDisposable {
|
||||
let disposable: IDisposable;
|
||||
let id: string;
|
||||
if (types.isString(idOrTask)) {
|
||||
disposable = CommandsRegistry.registerCommand(idOrTask, handler);
|
||||
id = idOrTask;
|
||||
} else {
|
||||
if (idOrTask.iconClass) {
|
||||
this.taskIdToIconClassNameMap.set(idOrTask.id, idOrTask.iconClass);
|
||||
}
|
||||
disposable = CommandsRegistry.registerCommand(idOrTask);
|
||||
id = idOrTask.id;
|
||||
}
|
||||
|
||||
this._tasks.push(id);
|
||||
this._onTaskRegistered.fire(id);
|
||||
|
||||
return {
|
||||
dispose: () => {
|
||||
let index = this._tasks.indexOf(id);
|
||||
if (index >= 0) {
|
||||
this._tasks = this._tasks.splice(index, 1);
|
||||
}
|
||||
disposable.dispose();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
getOrCreateTaskIconClassName(item: ICommandAction): string {
|
||||
let iconClass = null;
|
||||
if (this.taskIdToIconClassNameMap.has(item.id)) {
|
||||
iconClass = this.taskIdToIconClassNameMap.get(item.id);
|
||||
} else if (item.iconLocation) {
|
||||
iconClass = ids.nextId();
|
||||
createCSSRule(`.icon.${iconClass}`, `background-image: url("${(item.iconLocation.light || item.iconLocation.dark).toString()}")`);
|
||||
createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: url("${(item.iconLocation.dark).toString()}")`);
|
||||
this.taskIdToIconClassNameMap.set(item.id, iconClass);
|
||||
}
|
||||
return iconClass;
|
||||
}
|
||||
|
||||
getTasks(): string[] {
|
||||
return this._tasks.slice(0);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,179 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { TelemetryView } from 'sql/platform/telemetry/telemetryKeys';
|
||||
import { IAdsTelemetryService } from 'sql/platform/telemetry/telemetry';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { ICommandService, ICommandEvent } from 'vs/platform/commands/common/commands';
|
||||
|
||||
export class SqlTelemetryContribution extends Disposable implements IWorkbenchContribution {
|
||||
|
||||
constructor(
|
||||
@IAdsTelemetryService private telemetryService: IAdsTelemetryService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@ICommandService commandService: ICommandService
|
||||
) {
|
||||
super();
|
||||
|
||||
this._register(
|
||||
commandService.onWillExecuteCommand(
|
||||
(e: ICommandEvent) => {
|
||||
// Filter out high-frequency events
|
||||
if (!['type', 'cursorUp', 'cursorDown', 'cursorRight', 'cursorLeft', 'deleteLeft', 'deleteRight'].find(id => id === e.commandId)) {
|
||||
telemetryService.sendActionEvent(TelemetryView.Shell, 'adsCommandExecuted', e.commandId);
|
||||
}
|
||||
}));
|
||||
const dailyLastUseDate: number = Date.parse(storageService.get('telemetry.dailyLastUseDate', StorageScope.GLOBAL, '0'));
|
||||
const weeklyLastUseDate: number = Date.parse(storageService.get('telemetry.weeklyLastUseDate', StorageScope.GLOBAL, '0'));
|
||||
const monthlyLastUseDate: number = Date.parse(storageService.get('telemetry.monthlyLastUseDate', StorageScope.GLOBAL, '0'));
|
||||
const firstTimeUser: boolean = dailyLastUseDate === Date.parse('0');
|
||||
|
||||
let todayString: string = new Date().toUTCString();
|
||||
|
||||
// daily user event
|
||||
if (this.didDayChange(dailyLastUseDate)) {
|
||||
// daily first use
|
||||
telemetryService.sendTelemetryEvent('telemetry.dailyFirstUse', { dailyFirstUse: 'true' });
|
||||
storageService.store('telemetry.dailyLastUseDate', todayString, StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
// weekly user event
|
||||
if (this.didWeekChange(weeklyLastUseDate)) {
|
||||
// weekly first use
|
||||
telemetryService.sendTelemetryEvent('telemetry.weeklyFirstUse', { weeklyFirstUse: 'true' });
|
||||
storageService.store('telemetry.weeklyLastUseDate', todayString, StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
|
||||
/* send monthly uses once the user launches on a day that's in a month
|
||||
after the last time we sent a monthly usage count */
|
||||
const monthlyUseCount: number = storageService.getNumber('telemetry.monthlyUseCount', StorageScope.GLOBAL, 0);
|
||||
if (this.didMonthChange(monthlyLastUseDate)) {
|
||||
telemetryService.sendTelemetryEvent('telemetry.monthlyUse', { monthlyFirstUse: 'true' });
|
||||
// the month changed, so send the user usage type event based on monthly count for last month
|
||||
// and reset the count for this month
|
||||
let lastMonthDate = new Date(monthlyLastUseDate);
|
||||
this.sendUsageEvent(monthlyUseCount, lastMonthDate);
|
||||
|
||||
const wasActiveLastMonth: boolean = storageService.getBoolean('telemetry.wasActiveLastMonth', StorageScope.GLOBAL, false);
|
||||
|
||||
if (firstTimeUser) {
|
||||
// new user
|
||||
this.sendGrowthTypeEvent(UserGrowthType.NewUser, lastMonthDate);
|
||||
}
|
||||
|
||||
// continuing or returning user
|
||||
this.sendGrowthTypeEvent(wasActiveLastMonth ? UserGrowthType.ContinuingUser : UserGrowthType.ReturningUser, lastMonthDate);
|
||||
|
||||
// set wasActiveUserLastMonth
|
||||
storageService.store('telemetry.wasActiveLastMonth', true, StorageScope.GLOBAL);
|
||||
|
||||
// reset the monthly count for the new month
|
||||
storageService.store('telemetry.monthlyUseCount', 1, StorageScope.GLOBAL);
|
||||
storageService.store('telemetry.monthlyLastUseDate', todayString, StorageScope.GLOBAL);
|
||||
} else {
|
||||
// if it's the same month, increment the monthly use count
|
||||
storageService.store('telemetry.monthlyUseCount', monthlyUseCount + 1, StorageScope.GLOBAL);
|
||||
}
|
||||
}
|
||||
|
||||
private didDayChange(lastUseDateNumber: number): boolean {
|
||||
let nowDateNumber: number = Date.parse(new Date().toUTCString());
|
||||
if (this.diffInDays(nowDateNumber, lastUseDateNumber) >= 1) {
|
||||
return true;
|
||||
} else {
|
||||
let nowDate = new Date(nowDateNumber);
|
||||
let lastUseDate = new Date(lastUseDateNumber);
|
||||
return nowDate.getUTCDay() !== lastUseDate.getUTCDay();
|
||||
}
|
||||
}
|
||||
|
||||
private didWeekChange(lastUseDateNumber: number): boolean {
|
||||
let nowDateNumber: number = Date.parse(new Date().toUTCString());
|
||||
if (this.diffInDays(nowDateNumber, lastUseDateNumber) >= 7) {
|
||||
return true;
|
||||
} else {
|
||||
let nowDate = new Date(nowDateNumber);
|
||||
let lastUseDate = new Date(lastUseDateNumber);
|
||||
return nowDate.getUTCDay() < lastUseDate.getUTCDay();
|
||||
}
|
||||
}
|
||||
|
||||
private didMonthChange(lastUseDateNumber: number): boolean {
|
||||
let nowDateNumber: number = Date.parse(new Date().toUTCString());
|
||||
if (this.diffInDays(nowDateNumber, lastUseDateNumber) >= 30) {
|
||||
return true;
|
||||
} else {
|
||||
let nowDate = new Date(nowDateNumber);
|
||||
let lastUseDate = new Date(lastUseDateNumber);
|
||||
return nowDate.getUTCMonth() !== lastUseDate.getUTCMonth();
|
||||
}
|
||||
}
|
||||
|
||||
private diffInDays(nowDate: number, lastUseDate: number): number {
|
||||
return (nowDate - lastUseDate) / (3600 * 1000 * 24);
|
||||
}
|
||||
|
||||
// Usage Metrics
|
||||
private sendUsageEvent(monthlyUseCount: number, lastMonthDate: Date): void {
|
||||
let userUsageType: UserUsageType | undefined;
|
||||
if (monthlyUseCount === 1) {
|
||||
userUsageType = UserUsageType.TireKicker;
|
||||
} else if (monthlyUseCount >= 2 && monthlyUseCount <= 11) {
|
||||
userUsageType = UserUsageType.Occasional;
|
||||
} else if (monthlyUseCount >= 12 && monthlyUseCount <= 20) {
|
||||
userUsageType = UserUsageType.Engaged;
|
||||
} else if (monthlyUseCount > 20) {
|
||||
userUsageType = UserUsageType.Dedicated;
|
||||
}
|
||||
if (userUsageType) {
|
||||
this.telemetryService.sendTelemetryEvent('telemetry.userUsage',
|
||||
{ userType: userUsageType.toString(), monthlyUseCount: monthlyUseCount.toString(), month: lastMonthDate.getMonth().toString(), year: lastMonthDate.getFullYear().toString() });
|
||||
}
|
||||
}
|
||||
|
||||
// Growth Metrics
|
||||
private sendGrowthTypeEvent(growthType: UserGrowthType, lastMonthDate: Date): void {
|
||||
this.telemetryService.sendTelemetryEvent('telemetry.userGrowthType', {
|
||||
userGrowthType: growthType.toString(), month: lastMonthDate.getMonth().toString(), year: lastMonthDate.getFullYear().toString()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Growth Metrics
|
||||
* Active here means opened app atleast 1 time in a month
|
||||
*/
|
||||
export enum UserGrowthType {
|
||||
// first time opening app
|
||||
NewUser = 1,
|
||||
// was active before, wasn't active last month, but is active this month
|
||||
ReturningUser = 2,
|
||||
// was active last month and this month
|
||||
ContinuingUser = 3
|
||||
}
|
||||
|
||||
/**
|
||||
* Usage Metrics
|
||||
* TireKicker = 1 day/month
|
||||
* Occasional = 2-11 days/month
|
||||
* Engaged = 12-20 days/month
|
||||
* Dedicated = 20+ days/month
|
||||
*/
|
||||
export enum UserUsageType {
|
||||
/* 1 day per month */
|
||||
TireKicker = 1,
|
||||
/* 2-11 days per month */
|
||||
Occasional = 2,
|
||||
/* 12-20 days per month */
|
||||
Engaged = 3,
|
||||
/* 20+ days per month */
|
||||
Dedicated = 4
|
||||
}
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(SqlTelemetryContribution, LifecyclePhase.Starting);
|
||||
@@ -0,0 +1,107 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as TelemetryUtils from 'sql/platform/telemetry/common/telemetryUtilities';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import * as assert from 'assert';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { SimpleTelemetryService } from 'vs/workbench/browser/web.simpleservices';
|
||||
|
||||
suite('SQL Telemetry Utilities tests', () => {
|
||||
let telemetryService: TypeMoq.Mock<ITelemetryService>;
|
||||
let none: void;
|
||||
let providerName: string = 'provider name';
|
||||
let telemetryKey: string = 'tel key';
|
||||
|
||||
let connectionProfile = {
|
||||
connectionName: '',
|
||||
databaseName: '',
|
||||
serverName: '',
|
||||
authenticationType: '',
|
||||
getOptionsKey: () => '',
|
||||
matches: undefined,
|
||||
groupFullName: '',
|
||||
groupId: '',
|
||||
id: '',
|
||||
options: {},
|
||||
password: '',
|
||||
providerName: providerName,
|
||||
savePassword: true,
|
||||
saveProfile: true,
|
||||
userName: ''
|
||||
};
|
||||
|
||||
setup(() => {
|
||||
telemetryService = TypeMoq.Mock.ofType(SimpleTelemetryService, TypeMoq.MockBehavior.Strict);
|
||||
telemetryService.setup(x => x.publicLog(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(x => Promise.resolve(none));
|
||||
});
|
||||
|
||||
test('addTelemetry should add provider id using the connection', (done) => {
|
||||
let data: TelemetryUtils.IConnectionTelemetryData = {
|
||||
};
|
||||
const logService = new NullLogService();
|
||||
TelemetryUtils.addTelemetry(telemetryService.object, logService, telemetryKey, data, connectionProfile).then(() => {
|
||||
telemetryService.verify(x => x.publicLog(TypeMoq.It.is(a => a === telemetryKey), TypeMoq.It.is(b => b.provider === providerName)), TypeMoq.Times.once());
|
||||
done();
|
||||
}).catch(err => {
|
||||
assert.fail(err);
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('addTelemetry should pass the telemetry data to telemetry service', (done) => {
|
||||
let data: TelemetryUtils.IConnectionTelemetryData = {
|
||||
target: 'target',
|
||||
from: 'from'
|
||||
};
|
||||
data.test1 = '1';
|
||||
|
||||
const logService = new NullLogService();
|
||||
TelemetryUtils.addTelemetry(telemetryService.object, logService, telemetryKey, data, connectionProfile).then(() => {
|
||||
telemetryService.verify(x => x.publicLog(
|
||||
TypeMoq.It.is(a => a === telemetryKey),
|
||||
TypeMoq.It.is(b => b.provider === providerName
|
||||
&& b.from === data.from
|
||||
&& b.target === data.target
|
||||
&& b.test1 === data.test1
|
||||
&& b.connection === undefined)), TypeMoq.Times.once());
|
||||
done();
|
||||
}).catch(err => {
|
||||
assert.fail(err);
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('addTelemetry should not crash not given data', (done) => {
|
||||
|
||||
const logService = new NullLogService();
|
||||
TelemetryUtils.addTelemetry(telemetryService.object, logService, telemetryKey).then(() => {
|
||||
telemetryService.verify(x => x.publicLog(
|
||||
TypeMoq.It.is(a => a === telemetryKey),
|
||||
TypeMoq.It.is(b => b !== undefined)), TypeMoq.Times.once());
|
||||
done();
|
||||
}).catch(err => {
|
||||
assert.fail(err);
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('addTelemetry should try to get the provider name from data first', (done) => {
|
||||
let data: TelemetryUtils.IConnectionTelemetryData = {
|
||||
connection: connectionProfile
|
||||
};
|
||||
data.provider = providerName + '1';
|
||||
|
||||
const logService = new NullLogService();
|
||||
TelemetryUtils.addTelemetry(telemetryService.object, logService, telemetryKey, data, connectionProfile).then(() => {
|
||||
telemetryService.verify(x => x.publicLog(TypeMoq.It.is(a => a === telemetryKey), TypeMoq.It.is(b => b.provider === data.provider)), TypeMoq.Times.once());
|
||||
done();
|
||||
}).catch(err => {
|
||||
assert.fail(err);
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -10,7 +10,6 @@ import * as cr from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IThemable, attachStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { SIDE_BAR_BACKGROUND, SIDE_BAR_SECTION_HEADER_FOREGROUND, SIDE_BAR_SECTION_HEADER_BACKGROUND, SIDE_BAR_DRAG_AND_DROP_BACKGROUND, PANEL_INACTIVE_TITLE_FOREGROUND, PANEL_ACTIVE_TITLE_BORDER, PANEL_ACTIVE_TITLE_FOREGROUND } from 'vs/workbench/common/theme';
|
||||
import { IPanelColors } from 'vs/workbench/browser/parts/views/panelViewlet';
|
||||
|
||||
export function attachModalDialogStyler(widget: IThemable, themeService: IThemeService, style?:
|
||||
{
|
||||
@@ -272,7 +271,7 @@ export function attachCheckboxStyler(widget: IThemable, themeService: IThemeServ
|
||||
}
|
||||
|
||||
export function attachPanelStyler(widget: IThemable, themeService: IThemeService) {
|
||||
return attachStyler<IPanelColors>(themeService, {
|
||||
return attachStyler(themeService, {
|
||||
headerForeground: SIDE_BAR_SECTION_HEADER_FOREGROUND,
|
||||
headerBackground: SIDE_BAR_SECTION_HEADER_BACKGROUND,
|
||||
// headerHighContrastBorder: index === 0 ? null : contrastBorder,
|
||||
|
||||
Reference in New Issue
Block a user