mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Use azdata-test modelview stubs (#13818)
This commit is contained in:
@@ -1072,6 +1072,7 @@
|
|||||||
"@types/sinon": "^9.0.4",
|
"@types/sinon": "^9.0.4",
|
||||||
"@types/uuid": "^8.3.0",
|
"@types/uuid": "^8.3.0",
|
||||||
"@types/yamljs": "^0.2.31",
|
"@types/yamljs": "^0.2.31",
|
||||||
|
"azdata-test": "^1.0.0",
|
||||||
"mocha": "^5.2.0",
|
"mocha": "^5.2.0",
|
||||||
"mocha-junit-reporter": "^1.17.0",
|
"mocha-junit-reporter": "^1.17.0",
|
||||||
"mocha-multi-reporters": "^1.1.7",
|
"mocha-multi-reporters": "^1.1.7",
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import * as azdata from 'azdata';
|
|
||||||
import * as vscode from 'vscode';
|
|
||||||
|
|
||||||
export class FakeRadioButton implements azdata.RadioButtonComponent {
|
|
||||||
|
|
||||||
private _onDidClickEmitter = new vscode.EventEmitter<any>();
|
|
||||||
|
|
||||||
onDidClick = this._onDidClickEmitter.event;
|
|
||||||
|
|
||||||
constructor(props: azdata.RadioButtonProperties) {
|
|
||||||
this.label = props.label;
|
|
||||||
this.value = props.value;
|
|
||||||
this.checked = props.checked;
|
|
||||||
this.enabled = props.enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
//#region RadioButtonProperties implementation
|
|
||||||
label?: string;
|
|
||||||
value?: string;
|
|
||||||
checked?: boolean;
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
click() {
|
|
||||||
this.checked = true;
|
|
||||||
this._onDidClickEmitter.fire(this);
|
|
||||||
}
|
|
||||||
//#region Component Implementation
|
|
||||||
id: string = '';
|
|
||||||
updateProperties(_properties: { [key: string]: any; }): Thenable<void> {
|
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
}
|
|
||||||
updateProperty(_key: string, _value: any): Thenable<void> {
|
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
}
|
|
||||||
updateCssStyles(_cssStyles: { [key: string]: string; }): Thenable<void> {
|
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
}
|
|
||||||
onValidityChanged: vscode.Event<boolean> = <vscode.Event<boolean>>{};
|
|
||||||
valid: boolean = false;
|
|
||||||
validate(): Thenable<boolean> {
|
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
}
|
|
||||||
focus(): Thenable<void> {
|
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
}
|
|
||||||
ariaHidden?: boolean | undefined;
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
//#region ComponentProperties Implementation
|
|
||||||
height?: number | string;
|
|
||||||
width?: number | string;
|
|
||||||
/**
|
|
||||||
* The position CSS property. Empty by default.
|
|
||||||
* This is particularly useful if laying out components inside a FlexContainer and
|
|
||||||
* the size of the component is meant to be a fixed size. In this case the position must be
|
|
||||||
* set to 'absolute', with the parent FlexContainer having 'relative' position.
|
|
||||||
* Without this the component will fail to correctly size itself
|
|
||||||
*/
|
|
||||||
position?: azdata.PositionType;
|
|
||||||
/**
|
|
||||||
* Whether the component is enabled in the DOM
|
|
||||||
*/
|
|
||||||
enabled?: boolean;
|
|
||||||
/**
|
|
||||||
* Corresponds to the display CSS property for the element
|
|
||||||
*/
|
|
||||||
display?: azdata.DisplayType;
|
|
||||||
/**
|
|
||||||
* Corresponds to the aria-label accessibility attribute for this component
|
|
||||||
*/
|
|
||||||
ariaLabel?: string;
|
|
||||||
/**
|
|
||||||
* Corresponds to the role accessibility attribute for this component
|
|
||||||
*/
|
|
||||||
ariaRole?: string;
|
|
||||||
/**
|
|
||||||
* Corresponds to the aria-selected accessibility attribute for this component
|
|
||||||
*/
|
|
||||||
ariaSelected?: boolean;
|
|
||||||
/**
|
|
||||||
* Matches the CSS style key and its available values.
|
|
||||||
*/
|
|
||||||
CSSStyles?: { [key: string]: string };
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -3,107 +3,8 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as azdata from 'azdata';
|
|
||||||
import * as TypeMoq from 'typemoq';
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
|
|
||||||
interface ModelViewMocks {
|
|
||||||
mockModelView: TypeMoq.IMock<azdata.ModelView>,
|
|
||||||
mockModelBuilder: TypeMoq.IMock<azdata.ModelBuilder>,
|
|
||||||
mockTextBuilder: TypeMoq.IMock<azdata.ComponentBuilder<azdata.TextComponent, azdata.TextComponentProperties>>,
|
|
||||||
mockInputBoxBuilder: TypeMoq.IMock<azdata.ComponentBuilder<azdata.InputBoxComponent, azdata.InputBoxProperties>>,
|
|
||||||
mockButtonBuilder: TypeMoq.IMock<azdata.ComponentBuilder<azdata.ButtonComponent, azdata.ButtonProperties>>,
|
|
||||||
mockRadioButtonBuilder: TypeMoq.IMock<azdata.ComponentBuilder<azdata.RadioButtonComponent, azdata.RadioButtonProperties>>,
|
|
||||||
mockDivBuilder: TypeMoq.IMock<azdata.DivBuilder>,
|
|
||||||
mockFlexBuilder: TypeMoq.IMock<azdata.FlexBuilder>,
|
|
||||||
mockLoadingBuilder: TypeMoq.IMock<azdata.LoadingComponentBuilder>
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createModelViewMock(buttonClickEmitter?: vscode.EventEmitter<any>): ModelViewMocks {
|
|
||||||
const mockModelBuilder = TypeMoq.Mock.ofType<azdata.ModelBuilder>();
|
|
||||||
const mockTextBuilder = setupMockComponentBuilder<azdata.TextComponent, azdata.TextComponentProperties>();
|
|
||||||
const mockInputBoxBuilder = setupMockComponentBuilder<azdata.InputBoxComponent, azdata.InputBoxProperties>();
|
|
||||||
buttonClickEmitter = buttonClickEmitter ?? new vscode.EventEmitter<any>();
|
|
||||||
const mockButtonBuilder = setupMockButtonBuilderWithClickEmitter(buttonClickEmitter);
|
|
||||||
const mockRadioButtonBuilder = setupMockComponentBuilder<azdata.RadioButtonComponent, azdata.RadioButtonProperties>();
|
|
||||||
const mockDivBuilder = setupMockContainerBuilder<azdata.DivContainer, azdata.DivContainerProperties, azdata.DivBuilder>();
|
|
||||||
const mockFlexBuilder = setupMockContainerBuilder<azdata.FlexContainer, azdata.ComponentProperties, azdata.FlexBuilder>();
|
|
||||||
const mockLoadingBuilder = setupMockLoadingBuilder();
|
|
||||||
mockModelBuilder.setup(b => b.loadingComponent()).returns(() => mockLoadingBuilder.object);
|
|
||||||
mockModelBuilder.setup(b => b.text()).returns(() => mockTextBuilder.object);
|
|
||||||
mockModelBuilder.setup(b => b.inputBox()).returns(() => mockInputBoxBuilder.object);
|
|
||||||
mockModelBuilder.setup(b => b.button()).returns(() => mockButtonBuilder.object);
|
|
||||||
mockModelBuilder.setup(b => b.radioButton()).returns(() => mockRadioButtonBuilder.object);
|
|
||||||
mockModelBuilder.setup(b => b.divContainer()).returns(() => mockDivBuilder.object);
|
|
||||||
mockModelBuilder.setup(b => b.flexContainer()).returns(() => mockFlexBuilder.object);
|
|
||||||
const mockModelView = TypeMoq.Mock.ofType<azdata.ModelView>();
|
|
||||||
mockModelView.setup(mv => mv.modelBuilder).returns(() => mockModelBuilder.object);
|
|
||||||
return { mockModelView, mockModelBuilder, mockTextBuilder, mockInputBoxBuilder, mockButtonBuilder, mockRadioButtonBuilder, mockDivBuilder, mockFlexBuilder, mockLoadingBuilder };
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupMockButtonBuilderWithClickEmitter(buttonClickEmitter: vscode.EventEmitter<any>): TypeMoq.IMock<azdata.ComponentBuilder<azdata.ButtonComponent, azdata.ButtonProperties>> {
|
|
||||||
const { mockComponentBuilder: mockButtonBuilder, mockComponent: mockButtonComponent } = setupMockComponentBuilderAndComponent<azdata.ButtonComponent, azdata.ButtonProperties>();
|
|
||||||
mockButtonComponent.setup(b => b.onDidClick(TypeMoq.It.isAny())).returns(buttonClickEmitter.event);
|
|
||||||
return mockButtonBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupMockLoadingBuilder(
|
|
||||||
loadingBuilderGetter?: (item: azdata.Component) => azdata.LoadingComponentBuilder,
|
|
||||||
mockLoadingBuilder?: TypeMoq.IMock<azdata.LoadingComponentBuilder>
|
|
||||||
): TypeMoq.IMock<azdata.LoadingComponentBuilder> {
|
|
||||||
mockLoadingBuilder = mockLoadingBuilder ?? setupMockComponentBuilder<azdata.LoadingComponent, azdata.LoadingComponentProperties, azdata.LoadingComponentBuilder>();
|
|
||||||
let item: azdata.Component;
|
|
||||||
mockLoadingBuilder.setup(b => b.withItem(TypeMoq.It.isAny())).callback((_item) => item = _item).returns(() => loadingBuilderGetter ? loadingBuilderGetter(item) : mockLoadingBuilder!.object);
|
|
||||||
return mockLoadingBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setupMockComponentBuilder<T extends azdata.Component, P extends azdata.ComponentProperties, B extends azdata.ComponentBuilder<T, P> = azdata.ComponentBuilder<T, P>>(
|
|
||||||
componentGetter?: (props: P) => T,
|
|
||||||
mockComponentBuilder?: TypeMoq.IMock<B>,
|
|
||||||
): TypeMoq.IMock<B> {
|
|
||||||
mockComponentBuilder = mockComponentBuilder ?? TypeMoq.Mock.ofType<B>();
|
|
||||||
setupMockComponentBuilderAndComponent<T, P, B>(mockComponentBuilder, componentGetter);
|
|
||||||
return mockComponentBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupMockComponentBuilderAndComponent<T extends azdata.Component, P extends azdata.ComponentProperties, B extends azdata.ComponentBuilder<T, P> = azdata.ComponentBuilder<T, P>>(
|
|
||||||
mockComponentBuilder?: TypeMoq.IMock<B>,
|
|
||||||
componentGetter?: ((props: P) => T)
|
|
||||||
): { mockComponentBuilder: TypeMoq.IMock<B>, mockComponent: TypeMoq.IMock<T> } {
|
|
||||||
mockComponentBuilder = mockComponentBuilder ?? TypeMoq.Mock.ofType<B>();
|
|
||||||
const mockComponent = createComponentMock<T>();
|
|
||||||
let compProps: P;
|
|
||||||
mockComponentBuilder.setup(b => b.withProperties(TypeMoq.It.isAny())).callback((props: P) => compProps = props).returns(() => mockComponentBuilder!.object);
|
|
||||||
mockComponentBuilder.setup(b => b.component()).returns(() => {
|
|
||||||
return componentGetter ? componentGetter(compProps) : Object.assign<T, P>(Object.assign({}, mockComponent.object), compProps);
|
|
||||||
});
|
|
||||||
|
|
||||||
// For now just have these be passthrough - can hook up additional functionality later if needed
|
|
||||||
mockComponentBuilder.setup(b => b.withValidation(TypeMoq.It.isAny())).returns(() => mockComponentBuilder!.object);
|
|
||||||
return { mockComponentBuilder, mockComponent };
|
|
||||||
}
|
|
||||||
|
|
||||||
function createComponentMock<T extends azdata.Component>(): TypeMoq.IMock<T> {
|
|
||||||
const mockComponent = TypeMoq.Mock.ofType<T>();
|
|
||||||
// Need to setup 'then' for when a mocked object is resolved otherwise the test will hang : https://github.com/florinn/typemoq/issues/66
|
|
||||||
mockComponent.setup((x: any) => x.then).returns(() => { });
|
|
||||||
return mockComponent;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setupMockContainerBuilder<T extends azdata.Container<any, any>, P extends azdata.ComponentProperties, B extends azdata.ContainerBuilder<T, any, any, any> = azdata.ContainerBuilder<T, any, any, any>>(
|
|
||||||
mockContainerBuilder?: TypeMoq.IMock<B>
|
|
||||||
): TypeMoq.IMock<B> {
|
|
||||||
const items: azdata.Component[] = [];
|
|
||||||
const mockContainer = createComponentMock<T>(); // T is azdata.Container type so this creates a azdata.Container mock
|
|
||||||
mockContainer.setup(c => c.items).returns(() => items);
|
|
||||||
mockContainerBuilder = mockContainerBuilder ?? setupMockComponentBuilder<T, P, B>((_props) => mockContainer.object);
|
|
||||||
|
|
||||||
mockContainerBuilder.setup(b => b.withItems(TypeMoq.It.isAny(), TypeMoq.It.isAny())).callback((_items, _itemsStyle) => items.push(..._items)).returns(() => mockContainerBuilder!.object);
|
|
||||||
// For now just have these be passthrough - can hook up additional functionality later if needed
|
|
||||||
mockContainerBuilder.setup(b => b.withLayout(TypeMoq.It.isAny())).returns(() => mockContainerBuilder!.object);
|
|
||||||
return mockContainerBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MockInputBox implements vscode.InputBox {
|
export class MockInputBox implements vscode.InputBox {
|
||||||
private _value: string = '';
|
private _value: string = '';
|
||||||
public get value(): string {
|
public get value(): string {
|
||||||
@@ -118,17 +19,17 @@ export class MockInputBox implements vscode.InputBox {
|
|||||||
placeholder: string | undefined;
|
placeholder: string | undefined;
|
||||||
password: boolean = false;
|
password: boolean = false;
|
||||||
private _onDidChangeValueCallback: ((e: string) => any) | undefined = undefined;
|
private _onDidChangeValueCallback: ((e: string) => any) | undefined = undefined;
|
||||||
onDidChangeValue: vscode.Event<string> = (listener) => {
|
onDidChangeValue: vscode.Event<string> = (listener: (value: string) => void) => {
|
||||||
this._onDidChangeValueCallback = listener;
|
this._onDidChangeValueCallback = listener;
|
||||||
return new vscode.Disposable(() => { });
|
return new vscode.Disposable(() => { });
|
||||||
};
|
};
|
||||||
private _onDidAcceptCallback: ((e: void) => any) | undefined = undefined;
|
private _onDidAcceptCallback: ((e: void) => any) | undefined = undefined;
|
||||||
public onDidAccept: vscode.Event<void> = (listener) => {
|
public onDidAccept: vscode.Event<void> = (listener: () => void) => {
|
||||||
this._onDidAcceptCallback = listener;
|
this._onDidAcceptCallback = listener;
|
||||||
return new vscode.Disposable(() => { });
|
return new vscode.Disposable(() => { });
|
||||||
};
|
};
|
||||||
buttons: readonly vscode.QuickInputButton[] = [];
|
buttons: readonly vscode.QuickInputButton[] = [];
|
||||||
onDidTriggerButton: vscode.Event<vscode.QuickInputButton> = (_) => { return new vscode.Disposable(() => { }); };
|
onDidTriggerButton: vscode.Event<vscode.QuickInputButton> = () => { return new vscode.Disposable(() => { }); };
|
||||||
prompt: string | undefined;
|
prompt: string | undefined;
|
||||||
validationMessage: string | undefined;
|
validationMessage: string | undefined;
|
||||||
title: string | undefined;
|
title: string | undefined;
|
||||||
@@ -145,7 +46,7 @@ export class MockInputBox implements vscode.InputBox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
private _onDidHideCallback: ((e: void) => any) | undefined = undefined;
|
private _onDidHideCallback: ((e: void) => any) | undefined = undefined;
|
||||||
onDidHide: vscode.Event<void> = (listener) => {
|
onDidHide: vscode.Event<void> = (listener: () => void) => {
|
||||||
this._onDidHideCallback = listener;
|
this._onDidHideCallback = listener;
|
||||||
return new vscode.Disposable(() => { });
|
return new vscode.Disposable(() => { });
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,29 +3,22 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as azdata from 'azdata';
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as should from 'should';
|
import * as should from 'should';
|
||||||
import * as sinon from 'sinon';
|
import * as sinon from 'sinon';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { Deferred } from '../../../common/promise';
|
import { Deferred } from '../../../common/promise';
|
||||||
import { FilePicker } from '../../../ui/components/filePicker';
|
import { FilePicker } from '../../../ui/components/filePicker';
|
||||||
import { createModelViewMock } from '../../stubs';
|
import { createModelViewMock } from 'azdata-test/out/mocks/modelView/modelViewMock';
|
||||||
|
import { StubButton } from 'azdata-test/out/stubs/modelView/stubButton';
|
||||||
|
|
||||||
let filePicker: FilePicker;
|
let filePicker: FilePicker;
|
||||||
const initialPath = path.join('path', 'to', '.kube','config');
|
const initialPath = path.join('path', 'to', '.kube','config');
|
||||||
const newFileUri = vscode.Uri.file(path.join('path', 'to', 'new', '.kube', 'config'));
|
const newFileUri = vscode.Uri.file(path.join('path', 'to', 'new', '.kube', 'config'));
|
||||||
let filePathInputBox: azdata.InputBoxComponent;
|
|
||||||
let browseButton: azdata.ButtonComponent;
|
|
||||||
let flexContainer: azdata.FlexContainer;
|
|
||||||
const browseButtonEmitter = new vscode.EventEmitter<undefined>();
|
|
||||||
describe('filePicker', function (): void {
|
describe('filePicker', function (): void {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const { mockModelBuilder, mockInputBoxBuilder, mockButtonBuilder, mockFlexBuilder } = createModelViewMock(browseButtonEmitter);
|
const { modelBuilderMock } = createModelViewMock();
|
||||||
filePicker = new FilePicker(mockModelBuilder.object, initialPath, (_disposable) => { });
|
filePicker = new FilePicker(modelBuilderMock.object, initialPath, (_disposable) => { });
|
||||||
filePathInputBox = mockInputBoxBuilder.object.component();
|
|
||||||
browseButton = mockButtonBuilder.object.component();
|
|
||||||
flexContainer = mockFlexBuilder.object.component();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -33,22 +26,22 @@ describe('filePicker', function (): void {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('browse Button chooses new FilePath', async () => {
|
it('browse Button chooses new FilePath', async () => {
|
||||||
should(filePathInputBox.value).should.not.be.undefined();
|
should(filePicker.filePathInputBox.value).should.not.be.undefined();
|
||||||
filePicker.value!.should.equal(initialPath);
|
filePicker.value!.should.equal(initialPath);
|
||||||
flexContainer.items.should.deepEqual([filePathInputBox, browseButton]);
|
filePicker.component().items.length.should.equal(2, 'Filepicker container should have two components');
|
||||||
const deferred = new Deferred();
|
const deferred = new Deferred();
|
||||||
sinon.stub(vscode.window, 'showOpenDialog').callsFake(async (_options) => {
|
sinon.stub(vscode.window, 'showOpenDialog').callsFake(async (_options) => {
|
||||||
deferred.resolve();
|
deferred.resolve();
|
||||||
return [newFileUri];
|
return [newFileUri];
|
||||||
});
|
});
|
||||||
browseButtonEmitter.fire(undefined); //simulate the click of the browseButton
|
(filePicker.filePickerButton as StubButton).click();
|
||||||
await deferred;
|
await deferred;
|
||||||
filePicker.value!.should.equal(newFileUri.fsPath);
|
filePicker.value!.should.equal(newFileUri.fsPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getters and setters', async () => {
|
describe('getters and setters', async () => {
|
||||||
it('component getter', () => {
|
it('component getter', () => {
|
||||||
should(filePicker.component()).equal(flexContainer);
|
should(filePicker.component()).not.be.undefined();
|
||||||
});
|
});
|
||||||
[true, false].forEach(testValue => {
|
[true, false].forEach(testValue => {
|
||||||
it(`Test readOnly with testValue: ${testValue}`, () => {
|
it(`Test readOnly with testValue: ${testValue}`, () => {
|
||||||
|
|||||||
@@ -7,43 +7,26 @@ import * as azdata from 'azdata';
|
|||||||
import * as should from 'should';
|
import * as should from 'should';
|
||||||
import { getErrorMessage } from '../../../common/utils';
|
import { getErrorMessage } from '../../../common/utils';
|
||||||
import { RadioOptionsGroup, RadioOptionsInfo } from '../../../ui/components/radioOptionsGroup';
|
import { RadioOptionsGroup, RadioOptionsInfo } from '../../../ui/components/radioOptionsGroup';
|
||||||
import { FakeRadioButton } from '../../mocks/fakeRadioButton';
|
import { createModelViewMock } from 'azdata-test/out/mocks/modelView/modelViewMock';
|
||||||
import { setupMockComponentBuilder, createModelViewMock } from '../../stubs';
|
import { StubRadioButton } from 'azdata-test/out/stubs/modelView/stubRadioButton';
|
||||||
|
|
||||||
|
|
||||||
const loadingError = new Error('Error loading options');
|
const loadingError = new Error('Error loading options');
|
||||||
const radioOptionsInfo = <RadioOptionsInfo>{
|
const radioOptionsInfo: RadioOptionsInfo = {
|
||||||
values: [
|
values: [
|
||||||
'value1',
|
'value1',
|
||||||
'value2'
|
'value2'
|
||||||
],
|
],
|
||||||
defaultValue: 'value2'
|
defaultValue: 'value2'
|
||||||
};
|
};
|
||||||
const divItems: azdata.Component[] = [];
|
|
||||||
let radioOptionsGroup: RadioOptionsGroup;
|
let radioOptionsGroup: RadioOptionsGroup;
|
||||||
let loadingComponent: azdata.LoadingComponent;
|
|
||||||
|
|
||||||
describe('radioOptionsGroup', function (): void {
|
describe('radioOptionsGroup', function (): void {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const { mockModelBuilder, mockRadioButtonBuilder, mockDivBuilder, mockLoadingBuilder } = createModelViewMock();
|
const { modelBuilderMock } = createModelViewMock();
|
||||||
mockRadioButtonBuilder.reset(); // reset any previous mock so that we can set our own.
|
radioOptionsGroup = new RadioOptionsGroup(modelBuilderMock.object, (_disposable) => { });
|
||||||
setupMockComponentBuilder<azdata.RadioButtonComponent, azdata.RadioButtonProperties>(
|
|
||||||
(props) => new FakeRadioButton(props),
|
|
||||||
mockRadioButtonBuilder,
|
|
||||||
);
|
|
||||||
mockDivBuilder.reset(); // reset previous setups so new setups we are about to create will replace the setups instead creating a recording chain
|
|
||||||
// create new setups for the DivContainer with custom behavior
|
|
||||||
setupMockComponentBuilder<azdata.DivContainer, azdata.DivContainerProperties, azdata.DivBuilder>(
|
|
||||||
() => <azdata.DivContainer>{
|
|
||||||
addItem: (item) => { divItems.push(item); },
|
|
||||||
clearItems: () => { divItems.length = 0; },
|
|
||||||
get items() { return divItems; },
|
|
||||||
},
|
|
||||||
mockDivBuilder
|
|
||||||
);
|
|
||||||
radioOptionsGroup = new RadioOptionsGroup(mockModelBuilder.object, (_disposable) => { });
|
|
||||||
await radioOptionsGroup.load(async () => radioOptionsInfo);
|
await radioOptionsGroup.load(async () => radioOptionsInfo);
|
||||||
loadingComponent = mockLoadingBuilder.object.component();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('verify construction and load', async () => {
|
it('verify construction and load', async () => {
|
||||||
@@ -56,17 +39,17 @@ describe('radioOptionsGroup', function (): void {
|
|||||||
|
|
||||||
it('onClick', async () => {
|
it('onClick', async () => {
|
||||||
// click the radioButton corresponding to 'value1'
|
// click the radioButton corresponding to 'value1'
|
||||||
(divItems as FakeRadioButton[]).filter(r => r.value === 'value1').pop()!.click();
|
((radioOptionsGroup.items as azdata.RadioButtonComponent[]).find(r => r.value === 'value1') as StubRadioButton).click();
|
||||||
radioOptionsGroup.value!.should.equal('value1', 'radio options group should correspond to the radioButton that we clicked');
|
radioOptionsGroup.value!.should.equal('value1', 'radio options group should correspond to the radioButton that we clicked');
|
||||||
// verify all the radioButtons created in the group
|
// verify all the radioButtons created in the group
|
||||||
verifyRadioGroup();
|
verifyRadioGroup();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('load throws', async () => {
|
it('load throws', async () => {
|
||||||
radioOptionsGroup.load(() => { throw loadingError; });
|
await radioOptionsGroup.load(() => { throw loadingError; });
|
||||||
//in error case radioButtons array wont hold radioButtons but holds a TextComponent with value equal to error string
|
//in error case radioButtons array wont hold radioButtons but holds a TextComponent with value equal to error string
|
||||||
divItems.length.should.equal(1, 'There is should be only one element in the divContainer when loading error happens');
|
radioOptionsGroup.items.length.should.equal(1, 'There is should be only one element in the divContainer when loading error happens');
|
||||||
const label = divItems[0] as azdata.TextComponent;
|
const label = radioOptionsGroup.items[0] as azdata.TextComponent;
|
||||||
should(label.value).not.be.undefined();
|
should(label.value).not.be.undefined();
|
||||||
label.value!.should.deepEqual(getErrorMessage(loadingError));
|
label.value!.should.deepEqual(getErrorMessage(loadingError));
|
||||||
should(label.CSSStyles).not.be.undefined();
|
should(label.CSSStyles).not.be.undefined();
|
||||||
@@ -76,7 +59,7 @@ describe('radioOptionsGroup', function (): void {
|
|||||||
|
|
||||||
describe('getters and setters', async () => {
|
describe('getters and setters', async () => {
|
||||||
it(`component getter`, () => {
|
it(`component getter`, () => {
|
||||||
radioOptionsGroup.component().should.deepEqual(loadingComponent);
|
should(radioOptionsGroup.component()).not.be.undefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
[true, false].forEach(testValue => {
|
[true, false].forEach(testValue => {
|
||||||
@@ -93,14 +76,14 @@ describe('radioOptionsGroup', function (): void {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function verifyRadioGroup() {
|
function verifyRadioGroup() {
|
||||||
const radioButtons = divItems as FakeRadioButton[];
|
const radioButtons = radioOptionsGroup.items as azdata.RadioButtonComponent[];
|
||||||
radioButtons.length.should.equal(radioOptionsInfo.values!.length);
|
radioButtons.length.should.equal(radioOptionsInfo.values!.length, 'Unexpected number of radio buttons');
|
||||||
radioButtons.forEach(rb => {
|
radioButtons.forEach(rb => {
|
||||||
should(rb.label).not.be.undefined();
|
should(rb.label).not.equal(undefined, 'Radio Button label should not be undefined');
|
||||||
should(rb.value).not.be.undefined();
|
should(rb.value).not.equal(undefined, 'Radio button value should not be undefined');
|
||||||
should(rb.enabled).not.be.undefined();
|
should(rb.enabled).not.equal(undefined, 'Enabled should not be undefined');
|
||||||
rb.label!.should.equal(rb.value);
|
rb.label!.should.equal(rb.value, 'Radio button label did not match');
|
||||||
rb.enabled!.should.be.true();
|
rb.enabled!.should.be.true('Radio button should be enabled');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,30 +15,30 @@ export interface RadioOptionsInfo {
|
|||||||
|
|
||||||
export class FilePicker implements IReadOnly {
|
export class FilePicker implements IReadOnly {
|
||||||
private _flexContainer: azdata.FlexContainer;
|
private _flexContainer: azdata.FlexContainer;
|
||||||
private _filePathInputBox: azdata.InputBoxComponent;
|
public readonly filePathInputBox: azdata.InputBoxComponent;
|
||||||
private _filePickerButton: azdata.ButtonComponent;
|
public readonly filePickerButton: azdata.ButtonComponent;
|
||||||
constructor(
|
constructor(
|
||||||
modelBuilder: azdata.ModelBuilder,
|
modelBuilder: azdata.ModelBuilder,
|
||||||
initialPath: string, onNewDisposableCreated: (disposable: vscode.Disposable) => void
|
initialPath: string, onNewDisposableCreated: (disposable: vscode.Disposable) => void
|
||||||
) {
|
) {
|
||||||
const buttonWidth = 80;
|
const buttonWidth = 80;
|
||||||
this._filePathInputBox = modelBuilder.inputBox()
|
this.filePathInputBox = modelBuilder.inputBox()
|
||||||
.withProperties<azdata.InputBoxProperties>({
|
.withProperties<azdata.InputBoxProperties>({
|
||||||
value: initialPath,
|
value: initialPath,
|
||||||
width: 350
|
width: 350
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this._filePickerButton = modelBuilder.button()
|
this.filePickerButton = modelBuilder.button()
|
||||||
.withProperties<azdata.ButtonProperties>({
|
.withProperties<azdata.ButtonProperties>({
|
||||||
label: loc.browse,
|
label: loc.browse,
|
||||||
width: buttonWidth
|
width: buttonWidth
|
||||||
}).component();
|
}).component();
|
||||||
onNewDisposableCreated(this._filePickerButton.onDidClick(async () => {
|
onNewDisposableCreated(this.filePickerButton.onDidClick(async () => {
|
||||||
const fileUris = await vscode.window.showOpenDialog({
|
const fileUris = await vscode.window.showOpenDialog({
|
||||||
canSelectFiles: true,
|
canSelectFiles: true,
|
||||||
canSelectFolders: false,
|
canSelectFolders: false,
|
||||||
canSelectMany: false,
|
canSelectMany: false,
|
||||||
defaultUri: this._filePathInputBox.value ? vscode.Uri.file(path.dirname(this._filePathInputBox.value)) : undefined,
|
defaultUri: this.filePathInputBox.value ? vscode.Uri.file(path.dirname(this.filePathInputBox.value)) : undefined,
|
||||||
openLabel: loc.select,
|
openLabel: loc.select,
|
||||||
filters: undefined /* file type filters */
|
filters: undefined /* file type filters */
|
||||||
});
|
});
|
||||||
@@ -47,21 +47,21 @@ export class FilePicker implements IReadOnly {
|
|||||||
return; // This can happen when a user cancels out. we don't throw and the user just won't be able to move on until they select something.
|
return; // This can happen when a user cancels out. we don't throw and the user just won't be able to move on until they select something.
|
||||||
}
|
}
|
||||||
const fileUri = fileUris[0]; //we allow the user to select only one file in the dialog
|
const fileUri = fileUris[0]; //we allow the user to select only one file in the dialog
|
||||||
this._filePathInputBox.value = fileUri.fsPath;
|
this.filePathInputBox.value = fileUri.fsPath;
|
||||||
}));
|
}));
|
||||||
this._flexContainer = createFlexContainer(modelBuilder, [this._filePathInputBox, this._filePickerButton]);
|
this._flexContainer = createFlexContainer(modelBuilder, [this.filePathInputBox, this.filePickerButton]);
|
||||||
}
|
}
|
||||||
|
|
||||||
component(): azdata.Component {
|
component(): azdata.FlexContainer {
|
||||||
return this._flexContainer;
|
return this._flexContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
get onTextChanged() {
|
get onTextChanged() {
|
||||||
return this._filePathInputBox.onTextChanged;
|
return this.filePathInputBox.onTextChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
get value(): string | undefined {
|
get value(): string | undefined {
|
||||||
return this._filePathInputBox?.value;
|
return this.filePathInputBox?.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
get readOnly(): boolean {
|
get readOnly(): boolean {
|
||||||
|
|||||||
@@ -84,4 +84,8 @@ export class RadioOptionsGroup implements IReadOnly {
|
|||||||
this._divContainer.items.forEach(r => r.enabled = value);
|
this._divContainer.items.forEach(r => r.enabled = value);
|
||||||
this._divContainer.enabled = value;
|
this._divContainer.enabled = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get items(): azdata.Component[] {
|
||||||
|
return this._divContainer.items;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ abstract class ControllerDialogBase extends InitializingComponent {
|
|||||||
return this.dialog;
|
return this.dialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract async validate(): Promise<boolean>;
|
public abstract validate(): Promise<boolean>;
|
||||||
|
|
||||||
private handleCancel(): void {
|
private handleCancel(): void {
|
||||||
this.completionPromise.resolve(undefined);
|
this.completionPromise.resolve(undefined);
|
||||||
|
|||||||
@@ -275,6 +275,13 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/yamljs/-/yamljs-0.2.31.tgz#b1a620b115c96db7b3bfdf0cf54aee0c57139245"
|
resolved "https://registry.yarnpkg.com/@types/yamljs/-/yamljs-0.2.31.tgz#b1a620b115c96db7b3bfdf0cf54aee0c57139245"
|
||||||
integrity sha512-QcJ5ZczaXAqbVD3o8mw/mEBhRvO5UAdTtbvgwL/OgoWubvNBh6/MxLBAigtcgIFaq3shon9m3POIxQaLQt4fxQ==
|
integrity sha512-QcJ5ZczaXAqbVD3o8mw/mEBhRvO5UAdTtbvgwL/OgoWubvNBh6/MxLBAigtcgIFaq3shon9m3POIxQaLQt4fxQ==
|
||||||
|
|
||||||
|
agent-base@4, agent-base@^4.3.0:
|
||||||
|
version "4.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
|
||||||
|
integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==
|
||||||
|
dependencies:
|
||||||
|
es6-promisify "^5.0.0"
|
||||||
|
|
||||||
ajv@^6.5.5:
|
ajv@^6.5.5:
|
||||||
version "6.12.0"
|
version "6.12.0"
|
||||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7"
|
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7"
|
||||||
@@ -338,6 +345,16 @@ aws4@^1.8.0:
|
|||||||
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
|
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
|
||||||
integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==
|
integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==
|
||||||
|
|
||||||
|
azdata-test@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/azdata-test/-/azdata-test-1.0.0.tgz#b8821a5a313e6919dac8b8ef56f22dc8592cad2c"
|
||||||
|
integrity sha512-/QZkHeVKc0HbJtx45nFXNQLTe3lY8la37Re+YfBIzybMIul3FmBszwIyq9/MPHMhrjkvf5xDkEF+CuSlI4KeIA==
|
||||||
|
dependencies:
|
||||||
|
http-proxy-agent "^2.1.0"
|
||||||
|
https-proxy-agent "^2.2.4"
|
||||||
|
rimraf "^2.6.3"
|
||||||
|
typemoq "^2.1.0"
|
||||||
|
|
||||||
balanced-match@^1.0.0:
|
balanced-match@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||||
@@ -510,6 +527,18 @@ ecc-jsbn@~0.1.1:
|
|||||||
jsbn "~0.1.0"
|
jsbn "~0.1.0"
|
||||||
safer-buffer "^2.1.0"
|
safer-buffer "^2.1.0"
|
||||||
|
|
||||||
|
es6-promise@^4.0.3:
|
||||||
|
version "4.2.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
|
||||||
|
integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
|
||||||
|
|
||||||
|
es6-promisify@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
|
||||||
|
integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=
|
||||||
|
dependencies:
|
||||||
|
es6-promise "^4.0.3"
|
||||||
|
|
||||||
escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5:
|
escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||||
@@ -647,6 +676,14 @@ html-escaper@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
|
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
|
||||||
integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
|
integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
|
||||||
|
|
||||||
|
http-proxy-agent@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
|
||||||
|
integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==
|
||||||
|
dependencies:
|
||||||
|
agent-base "4"
|
||||||
|
debug "3.1.0"
|
||||||
|
|
||||||
http-signature@~1.2.0:
|
http-signature@~1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
|
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
|
||||||
@@ -656,6 +693,14 @@ http-signature@~1.2.0:
|
|||||||
jsprim "^1.2.2"
|
jsprim "^1.2.2"
|
||||||
sshpk "^1.7.0"
|
sshpk "^1.7.0"
|
||||||
|
|
||||||
|
https-proxy-agent@^2.2.4:
|
||||||
|
version "2.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b"
|
||||||
|
integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==
|
||||||
|
dependencies:
|
||||||
|
agent-base "^4.3.0"
|
||||||
|
debug "^3.1.0"
|
||||||
|
|
||||||
inflight@^1.0.4:
|
inflight@^1.0.4:
|
||||||
version "1.0.6"
|
version "1.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
|
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
|
||||||
@@ -1204,7 +1249,7 @@ type-detect@4.0.8, type-detect@^4.0.8:
|
|||||||
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
|
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
|
||||||
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
|
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
|
||||||
|
|
||||||
typemoq@2.1.0:
|
typemoq@2.1.0, typemoq@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/typemoq/-/typemoq-2.1.0.tgz#4452ce360d92cf2a1a180f0c29de2803f87af1e8"
|
resolved "https://registry.yarnpkg.com/typemoq/-/typemoq-2.1.0.tgz#4452ce360d92cf2a1a180f0c29de2803f87af1e8"
|
||||||
integrity sha512-DtRNLb7x8yCTv/KHlwes+NI+aGb4Vl1iPC63Hhtcvk1DpxSAZzKWQv0RQFY0jX2Uqj0SDBNl8Na4e6MV6TNDgw==
|
integrity sha512-DtRNLb7x8yCTv/KHlwes+NI+aGb4Vl1iPC63Hhtcvk1DpxSAZzKWQv0RQFY0jX2Uqj0SDBNl8Na4e6MV6TNDgw==
|
||||||
|
|||||||
@@ -524,6 +524,7 @@
|
|||||||
"@types/semver": "^7.3.1",
|
"@types/semver": "^7.3.1",
|
||||||
"@types/sinon": "^9.0.8",
|
"@types/sinon": "^9.0.8",
|
||||||
"@types/yamljs": "0.2.30",
|
"@types/yamljs": "0.2.30",
|
||||||
|
"azdata-test": "^1.0.0",
|
||||||
"mocha": "^5.2.0",
|
"mocha": "^5.2.0",
|
||||||
"mocha-junit-reporter": "^1.17.0",
|
"mocha-junit-reporter": "^1.17.0",
|
||||||
"mocha-multi-reporters": "^1.1.7",
|
"mocha-multi-reporters": "^1.1.7",
|
||||||
|
|||||||
@@ -3,11 +3,8 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* 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 events from 'events';
|
import * as events from 'events';
|
||||||
import * as cp from 'promisify-child-process';
|
import * as cp from 'promisify-child-process';
|
||||||
import * as TypeMoq from 'typemoq';
|
|
||||||
import { Readable } from 'stream';
|
import { Readable } from 'stream';
|
||||||
|
|
||||||
export class TestChildProcessPromise<T> implements cp.ChildProcessPromise {
|
export class TestChildProcessPromise<T> implements cp.ChildProcessPromise {
|
||||||
@@ -106,117 +103,3 @@ export class TestChildProcessPromise<T> implements cp.ChildProcessPromise {
|
|||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComponentAndMockComponentBuilder<C, B> = {
|
|
||||||
component: C,
|
|
||||||
mockBuilder: TypeMoq.IMock<B>
|
|
||||||
};
|
|
||||||
|
|
||||||
export function createModelViewMock(): {
|
|
||||||
modelBuilder: TypeMoq.IMock<azdata.ModelBuilder>,
|
|
||||||
modelView: TypeMoq.IMock<azdata.ModelView>
|
|
||||||
} {
|
|
||||||
const mockModelView = TypeMoq.Mock.ofType<azdata.ModelView>();
|
|
||||||
const mockModelBuilder = TypeMoq.Mock.ofType<azdata.ModelBuilder>();
|
|
||||||
const mockTextBuilder = createMockComponentBuilder<azdata.TextComponent>();
|
|
||||||
const mockGroupContainerBuilder = createMockContainerBuilder<azdata.GroupContainer>();
|
|
||||||
const mockFormContainerBuilder = createMockFormContainerBuilder();
|
|
||||||
mockModelBuilder.setup(b => b.text()).returns(() => mockTextBuilder.mockBuilder.object);
|
|
||||||
mockModelBuilder.setup(b => b.groupContainer()).returns(() => mockGroupContainerBuilder.mockBuilder.object);
|
|
||||||
mockModelBuilder.setup(b => b.formContainer()).returns(() => mockFormContainerBuilder.object);
|
|
||||||
mockModelView.setup(mv => mv.modelBuilder).returns(() => mockModelBuilder.object);
|
|
||||||
return {
|
|
||||||
modelBuilder: mockModelBuilder,
|
|
||||||
modelView: mockModelView
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createMockComponentBuilder<C extends azdata.Component, B extends azdata.ComponentBuilder<C, any> = azdata.ComponentBuilder<C, any>>(component?: C): ComponentAndMockComponentBuilder<C, B> {
|
|
||||||
const mockComponentBuilder = TypeMoq.Mock.ofType<B>();
|
|
||||||
// Create a mocked dynamic component if we don't have a stub instance to use.
|
|
||||||
// Note that we don't use ofInstance here for the component because there's some limitations around properties that I was
|
|
||||||
// hitting preventing me from easily using TypeMoq. Passing in the stub instance lets users control the object being stubbed - which means
|
|
||||||
// they can use things like sinon to then override specific functions if desired.
|
|
||||||
if (!component) {
|
|
||||||
const mockComponent = TypeMoq.Mock.ofType<C>();
|
|
||||||
// Need to setup then for when a dynamic mocked object is resolved otherwise the test will hang : https://github.com/florinn/typemoq/issues/66
|
|
||||||
mockComponent.setup((x: any) => x.then).returns(() => undefined);
|
|
||||||
component = mockComponent.object;
|
|
||||||
}
|
|
||||||
// For now just have these be passthrough - can hook up additional functionality later if needed
|
|
||||||
mockComponentBuilder.setup(b => b.withProperties(TypeMoq.It.isAny())).returns(() => mockComponentBuilder.object);
|
|
||||||
mockComponentBuilder.setup(b => b.withValidation(TypeMoq.It.isAny())).returns(() => mockComponentBuilder.object);
|
|
||||||
mockComponentBuilder.setup(b => b.component()).returns(() => component! /*mockComponent.object*/);
|
|
||||||
return {
|
|
||||||
component: component!,
|
|
||||||
mockBuilder: mockComponentBuilder
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createMockContainerBuilder<C extends azdata.Container<any, any>, B extends azdata.ContainerBuilder<C, any, any, any> = azdata.ContainerBuilder<C, any, any, any>>(): ComponentAndMockComponentBuilder<C, B> {
|
|
||||||
const mockContainerBuilder = createMockComponentBuilder<C, B>();
|
|
||||||
// For now just have these be passthrough - can hook up additional functionality later if needed
|
|
||||||
mockContainerBuilder.mockBuilder.setup(b => b.withItems(TypeMoq.It.isAny(), undefined)).returns(() => mockContainerBuilder.mockBuilder.object);
|
|
||||||
mockContainerBuilder.mockBuilder.setup(b => b.withLayout(TypeMoq.It.isAny())).returns(() => mockContainerBuilder.mockBuilder.object);
|
|
||||||
return mockContainerBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createMockFormContainerBuilder(): TypeMoq.IMock<azdata.FormBuilder> {
|
|
||||||
const mockContainerBuilder = createMockContainerBuilder<azdata.FormContainer, azdata.FormBuilder>();
|
|
||||||
mockContainerBuilder.mockBuilder.setup(b => b.withFormItems(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => mockContainerBuilder.mockBuilder.object);
|
|
||||||
return mockContainerBuilder.mockBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class StubInputBox implements azdata.InputBoxComponent {
|
|
||||||
readonly id = 'input-box';
|
|
||||||
public enabled: boolean = false;
|
|
||||||
|
|
||||||
onTextChanged: vscode.Event<any> = undefined!;
|
|
||||||
onEnterKeyPressed: vscode.Event<string> = undefined!;
|
|
||||||
|
|
||||||
updateProperties(properties: { [key: string]: any }): Thenable<void> { throw new Error('Not implemented'); }
|
|
||||||
|
|
||||||
updateProperty(key: string, value: any): Thenable<void> { throw new Error('Not implemented'); }
|
|
||||||
|
|
||||||
updateCssStyles(cssStyles: { [key: string]: string }): Thenable<void> { throw new Error('Not implemented'); }
|
|
||||||
|
|
||||||
readonly onValidityChanged: vscode.Event<boolean> = undefined!;
|
|
||||||
|
|
||||||
readonly valid: boolean = true;
|
|
||||||
|
|
||||||
validate(): Thenable<boolean> { throw new Error('Not implemented'); }
|
|
||||||
|
|
||||||
focus(): Thenable<void> { return Promise.resolve(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class StubCheckbox implements azdata.CheckBoxComponent {
|
|
||||||
private _onChanged = new vscode.EventEmitter<void>();
|
|
||||||
private _checked = false;
|
|
||||||
|
|
||||||
readonly id = 'stub-checkbox';
|
|
||||||
public enabled: boolean = false;
|
|
||||||
|
|
||||||
get checked(): boolean {
|
|
||||||
return this._checked;
|
|
||||||
}
|
|
||||||
set checked(value: boolean) {
|
|
||||||
this._checked = value;
|
|
||||||
this._onChanged.fire();
|
|
||||||
}
|
|
||||||
|
|
||||||
onChanged: vscode.Event<any> = this._onChanged.event;
|
|
||||||
|
|
||||||
updateProperties(properties: { [key: string]: any }): Thenable<void> { throw new Error('Not implemented'); }
|
|
||||||
|
|
||||||
updateProperty(key: string, value: any): Thenable<void> { throw new Error('Not implemented'); }
|
|
||||||
|
|
||||||
updateCssStyles(cssStyles: { [key: string]: string }): Thenable<void> { throw new Error('Not implemented'); }
|
|
||||||
|
|
||||||
readonly onValidityChanged: vscode.Event<boolean> = undefined!;
|
|
||||||
|
|
||||||
readonly valid: boolean = true;
|
|
||||||
|
|
||||||
validate(): Thenable<boolean> { throw new Error('Not implemented'); }
|
|
||||||
|
|
||||||
focus(): Thenable<void> { return Promise.resolve(); }
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -11,23 +11,29 @@ import { initializeWizardPage, InputComponent, InputComponentInfo, Validator, Wi
|
|||||||
import { FieldType } from '../../../interfaces';
|
import { FieldType } from '../../../interfaces';
|
||||||
import { IToolsService } from '../../../services/toolsService';
|
import { IToolsService } from '../../../services/toolsService';
|
||||||
import { Deferred } from '../../utils';
|
import { Deferred } from '../../utils';
|
||||||
import { createMockComponentBuilder, createModelViewMock as createMockModelView, StubCheckbox, StubInputBox } from '../../stubs';
|
import { createModelViewMock } from 'azdata-test/out/mocks/modelView/modelViewMock';
|
||||||
|
import { StubCheckbox } from 'azdata-test/out/stubs/modelView/stubCheckbox';
|
||||||
|
import { StubInputBox } from 'azdata-test/out/stubs/modelView/stubInputBox';
|
||||||
import * as should from 'should';
|
import * as should from 'should';
|
||||||
import * as sinon from 'sinon';
|
import * as sinon from 'sinon';
|
||||||
|
|
||||||
|
|
||||||
describe('WizardPage', () => {
|
describe('WizardPage', () => {
|
||||||
let mockModelBuilder: TypeMoq.IMock<azdata.ModelBuilder>;
|
let stubCheckbox: StubCheckbox;
|
||||||
|
let stubInputBox: StubInputBox;
|
||||||
let testWizardPage: WizardPageContext;
|
let testWizardPage: WizardPageContext;
|
||||||
let contentRegistered: Deferred<void>;
|
let contentRegistered: Deferred<void>;
|
||||||
|
|
||||||
before(function () {
|
before(function () {
|
||||||
contentRegistered = new Deferred<void>();
|
contentRegistered = new Deferred<void>();
|
||||||
const mockWizardPage = TypeMoq.Mock.ofType<azdata.window.WizardPage>();
|
const mockWizardPage = TypeMoq.Mock.ofType<azdata.window.WizardPage>();
|
||||||
const mockModelView = createMockModelView();
|
stubCheckbox = new StubCheckbox();
|
||||||
mockModelBuilder = mockModelView.modelBuilder;
|
stubInputBox = new StubInputBox();
|
||||||
|
const mockModelView = createModelViewMock({
|
||||||
|
checkBox: () => stubCheckbox,
|
||||||
|
inputBox: () => stubInputBox
|
||||||
|
});
|
||||||
mockWizardPage.setup(p => p.registerContent(TypeMoq.It.isAny())).callback(async (handler: (view: azdata.ModelView) => Thenable<void>) => {
|
mockWizardPage.setup(p => p.registerContent(TypeMoq.It.isAny())).callback(async (handler: (view: azdata.ModelView) => Thenable<void>) => {
|
||||||
await handler(mockModelView.modelView.object);
|
await handler(mockModelView.modelViewMock.object);
|
||||||
contentRegistered.resolve();
|
contentRegistered.resolve();
|
||||||
});
|
});
|
||||||
const mockWizard = TypeMoq.Mock.ofType<azdata.window.Wizard>();
|
const mockWizard = TypeMoq.Mock.ofType<azdata.window.Wizard>();
|
||||||
@@ -75,9 +81,7 @@ describe('WizardPage', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('dynamic enablement', async function (): Promise<void> {
|
it('dynamic enablement', async function (): Promise<void> {
|
||||||
const stubCheckbox = new StubCheckbox();
|
|
||||||
const mockCheckboxBuilder = createMockComponentBuilder<azdata.CheckBoxComponent>(stubCheckbox);
|
|
||||||
const stubInputBox = new StubInputBox();
|
|
||||||
// Stub out the enabled property so we can hook into when that's set to ensure we wait for the state to be updated
|
// Stub out the enabled property so we can hook into when that's set to ensure we wait for the state to be updated
|
||||||
// before continuing the test
|
// before continuing the test
|
||||||
let enabled = false;
|
let enabled = false;
|
||||||
@@ -88,11 +92,8 @@ describe('WizardPage', () => {
|
|||||||
sinon.stub(stubInputBox, 'enabled').get(() => {
|
sinon.stub(stubInputBox, 'enabled').get(() => {
|
||||||
return enabled;
|
return enabled;
|
||||||
});
|
});
|
||||||
const mockInputBoxBuilder = createMockComponentBuilder<azdata.InputBoxComponent>(stubInputBox);
|
|
||||||
// Used to ensure that we wait until the enabled state is updated for our mocked components before continuing
|
// Used to ensure that we wait until the enabled state is updated for our mocked components before continuing
|
||||||
let enabledDeferred = new Deferred();
|
let enabledDeferred = new Deferred();
|
||||||
mockModelBuilder.setup(b => b.checkBox()).returns(() => mockCheckboxBuilder.mockBuilder.object);
|
|
||||||
mockModelBuilder.setup(b => b.inputBox()).returns(() => mockInputBoxBuilder.mockBuilder.object);
|
|
||||||
|
|
||||||
initializeWizardPage(testWizardPage);
|
initializeWizardPage(testWizardPage);
|
||||||
await contentRegistered.promise;
|
await contentRegistered.promise;
|
||||||
|
|||||||
@@ -252,6 +252,13 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/yamljs/-/yamljs-0.2.30.tgz#d034e1d329e46e8d0f737c9a8db97f68f81b5382"
|
resolved "https://registry.yarnpkg.com/@types/yamljs/-/yamljs-0.2.30.tgz#d034e1d329e46e8d0f737c9a8db97f68f81b5382"
|
||||||
integrity sha1-0DTh0ynkbo0Pc3yajbl/aPgbU4I=
|
integrity sha1-0DTh0ynkbo0Pc3yajbl/aPgbU4I=
|
||||||
|
|
||||||
|
agent-base@4, agent-base@^4.3.0:
|
||||||
|
version "4.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
|
||||||
|
integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==
|
||||||
|
dependencies:
|
||||||
|
es6-promisify "^5.0.0"
|
||||||
|
|
||||||
ansi-regex@^3.0.0:
|
ansi-regex@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
|
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
|
||||||
@@ -278,6 +285,16 @@ argparse@^1.0.7:
|
|||||||
dependencies:
|
dependencies:
|
||||||
sprintf-js "~1.0.2"
|
sprintf-js "~1.0.2"
|
||||||
|
|
||||||
|
azdata-test@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/azdata-test/-/azdata-test-1.0.0.tgz#b8821a5a313e6919dac8b8ef56f22dc8592cad2c"
|
||||||
|
integrity sha512-/QZkHeVKc0HbJtx45nFXNQLTe3lY8la37Re+YfBIzybMIul3FmBszwIyq9/MPHMhrjkvf5xDkEF+CuSlI4KeIA==
|
||||||
|
dependencies:
|
||||||
|
http-proxy-agent "^2.1.0"
|
||||||
|
https-proxy-agent "^2.2.4"
|
||||||
|
rimraf "^2.6.3"
|
||||||
|
typemoq "^2.1.0"
|
||||||
|
|
||||||
balanced-match@^1.0.0:
|
balanced-match@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||||
@@ -406,6 +423,18 @@ diff@^4.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
|
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
|
||||||
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
|
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
|
||||||
|
|
||||||
|
es6-promise@^4.0.3:
|
||||||
|
version "4.2.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
|
||||||
|
integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
|
||||||
|
|
||||||
|
es6-promisify@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
|
||||||
|
integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=
|
||||||
|
dependencies:
|
||||||
|
es6-promise "^4.0.3"
|
||||||
|
|
||||||
escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5:
|
escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||||
@@ -487,6 +516,22 @@ html-escaper@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
|
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
|
||||||
integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
|
integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
|
||||||
|
|
||||||
|
http-proxy-agent@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
|
||||||
|
integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==
|
||||||
|
dependencies:
|
||||||
|
agent-base "4"
|
||||||
|
debug "3.1.0"
|
||||||
|
|
||||||
|
https-proxy-agent@^2.2.4:
|
||||||
|
version "2.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b"
|
||||||
|
integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==
|
||||||
|
dependencies:
|
||||||
|
agent-base "^4.3.0"
|
||||||
|
debug "^3.1.0"
|
||||||
|
|
||||||
inflight@^1.0.4:
|
inflight@^1.0.4:
|
||||||
version "1.0.6"
|
version "1.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
|
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
|
||||||
|
|||||||
Reference in New Issue
Block a user