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:
Anthony Dresser
2019-07-18 17:29:17 -07:00
committed by GitHub
parent 45c13116de
commit c23738f935
576 changed files with 2090 additions and 2788 deletions

View File

@@ -0,0 +1,460 @@
/*---------------------------------------------------------------------------------------------
* 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 { AccountProviderStub, TestAccountManagementService } from 'sql/platform/accounts/test/common/testAccountManagementService';
import { ExtHostAccountManagement } from 'sql/workbench/api/common/extHostAccountManagement';
import { TestRPCProtocol } from 'vs/workbench/test/electron-browser/api/testRPCProtocol';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { IRPCProtocol } from 'vs/workbench/services/extensions/common/proxyIdentifier';
import { SqlMainContext } from 'sql/workbench/api/common/sqlExtHost.protocol';
import { MainThreadAccountManagement } from 'sql/workbench/api/browser/mainThreadAccountManagement';
import { IAccountManagementService, AzureResource } from 'sql/platform/accounts/common/interfaces';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
const IRPCProtocol = createDecorator<IRPCProtocol>('rpcProtocol');
// SUITE STATE /////////////////////////////////////////////////////////////
let instantiationService: TestInstantiationService;
let mockAccountMetadata: azdata.AccountProviderMetadata;
let mockAccount: azdata.Account;
let threadService: TestRPCProtocol;
// TESTS ///////////////////////////////////////////////////////////////////
suite('ExtHostAccountManagement', () => {
suiteSetup(() => {
threadService = new TestRPCProtocol();
const accountMgmtStub = new TestAccountManagementService();
instantiationService = new TestInstantiationService();
instantiationService.stub(IRPCProtocol, threadService);
instantiationService.stub(IAccountManagementService, accountMgmtStub);
const accountMgmtService = instantiationService.createInstance(MainThreadAccountManagement, undefined);
threadService.set(SqlMainContext.MainThreadAccountManagement, accountMgmtService);
mockAccountMetadata = {
args: {},
displayName: 'Test Account Provider',
id: 'test_account_provider',
settings: {}
};
mockAccount = {
key: {
providerId: mockAccountMetadata.id,
providerArgs: {},
accountId: 'test_account'
},
properties: {},
displayInfo: {
displayName: 'Test Account',
userId: 'user@email.com',
contextualDisplayName: 'Test Kind Of Account',
accountType: 'test'
},
isStale: false
};
});
test('Constructor', () => {
// If: I construct a new extension host account management
let extHost = new ExtHostAccountManagement(threadService);
// Then: There shouldn't be any account providers registered
assert.equal(extHost.getProviderCount(), 0);
});
// REGISTER TESTS //////////////////////////////////////////////////////
test('Register Account Provider - Success', () => {
// Setup: Create an extension host account management
let extHost = new ExtHostAccountManagement(threadService);
let mockProvider = getMockAccountProvider();
// If: I register a mock account provider
extHost.$registerAccountProvider(mockAccountMetadata, mockProvider.object);
// Then: The account provider should be registered
assert.equal(extHost.getProviderCount(), 1);
});
test('Register Account Provider - Account Provider Already Registered', () => {
// Setup: Create an extension host account management and register an account provider
let extHost = new ExtHostAccountManagement(threadService);
let mockProvider = getMockAccountProvider();
extHost.$registerAccountProvider(mockAccountMetadata, mockProvider.object);
// If: I register an account provider again
// Then
// ... It should throw
assert.throws(() => {
extHost.$registerAccountProvider(mockAccountMetadata, mockProvider.object);
});
// ... There should only be one account provider
assert.equal(extHost.getProviderCount(), 1);
});
// TODO: Test for unregistering a provider
// CLEAR TESTS /////////////////////////////////////////////////////////
test('Clear - Success', (done) => {
// Setup: Create ext host account management with registered account provider
let extHost = new ExtHostAccountManagement(threadService);
let mockProvider = getMockAccountProvider();
extHost.$registerAccountProvider(mockAccountMetadata, mockProvider.object);
// If: I clear an account
extHost.$clear(0, mockAccount.key)
.then(() => {
// Then: The call should have been passed to the provider
mockProvider.verify(
(obj) => obj.clear(TypeMoq.It.isValue(mockAccount.key)),
TypeMoq.Times.once()
);
})
.then(() => done(), (err) => done(err));
});
test('Clear - Handle does not exist', (done) => {
// Setup: Create ext host account management with registered account provider
let extHost = new ExtHostAccountManagement(threadService);
let mockProvider = getMockAccountProvider();
extHost.$registerAccountProvider(mockAccountMetadata, mockProvider.object);
// If: I clear an account for a handle that doesn't exist
// Then: It should fail
extHost.$clear(1, mockAccount.key)
.then(() => done('Clear succeeded when it should have failed'))
.then(null, () => {
// The provider's clear should not have been called
mockProvider.verify(
(obj) => obj.clear(TypeMoq.It.isAny()),
TypeMoq.Times.never()
);
})
.then(() => done(), (err) => done(err));
});
// INITIALIZE TESTS ////////////////////////////////////////////////////
test('Initialize - Success', (done) => {
// Setup: Create ext host account management with registered account provider
let extHost = new ExtHostAccountManagement(threadService);
let mockProvider = getMockAccountProvider();
extHost.$registerAccountProvider(mockAccountMetadata, mockProvider.object);
// If: I initialize the provider
extHost.$initialize(0, [mockAccount])
.then(() => {
// Then: The call should have been passed to the provider
mockProvider.verify(
(obj) => obj.initialize(TypeMoq.It.isValue([mockAccount])),
TypeMoq.Times.once()
);
})
.then(() => done(), (err) => done(err));
});
test('Initialize - Handle does not exist', (done) => {
// Setup: Create ext host account management with registered account provider
let extHost = new ExtHostAccountManagement(threadService);
let mockProvider = getMockAccountProvider();
extHost.$registerAccountProvider(mockAccountMetadata, mockProvider.object);
// If: I initialize for a handle that doesn't exist
// Then: It should fail
extHost.$initialize(1, [mockAccount])
.then(() => done('Initialize succeeded when it should have failed'))
.then(null, () => {
// The provider's clear should not have been called
mockProvider.verify(
(obj) => obj.initialize(TypeMoq.It.isAny()),
TypeMoq.Times.never()
);
})
.then(() => done(), (err) => done(err));
});
// PROMPT TESTS ////////////////////////////////////////////////////////
test('Prompt - Success', (done) => {
// Setup: Create ext host account management with registered account provider
let extHost = new ExtHostAccountManagement(threadService);
let mockProvider = getMockAccountProvider();
extHost.$registerAccountProvider(mockAccountMetadata, mockProvider.object);
// If: I prompt for an account
extHost.$prompt(0)
.then(() => {
// Then: The call should have been passed to the provider
mockProvider.verify(
(obj) => obj.prompt(),
TypeMoq.Times.once()
);
})
.then(() => done(), (err) => done(err));
});
test('Prompt - Handle does not exist', (done) => {
// Setup: Create ext host account management with registered account provider
let extHost = new ExtHostAccountManagement(threadService);
let mockProvider = getMockAccountProvider();
extHost.$registerAccountProvider(mockAccountMetadata, mockProvider.object);
// If: I prompt with a handle that doesn't exist
// Then: It should fail
extHost.$prompt(1)
.then(() => done('Prompt succeeded when it should have failed'))
.then(null, () => {
// The provider's clear should not have been called
mockProvider.verify(
(obj) => obj.prompt(),
TypeMoq.Times.never()
);
})
.then(() => done(), (err) => done(err));
});
// REFRESH TESTS ///////////////////////////////////////////////////////
test('Refresh - Success', (done) => {
// Setup: Create ext host account management with registered account provider
let extHost = new ExtHostAccountManagement(threadService);
let mockProvider = getMockAccountProvider();
extHost.$registerAccountProvider(mockAccountMetadata, mockProvider.object);
// If: I refresh an account
extHost.$refresh(0, mockAccount)
.then(() => {
// Then: The call should have been passed to the provider
mockProvider.verify(
(obj) => obj.refresh(TypeMoq.It.isValue(mockAccount)),
TypeMoq.Times.once()
);
})
.then(() => done(), (err) => done(err));
});
test('Refresh - Handle does not exist', (done) => {
// Setup: Create ext host account management with registered account provider
let extHost = new ExtHostAccountManagement(threadService);
let mockProvider = getMockAccountProvider();
extHost.$registerAccountProvider(mockAccountMetadata, mockProvider.object);
// If: I refresh an account for a handle that doesn't exist
// Then: It should fail
extHost.$refresh(1, mockAccount)
.then(() => done('Refresh succeeded when it should have failed'))
.then(null, () => {
// The provider's clear should not have been called
mockProvider.verify(
(obj) => obj.refresh(TypeMoq.It.isAny()),
TypeMoq.Times.never()
);
})
.then(() => done(), (err) => done(err));
});
// GETALLACCOUNTS TESTS ///////////////////////////////////////////////////////
test('GetAllAccounts - Success', (done) => {
let mockAccountProviderMetadata = {
id: 'azure',
displayName: 'Azure'
};
let mockAccount1 = {
key: {
providerId: mockAccountProviderMetadata.id,
accountId: 'azure_account_1'
},
displayInfo: {
contextualDisplayName: 'Microsoft Account',
accountType: 'microsoft',
displayName: 'Azure Account 1',
userId: 'user@email.com'
},
properties: [],
isStale: false
};
let mockAccount2 = {
key: {
providerId: mockAccountProviderMetadata.id,
accountId: 'azure_account_2'
},
displayInfo: {
contextualDisplayName: 'Work/School Account',
accountType: 'microsoft',
displayName: 'Azure Account 2',
userId: 'user@email.com'
},
properties: [],
isStale: false
};
let mockAccounts = [mockAccount1, mockAccount2];
let expectedAccounts = [mockAccount1, mockAccount2];
let mockAccountManagementService = getMockAccountManagementService(mockAccounts);
instantiationService.stub(IAccountManagementService, mockAccountManagementService.object);
let accountManagementService = instantiationService.createInstance(MainThreadAccountManagement, undefined);
threadService.set(SqlMainContext.MainThreadAccountManagement, accountManagementService);
// Setup: Create ext host account management with registered account provider
let extHost = new ExtHostAccountManagement(threadService);
extHost.$registerAccountProvider(mockAccountProviderMetadata, new AccountProviderStub());
// If: I get all accounts
extHost.$getAllAccounts()
.then((accounts) => {
// Then: The call should have been passed to the account management service
mockAccountManagementService.verify(
(obj) => obj.getAccountsForProvider(TypeMoq.It.isAny()),
TypeMoq.Times.once()
);
assert.ok(Array.isArray(accounts));
assert.equal(accounts.length, expectedAccounts.length);
assert.deepStrictEqual(accounts, expectedAccounts);
})
.then(() => done(), (err) => done(err));
});
test('GetAllAccounts - No account providers', (done) => {
// Setup: Create ext host account management with no registered account providers
let extHost = new ExtHostAccountManagement(threadService);
// If: I get all accounts
// Then: It should throw
assert.throws(
() => extHost.$getAllAccounts(),
(error) => {
return error.message === 'No account providers registered.';
});
done();
});
test('GetSecurityToken - Success', (done) => {
let mockAccountProviderMetadata = {
id: 'azure',
displayName: 'Azure'
};
let mockAccount1 = {
key: {
providerId: mockAccountProviderMetadata.id,
accountId: 'azure_account_1'
},
displayInfo: {
contextualDisplayName: 'Microsoft Account',
accountType: 'microsoft',
displayName: 'Azure Account 1',
userId: 'user@email.com'
},
properties: [],
isStale: false
};
let mockAccounts = [mockAccount1];
let mockAccountManagementService = getMockAccountManagementService(mockAccounts);
instantiationService.stub(IAccountManagementService, mockAccountManagementService.object);
let accountManagementService = instantiationService.createInstance(MainThreadAccountManagement, undefined);
threadService.set(SqlMainContext.MainThreadAccountManagement, accountManagementService);
// Setup: Create ext host account management with registered account provider
let extHost = new ExtHostAccountManagement(threadService);
extHost.$registerAccountProvider(mockAccountProviderMetadata, new AccountProviderStub());
extHost.$getAllAccounts()
.then((accounts) => {
// If: I get security token it will not throw
return extHost.$getSecurityToken(mockAccount1, AzureResource.ResourceManagement);
}
).then(() => done(), (err) => done(new Error(err)));
});
test('GetSecurityToken - Account not found', (done) => {
let mockAccountProviderMetadata = {
id: 'azure',
displayName: 'Azure'
};
let mockAccount1 = {
key: {
providerId: mockAccountProviderMetadata.id,
accountId: 'azure_account_1'
},
displayInfo: {
contextualDisplayName: 'Microsoft Account',
accountType: 'microsoft',
displayName: 'Azure Account 1',
userId: 'user@email.com'
},
properties: [],
isStale: false
};
let mockAccounts = [mockAccount1];
let mockAccountManagementService = getMockAccountManagementService(mockAccounts);
instantiationService.stub(IAccountManagementService, mockAccountManagementService.object);
let accountManagementService = instantiationService.createInstance(MainThreadAccountManagement, undefined);
threadService.set(SqlMainContext.MainThreadAccountManagement, accountManagementService);
// Setup: Create ext host account management with registered account provider
let extHost = new ExtHostAccountManagement(threadService);
extHost.$registerAccountProvider(mockAccountProviderMetadata, new AccountProviderStub());
let mockAccount2 = {
key: {
providerId: mockAccountProviderMetadata.id,
accountId: 'azure_account_2'
},
displayInfo: {
contextualDisplayName: 'Work/School Account',
accountType: 'microsoft',
displayName: 'Azure Account 2',
userId: 'user@email.com'
},
properties: [],
isStale: false
};
extHost.$getAllAccounts()
.then(accounts => {
return extHost.$getSecurityToken(mockAccount2, AzureResource.ResourceManagement);
})
.then((noError) => {
done(new Error('Expected getSecurityToken to throw'));
}, (err) => {
// Expected error caught
done();
});
});
});
function getMockAccountProvider(): TypeMoq.Mock<azdata.AccountProvider> {
let mock = TypeMoq.Mock.ofType<azdata.AccountProvider>(AccountProviderStub);
mock.setup((obj) => obj.clear(TypeMoq.It.isValue(mockAccount.key)))
.returns(() => Promise.resolve(undefined));
mock.setup((obj) => obj.refresh(TypeMoq.It.isValue(mockAccount)))
.returns(() => Promise.resolve(undefined));
mock.setup((obj) => obj.initialize(TypeMoq.It.isValue([mockAccount])))
.returns(() => Promise.resolve(undefined));
mock.setup((obj) => obj.prompt())
.returns(() => Promise.resolve(undefined));
return mock;
}
function getMockAccountManagementService(accounts: azdata.Account[]): TypeMoq.Mock<TestAccountManagementService> {
let mockAccountManagementService = TypeMoq.Mock.ofType(TestAccountManagementService);
mockAccountManagementService.setup(x => x.getAccountsForProvider(TypeMoq.It.isAny()))
.returns(() => Promise.resolve(accounts));
mockAccountManagementService.setup(x => x.getSecurityToken(TypeMoq.It.isValue(accounts[0]), TypeMoq.It.isAny()))
.returns(() => Promise.resolve({}));
mockAccountManagementService.setup(x => x.updateAccountListEvent)
.returns(() => () => { return undefined; });
return mockAccountManagementService;
}

View File

@@ -0,0 +1,106 @@
/*---------------------------------------------------------------------------------------------
* 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 { Mock, It, Times } from 'typemoq';
import { ExtHostBackgroundTaskManagement, TaskStatus } from 'sql/workbench/api/common/extHostBackgroundTaskManagement';
import { MainThreadBackgroundTaskManagementShape } from 'sql/workbench/api/common/sqlExtHost.protocol';
import { IMainContext } from 'vs/workbench/api/common/extHost.protocol';
suite('ExtHostBackgroundTaskManagement Tests', () => {
let extHostBackgroundTaskManagement: ExtHostBackgroundTaskManagement;
let mockProxy: Mock<MainThreadBackgroundTaskManagementShape>;
let nothing: void;
let operationId = 'operation is';
setup(() => {
mockProxy = Mock.ofInstance(<MainThreadBackgroundTaskManagementShape>{
$registerTask: (taskInfo: azdata.TaskInfo) => nothing,
$updateTask: (taskProgressInfo: azdata.TaskProgressInfo) => nothing
});
let mainContext = <IMainContext>{
getProxy: proxyType => mockProxy.object
};
mockProxy.setup(x => x.$registerTask(It.isAny())).callback(() => {
extHostBackgroundTaskManagement.$onTaskRegistered(operationId);
});
extHostBackgroundTaskManagement = new ExtHostBackgroundTaskManagement(mainContext);
});
test('RegisterTask should successfully create background task and update status', () => {
let operationInfo: azdata.BackgroundOperationInfo = {
connection: undefined,
description: 'description',
displayName: 'displayName',
isCancelable: true,
operation: (op: azdata.BackgroundOperation) => { op.updateStatus(TaskStatus.Succeeded); },
operationId: operationId
};
extHostBackgroundTaskManagement.$registerTask(operationInfo);
mockProxy.verify(x => x.$registerTask(It.is(
t => t.name === operationInfo.displayName &&
t.description === operationInfo.description &&
t.taskId === operationId &&
t.isCancelable === operationInfo.isCancelable &&
t.providerName === undefined
)), Times.once());
mockProxy.verify(x => x.$updateTask(It.is(t => t.status === TaskStatus.Succeeded)), Times.once());
extHostBackgroundTaskManagement.$removeTask(operationId);
});
test('Canceling the task should notify the extension', () => {
let operationInfo: azdata.BackgroundOperationInfo = {
connection: undefined,
description: 'description',
displayName: 'displayName',
isCancelable: true,
operation: (op: azdata.BackgroundOperation) => {
op.onCanceled(() => {
op.updateStatus(TaskStatus.Canceled);
});
},
operationId: operationId
};
extHostBackgroundTaskManagement.$registerTask(operationInfo);
extHostBackgroundTaskManagement.$onTaskCanceled(operationId);
mockProxy.verify(x => x.$updateTask(It.is(t => t.status === TaskStatus.Canceled)), Times.once());
extHostBackgroundTaskManagement.$removeTask(operationId);
});
test('RegisterTask should assign unique id to the operation is not assigned', () => {
let operationInfo: azdata.BackgroundOperationInfo = {
connection: undefined,
description: 'description',
displayName: 'displayName',
isCancelable: true,
operation: (op: azdata.BackgroundOperation) => { op.updateStatus(TaskStatus.Succeeded); },
operationId: undefined
};
extHostBackgroundTaskManagement.$registerTask(operationInfo);
mockProxy.verify(x => x.$registerTask(It.is(t => t.taskId !== undefined)), Times.once());
extHostBackgroundTaskManagement.$removeTask(operationId);
});
test('RegisterTask should fail given id of an existing operation', () => {
let operationInfo: azdata.BackgroundOperationInfo = {
connection: undefined,
description: 'description',
displayName: 'displayName',
isCancelable: true,
operation: (op: azdata.BackgroundOperation) => { op.updateStatus(TaskStatus.Succeeded); },
operationId: operationId
};
extHostBackgroundTaskManagement.$registerTask(operationInfo);
mockProxy.verify(x => x.$registerTask(It.is(t => t.taskId === operationId)), Times.once());
assert.throws(() => extHostBackgroundTaskManagement.$registerTask(operationInfo));
extHostBackgroundTaskManagement.$removeTask(operationId);
});
});

View File

@@ -0,0 +1,131 @@
/*---------------------------------------------------------------------------------------------
* 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 { TestRPCProtocol } from 'vs/workbench/test/electron-browser/api/testRPCProtocol';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { ExtHostCredentialManagement } from 'sql/workbench/api/common/extHostCredentialManagement';
import { SqlMainContext } from 'sql/workbench/api/common/sqlExtHost.protocol';
import { IRPCProtocol } from 'vs/workbench/services/extensions/common/proxyIdentifier';
import { MainThreadCredentialManagement } from 'sql/workbench/api/browser/mainThreadCredentialManagement';
import { ICredentialsService } from 'sql/platform/credentials/common/credentialsService';
import { Credential, CredentialProvider } from 'azdata';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { TestCredentialsService, TestCredentialsProvider } from 'sql/platform/credentials/test/common/testCredentialsService';
const IRPCProtocol = createDecorator<IRPCProtocol>('rpcProtocol');
// SUITE STATE /////////////////////////////////////////////////////////////
let credentialServiceStub: TestCredentialsService;
let instantiationService: TestInstantiationService;
let threadService: TestRPCProtocol;
// TESTS ///////////////////////////////////////////////////////////////////
suite('ExtHostCredentialManagement', () => {
suiteSetup(() => {
threadService = new TestRPCProtocol();
credentialServiceStub = new TestCredentialsService();
instantiationService = new TestInstantiationService();
instantiationService.stub(IRPCProtocol, threadService);
instantiationService.stub(ICredentialsService, credentialServiceStub);
const credentialService = instantiationService.createInstance(MainThreadCredentialManagement, undefined);
threadService.set(SqlMainContext.MainThreadCredentialManagement, credentialService);
});
test('Construct ExtHostCredentialManagement', () => {
// If: I construct an credential management extension host
let extHost = new ExtHostCredentialManagement(threadService);
// Then: The extension host should not have any providers registered
assert.equal(extHost.getProviderCount(), 0);
});
test('Register Credential Provider', () => {
// Setup: Create a mock credential provider
let extHost = new ExtHostCredentialManagement(threadService);
let mockCredentialProvider = new TestCredentialsProvider();
// If: I register the credential provider with the extension host
extHost.$registerCredentialProvider(mockCredentialProvider);
// Then: There should be one provider registered
assert.equal(extHost.getProviderCount(), 1);
});
test('Get Credential Provider - Success', (done) => {
// Setup: Register a mock credential provider
let extHost = new ExtHostCredentialManagement(threadService);
let mockCredentialProvider = new TestCredentialsProvider();
extHost.$registerCredentialProvider(mockCredentialProvider);
// If: I get the credential provider
let namespaceId = 'test_namespace';
let credentialId = 'test_id';
let credential = 'test_credential';
let expectedCredentialId = `${namespaceId}|${credentialId}`;
let credProvider: CredentialProvider;
extHost.$getCredentialProvider(namespaceId)
.then((provider) => {
// Then: There should still only be one provider registered
assert.equal(extHost.getProviderCount(), 1);
credProvider = provider;
})
.then(() => {
// If: I write a credential
return credProvider.saveCredential(credentialId, credential);
})
.then(() => {
// Then: The credential should have been stored with its namespace
assert.notStrictEqual(mockCredentialProvider.storedCredentials[expectedCredentialId], undefined);
assert.equal(mockCredentialProvider.storedCredentials[expectedCredentialId].credentialId, expectedCredentialId);
assert.equal(mockCredentialProvider.storedCredentials[expectedCredentialId].password, credential);
})
.then(() => {
// If: I read a credential
return credProvider.readCredential(credentialId);
})
.then((returnedCredential: Credential) => {
// Then: The credential ID should be namespaced
assert.equal(returnedCredential.credentialId, expectedCredentialId);
assert.equal(returnedCredential.password, credential);
})
.then(() => {
// If: I delete a credential
return credProvider.deleteCredential(credentialId);
})
.then(() => {
// Then: The credential with its namespace should no longer exist
assert.strictEqual(mockCredentialProvider.storedCredentials[expectedCredentialId], undefined);
})
.then(() => done(), (err) => done(err));
});
test('Get Credential Provider - No Namespace', (done) => {
// Setup: Register a mock credential provider
let extHost = new ExtHostCredentialManagement(threadService);
let mockCredentialProvider = new TestCredentialsProvider();
extHost.$registerCredentialProvider(mockCredentialProvider);
// If: I get a credential provider with an invalid namespace ID
// Then: I should get an error
extHost.$getCredentialProvider(undefined)
.then(
() => { done('Provider was returned from undefined'); },
() => { /* Swallow error, this is success path */ }
)
.then(() => { return extHost.$getCredentialProvider(null); })
.then(
() => { done('Provider was returned from null'); },
() => { /* Swallow error, this is success path */ }
)
.then(() => { return extHost.$getCredentialProvider(''); })
.then(
() => { done('Provider was returned from \'\''); },
() => { done(); }
);
});
});

View File

@@ -0,0 +1,61 @@
/*---------------------------------------------------------------------------------------------
* 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 { Mock } from 'typemoq';
import * as azdata from 'azdata';
import { ExtHostDataProtocol } from 'sql/workbench/api/common/extHostDataProtocol';
import { DataProviderType } from 'sql/workbench/api/common/sqlExtHostTypes';
suite('ExtHostDataProtocol', () => {
let extHostDataProtocol: ExtHostDataProtocol;
setup(() => {
extHostDataProtocol = new ExtHostDataProtocol({
getProxy: identifier => {
return {
$registerMetadataProvider: (providerId, handle) => Promise.resolve(),
$registerConnectionProvider: (providerId, handle) => Promise.resolve()
} as any;
}
} as any);
});
test('Providers are exposed to other extensions', () => {
let extension1Id = 'provider1';
let extension1MetadataMock = Mock.ofInstance({
getMetadata: () => undefined,
getDatabases: () => undefined,
getTableInfo: () => undefined,
getViewInfo: () => undefined,
providerId: extension1Id
} as azdata.MetadataProvider);
let extension2Id = 'provider2';
let extension2MetadataMock = Mock.ofInstance({
getMetadata: () => undefined,
getDatabases: () => undefined,
getTableInfo: () => undefined,
getViewInfo: () => undefined,
providerId: extension2Id
} as azdata.MetadataProvider);
// If I register both providers and then get them using the getProvider API
extHostDataProtocol.$registerMetadataProvider(extension1MetadataMock.object);
extHostDataProtocol.$registerMetadataProvider(extension2MetadataMock.object);
extHostDataProtocol.$registerConnectionProvider({} as azdata.ConnectionProvider);
let retrievedProvider1 = extHostDataProtocol.getProvider<azdata.MetadataProvider>(extension1Id, DataProviderType.MetadataProvider);
let retrievedProvider2 = extHostDataProtocol.getProvider<azdata.MetadataProvider>(extension2Id, DataProviderType.MetadataProvider);
let allProviders = extHostDataProtocol.getProvidersByType<azdata.MetadataProvider>(DataProviderType.MetadataProvider);
// Then each provider was retrieved successfully
assert.equal(retrievedProvider1, extension1MetadataMock.object, 'Expected metadata provider was not retrieved for extension 1');
assert.equal(retrievedProvider2, extension2MetadataMock.object, 'Expected metadata provider was not retrieved for extension 2');
assert.equal(allProviders.length, 2, 'All metadata providers had unexpected length');
assert.equal(allProviders.some(provider => provider === extension1MetadataMock.object), true, 'All metadata providers did not include extension 1 metadata provider');
assert.equal(allProviders.some(provider => provider === extension2MetadataMock.object), true, 'All metadata providers did not include extension 2 metadata provider');
});
});

View File

@@ -0,0 +1,381 @@
/*---------------------------------------------------------------------------------------------
* 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 { Mock, It, Times, MockBehavior } from 'typemoq';
import * as azdata from 'azdata';
import { ExtHostModelView } from 'sql/workbench/api/common/extHostModelView';
import { MainThreadModelViewShape } from 'sql/workbench/api/common/sqlExtHost.protocol';
import { IMainContext } from 'vs/workbench/api/common/extHost.protocol';
import { IComponentShape, IItemConfig, ComponentEventType, IComponentEventArgs, ModelComponentTypes } from 'sql/workbench/api/common/sqlExtHostTypes';
import { TitledFormItemLayout } from 'sql/workbench/browser/modelComponents/formContainer.component';
interface InternalItemConfig {
toIItemConfig(): IItemConfig;
}
interface IWithItemConfig {
itemConfigs?: InternalItemConfig[];
}
suite('ExtHostModelView Validation Tests', () => {
let extHostModelView: ExtHostModelView;
let mockProxy: Mock<MainThreadModelViewShape>;
let modelView: azdata.ModelView;
let inputBox: azdata.InputBoxComponent;
let validText = 'valid';
let widgetId = 'widget_id';
let handle = 1;
// let viewInitialized: Deferred<void>;
let initializedModels: IComponentShape[];
setup(done => {
// Set up the MainThreadModelViewShape proxy
mockProxy = Mock.ofInstance(<MainThreadModelViewShape>{
$registerProvider: (id: string) => undefined,
$initializeModel: (handle: number, rootComponent: IComponentShape) => undefined,
$clearContainer: (handle: number, componentId: string) => undefined,
$addToContainer: (handle: number, containerId: string, item: IItemConfig) => undefined,
$removeFromContainer: (handle: number, containerId: string, item: IItemConfig) => undefined,
$setLayout: (handle: number, componentId: string, layout: any) => undefined,
$setProperties: (handle: number, componentId: string, properties: { [key: string]: any }) => undefined,
$registerEvent: (handle: number, componentId: string) => undefined,
dispose: () => undefined,
$validate: (handle: number, componentId: string) => undefined
}, MockBehavior.Loose);
let mainContext = <IMainContext>{
getProxy: proxyType => mockProxy.object
};
mockProxy.setup(x => x.$initializeModel(It.isAny(), It.isAny())).returns(() => Promise.resolve());
mockProxy.setup(x => x.$registerEvent(It.isAny(), It.isAny())).returns(() => Promise.resolve());
mockProxy.setup(x => x.$setProperties(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
// Register a model view of an input box and drop down box inside a form container inside a flex container
extHostModelView = new ExtHostModelView(mainContext, undefined);
extHostModelView.$registerProvider(widgetId, async view => {
modelView = view;
inputBox = view.modelBuilder.inputBox()
.withValidation(component => component.value === validText)
.component();
let dropDownBox = view.modelBuilder.dropDown().component();
let formContainer = view.modelBuilder.formContainer()
.withItems([inputBox, dropDownBox])
.component();
let flexContainer = view.modelBuilder.flexContainer()
.withItems([formContainer])
.component();
await view.initializeModel(flexContainer);
done();
}, undefined);
extHostModelView.$registerWidget(handle, widgetId, undefined, undefined);
});
test('The custom validation output of a component gets set when it is initialized', done => {
extHostModelView.$runCustomValidations(handle, inputBox.id).then(valid => {
try {
assert.equal(valid, false, 'Empty input box did not validate as false');
done();
} catch (err) {
done(err);
}
}, err => done(err));
});
test('The custom validation output of a component changes if its value changes', done => {
inputBox.value = validText;
extHostModelView.$runCustomValidations(handle, inputBox.id).then(valid => {
try {
assert.equal(valid, true, 'Valid input box did not validate as valid');
done();
} catch (err) {
done(err);
}
}, err => done(err));
});
test('The custom validation output of a component changes after a PropertiesChanged event', done => {
extHostModelView.$handleEvent(handle, inputBox.id, {
args: {
'value': validText
},
eventType: ComponentEventType.PropertiesChanged
} as IComponentEventArgs);
extHostModelView.$runCustomValidations(handle, inputBox.id).then(valid => {
try {
assert.equal(valid, true, 'Valid input box did not validate as valid after PropertiesChanged event');
done();
} catch (err) {
done(err);
}
}, err => done(err));
});
test('The validity of a component is set by main thread validationChanged events', () => {
assert.equal(inputBox.valid, true, 'Component validity is true by default');
extHostModelView.$handleEvent(handle, inputBox.id, {
eventType: ComponentEventType.validityChanged,
args: false
});
assert.equal(inputBox.valid, false, 'Input box did not update validity to false based on the validityChanged event');
extHostModelView.$handleEvent(handle, inputBox.id, {
eventType: ComponentEventType.validityChanged,
args: true
});
assert.equal(inputBox.valid, true, 'Input box did not update validity to true based on the validityChanged event');
});
test('Main thread validityChanged events cause component to fire validity changed events', () => {
let validityFromEvent: boolean = undefined;
inputBox.onValidityChanged(valid => validityFromEvent = valid);
extHostModelView.$handleEvent(handle, inputBox.id, {
eventType: ComponentEventType.validityChanged,
args: false
});
assert.equal(validityFromEvent, false, 'Main thread validityChanged event did not cause component to fire its own event');
});
test('Setting a form component as required initializes the model with the component required', () => {
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), undefined)).returns(() => Promise.resolve());
// Set up the input component with required initially set to false
let inputComponent = modelView.modelBuilder.inputBox().component();
inputComponent.required = false;
// If I build a form that sets the input component as required
let inputFormComponent: azdata.FormComponent = {
component: inputComponent,
title: 'test_input',
required: true
};
let requiredFormContainer = modelView.modelBuilder.formContainer().withFormItems([inputFormComponent]).component();
modelView.initializeModel(requiredFormContainer);
// Then the input component is sent to the main thread with required set to true
mockProxy.verify(x => x.$initializeModel(It.isAny(), It.is(rootComponent => {
return rootComponent.itemConfigs.length === 1 && rootComponent.itemConfigs[0].componentShape.id === inputComponent.id && rootComponent.itemConfigs[0].componentShape.properties['required'] === true;
})), Times.once());
});
test('Form component groups are handled correctly by adding each item in the group and a label to the form', () => {
// Set up the mock proxy to save the component that gets initialized so that it can be verified
let rootComponent: IComponentShape;
mockProxy.setup(x => x.$initializeModel(It.isAny(), It.isAny())).callback((handle, componentShape) => rootComponent = componentShape);
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), undefined)).returns(() => Promise.resolve());
// Set up the form with a top level component and a group
let topLevelList = modelView.modelBuilder.listBox().component();
let groupInput = modelView.modelBuilder.inputBox().component();
let groupDropdown = modelView.modelBuilder.dropDown().component();
let topLevelInputFormComponent: azdata.FormComponent = { component: topLevelList, title: 'top_level_input' };
let groupInputFormComponent: azdata.FormComponent = { component: groupInput, title: 'group_input' };
let groupDropdownFormComponent: azdata.FormComponent = { component: groupDropdown, title: 'group_dropdown' };
let groupTitle = 'group_title';
// Give the group a default layout and add one just for the input component too
let defaultLayout: azdata.FormItemLayout = {
horizontal: true
};
let groupInputLayout: azdata.FormItemLayout = {
horizontal: false
};
// If I build a form that has a group with a default layout where one item in the group has its own layout
let formContainer = modelView.modelBuilder.formContainer().withFormItems([
topLevelInputFormComponent,
{
components: [
Object.assign(groupInputFormComponent, { layout: groupInputLayout }),
groupDropdownFormComponent
],
title: groupTitle
}
], defaultLayout).component();
modelView.initializeModel(formContainer);
// Then all the items plus a group label are added and have the correct layouts
assert.equal(rootComponent.itemConfigs.length, 4);
let listBoxConfig = rootComponent.itemConfigs[0];
let groupLabelConfig = rootComponent.itemConfigs[1];
let inputBoxConfig = rootComponent.itemConfigs[2];
let dropdownConfig = rootComponent.itemConfigs[3];
// Verify that the correct items were added
assert.equal(listBoxConfig.componentShape.type, ModelComponentTypes.ListBox);
assert.equal(groupLabelConfig.componentShape.type, ModelComponentTypes.Text);
assert.equal(inputBoxConfig.componentShape.type, ModelComponentTypes.InputBox);
assert.equal(dropdownConfig.componentShape.type, ModelComponentTypes.DropDown);
// Verify that the group title was set up correctly
assert.equal(groupLabelConfig.componentShape.properties['value'], groupTitle);
assert.equal((groupLabelConfig.config as TitledFormItemLayout).isGroupLabel, true);
// Verify that the components' layouts are correct
assert.equal((listBoxConfig.config as azdata.FormItemLayout).horizontal, defaultLayout.horizontal);
assert.equal((inputBoxConfig.config as azdata.FormItemLayout).horizontal, groupInputLayout.horizontal);
assert.equal((dropdownConfig.config as azdata.FormItemLayout).horizontal, defaultLayout.horizontal);
});
test('Inserting and removing components from a container should work correctly', () => {
// Set up the mock proxy to save the component that gets initialized so that it can be verified
let rootComponent: IComponentShape;
mockProxy.setup(x => x.$initializeModel(It.isAny(), It.isAny())).callback((handle, componentShape) => rootComponent = componentShape);
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), undefined)).returns(() => Promise.resolve());
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
mockProxy.setup(x => x.$removeFromContainer(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
// Set up the form with a top level component and a group
let listBox = modelView.modelBuilder.listBox().component();
let inputBox = modelView.modelBuilder.inputBox().component();
let dropDown = modelView.modelBuilder.dropDown().component();
let flex = modelView.modelBuilder.flexContainer().withItems([listBox, inputBox]).component();
modelView.initializeModel(flex);
assert.equal((flex as IWithItemConfig).itemConfigs.length, 2);
flex.insertItem(dropDown, 1);
assert.equal((flex as IWithItemConfig).itemConfigs.length, 3);
assert.equal((flex as IWithItemConfig).itemConfigs[1].toIItemConfig().componentShape.type, ModelComponentTypes.DropDown);
flex.removeItem(listBox);
assert.equal((flex as IWithItemConfig).itemConfigs.length, 2);
assert.equal((flex as IWithItemConfig).itemConfigs[0].toIItemConfig().componentShape.type, ModelComponentTypes.DropDown);
});
test('Inserting component give negative number fails', () => {
// Set up the mock proxy to save the component that gets initialized so that it can be verified
let rootComponent: IComponentShape;
mockProxy.setup(x => x.$initializeModel(It.isAny(), It.isAny())).callback((handle, componentShape) => rootComponent = componentShape);
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), undefined)).returns(() => Promise.resolve());
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
mockProxy.setup(x => x.$removeFromContainer(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
// Set up the form with a top level component and a group
let listBox = modelView.modelBuilder.listBox().component();
let inputBox = modelView.modelBuilder.inputBox().component();
let dropDown = modelView.modelBuilder.dropDown().component();
let flex = modelView.modelBuilder.flexContainer().withItems([listBox, inputBox]).component();
modelView.initializeModel(flex);
assert.equal((flex as IWithItemConfig).itemConfigs.length, 2);
assert.throws(() => flex.insertItem(dropDown, -1));
});
test('Inserting component give wrong number fails', () => {
// Set up the mock proxy to save the component that gets initialized so that it can be verified
let rootComponent: IComponentShape;
mockProxy.setup(x => x.$initializeModel(It.isAny(), It.isAny())).callback((handle, componentShape) => rootComponent = componentShape);
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), undefined)).returns(() => Promise.resolve());
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
mockProxy.setup(x => x.$removeFromContainer(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
// Set up the form with a top level component and a group
let listBox = modelView.modelBuilder.listBox().component();
let inputBox = modelView.modelBuilder.inputBox().component();
let dropDown = modelView.modelBuilder.dropDown().component();
let flex = modelView.modelBuilder.flexContainer().withItems([listBox, inputBox]).component();
modelView.initializeModel(flex);
assert.equal((flex as IWithItemConfig).itemConfigs.length, 2);
assert.throws(() => flex.insertItem(dropDown, 10));
});
test('Inserting component give end of the list fails', () => {
// Set up the mock proxy to save the component that gets initialized so that it can be verified
let rootComponent: IComponentShape;
mockProxy.setup(x => x.$initializeModel(It.isAny(), It.isAny())).callback((handle, componentShape) => rootComponent = componentShape);
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), undefined)).returns(() => Promise.resolve());
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
mockProxy.setup(x => x.$removeFromContainer(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
// Set up the form with a top level component and a group
let listBox = modelView.modelBuilder.listBox().component();
let inputBox = modelView.modelBuilder.inputBox().component();
let dropDown = modelView.modelBuilder.dropDown().component();
let flex = modelView.modelBuilder.flexContainer().withItems([listBox, inputBox]).component();
modelView.initializeModel(flex);
assert.equal((flex as IWithItemConfig).itemConfigs.length, 2);
assert.throws(() => flex.insertItem(dropDown, 2));
});
test('Removing a component that does not exist does not fail', () => {
// Set up the mock proxy to save the component that gets initialized so that it can be verified
let rootComponent: IComponentShape;
mockProxy.setup(x => x.$initializeModel(It.isAny(), It.isAny())).callback((handle, componentShape) => rootComponent = componentShape);
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), undefined)).returns(() => Promise.resolve());
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
// Set up the form with a top level component and a group
let listBox = modelView.modelBuilder.listBox().component();
let inputBox = modelView.modelBuilder.inputBox().component();
let dropDown = modelView.modelBuilder.dropDown().component();
let flex = modelView.modelBuilder.flexContainer().withItems([listBox, inputBox]).component();
modelView.initializeModel(flex);
let result = flex.removeItem(dropDown);
assert.equal(result, false);
assert.equal((flex as IWithItemConfig).itemConfigs.length, 2);
assert.equal((flex as IWithItemConfig).itemConfigs[0].toIItemConfig().componentShape.type, ModelComponentTypes.ListBox);
});
test('Inserting and removing component in a form should work correctly', () => {
// Set up the mock proxy to save the component that gets initialized so that it can be verified
let rootComponent: IComponentShape;
mockProxy.setup(x => x.$initializeModel(It.isAny(), It.isAny())).callback((handle, componentShape) => rootComponent = componentShape);
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), undefined)).returns(() => Promise.resolve());
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
mockProxy.setup(x => x.$removeFromContainer(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
// Set up the form with a top level component and a group
let listBox = modelView.modelBuilder.listBox().component();
let inputBox = modelView.modelBuilder.inputBox().component();
let dropDown = modelView.modelBuilder.dropDown().component();
let checkBox = modelView.modelBuilder.checkBox().component();
let groupItems: azdata.FormComponentGroup = {
title: 'Group',
components: [{
title: 'Drop Down',
component: dropDown
}, {
title: 'Check Box',
component: checkBox
}]
};
let listBoxFormItem: azdata.FormComponent = {
title: 'List Box',
component: listBox
};
let inputBoxFormItem: azdata.FormComponent = {
title: 'Input Box',
component: inputBox
};
let formBuilder = modelView.modelBuilder.formContainer();
formBuilder.addFormItem(listBoxFormItem);
let form = formBuilder.component();
modelView.initializeModel(formBuilder.component());
assert.equal((form as IWithItemConfig).itemConfigs.length, 1);
formBuilder.insertFormItem(inputBoxFormItem, 0);
assert.equal((form as IWithItemConfig).itemConfigs.length, 2);
assert.equal((form as IWithItemConfig).itemConfigs[0].toIItemConfig().componentShape.type, ModelComponentTypes.InputBox);
formBuilder.insertFormItem(groupItems, 0);
assert.equal((form as IWithItemConfig).itemConfigs.length, 5);
formBuilder.removeFormItem(listBoxFormItem);
assert.equal((form as IWithItemConfig).itemConfigs.length, 4);
formBuilder.removeFormItem(groupItems);
assert.equal((form as IWithItemConfig).itemConfigs.length, 1);
formBuilder.addFormItem(listBoxFormItem);
assert.equal((form as IWithItemConfig).itemConfigs.length, 2);
assert.equal((form as IWithItemConfig).itemConfigs[1].toIItemConfig().componentShape.type, ModelComponentTypes.ListBox);
});
});

View File

@@ -0,0 +1,329 @@
/*---------------------------------------------------------------------------------------------
* 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 { Mock, It, Times } from 'typemoq';
import { ExtHostModelViewDialog } from 'sql/workbench/api/common/extHostModelViewDialog';
import { MainThreadModelViewDialogShape, ExtHostModelViewShape } from 'sql/workbench/api/common/sqlExtHost.protocol';
import { IMainContext } from 'vs/workbench/api/common/extHost.protocol';
import { MessageLevel } from 'sql/workbench/api/common/sqlExtHostTypes';
suite('ExtHostModelViewDialog Tests', () => {
let extHostModelViewDialog: ExtHostModelViewDialog;
let mockProxy: Mock<MainThreadModelViewDialogShape>;
let extHostModelView: Mock<ExtHostModelViewShape>;
setup(() => {
mockProxy = Mock.ofInstance(<MainThreadModelViewDialogShape>{
$openDialog: handle => undefined,
$closeDialog: handle => undefined,
$setDialogDetails: (handle, details) => undefined,
$setTabDetails: (handle, details) => undefined,
$setButtonDetails: (handle, details) => undefined,
$openWizard: handle => undefined,
$closeWizard: handle => undefined,
$setWizardPageDetails: (handle, details) => undefined,
$setWizardDetails: (handle, details) => undefined
});
let mainContext = <IMainContext>{
getProxy: proxyType => mockProxy.object
};
extHostModelView = Mock.ofInstance(<ExtHostModelViewShape>{
$registerProvider: (widget, handler, extensionLocation) => undefined
});
extHostModelViewDialog = new ExtHostModelViewDialog(mainContext, extHostModelView.object, undefined);
});
test('Creating a dialog returns a dialog with initialized ok and cancel buttons and the given title', () => {
let title = 'dialog_title';
let dialog = extHostModelViewDialog.createDialog(title);
assert.equal(dialog.title, title);
assert.equal(dialog.okButton.enabled, true);
assert.equal(dialog.cancelButton.enabled, true);
});
test('Creating a tab returns a tab with the given title', () => {
let title = 'tab_title';
let tab = extHostModelViewDialog.createTab(title);
assert.equal(tab.title, title);
});
test('Creating a button returns an enabled button with the given label', () => {
let label = 'button_label';
let button = extHostModelViewDialog.createButton(label);
assert.equal(button.label, label);
assert.equal(button.enabled, true);
});
test('Opening a dialog updates its tabs and buttons on the main thread', () => {
mockProxy.setup(x => x.$openDialog(It.isAny()));
mockProxy.setup(x => x.$setDialogDetails(It.isAny(), It.isAny()));
mockProxy.setup(x => x.$setTabDetails(It.isAny(), It.isAny()));
mockProxy.setup(x => x.$setButtonDetails(It.isAny(), It.isAny()));
// Create a dialog with 2 tabs and 2 custom buttons
let dialogTitle = 'dialog_title';
let dialog = extHostModelViewDialog.createDialog(dialogTitle);
let tab1Title = 'tab_1';
let tab1 = extHostModelViewDialog.createTab(tab1Title);
let tab2Title = 'tab_2';
let tab2 = extHostModelViewDialog.createTab(tab2Title);
dialog.content = [tab1, tab2];
let button1Label = 'button_1';
let button1 = extHostModelViewDialog.createButton(button1Label);
button1.enabled = false;
let button2Label = 'button_2';
let button2 = extHostModelViewDialog.createButton(button2Label);
dialog.customButtons = [button1, button2];
// Open the dialog and verify that the correct main thread methods were called
extHostModelViewDialog.openDialog(dialog);
mockProxy.verify(x => x.$setButtonDetails(It.isAny(), It.is(details => {
return details.enabled === false && details.label === button1Label;
})), Times.atLeastOnce());
mockProxy.verify(x => x.$setButtonDetails(It.isAny(), It.is(details => {
return details.enabled === true && details.label === button2Label;
})), Times.atLeastOnce());
mockProxy.verify(x => x.$setTabDetails(It.isAny(), It.is(details => {
return details.title === tab1Title;
})), Times.atLeastOnce());
mockProxy.verify(x => x.$setTabDetails(It.isAny(), It.is(details => {
return details.title === tab2Title;
})), Times.atLeastOnce());
mockProxy.verify(x => x.$setDialogDetails(It.isAny(), It.is(details => {
return details.title === dialogTitle && details.content.length === 2 && details.customButtons.length === 2;
})), Times.atLeastOnce());
mockProxy.verify(x => x.$openDialog(It.isAny()), Times.once());
});
test('Button clicks are forwarded to the correct button', () => {
// Set up the proxy to record button handles
let handles = [];
mockProxy.setup(x => x.$setButtonDetails(It.isAny(), It.isAny())).callback((handle, details) => handles.push(handle));
// Set up the buttons to record click events
let label1 = 'button_1';
let label2 = 'button_2';
let button1 = extHostModelViewDialog.createButton(label1);
let button2 = extHostModelViewDialog.createButton(label2);
let clickEvents = [];
button1.onClick(() => clickEvents.push(1));
button2.onClick(() => clickEvents.push(2));
extHostModelViewDialog.updateButton(button1);
extHostModelViewDialog.updateButton(button2);
// If the main thread sends some notifications that the buttons have been clicked
extHostModelViewDialog.$onButtonClick(handles[0]);
extHostModelViewDialog.$onButtonClick(handles[1]);
extHostModelViewDialog.$onButtonClick(handles[1]);
extHostModelViewDialog.$onButtonClick(handles[0]);
// Then the clicks should have been handled by the expected handlers
assert.deepEqual(clickEvents, [1, 2, 2, 1]);
});
test('Creating a wizard returns a wizard with initialized buttons and the given title', () => {
let title = 'wizard_title';
let wizard = extHostModelViewDialog.createWizard(title);
assert.equal(wizard.title, title);
assert.equal(wizard.doneButton.enabled, true);
assert.equal(wizard.cancelButton.enabled, true);
assert.equal(wizard.nextButton.enabled, true);
assert.equal(wizard.backButton.enabled, true);
assert.deepEqual(wizard.pages, []);
});
test('Opening a wizard updates its pages and buttons on the main thread', () => {
mockProxy.setup(x => x.$openWizard(It.isAny()));
mockProxy.setup(x => x.$setWizardDetails(It.isAny(), It.isAny()));
mockProxy.setup(x => x.$setWizardPageDetails(It.isAny(), It.isAny()));
mockProxy.setup(x => x.$setButtonDetails(It.isAny(), It.isAny()));
// Create a wizard with 2 pages and 2 custom buttons
let wizardTitle = 'wizard_title';
let wizard = extHostModelViewDialog.createWizard(wizardTitle);
let page1Title = 'page_1';
let page1 = extHostModelViewDialog.createWizardPage(page1Title);
let page2Title = 'page_2';
let page2 = extHostModelViewDialog.createWizardPage(page2Title);
wizard.pages = [page1, page2];
let button1Label = 'button_1';
let button1 = extHostModelViewDialog.createButton(button1Label);
button1.enabled = false;
let button2Label = 'button_2';
let button2 = extHostModelViewDialog.createButton(button2Label);
wizard.customButtons = [button1, button2];
// Open the wizard and verify that the correct main thread methods were called
extHostModelViewDialog.openWizard(wizard);
mockProxy.verify(x => x.$setButtonDetails(It.isAny(), It.is(details => {
return details.enabled === false && details.label === button1Label;
})), Times.atLeastOnce());
mockProxy.verify(x => x.$setButtonDetails(It.isAny(), It.is(details => {
return details.enabled === true && details.label === button2Label;
})), Times.atLeastOnce());
mockProxy.verify(x => x.$setWizardPageDetails(It.isAny(), It.is(details => {
return details.title === page1Title;
})), Times.atLeastOnce());
mockProxy.verify(x => x.$setWizardPageDetails(It.isAny(), It.is(details => {
return details.title === page2Title;
})), Times.atLeastOnce());
mockProxy.verify(x => x.$setWizardDetails(It.isAny(), It.is(details => {
return details.title === wizardTitle && details.pages.length === 2 && details.customButtons.length === 2 &&
details.displayPageTitles === true;
})), Times.atLeastOnce());
mockProxy.verify(x => x.$openWizard(It.isAny()), Times.once());
});
test('Wizard page changed events are handled correctly', () => {
// Set up the main thread mock to record the handle assigned to the wizard
let wizardHandle: number;
mockProxy.setup(x => x.$setWizardDetails(It.isAny(), It.isAny())).callback((handle, details) => wizardHandle = handle);
// Set up the wizard with 2 pages
let wizard = extHostModelViewDialog.createWizard('test_wizard');
let page1 = extHostModelViewDialog.createWizardPage('page_1');
let page2 = extHostModelViewDialog.createWizardPage('page_2');
wizard.pages = [page1, page2];
extHostModelViewDialog.updateWizard(wizard);
// Record page changed events
let actualPageChangeInfo = [];
wizard.onPageChanged(pageChangeInfo => {
actualPageChangeInfo.push(pageChangeInfo);
});
// Call the page changed event and verify that it was handled
let expectedPageChangeInfo = {
lastPage: 0,
newPage: 1
};
extHostModelViewDialog.$onWizardPageChanged(wizardHandle, expectedPageChangeInfo);
assert.equal(actualPageChangeInfo.length, 1);
assert.equal(actualPageChangeInfo[0], expectedPageChangeInfo);
assert.equal(wizard.currentPage, expectedPageChangeInfo.newPage);
});
test('Validity changed events are handled correctly', () => {
// Set up the main thread mock to record handles assigned to tabs
let tabHandles = [];
mockProxy.setup(x => x.$setTabDetails(It.isAny(), It.isAny())).callback((handle, details) => tabHandles.push(handle));
// Set up the dialog with 2 tabs
let dialog = extHostModelViewDialog.createDialog('test_dialog');
let tab1 = extHostModelViewDialog.createTab('tab_1');
let tab2 = extHostModelViewDialog.createTab('tab_2');
dialog.content = [tab1, tab2];
extHostModelViewDialog.updateDialogContent(dialog);
// Record tab validity changed events
let tab1ValidityChangedEvents = [];
let tab2ValidityChangedEvents = [];
tab1.onValidityChanged(valid => tab1ValidityChangedEvents.push(valid));
tab2.onValidityChanged(valid => tab2ValidityChangedEvents.push(valid));
// Call the validity changed event on tab 2 and verify that it was handled but tab 1 is still not valid
extHostModelViewDialog.$onPanelValidityChanged(tabHandles[1], false);
assert.equal(tab1ValidityChangedEvents.length, 0);
assert.equal(tab1.valid, true);
assert.equal(tab2ValidityChangedEvents.length, 1);
assert.equal(tab2ValidityChangedEvents[0], false);
assert.equal(tab2.valid, false);
});
test('Verify validity changed events update validity for all panel types', () => {
// Set up the main thread mock to record handles for the tab, dialog, and page
let tabHandle: number;
let dialogHandle: number;
let pageHandle: number;
mockProxy.setup(x => x.$setTabDetails(It.isAny(), It.isAny())).callback((handle, details) => tabHandle = handle);
mockProxy.setup(x => x.$setDialogDetails(It.isAny(), It.isAny())).callback((handle, details) => dialogHandle = handle);
mockProxy.setup(x => x.$setWizardPageDetails(It.isAny(), It.isAny())).callback((handle, details) => pageHandle = handle);
// Initialize a tab, dialog, and page
let tab = extHostModelViewDialog.createTab('tab_1');
extHostModelViewDialog.updateTabContent(tab);
let dialog = extHostModelViewDialog.createDialog('dialog_1');
extHostModelViewDialog.updateDialogContent(dialog);
let page = extHostModelViewDialog.createWizardPage('page_1');
extHostModelViewDialog.updateWizardPage(page);
// Call the validity changed event on each object and verify that the object's validity was updated
extHostModelViewDialog.$onPanelValidityChanged(tabHandle, false);
assert.equal(tab.valid, false);
extHostModelViewDialog.$onPanelValidityChanged(dialogHandle, false);
assert.equal(dialog.valid, false);
extHostModelViewDialog.$onPanelValidityChanged(pageHandle, false);
assert.equal(page.valid, false);
});
test('Main thread can execute wizard navigation validation', () => {
// Set up the main thread mock to record the wizard handle
let wizardHandle: number;
mockProxy.setup(x => x.$setWizardDetails(It.isAny(), It.isAny())).callback((handle, details) => wizardHandle = handle);
// Create the wizard and add a validation that records that it has been called
let wizard = extHostModelViewDialog.createWizard('wizard_1');
extHostModelViewDialog.updateWizard(wizard);
let validationInfo: azdata.window.WizardPageChangeInfo;
wizard.registerNavigationValidator(info => {
validationInfo = info;
return true;
});
// If I call the validation from the main thread then it should run and record the correct page change info
let lastPage = 0;
let newPage = 1;
extHostModelViewDialog.$validateNavigation(wizardHandle, {
lastPage: lastPage,
newPage: newPage
});
assert.notEqual(validationInfo, undefined);
assert.equal(validationInfo.lastPage, lastPage);
assert.equal(validationInfo.newPage, newPage);
});
test('Changing the wizard message sends the new message to the main thread', () => {
// Set up the main thread mock to record the call
mockProxy.setup(x => x.$setWizardDetails(It.isAny(), It.isAny()));
let wizard = extHostModelViewDialog.createWizard('wizard_1');
// If I update the wizard's message
let newMessage = {
level: MessageLevel.Error,
text: 'test message'
};
wizard.message = newMessage;
// Then the main thread gets notified of the new details
mockProxy.verify(x => x.$setWizardDetails(It.isAny(), It.is(x => x.message === newMessage)), Times.once());
});
test('Main thread can execute dialog close validation', () => {
// Set up the main thread mock to record the dialog handle
let dialogHandle: number;
mockProxy.setup(x => x.$setDialogDetails(It.isAny(), It.isAny())).callback((handle, details) => dialogHandle = handle);
// Create the dialog and add a validation that records that it has been called
let dialog = extHostModelViewDialog.createDialog('dialog_1');
extHostModelViewDialog.updateDialogContent(dialog);
let callCount = 0;
dialog.registerCloseValidator(() => {
callCount++;
return true;
});
// If I call the validation from the main thread then it should run
extHostModelViewDialog.$validateDialogClose(dialogHandle);
assert.equal(callCount, 1);
});
});

View File

@@ -0,0 +1,111 @@
/*---------------------------------------------------------------------------------------------
* 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 { MainThreadObjectExplorerShape } from 'sql/workbench/api/common/sqlExtHost.protocol';
import { ExtHostObjectExplorerNode } from 'sql/workbench/api/common/extHostObjectExplorer';
const nodes: { [nodeName: string]: azdata.NodeInfo } =
{
'Server1': {
nodePath: 'MyServer',
nodeStatus: '',
nodeSubType: '',
nodeType: 'Server',
isLeaf: false,
label: 'MyServer',
metadata: undefined,
errorMessage: ''
},
'DatabasesFolder': {
nodePath: 'MyServer/Databases',
nodeStatus: '',
nodeSubType: '',
nodeType: 'Folder',
isLeaf: false,
label: 'Databases',
metadata: undefined,
errorMessage: ''
},
'Database1': {
nodePath: 'MyServer/Databases/MyDatabase',
nodeStatus: '',
nodeSubType: '',
nodeType: 'Database',
isLeaf: false,
label: 'MyDatabase',
metadata: undefined,
errorMessage: ''
},
'Database2': {
nodePath: 'MyServer/Databases/My/TrickyDatabase',
nodeStatus: '',
nodeSubType: '',
nodeType: 'Database',
isLeaf: false,
label: 'My/TrickyDatabase',
metadata: undefined,
errorMessage: ''
},
'TablesFolder': {
nodePath: 'MyServer/Databases/My/TrickyDatabase/Tables',
nodeStatus: '',
nodeSubType: '',
nodeType: 'Folder',
isLeaf: false,
label: 'Tables',
metadata: undefined,
errorMessage: ''
}
};
suite('ExtHostObjectExplorer Tests', () => {
let mockProxy: TypeMoq.Mock<MainThreadObjectExplorerShape>;
suiteSetup(() => {
mockProxy = TypeMoq.Mock.ofInstance(<MainThreadObjectExplorerShape>{
$getNode: (connectionId: string, nodePath?: string): Thenable<azdata.NodeInfo> => undefined
});
mockProxy.setup(p =>
p.$getNode(TypeMoq.It.isAny(), TypeMoq.It.isAny()))
.returns((connectionId, nodePath) => {
return Promise.resolve<azdata.NodeInfo>(nodes[Object.keys(nodes).find(key =>
nodes[key].nodePath === nodePath)]);
});
});
suite('ExtHostObjectExplorerNode', () => {
let extHostObjectExplorerNode: ExtHostObjectExplorerNode;
suite('getParent', () => {
test('Should return undefined if no parent', async () => {
extHostObjectExplorerNode = new ExtHostObjectExplorerNode(nodes['Server1'], 'connectionId', mockProxy.object);
assert.equal(await extHostObjectExplorerNode.getParent(), undefined);
});
test('should return root with direct descendent of root', async () => {
extHostObjectExplorerNode = new ExtHostObjectExplorerNode(nodes['DatabasesFolder'], 'connectionId', mockProxy.object);
assert.equal((await extHostObjectExplorerNode.getParent()).nodePath, nodes['Server1'].nodePath);
});
test('should return correct parent with further descendent of root', async () => {
extHostObjectExplorerNode = new ExtHostObjectExplorerNode(nodes['Database1'], 'connectionId', mockProxy.object);
assert.equal((await extHostObjectExplorerNode.getParent()).nodePath, nodes['DatabasesFolder'].nodePath);
});
test('should return correct parent with node having / in its name', async () => {
extHostObjectExplorerNode = new ExtHostObjectExplorerNode(nodes['Database2'], 'connectionId', mockProxy.object);
assert.equal((await extHostObjectExplorerNode.getParent()).nodePath, nodes['DatabasesFolder'].nodePath);
});
test('should return correct parent with parent node having / in its name', async () => {
extHostObjectExplorerNode = new ExtHostObjectExplorerNode(nodes['TablesFolder'], 'connectionId', mockProxy.object);
assert.equal((await extHostObjectExplorerNode.getParent()).nodePath, nodes['Database2'].nodePath);
});
});
});
});

View File

@@ -0,0 +1,147 @@
/*---------------------------------------------------------------------------------------------
* 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 vscode from 'vscode';
import * as assert from 'assert';
import * as TypeMoq from 'typemoq';
import { URI } from 'vs/base/common/uri';
import { IMainContext } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostNotebook } from 'sql/workbench/api/common/extHostNotebook';
import { MainThreadNotebookShape } from 'sql/workbench/api/common/sqlExtHost.protocol';
import * as testUtils from '../../../../../sqltest/utils/testUtils';
import { INotebookManagerDetails } from 'sql/workbench/api/common/sqlExtHostTypes';
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
suite('ExtHostNotebook Tests', () => {
let extHostNotebook: ExtHostNotebook;
let mockProxy: TypeMoq.Mock<MainThreadNotebookShape>;
let notebookUri: URI;
let notebookProviderMock: TypeMoq.Mock<NotebookProviderStub>;
setup(() => {
mockProxy = TypeMoq.Mock.ofInstance(<MainThreadNotebookShape>{
$registerNotebookProvider: (providerId, handle) => undefined,
$unregisterNotebookProvider: (handle) => undefined,
dispose: () => undefined
});
let mainContext = <IMainContext>{
getProxy: proxyType => mockProxy.object
};
extHostNotebook = new ExtHostNotebook(mainContext);
notebookUri = URI.parse('file:/user/default/my.ipynb');
notebookProviderMock = TypeMoq.Mock.ofType(NotebookProviderStub, TypeMoq.MockBehavior.Loose);
notebookProviderMock.callBase = true;
});
suite('getNotebookManager', () => {
test('Should throw if no matching provider is defined', async () => {
await testUtils.assertThrowsAsync(() => extHostNotebook.$getNotebookManager(-1, notebookUri));
});
suite('with provider', () => {
let providerHandle: number = -1;
setup(() => {
mockProxy.setup(p =>
p.$registerNotebookProvider(TypeMoq.It.isValue(notebookProviderMock.object.providerId), TypeMoq.It.isAnyNumber()))
.returns((providerId, handle) => {
providerHandle = handle;
return undefined;
});
// Register the provider so we can test behavior with this present
extHostNotebook.registerNotebookProvider(notebookProviderMock.object);
});
test('Should return a notebook manager with correct info on content and server manager existence', async () => {
// Given the provider returns a manager with no
let expectedManager = new NotebookManagerStub();
notebookProviderMock.setup(p => p.getNotebookManager(TypeMoq.It.isAny())).returns(() => Promise.resolve(expectedManager));
// When I call through using the handle provided during registration
let managerDetails: INotebookManagerDetails = await extHostNotebook.$getNotebookManager(providerHandle, notebookUri);
// Then I expect the same manager to be returned
assert.ok(managerDetails.hasContentManager === false, 'Expect no content manager defined');
assert.ok(managerDetails.hasServerManager === false, 'Expect no server manager defined');
assert.ok(managerDetails.handle > 0, 'Expect a valid handle defined');
});
test('Should have a unique handle for each notebook URI', async () => {
// Given the we request 2 URIs
let expectedManager = new NotebookManagerStub();
notebookProviderMock.setup(p => p.getNotebookManager(TypeMoq.It.isAny())).returns(() => Promise.resolve(expectedManager));
// When I call through using the handle provided during registration
let originalManagerDetails = await extHostNotebook.$getNotebookManager(providerHandle, notebookUri);
let differentDetails = await extHostNotebook.$getNotebookManager(providerHandle, URI.parse('file://other/file.ipynb'));
let sameDetails = await extHostNotebook.$getNotebookManager(providerHandle, notebookUri);
// Then I expect the 2 different handles in the managers returned.
// This is because we can't easily track identity of the managers, so just track which one is assigned to
// a notebook by the handle ID
assert.notEqual(originalManagerDetails.handle, differentDetails.handle, 'Should have unique handle for each manager');
assert.equal(originalManagerDetails.handle, sameDetails.handle, 'Should have same handle when same URI is passed in');
});
});
});
suite('registerNotebookProvider', () => {
let savedHandle: number = -1;
setup(() => {
mockProxy.setup(p =>
p.$registerNotebookProvider(TypeMoq.It.isValue(notebookProviderMock.object.providerId), TypeMoq.It.isAnyNumber()))
.returns((providerId, handle) => {
savedHandle = handle;
return undefined;
});
});
test('Should register with a new handle to the proxy', () => {
extHostNotebook.registerNotebookProvider(notebookProviderMock.object);
mockProxy.verify(p =>
p.$registerNotebookProvider(TypeMoq.It.isValue(notebookProviderMock.object.providerId),
TypeMoq.It.isAnyNumber()), TypeMoq.Times.once());
// It shouldn't unregister until requested
mockProxy.verify(p => p.$unregisterNotebookProvider(TypeMoq.It.isValue(savedHandle)), TypeMoq.Times.never());
});
test('Should not call unregister on disposing', () => {
let disposable = extHostNotebook.registerNotebookProvider(notebookProviderMock.object);
disposable.dispose();
mockProxy.verify(p => p.$unregisterNotebookProvider(TypeMoq.It.isValue(savedHandle)), TypeMoq.Times.never());
});
});
});
class NotebookProviderStub implements azdata.nb.NotebookProvider {
providerId: string = 'TestProvider';
standardKernels: azdata.nb.IStandardKernel[] = [{ name: 'fakeKernel', displayName: 'fakeKernel', connectionProviderIds: [mssqlProviderName] }];
getNotebookManager(notebookUri: vscode.Uri): Thenable<azdata.nb.NotebookManager> {
throw new Error('Method not implemented.');
}
handleNotebookClosed(notebookUri: vscode.Uri): void {
throw new Error('Method not implemented.');
}
}
class NotebookManagerStub implements azdata.nb.NotebookManager {
get contentManager(): azdata.nb.ContentManager {
return undefined;
}
get sessionManager(): azdata.nb.SessionManager {
return undefined;
}
get serverManager(): azdata.nb.ServerManager {
return undefined;
}
}

View File

@@ -0,0 +1,94 @@
/*---------------------------------------------------------------------------------------------
* 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 { Mock, It, Times } from 'typemoq';
import { MainThreadBackgroundTaskManagement, TaskStatus } from 'sql/workbench/api/browser/mainThreadBackgroundTaskManagement';
import { ITaskService } from 'sql/platform/tasks/common/tasksService';
import { IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
import { TaskNode } from 'sql/platform/tasks/common/tasksNode';
import { Emitter } from 'vs/base/common/event';
import { ExtHostBackgroundTaskManagementShape } from 'sql/workbench/api/common/sqlExtHost.protocol';
suite('MainThreadBackgroundTaskManagement Tests', () => {
let mainThreadBackgroundTaskManagement: MainThreadBackgroundTaskManagement;
let mockProxy: Mock<ExtHostBackgroundTaskManagementShape>;
let taskService: Mock<ITaskService>;
let nothing: void;
let operationId = 'operation is';
let onTaskComplete = new Emitter<TaskNode>();
setup(() => {
mockProxy = Mock.ofInstance(<ExtHostBackgroundTaskManagementShape>{
$onTaskRegistered: (operationId: string) => nothing,
$onTaskCanceled: (operationId: string) => nothing,
$registerTask: (operationInfo: azdata.BackgroundOperationInfo) => nothing,
$removeTask: (operationId: string) => nothing,
});
taskService = Mock.ofInstance(<ITaskService>{
_serviceBrand: undefined,
onTaskComplete: undefined,
onAddNewTask: undefined,
handleNewTask: undefined,
handleTaskComplete: undefined,
getAllTasks: undefined,
getNumberOfInProgressTasks: undefined,
onNewTaskCreated: undefined,
createNewTask: (taskInfo: azdata.TaskInfo) => nothing,
updateTask: (taskProgressInfo: azdata.TaskProgressInfo) => nothing,
onTaskStatusChanged: undefined,
cancelTask: undefined,
registerProvider: undefined
});
let mainContext = <IExtHostContext>{
getProxy: proxyType => mockProxy.object
};
taskService.setup(x => x.onTaskComplete).returns(() => onTaskComplete.event);
mainThreadBackgroundTaskManagement = new MainThreadBackgroundTaskManagement(mainContext, taskService.object);
});
test('RegisterTask should successfully create background task', () => {
let taskInfo: azdata.TaskInfo = {
taskId: operationId,
databaseName: undefined,
description: undefined,
isCancelable: true,
name: 'task name',
providerName: undefined,
serverName: undefined,
status: TaskStatus.NotStarted,
taskExecutionMode: 0
};
mainThreadBackgroundTaskManagement.$registerTask(taskInfo);
taskService.verify(x => x.createNewTask(It.is(t => t.status === TaskStatus.NotStarted)), Times.once());
mockProxy.verify(x => x.$onTaskRegistered(operationId), Times.once());
});
test('UpdateTask should successfully update the background task status', () => {
let taskInfo: azdata.TaskProgressInfo = {
taskId: operationId,
status: TaskStatus.InProgress,
message: undefined,
};
mainThreadBackgroundTaskManagement.$updateTask(taskInfo);
taskService.verify(x => x.updateTask(It.is(t => t.status === TaskStatus.InProgress)), Times.once());
});
test('Canceling the task should notify the proxy', () => {
let taskInfo: azdata.TaskProgressInfo = {
taskId: operationId,
status: TaskStatus.InProgress,
message: undefined,
};
let taskNode = new TaskNode('', '', '', operationId, undefined);
taskNode.status = TaskStatus.Canceling;
onTaskComplete.fire(taskNode);
mainThreadBackgroundTaskManagement.$updateTask(taskInfo);
mockProxy.verify(x => x.$onTaskCanceled(It.is(t => t === operationId)), Times.once());
});
});

View File

@@ -0,0 +1,371 @@
/*---------------------------------------------------------------------------------------------
* 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 { Mock, It, Times } from 'typemoq';
import { MainThreadModelViewDialog } from 'sql/workbench/api/electron-browser/mainThreadModelViewDialog';
import { IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
import { IModelViewButtonDetails, IModelViewTabDetails, IModelViewDialogDetails, IModelViewWizardPageDetails, IModelViewWizardDetails, DialogMessage, MessageLevel } from 'sql/workbench/api/common/sqlExtHostTypes';
import { CustomDialogService } from 'sql/platform/dialog/browser/customDialogService';
import { Dialog, DialogTab, Wizard } from 'sql/platform/dialog/common/dialogTypes';
import { ExtHostModelViewDialogShape } from 'sql/workbench/api/common/sqlExtHost.protocol';
import { Emitter } from 'vs/base/common/event';
suite('MainThreadModelViewDialog Tests', () => {
let mainThreadModelViewDialog: MainThreadModelViewDialog;
let mockExtHostModelViewDialog: Mock<ExtHostModelViewDialogShape>;
let mockDialogService: Mock<CustomDialogService>;
let openedDialog: Dialog;
let openedWizard: Wizard;
// Dialog details
let button1Details: IModelViewButtonDetails;
let button2Details: IModelViewButtonDetails;
let okButtonDetails: IModelViewButtonDetails;
let cancelButtonDetails: IModelViewButtonDetails;
let tab1Details: IModelViewTabDetails;
let tab2Details: IModelViewTabDetails;
let dialogDetails: IModelViewDialogDetails;
let button1Handle = 1;
let button2Handle = 2;
let okButtonHandle = 3;
let cancelButtonHandle = 4;
let tab1Handle = 5;
let tab2Handle = 6;
let dialogHandle = 7;
// Wizard details
let nextButtonDetails: IModelViewButtonDetails;
let backButtonDetails: IModelViewButtonDetails;
let generateScriptButtonDetails: IModelViewButtonDetails;
let page1Details: IModelViewWizardPageDetails;
let page2Details: IModelViewWizardPageDetails;
let page3Details: IModelViewWizardPageDetails;
let wizardDetails: IModelViewWizardDetails;
let nextButtonHandle = 8;
let backButtonHandle = 9;
let generateScriptButtonHandle = 10;
let page1Handle = 11;
let page2Handle = 12;
let wizardHandle = 13;
let page3Handle = 14;
setup(() => {
mockExtHostModelViewDialog = Mock.ofInstance(<ExtHostModelViewDialogShape>{
$onButtonClick: handle => undefined,
$onPanelValidityChanged: (handle, valid) => undefined,
$onWizardPageChanged: (handle, info) => undefined,
$updateWizardPageInfo: (wizardHandle, pageHandles, currentPageIndex) => undefined,
$validateNavigation: (handle, info) => undefined,
$validateDialogClose: handle => undefined
});
let extHostContext = <IExtHostContext>{
getProxy: proxyType => mockExtHostModelViewDialog.object
};
mainThreadModelViewDialog = new MainThreadModelViewDialog(extHostContext, undefined, undefined, undefined);
// Set up the mock dialog service
mockDialogService = Mock.ofType(CustomDialogService, undefined);
openedDialog = undefined;
mockDialogService.setup(x => x.showDialog(It.isAny(), undefined, It.isAny())).callback((dialog) => {
openedDialog = dialog;
});
mockDialogService.setup(x => x.showWizard(It.isAny())).callback(wizard => {
openedWizard = wizard;
// The actual service will set the page to 0 when it opens the wizard
openedWizard.setCurrentPage(0);
});
(mainThreadModelViewDialog as any)._dialogService = mockDialogService.object;
// Set up the dialog details
button1Details = {
label: 'button1',
enabled: false,
hidden: false
};
button2Details = {
label: 'button2',
enabled: true,
hidden: false
};
okButtonDetails = {
label: 'ok_label',
enabled: true,
hidden: false
};
cancelButtonDetails = {
label: 'cancel_label',
enabled: true,
hidden: false
};
tab1Details = {
title: 'tab1',
content: 'content1'
};
tab2Details = {
title: 'tab2',
content: 'content2'
};
dialogDetails = {
title: 'dialog1',
isWide: false,
content: [tab1Handle, tab2Handle],
okButton: okButtonHandle,
cancelButton: cancelButtonHandle,
customButtons: [button1Handle, button2Handle],
message: undefined
};
// Set up the wizard details
nextButtonDetails = {
label: 'next_label',
enabled: true,
hidden: false
};
backButtonDetails = {
label: 'back_label',
enabled: true,
hidden: false
};
generateScriptButtonDetails = {
label: 'generate_script_label',
enabled: true,
hidden: false
};
page1Details = {
title: 'page1',
content: 'content1',
enabled: true,
customButtons: [],
description: 'description1'
};
page2Details = {
title: 'page2',
content: 'content2',
enabled: true,
customButtons: [button1Handle, button2Handle],
description: 'description2'
};
wizardDetails = {
backButton: backButtonHandle,
nextButton: nextButtonHandle,
generateScriptButton: generateScriptButtonHandle,
cancelButton: cancelButtonHandle,
doneButton: okButtonHandle,
currentPage: undefined,
title: 'wizard_title',
customButtons: [],
pages: [page1Handle, page2Handle],
message: undefined,
displayPageTitles: false
};
// Register the buttons, tabs, and dialog
mainThreadModelViewDialog.$setButtonDetails(button1Handle, button1Details);
mainThreadModelViewDialog.$setButtonDetails(button2Handle, button2Details);
mainThreadModelViewDialog.$setButtonDetails(okButtonHandle, okButtonDetails);
mainThreadModelViewDialog.$setButtonDetails(cancelButtonHandle, cancelButtonDetails);
mainThreadModelViewDialog.$setTabDetails(tab1Handle, tab1Details);
mainThreadModelViewDialog.$setTabDetails(tab2Handle, tab2Details);
mainThreadModelViewDialog.$setDialogDetails(dialogHandle, dialogDetails);
// Register the wizard and its pages and buttons
mainThreadModelViewDialog.$setButtonDetails(nextButtonHandle, nextButtonDetails);
mainThreadModelViewDialog.$setButtonDetails(backButtonHandle, backButtonDetails);
mainThreadModelViewDialog.$setButtonDetails(generateScriptButtonHandle, generateScriptButtonDetails);
mainThreadModelViewDialog.$setWizardPageDetails(page1Handle, page1Details);
mainThreadModelViewDialog.$setWizardPageDetails(page2Handle, page2Details);
mainThreadModelViewDialog.$setWizardDetails(wizardHandle, wizardDetails);
});
test('Creating a dialog and calling open on it causes a dialog with correct content and buttons to open', () => {
// If I open the dialog
mainThreadModelViewDialog.$openDialog(dialogHandle);
// Then the opened dialog's content and buttons match what was set
mockDialogService.verify(x => x.showDialog(It.isAny(), undefined, It.isAny()), Times.once());
assert.notEqual(openedDialog, undefined);
assert.equal(openedDialog.title, dialogDetails.title);
assert.equal(openedDialog.okButton.label, okButtonDetails.label);
assert.equal(openedDialog.okButton.enabled, okButtonDetails.enabled);
assert.equal(openedDialog.cancelButton.label, cancelButtonDetails.label);
assert.equal(openedDialog.cancelButton.enabled, cancelButtonDetails.enabled);
assert.equal(openedDialog.customButtons.length, 2);
assert.equal(openedDialog.customButtons[0].label, button1Details.label);
assert.equal(openedDialog.customButtons[0].enabled, button1Details.enabled);
assert.equal(openedDialog.customButtons[1].label, button2Details.label);
assert.equal(openedDialog.customButtons[1].enabled, button2Details.enabled);
assert.equal(openedDialog.content.length, 2);
assert.equal((openedDialog.content[0] as DialogTab).content, tab1Details.content);
assert.equal((openedDialog.content[0] as DialogTab).title, tab1Details.title);
assert.equal((openedDialog.content[1] as DialogTab).content, tab2Details.content);
assert.equal((openedDialog.content[1] as DialogTab).title, tab2Details.title);
});
test('Button presses are forwarded to the extension host', () => {
// Set up the mock proxy to capture button presses
let pressedHandles = [];
mockExtHostModelViewDialog.setup(x => x.$onButtonClick(It.isAny())).callback(handle => pressedHandles.push(handle));
// Open the dialog so that its buttons can be accessed
mainThreadModelViewDialog.$openDialog(dialogHandle);
// Set up click emitters for each button
let okEmitter = new Emitter<void>();
let cancelEmitter = new Emitter<void>();
let button1Emitter = new Emitter<void>();
let button2Emitter = new Emitter<void>();
openedDialog.okButton.registerClickEvent(okEmitter.event);
openedDialog.cancelButton.registerClickEvent(cancelEmitter.event);
openedDialog.customButtons[0].registerClickEvent(button1Emitter.event);
openedDialog.customButtons[1].registerClickEvent(button2Emitter.event);
// Click the buttons
button1Emitter.fire();
button2Emitter.fire();
okEmitter.fire();
cancelEmitter.fire();
button2Emitter.fire();
cancelEmitter.fire();
button1Emitter.fire();
okEmitter.fire();
// Verify that the correct button click notifications were sent to the proxy
assert.deepEqual(pressedHandles, [button1Handle, button2Handle, okButtonHandle, cancelButtonHandle, button2Handle, cancelButtonHandle, button1Handle, okButtonHandle]);
});
test('Creating a wizard and calling open on it causes a wizard with correct pages and buttons to open', () => {
// If I open the wizard
mainThreadModelViewDialog.$openWizard(wizardHandle);
// Then the opened wizard's content and buttons match what was set
mockDialogService.verify(x => x.showWizard(It.isAny()), Times.once());
assert.notEqual(openedWizard, undefined);
assert.equal(openedWizard.title, wizardDetails.title);
assert.equal(openedWizard.doneButton.label, okButtonDetails.label);
assert.equal(openedWizard.doneButton.enabled, okButtonDetails.enabled);
assert.equal(openedWizard.cancelButton.label, cancelButtonDetails.label);
assert.equal(openedWizard.cancelButton.enabled, cancelButtonDetails.enabled);
assert.equal(openedWizard.customButtons.length, 0);
assert.equal(openedWizard.pages.length, 2);
assert.equal(openedWizard.currentPage, 0);
assert.equal(openedWizard.displayPageTitles, wizardDetails.displayPageTitles);
let page1 = openedWizard.pages[0];
assert.equal(page1.title, page1Details.title);
assert.equal(page1.content, page1Details.content);
assert.equal(page1.enabled, page1Details.enabled);
assert.equal(page1.valid, true);
assert.equal(page1.customButtons.length, 0);
assert.equal(page1.description, page1Details.description);
let page2 = openedWizard.pages[1];
assert.equal(page2.title, page2Details.title);
assert.equal(page2.content, page2Details.content);
assert.equal(page2.enabled, page2Details.enabled);
assert.equal(page2.valid, true);
assert.equal(page2.customButtons.length, 2);
assert.equal(page2.description, page2Details.description);
});
test('The extension host gets notified when wizard page change events occur', () => {
mockExtHostModelViewDialog.setup(x => x.$onWizardPageChanged(It.isAny(), It.isAny()));
// If I open the wizard and change the page to index 1
mainThreadModelViewDialog.$openWizard(wizardHandle);
openedWizard.setCurrentPage(1);
// Then a page changed event gets sent to the extension host
mockExtHostModelViewDialog.verify(x => x.$onWizardPageChanged(It.is(handle => handle === wizardHandle),
It.is(pageChangeInfo => pageChangeInfo.lastPage === 0 && pageChangeInfo.newPage === 1)), Times.once());
});
test('Validity changed events are forwarded to the extension host', () => {
mockExtHostModelViewDialog.setup(x => x.$onPanelValidityChanged(It.isAny(), It.isAny()));
// If I open the dialog and set its validity and its 2nd tab's validity to false
mainThreadModelViewDialog.$openDialog(dialogHandle);
(openedDialog.content[1] as DialogTab).notifyValidityChanged(false);
openedDialog.notifyValidityChanged(false);
// Then a validity changed event gets sent to the extension host for the tab and the dialog
mockExtHostModelViewDialog.verify(x => x.$onPanelValidityChanged(It.is(handle => handle === dialogHandle), It.is(valid => valid === false)), Times.once());
mockExtHostModelViewDialog.verify(x => x.$onPanelValidityChanged(It.is(handle => handle === tab2Handle), It.is(valid => valid === false)), Times.once());
});
test('addWizardPage method inserts pages at the correct spot and notifies the extension host', () => {
mockExtHostModelViewDialog.setup(x => x.$updateWizardPageInfo(It.isAny(), It.isAny(), It.isAny()));
page3Details = {
title: 'page_3',
content: 'content_3',
customButtons: [],
enabled: true,
description: undefined
};
// If I open the wizard and then add a page
mainThreadModelViewDialog.$openWizard(wizardHandle);
mainThreadModelViewDialog.$setWizardPageDetails(page3Handle, page3Details);
mainThreadModelViewDialog.$addWizardPage(wizardHandle, page3Handle, 0);
// Then the updated page info gets sent to the extension host
mockExtHostModelViewDialog.verify(x => x.$updateWizardPageInfo(
It.is(handle => handle === wizardHandle),
It.is(pageHandles => pageHandles.length === 3 && pageHandles[0] === page3Handle),
It.is(currentPage => currentPage === 1)), Times.once());
});
test('removeWizardPage method removes pages at the correct spot and notifies the extension host', () => {
mockExtHostModelViewDialog.setup(x => x.$updateWizardPageInfo(It.isAny(), It.isAny(), It.isAny()));
// If I open the wizard and then remove a page
mainThreadModelViewDialog.$openWizard(wizardHandle);
mainThreadModelViewDialog.$removeWizardPage(wizardHandle, 0);
// Then the updated page info gets sent to the extension host
mockExtHostModelViewDialog.verify(x => x.$updateWizardPageInfo(
It.is(handle => handle === wizardHandle),
It.is(pageHandles => pageHandles.length === 1 && pageHandles[0] === page2Handle),
It.is(currentPage => currentPage === 0)), Times.once());
});
test('Creating a wizard adds a navigation validation that calls the extension host', () => {
mockExtHostModelViewDialog.setup(x => x.$validateNavigation(It.isAny(), It.isAny()));
// If I call validateNavigation on the wizard that gets created
mainThreadModelViewDialog.$openWizard(wizardHandle);
openedWizard.validateNavigation(1);
// Then the call gets forwarded to the extension host
mockExtHostModelViewDialog.verify(x => x.$validateNavigation(It.is(handle => handle === wizardHandle), It.is(info => info.newPage === 1)), Times.once());
});
test('Adding a message to a wizard fires events on the created wizard', () => {
mainThreadModelViewDialog.$openWizard(wizardHandle);
let newMessage: DialogMessage;
openedWizard.onMessageChange(message => newMessage = message);
// If I change the wizard's message
wizardDetails.message = {
level: MessageLevel.Error,
text: 'test message'
};
mainThreadModelViewDialog.$setWizardDetails(wizardHandle, wizardDetails);
// Then the message gets changed on the wizard
assert.equal(newMessage, wizardDetails.message, 'New message was not included in the fired event');
assert.equal(openedWizard.message, wizardDetails.message, 'New message was not set on the wizard');
});
test('Creating a dialog adds a close validation that calls the extension host', () => {
mockExtHostModelViewDialog.setup(x => x.$validateDialogClose(It.isAny()));
// If I call validateClose on the dialog that gets created
mainThreadModelViewDialog.$openDialog(dialogHandle);
openedDialog.validateClose();
// Then the call gets forwarded to the extension host
mockExtHostModelViewDialog.verify(x => x.$validateDialogClose(It.is(handle => handle === dialogHandle)), Times.once());
});
});

View File

@@ -0,0 +1,180 @@
/*---------------------------------------------------------------------------------------------
* 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 * as azdata from 'azdata';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
import { MainThreadNotebook } from 'sql/workbench/api/browser/mainThreadNotebook';
import { NotebookService } from 'sql/workbench/services/notebook/common/notebookServiceImpl';
import { INotebookProvider } from 'sql/workbench/services/notebook/common/notebookService';
import { INotebookManagerDetails, INotebookSessionDetails, INotebookKernelDetails, INotebookFutureDetails } from 'sql/workbench/api/common/sqlExtHostTypes';
import { LocalContentManager } from 'sql/workbench/services/notebook/node/localContentManager';
import { TestLifecycleService } from 'vs/workbench/test/workbenchTestServices';
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
import { ExtHostNotebookShape } from 'sql/workbench/api/common/sqlExtHost.protocol';
suite('MainThreadNotebook Tests', () => {
let mainThreadNotebook: MainThreadNotebook;
let mockProxy: TypeMoq.Mock<ExtHostNotebookShape>;
let notebookUri: URI;
let mockNotebookService: TypeMoq.Mock<NotebookService>;
let providerId = 'TestProvider';
setup(() => {
mockProxy = TypeMoq.Mock.ofType(ExtHostNotebookStub);
let extContext = <IExtHostContext>{
getProxy: proxyType => mockProxy.object
};
mockNotebookService = TypeMoq.Mock.ofType(NotebookService, undefined, new TestLifecycleService(), undefined, undefined, undefined, undefined, new MockContextKeyService());
notebookUri = URI.parse('file:/user/default/my.ipynb');
mainThreadNotebook = new MainThreadNotebook(extContext, mockNotebookService.object);
});
suite('On registering a provider', () => {
let provider: INotebookProvider;
let registeredProviderId: string;
setup(() => {
mockNotebookService.setup(s => s.registerProvider(TypeMoq.It.isAnyString(), TypeMoq.It.isAny())).returns((id, providerImpl) => {
registeredProviderId = id;
provider = providerImpl;
});
});
test('should call through to notebook service', () => {
// When I register a provider
mainThreadNotebook.$registerNotebookProvider(providerId, 1);
// Then I expect a provider implementation to be passed to the service
mockNotebookService.verify(s => s.registerProvider(TypeMoq.It.isAnyString(), TypeMoq.It.isAny()), TypeMoq.Times.once());
assert.equal(provider.providerId, providerId);
});
test('should unregister in service', () => {
// Given we have a provider
mainThreadNotebook.$registerNotebookProvider(providerId, 1);
// When I unregister a provider twice
mainThreadNotebook.$unregisterNotebookProvider(1);
mainThreadNotebook.$unregisterNotebookProvider(1);
// Then I expect it to be unregistered in the service just 1 time
mockNotebookService.verify(s => s.unregisterProvider(TypeMoq.It.isValue(providerId)), TypeMoq.Times.once());
});
});
suite('getNotebookManager', () => {
let managerWithAllFeatures: INotebookManagerDetails;
let provider: INotebookProvider;
setup(() => {
managerWithAllFeatures = {
handle: 2,
hasContentManager: true,
hasServerManager: true
};
mockNotebookService.setup(s => s.registerProvider(TypeMoq.It.isAnyString(), TypeMoq.It.isAny())).returns((id, providerImpl) => {
provider = providerImpl;
});
mainThreadNotebook.$registerNotebookProvider(providerId, 1);
// Always return empty specs in this test suite
mockProxy.setup(p => p.$refreshSpecs(TypeMoq.It.isAnyNumber())).returns(() => Promise.resolve(undefined));
});
test('should return manager with default content manager & undefined server manager if extension host has none', async () => {
// Given the extension provider doesn't have acontent or server manager
let details: INotebookManagerDetails = {
handle: 2,
hasContentManager: false,
hasServerManager: false
};
mockProxy.setup(p => p.$getNotebookManager(TypeMoq.It.isAnyNumber(), TypeMoq.It.isValue(notebookUri)))
.returns(() => Promise.resolve(details));
// When I get the notebook manager
let manager = await provider.getNotebookManager(notebookUri);
// Then it should use the built-in content manager
assert.ok(manager.contentManager instanceof LocalContentManager);
// And it should not define a server manager
assert.equal(manager.serverManager, undefined);
});
test('should return manager with a content & server manager if extension host has these', async () => {
// Given the extension provider doesn't have acontent or server manager
mockProxy.setup(p => p.$getNotebookManager(TypeMoq.It.isAnyNumber(), TypeMoq.It.isValue(notebookUri)))
.returns(() => Promise.resolve(managerWithAllFeatures));
// When I get the notebook manager
let manager = await provider.getNotebookManager(notebookUri);
// Then it shouldn't have wrappers for the content or server manager
assert.ok(!(manager.contentManager instanceof LocalContentManager));
assert.notEqual(manager.serverManager, undefined);
});
});
});
class ExtHostNotebookStub implements ExtHostNotebookShape {
$getNotebookManager(providerHandle: number, notebookUri: UriComponents): Thenable<INotebookManagerDetails> {
throw new Error('Method not implemented.');
}
$handleNotebookClosed(notebookUri: UriComponents): void {
throw new Error('Method not implemented.');
}
$doStartServer(managerHandle: number): Thenable<void> {
throw new Error('Method not implemented.');
}
$doStopServer(managerHandle: number): Thenable<void> {
throw new Error('Method not implemented.');
}
$getNotebookContents(managerHandle: number, notebookUri: UriComponents): Thenable<azdata.nb.INotebookContents> {
throw new Error('Method not implemented.');
}
$save(managerHandle: number, notebookUri: UriComponents, notebook: azdata.nb.INotebookContents): Thenable<azdata.nb.INotebookContents> {
throw new Error('Method not implemented.');
}
$refreshSpecs(managerHandle: number): Thenable<azdata.nb.IAllKernels> {
throw new Error('Method not implemented.');
}
$startNewSession(managerHandle: number, options: azdata.nb.ISessionOptions): Thenable<INotebookSessionDetails> {
throw new Error('Method not implemented.');
}
$shutdownSession(managerHandle: number, sessionId: string): Thenable<void> {
throw new Error('Method not implemented.');
}
$changeKernel(sessionId: number, kernelInfo: azdata.nb.IKernelSpec): Thenable<INotebookKernelDetails> {
throw new Error('Method not implemented.');
}
$configureKernel(sessionId: number, kernelInfo: azdata.nb.IKernelSpec): Thenable<void> {
throw new Error('Method not implemented.');
}
$configureConnection(sessionId: number, conneection: azdata.IConnectionProfile): Thenable<void> {
throw new Error('Method not implemented.');
}
$getKernelReadyStatus(kernelId: number): Thenable<azdata.nb.IInfoReply> {
throw new Error('Method not implemented.');
}
$getKernelSpec(kernelId: number): Thenable<azdata.nb.IKernelSpec> {
throw new Error('Method not implemented.');
}
$requestComplete(kernelId: number, content: azdata.nb.ICompleteRequest): Thenable<azdata.nb.ICompleteReplyMsg> {
throw new Error('Method not implemented.');
}
$requestExecute(kernelId: number, content: azdata.nb.IExecuteRequest, disposeOnDone?: boolean): Thenable<INotebookFutureDetails> {
throw new Error('Method not implemented.');
}
$interruptKernel(kernelId: number): Thenable<void> {
throw new Error('Method not implemented.');
}
$sendInputReply(futureId: number, content: azdata.nb.IInputReply): void {
throw new Error('Method not implemented.');
}
$disposeFuture(futureId: number): void {
throw new Error('Method not implemented.');
}
}