botium-core 1.14.8 → 1.14.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -4
- package/dist/botium-cjs.js +72 -26
- package/dist/botium-cjs.js.map +1 -1
- package/dist/botium-es.js +72 -26
- package/dist/botium-es.js.map +1 -1
- package/package.json +2 -1
- package/src/scripting/Convo.js +43 -3
- package/src/scripting/ScriptingProvider.js +18 -11
- package/src/scripting/helper.js +4 -7
- package/src/scripting/logichook/LogicHookUtils.js +8 -10
- package/src/scripting/logichook/userinput/MediaInput.js +2 -2
- package/test/convo/convos/welcome_multiple_botsteps_opt.convo.txt +22 -0
- package/test/convo/transcript.spec.js +146 -0
- package/test/scripting/asserters/convoStepParameters.spec.js +6 -0
- package/test/scripting/asserters/convos/convo_step_parameter_optional_with_timeout.convo.txt +16 -0
- package/test/scripting/logichooks/localvsglobal.spec.js +105 -0
- package/test/scripting/txt/decompile.spec.js +0 -25
package/README.md
CHANGED
|
@@ -13,10 +13,6 @@
|
|
|
13
13
|
|
|
14
14
|
**_IF YOU LIKE WHAT YOU SEE, PLEASE CONSIDER GIVING US A STAR ON GITHUB!_**
|
|
15
15
|
|
|
16
|
-
**UPDATE 2020/11/05:** Botium has a FREE, hosted plan available! The new Botium Box Mini is our ❤️ to the community. [Take it for a test drive 🚗 ...](https://www.botium.ai/pricing/)
|
|
17
|
-
|
|
18
|
-
[](https://www.youtube.com/watch?v=ciVxojvRfng "Botium Box Mini")
|
|
19
|
-
|
|
20
16
|
## Getting Help
|
|
21
17
|
|
|
22
18
|
See our [Botium Forum](https://forum.botium.ai/)
|
package/dist/botium-cjs.js
CHANGED
|
@@ -77,7 +77,7 @@ var express__default = /*#__PURE__*/_interopDefaultLegacy(express);
|
|
|
77
77
|
var bodyParser__default = /*#__PURE__*/_interopDefaultLegacy(bodyParser);
|
|
78
78
|
|
|
79
79
|
var name = "botium-core";
|
|
80
|
-
var version$1 = "1.14.
|
|
80
|
+
var version$1 = "1.14.10";
|
|
81
81
|
var description = "The Selenium for Chatbots";
|
|
82
82
|
var main = "index.js";
|
|
83
83
|
var module$1 = "dist/botium-es.js";
|
|
@@ -140,6 +140,7 @@ var dependencies = {
|
|
|
140
140
|
"socketio-auth": "^0.1.1",
|
|
141
141
|
"swagger-jsdoc": "^6.2.8",
|
|
142
142
|
"swagger-ui-express": "^5.0.0",
|
|
143
|
+
tinyglobby: "^0.2.10",
|
|
143
144
|
uuid: "^9.0.1",
|
|
144
145
|
"word-error-rate": "0.0.7",
|
|
145
146
|
"write-yaml": "^1.0.0",
|
|
@@ -1124,9 +1125,9 @@ var LogicHookUtils_1 = class LogicHookUtils {
|
|
|
1124
1125
|
caps
|
|
1125
1126
|
}) {
|
|
1126
1127
|
this.asserters = {};
|
|
1127
|
-
this.
|
|
1128
|
+
this.globalAsserterNames = [];
|
|
1128
1129
|
this.logicHooks = {};
|
|
1129
|
-
this.
|
|
1130
|
+
this.globalLogicHookNames = [];
|
|
1130
1131
|
this.userInputs = {};
|
|
1131
1132
|
this.buildScriptContext = buildScriptContext;
|
|
1132
1133
|
this.caps = caps;
|
|
@@ -1159,7 +1160,7 @@ var LogicHookUtils_1 = class LogicHookUtils {
|
|
|
1159
1160
|
}
|
|
1160
1161
|
this.asserters[asserter.ref] = this._loadClass(asserter, 'asserter');
|
|
1161
1162
|
if (asserter.global) {
|
|
1162
|
-
this.
|
|
1163
|
+
this.globalAsserterNames.push(asserter.ref);
|
|
1163
1164
|
}
|
|
1164
1165
|
});
|
|
1165
1166
|
}
|
|
@@ -1170,7 +1171,7 @@ var LogicHookUtils_1 = class LogicHookUtils {
|
|
|
1170
1171
|
}
|
|
1171
1172
|
this.logicHooks[logicHook.ref] = this._loadClass(logicHook, 'logichook');
|
|
1172
1173
|
if (logicHook.global) {
|
|
1173
|
-
this.
|
|
1174
|
+
this.globalLogicHookNames.push(logicHook.ref);
|
|
1174
1175
|
}
|
|
1175
1176
|
});
|
|
1176
1177
|
}
|
|
@@ -1182,11 +1183,17 @@ var LogicHookUtils_1 = class LogicHookUtils {
|
|
|
1182
1183
|
this.userInputs[userInput.ref] = this._loadClass(userInput, 'userinput');
|
|
1183
1184
|
});
|
|
1184
1185
|
}
|
|
1185
|
-
|
|
1186
|
-
return this.
|
|
1186
|
+
getGlobalAsserters() {
|
|
1187
|
+
return this.globalAsserterNames.reduce((agg, name) => ({
|
|
1188
|
+
...agg,
|
|
1189
|
+
[name]: this.asserters[name]
|
|
1190
|
+
}), {});
|
|
1187
1191
|
}
|
|
1188
|
-
|
|
1189
|
-
return this.
|
|
1192
|
+
getGlobalLogicHooks() {
|
|
1193
|
+
return this.globalLogicHookNames.reduce((agg, name) => ({
|
|
1194
|
+
...agg,
|
|
1195
|
+
[name]: this.logicHooks[name]
|
|
1196
|
+
}), {});
|
|
1190
1197
|
}
|
|
1191
1198
|
_loadClass({
|
|
1192
1199
|
src,
|
|
@@ -1970,12 +1977,6 @@ const validateConvo$2 = convo => {
|
|
|
1970
1977
|
if (optionalSet.size > 1) {
|
|
1971
1978
|
validationResult.errors.push(new Error(`Step ${i + 1}: Failed to decompile conversation. Mixed optional flag is not allowed inside one step.`));
|
|
1972
1979
|
}
|
|
1973
|
-
if (optionalSet.size === 1 && optionalSet.has(true)) {
|
|
1974
|
-
const nextStep = convo.conversation[i + 1];
|
|
1975
|
-
if (!nextStep || nextStep.sender !== 'bot') {
|
|
1976
|
-
validationResult.errors.push(new Error(`Step ${i + 1}: Optional bot convo step has to be followed by a bot convo step.`));
|
|
1977
|
-
}
|
|
1978
|
-
}
|
|
1979
1980
|
}
|
|
1980
1981
|
if (!validateSender$1(step.sender)) {
|
|
1981
1982
|
validationResult.errors.push(new Error(`Step ${i + 1}: Sender #${step.sender} is invalid.`));
|
|
@@ -2019,6 +2020,10 @@ const convoStepToLines$2 = step => {
|
|
|
2019
2020
|
} else {
|
|
2020
2021
|
if (step.messageText) {
|
|
2021
2022
|
lines.push((step.optional ? '?' : '') + (step.not ? '!' : '') + step.messageText);
|
|
2023
|
+
} else {
|
|
2024
|
+
if (step.optional) {
|
|
2025
|
+
lines.push('?');
|
|
2026
|
+
}
|
|
2022
2027
|
}
|
|
2023
2028
|
if (step.buttons && step.buttons.length > 0) lines.push('BUTTONS' + _formatAppendArgs(step.buttons.filter(b => b.text).map(b => flatString(b.text))));
|
|
2024
2029
|
if (step.media && step.media.length > 0) lines.push('MEDIA' + _formatAppendArgs(step.media.filter(m => !m.buffer && m.mediaUri).map(m => m.mediaUri)));
|
|
@@ -2978,6 +2983,10 @@ class Convo$6 {
|
|
|
2978
2983
|
let skipTranscriptStep = false;
|
|
2979
2984
|
let conditionalGroupId = null;
|
|
2980
2985
|
let conditionMetInGroup = false;
|
|
2986
|
+
let skipOptionalStep = false;
|
|
2987
|
+
// If there are optional step(s) in the conversation, and the message from the bot fails on each optional bot step(s) and/or mandatory bot step, then we have an unexpected message.
|
|
2988
|
+
// So in this case an unexpected error should be shown instead of the latest assertion error.
|
|
2989
|
+
let optionalStepAssertionError = false;
|
|
2981
2990
|
let globalConvoStepParameters = container.caps[Capabilities.SCRIPTING_CONVO_STEP_PARAMETERS] || {};
|
|
2982
2991
|
let retryBotMessageTimeoutEnd = null;
|
|
2983
2992
|
let retryBotMessageConvoId = null;
|
|
@@ -2985,6 +2994,13 @@ class Convo$6 {
|
|
|
2985
2994
|
for (let i = 0; i < this.conversation.length; i = retryBotMessageDropBotResponse ? i : i + 1) {
|
|
2986
2995
|
retryBotMessageDropBotResponse = false;
|
|
2987
2996
|
const convoStep = this.conversation[i];
|
|
2997
|
+
if (!convoStep.optional) {
|
|
2998
|
+
skipOptionalStep = false;
|
|
2999
|
+
}
|
|
3000
|
+
if (convoStep.optional && skipOptionalStep) {
|
|
3001
|
+
// If there are multiple optional steps, and the previous optional step was timeout, then the next optional step should be skipped to prevent too long convo run with multiple timeout.
|
|
3002
|
+
continue;
|
|
3003
|
+
}
|
|
2988
3004
|
const rawConvoStepParameters = convoStep.logicHooks.find(lh => lh.name === 'CONVO_STEP_PARAMETERS')?.args;
|
|
2989
3005
|
let convoStepParameters = {};
|
|
2990
3006
|
if (rawConvoStepParameters && rawConvoStepParameters.length) {
|
|
@@ -3161,7 +3177,7 @@ class Convo$6 {
|
|
|
3161
3177
|
});
|
|
3162
3178
|
transcriptStep.botBegin = new Date();
|
|
3163
3179
|
if (!botMsg) {
|
|
3164
|
-
botMsg = await container.WaitBotSays(convoStep.channel);
|
|
3180
|
+
botMsg = await container.WaitBotSays(convoStep.channel, convoStepParameters?.stepTimeout);
|
|
3165
3181
|
}
|
|
3166
3182
|
transcriptStep.botEnd = new Date();
|
|
3167
3183
|
transcriptStep.actual = new BotiumMockMessage_1(botMsg);
|
|
@@ -3169,6 +3185,10 @@ class Convo$6 {
|
|
|
3169
3185
|
debug$k(`${this.header.name}: bot says (cleaned by binary and base64 data and sourceData) ${JSON.stringify(coreMsg, null, 2)}`);
|
|
3170
3186
|
} catch (err) {
|
|
3171
3187
|
transcriptStep.botEnd = new Date();
|
|
3188
|
+
if (!(err.message.indexOf('Bot did not respond within') < 0) && convoStep.optional) {
|
|
3189
|
+
skipOptionalStep = true;
|
|
3190
|
+
continue;
|
|
3191
|
+
}
|
|
3172
3192
|
const failErr = botiumErrorFromErr$1(`${this.header.name}/${convoStep.stepTag}: error waiting for bot - ${err.message}`, err);
|
|
3173
3193
|
debug$k(failErr);
|
|
3174
3194
|
try {
|
|
@@ -3250,6 +3270,7 @@ class Convo$6 {
|
|
|
3250
3270
|
}
|
|
3251
3271
|
waitForBotSays = false;
|
|
3252
3272
|
skipTranscriptStep = true;
|
|
3273
|
+
optionalStepAssertionError = true;
|
|
3253
3274
|
return true;
|
|
3254
3275
|
} else if (retryOn) {
|
|
3255
3276
|
if (!retryBotMessageTimeoutEnd || retryBotMessageConvoId !== convoStep.stepTag) {
|
|
@@ -3267,9 +3288,18 @@ class Convo$6 {
|
|
|
3267
3288
|
}
|
|
3268
3289
|
}
|
|
3269
3290
|
if (container.caps[Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS]) {
|
|
3270
|
-
|
|
3291
|
+
if (optionalStepAssertionError) {
|
|
3292
|
+
optionalStepAssertionError = false;
|
|
3293
|
+
assertErrors.push(new BotiumError$2(`${this.header.name}: Unexpected message.`));
|
|
3294
|
+
} else {
|
|
3295
|
+
assertErrors.push(err);
|
|
3296
|
+
}
|
|
3271
3297
|
return false;
|
|
3272
3298
|
} else {
|
|
3299
|
+
if (optionalStepAssertionError) {
|
|
3300
|
+
optionalStepAssertionError = false;
|
|
3301
|
+
throw new BotiumError$2(`${this.header.name}: Unexpected message.`);
|
|
3302
|
+
}
|
|
3273
3303
|
throw err;
|
|
3274
3304
|
}
|
|
3275
3305
|
};
|
|
@@ -3283,6 +3313,7 @@ class Convo$6 {
|
|
|
3283
3313
|
if (convoStep.not) {
|
|
3284
3314
|
try {
|
|
3285
3315
|
this.scriptingEvents.assertBotNotResponse(response, tomatch, `${this.header.name}/${convoStep.stepTag}`, lastMeConvoStep, convoStepParameters);
|
|
3316
|
+
optionalStepAssertionError = false;
|
|
3286
3317
|
} catch (err) {
|
|
3287
3318
|
if (isErrorHandledWithOptionConvoStep(err)) {
|
|
3288
3319
|
continue;
|
|
@@ -3291,6 +3322,7 @@ class Convo$6 {
|
|
|
3291
3322
|
} else {
|
|
3292
3323
|
try {
|
|
3293
3324
|
this.scriptingEvents.assertBotResponse(response, tomatch, `${this.header.name}/${convoStep.stepTag}`, lastMeConvoStep, convoStepParameters);
|
|
3325
|
+
optionalStepAssertionError = false;
|
|
3294
3326
|
} catch (err) {
|
|
3295
3327
|
if (isErrorHandledWithOptionConvoStep(err)) {
|
|
3296
3328
|
continue;
|
|
@@ -3300,6 +3332,7 @@ class Convo$6 {
|
|
|
3300
3332
|
} else if (convoStep.sourceData) {
|
|
3301
3333
|
try {
|
|
3302
3334
|
this._compareObject(container, scriptingMemory, convoStep, botMsg.sourceData, convoStep.sourceData, botMsg, convoStepParameters);
|
|
3335
|
+
optionalStepAssertionError = false;
|
|
3303
3336
|
} catch (err) {
|
|
3304
3337
|
if (isErrorHandledWithOptionConvoStep(err)) {
|
|
3305
3338
|
continue;
|
|
@@ -3326,11 +3359,13 @@ class Convo$6 {
|
|
|
3326
3359
|
transcript,
|
|
3327
3360
|
transcriptStep
|
|
3328
3361
|
});
|
|
3362
|
+
optionalStepAssertionError = false;
|
|
3329
3363
|
} catch (err) {
|
|
3330
3364
|
const nextConvoStep = this.conversation[i + 1];
|
|
3331
3365
|
if (convoStep.optional && nextConvoStep && nextConvoStep.sender === 'bot') {
|
|
3332
3366
|
waitForBotSays = false;
|
|
3333
3367
|
skipTranscriptStep = true;
|
|
3368
|
+
optionalStepAssertionError = true;
|
|
3334
3369
|
continue;
|
|
3335
3370
|
}
|
|
3336
3371
|
const errors = err.toArray ? err.toArray() : [];
|
|
@@ -3359,8 +3394,17 @@ class Convo$6 {
|
|
|
3359
3394
|
this.scriptingEvents.fail && this.scriptingEvents.fail(failErr, lastMeConvoStep);
|
|
3360
3395
|
} catch (failErr) {}
|
|
3361
3396
|
if (container.caps[Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS] && err instanceof BotiumError$2) {
|
|
3362
|
-
|
|
3397
|
+
if (optionalStepAssertionError) {
|
|
3398
|
+
optionalStepAssertionError = false;
|
|
3399
|
+
assertErrors.push(new BotiumError$2(`${this.header.name}: Unexpected message.`));
|
|
3400
|
+
} else {
|
|
3401
|
+
assertErrors.push(err);
|
|
3402
|
+
}
|
|
3363
3403
|
} else {
|
|
3404
|
+
if (optionalStepAssertionError) {
|
|
3405
|
+
optionalStepAssertionError = false;
|
|
3406
|
+
throw new BotiumError$2(`${this.header.name}: Unexpected message.`);
|
|
3407
|
+
}
|
|
3364
3408
|
throw failErr;
|
|
3365
3409
|
}
|
|
3366
3410
|
}
|
|
@@ -5561,9 +5605,9 @@ var ScriptingProvider_1 = class ScriptingProvider {
|
|
|
5561
5605
|
this.utterances = {};
|
|
5562
5606
|
this.matchFn = null;
|
|
5563
5607
|
this.asserters = {};
|
|
5564
|
-
this.
|
|
5608
|
+
this.globalAsserters = {};
|
|
5565
5609
|
this.logicHooks = {};
|
|
5566
|
-
this.
|
|
5610
|
+
this.globalLogicHooks = {};
|
|
5567
5611
|
this.userInputs = {};
|
|
5568
5612
|
this.partialConvos = {};
|
|
5569
5613
|
this.scriptingMemories = [];
|
|
@@ -5932,7 +5976,8 @@ var ScriptingProvider_1 = class ScriptingProvider {
|
|
|
5932
5976
|
return p(this.retryHelperAsserter, () => asserter[asserterType](params));
|
|
5933
5977
|
}
|
|
5934
5978
|
};
|
|
5935
|
-
const
|
|
5979
|
+
const localAsserters = (asserters || []).filter(a => this.asserters[a.name][asserterType]);
|
|
5980
|
+
const convoStepPromises = localAsserters.map(a => ({
|
|
5936
5981
|
asserter: a,
|
|
5937
5982
|
promise: callAsserter(a, this.asserters[a.name], {
|
|
5938
5983
|
convo,
|
|
@@ -5947,7 +5992,8 @@ var ScriptingProvider_1 = class ScriptingProvider {
|
|
|
5947
5992
|
promise,
|
|
5948
5993
|
asserter
|
|
5949
5994
|
}) => updateExceptionContext(promise, asserter));
|
|
5950
|
-
const
|
|
5995
|
+
const globalAsserters = Object.keys(this.globalAsserters).filter(name => localAsserters.map(a => a.name).indexOf(name) < 0).reduce((agg, name) => [...agg, this.globalAsserters[name]], []).filter(a => a[asserterType]);
|
|
5996
|
+
const globalPromises = globalAsserters.map(a => ({
|
|
5951
5997
|
asserter: a,
|
|
5952
5998
|
promise: p(this.retryHelperAsserter, () => a[asserterType]({
|
|
5953
5999
|
convo,
|
|
@@ -5962,7 +6008,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
|
|
|
5962
6008
|
promise,
|
|
5963
6009
|
asserter
|
|
5964
6010
|
}) => updateExceptionContext(promise, asserter));
|
|
5965
|
-
const allPromises = [...
|
|
6011
|
+
const allPromises = [...convoStepPromises, ...globalPromises];
|
|
5966
6012
|
if (this.caps[Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS]) {
|
|
5967
6013
|
return Promise.allSettled(allPromises).then(results => {
|
|
5968
6014
|
const rejected = results.filter(result => result.status === 'rejected').map(result => result.reason);
|
|
@@ -5997,7 +6043,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
|
|
|
5997
6043
|
isGlobal: false,
|
|
5998
6044
|
...rest
|
|
5999
6045
|
})));
|
|
6000
|
-
const globalHooks = Object.
|
|
6046
|
+
const globalHooks = Object.keys(this.globalLogicHooks).filter(name => localHooks.map(l => l.name).indexOf(name) < 0).reduce((agg, name) => [...agg, this.globalLogicHooks[name]], []).filter(l => l[hookType]);
|
|
6001
6047
|
const globalPromises = globalHooks.map(l => p(this.retryHelperLogicHook, () => l[hookType]({
|
|
6002
6048
|
convo,
|
|
6003
6049
|
convoStep,
|
|
@@ -6117,9 +6163,9 @@ var ScriptingProvider_1 = class ScriptingProvider {
|
|
|
6117
6163
|
caps: this.caps
|
|
6118
6164
|
});
|
|
6119
6165
|
this.asserters = logicHookUtils.asserters;
|
|
6120
|
-
this.
|
|
6166
|
+
this.globalAsserters = logicHookUtils.getGlobalAsserters();
|
|
6121
6167
|
this.logicHooks = logicHookUtils.logicHooks;
|
|
6122
|
-
this.
|
|
6168
|
+
this.globalLogicHooks = logicHookUtils.getGlobalLogicHooks();
|
|
6123
6169
|
this.userInputs = logicHookUtils.userInputs;
|
|
6124
6170
|
}
|
|
6125
6171
|
IsAsserterValid(name) {
|