diff --git a/extensions/dacpac/package.json b/extensions/dacpac/package.json index 1f945f4763..e4feb11482 100644 --- a/extensions/dacpac/package.json +++ b/extensions/dacpac/package.json @@ -58,10 +58,13 @@ "devDependencies": { "@types/mocha": "^5.2.5", "@types/node": "^12.11.7", + "@types/sinon": "^9.0.4", "mocha": "^5.2.0", "mocha-junit-reporter": "^1.17.0", "mocha-multi-reporters": "^1.1.7", - "should": "^13.2.1", + "should": "^13.2.3", + "sinon": "^9.0.2", + "typemoq": "^2.1.0", "vscodetestcover": "^1.0.9" }, "__metadata": { diff --git a/extensions/dacpac/src/test/configPages.test.ts b/extensions/dacpac/src/test/configPages.test.ts index 26598f16ed..292f44a2b7 100644 --- a/extensions/dacpac/src/test/configPages.test.ts +++ b/extensions/dacpac/src/test/configPages.test.ts @@ -8,7 +8,7 @@ import * as should from 'should'; import { DataTierApplicationWizard, PageName } from '../wizard/dataTierApplicationWizard'; import { DacFxDataModel } from '../wizard/api/models'; import { TestContext, createContext } from './testContext'; -import { TestDeployConfigPage, TestExtractConfigPage } from './DacFxTestConfigPages'; +import { TestDeployConfigPage, TestExtractConfigPage } from './testDacFxConfigPages'; let wizard: DataTierApplicationWizard; let testContext: TestContext; diff --git a/extensions/dacpac/src/test/selectOperationPage.test.ts b/extensions/dacpac/src/test/selectOperationPage.test.ts new file mode 100644 index 0000000000..6ee54aded0 --- /dev/null +++ b/extensions/dacpac/src/test/selectOperationPage.test.ts @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'mocha'; +import * as should from 'should'; +import * as loc from '../localizedConstants'; +import { DataTierApplicationWizard, PageName } from '../wizard/dataTierApplicationWizard'; +import { DacFxDataModel } from '../wizard/api/models'; +import { TestContext, createContext } from './testContext'; +import { TestSelectOperationPage } from './testDacFxConfigPages'; + +let wizard: DataTierApplicationWizard; +let testContext: TestContext; + +describe('Dacpac Select OperationPage Tests', function (): void { + beforeEach(async function (): Promise { + wizard = new DataTierApplicationWizard(); + wizard.model = {}; + wizard.model.server = undefined; + }); + + it('Select Operations Page should start correctly', async () => { + testContext = createContext(); + wizard.setPages(); + + const opPage: TestSelectOperationPage = new TestSelectOperationPage(wizard, wizard.pages.get(PageName.selectOperation).wizardPage, wizard.model, testContext.viewContext.view); + const onPageStart = await opPage.start(); + const onPageEnter = await opPage.onPageEnter(); + should(onPageStart).equal(true); + should(onPageEnter).equal(true); + should(opPage.WizardState.wizard.pages.length).equal(4); + + should(opPage.WizardState.wizard.pages[1].title).equal(loc.deployConfigPageName); + should(opPage.WizardState.wizard.pages[2].title).equal(loc.deployPlanPageName); + should(opPage.WizardState.wizard.pages[3].title).equal(loc.summaryPageName); + }); + + it('Select Operations Page clicks should work correctly', async () => { + testContext = createContext(); + wizard.setPages(); + + const opPage: TestSelectOperationPage = new TestSelectOperationPage(wizard, wizard.pages.get(PageName.selectOperation).wizardPage, wizard.model, testContext.viewContext.view); + await opPage.start(); + + testContext.viewContext.extractOnClick.fire(undefined); + should(opPage.WizardState.wizard.doneButton.label).equal(loc.extract); + + testContext.viewContext.exportOnClick.fire(undefined); + should(opPage.WizardState.wizard.doneButton.label).equal(loc.exportText); + + testContext.viewContext.importOnClick.fire(undefined); + should(opPage.WizardState.wizard.doneButton.label).equal(loc.importText); + + testContext.viewContext.deployOnClick.fire(undefined); + should(opPage.WizardState.wizard.doneButton.label).equal(loc.deploy); + }); +}); diff --git a/extensions/dacpac/src/test/testContext.ts b/extensions/dacpac/src/test/testContext.ts index 03e5321bea..ced638a7bb 100644 --- a/extensions/dacpac/src/test/testContext.ts +++ b/extensions/dacpac/src/test/testContext.ts @@ -47,6 +47,10 @@ export interface ViewTestContext { onValueChanged: vscode.EventEmitter; newDatabaseRadioOnClick: vscode.EventEmitter; updateExistingRadioOnClick: vscode.EventEmitter; + deployOnClick: vscode.EventEmitter, + extractOnClick: vscode.EventEmitter, + exportOnClick: vscode.EventEmitter, + importOnClick: vscode.EventEmitter, } export function createViewContext(): ViewTestContext { @@ -55,6 +59,10 @@ export function createViewContext(): ViewTestContext { let onValueChanged: vscode.EventEmitter = new vscode.EventEmitter(); let newDatabaseRadioOnClick: vscode.EventEmitter = new vscode.EventEmitter(); let updateExistingRadioOnClick: vscode.EventEmitter = new vscode.EventEmitter(); + let deployOnClick: vscode.EventEmitter = new vscode.EventEmitter(); + let extractOnClick: vscode.EventEmitter = new vscode.EventEmitter(); + let exportOnClick: vscode.EventEmitter = new vscode.EventEmitter(); + let importOnClick: vscode.EventEmitter = new vscode.EventEmitter(); let componentBase: azdata.Component = { id: '', @@ -111,13 +119,31 @@ export function createViewContext(): ViewTestContext { let builder: azdata.ComponentBuilder = { component: () => button, withProperties: (properties) => { - if ((properties as any).label === loc.newDatabase) { - button.label = loc.newDatabase; - button.onDidClick = newDatabaseRadioOnClick.event; - } - else if ((properties as any).label === loc.upgradeExistingDatabase) { - button.label = loc.upgradeExistingDatabase; - button.onDidClick = updateExistingRadioOnClick.event; + switch ((properties as any).label) { + case loc.newDatabase: + button.label = loc.newDatabase; + button.onDidClick = newDatabaseRadioOnClick.event; + break; + case loc.upgradeExistingDatabase: + button.label = loc.upgradeExistingDatabase; + button.onDidClick = updateExistingRadioOnClick.event; + break; + case loc.deployDescription: + button.label = loc.deployDescription; + button.onDidClick = deployOnClick.event; + break; + case loc.exportDescription: + button.label = loc.exportDescription; + button.onDidClick = exportOnClick.event; + break; + case loc.extractDescription: + button.label = loc.extractDescription; + button.onDidClick = extractOnClick.event; + break; + case loc.importDescription: + button.label = loc.importDescription; + button.onDidClick = importOnClick.event; + break; } return builder; }, @@ -265,5 +291,9 @@ export function createViewContext(): ViewTestContext { onValueChanged: onValueChanged, newDatabaseRadioOnClick: newDatabaseRadioOnClick, updateExistingRadioOnClick: updateExistingRadioOnClick, + deployOnClick: deployOnClick, + extractOnClick: extractOnClick, + exportOnClick: exportOnClick, + importOnClick: importOnClick, }; } diff --git a/extensions/dacpac/src/test/DacFxTestConfigPages.ts b/extensions/dacpac/src/test/testDacFxConfigPages.ts similarity index 78% rename from extensions/dacpac/src/test/DacFxTestConfigPages.ts rename to extensions/dacpac/src/test/testDacFxConfigPages.ts index a9d0cf4937..8c2b60bed1 100644 --- a/extensions/dacpac/src/test/DacFxTestConfigPages.ts +++ b/extensions/dacpac/src/test/testDacFxConfigPages.ts @@ -8,6 +8,7 @@ import { DacFxDataModel } from '../wizard/api/models'; import { DeployConfigPage } from '../wizard/pages/deployConfigPage'; import { ExtractConfigPage } from '../wizard/pages/extractConfigPage'; import { DataTierApplicationWizard } from '../wizard/dataTierApplicationWizard'; +import { SelectOperationPage } from '../wizard/pages/selectOperationpage'; export class TestDeployConfigPage extends DeployConfigPage { @@ -37,5 +38,15 @@ export class TestExtractConfigPage extends ExtractConfigPage { get Model(): DacFxDataModel { return this.model; } - +} + +export class TestSelectOperationPage extends SelectOperationPage { + + constructor(instance: DataTierApplicationWizard, wizardPage: azdata.window.WizardPage, model: DacFxDataModel, view: azdata.ModelView) { + super(instance, wizardPage, model, view); + } + + get WizardState(): DataTierApplicationWizard { + return this.instance; + } } diff --git a/extensions/dacpac/src/test/testDacFxService.ts b/extensions/dacpac/src/test/testDacFxService.ts new file mode 100644 index 0000000000..2241c985bb --- /dev/null +++ b/extensions/dacpac/src/test/testDacFxService.ts @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- + * 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 mssql from '../../../mssql/src/mssql'; + +export const deployOperationId = 'deploy dacpac'; +export const extractOperationId = 'extract dacpac'; +export const exportOperationId = 'export bacpac'; +export const importOperationId = 'import bacpac'; +export const generateScript = 'genenrate script'; +export const generateDeployPlan = 'genenrate deploy plan'; + +export class DacFxTestService implements mssql.IDacFxService { + dacfxResult: mssql.DacFxResult = { + success: true, + operationId: 'test', + errorMessage: '' + }; + constructor() { + } + + exportBacpac(databaseName: string, packageFilePath: string, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode): Thenable { + this.dacfxResult.operationId = exportOperationId; + return Promise.resolve(this.dacfxResult); + } + importBacpac(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode): Thenable { + this.dacfxResult.operationId = importOperationId; + return Promise.resolve(this.dacfxResult); + } + extractDacpac(databaseName: string, packageFilePath: string, applicationName: string, applicationVersion: string, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode): Thenable { + this.dacfxResult.operationId = extractOperationId; + return Promise.resolve(this.dacfxResult); + } + importDatabaseProject(databaseName: string, targetFilePath: string, applicationName: string, applicationVersion: string, ownerUri: string, extractTarget: mssql.ExtractTarget, taskExecutionMode: azdata.TaskExecutionMode): Thenable { + this.dacfxResult.operationId = importOperationId; + return Promise.resolve(this.dacfxResult); + } + deployDacpac(packageFilePath: string, databaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode, sqlCommandVariableValues?: Record): Thenable { + this.dacfxResult.operationId = deployOperationId; + return Promise.resolve(this.dacfxResult); + } + generateDeployScript(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode, sqlCommandVariableValues?: Record): Thenable { + this.dacfxResult.operationId = generateScript; + return Promise.resolve(this.dacfxResult); + } + generateDeployPlan(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode): Thenable { + this.dacfxResult.operationId = generateDeployPlan; + const deployPlan: mssql.GenerateDeployPlanResult = { + operationId: generateDeployPlan, + success: true, + errorMessage: '', + report: generateDeployPlan + }; + return Promise.resolve(deployPlan); + } +} diff --git a/extensions/dacpac/src/test/wizardServiceInteraction.test.ts b/extensions/dacpac/src/test/wizardServiceInteraction.test.ts new file mode 100644 index 0000000000..8c30bb6b23 --- /dev/null +++ b/extensions/dacpac/src/test/wizardServiceInteraction.test.ts @@ -0,0 +1,76 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'mocha'; +import * as azdata from 'azdata'; +import * as should from 'should'; +import * as sinon from 'sinon'; +import { DataTierApplicationWizard, Operation } from '../wizard/dataTierApplicationWizard'; +import { DacFxDataModel } from '../wizard/api/models'; +import { DacFxTestService, deployOperationId, extractOperationId, importOperationId, exportOperationId, generateDeployPlan } from './testDacFxService'; + +let wizard: DataTierApplicationWizard; +let connectionProfileMock: azdata.connection.ConnectionProfile = new azdata.connection.ConnectionProfile(); +connectionProfileMock.connectionId = 'TEST ID'; +let dacfxServiceMock: DacFxTestService = new DacFxTestService(); +let connectionMock: azdata.connection.Connection = { + connectionId: 'TEST ID', + providerName: 'MsSql', + options: null, +}; + +describe('Dacfx wizard with connection', function (): void { + beforeEach(async function (): Promise { + sinon.stub(azdata.connection, 'getConnections').returns(Promise.resolve([])); + sinon.stub(azdata.connection, 'getUriForConnection').returns(Promise.resolve('my test uri')); + wizard = new DataTierApplicationWizard(dacfxServiceMock); + wizard.model = {}; + }); + afterEach(function (): void { + sinon.restore(); + }); + + it('Should return false if connection is not present', async () => { + sinon.stub(azdata.connection, 'getCurrentConnection').returns(Promise.resolve(undefined)); + sinon.stub(azdata.connection, 'openConnectionDialog').returns(Promise.resolve(undefined)); + let profile = { connectionProfile: connectionProfileMock }; + + const result = await wizard.start(profile); + should(result).equal(false); + }); + + // [udgautam] Skipping this for now since it gives intermittent Error: write EPIPE error. Investigating... + it.skip('Should return true if connection is present', async () => { + sinon.stub(azdata.connection, 'getCurrentConnection').returns(Promise.resolve(connectionProfileMock)); + sinon.stub(azdata.connection, 'openConnectionDialog').returns(Promise.resolve(connectionMock)); + let profile = { connectionProfile: connectionProfileMock }; + + const result = await wizard.start(profile); + should(result).equal(true); + }); + + it('Should call all service methods correctly', async () => { + wizard.model.server = connectionProfileMock; + + await validateServiceCalls(wizard, Operation.deploy, deployOperationId); + await validateServiceCalls(wizard, Operation.extract, extractOperationId); + await validateServiceCalls(wizard, Operation.import, importOperationId); + await validateServiceCalls(wizard, Operation.export, exportOperationId); + }); + + it('Should call deploy plan generator correctly', async () => { + wizard.model.server = connectionProfileMock; + + const report = await wizard.generateDeployPlan(); + should(report).equal(generateDeployPlan); + }); + + async function validateServiceCalls(wizard: DataTierApplicationWizard, selectedOperation: Operation, expectedOperationId: string): Promise { + wizard.selectedOperation = selectedOperation; + let result = await wizard.executeOperation(); + should(result.success).equal(true); + should(result.operationId).equal(expectedOperationId); + } +}); diff --git a/extensions/dacpac/src/wizard/dataTierApplicationWizard.ts b/extensions/dacpac/src/wizard/dataTierApplicationWizard.ts index 6d083c0d12..de5e489d46 100644 --- a/extensions/dacpac/src/wizard/dataTierApplicationWizard.ts +++ b/extensions/dacpac/src/wizard/dataTierApplicationWizard.ts @@ -79,15 +79,17 @@ export enum PageName { export class DataTierApplicationWizard { public wizard: azdata.window.Wizard; private connection: azdata.connection.ConnectionProfile; + private dacfxService: mssql.IDacFxService; public model: DacFxDataModel; public pages: Map = new Map(); public selectedOperation: Operation; - constructor() { + constructor(dacfxInputService?: mssql.IDacFxService) { this.wizard = azdata.window.createWizard(loc.wizardTitle); + this.dacfxService = dacfxInputService; } - public async start(p: any, ...args: any[]) { + public async start(p: any, ...args: any[]): Promise { this.model = {}; let profile = p ? p.connectionProfile : undefined; @@ -109,7 +111,7 @@ export class DataTierApplicationWizard { } // don't open the wizard if connection dialog is cancelled if (!this.connection) { - return; + return false; } } @@ -121,6 +123,7 @@ export class DataTierApplicationWizard { this.wizard.doneButton.onClick(async () => await this.executeOperation()); this.wizard.open(); + return true; } public setPages(): void { @@ -241,61 +244,56 @@ export class DataTierApplicationWizard { } } - private async executeOperation() { + public async executeOperation(): Promise { switch (this.selectedOperation) { case Operation.deploy: { - await this.deploy(); - break; + return await this.deploy(); } case Operation.extract: { - await this.extract(); - break; + return await this.extract(); } case Operation.import: { - await this.import(); - break; + return await this.import(); } case Operation.export: { - await this.export(); - break; + return await this.export(); } case Operation.generateDeployScript: { - await this.generateDeployScript(); - break; + return await this.generateDeployScript(); } } } - private async deploy(): Promise { - const service = await DataTierApplicationWizard.getService(msSqlProvider); + public async deploy(): Promise { + const service = await this.getService(msSqlProvider); const ownerUri = await azdata.connection.getUriForConnection(this.model.server.connectionId); - await service.deployDacpac(this.model.filePath, this.model.database, this.model.upgradeExisting, ownerUri, azdata.TaskExecutionMode.execute); + return await service.deployDacpac(this.model.filePath, this.model.database, this.model.upgradeExisting, ownerUri, azdata.TaskExecutionMode.execute); } - private async extract(): Promise { - const service = await DataTierApplicationWizard.getService(msSqlProvider); + private async extract(): Promise { + const service = await this.getService(msSqlProvider); const ownerUri = await azdata.connection.getUriForConnection(this.model.server.connectionId); - await service.extractDacpac(this.model.database, this.model.filePath, this.model.database, this.model.version, ownerUri, azdata.TaskExecutionMode.execute); + return await service.extractDacpac(this.model.database, this.model.filePath, this.model.database, this.model.version, ownerUri, azdata.TaskExecutionMode.execute); } - private async export(): Promise { - const service = await DataTierApplicationWizard.getService(msSqlProvider); + private async export(): Promise { + const service = await this.getService(msSqlProvider); const ownerUri = await azdata.connection.getUriForConnection(this.model.server.connectionId); - await service.exportBacpac(this.model.database, this.model.filePath, ownerUri, azdata.TaskExecutionMode.execute); + return await service.exportBacpac(this.model.database, this.model.filePath, ownerUri, azdata.TaskExecutionMode.execute); } - private async import(): Promise { - const service = await DataTierApplicationWizard.getService(msSqlProvider); + private async import(): Promise { + const service = await this.getService(msSqlProvider); const ownerUri = await azdata.connection.getUriForConnection(this.model.server.connectionId); - await service.importBacpac(this.model.filePath, this.model.database, ownerUri, azdata.TaskExecutionMode.execute); + return await service.importBacpac(this.model.filePath, this.model.database, ownerUri, azdata.TaskExecutionMode.execute); } - private async generateDeployScript(): Promise { - const service = await DataTierApplicationWizard.getService(msSqlProvider); + private async generateDeployScript(): Promise { + const service = await this.getService(msSqlProvider); const ownerUri = await azdata.connection.getUriForConnection(this.model.server.connectionId); this.wizard.message = { text: loc.generatingScriptMessage, @@ -303,7 +301,7 @@ export class DataTierApplicationWizard { description: '' }; - await service.generateDeployScript(this.model.filePath, this.model.database, ownerUri, azdata.TaskExecutionMode.script); + return await service.generateDeployScript(this.model.filePath, this.model.database, ownerUri, azdata.TaskExecutionMode.script); } public getPage(idx: number): Page { @@ -346,7 +344,7 @@ export class DataTierApplicationWizard { } public async generateDeployPlan(): Promise { - const service = await DataTierApplicationWizard.getService(msSqlProvider); + const service = await this.getService(msSqlProvider); const ownerUri = await azdata.connection.getUriForConnection(this.model.server.connectionId); const result = await service.generateDeployPlan(this.model.filePath, this.model.database, ownerUri, azdata.TaskExecutionMode.execute); @@ -358,8 +356,10 @@ export class DataTierApplicationWizard { return result.report; } - private static async getService(providerName: string): Promise { - const service = (vscode.extensions.getExtension(mssql.extension.name).exports as mssql.IExtension).dacFx; - return service; + private async getService(providerName: string): Promise { + if (!this.dacfxService) { + this.dacfxService = (vscode.extensions.getExtension(mssql.extension.name).exports as mssql.IExtension).dacFx; + } + return this.dacfxService; } } diff --git a/extensions/dacpac/yarn.lock b/extensions/dacpac/yarn.lock index 899231404b..3172fc502d 100644 --- a/extensions/dacpac/yarn.lock +++ b/extensions/dacpac/yarn.lock @@ -182,6 +182,42 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== +"@sinonjs/commons@^1", "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.7.2": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.1.tgz#e7df00f98a203324f6dc7cc606cad9d4a8ab2217" + integrity sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^6.0.0", "@sinonjs/fake-timers@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" + integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@sinonjs/formatio@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-5.0.1.tgz#f13e713cb3313b1ab965901b01b0828ea6b77089" + integrity sha512-KaiQ5pBf1MpS09MuA0kp6KBQt2JUOQycqVG1NZXvzeaXe5LGFqAKueIS0bw4w0P9r7KuBSVdUk5QjXsUdu2CxQ== + dependencies: + "@sinonjs/commons" "^1" + "@sinonjs/samsam" "^5.0.2" + +"@sinonjs/samsam@^5.0.2", "@sinonjs/samsam@^5.0.3": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.0.3.tgz#86f21bdb3d52480faf0892a480c9906aa5a52938" + integrity sha512-QucHkc2uMJ0pFGjJUDP3F9dq5dx8QIaqISl9QgwLOh6P9yv877uONPGXh/OH/0zmM3tW1JjuJltAZV2l7zU+uQ== + dependencies: + "@sinonjs/commons" "^1.6.0" + lodash.get "^4.4.2" + type-detect "^4.0.8" + +"@sinonjs/text-encoding@^0.7.1": + version "0.7.1" + resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" + integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== + "@types/mocha@^5.2.5": version "5.2.6" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.6.tgz#b8622d50557dd155e9f2f634b7d68fd38de5e94b" @@ -192,6 +228,18 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.7.tgz#01e4ea724d9e3bd50d90c11fd5980ba317d8fa11" integrity sha512-E6Zn0rffhgd130zbCbAr/JdXfXkoOUFAKNs/rF8qnafSJ8KYaA/j3oz7dcwal+lYjLA7xvdd5J4wdYpCTlP8+w== +"@types/sinon@^9.0.4": + version "9.0.4" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-9.0.4.tgz#e934f904606632287a6e7f7ab0ce3f08a0dad4b1" + integrity sha512-sJmb32asJZY6Z2u09bl0G2wglSxDlROlAejCjsnor+LzBMz17gu8IU7vKC/vWDnv9zEq2wqADHVXFjf4eE8Gdw== + dependencies: + "@types/sinonjs__fake-timers" "*" + +"@types/sinonjs__fake-timers@*": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.1.tgz#681df970358c82836b42f989188d133e218c458e" + integrity sha512-yYezQwGWty8ziyYLdZjwxyMb0CZR49h8JALHGrxjQHWlqGgc8kLdHEgWrgL0uZ29DMvEVBDnHU2Wg36zKSIUtA== + ansi-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" @@ -248,6 +296,11 @@ charenc@~0.0.1: resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= +circular-json@^0.3.1: + version "0.3.3" + resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" + integrity sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A== + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -329,6 +382,11 @@ diff@3.5.0: resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== +diff@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + dom-serializer@0: version "0.1.1" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" @@ -473,6 +531,11 @@ is-buffer@~1.1.1: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + istanbul-lib-coverage@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz#675f0ab69503fad4b1d849f736baaca803344f49" @@ -548,7 +611,17 @@ json5@^2.1.2: dependencies: minimist "^1.2.5" -lodash@^4.16.4, lodash@^4.17.13: +just-extend@^4.0.2: + version "4.1.0" + resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.1.0.tgz#7278a4027d889601640ee0ce0e5a00b992467da4" + integrity sha512-ApcjaOdVTJ7y4r08xI5wIqpvwS48Q0PBG4DJROcEkH1f8MdAiNFyFxz3xoL0LWAVwjrwPYZdVHHxhRHcx/uGLA== + +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= + +lodash@^4.16.4, lodash@^4.17.13, lodash@^4.17.4: version "4.17.19" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== @@ -647,6 +720,17 @@ ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== +nise@^4.0.1: + version "4.0.4" + resolved "https://registry.yarnpkg.com/nise/-/nise-4.0.4.tgz#d73dea3e5731e6561992b8f570be9e363c4512dd" + integrity sha512-bTTRUNlemx6deJa+ZyoCUTRvH3liK5+N6VQZ4NIw90AgDXY6iPnsqplNFf6STcj+ePk0H/xqxnP75Lr0J0Fq3A== + dependencies: + "@sinonjs/commons" "^1.7.0" + "@sinonjs/fake-timers" "^6.0.0" + "@sinonjs/text-encoding" "^0.7.1" + just-extend "^4.0.2" + path-to-regexp "^1.7.0" + once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -664,11 +748,23 @@ path-parse@^1.0.6: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== +path-to-regexp@^1.7.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" + integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== + dependencies: + isarray "0.0.1" + pify@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== +postinstall-build@^5.0.1: + version "5.0.3" + resolved "https://registry.yarnpkg.com/postinstall-build/-/postinstall-build-5.0.3.tgz#238692f712a481d8f5bc8960e94786036241efc7" + integrity sha512-vPvPe8TKgp4FLgY3+DfxCE5PIfoXBK2lyLfNCxsRbDsV6vS4oU5RG/IWxrblMn6heagbnMED3MemUQllQ2bQUg== + readable-stream@^3.1.1: version "3.2.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.2.0.tgz#de17f229864c120a9f56945756e4f32c4045245d" @@ -740,7 +836,7 @@ should-util@^1.0.0: resolved "https://registry.yarnpkg.com/should-util/-/should-util-1.0.0.tgz#c98cda374aa6b190df8ba87c9889c2b4db620063" integrity sha1-yYzaN0qmsZDfi6h8mInCtNtiAGM= -should@^13.2.1: +should@^13.2.3: version "13.2.3" resolved "https://registry.yarnpkg.com/should/-/should-13.2.3.tgz#96d8e5acf3e97b49d89b51feaa5ae8d07ef58f10" integrity sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ== @@ -751,6 +847,19 @@ should@^13.2.1: should-type-adaptors "^1.0.1" should-util "^1.0.0" +sinon@^9.0.2: + version "9.0.2" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.0.2.tgz#b9017e24633f4b1c98dfb6e784a5f0509f5fd85d" + integrity sha512-0uF8Q/QHkizNUmbK3LRFqx5cpTttEVXudywY9Uwzy8bTfZUhljZ7ARzSxnRHWYWtVTeh4Cw+tTb3iU21FQVO9A== + dependencies: + "@sinonjs/commons" "^1.7.2" + "@sinonjs/fake-timers" "^6.0.1" + "@sinonjs/formatio" "^5.0.1" + "@sinonjs/samsam" "^5.0.3" + diff "^4.0.2" + nise "^4.0.1" + supports-color "^7.1.0" + source-map@^0.5.0: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -806,6 +915,20 @@ to-fast-properties@^2.0.0: resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= +type-detect@4.0.8, type-detect@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +typemoq@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/typemoq/-/typemoq-2.1.0.tgz#4452ce360d92cf2a1a180f0c29de2803f87af1e8" + integrity sha512-DtRNLb7x8yCTv/KHlwes+NI+aGb4Vl1iPC63Hhtcvk1DpxSAZzKWQv0RQFY0jX2Uqj0SDBNl8Na4e6MV6TNDgw== + dependencies: + circular-json "^0.3.1" + lodash "^4.17.4" + postinstall-build "^5.0.1" + util-deprecate@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"