From 81a1b1a55a9b54994e7ef217ff8b71e034a4a096 Mon Sep 17 00:00:00 2001 From: Sakshi Sharma <57200045+SakshiS-harma@users.noreply.github.com> Date: Thu, 7 Jan 2021 10:15:09 -0800 Subject: [PATCH] Sakshis/scmp test (#13904) * Fixed a few await issues * Introduced ModelView as a class member in schemaCompareMainWindow * Added a few button tests * Fixed tests * Add a missing await --- extensions/schema-compare/src/extension.ts | 2 +- .../src/schemaCompareMainWindow.ts | 135 ++++---- .../src/test/schemaCompare.test.ts | 199 ++++++++++- .../src/test/schemaCompareDialog.test.ts | 2 +- .../schema-compare/src/test/testContext.ts | 327 ++++++++++++++++++ .../src/test/testSchemaCompareMainWindow.ts | 6 +- .../src/test/testSchemaCompareService.ts | 32 +- 7 files changed, 622 insertions(+), 81 deletions(-) diff --git a/extensions/schema-compare/src/extension.ts b/extensions/schema-compare/src/extension.ts index 57a379dfb1..ce2fa777d0 100644 --- a/extensions/schema-compare/src/extension.ts +++ b/extensions/schema-compare/src/extension.ts @@ -7,7 +7,7 @@ import * as vscode from 'vscode'; import { SchemaCompareMainWindow } from './schemaCompareMainWindow'; export async function activate(extensionContext: vscode.ExtensionContext): Promise { - vscode.commands.registerCommand('schemaCompare.start', async (context: any) => { await new SchemaCompareMainWindow(undefined, extensionContext).start(context); }); + vscode.commands.registerCommand('schemaCompare.start', async (context: any) => { await new SchemaCompareMainWindow(undefined, extensionContext, undefined).start(context); }); } export function deactivate(): void { diff --git a/extensions/schema-compare/src/schemaCompareMainWindow.ts b/extensions/schema-compare/src/schemaCompareMainWindow.ts index d0cc428c54..d1431eb10a 100644 --- a/extensions/schema-compare/src/schemaCompareMainWindow.ts +++ b/extensions/schema-compare/src/schemaCompareMainWindow.ts @@ -68,7 +68,11 @@ export class SchemaCompareMainWindow { public sourceEndpointInfo: mssql.SchemaCompareEndpointInfo; public targetEndpointInfo: mssql.SchemaCompareEndpointInfo; - constructor(private schemaCompareService?: mssql.ISchemaCompareService, private extensionContext?: vscode.ExtensionContext) { + public promise; + + public schemaCompareDialog: SchemaCompareDialog; + + constructor(private schemaCompareService?: mssql.ISchemaCompareService, private extensionContext?: vscode.ExtensionContext, private view?: azdata.ModelView) { this.SchemaCompareActionMap = new Map(); this.SchemaCompareActionMap[mssql.SchemaUpdateAction.Delete] = loc.deleteAction; this.SchemaCompareActionMap[mssql.SchemaUpdateAction.Change] = loc.changeAction; @@ -117,52 +121,56 @@ export class SchemaCompareMainWindow { private async registerContent(): Promise { return new Promise((resolve) => { - this.editor.registerContent(async view => { - this.differencesTable = view.modelBuilder.table().withProperties({ + this.editor.registerContent(async (view) => { + if (isNullOrUndefined(this.view)) { + this.view = view; + } + + this.differencesTable = this.view.modelBuilder.table().withProperties({ data: [], title: loc.differencesTableTitle }).component(); - this.diffEditor = view.modelBuilder.diffeditor().withProperties({ + this.diffEditor = this.view.modelBuilder.diffeditor().withProperties({ contentLeft: os.EOL, contentRight: os.EOL, height: 500, title: loc.diffEditorTitle }).component(); - this.splitView = view.modelBuilder.splitViewContainer().component(); + this.splitView = this.view.modelBuilder.splitViewContainer().component(); - let sourceTargetLabels = view.modelBuilder.flexContainer() + let sourceTargetLabels = this.view.modelBuilder.flexContainer() .withProperties({ alignItems: 'stretch', horizontal: true }).component(); - this.sourceTargetFlexLayout = view.modelBuilder.flexContainer() + this.sourceTargetFlexLayout = this.view.modelBuilder.flexContainer() .withProperties({ alignItems: 'stretch', horizontal: true }).component(); - this.createSwitchButton(view); - this.createCompareButton(view); - this.createCancelButton(view); - this.createGenerateScriptButton(view); - this.createApplyButton(view); - this.createOptionsButton(view); - this.createOpenScmpButton(view); - this.createSaveScmpButton(view); - this.createSourceAndTargetButtons(view); + this.createSwitchButton(); + this.createCompareButton(); + this.createCancelButton(); + this.createGenerateScriptButton(); + this.createApplyButton(); + this.createOptionsButton(); + this.createOpenScmpButton(); + this.createSaveScmpButton(); + this.createSourceAndTargetButtons(); this.sourceName = getEndpointName(this.sourceEndpointInfo); this.targetName = ' '; - this.sourceNameComponent = view.modelBuilder.inputBox().withProperties({ + this.sourceNameComponent = this.view.modelBuilder.inputBox().withProperties({ value: this.sourceName, title: this.sourceName, enabled: false }).component(); - this.targetNameComponent = view.modelBuilder.inputBox().withProperties({ + this.targetNameComponent = this.view.modelBuilder.inputBox().withProperties({ value: this.targetName, title: this.targetName, enabled: false @@ -170,7 +178,7 @@ export class SchemaCompareMainWindow { this.resetButtons(ResetButtonState.noSourceTarget); - let toolBar = view.modelBuilder.toolbarContainer(); + let toolBar = this.view.modelBuilder.toolbarContainer(); toolBar.addToolbarItems([{ component: this.compareButton }, { @@ -191,17 +199,17 @@ export class SchemaCompareMainWindow { component: this.saveScmpButton }]); - let sourceLabel = view.modelBuilder.text().withProperties({ + let sourceLabel = this.view.modelBuilder.text().withProperties({ value: loc.sourceTitle, CSSStyles: { 'margin-bottom': '0px' } }).component(); - let targetLabel = view.modelBuilder.text().withProperties({ + let targetLabel = this.view.modelBuilder.text().withProperties({ value: loc.targetTitle, CSSStyles: { 'margin-bottom': '0px' } }).component(); - let arrowLabel = view.modelBuilder.text().withProperties({ + let arrowLabel = this.view.modelBuilder.text().withProperties({ value: '➔' }).component(); @@ -213,20 +221,20 @@ export class SchemaCompareMainWindow { this.sourceTargetFlexLayout.addItem(this.targetNameComponent, { CSSStyles: { 'width': '40%', 'height': '25px', 'margin-top': '10px', 'margin-left': '15px', 'margin-right': '10px' } }); this.sourceTargetFlexLayout.addItem(this.selectTargetButton, { CSSStyles: { 'margin-top': '10px' } }); - this.loader = view.modelBuilder.loadingComponent().component(); - this.waitText = view.modelBuilder.text().withProperties({ + this.loader = this.view.modelBuilder.loadingComponent().component(); + this.waitText = this.view.modelBuilder.text().withProperties({ value: loc.waitText }).component(); - this.startText = view.modelBuilder.text().withProperties({ + this.startText = this.view.modelBuilder.text().withProperties({ value: loc.startText }).component(); - this.noDifferencesLabel = view.modelBuilder.text().withProperties({ + this.noDifferencesLabel = this.view.modelBuilder.text().withProperties({ value: loc.noDifferencesText }).component(); - this.flexModel = view.modelBuilder.flexContainer().component(); + this.flexModel = this.view.modelBuilder.flexContainer().component(); this.flexModel.addItem(toolBar.component(), { flex: 'none' }); this.flexModel.addItem(sourceTargetLabels, { flex: 'none' }); this.flexModel.addItem(this.sourceTargetFlexLayout, { flex: 'none' }); @@ -237,7 +245,7 @@ export class SchemaCompareMainWindow { height: '100%' }); - await view.initializeModel(this.flexModel); + await this.view.initializeModel(this.flexModel); resolve(); }); }); @@ -268,13 +276,15 @@ export class SchemaCompareMainWindow { this.deploymentOptions = deploymentOptions; } - public async execute(): Promise { + public async execute() { TelemetryReporter.sendActionEvent(TelemetryViews.SchemaCompareMainWindow, 'SchemaComparisonStarted'); const service = await this.getService(); + if (!this.operationId) { // create once per page this.operationId = generateGuid(); } + this.comparisonResult = await service.schemaCompare(this.operationId, this.sourceEndpointInfo, this.targetEndpointInfo, azdata.TaskExecutionMode.execute, this.deploymentOptions); if (!this.comparisonResult || !this.comparisonResult.success) { TelemetryReporter.createErrorEvent(TelemetryViews.SchemaCompareMainWindow, 'SchemaComparisonFailed', undefined, getTelemetryErrorType(this.comparisonResult?.errorMessage)) @@ -362,8 +372,8 @@ export class SchemaCompareMainWindow { // explicitly exclude things that were excluded in previous compare const thingsToExclude = this.sourceTargetSwitched ? this.originalTargetExcludes : this.originalSourceExcludes; if (thingsToExclude) { - thingsToExclude.forEach(item => { - service.schemaCompareIncludeExcludeNode(this.comparisonResult.operationId, item, false, azdata.TaskExecutionMode.execute); + thingsToExclude.forEach(async (item) => { + await service.schemaCompareIncludeExcludeNode(this.comparisonResult.operationId, item, false, azdata.TaskExecutionMode.execute); }); // disable apply and generate script buttons if no changes are included @@ -586,7 +596,7 @@ export class SchemaCompareMainWindow { return script; } - public startCompare(): void { + public async startCompare(): Promise { this.flexModel.removeItem(this.splitView); this.flexModel.removeItem(this.noDifferencesLabel); this.flexModel.removeItem(this.startText); @@ -604,11 +614,12 @@ export class SchemaCompareMainWindow { this.tablelistenersToDispose.forEach(x => x.dispose()); } this.resetButtons(ResetButtonState.comparing); - this.execute(); + this.promise = this.execute(); + await this.promise; } - private createCompareButton(view: azdata.ModelView): void { - this.compareButton = view.modelBuilder.button().withProperties({ + private createCompareButton(): void { + this.compareButton = this.view.modelBuilder.button().withProperties({ label: loc.compare, iconPath: { light: path.join(this.extensionContext.extensionPath, 'media', 'compare.svg'), @@ -618,12 +629,12 @@ export class SchemaCompareMainWindow { }).component(); this.compareButton.onDidClick(async (click) => { - this.startCompare(); + await this.startCompare(); }); } - private createCancelButton(view: azdata.ModelView): void { - this.cancelCompareButton = view.modelBuilder.button().withProperties({ + private createCancelButton(): void { + this.cancelCompareButton = this.view.modelBuilder.button().withProperties({ label: loc.stop, iconPath: { light: path.join(this.extensionContext.extensionPath, 'media', 'stop.svg'), @@ -671,8 +682,8 @@ export class SchemaCompareMainWindow { } } - private createGenerateScriptButton(view: azdata.ModelView): void { - this.generateScriptButton = view.modelBuilder.button().withProperties({ + private createGenerateScriptButton(): void { + this.generateScriptButton = this.view.modelBuilder.button().withProperties({ label: loc.generateScript, iconPath: { light: path.join(this.extensionContext.extensionPath, 'media', 'generate-script.svg'), @@ -707,8 +718,8 @@ export class SchemaCompareMainWindow { }).send(); } - private createOptionsButton(view: azdata.ModelView) { - this.optionsButton = view.modelBuilder.button().withProperties({ + private createOptionsButton() { + this.optionsButton = this.view.modelBuilder.button().withProperties({ label: loc.options, iconPath: { light: path.join(this.extensionContext.extensionPath, 'media', 'options.svg'), @@ -725,9 +736,9 @@ export class SchemaCompareMainWindow { }); } - private createApplyButton(view: azdata.ModelView) { + private createApplyButton() { - this.applyButton = view.modelBuilder.button().withProperties({ + this.applyButton = this.view.modelBuilder.button().withProperties({ label: loc.apply, iconPath: { light: path.join(this.extensionContext.extensionPath, 'media', 'start.svg'), @@ -837,8 +848,8 @@ export class SchemaCompareMainWindow { this.flexModel.addItem(this.startText, { CSSStyles: { 'margin': 'auto' } }); } - private createSwitchButton(view: azdata.ModelView): void { - this.switchButton = view.modelBuilder.button().withProperties({ + private createSwitchButton(): void { + this.switchButton = this.view.modelBuilder.button().withProperties({ label: loc.switchDirection, iconPath: { light: path.join(this.extensionContext.extensionPath, 'media', 'switch-directions.svg'), @@ -868,39 +879,41 @@ export class SchemaCompareMainWindow { // only compare if both source and target are set if (this.sourceEndpointInfo && this.targetEndpointInfo) { - this.startCompare(); + await this.startCompare(); } }); } - private createSourceAndTargetButtons(view: azdata.ModelView): void { - this.selectSourceButton = view.modelBuilder.button().withProperties({ + private createSourceAndTargetButtons(): void { + this.selectSourceButton = this.view.modelBuilder.button().withProperties({ label: '•••', title: loc.selectSource, ariaLabel: loc.selectSource }).component(); - this.selectSourceButton.onDidClick(() => { + this.selectSourceButton.onDidClick(async () => { TelemetryReporter.sendActionEvent(TelemetryViews.SchemaCompareMainWindow, 'SchemaCompareSelectSource'); - let dialog = new SchemaCompareDialog(this); - dialog.openDialog(); + this.schemaCompareDialog = new SchemaCompareDialog(this); + this.promise = this.schemaCompareDialog.openDialog(); + await this.promise; }); - this.selectTargetButton = view.modelBuilder.button().withProperties({ + this.selectTargetButton = this.view.modelBuilder.button().withProperties({ label: '•••', title: loc.selectTarget, ariaLabel: loc.selectTarget }).component(); - this.selectTargetButton.onDidClick(() => { + this.selectTargetButton.onDidClick(async () => { TelemetryReporter.sendActionEvent(TelemetryViews.SchemaCompareMainWindow, 'SchemaCompareSelectTarget'); - let dialog = new SchemaCompareDialog(this); - dialog.openDialog(); + this.schemaCompareDialog = new SchemaCompareDialog(this); + this.promise = await this.schemaCompareDialog.openDialog(); + await this.promise; }); } - private createOpenScmpButton(view: azdata.ModelView) { - this.openScmpButton = view.modelBuilder.button().withProperties({ + private createOpenScmpButton() { + this.openScmpButton = this.view.modelBuilder.button().withProperties({ label: loc.openScmp, iconPath: { light: path.join(this.extensionContext.extensionPath, 'media', 'open-scmp.svg'), @@ -987,8 +1000,8 @@ export class SchemaCompareMainWindow { return endpointInfo; } - private createSaveScmpButton(view: azdata.ModelView): void { - this.saveScmpButton = view.modelBuilder.button().withProperties({ + private createSaveScmpButton(): void { + this.saveScmpButton = this.view.modelBuilder.button().withProperties({ label: loc.saveScmp, iconPath: { light: path.join(this.extensionContext.extensionPath, 'media', 'save-scmp.svg'), @@ -1068,7 +1081,7 @@ export class SchemaCompareMainWindow { private async getService(): Promise { if (this.schemaCompareService === null || this.schemaCompareService === undefined) { - this.schemaCompareService = (vscode.extensions.getExtension(mssql.extension.name).exports as mssql.IExtension).schemaCompare; + this.schemaCompareService = (await vscode.extensions.getExtension(mssql.extension.name).exports as mssql.IExtension).schemaCompare; } return this.schemaCompareService; } diff --git a/extensions/schema-compare/src/test/schemaCompare.test.ts b/extensions/schema-compare/src/test/schemaCompare.test.ts index f894743588..6dc9c0d2ba 100644 --- a/extensions/schema-compare/src/test/schemaCompare.test.ts +++ b/extensions/schema-compare/src/test/schemaCompare.test.ts @@ -40,7 +40,7 @@ describe('SchemaCompareMainWindow.start @DacFx@', function (): void { it('Should be correct when created.', async function (): Promise { let sc = new SchemaCompareTestService(); - let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object); + let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object, null); await result.start(undefined); should(result.getComparisonResult() === undefined); @@ -56,7 +56,7 @@ describe('SchemaCompareMainWindow.start @DacFx@', function (): void { it('Should start with the source as undefined', async function (): Promise { let sc = new SchemaCompareTestService(); - let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object); + let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object, null); await result.start(undefined); should.equal(result.sourceEndpointInfo, undefined); @@ -66,8 +66,8 @@ describe('SchemaCompareMainWindow.start @DacFx@', function (): void { it('Should start with the source as database', async function (): Promise { let sc = new SchemaCompareTestService(); - let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object); - await result.start({ connectionProfile: mockIConnectionProfile }); + let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object, null); + await result.start({connectionProfile: mockIConnectionProfile}); should.notEqual(result.sourceEndpointInfo, undefined); should.equal(result.sourceEndpointInfo.endpointType, mssql.SchemaCompareEndpointType.Database); @@ -79,7 +79,7 @@ describe('SchemaCompareMainWindow.start @DacFx@', function (): void { it('Should start with the source as dacpac.', async function (): Promise { let sc = new SchemaCompareTestService(); - let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object); + let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object, null); const dacpacPath = mockFilePath; await result.start(dacpacPath); @@ -392,7 +392,7 @@ describe('SchemaCompareMainWindow.execute', function (): void { throw new Error(message); }); - let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object); + let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object, null); await result.start(undefined); should(result.getComparisonResult() === undefined); @@ -406,7 +406,7 @@ describe('SchemaCompareMainWindow.execute', function (): void { it('Should exit for failing Schema Compare service', async function (): Promise { let sc = new SchemaCompareTestService(testStateScmp.FAILURE); - let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object); + let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object, null); await result.start(undefined); @@ -421,7 +421,7 @@ describe('SchemaCompareMainWindow.execute', function (): void { it('Should disable script button and apply button for Schema Compare service for dacpac', async function (): Promise { let sc = new SchemaCompareTestService(testStateScmp.SUCCESS_NOT_EQUAL); - let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object); + let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object, null); await result.start(undefined); @@ -447,10 +447,10 @@ describe('SchemaCompareMainWindow.execute', function (): void { }); }); - it('Should disable script button and apply button for Schema Compare service for database', async function (): Promise { + it('Should enable script button and apply button for Schema Compare service for database', async function (): Promise { let sc = new SchemaCompareTestService(testStateScmp.SUCCESS_NOT_EQUAL); - let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object); + let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object, null); await result.start(undefined); @@ -476,6 +476,35 @@ describe('SchemaCompareMainWindow.execute', function (): void { }); }); + it('Should enable script button and apply button for Schema Compare service for database, using button onClick fire event', async function (): Promise { + let sc = new SchemaCompareTestService(testStateScmp.SUCCESS_NOT_EQUAL); + + let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object, testContext.viewContext.view); + + await result.start(undefined); + + should(result.getComparisonResult() === undefined); + + result.sourceEndpointInfo = setDacpacEndpointInfo(mocksource); + result.targetEndpointInfo = setDatabaseEndpointInfo(); + + testContext.viewContext.compareButtonOnClick.fire(undefined); + await result.promise; + + //Generate script button and apply button should be enabled for database comparison + result.verifyButtonsState({ + compareButtonState: true, + optionsButtonState: true, + switchButtonState: true, + openScmpButtonState: true, + saveScmpButtonState: true, + cancelCompareButtonState: false, + selectSourceButtonState: true, + selectTargetButtonState: true, + generateScriptButtonState: true, + applyButtonState: true} ); + }); + }); describe('SchemaCompareMainWindow.updateSourceAndTarget', function (): void { @@ -489,7 +518,7 @@ describe('SchemaCompareMainWindow.updateSourceAndTarget', function (): void { let sc = new SchemaCompareTestService(); let endpointInfo: mssql.SchemaCompareEndpointInfo; - let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object); + let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object, null); await result.start(undefined); @@ -518,7 +547,7 @@ describe('SchemaCompareMainWindow.updateSourceAndTarget', function (): void { let sc = new SchemaCompareTestService(); let endpointInfo: mssql.SchemaCompareEndpointInfo; - let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object); + let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object, null); await result.start(undefined); @@ -545,9 +574,8 @@ describe('SchemaCompareMainWindow.updateSourceAndTarget', function (): void { it('Should set buttons appropriately when source and target endpoints are populated', async function (): Promise { let sc = new SchemaCompareTestService(); - let endpointInfo: mssql.SchemaCompareEndpointInfo; - let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object); + let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object, null); await result.start(undefined); @@ -573,3 +601,146 @@ describe('SchemaCompareMainWindow.updateSourceAndTarget', function (): void { }); }); + +describe('SchemaCompareMainWindow: Button clicks', function (): void { + before(() => { + mockExtensionContext = TypeMoq.Mock.ofType(); + mockExtensionContext.setup(x => x.extensionPath).returns(() => ''); + testContext = createContext(); + }); + this.beforeEach(() => { + sinon.restore(); + showErrorMessageSpy = sinon.spy(vscode.window, 'showErrorMessage'); + }); + this.afterEach(() => { + sinon.restore(); + }); + + it('createSwitchButton click to start schema comparison', async function (): Promise { + let sc = new SchemaCompareTestService(); + + let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object, testContext.viewContext.view); + + await result.start(undefined); + + should(result.getComparisonResult() === undefined); + + result.sourceEndpointInfo = setDacpacEndpointInfo(mocksource); + result.targetEndpointInfo = setDatabaseEndpointInfo(); + + testContext.viewContext.switchDirectionButtonOnClick.fire(undefined); + await result.promise; + + //Verify that switch actually happened + should.notEqual(result.sourceEndpointInfo, undefined); + should.equal(result.sourceEndpointInfo.endpointType, mssql.SchemaCompareEndpointType.Database); + should.equal(result.sourceEndpointInfo.serverName, 'My Server'); + should.equal(result.sourceEndpointInfo.databaseName, 'My Database'); + should.notEqual(result.targetEndpointInfo, undefined); + should.equal(result.targetEndpointInfo.endpointType, mssql.SchemaCompareEndpointType.Dacpac); + should.equal(result.targetEndpointInfo.packageFilePath, 'source.dacpac'); + + //Generate script button and apply button should be disabled for database to dacpac comparison + result.verifyButtonsState({ + compareButtonState: true, + optionsButtonState: true, + switchButtonState: true, + openScmpButtonState: true, + saveScmpButtonState: true, + cancelCompareButtonState: false, + selectSourceButtonState: true, + selectTargetButtonState: true, + generateScriptButtonState: false, + applyButtonState: false + }); + }); + + it('SourceAndTarget selection should open schemaCompareDialog', async function (): Promise { + let sc = new SchemaCompareTestService(); + + let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object, testContext.viewContext.view); + + await result.start(undefined); + + should(result.getComparisonResult() === undefined); + + result.sourceEndpointInfo = setDacpacEndpointInfo(mocksource); + result.targetEndpointInfo = setDatabaseEndpointInfo(); + + testContext.viewContext.selectButtonOnClick.fire(undefined); + await result.promise; + + //Verify that Select button click opened a Schema Compare dialog + should.notEqual(result.schemaCompareDialog, undefined); + should.notEqual(result.schemaCompareDialog.dialog, undefined); + should(result.schemaCompareDialog.dialog.title).equal(loc.SchemaCompareLabel); + should(result.schemaCompareDialog.dialog.okButton.label).equal(loc.OkButtonText); + should(result.schemaCompareDialog.dialog.okButton.enabled).equal(false); // Should be false when open + }); + + it('cancelCompare click to cancel schema comparison', async function (): Promise { + let sc = new SchemaCompareTestService(); + + let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object, testContext.viewContext.view); + + await result.start(undefined); + + should(result.getComparisonResult() === undefined); + + result.sourceEndpointInfo = setDacpacEndpointInfo(mocksource); + result.targetEndpointInfo = setDatabaseEndpointInfo(); + await result.execute(); + + testContext.viewContext.cancelCompareButtonOnClick.fire(undefined); + + //Verify no error message was thrown + should(showErrorMessageSpy.notCalled).be.true(); + + //Generate script button and apply button should be enabled for database comparison + result.verifyButtonsState({ + compareButtonState: true, + optionsButtonState: true, + switchButtonState: true, + openScmpButtonState: true, + saveScmpButtonState: true, + cancelCompareButtonState: false, + selectSourceButtonState: true, + selectTargetButtonState: true, + generateScriptButtonState: false, + applyButtonState: false + }); + }); + + it('generateScript click to generate script for schema comparison', async function (): Promise { + let sc = new SchemaCompareTestService(); + + let result = new SchemaCompareMainWindowTest(sc, mockExtensionContext.object, testContext.viewContext.view); + + await result.start(undefined); + + should(result.getComparisonResult() === undefined); + + result.sourceEndpointInfo = setDacpacEndpointInfo(mocksource); + result.targetEndpointInfo = setDatabaseEndpointInfo(); + await result.execute(); + + testContext.viewContext.generateScriptButtonOnClick.fire(undefined); + + //Verify no error message was thrown + should(showErrorMessageSpy.notCalled).be.true(); + + result.verifyButtonsState({ + compareButtonState: true, + optionsButtonState: true, + switchButtonState: true, + openScmpButtonState: true, + saveScmpButtonState: true, + cancelCompareButtonState: false, + selectSourceButtonState: true, + selectTargetButtonState: true, + generateScriptButtonState: false, + applyButtonState: false + }); + }); +}); + diff --git a/extensions/schema-compare/src/test/schemaCompareDialog.test.ts b/extensions/schema-compare/src/test/schemaCompareDialog.test.ts index aaeeac0eaa..10ec1b54c9 100644 --- a/extensions/schema-compare/src/test/schemaCompareDialog.test.ts +++ b/extensions/schema-compare/src/test/schemaCompareDialog.test.ts @@ -42,7 +42,7 @@ describe('SchemaCompareDialog.openDialog @DacFx@', function (): void { }); it('Simulate ok button- with both endpoints set to dacpac', async function (): Promise { - let schemaCompareResult = new SchemaCompareMainWindowTest(undefined, mockExtensionContext.object); + let schemaCompareResult = new SchemaCompareMainWindowTest(undefined, mockExtensionContext.object, undefined); await schemaCompareResult.start(undefined); schemaCompareResult.sourceEndpointInfo = setDacpacEndpointInfo(mocksource); schemaCompareResult.targetEndpointInfo = setDacpacEndpointInfo(mocktarget); diff --git a/extensions/schema-compare/src/test/testContext.ts b/extensions/schema-compare/src/test/testContext.ts index b4d0261011..35e939541f 100644 --- a/extensions/schema-compare/src/test/testContext.ts +++ b/extensions/schema-compare/src/test/testContext.ts @@ -3,16 +3,21 @@ * 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 path from 'path'; +import * as loc from './../localizedConstants'; export interface TestContext { context: vscode.ExtensionContext; + viewContext: ViewTestContext; } export function createContext(): TestContext { let extensionPath = path.join(__dirname, '..', '..'); + let viewContext = createViewContext(); + return { context: { subscriptions: [], @@ -36,5 +41,327 @@ export function createContext(): TestContext { logUri: undefined, storageUri: undefined }, + viewContext: viewContext + }; +} + +export interface ViewTestContext { + view: azdata.ModelView; + onClick: vscode.EventEmitter; + onTextChanged: vscode.EventEmitter; + onValueChanged: vscode.EventEmitter; + compareButtonOnClick: vscode.EventEmitter; + selectButtonOnClick: vscode.EventEmitter; + switchDirectionButtonOnClick: vscode.EventEmitter; + cancelCompareButtonOnClick: vscode.EventEmitter; + generateScriptButtonOnClick: vscode.EventEmitter; +} + +export function createViewContext(): ViewTestContext { + let onClick: vscode.EventEmitter = new vscode.EventEmitter(); + let onTextChanged: vscode.EventEmitter = new vscode.EventEmitter(); + let onValueChanged: vscode.EventEmitter = new vscode.EventEmitter(); + let compareButtonOnClick: vscode.EventEmitter = new vscode.EventEmitter(); + let selectButtonOnClick: vscode.EventEmitter = new vscode.EventEmitter(); + let switchDirectionButtonOnClick: vscode.EventEmitter = new vscode.EventEmitter(); + let cancelCompareButtonOnClick: vscode.EventEmitter = new vscode.EventEmitter(); + let generateScriptButtonOnClick: vscode.EventEmitter = new vscode.EventEmitter(); + + let componentBase: azdata.Component = { + id: '', + updateProperties: () => Promise.resolve(), + updateProperty: () => Promise.resolve(), + updateCssStyles: undefined!, + onValidityChanged: undefined!, + valid: true, + validate: undefined!, + focus: () => Promise.resolve() + }; + + let container = { + clearItems: () => { }, + addItems: () => { }, + addItem: () => { }, + removeItem: () => true, + insertItem: () => { }, + items: [] as any[], + setLayout: () => { }, + setItemLayout: () => { } + }; + + let button = () => { + let button: azdata.ButtonComponent = Object.assign({}, componentBase, { + name: '', + label: '', + onDidClick: onClick.event + }); + return button; + }; + let buttonBuilder = () => { + let b = button(); + let builder: azdata.ComponentBuilder = { + component: () => b, + withProperties: (properties: any) => { + if ((properties as any).label === loc.compare) { + b.label = loc.compare; + b.onDidClick = compareButtonOnClick.event; + } else if ((properties as any).label === '•••') { + b.label = '•••'; + b.onDidClick = selectButtonOnClick.event; + } else if ((properties as any).label === loc.switchDirection) { + b.label = loc.switchDirection; + b.onDidClick = switchDirectionButtonOnClick.event; + } else if ((properties as any).label === loc.stop) { + b.label = loc.stop; + b.onDidClick = cancelCompareButtonOnClick.event; + } else if ((properties as any).label === loc.generateScript) { + b.label = loc.generateScript; + b.onDidClick = generateScriptButtonOnClick.event; + } + return builder; + }, + withProps: (properties) => { + if ((properties as any).label === loc.compare) { + b.label = loc.compare; + b.onDidClick = compareButtonOnClick.event; + } else if ((properties as any).label === '•••') { + b.label = '•••'; + b.onDidClick = selectButtonOnClick.event; + } else if ((properties as any).label === loc.switchDirection) { + b.label = loc.switchDirection; + b.onDidClick = switchDirectionButtonOnClick.event; + } else if ((properties as any).label === loc.stop) { + b.label = loc.stop; + b.onDidClick = cancelCompareButtonOnClick.event; + } else if ((properties as any).label === loc.generateScript) { + b.label = loc.generateScript; + b.onDidClick = generateScriptButtonOnClick.event; + } + return builder; + }, + withValidation: () => builder + }; + return builder; + }; + + let radioButton: azdata.RadioButtonComponent = Object.assign({}, componentBase, { + checked: false, + onDidClick: onClick.event, + }); + let radioButtonBuilder: azdata.ComponentBuilder = { + component: () => radioButton, + withProperties: () => radioButtonBuilder, + withProps: () => radioButtonBuilder, + withValidation: () => radioButtonBuilder + }; + + let checkbox: azdata.CheckBoxComponent = Object.assign({}, componentBase, { + checked: true, + onChanged: onClick.event + }); + let checkBoxBuilder: azdata.ComponentBuilder = { + component: () => checkbox, + withProperties: () => checkBoxBuilder, + withProps: () => checkBoxBuilder, + withValidation: () => checkBoxBuilder + }; + + let form: azdata.FormContainer = Object.assign({}, componentBase, container, { + }); + let formBuilder: azdata.FormBuilder = Object.assign({}, { + component: () => form, + addFormItem: () => { }, + insertFormItem: () => { }, + removeFormItem: () => true, + addFormItems: () => { }, + withFormItems: () => formBuilder, + withProperties: () => formBuilder, + withValidation: () => formBuilder, + withItems: () => formBuilder, + withLayout: () => formBuilder, + withProps: () => formBuilder + }); + + let toolbar: azdata.ToolbarContainer = Object.assign({}, componentBase, container, { + }); + let toolbarBuilder: azdata.ToolbarBuilder = Object.assign({}, { + component: () => toolbar, + withToolbarItems: () => toolbarBuilder, + addToolbarItems: () => { }, + addToolbarItem: () => { }, + withProperties: () => toolbarBuilder, + withValidation: () => toolbarBuilder, + withItems: () => toolbarBuilder, + withLayout: () => toolbarBuilder, + withProps: () => toolbarBuilder + }); + + let flex: azdata.FlexContainer = Object.assign({}, componentBase, container, { + }); + let flexBuilder: azdata.FlexBuilder = Object.assign({}, { + component: () => flex, + withProperties: () => flexBuilder, + withValidation: () => flexBuilder, + withItems: () => flexBuilder, + withLayout: () => flexBuilder, + withProps: () => flexBuilder + }); + + let div: azdata.DivContainer = Object.assign({}, componentBase, container, { + onDidClick: onClick.event + }); + let divBuilder: azdata.DivBuilder = Object.assign({}, { + component: () => div, + withProperties: () => divBuilder, + withValidation: () => divBuilder, + withItems: () => divBuilder, + withLayout: () => divBuilder, + withProps: () => divBuilder + }); + + let splitView: azdata.SplitViewContainer = Object.assign({}, componentBase, container, { + onDidClick: onClick.event + }); + let splitViewBuilder: azdata.SplitViewBuilder = Object.assign({}, { + component: () => splitView, + withProperties: () => splitViewBuilder, + withValidation: () => splitViewBuilder, + withItems: () => splitViewBuilder, + withLayout: () => splitViewBuilder, + withProps: () => splitViewBuilder + }); + + let diffEditor: () => azdata.DiffEditorComponent = () => Object.assign({}, componentBase, { + contentLeft: '', + contentRight: '', + languageMode: '', + editorUriLeft: '', + editorUriRight: '', + onContentChanged: onClick.event, + onEditorCreated: onClick.event, + isAutoResizable: false, + minimumHeight: 0, + title: '' + }); + let diffEditorBuilder: azdata.ComponentBuilder = { + component: () => diffEditor(), + withProperties: () => diffEditorBuilder, + withValidation: () => diffEditorBuilder, + withProps: () => diffEditorBuilder + }; + + let inputBox: () => azdata.InputBoxComponent = () => Object.assign({}, componentBase, { + onTextChanged: onTextChanged.event, + onEnterKeyPressed: onClick.event, + value: '' + }); + let inputBoxBuilder: azdata.ComponentBuilder = { + component: () => { + let r = inputBox(); + return r; + }, + withProperties: () => inputBoxBuilder, + withProps: () => inputBoxBuilder, + withValidation: () => inputBoxBuilder + }; + + let dropdown: () => azdata.DropDownComponent = () => Object.assign({}, componentBase, { + onValueChanged: onValueChanged.event, + value: { + name: '', + displayName: '' + }, + values: [] + }); + let dropdownBuilder: azdata.ComponentBuilder = { + component: () => { + let r = dropdown(); + return r; + }, + withProperties: () => dropdownBuilder, + withProps: () => dropdownBuilder, + withValidation: () => dropdownBuilder + }; + + let table: () => azdata.TableComponent = () => Object.assign({}, componentBase, { + data: [] as any[][], + columns: [] as string[], + onRowSelected: onClick.event, + onCellAction: onClick.event, + appendData: (data: any[][]) => undefined + }); + let tableBuilder: azdata.ComponentBuilder = { + component: () => table(), + withProperties: () => tableBuilder, + withProps: () => tableBuilder, + withValidation: () => tableBuilder + }; + + let loadingComponent: () => azdata.LoadingComponent = () => Object.assign({}, componentBase, { + loading: false, + component: undefined! + }); + let loadingBuilder: azdata.LoadingComponentBuilder = { + component: () => loadingComponent(), + withProperties: () => loadingBuilder, + withProps: () => loadingBuilder, + withValidation: () => loadingBuilder, + withItem: () => loadingBuilder + }; + + let view: azdata.ModelView = { + onClosed: undefined!, + connection: undefined!, + serverInfo: undefined!, + valid: true, + onValidityChanged: undefined!, + validate: undefined!, + initializeModel: () => { return Promise.resolve(); }, + modelBuilder: { + listView: undefined!, + radioCardGroup: undefined!, + navContainer: undefined!, + divContainer: () => divBuilder, + flexContainer: () => flexBuilder, + splitViewContainer: () => splitViewBuilder, + dom: undefined!, + card: () => undefined!, + inputBox: () => inputBoxBuilder, + checkBox: () => checkBoxBuilder!, + radioButton: () => radioButtonBuilder, + webView: undefined!, + editor: undefined!, + diffeditor: () => diffEditorBuilder, + text: () => inputBoxBuilder, + image: () => undefined!, + button: () => buttonBuilder(), + dropDown: () => dropdownBuilder, + tree: undefined!, + listBox: undefined!, + table: () => tableBuilder, + declarativeTable: () => undefined!, + dashboardWidget: undefined!, + dashboardWebview: undefined!, + formContainer: () => formBuilder, + groupContainer: () => undefined!, + toolbarContainer: () => toolbarBuilder, + loadingComponent: () => loadingBuilder, + fileBrowserTree: undefined!, + hyperlink: () => undefined!, + tabbedPanel: undefined!, + separator: undefined!, + propertiesContainer: undefined! + } + }; + return { + view: view, + onClick: onClick, + onTextChanged: onTextChanged, + onValueChanged: onValueChanged, + compareButtonOnClick: compareButtonOnClick, + selectButtonOnClick: selectButtonOnClick, + switchDirectionButtonOnClick: switchDirectionButtonOnClick, + cancelCompareButtonOnClick: cancelCompareButtonOnClick, + generateScriptButtonOnClick: generateScriptButtonOnClick, }; } diff --git a/extensions/schema-compare/src/test/testSchemaCompareMainWindow.ts b/extensions/schema-compare/src/test/testSchemaCompareMainWindow.ts index 5488f3a217..36c0482eef 100644 --- a/extensions/schema-compare/src/test/testSchemaCompareMainWindow.ts +++ b/extensions/schema-compare/src/test/testSchemaCompareMainWindow.ts @@ -3,6 +3,7 @@ * 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 mssql from '../../../mssql'; import * as should from 'should'; @@ -24,8 +25,9 @@ export class SchemaCompareMainWindowTest extends SchemaCompareMainWindow { constructor( schemaCompareService: mssql.ISchemaCompareService, - extensionContext: vscode.ExtensionContext) { - super(schemaCompareService, extensionContext); + extensionContext: vscode.ExtensionContext, + view: azdata.ModelView) { + super(schemaCompareService, extensionContext, view); } // only for test diff --git a/extensions/schema-compare/src/test/testSchemaCompareService.ts b/extensions/schema-compare/src/test/testSchemaCompareService.ts index 5da1da9a3e..7645cb954d 100644 --- a/extensions/schema-compare/src/test/testSchemaCompareService.ts +++ b/extensions/schema-compare/src/test/testSchemaCompareService.ts @@ -103,11 +103,39 @@ export class SchemaCompareTestService implements mssql.ISchemaCompareService { } schemaCompareGenerateScript(operationId: string, targetServerName: string, targetDatabaseName: string, taskExecutionMode: azdata.TaskExecutionMode): Thenable { - return undefined; + let result: azdata.ResultStatus; + if (this.testState === testStateScmp.FAILURE) { + result = { + success: false, + errorMessage: 'Test failure' + }; + } + else { + result = { + success: true, + errorMessage: '' + }; + } + + return Promise.resolve(result); } schemaCompareCancel(operationId: string): Thenable { - return undefined; + let result: azdata.ResultStatus; + if (this.testState === testStateScmp.FAILURE) { + result = { + success: false, + errorMessage: 'Test failure' + }; + } + else { + result = { + success: true, + errorMessage: '' + }; + } + + return Promise.resolve(result); } handle?: number;