Skip to content
This repository was archived by the owner on Jan 5, 2026. It is now read-only.

Commit 36b01c0

Browse files
authored
[#2042] Changed convo infrastructure to use Web Sockets. (#2034)
* Changed conversation infra to use Web Sockets. * Fixed e2e tests. * Addressed PR feedback.
1 parent cea9a86 commit 36b01c0

114 files changed

Lines changed: 5129 additions & 4985 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## Unreleased
88

9+
## Added
10+
- [client/main] Added Ngrok Debugger UI in PR [2032](https://github.com/microsoft/BotFramework-Emulator/pull/2032)
11+
- [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)
12+
913
## v4.7.0 - 2019 - 12 - 13
1014
## Fixed
1115
- [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)

package-lock.json

Lines changed: 1536 additions & 1141 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/app/client/src/commands/emulatorCommands.spec.ts

Lines changed: 68 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -30,27 +30,22 @@
3030
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
3131
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3232
//
33-
import { newNotification, SharedConstants } from '@bfemulator/app-shared';
34-
import { combineReducers, createStore } from 'redux';
33+
34+
import { SharedConstants } from '@bfemulator/app-shared';
3535
import { CommandRegistry, CommandServiceImpl, CommandServiceInstance } from '@bfemulator/sdk-shared';
3636

37-
import { clientAwareSettingsChanged } from '../state/actions/clientAwareSettingsActions';
3837
import { beginAdd } from '../state/actions/notificationActions';
39-
import { bot } from '../state/reducers/bot';
40-
import { chat } from '../state/reducers/chat';
41-
import { clientAwareSettings } from '../state/reducers/clientAwareSettings';
42-
import { editor } from '../state/reducers/editor';
43-
import { framework } from '../state/reducers/framework';
44-
import { RootState } from '../state/store';
45-
import { setFrameworkSettings } from '../state/actions/frameworkSettingsActions';
38+
import { close } from '../state/actions/editorActions';
39+
import { openTranscript, closeDocument } from '../state/actions/chatActions';
40+
import { openBotViaUrlAction } from '../state';
4641

4742
import { EmulatorCommands } from './emulatorCommands';
4843

49-
const mockEndpoint = {
50-
endpoint: 'https://localhost:8080/api/messages',
44+
let mockState = {};
45+
const mockStore = {
46+
dispatch: jest.fn(),
47+
getState: () => mockState,
5148
};
52-
53-
let mockStore;
5449
jest.mock('../state/store', () => ({
5550
get store() {
5651
return mockStore;
@@ -85,6 +80,8 @@ jest.mock('electron', () => ({
8580
describe('The emulator commands', () => {
8681
let commandService: CommandServiceImpl;
8782
let registry: CommandRegistry;
83+
const { Emulator } = SharedConstants.Commands;
84+
8885
beforeAll(() => {
8986
new EmulatorCommands();
9087
const decorator = CommandServiceInstance();
@@ -94,129 +91,75 @@ describe('The emulator commands', () => {
9491
});
9592

9693
beforeEach(() => {
97-
mockStore = createStore(combineReducers({ bot, chat, clientAwareSettings, editor, framework }));
98-
mockStore.dispatch(
99-
clientAwareSettingsChanged({
100-
users: { currentUserId: '1234' },
101-
cwd: 'path',
102-
locale: 'en-us',
103-
serverUrl: 'https://localhost',
104-
debugMode: 1,
105-
appPath: '',
106-
savedBotUrls: [],
107-
})
108-
);
109-
});
110-
111-
it('Should open a new emulator tabbed document for an endpoint', () => {
112-
const handler = registry.getCommand(SharedConstants.Commands.Emulator.NewLiveChat);
113-
const documentId = handler(mockEndpoint, false);
114-
const state: RootState = mockStore.getState();
115-
const documentIds = Object.keys(state.chat.chats);
116-
const document = state.chat.chats[documentId];
117-
expect(document.userId).toEqual('1234');
118-
expect(documentIds.length).toBe(1);
119-
expect(state.editor.editors.primary.activeDocumentId).toBe(documentId);
120-
});
121-
122-
it('should open a new emulator tabbed document for an endpoint and use the custom user id', () => {
123-
let state: RootState = mockStore.getState();
124-
mockStore.dispatch(setFrameworkSettings({ ...state.framework, userGUID: 'customUserId' }));
125-
const handler = registry.getCommand(SharedConstants.Commands.Emulator.NewLiveChat);
126-
const documentId = handler(mockEndpoint, false);
127-
state = mockStore.getState();
128-
const document = state.chat.chats[documentId];
129-
expect(document.userId).toEqual('customUserId');
94+
mockState = {};
95+
mockStore.dispatch.mockClear();
13096
});
13197

132-
it('should set the active tab of an existing chat', () => {
133-
const handler = registry.getCommand(SharedConstants.Commands.Emulator.NewLiveChat);
134-
const documentId = handler(mockEndpoint, false);
135-
const secondDocumentId = handler({
136-
endpoint: 'https://localhost:8181/api/messages',
137-
});
138-
// At this point we should have 2 open documents
139-
// with the second on
140-
expect(mockStore.getState().editor.editors.primary.activeDocumentId).toBe(secondDocumentId);
141-
handler(mockEndpoint, true); // re-open the original document
142-
expect(mockStore.getState().editor.editors.primary.activeDocumentId).toBe(documentId);
98+
it('should open a new live chat', () => {
99+
const mockEndpoint: any = {
100+
appId: 'someAppId',
101+
appPassword: 'someAppPw',
102+
channelService: 'someChannelService',
103+
endpoint: 'http://localhost:3978/api/messages',
104+
};
105+
const handler = registry.getCommand(Emulator.NewLiveChat);
106+
handler(mockEndpoint, 'livechat');
107+
108+
expect(mockStore.dispatch).toHaveBeenCalledWith(
109+
openBotViaUrlAction({
110+
appId: 'someAppId',
111+
appPassword: 'someAppPw',
112+
channelService: 'someChannelService' as any,
113+
endpoint: 'http://localhost:3978/api/messages',
114+
isFromBotFile: true,
115+
mode: 'livechat',
116+
})
117+
);
143118
});
144119

145-
it('should open a transcript', () => {
146-
const handler = registry.getCommand(SharedConstants.Commands.Emulator.OpenTranscript);
147-
const filePath = 'transcript.transcript';
148-
handler(filePath, filePath);
120+
it('should prompt to open a transcript', async () => {
121+
const mockFileName = 'test.transcript';
122+
const remoteCallSpy = jest.spyOn(commandService, 'remoteCall').mockResolvedValueOnce(mockFileName);
123+
const handler = registry.getCommand(Emulator.PromptToOpenTranscript);
124+
await handler();
149125

150-
const state = mockStore.getState();
151-
expect(state.chat.chats[filePath]).toBeTruthy();
152-
expect(state.editor.editors.primary.activeDocumentId).toBe(filePath);
126+
expect(mockStore.dispatch).toHaveBeenCalledWith(openTranscript(mockFileName));
127+
remoteCallSpy.mockClear();
153128
});
154129

155-
it('Should prompt to open a transcript', async () => {
156-
const handler = registry.getCommand(SharedConstants.Commands.Emulator.PromptToOpenTranscript);
157-
const remoteCallSpy = jest.spyOn(commandService, 'remoteCall').mockResolvedValue('transcript.transcript');
158-
const callSpy = jest.spyOn(commandService, 'call').mockResolvedValue(null);
159-
130+
it('should spawn a notification if prompting to open a transcript fails', async () => {
131+
const remoteCallSpy = jest
132+
.spyOn(commandService, 'remoteCall')
133+
.mockRejectedValueOnce(new Error('Something went wrong.'));
134+
const handler = registry.getCommand(Emulator.PromptToOpenTranscript);
160135
await handler();
161136

162-
expect(remoteCallSpy).toHaveBeenCalledWith('shell:showExplorer-open-dialog', {
163-
buttonLabel: 'Choose file',
164-
filters: [{ extensions: ['transcript'], name: 'Transcript Files' }],
165-
properties: ['openFile'],
166-
title: 'Open transcript file',
167-
});
168-
expect(remoteCallSpy).toHaveBeenCalledWith(SharedConstants.Commands.Telemetry.TrackEvent, 'transcriptFile_open', {
169-
method: 'file_menu',
137+
expect(mockStore.dispatch).toHaveBeenCalledWith({
138+
...beginAdd(undefined),
139+
payload: jasmine.any(Object),
170140
});
171-
172-
expect(callSpy).toHaveBeenCalledWith('transcript:open', 'transcript.transcript');
141+
remoteCallSpy.mockClear();
173142
});
174143

175-
it('should dispatch a notification when opening a transcript fails', async () => {
176-
const handler = registry.getCommand(SharedConstants.Commands.Emulator.PromptToOpenTranscript);
177-
const remoteCallSpy = jest.spyOn(commandService, 'remoteCall').mockResolvedValue('transcript.transcript');
178-
const callSpy = jest.spyOn(commandService, 'call').mockImplementationOnce(() => {
179-
throw new Error('Oh noes!');
180-
});
181-
const dispatchSpy = jest.spyOn(mockStore, 'dispatch');
182-
const errMsg = `Error while opening transcript file: Error: Oh noes!`;
183-
const notification = newNotification(errMsg);
184-
const action = beginAdd(notification);
185-
action.payload.notification.timestamp = jasmine.any(Number) as any;
186-
action.payload.notification.id = jasmine.any(String) as any;
187-
await handler();
188-
expect(remoteCallSpy).toHaveBeenCalled();
189-
expect(callSpy).toHaveBeenCalledWith('transcript:open', 'transcript.transcript');
190-
expect(dispatchSpy).toHaveBeenCalledWith(action);
191-
jest.restoreAllMocks();
192-
});
193-
194-
it('should reload a transcript', async () => {
195-
const openTranscriptHandler = registry.getCommand(SharedConstants.Commands.Emulator.OpenTranscript);
196-
await openTranscriptHandler('transcript.transcript');
197-
let state = mockStore.getState();
198-
expect(state.chat.changeKey).toBe(1);
199-
const handler = registry.getCommand(SharedConstants.Commands.Emulator.ReloadTranscript);
200-
await handler('transcript.transcript');
201-
state = mockStore.getState();
202-
expect(state.chat.changeKey).toBe(3);
203-
});
144+
it('should reload a transcript', () => {
145+
const mockFilePath = '/dir/test.transcript';
146+
const mockFilename = 'test.transcript';
147+
mockState = {
148+
editor: {
149+
editors: {
150+
[SharedConstants.EDITOR_KEY_PRIMARY]: {
151+
documents: {
152+
[mockFilePath]: {},
153+
},
154+
},
155+
},
156+
},
157+
};
158+
const handler = registry.getCommand(Emulator.ReloadTranscript);
159+
handler(mockFilePath, mockFilename);
204160

205-
it('should open a chat file', async () => {
206-
const callSpy = jest.spyOn(commandService, 'call').mockResolvedValue(true);
207-
const remoteCallSpy = jest.spyOn(commandService, 'remoteCall').mockResolvedValue(true);
208-
209-
const openChatFileHandler = registry.getCommand(SharedConstants.Commands.Emulator.OpenChatFile);
210-
await openChatFileHandler('some/path.chat', true);
211-
expect(remoteCallSpy).toHaveBeenCalledWith(SharedConstants.Commands.Emulator.OpenChatFile, 'some/path.chat');
212-
expect(callSpy).toHaveBeenCalledWith(
213-
SharedConstants.Commands.Emulator.ReloadTranscript,
214-
'some/path.chat',
215-
undefined,
216-
{
217-
activities: undefined,
218-
inMemory: true,
219-
}
220-
);
161+
expect(mockStore.dispatch).toHaveBeenCalledWith(close(SharedConstants.EDITOR_KEY_PRIMARY, mockFilePath));
162+
expect(mockStore.dispatch).toHaveBeenCalledWith(closeDocument(mockFilePath));
163+
expect(mockStore.dispatch).toHaveBeenCalledWith(openTranscript(mockFilename));
221164
});
222165
});

0 commit comments

Comments
 (0)