From c963e6ec081efd380a0dbdd92530b323960f3562 Mon Sep 17 00:00:00 2001 From: Benjin Dubishar Date: Thu, 23 Jul 2020 02:15:05 -0700 Subject: [PATCH] Add test coverage for dacpac wizard import flow (#11483) * Adding importConfig onPageEnter() test * Removing redundancy from dacpac wizard pages * promisifying file selection so it can be awaited in the test * removing debug prints * PR feedback --- .../dacpac/src/test/configPages.test.ts | 37 +++++++++++- extensions/dacpac/src/test/testContext.ts | 11 +++- .../dacpac/src/test/testDacFxConfigPages.ts | 14 ++++- extensions/dacpac/src/test/testUtils.ts | 22 +++++++ extensions/dacpac/src/wizard/api/basePage.ts | 10 +++- .../dacpac/src/wizard/api/dacFxConfigPage.ts | 12 +--- .../src/wizard/pages/dacFxSummaryPage.ts | 12 +--- .../src/wizard/pages/deployConfigPage.ts | 5 -- .../dacpac/src/wizard/pages/deployPlanPage.ts | 4 -- .../src/wizard/pages/exportConfigPage.ts | 6 -- .../src/wizard/pages/extractConfigPage.ts | 6 -- .../src/wizard/pages/importConfigPage.ts | 57 +++++++++---------- .../src/wizard/pages/selectOperationpage.ts | 12 +--- 13 files changed, 119 insertions(+), 89 deletions(-) create mode 100644 extensions/dacpac/src/test/testUtils.ts diff --git a/extensions/dacpac/src/test/configPages.test.ts b/extensions/dacpac/src/test/configPages.test.ts index 292f44a2b7..816610938c 100644 --- a/extensions/dacpac/src/test/configPages.test.ts +++ b/extensions/dacpac/src/test/configPages.test.ts @@ -5,10 +5,17 @@ import 'mocha'; import * as should from 'should'; +import * as sinon from 'sinon'; +import * as azdata from 'azdata'; +import * as vscode from 'vscode'; +import * as os from 'os'; +import * as path from 'path'; + import { DataTierApplicationWizard, PageName } from '../wizard/dataTierApplicationWizard'; import { DacFxDataModel } from '../wizard/api/models'; import { TestContext, createContext } from './testContext'; -import { TestDeployConfigPage, TestExtractConfigPage } from './testDacFxConfigPages'; +import { TestDeployConfigPage, TestExtractConfigPage, TestImportConfigPage } from './testDacFxConfigPages'; +import { mockConnectionProfile } from './testUtils'; let wizard: DataTierApplicationWizard; let testContext: TestContext; @@ -20,6 +27,10 @@ describe('Dacfx Wizard Pages', function (): void { wizard.model.server = undefined; }); + afterEach(function (): void { + sinon.restore(); + }); + it('Should open and edit deploy config page correctly', async () => { testContext = createContext(); wizard.setPages(); @@ -74,4 +85,28 @@ describe('Dacfx Wizard Pages', function (): void { await extractConfigPage.onPageLeave(); should.equal(extractConfigPage.Model.filePath, 'DummyPath.dacpac'); }); + + it('Should open and edit import config page correctly', async () => { + const dacpacPath = vscode.Uri.file(path.join(os.tmpdir(), 'myDatabase.dacpac')).fsPath; + + testContext = createContext(); + wizard.setPages(); + + sinon.stub(azdata.connection, 'getConnections').resolves([mockConnectionProfile]); + sinon.stub(azdata.connection, 'listDatabases').resolves(['fakeDatabaseName']); + sinon.stub(vscode.window, 'showOpenDialog').resolves([vscode.Uri.file(dacpacPath)]); + + let importConfigPage = new TestImportConfigPage(wizard, wizard.pages.get(PageName.importConfig).wizardPage, wizard.model, testContext.viewContext.view); + await importConfigPage.start(); + + let result = await importConfigPage.onPageEnter(); + should(result).equal(true, 'onPageEnter() should successfullly load connection profiles'); + + testContext.viewContext.fileButtonOnClick.fire(undefined); + await importConfigPage.selectionPromise; + should(importConfigPage.Model.filePath).equal(dacpacPath); + should(importConfigPage.Model.database).equal('myDatabase'); + }); }); + + diff --git a/extensions/dacpac/src/test/testContext.ts b/extensions/dacpac/src/test/testContext.ts index ced638a7bb..8e5712c449 100644 --- a/extensions/dacpac/src/test/testContext.ts +++ b/extensions/dacpac/src/test/testContext.ts @@ -51,6 +51,7 @@ export interface ViewTestContext { extractOnClick: vscode.EventEmitter, exportOnClick: vscode.EventEmitter, importOnClick: vscode.EventEmitter, + fileButtonOnClick: vscode.EventEmitter; } export function createViewContext(): ViewTestContext { @@ -63,6 +64,7 @@ export function createViewContext(): ViewTestContext { let extractOnClick: vscode.EventEmitter = new vscode.EventEmitter(); let exportOnClick: vscode.EventEmitter = new vscode.EventEmitter(); let importOnClick: vscode.EventEmitter = new vscode.EventEmitter(); + let fileButtonOnClick: vscode.EventEmitter = new vscode.EventEmitter(); let componentBase: azdata.Component = { id: '', @@ -110,7 +112,13 @@ export function createViewContext(): ViewTestContext { let buttonBuilder: azdata.ComponentBuilder = { component: () => button, - withProperties: () => buttonBuilder, + withProperties: (properties) => { + if ((properties as any).label === '•••') { + button.label = '•••'; + button.onDidClick = fileButtonOnClick.event; + } + return buttonBuilder; + }, withValidation: () => buttonBuilder }; @@ -295,5 +303,6 @@ export function createViewContext(): ViewTestContext { extractOnClick: extractOnClick, exportOnClick: exportOnClick, importOnClick: importOnClick, + fileButtonOnClick: fileButtonOnClick }; } diff --git a/extensions/dacpac/src/test/testDacFxConfigPages.ts b/extensions/dacpac/src/test/testDacFxConfigPages.ts index 8c2b60bed1..3aa1bcc853 100644 --- a/extensions/dacpac/src/test/testDacFxConfigPages.ts +++ b/extensions/dacpac/src/test/testDacFxConfigPages.ts @@ -9,9 +9,9 @@ import { DeployConfigPage } from '../wizard/pages/deployConfigPage'; import { ExtractConfigPage } from '../wizard/pages/extractConfigPage'; import { DataTierApplicationWizard } from '../wizard/dataTierApplicationWizard'; import { SelectOperationPage } from '../wizard/pages/selectOperationpage'; +import { ImportConfigPage } from '../wizard/pages/importConfigPage'; export class TestDeployConfigPage extends DeployConfigPage { - constructor(instance: DataTierApplicationWizard, wizardPage: azdata.window.WizardPage, model: DacFxDataModel, view: azdata.ModelView) { super(instance, wizardPage, model, view); } @@ -30,7 +30,6 @@ export class TestDeployConfigPage extends DeployConfigPage { } export class TestExtractConfigPage extends ExtractConfigPage { - constructor(instance: DataTierApplicationWizard, wizardPage: azdata.window.WizardPage, model: DacFxDataModel, view: azdata.ModelView) { super(instance, wizardPage, model, view); } @@ -41,7 +40,6 @@ export class TestExtractConfigPage extends ExtractConfigPage { } export class TestSelectOperationPage extends SelectOperationPage { - constructor(instance: DataTierApplicationWizard, wizardPage: azdata.window.WizardPage, model: DacFxDataModel, view: azdata.ModelView) { super(instance, wizardPage, model, view); } @@ -50,3 +48,13 @@ export class TestSelectOperationPage extends SelectOperationPage { return this.instance; } } + +export class TestImportConfigPage extends ImportConfigPage { + constructor(instance: DataTierApplicationWizard, wizardPage: azdata.window.WizardPage, model: DacFxDataModel, view: azdata.ModelView) { + super(instance, wizardPage, model, view); + } + + get Model(): DacFxDataModel { + return this.model; + } +} diff --git a/extensions/dacpac/src/test/testUtils.ts b/extensions/dacpac/src/test/testUtils.ts new file mode 100644 index 0000000000..aaabf9085b --- /dev/null +++ b/extensions/dacpac/src/test/testUtils.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; + +export const mockConnectionProfile: azdata.connection.ConnectionProfile = { + providerId: 'MSSQL', + connectionId: 'My Connection ID', + connectionName: 'My Connection', + serverName: 'My Server', + databaseName: 'My Database', + userName: 'My User', + password: 'My Pwd', + authenticationType: 'SqlLogin', + savePassword: false, + groupFullName: 'My groupName', + groupId: 'My GroupId', + saveProfile: true, + options: {} +}; diff --git a/extensions/dacpac/src/wizard/api/basePage.ts b/extensions/dacpac/src/wizard/api/basePage.ts index e480f7bd6e..1b9e27d3ce 100644 --- a/extensions/dacpac/src/wizard/api/basePage.ts +++ b/extensions/dacpac/src/wizard/api/basePage.ts @@ -6,14 +6,22 @@ import * as azdata from 'azdata'; import * as loc from '../../localizedConstants'; import { DacFxDataModel } from './models'; +import { DataTierApplicationWizard } from '../dataTierApplicationWizard'; export abstract class BasePage { - + protected readonly instance: DataTierApplicationWizard; protected readonly wizardPage: azdata.window.WizardPage; protected readonly model: DacFxDataModel; protected readonly view: azdata.ModelView; public databaseValues: string[]; + protected constructor(instance: DataTierApplicationWizard, wizardPage: azdata.window.WizardPage, model: DacFxDataModel, view: azdata.ModelView) { + this.instance = instance; + this.wizardPage = wizardPage; + this.model = model; + this.view = view; + } + /** * This method constructs all the elements of the page. */ diff --git a/extensions/dacpac/src/wizard/api/dacFxConfigPage.ts b/extensions/dacpac/src/wizard/api/dacFxConfigPage.ts index aa51d620d9..0d7b1c851a 100644 --- a/extensions/dacpac/src/wizard/api/dacFxConfigPage.ts +++ b/extensions/dacpac/src/wizard/api/dacFxConfigPage.ts @@ -14,11 +14,6 @@ import { BasePage } from './basePage'; import { sanitizeStringForFilename, isValidBasename, isValidBasenameErrorMessage } from './utils'; export abstract class DacFxConfigPage extends BasePage { - - protected readonly wizardPage: azdata.window.WizardPage; - protected readonly instance: DataTierApplicationWizard; - protected readonly model: DacFxDataModel; - protected readonly view: azdata.ModelView; protected serverDropdown: azdata.DropDownComponent; protected databaseTextBox: azdata.InputBoxComponent; protected databaseDropdown: azdata.DropDownComponent; @@ -28,11 +23,7 @@ export abstract class DacFxConfigPage extends BasePage { protected fileExtension: string; protected constructor(instance: DataTierApplicationWizard, wizardPage: azdata.window.WizardPage, model: DacFxDataModel, view: azdata.ModelView) { - super(); - this.instance = instance; - this.wizardPage = wizardPage; - this.model = model; - this.view = view; + super(instance, wizardPage, model, view); } public setupNavigationValidator(): void { @@ -67,6 +58,7 @@ export abstract class DacFxConfigPage extends BasePage { protected async populateServerDropdown(): Promise { let values = await this.getServerValues(); + if (values === undefined) { return false; } diff --git a/extensions/dacpac/src/wizard/pages/dacFxSummaryPage.ts b/extensions/dacpac/src/wizard/pages/dacFxSummaryPage.ts index 46a44957be..1d143d3955 100644 --- a/extensions/dacpac/src/wizard/pages/dacFxSummaryPage.ts +++ b/extensions/dacpac/src/wizard/pages/dacFxSummaryPage.ts @@ -10,22 +10,12 @@ import { DataTierApplicationWizard, Operation } from '../dataTierApplicationWiza import { BasePage } from '../api/basePage'; export class DacFxSummaryPage extends BasePage { - - protected readonly wizardPage: azdata.window.WizardPage; - protected readonly instance: DataTierApplicationWizard; - protected readonly model: DacFxDataModel; - protected readonly view: azdata.ModelView; - private form: azdata.FormContainer; private table: azdata.TableComponent; private loader: azdata.LoadingComponent; public constructor(instance: DataTierApplicationWizard, wizardPage: azdata.window.WizardPage, model: DacFxDataModel, view: azdata.ModelView) { - super(); - this.instance = instance; - this.wizardPage = wizardPage; - this.model = model; - this.view = view; + super(instance, wizardPage, model, view); } async start(): Promise { diff --git a/extensions/dacpac/src/wizard/pages/deployConfigPage.ts b/extensions/dacpac/src/wizard/pages/deployConfigPage.ts index 30afebc48b..ca76f31ac2 100644 --- a/extensions/dacpac/src/wizard/pages/deployConfigPage.ts +++ b/extensions/dacpac/src/wizard/pages/deployConfigPage.ts @@ -12,11 +12,6 @@ import { DacFxConfigPage } from '../api/dacFxConfigPage'; import { generateDatabaseName } from '../api/utils'; export class DeployConfigPage extends DacFxConfigPage { - - protected readonly wizardPage: azdata.window.WizardPage; - protected readonly instance: DataTierApplicationWizard; - protected readonly model: DacFxDataModel; - protected readonly view: azdata.ModelView; private databaseDropdownComponent: azdata.FormComponent; private databaseComponent: azdata.FormComponent; private formBuilder: azdata.FormBuilder; diff --git a/extensions/dacpac/src/wizard/pages/deployPlanPage.ts b/extensions/dacpac/src/wizard/pages/deployPlanPage.ts index 288355960b..8ca68668aa 100644 --- a/extensions/dacpac/src/wizard/pages/deployPlanPage.ts +++ b/extensions/dacpac/src/wizard/pages/deployPlanPage.ts @@ -34,10 +34,6 @@ class DeployPlanResult { } export class DeployPlanPage extends DacFxConfigPage { - protected readonly wizardPage: azdata.window.WizardPage; - protected readonly instance: DataTierApplicationWizard; - protected readonly model: DacFxDataModel; - protected readonly view: azdata.ModelView; private formBuilder: azdata.FormBuilder; private form: azdata.FormContainer; private table: azdata.TableComponent; diff --git a/extensions/dacpac/src/wizard/pages/exportConfigPage.ts b/extensions/dacpac/src/wizard/pages/exportConfigPage.ts index 310f9e4e3d..ccde36d433 100644 --- a/extensions/dacpac/src/wizard/pages/exportConfigPage.ts +++ b/extensions/dacpac/src/wizard/pages/exportConfigPage.ts @@ -11,12 +11,6 @@ import { DataTierApplicationWizard } from '../dataTierApplicationWizard'; import { DacFxConfigPage } from '../api/dacFxConfigPage'; export class ExportConfigPage extends DacFxConfigPage { - - protected readonly wizardPage: azdata.window.WizardPage; - protected readonly instance: DataTierApplicationWizard; - protected readonly model: DacFxDataModel; - protected readonly view: azdata.ModelView; - private form: azdata.FormContainer; public constructor(instance: DataTierApplicationWizard, wizardPage: azdata.window.WizardPage, model: DacFxDataModel, view: azdata.ModelView) { diff --git a/extensions/dacpac/src/wizard/pages/extractConfigPage.ts b/extensions/dacpac/src/wizard/pages/extractConfigPage.ts index ca5d383544..7931e9a21d 100644 --- a/extensions/dacpac/src/wizard/pages/extractConfigPage.ts +++ b/extensions/dacpac/src/wizard/pages/extractConfigPage.ts @@ -11,12 +11,6 @@ import { DataTierApplicationWizard } from '../dataTierApplicationWizard'; import { DacFxConfigPage } from '../api/dacFxConfigPage'; export class ExtractConfigPage extends DacFxConfigPage { - - protected readonly wizardPage: azdata.window.WizardPage; - protected readonly instance: DataTierApplicationWizard; - protected readonly model: DacFxDataModel; - protected readonly view: azdata.ModelView; - private form: azdata.FormContainer; private versionTextBox: azdata.InputBoxComponent; diff --git a/extensions/dacpac/src/wizard/pages/importConfigPage.ts b/extensions/dacpac/src/wizard/pages/importConfigPage.ts index ddc6f99a44..f124664835 100644 --- a/extensions/dacpac/src/wizard/pages/importConfigPage.ts +++ b/extensions/dacpac/src/wizard/pages/importConfigPage.ts @@ -12,13 +12,8 @@ import { DacFxConfigPage } from '../api/dacFxConfigPage'; import { generateDatabaseName } from '../api/utils'; export class ImportConfigPage extends DacFxConfigPage { - - protected readonly wizardPage: azdata.window.WizardPage; - protected readonly instance: DataTierApplicationWizard; - protected readonly model: DacFxDataModel; - protected readonly view: azdata.ModelView; - private form: azdata.FormContainer; + public selectionPromise: Promise; public constructor(instance: DataTierApplicationWizard, wizardPage: azdata.window.WizardPage, model: DacFxDataModel, view: azdata.ModelView) { super(instance, wizardPage, model, view); @@ -54,30 +49,7 @@ export class ImportConfigPage extends DacFxConfigPage { private async createFileBrowser(): Promise { this.createFileBrowserParts(); - this.fileButton.onDidClick(async (click) => { - let fileUris = await vscode.window.showOpenDialog( - { - canSelectFiles: true, - canSelectFolders: false, - canSelectMany: false, - defaultUri: vscode.Uri.file(this.getRootPath()), - openLabel: loc.open, - filters: { - 'bacpac Files': ['bacpac'], - } - } - ); - - if (!fileUris || fileUris.length === 0) { - return; - } - - let fileUri = fileUris[0]; - this.fileTextBox.value = fileUri.fsPath; - this.model.filePath = fileUri.fsPath; - this.model.database = generateDatabaseName(this.model.filePath); - this.databaseTextBox.value = this.model.database; - }); + this.fileButton.onDidClick(async (click) => { this.selectionPromise = this.handleFileSelection(); }); this.fileTextBox.onTextChanged(async () => { this.model.filePath = this.fileTextBox.value; @@ -91,4 +63,29 @@ export class ImportConfigPage extends DacFxConfigPage { actions: [this.fileButton] }; } + + private async handleFileSelection(): Promise { + let fileUris = await vscode.window.showOpenDialog( + { + canSelectFiles: true, + canSelectFolders: false, + canSelectMany: false, + defaultUri: vscode.Uri.file(this.getRootPath()), + openLabel: loc.open, + filters: { + 'bacpac Files': ['bacpac'], + } + } + ); + + if (!fileUris || fileUris.length === 0) { + return; + } + + let fileUri = fileUris[0]; + this.fileTextBox.value = fileUri.fsPath; + this.model.filePath = fileUri.fsPath; + this.model.database = generateDatabaseName(this.model.filePath); + this.databaseTextBox.value = this.model.database; + } } diff --git a/extensions/dacpac/src/wizard/pages/selectOperationpage.ts b/extensions/dacpac/src/wizard/pages/selectOperationpage.ts index 37636352bc..d4085ddc20 100644 --- a/extensions/dacpac/src/wizard/pages/selectOperationpage.ts +++ b/extensions/dacpac/src/wizard/pages/selectOperationpage.ts @@ -10,12 +10,6 @@ import { DataTierApplicationWizard, Operation, DeployOperationPath, ExtractOpera import { BasePage } from '../api/basePage'; export class SelectOperationPage extends BasePage { - - protected readonly wizardPage: azdata.window.WizardPage; - protected readonly instance: DataTierApplicationWizard; - protected readonly model: DacFxDataModel; - protected readonly view: azdata.ModelView; - private deployRadioButton: azdata.RadioButtonComponent; private extractRadioButton: azdata.RadioButtonComponent; private importRadioButton: azdata.RadioButtonComponent; @@ -23,11 +17,7 @@ export class SelectOperationPage extends BasePage { private form: azdata.FormContainer; public constructor(instance: DataTierApplicationWizard, wizardPage: azdata.window.WizardPage, model: DacFxDataModel, view: azdata.ModelView) { - super(); - this.instance = instance; - this.wizardPage = wizardPage; - this.model = model; - this.view = view; + super(instance, wizardPage, model, view); } async start(): Promise {