diff --git a/.vscode/launch.json b/.vscode/launch.json index b0fba2413..6d29c9e14 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -33,7 +33,7 @@ "outputCapture": "std", "internalConsoleOptions": "openOnSessionStart", "cwd": "${workspaceFolder}/packages/app/main", - "outFiles": [ "${workspaceRoot}/packages/app/main/app/server/*" ] + "outFiles": [ "${workspaceRoot}/packages/app/main/app/**/*.js"] }, { "type": "node", diff --git a/CHANGELOG.md b/CHANGELOG.md index c13eda712..2781711c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased +- [client/main] Added UI to open bot dialog that allows the user to set transcript testing properties `randomSeed` and `randomValue` in PR [2209](https://github.com/microsoft/BotFramework-Emulator/pull/2209) + ## v4.11.0 - 2020 - 11 - 05 - [client] Moved from master to main as the default branch. [2194](https://github.com/microsoft/BotFramework-Emulator/pull/2194) - [client] Updated support for new activity middleware contract provided by Web Chat. [2202](https://github.com/microsoft/BotFramework-Emulator/pull/2202) diff --git a/packages/app/client/src/state/sagas/botSagas.ts b/packages/app/client/src/state/sagas/botSagas.ts index 802c8b249..f8fe19977 100644 --- a/packages/app/client/src/state/sagas/botSagas.ts +++ b/packages/app/client/src/state/sagas/botSagas.ts @@ -130,6 +130,8 @@ export class BotSagas { mode: action.payload.mode, msaAppId: action.payload.appId, msaPassword: action.payload.appPassword, + randomSeed: action.payload.randomSeed, + randomValue: action.payload.randomValue, speechKey: action.payload.speechKey, speechRegion: action.payload.speechRegion, user, @@ -153,7 +155,13 @@ export class BotSagas { // send CU or debug INSPECT message if (!(action.payload.speechKey && action.payload.speechRegion)) { - res = yield ChatSagas.sendInitialActivity({ conversationId, members, mode: action.payload.mode }); + res = yield ChatSagas.sendInitialActivities({ + conversationId, + members, + mode: action.payload.mode, + randomSeed: action.payload.randomSeed, + randomValue: action.payload.randomValue, + }); if (!res.ok) { yield* throwErrorFromResponse('Error occurred while sending the initial activity', res); } diff --git a/packages/app/client/src/state/sagas/chatSagas.spec.ts b/packages/app/client/src/state/sagas/chatSagas.spec.ts index 0684d9468..0f7e02388 100644 --- a/packages/app/client/src/state/sagas/chatSagas.spec.ts +++ b/packages/app/client/src/state/sagas/chatSagas.spec.ts @@ -754,9 +754,9 @@ describe('The ChatSagas,', () => { ) ); - // call sendInitialActivity + // call sendInitialActivities expect(gen.next({ ok: true }).value).toEqual( - call([ChatSagas, ChatSagas.sendInitialActivity], { conversationId, members: json.members, mode: chat.mode }) + call([ChatSagas, ChatSagas.sendInitialActivities], { conversationId, members: json.members, mode: chat.mode }) ); // put updatePendingSpeechTokenRetrieval @@ -889,9 +889,9 @@ describe('The ChatSagas,', () => { ) ); - // call sendInitialActivity + // call sendInitialActivities expect(gen.next({ ok: true }).value).toEqual( - call([ChatSagas, ChatSagas.sendInitialActivity], { conversationId, members: json.members, mode: chat.mode }) + call([ChatSagas, ChatSagas.sendInitialActivities], { conversationId, members: json.members, mode: chat.mode }) ); // put updatePendingSpeechTokenRetrieval @@ -1114,13 +1114,41 @@ describe('The ChatSagas,', () => { }); }); + it('should send a SetTestOptions event activity for non-debug mode conversations', () => { + const payload = { + conversationId: 'someConvoId', + members: [], + mode: 'livechat' as EmulatorMode, + randomSeed: 123, + randomValue: 456, + }; + const gen = ChatSagas.sendInitialActivities(payload); + + // select server url + expect(gen.next().value).toEqual(select(getServerUrl)); + + // call sendActivityToBot + const serverUrl = 'http://localhost:58267'; + const activity = { + name: 'SetTestOptions', + type: 'event', + value: { + randomSeed: payload.randomSeed, + randomValue: payload.randomValue, + }, + }; + expect(gen.next(serverUrl).value).toEqual( + call([ConversationService, ConversationService.sendActivityToBot], serverUrl, payload.conversationId, activity) + ); + }); + it('should send a conversation update for non-debug mode conversations', () => { const payload = { conversationId: 'someConvoId', members: [], - mode: 'livechat', + mode: 'livechat' as EmulatorMode, }; - const gen = ChatSagas.sendInitialActivity(payload); + const gen = ChatSagas.sendInitialActivities(payload); // select server url expect(gen.next().value).toEqual(select(getServerUrl)); @@ -1141,9 +1169,9 @@ describe('The ChatSagas,', () => { const payload = { conversationId: 'someConvoId', members: [], - mode: 'debug', + mode: 'debug' as EmulatorMode, }; - const gen = ChatSagas.sendInitialActivity(payload); + const gen = ChatSagas.sendInitialActivities(payload); // select server url expect(gen.next().value).toEqual(select(getServerUrl)); diff --git a/packages/app/client/src/state/sagas/chatSagas.ts b/packages/app/client/src/state/sagas/chatSagas.ts index d0a8ce696..e5feb7943 100644 --- a/packages/app/client/src/state/sagas/chatSagas.ts +++ b/packages/app/client/src/state/sagas/chatSagas.ts @@ -184,11 +184,21 @@ interface BootstrapChatPayload { mode: EmulatorMode; msaAppId?: string; msaPassword?: string; + randomSeed?: number; + randomValue?: number; speechKey?: string; speechRegion?: string; user: User; } +interface InitialActivitiesPayload { + conversationId: string; + members: any[]; + mode: EmulatorMode; + randomSeed?: number; + randomValue?: number; +} + export class ChatSagas { @CommandServiceInstance() private static commandService: CommandServiceImpl; @@ -386,6 +396,8 @@ export class ChatSagas { msaAppId, msaPassword, user, + randomSeed, + randomValue, speechKey, speechRegion, } = payload; @@ -417,6 +429,8 @@ export class ChatSagas { conversationId, directLine, userId: user.id, + randomSeed, + randomValue, speechKey, speechRegion, }) @@ -570,6 +584,8 @@ export class ChatSagas { newChat(documentId, chat.mode, { conversationId, directLine, + randomSeed: chat.randomSeed, + randomValue: chat.randomValue, speechKey: chat.speechKey, speechRegion: chat.speechRegion, userId, @@ -590,7 +606,13 @@ export class ChatSagas { // send CU or /INSPECT open (DL Speech will do this automatically) if (!isDLSpeechBot) { - res = yield call([ChatSagas, ChatSagas.sendInitialActivity], { conversationId, members, mode: chat.mode }); + res = yield call([ChatSagas, ChatSagas.sendInitialActivities], { + conversationId, + members, + mode: chat.mode, + randomSeed: chat.randomSeed, + randomValue: chat.randomValue, + }); if (!res.ok) { yield* throwErrorFromResponse('Error occurred while sending the initial activity', res); } @@ -644,8 +666,28 @@ export class ChatSagas { } } - public static *sendInitialActivity(payload: any): Iterator { - const { conversationId, members, mode } = payload; + public static *sendInitialActivities(payload: InitialActivitiesPayload): Iterator { + const { conversationId, members, mode, randomSeed, randomValue } = payload; + const serverUrl = yield select(getServerUrl); + + // if we have a randomSeed and / or randomValue set via the UI, we need to send + // a custom event to the bot containing these values to support transcript testing + if (randomSeed !== undefined || randomValue !== undefined) { + const testOptionsActivity = { + type: 'event', + name: 'SetTestOptions', + value: { + randomSeed, + randomValue, + }, + }; + yield call( + [ConversationService, ConversationService.sendActivityToBot], + serverUrl, + conversationId, + testOptionsActivity + ); + } let activity; if (mode === 'debug') { @@ -660,7 +702,6 @@ export class ChatSagas { membersRemoved: [], }; } - const serverUrl = yield select(getServerUrl); return yield call( [ConversationService, ConversationService.sendActivityToBot], serverUrl, diff --git a/packages/app/client/src/ui/dialogs/openBotDialog/openBotDialog.tsx b/packages/app/client/src/ui/dialogs/openBotDialog/openBotDialog.tsx index efe44e17a..87e1c1055 100644 --- a/packages/app/client/src/ui/dialogs/openBotDialog/openBotDialog.tsx +++ b/packages/app/client/src/ui/dialogs/openBotDialog/openBotDialog.tsx @@ -72,6 +72,8 @@ export interface OpenBotDialogState { appId?: string; appPassword?: string; isAzureGov?: boolean; + randomSeed?: number; + randomValue?: number; speechKey?: string; speechRegion?: string; } @@ -125,7 +127,18 @@ export class OpenBotDialog extends Component )} + + + + void): OpenBotDialogPr botUrl = '', mode = 'livechat-url', isAzureGov, + randomSeed, + randomValue, speechKey = '', speechRegion = '', } = componentState; @@ -64,6 +66,8 @@ const mapDispatchToProps = (dispatch: (action: Action) => void): OpenBotDialogPr endpoint: botUrl, mode, channelService: isAzureGov ? 'azureusgovernment' : 'public', + randomSeed: Number(randomSeed) || undefined, + randomValue: Number(randomValue) || undefined, speechKey, speechRegion, }) diff --git a/packages/app/shared/src/state/reducers/chat.ts b/packages/app/shared/src/state/reducers/chat.ts index b8bd774a9..1ca85b6fc 100644 --- a/packages/app/shared/src/state/reducers/chat.ts +++ b/packages/app/shared/src/state/reducers/chat.ts @@ -77,6 +77,8 @@ export interface ChatDocument extends Document { inspectorObjects: I[]; log: ChatLog; pendingSpeechTokenRetrieval: boolean; + randomSeed: number; + randomValue: number; speechKey: string; speechRegion: string; ui: DocumentUI; diff --git a/packages/sdk/shared/src/types/activity/startConversationParams.ts b/packages/sdk/shared/src/types/activity/startConversationParams.ts index 3f0248578..4944c8460 100644 --- a/packages/sdk/shared/src/types/activity/startConversationParams.ts +++ b/packages/sdk/shared/src/types/activity/startConversationParams.ts @@ -44,6 +44,8 @@ export interface StartConversationParams extends ConversationParameters { mode?: EmulatorMode; channelService?: ChannelService; conversationId?: string; + randomSeed?: number; + randomValue?: number; speechKey?: string; speechRegion?: string; }