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

[#2042] Changed convo infrastructure to use Web Sockets. #2034

Merged
merged 3 commits into from
Jan 23, 2020
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

## Added
- [client/main] Added Ngrok Debugger UI in PR [2032](https://github.com/microsoft/BotFramework-Emulator/pull/2032)
- [client/main] Changed conversation infrastructure to use Web Sockets to communicate with Web Chat in PR [2034](https://github.com/microsoft/BotFramework-Emulator/pull/2034)

## v4.7.0 - 2019 - 12 - 13
## Fixed
- [client] Added an empty state for the recent bots submenu in the app menu for Windows in PR [1945](https://github.com/microsoft/BotFramework-Emulator/pull/1945)
Expand Down
2,677 changes: 1,536 additions & 1,141 deletions package-lock.json

Large diffs are not rendered by default.

193 changes: 68 additions & 125 deletions packages/app/client/src/commands/emulatorCommands.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,27 +30,22 @@
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import { newNotification, SharedConstants } from '@bfemulator/app-shared';
import { combineReducers, createStore } from 'redux';

import { SharedConstants } from '@bfemulator/app-shared';
import { CommandRegistry, CommandServiceImpl, CommandServiceInstance } from '@bfemulator/sdk-shared';

import { clientAwareSettingsChanged } from '../state/actions/clientAwareSettingsActions';
import { beginAdd } from '../state/actions/notificationActions';
import { bot } from '../state/reducers/bot';
import { chat } from '../state/reducers/chat';
import { clientAwareSettings } from '../state/reducers/clientAwareSettings';
import { editor } from '../state/reducers/editor';
import { framework } from '../state/reducers/framework';
import { RootState } from '../state/store';
import { setFrameworkSettings } from '../state/actions/frameworkSettingsActions';
import { close } from '../state/actions/editorActions';
import { openTranscript, closeDocument } from '../state/actions/chatActions';
import { openBotViaUrlAction } from '../state';

import { EmulatorCommands } from './emulatorCommands';

const mockEndpoint = {
endpoint: 'https://localhost:8080/api/messages',
let mockState = {};
const mockStore = {
dispatch: jest.fn(),
getState: () => mockState,
};

let mockStore;
jest.mock('../state/store', () => ({
get store() {
return mockStore;
Expand Down Expand Up @@ -85,6 +80,8 @@ jest.mock('electron', () => ({
describe('The emulator commands', () => {
let commandService: CommandServiceImpl;
let registry: CommandRegistry;
const { Emulator } = SharedConstants.Commands;

beforeAll(() => {
new EmulatorCommands();
const decorator = CommandServiceInstance();
Expand All @@ -94,129 +91,75 @@ describe('The emulator commands', () => {
});

beforeEach(() => {
mockStore = createStore(combineReducers({ bot, chat, clientAwareSettings, editor, framework }));
mockStore.dispatch(
clientAwareSettingsChanged({
users: { currentUserId: '1234' },
cwd: 'path',
locale: 'en-us',
serverUrl: 'https://localhost',
debugMode: 1,
appPath: '',
savedBotUrls: [],
})
);
});

it('Should open a new emulator tabbed document for an endpoint', () => {
const handler = registry.getCommand(SharedConstants.Commands.Emulator.NewLiveChat);
const documentId = handler(mockEndpoint, false);
const state: RootState = mockStore.getState();
const documentIds = Object.keys(state.chat.chats);
const document = state.chat.chats[documentId];
expect(document.userId).toEqual('1234');
expect(documentIds.length).toBe(1);
expect(state.editor.editors.primary.activeDocumentId).toBe(documentId);
});

it('should open a new emulator tabbed document for an endpoint and use the custom user id', () => {
let state: RootState = mockStore.getState();
mockStore.dispatch(setFrameworkSettings({ ...state.framework, userGUID: 'customUserId' }));
const handler = registry.getCommand(SharedConstants.Commands.Emulator.NewLiveChat);
const documentId = handler(mockEndpoint, false);
state = mockStore.getState();
const document = state.chat.chats[documentId];
expect(document.userId).toEqual('customUserId');
mockState = {};
mockStore.dispatch.mockClear();
});

it('should set the active tab of an existing chat', () => {
const handler = registry.getCommand(SharedConstants.Commands.Emulator.NewLiveChat);
const documentId = handler(mockEndpoint, false);
const secondDocumentId = handler({
endpoint: 'https://localhost:8181/api/messages',
});
// At this point we should have 2 open documents
// with the second on
expect(mockStore.getState().editor.editors.primary.activeDocumentId).toBe(secondDocumentId);
handler(mockEndpoint, true); // re-open the original document
expect(mockStore.getState().editor.editors.primary.activeDocumentId).toBe(documentId);
it('should open a new live chat', () => {
const mockEndpoint: any = {
appId: 'someAppId',
appPassword: 'someAppPw',
channelService: 'someChannelService',
endpoint: 'http://localhost:3978/api/messages',
};
const handler = registry.getCommand(Emulator.NewLiveChat);
handler(mockEndpoint, 'livechat');

expect(mockStore.dispatch).toHaveBeenCalledWith(
openBotViaUrlAction({
appId: 'someAppId',
appPassword: 'someAppPw',
channelService: 'someChannelService' as any,
endpoint: 'http://localhost:3978/api/messages',
isFromBotFile: true,
mode: 'livechat',
})
);
});

it('should open a transcript', () => {
const handler = registry.getCommand(SharedConstants.Commands.Emulator.OpenTranscript);
const filePath = 'transcript.transcript';
handler(filePath, filePath);
it('should prompt to open a transcript', async () => {
const mockFileName = 'test.transcript';
const remoteCallSpy = jest.spyOn(commandService, 'remoteCall').mockResolvedValueOnce(mockFileName);
const handler = registry.getCommand(Emulator.PromptToOpenTranscript);
await handler();

const state = mockStore.getState();
expect(state.chat.chats[filePath]).toBeTruthy();
expect(state.editor.editors.primary.activeDocumentId).toBe(filePath);
expect(mockStore.dispatch).toHaveBeenCalledWith(openTranscript(mockFileName));
remoteCallSpy.mockClear();
});

it('Should prompt to open a transcript', async () => {
const handler = registry.getCommand(SharedConstants.Commands.Emulator.PromptToOpenTranscript);
const remoteCallSpy = jest.spyOn(commandService, 'remoteCall').mockResolvedValue('transcript.transcript');
const callSpy = jest.spyOn(commandService, 'call').mockResolvedValue(null);

it('should spawn a notification if prompting to open a transcript fails', async () => {
const remoteCallSpy = jest
.spyOn(commandService, 'remoteCall')
.mockRejectedValueOnce(new Error('Something went wrong.'));
const handler = registry.getCommand(Emulator.PromptToOpenTranscript);
await handler();

expect(remoteCallSpy).toHaveBeenCalledWith('shell:showExplorer-open-dialog', {
buttonLabel: 'Choose file',
filters: [{ extensions: ['transcript'], name: 'Transcript Files' }],
properties: ['openFile'],
title: 'Open transcript file',
});
expect(remoteCallSpy).toHaveBeenCalledWith(SharedConstants.Commands.Telemetry.TrackEvent, 'transcriptFile_open', {
method: 'file_menu',
expect(mockStore.dispatch).toHaveBeenCalledWith({
...beginAdd(undefined),
payload: jasmine.any(Object),
});

expect(callSpy).toHaveBeenCalledWith('transcript:open', 'transcript.transcript');
remoteCallSpy.mockClear();
});

it('should dispatch a notification when opening a transcript fails', async () => {
const handler = registry.getCommand(SharedConstants.Commands.Emulator.PromptToOpenTranscript);
const remoteCallSpy = jest.spyOn(commandService, 'remoteCall').mockResolvedValue('transcript.transcript');
const callSpy = jest.spyOn(commandService, 'call').mockImplementationOnce(() => {
throw new Error('Oh noes!');
});
const dispatchSpy = jest.spyOn(mockStore, 'dispatch');
const errMsg = `Error while opening transcript file: Error: Oh noes!`;
const notification = newNotification(errMsg);
const action = beginAdd(notification);
action.payload.notification.timestamp = jasmine.any(Number) as any;
action.payload.notification.id = jasmine.any(String) as any;
await handler();
expect(remoteCallSpy).toHaveBeenCalled();
expect(callSpy).toHaveBeenCalledWith('transcript:open', 'transcript.transcript');
expect(dispatchSpy).toHaveBeenCalledWith(action);
jest.restoreAllMocks();
});

it('should reload a transcript', async () => {
const openTranscriptHandler = registry.getCommand(SharedConstants.Commands.Emulator.OpenTranscript);
await openTranscriptHandler('transcript.transcript');
let state = mockStore.getState();
expect(state.chat.changeKey).toBe(1);
const handler = registry.getCommand(SharedConstants.Commands.Emulator.ReloadTranscript);
await handler('transcript.transcript');
state = mockStore.getState();
expect(state.chat.changeKey).toBe(3);
});
it('should reload a transcript', () => {
const mockFilePath = '/dir/test.transcript';
const mockFilename = 'test.transcript';
mockState = {
editor: {
editors: {
[SharedConstants.EDITOR_KEY_PRIMARY]: {
documents: {
[mockFilePath]: {},
},
},
},
},
};
const handler = registry.getCommand(Emulator.ReloadTranscript);
handler(mockFilePath, mockFilename);

it('should open a chat file', async () => {
const callSpy = jest.spyOn(commandService, 'call').mockResolvedValue(true);
const remoteCallSpy = jest.spyOn(commandService, 'remoteCall').mockResolvedValue(true);

const openChatFileHandler = registry.getCommand(SharedConstants.Commands.Emulator.OpenChatFile);
await openChatFileHandler('some/path.chat', true);
expect(remoteCallSpy).toHaveBeenCalledWith(SharedConstants.Commands.Emulator.OpenChatFile, 'some/path.chat');
expect(callSpy).toHaveBeenCalledWith(
SharedConstants.Commands.Emulator.ReloadTranscript,
'some/path.chat',
undefined,
{
activities: undefined,
inMemory: true,
}
);
expect(mockStore.dispatch).toHaveBeenCalledWith(close(SharedConstants.EDITOR_KEY_PRIMARY, mockFilePath));
expect(mockStore.dispatch).toHaveBeenCalledWith(closeDocument(mockFilePath));
expect(mockStore.dispatch).toHaveBeenCalledWith(openTranscript(mockFilename));
});
});
Loading