-
Notifications
You must be signed in to change notification settings - Fork 281
/
Copy pathdialogTestClient.ts
182 lines (166 loc) · 7.47 KB
/
dialogTestClient.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
/**
* @module botbuilder-testing
*/
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import {
Activity,
TestAdapter,
Middleware,
ConversationState,
MemoryStorage,
AutoSaveStateMiddleware,
TurnContext,
StatePropertyAccessor,
} from 'botbuilder-core';
import { Dialog, DialogContext, DialogSet, DialogTurnStatus, DialogTurnResult, DialogState } from 'botbuilder-dialogs';
/**
* A client for testing dialogs in isolation.
*/
export class DialogTestClient {
private readonly _callback: (turnContext: TurnContext) => Promise<void>;
private _dialogContext: DialogContext = null;
private readonly _testAdapter: TestAdapter;
/**
* A DialogTurnResult instance with the result of the last turn.
*/
dialogTurnResult: DialogTurnResult;
/**
* A ConversationState instance for the current test client.
*/
conversationState: ConversationState;
/**
* Creates a [DialogTestClient](xref:botbuilder-testing.DialogTestClient) to test a [Dialog](xref:botbuilder-dialogs.Dialog) without having to create a full-fledged adapter.
* ```javascript
* let client = new DialogTestClient('test', MY_DIALOG, MY_OPTIONS);
* let reply = await client.sendActivity('first message');
* assert.strictEqual(reply.text, 'first reply', 'reply failed');
* ```
*
* @param channelId The `channelId` to be used for the test.
* Use 'emulator' or 'test' if you are uncertain of the channel you are targeting.
* Otherwise, it is recommended that you use the id for the channel(s) your bot will be using and write a test case for each channel.
* @param targetDialog The [Dialog](xref:botbuilder-dialogs.Dialog) to be tested. This will be the root dialog for the test client.
* @param initialDialogOptions Optional. Additional argument(s) to pass to the [Dialog](xref:botbuilder-dialogs.Dialog) being started.
* @param middlewares Optional. A [Middleware](xref:botbuilder-core.Middleware) list to be added to the test adapter.
* @param conversationState Optional. A [ConversationState](xref:botbuilder-core.ConversationState) instance to use in the test client.
*/
constructor(
channelId: string,
targetDialog: Dialog,
initialDialogOptions?: unknown,
middlewares?: Middleware[],
conversationState?: ConversationState,
);
/**
* Creates a [DialogTestClient](xref:botbuilder-testing.DialogTestClient) to test a [Dialog](xref:botbuilder-dialogs.Dialog) without having to create a full-fledged adapter.
* ```javascript
* let client = new DialogTestClient(MY_DIALOG, MY_OPTIONS);
* let reply = await client.sendActivity('first message');
* assert.strictEqual(reply.text, 'first reply', 'reply failed');
* ```
*
* @param testAdapter The [TestAdapter](xref:botbuilder-core.TestAdapter) to use.
* @param targetDialog The [Dialog](xref:botbuilder-dialogs.Dialog) to be tested. This will be the root dialog for the test client.
* @param initialDialogOptions Optional. Additional argument(s) to pass to the [Dialog](xref:botbuilder-dialogs.Dialog) being started.
* @param middlewares Optional. A [Middleware](xref:botbuilder-core.Middleware) list to be added to the test adapter.
* @param conversationState Optional. A [ConversationState](xref:botbuilder-core.ConversationState) instance to use in the test client.
*/
constructor(
testAdapter: TestAdapter,
targetDialog: Dialog,
initialDialogOptions?: unknown,
middlewares?: Middleware[],
conversationState?: ConversationState,
);
/**
* Creates a [DialogTestClient](xref:botbuilder-testing.DialogTestClient) to test a [Dialog](xref:botbuilder-dialogs.Dialog) without having to create a full-fledged adapter.
*
* @param channelOrAdapter The `channelId` or the [TestAdapter](xref:botbuilder-core.TestAdapter) to be used for the test.
* @param targetDialog The [Dialog](xref:botbuilder-dialogs.Dialog) to be tested. This will be the root dialog for the test client.
* @param initialDialogOptions Optional. Additional argument(s) to pass to the [Dialog](xref:botbuilder-dialogs.Dialog) being started.
* @param middlewares Optional. A [Middleware](xref:botbuilder-core.Middleware) list to be added to the test adapter.
* @param conversationState Optional. A [ConversationState](xref:botbuilder-core.ConversationState) instance to use in the test client.
*/
constructor(
channelOrAdapter: string | TestAdapter,
targetDialog: Dialog,
initialDialogOptions?: object,
middlewares?: Middleware[],
conversationState?: ConversationState,
) {
this.conversationState = conversationState || new ConversationState(new MemoryStorage());
const dialogState = this.conversationState.createProperty('DialogState');
this._callback = this.getDefaultCallback(targetDialog, initialDialogOptions || null, dialogState);
if (typeof channelOrAdapter == 'string') {
const channelIdToUse: string = channelOrAdapter;
this._testAdapter = new TestAdapter(this._callback, { channelId: channelIdToUse }).use(
new AutoSaveStateMiddleware(this.conversationState),
);
} else {
const testAdapterToUse: TestAdapter = channelOrAdapter;
this._testAdapter = testAdapterToUse;
}
this.addUserMiddlewares(middlewares);
}
/**
* Gets a reference for the DialogContext.
*
* @remarks
* This property will be null until at least one activity is sent to DialogTestClient.
*
* @returns the dialog context
*/
get dialogContext(): DialogContext {
return this._dialogContext;
}
/**
* Send an activity into the dialog.
*
* ```javascript
* await DialogTest.send('hello').assertReply('hello yourself').startTest();
* ```
*
* @param activity an activity potentially with text
* @returns a TestFlow that can be used to assert replies etc
*/
async sendActivity(activity: Partial<Activity> | string): Promise<any> {
await this._testAdapter.receiveActivity(activity);
return this._testAdapter.activityBuffer.shift();
}
/**
* Get the next reply waiting to be delivered (if one exists)
*
* @returns the next reply
*/
getNextReply(): Partial<Activity> {
return this._testAdapter.activityBuffer.shift();
}
private getDefaultCallback(
targetDialog: Dialog,
initialDialogOptions: object,
dialogState: StatePropertyAccessor<DialogState>,
): (turnContext: TurnContext) => Promise<void> {
return async (turnContext: TurnContext): Promise<void> => {
const dialogSet = new DialogSet(dialogState);
dialogSet.add(targetDialog);
this._dialogContext = await dialogSet.createContext(turnContext);
this.dialogTurnResult = await this._dialogContext.continueDialog();
if (this.dialogTurnResult.status === DialogTurnStatus.empty) {
this.dialogTurnResult = await this._dialogContext.beginDialog(targetDialog.id, initialDialogOptions);
}
};
}
/**
* @private
*/
private addUserMiddlewares(middlewares: Middleware[]): void {
if (middlewares != null) {
middlewares.forEach((middleware): void => {
this._testAdapter.use(middleware);
});
}
}
}