Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#1237 - added new copy and updated functionality #1238

Merged
merged 5 commits into from
Jan 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
290 changes: 138 additions & 152 deletions package-lock.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,12 @@
.browse-button {
overflow: hidden;
position: absolute;
right: 0;
bottom: 12px;
right: 4px;
bottom: 11.5px;
}

.input-container {
margin-top: 10px;
padding-right: 115px;

& + div {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,12 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

import { newNotification, SharedConstants } from '@bfemulator/app-shared';
import { SharedConstants } from '@bfemulator/app-shared';
import { mount } from 'enzyme';
import * as React from 'react';
import { Provider } from 'react-redux';
import { combineReducers, createStore } from 'redux';
import * as BotActions from '../../../data/action/botActions';
import { beginAdd } from '../../../data/action/notificationActions';
import { bot } from '../../../data/reducer/bot';
import { CommandServiceImpl } from '../../../platform/commands/commandServiceImpl';
import { ActiveBotHelper } from '../../helpers/activeBotHelper';
Expand Down Expand Up @@ -72,13 +71,11 @@ const bots = [
];

describe('The OpenBotDialog', () => {
let mockDispatch;
let node;
let parent;
let instance;
beforeEach(() => {
mockStore.dispatch(BotActions.load(bots));
mockDispatch = jest.spyOn(mockStore, 'dispatch');
parent = mount(<Provider store={ mockStore }>
<OpenBotDialogContainer/>
</Provider>);
Expand All @@ -92,47 +89,6 @@ describe('The OpenBotDialog', () => {
expect(spy).toHaveBeenCalled();
});

it('should orchestrate the appropriate sequences when a recent bot is clicked', async () => {
const commandServiceSpy = jest.spyOn(CommandServiceImpl, 'call').mockResolvedValue(true);
const dialogSpy = jest.spyOn(DialogService, 'hideDialog');
await instance.onBotSelected(bots[0]);
const { Switch } = SharedConstants.Commands.Bot;
expect(commandServiceSpy).toHaveBeenCalledWith(Switch, '/some/path');
expect(dialogSpy).toHaveBeenCalled();
});

it('should send a notification when the bot fails to open', async () => {
await instance.onBotSelected(null);
const message = `An Error occurred on the Open Bot Dialog: TypeError: Cannot read property 'path' of null`;
const notification = newNotification(message);
const action = beginAdd(notification);
notification.timestamp = jasmine.any(Number) as any;
notification.id = jasmine.any(String) as any;
expect(mockDispatch).toHaveBeenLastCalledWith(action);
});

it('should make the appropriate calls when onCreateNewBotClick in called', async () => {
const commandServiceSpy = jest.spyOn(CommandServiceImpl, 'call').mockResolvedValue(true);
const dialogSpy = jest.spyOn(DialogService, 'hideDialog');

await instance.onCreateNewBotClick();
expect(commandServiceSpy).toHaveBeenLastCalledWith(SharedConstants.Commands.UI.ShowBotCreationDialog);
expect(dialogSpy).toHaveBeenCalled();
});

it('should send a notification when onCreateNewBotClick fails', async () => {
const commandServiceSpy = jest.spyOn(CommandServiceImpl, 'call').mockRejectedValue('oh noes!');
await instance.onCreateNewBotClick();
const message = `An Error occurred on the Open Bot Dialog: oh noes!`;
const notification = newNotification(message);
const action = beginAdd(notification);
notification.timestamp = jasmine.any(Number) as any;
notification.id = jasmine.any(String) as any;
expect(mockDispatch).toHaveBeenLastCalledWith(action);

expect(commandServiceSpy).toHaveBeenLastCalledWith(SharedConstants.Commands.UI.ShowBotCreationDialog);
});

it('should properly set the state when the input changes', () => {
instance.onInputChange({
target: {
Expand Down Expand Up @@ -174,7 +130,7 @@ describe('The OpenBotDialog', () => {
} as any);

const botHelperSpy = jest.spyOn(ActiveBotHelper, 'confirmAndOpenBotFromFile').mockResolvedValue(true);
await instance.onOpenClick();
await instance.onSubmit();

expect(botHelperSpy).toHaveBeenCalledWith('some/path/to/myBot.bot');
});
Expand All @@ -188,7 +144,7 @@ describe('The OpenBotDialog', () => {
} as any);

const commandServiceSpy = jest.spyOn(CommandServiceImpl, 'call');
await instance.onOpenClick();
await instance.onSubmit();

expect(commandServiceSpy).toHaveBeenCalledWith(SharedConstants.Commands.Emulator.NewLiveChat,
{ endpoint: 'http://localhost:6500/api/messages' });
Expand Down
92 changes: 34 additions & 58 deletions packages/app/client/src/ui/dialogs/openBotDialog/openBotDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,12 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

import { BotInfo } from '@bfemulator/app-shared';
import { DefaultButton, Dialog, DialogFooter, PrimaryButton, TextField } from '@bfemulator/ui-react';
import * as React from 'react';
import { ChangeEvent, FocusEvent, ReactNode } from 'react';
import { RecentBotsListContainer } from '../../editor/recentBotsList/recentBotsListContainer';
import * as styles from '../dialogStyles.scss';
import * as openBotStyles from './openBotDialog.scss';

export interface OpenBotDialogProps {
showCreateNewBotDialog?: () => Promise<void>;
onDialogCancel?: () => void;
openBot?: (urlOrPath: string) => Promise<void>;
sendNotification?: (error: Error) => void;
Expand Down Expand Up @@ -70,65 +66,45 @@ export class OpenBotDialog extends React.Component<OpenBotDialogProps, OpenBotDi
return (
<Dialog
cancel={ this.props.onDialogCancel }
className={ `${ styles.dialogMedium } ${ openBotStyles.themeOverrides }` }
title="Open a Bot">
<p>
{ `You can open a bot using just a URL, choose from a list of recently opened bots or ` }
<a href="javascript:void(0);" onClick={ this.onCreateNewBotClick }>create a new bot configuration</a>
</p>
<TextField
errorMessage={ errorMessage }
inputContainerClassName={ openBotStyles.inputContainer }
label="Bot URL or File Location"
onChange={ this.onInputChange }
onFocus={ this.onFocus }
value={ botUrl }>
className={ openBotStyles.themeOverrides }
title="Open a bot">
<form onSubmit={ this.onSubmit }>
<TextField
errorMessage={ errorMessage }
inputContainerClassName={ openBotStyles.inputContainer }
label="Bot URL or file location"
onChange={ this.onInputChange }
onFocus={ this.onFocus }
autoFocus={ true }
value={ botUrl }>

<PrimaryButton
className={ openBotStyles.browseButton }>
Browse
<input
className={ openBotStyles.fileInput }
onChange={ this.onInputChange }
accept=".bot"
type="file"/>
</PrimaryButton>
<PrimaryButton
className={ openBotStyles.browseButton }>
Browse
<input
className={ openBotStyles.fileInput }
onChange={ this.onInputChange }
accept=".bot"
type="file"/>
</PrimaryButton>

</TextField>
<RecentBotsListContainer onBotSelected={ this.onBotSelected }/>
<DialogFooter>
<PrimaryButton
text="Close"
onClick={ this.props.onDialogCancel }
/>
<DefaultButton
disabled={ !!errorMessage }
onClick={ this.onOpenClick }
text="Open/Connect"
/>
</DialogFooter>
</TextField>
<DialogFooter>
<DefaultButton
text="Cancel"
onClick={ this.props.onDialogCancel }
/>
<PrimaryButton
type="submit"
disabled={ !!errorMessage || !botUrl }
text="Connect"
/>
</DialogFooter>
</form>
</Dialog>
);
}

private onBotSelected = async (bot: BotInfo) => {
try {
await this.props.switchToBot(bot.path);
this.props.onDialogCancel();
} catch (e) {
this.props.sendNotification(e);
}
}

private onCreateNewBotClick = async () => {
try {
await this.props.showCreateNewBotDialog();
this.props.onDialogCancel();
} catch (e) {
this.props.sendNotification(e);
}
}

private onFocus = (event: FocusEvent<HTMLInputElement>) => {
const input = event.target as HTMLInputElement;
input.setSelectionRange(0, (input.value || '').length);
Expand All @@ -140,7 +116,7 @@ export class OpenBotDialog extends React.Component<OpenBotDialogProps, OpenBotDi
this.setState({ botUrl });
}

private onOpenClick = async () => {
private onSubmit = async () => {
try {
await this.props.openBot(this.state.botUrl);
} catch (e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,25 +32,17 @@
//

import { newNotification, SharedConstants } from '@bfemulator/app-shared';
import { Action } from 'redux';
import { IEndpointService } from 'botframework-config/lib/schema';
import { connect } from 'react-redux';
import { Action } from 'redux';
import { beginAdd } from '../../../data/action/notificationActions';
import { RootState } from '../../../data/store';
import { CommandServiceImpl } from '../../../platform/commands/commandServiceImpl';
import { ActiveBotHelper } from '../../helpers/activeBotHelper';
import { DialogService } from '../service';
import { OpenBotDialog, OpenBotDialogProps } from './openBotDialog';

const mapStateToProps = (state: RootState, ownProps: { [propName: string]: any }): OpenBotDialogProps => {
return {
showCreateNewBotDialog: () => CommandServiceImpl.call(SharedConstants.Commands.UI.ShowBotCreationDialog),
...ownProps
};
};

const mapDispatchToProps = (dispatch: (action: Action) => void): OpenBotDialogProps => {
const { Commands: { Emulator: { NewLiveChat }, Bot: { Switch } } } = SharedConstants;
const { Commands: { Emulator: { NewLiveChat } } } = SharedConstants;
return {
onDialogCancel: () => DialogService.hideDialog(),
openBot: (urlOrPath: string) => {
Expand All @@ -62,11 +54,10 @@ const mapDispatchToProps = (dispatch: (action: Action) => void): OpenBotDialogPr
},
sendNotification: (error: Error) =>
dispatch(beginAdd(newNotification(`An Error occurred on the Open Bot Dialog: ${ error }`))),
switchToBot: (path: string) => CommandServiceImpl.call(Switch, path)
};
};

export const OpenBotDialogContainer = connect(
mapStateToProps,
null,
mapDispatchToProps
)(OpenBotDialog);
60 changes: 41 additions & 19 deletions packages/app/client/src/ui/helpers/activeBotHelper.spec.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import { ActiveBotHelper } from './activeBotHelper';
import { CommandServiceImpl } from '../../platform/commands/commandServiceImpl';
import * as editorHelpers from '../../data/editorHelpers';
import { store } from '../../data/store';
import * as botHelpers from '../../data/botHelpers';
import { SharedConstants } from '@bfemulator/app-shared';
import { BotConfigWithPath } from '@bfemulator/sdk-shared';
import { IEndpointService, ServiceTypes } from 'botframework-config/lib/schema';
import { SharedConstants } from '@bfemulator/app-shared';
import * as botHelpers from '../../data/botHelpers';
import * as editorHelpers from '../../data/editorHelpers';
import { store } from '../../data/store';
import { CommandServiceImpl } from '../../platform/commands/commandServiceImpl';
import { ActiveBotHelper } from './activeBotHelper';

jest.mock('../../ui/dialogs', () => ({
AzureLoginPromptDialogContainer: function mock() {
return undefined;
},
AzureLoginSuccessDialogContainer: function mock() {
return undefined;
},
BotCreationDialog: function mock() {
return undefined;
},
DialogService: { showDialog: () => Promise.resolve(true) },
SecretPromptDialog: function mock() {
return undefined;
AzureLoginPromptDialogContainer: function mock() {
return undefined;
},
AzureLoginSuccessDialogContainer: function mock() {
return undefined;
},
BotCreationDialog: function mock() {
return undefined;
},
DialogService: { showDialog: () => Promise.resolve(true) },
SecretPromptDialog: function mock() {
return undefined;
}
}
}
));

describe('ActiveBotHelper tests', () => {
Expand Down Expand Up @@ -228,6 +228,28 @@ describe('ActiveBotHelper tests', () => {
ActiveBotHelper.confirmSwitchBot = backupConfirmSwitchBot;
});

it('should throw an error when confirmAndOpenBotFromFile fails', async () => {
jest.spyOn(ActiveBotHelper, 'browseForBotFile').mockRejectedValueOnce('oh noes!');
try {
await ActiveBotHelper.confirmAndOpenBotFromFile('');
expect(false);
} catch (e) {
expect(e).not.toBeNull();
}
});

it('should throw when confirmAndSwitchBots fails', async () => {
jest.spyOn(CommandServiceImpl, 'call').mockRejectedValueOnce('oh noes!');
jest.spyOn(botHelpers, 'getActiveBot').mockReturnValueOnce({path: ''});
try {
await ActiveBotHelper.confirmAndSwitchBots('');
expect(false);
} catch (e) {
expect(e).not.toBeNull();
}
jest.resetAllMocks();
});

it('confirmAndSwitchBots() functionality', async () => {
const backupBotAlreadyOpen = ActiveBotHelper.botAlreadyOpen;
const backupConfirmSwitchBot = ActiveBotHelper.confirmSwitchBot;
Expand Down
Loading