botium-core 1.14.1 → 1.14.3

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.
Files changed (30) hide show
  1. package/dist/botium-cjs.js +268 -77
  2. package/dist/botium-cjs.js.map +1 -1
  3. package/dist/botium-es.js +268 -77
  4. package/dist/botium-es.js.map +1 -1
  5. package/package.json +11 -11
  6. package/src/Capabilities.js +2 -0
  7. package/src/scripting/BotiumError.js +40 -3
  8. package/src/scripting/Convo.js +113 -19
  9. package/src/scripting/ScriptingMemory.js +7 -0
  10. package/src/scripting/ScriptingProvider.js +79 -30
  11. package/src/scripting/logichook/LogicHookConsts.js +5 -2
  12. package/src/scripting/logichook/LogicHookUtils.js +8 -6
  13. package/src/scripting/logichook/logichooks/ConvoStepParametersLogicHook.js +6 -0
  14. package/src/scripting/logichook/logichooks/OrderedListToButtonLogicHook.js +37 -0
  15. package/test/convo/fillAndApplyScriptingMemory.spec.js +11 -0
  16. package/test/logichooks/orderedListToButton.spec.js +35 -0
  17. package/test/scripting/asserters/convoStepParameters.spec.js +140 -0
  18. package/test/scripting/asserters/convos/TEXT_GOOD.convo.txt +6 -0
  19. package/test/scripting/asserters/convos/convo_step_parameter_matchmode_failed.convo.txt +8 -0
  20. package/test/scripting/asserters/convos/convo_step_parameter_retry_asserters_all_good.convo.txt +9 -0
  21. package/test/scripting/asserters/convos/convo_step_parameter_retry_asserters_botium_timeout.convo.txt +9 -0
  22. package/test/scripting/asserters/convos/convo_step_parameter_retry_asserters_good.convo.txt +9 -0
  23. package/test/scripting/asserters/convos/convo_step_parameter_retry_asserters_good_global.convo.txt +9 -0
  24. package/test/scripting/asserters/convos/convo_step_parameter_retry_main_and_asserter.convo.txt +10 -0
  25. package/test/scripting/asserters/convos/convo_step_parameter_retry_main_botium_timeout.convo.txt +9 -0
  26. package/test/scripting/asserters/convos/convo_step_parameter_retry_main_but_no_button.convo.txt +10 -0
  27. package/test/scripting/asserters/convos/convo_step_parameter_retry_main_good.convo.txt +9 -0
  28. package/test/scripting/asserters/convos/convo_step_parameter_retry_main_good_begin.convo.txt +11 -0
  29. package/test/scripting/matching/matchingmode.spec.js +4 -1
  30. package/test/scripting/scriptingProvider.spec.js +38 -12
package/dist/botium-es.js CHANGED
@@ -35,7 +35,7 @@ import express from 'express';
35
35
  import bodyParser from 'body-parser';
36
36
 
37
37
  var name = "botium-core";
38
- var version$1 = "1.14.1";
38
+ var version$1 = "1.14.3";
39
39
  var description = "The Selenium for Chatbots";
40
40
  var main = "index.js";
41
41
  var module = "dist/botium-es.js";
@@ -66,12 +66,12 @@ var bugs = {
66
66
  };
67
67
  var homepage = "https://www.botium.ai";
68
68
  var dependencies = {
69
- "@babel/runtime": "^7.23.2",
69
+ "@babel/runtime": "^7.23.5",
70
70
  async: "^3.2.5",
71
71
  "body-parser": "^1.20.2",
72
72
  boolean: "^3.2.0",
73
73
  bottleneck: "^2.19.5",
74
- "csv-parse": "^5.5.2",
74
+ "csv-parse": "^5.5.3",
75
75
  debug: "^4.3.4",
76
76
  express: "^4.18.2",
77
77
  globby: "11.0.4",
@@ -80,7 +80,7 @@ var dependencies = {
80
80
  "is-json": "^2.0.1",
81
81
  jsonpath: "^1.1.1",
82
82
  lodash: "^4.17.21",
83
- "markdown-it": "^13.0.2",
83
+ "markdown-it": "^14.0.0",
84
84
  "mime-types": "^2.1.35",
85
85
  mkdirp: "^3.0.1",
86
86
  moment: "^2.29.4",
@@ -106,23 +106,23 @@ var dependencies = {
106
106
  yaml: "^2.3.4"
107
107
  };
108
108
  var devDependencies = {
109
- "@babel/core": "^7.23.3",
109
+ "@babel/core": "^7.23.5",
110
110
  "@babel/node": "^7.22.19",
111
- "@babel/plugin-transform-runtime": "^7.23.3",
112
- "@babel/preset-env": "^7.23.3",
111
+ "@babel/plugin-transform-runtime": "^7.23.4",
112
+ "@babel/preset-env": "^7.23.5",
113
113
  chai: "^4.3.10",
114
114
  "chai-as-promised": "^7.1.1",
115
115
  "cross-env": "^7.0.3",
116
- eslint: "^8.53.0",
116
+ eslint: "^8.55.0",
117
117
  "eslint-config-standard": "^17.1.0",
118
118
  "eslint-plugin-import": "^2.29.0",
119
119
  "eslint-plugin-mocha": "^10.2.0",
120
- "eslint-plugin-n": "^16.3.1",
120
+ "eslint-plugin-n": "^16.4.0",
121
121
  "eslint-plugin-promise": "^6.1.1",
122
122
  "eslint-plugin-standard": "^4.1.0",
123
123
  mocha: "^10.2.0",
124
- nock: "^13.3.8",
125
- "npm-check-updates": "^16.14.6",
124
+ nock: "^13.4.0",
125
+ "npm-check-updates": "^16.14.11",
126
126
  nyc: "^15.1.0",
127
127
  rollup: "2.79.1",
128
128
  "rollup-plugin-babel": "^4.4.0",
@@ -325,6 +325,8 @@ var Capabilities = {
325
325
  // varnames, testcasenames
326
326
  SCRIPTING_MEMORY_COLUMN_MODE: 'SCRIPTING_MEMORY_COLUMN_MODE',
327
327
  // Botium Lifecycle Hooks
328
+ SCRIPTING_CONVO_STEP_PARAMETERS: 'SCRIPTING_CONVO_STEP_PARAMETERS',
329
+ // Botium Lifecycle Hooks
328
330
  CUSTOMHOOK_ONBUILD: 'CUSTOMHOOK_ONBUILD',
329
331
  CUSTOMHOOK_ONSTART: 'CUSTOMHOOK_ONSTART',
330
332
  CUSTOMHOOK_ONUSERSAYS: 'CUSTOMHOOK_ONUSERSAYS',
@@ -486,6 +488,7 @@ Capabilities.SCRIPTING_UTTEXPANSION_NAMING_UTTERANCE_MAX;
486
488
  Capabilities.SCRIPTING_MEMORYEXPANSION_KEEP_ORIG;
487
489
  Capabilities.SCRIPTING_MEMORY_MATCHING_MODE;
488
490
  Capabilities.SCRIPTING_MEMORY_COLUMN_MODE;
491
+ Capabilities.SCRIPTING_CONVO_STEP_PARAMETERS;
489
492
  Capabilities.CUSTOMHOOK_ONBUILD;
490
493
  Capabilities.CUSTOMHOOK_ONSTART;
491
494
  Capabilities.CUSTOMHOOK_ONUSERSAYS;
@@ -1034,6 +1037,12 @@ var LogicHookConsts = {
1034
1037
  }, {
1035
1038
  name: 'CONDITIONAL_STEP_JSON_PATH_BASED',
1036
1039
  className: 'ConditionalJsonPathBasedLogicHook.js'
1040
+ }, {
1041
+ name: 'CONVO_STEP_PARAMETERS',
1042
+ className: 'ConvoStepParametersLogicHook.js'
1043
+ }, {
1044
+ name: 'ORDERED_LIST_TO_BUTTON',
1045
+ className: 'OrderedListToButtonLogicHook'
1037
1046
  }],
1038
1047
  DEFAULT_USER_INPUTS: [{
1039
1048
  name: 'BUTTON',
@@ -1044,12 +1053,14 @@ var LogicHookConsts = {
1044
1053
  }, {
1045
1054
  name: 'FORM',
1046
1055
  className: 'FormInput'
1047
- }]
1056
+ }],
1057
+ LOGIC_HOOK_EVENTS: ['onConvoBegin', 'onMeStart', 'onMePrepare', 'onMeEnd', 'onBotStart', 'onBotEnd', 'onBotPrepare', 'onConvoEnd']
1048
1058
  };
1049
1059
  LogicHookConsts.LOGIC_HOOK_INCLUDE;
1050
1060
  LogicHookConsts.DEFAULT_ASSERTERS;
1051
1061
  LogicHookConsts.DEFAULT_LOGIC_HOOKS;
1052
1062
  LogicHookConsts.DEFAULT_USER_INPUTS;
1063
+ LogicHookConsts.LOGIC_HOOK_EVENTS;
1053
1064
 
1054
1065
  const debug$m = debug$n('botium-core-asserterUtils');
1055
1066
  const {
@@ -1200,6 +1211,7 @@ var LogicHookUtils_1 = class LogicHookUtils {
1200
1211
  throw new Error(`Logic Hook specification ${ref} ${hookType} (${packageName}) invalid: ${err.message}`);
1201
1212
  }
1202
1213
  }
1214
+ const typeAsText = hookType === 'asserter' ? 'Asserter' : hookType === 'logichook' ? 'Logic Hook' : hookType === 'userinput' ? 'User Input' : 'Unknown';
1203
1215
  if (isClass(src)) {
1204
1216
  try {
1205
1217
  const CheckClass = src;
@@ -1208,7 +1220,7 @@ var LogicHookUtils_1 = class LogicHookUtils {
1208
1220
  ...this.buildScriptContext
1209
1221
  }, this.caps, args);
1210
1222
  } catch (err) {
1211
- throw new Error(`Logic Hook specification ${ref} from class invalid: ${err.message}`);
1223
+ throw new Error(`${typeAsText} specification ${ref} from class invalid: ${err.message}`);
1212
1224
  }
1213
1225
  }
1214
1226
  if (lodash.isFunction(src)) {
@@ -1218,7 +1230,7 @@ var LogicHookUtils_1 = class LogicHookUtils {
1218
1230
  ...this.buildScriptContext
1219
1231
  }, this.caps, args);
1220
1232
  } catch (err) {
1221
- throw new Error(`Logic Hook specification ${ref} from function invalid: ${err.message}`);
1233
+ throw new Error(`${typeAsText} specification ${ref} from function invalid: ${err.message}`);
1222
1234
  }
1223
1235
  }
1224
1236
  if (lodash.isObject(src) && !lodash.isString(src)) {
@@ -1236,7 +1248,7 @@ var LogicHookUtils_1 = class LogicHookUtils {
1236
1248
  }, {});
1237
1249
  return hookObject;
1238
1250
  } catch (err) {
1239
- throw new Error(`Logic Hook specification ${ref} ${hookType} from provided src (${util.inspect(src)}) invalid: ${err.message}`);
1251
+ throw new Error(`${typeAsText} specification ${ref} ${hookType} from provided src (${util.inspect(src)}) invalid: ${err.message}`);
1240
1252
  }
1241
1253
  }
1242
1254
  if (lodash.isString(src)) {
@@ -1297,7 +1309,7 @@ var LogicHookUtils_1 = class LogicHookUtils {
1297
1309
  try {
1298
1310
  return tryLoadFromSource(tryLoadFile, tryLoad.tryLoadAsserterByName);
1299
1311
  } catch (err) {
1300
- loadErr.push(`Logic Hook specification ${ref} ${hookType} from "${src}" invalid: ${err.message} `);
1312
+ loadErr.push(`${typeAsText} specification ${ref} ${hookType} from "${src}" invalid: ${err.message} `);
1301
1313
  }
1302
1314
  }
1303
1315
  }
@@ -1306,13 +1318,13 @@ var LogicHookUtils_1 = class LogicHookUtils {
1306
1318
  try {
1307
1319
  return tryLoadFromSource(tryLoad.tryLoadPackageName, tryLoad.tryLoadAsserterByName);
1308
1320
  } catch (err) {
1309
- loadErr.push(`Logic Hook specification ${ref} ${hookType} from "${src}" invalid: ${err.message} `);
1321
+ loadErr.push(`${typeAsText} specification ${ref} ${hookType} from "${src}" invalid: ${err.message} `);
1310
1322
  }
1311
1323
  }
1312
1324
  }
1313
1325
  loadErr.forEach(debug$m);
1314
1326
  }
1315
- throw new Error(`Logic Hook specification ${ref} ${hookType} from "${util.inspect(src)}" invalid : no loader available`);
1327
+ throw new Error(`${typeAsText} specification ${ref} ${hookType} from "${util.inspect(src)}" invalid : no loader available`);
1316
1328
  }
1317
1329
  };
1318
1330
 
@@ -2205,6 +2217,44 @@ const BotiumError$4 = class BotiumError extends Error {
2205
2217
  return null;
2206
2218
  }
2207
2219
  }
2220
+ hasError({
2221
+ type,
2222
+ source
2223
+ }) {
2224
+ if (this.context) {
2225
+ const errArr = lodash.isArray(this.context) ? this.context : [this.context];
2226
+ for (const err of errArr) {
2227
+ if (err.type === 'list') {
2228
+ for (const internal of err.errors) {
2229
+ if ((!type || internal.type === type) && (!source || internal.source === source)) {
2230
+ return true;
2231
+ }
2232
+ }
2233
+ }
2234
+ if ((!type || err.type === type) && (!source || err.source === source)) {
2235
+ return true;
2236
+ }
2237
+ }
2238
+ } else {
2239
+ return false;
2240
+ }
2241
+ }
2242
+ toArray() {
2243
+ if (this.context) {
2244
+ let result = [];
2245
+ const errArr = lodash.isArray(this.context) ? this.context : [this.context];
2246
+ for (const err of errArr) {
2247
+ if (err.type === 'list') {
2248
+ result = result.concat(err.errors);
2249
+ } else {
2250
+ result.push(err);
2251
+ }
2252
+ }
2253
+ return result;
2254
+ } else {
2255
+ return [];
2256
+ }
2257
+ }
2208
2258
  };
2209
2259
  const _getChildErrorsFromContext = context => {
2210
2260
  if (context && context.errors && lodash.isArray(context.errors)) {
@@ -2212,12 +2262,16 @@ const _getChildErrorsFromContext = context => {
2212
2262
  }
2213
2263
  return false;
2214
2264
  };
2215
- const botiumErrorFromErr$2 = (message, err) => {
2265
+ const botiumErrorFromErr$2 = (message, err, context = {}) => {
2216
2266
  if (err instanceof BotiumError$4) {
2217
- return new BotiumError$4(message, err.context, true);
2267
+ return new BotiumError$4(message, {
2268
+ ...err.context,
2269
+ ...context
2270
+ }, true);
2218
2271
  } else {
2219
2272
  return new BotiumError$4(message, {
2220
- err
2273
+ err,
2274
+ ...context
2221
2275
  }, true);
2222
2276
  }
2223
2277
  };
@@ -2492,6 +2546,13 @@ const _apply = (scriptingMemory, str, caps, mockMsg) => {
2492
2546
  return arg;
2493
2547
  }
2494
2548
  });
2549
+ args = args.map(arg => {
2550
+ const argStr = `${arg}`;
2551
+ if (argStr.startsWith('$')) {
2552
+ return scriptingMemory[argStr.substring(1)] || arg;
2553
+ }
2554
+ return arg;
2555
+ });
2495
2556
  str = str.replace(match, SCRIPTING_FUNCTIONS$1[key].handler(caps, ...args, mockMsg));
2496
2557
  } else {
2497
2558
  str = str.replace(match, SCRIPTING_FUNCTIONS$1[key].handler(caps));
@@ -2871,8 +2932,52 @@ class Convo$6 {
2871
2932
  let skipTranscriptStep = false;
2872
2933
  let conditionalGroupId = null;
2873
2934
  let conditionMetInGroup = false;
2874
- for (let i = 0; i < this.conversation.length; i++) {
2935
+ let globalConvoStepParameters = container.caps[Capabilities.SCRIPTING_CONVO_STEP_PARAMETERS] || {};
2936
+ let retryBotMessageTimeoutEnd = null;
2937
+ let retryBotMessageConvoId = null;
2938
+ let retryBotMessageDropBotResponse = false;
2939
+ for (let i = 0; i < this.conversation.length; i = retryBotMessageDropBotResponse ? i : i + 1) {
2940
+ retryBotMessageDropBotResponse = false;
2875
2941
  const convoStep = this.conversation[i];
2942
+ const rawConvoStepParameters = convoStep.logicHooks.find(lh => lh.name === 'CONVO_STEP_PARAMETERS')?.args;
2943
+ let convoStepParameters = {};
2944
+ if (rawConvoStepParameters && rawConvoStepParameters.length) {
2945
+ let params;
2946
+ if (rawConvoStepParameters[0].trim().startsWith('{')) {
2947
+ try {
2948
+ params = JSON.parse(rawConvoStepParameters[0]);
2949
+ } catch (e) {
2950
+ debug$j(`${this.header.name}/${convoStep.stepTag}: Failed to parse convo step parameters from JSON ${rawConvoStepParameters[0]}`);
2951
+ }
2952
+ }
2953
+ if (!params || !Object.keys(params).length) {
2954
+ params = {};
2955
+ for (const param of rawConvoStepParameters) {
2956
+ const semicolon = param.indexOf(':');
2957
+ if (semicolon) {
2958
+ try {
2959
+ const name = param.substring(0, semicolon);
2960
+ const value = param.substring(semicolon + 1);
2961
+ params[name] = value;
2962
+ } catch (e) {
2963
+ debug$j(`${this.header.name}/${convoStep.stepTag}: Failed to parse convo step parameter from arg ${param}`);
2964
+ }
2965
+ }
2966
+ }
2967
+ }
2968
+ if (convoStep.sender === 'begin') {
2969
+ globalConvoStepParameters = Object.assign({}, globalConvoStepParameters || {}, params);
2970
+ } else {
2971
+ convoStepParameters = Object.assign({}, globalConvoStepParameters || {}, params);
2972
+ }
2973
+ } else {
2974
+ if (convoStep.sender !== 'begin') {
2975
+ convoStepParameters = globalConvoStepParameters;
2976
+ }
2977
+ }
2978
+ if (Object.keys(convoStepParameters).length) {
2979
+ debug$j(`${this.header.name}: using convo step parameters ${JSON.stringify(convoStepParameters)}`);
2980
+ }
2876
2981
  const currentStepIndex = i;
2877
2982
  container.eventEmitter.emit(Events.CONVO_STEP_NEXT, container, convoStep, i);
2878
2983
  skipTranscriptStep = false;
@@ -2936,8 +3041,8 @@ class Convo$6 {
2936
3041
  const coreMsg = lodash.omit(removeBuffers(meMsg), ['sourceData']);
2937
3042
  debug$j(`${this.header.name}/${convoStep.stepTag}: user says (cleaned by binary and base64 data and sourceData) ${JSON.stringify(coreMsg, null, 2)}`);
2938
3043
  await new Promise(resolve => {
2939
- if (container.caps.SIMULATE_WRITING_SPEED && meMsg.messageText && meMsg.messageText.length) {
2940
- setTimeout(() => resolve(), container.caps.SIMULATE_WRITING_SPEED * meMsg.messageText.length);
3044
+ if (container.caps[Capabilities.SIMULATE_WRITING_SPEED] && meMsg.messageText && meMsg.messageText.length) {
3045
+ setTimeout(() => resolve(), container.caps[Capabilities.SIMULATE_WRITING_SPEED] * meMsg.messageText.length);
2941
3046
  } else {
2942
3047
  resolve();
2943
3048
  }
@@ -3091,10 +3196,29 @@ class Convo$6 {
3091
3196
  }
3092
3197
  const isErrorHandledWithOptionConvoStep = err => {
3093
3198
  const nextConvoStep = this.conversation[i + 1];
3199
+ const retryConfig = convoStepParameters?.ignoreNotMatchedBotResponses;
3200
+ const retryOn = convoStep.sender === 'bot' && retryConfig && retryConfig.timeout && retryConfig.mainAsserter;
3094
3201
  if (convoStep.optional && nextConvoStep && nextConvoStep.sender === 'bot') {
3202
+ if (retryOn) {
3203
+ debug$j(`${this.header.name}/${convoStep.stepTag}: Retry failed asserter is ignored on optional convo`);
3204
+ }
3095
3205
  waitForBotSays = false;
3096
3206
  skipTranscriptStep = true;
3097
3207
  return true;
3208
+ } else if (retryOn) {
3209
+ if (!retryBotMessageTimeoutEnd || retryBotMessageConvoId !== convoStep.stepTag) {
3210
+ retryBotMessageTimeoutEnd = transcriptStep.stepBegin.getTime() + +retryConfig.timeout;
3211
+ retryBotMessageConvoId = convoStep.stepTag;
3212
+ }
3213
+ const now = new Date().getTime();
3214
+ const timeoutRemaining = retryBotMessageTimeoutEnd - now;
3215
+ if (timeoutRemaining > 0) {
3216
+ debug$j(`${this.header.name}/${convoStep.stepTag}: Convo step retry on, timeout remaining: ${timeoutRemaining}, error: "${err.message}"`);
3217
+ retryBotMessageDropBotResponse = true;
3218
+ return false;
3219
+ } else {
3220
+ debug$j(`${this.header.name}/${convoStep.stepTag}: Convo step retry on, but timeout is over. error: "${err.message}"`);
3221
+ }
3098
3222
  }
3099
3223
  if (container.caps[Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS]) {
3100
3224
  assertErrors.push(err);
@@ -3112,7 +3236,7 @@ class Convo$6 {
3112
3236
  const tomatch = this._resolveUtterancesToMatch(container, Object.assign({}, scriptingMemoryUpdate, scriptingMemory), messageText, botMsg);
3113
3237
  if (convoStep.not) {
3114
3238
  try {
3115
- this.scriptingEvents.assertBotNotResponse(response, tomatch, `${this.header.name}/${convoStep.stepTag}`, lastMeConvoStep);
3239
+ this.scriptingEvents.assertBotNotResponse(response, tomatch, `${this.header.name}/${convoStep.stepTag}`, lastMeConvoStep, convoStepParameters);
3116
3240
  } catch (err) {
3117
3241
  if (isErrorHandledWithOptionConvoStep(err)) {
3118
3242
  continue;
@@ -3120,7 +3244,7 @@ class Convo$6 {
3120
3244
  }
3121
3245
  } else {
3122
3246
  try {
3123
- this.scriptingEvents.assertBotResponse(response, tomatch, `${this.header.name}/${convoStep.stepTag}`, lastMeConvoStep);
3247
+ this.scriptingEvents.assertBotResponse(response, tomatch, `${this.header.name}/${convoStep.stepTag}`, lastMeConvoStep, convoStepParameters);
3124
3248
  } catch (err) {
3125
3249
  if (isErrorHandledWithOptionConvoStep(err)) {
3126
3250
  continue;
@@ -3129,7 +3253,7 @@ class Convo$6 {
3129
3253
  }
3130
3254
  } else if (convoStep.sourceData) {
3131
3255
  try {
3132
- this._compareObject(container, scriptingMemory, convoStep, botMsg.sourceData, convoStep.sourceData, botMsg);
3256
+ this._compareObject(container, scriptingMemory, convoStep, botMsg.sourceData, convoStep.sourceData, botMsg, convoStepParameters);
3133
3257
  } catch (err) {
3134
3258
  if (isErrorHandledWithOptionConvoStep(err)) {
3135
3259
  continue;
@@ -3163,19 +3287,42 @@ class Convo$6 {
3163
3287
  skipTranscriptStep = true;
3164
3288
  continue;
3165
3289
  }
3166
- const failErr = botiumErrorFromErr$1(`${this.header.name}/${convoStep.stepTag}: assertion error - ${err.message || err}`, err);
3167
- debug$j(failErr);
3168
- try {
3169
- this.scriptingEvents.fail && this.scriptingEvents.fail(failErr, lastMeConvoStep);
3170
- } catch (failErr) {}
3171
- if (container.caps[Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS] && err instanceof BotiumError$2) {
3172
- assertErrors.push(err);
3290
+ const errors = err.toArray ? err.toArray() : [];
3291
+ const retryConfig = convoStepParameters?.ignoreNotMatchedBotResponses;
3292
+ const retryOn = convoStep.sender === 'bot' && retryConfig && retryConfig.timeout && errors.length && errors.filter(({
3293
+ type,
3294
+ source,
3295
+ asserter
3296
+ }) => type === 'asserter' && (retryConfig.allAsserters || retryConfig.asserters && retryConfig.asserters.includes(asserter))).length;
3297
+ if (retryOn && (!retryBotMessageTimeoutEnd || retryBotMessageConvoId !== convoStep.stepTag)) {
3298
+ retryBotMessageTimeoutEnd = transcriptStep.stepBegin.getTime() + +retryConfig.timeout;
3299
+ retryBotMessageConvoId = convoStep.stepTag;
3300
+ }
3301
+ const now = new Date().getTime();
3302
+ const timeoutRemaining = retryOn && retryBotMessageTimeoutEnd - now;
3303
+ if (retryOn && timeoutRemaining > 0) {
3304
+ debug$j(`${this.header.name}/${convoStep.stepTag}: Convo step retry on, timeout remaining: ${timeoutRemaining}, error: "${err.message}"`);
3305
+ retryBotMessageDropBotResponse = true;
3173
3306
  } else {
3174
- throw failErr;
3307
+ if (retryOn && timeoutRemaining <= 0) {
3308
+ debug$j(`${this.header.name}/${convoStep.stepTag}: Convo step retry on, but timeout is over. error: "${err.message}"`);
3309
+ }
3310
+ const failErr = botiumErrorFromErr$1(`${this.header.name}/${convoStep.stepTag}: assertion error - ${err.message || err}`, err);
3311
+ debug$j(failErr);
3312
+ try {
3313
+ this.scriptingEvents.fail && this.scriptingEvents.fail(failErr, lastMeConvoStep);
3314
+ } catch (failErr) {}
3315
+ if (container.caps[Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS] && err instanceof BotiumError$2) {
3316
+ assertErrors.push(err);
3317
+ } else {
3318
+ throw failErr;
3319
+ }
3175
3320
  }
3176
3321
  }
3177
3322
  if (container.caps[Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS]) {
3178
3323
  if (assertErrors.length > 0) {
3324
+ // this has no effect, but logically it has to be false
3325
+ retryBotMessageDropBotResponse = false;
3179
3326
  throw botiumErrorFromList$1(assertErrors, {});
3180
3327
  }
3181
3328
  } else {
@@ -3235,7 +3382,7 @@ class Convo$6 {
3235
3382
  }
3236
3383
  }
3237
3384
  }
3238
- _compareObject(container, scriptingMemory, convoStep, result, expected, botMsg) {
3385
+ _compareObject(container, scriptingMemory, convoStep, result, expected, botMsg, convoStepParameters) {
3239
3386
  if (expected === null || expected === undefined) return;
3240
3387
  if (lodash.isArray(expected)) {
3241
3388
  if (!lodash.isArray(result)) {
@@ -3245,12 +3392,12 @@ class Convo$6 {
3245
3392
  throw new BotiumError$2(`${this.header.name}/${convoStep.stepTag}: bot response expected array length ${expected.length}, got ${result.length}`);
3246
3393
  }
3247
3394
  for (let i = 0; i < expected.length; i++) {
3248
- this._compareObject(container, scriptingMemory, convoStep, result[i], expected[i]);
3395
+ this._compareObject(container, scriptingMemory, convoStep, result[i], expected[i], null, convoStepParameters);
3249
3396
  }
3250
3397
  } else if (lodash.isObject(expected)) {
3251
3398
  lodash.forOwn(expected, (value, key) => {
3252
3399
  if (Object.prototype.hasOwnProperty.call(result, key)) {
3253
- this._compareObject(container, scriptingMemory, convoStep, result[key], expected[key]);
3400
+ this._compareObject(container, scriptingMemory, convoStep, result[key], expected[key], null, convoStepParameters);
3254
3401
  } else {
3255
3402
  throw new BotiumError$2(`${this.header.name}/${convoStep.stepTag}: bot response "${result}" missing expected property: ${key}`);
3256
3403
  }
@@ -3259,7 +3406,7 @@ class Convo$6 {
3259
3406
  ScriptingMemory.fill(container, scriptingMemory, result, expected, this.scriptingEvents);
3260
3407
  const response = this._checkNormalizeText(container, result);
3261
3408
  const tomatch = this._resolveUtterancesToMatch(container, scriptingMemory, expected, botMsg);
3262
- this.scriptingEvents.assertBotResponse(response, tomatch, `${this.header.name}/${convoStep.stepTag}`);
3409
+ this.scriptingEvents.assertBotResponse(response, tomatch, `${this.header.name}/${convoStep.stepTag}`, null, convoStepParameters);
3263
3410
  }
3264
3411
  }
3265
3412
  GetScriptingMemoryAllVariables(container) {
@@ -3319,7 +3466,7 @@ class Convo$6 {
3319
3466
  }, []);
3320
3467
  }
3321
3468
  _checkBotRepliesConsumed(container) {
3322
- if (container.caps.SCRIPTING_FORCE_BOT_CONSUMED) {
3469
+ if (container.caps[Capabilities.SCRIPTING_FORCE_BOT_CONSUMED]) {
3323
3470
  const queueLength = container._QueueLength();
3324
3471
  if (queueLength === 1) {
3325
3472
  throw new Error('There is an unread bot reply in queue');
@@ -5363,7 +5510,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5363
5510
  }) => {
5364
5511
  return this._createLogicHookPromises({
5365
5512
  hookType: 'onConvoBegin',
5366
- logicHooks: convo.beginLogicHook || [],
5513
+ logicHooks: convo?.beginLogicHook || [],
5367
5514
  convo,
5368
5515
  convoStep,
5369
5516
  scriptingMemory,
@@ -5378,7 +5525,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5378
5525
  }) => {
5379
5526
  return this._createLogicHookPromises({
5380
5527
  hookType: 'onConvoEnd',
5381
- logicHooks: convo.endLogicHook || [],
5528
+ logicHooks: convo?.endLogicHook || [],
5382
5529
  convo,
5383
5530
  convoStep,
5384
5531
  scriptingMemory,
@@ -5393,7 +5540,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5393
5540
  }) => {
5394
5541
  return this._createLogicHookPromises({
5395
5542
  hookType: 'onMeStart',
5396
- logicHooks: convoStep.logicHooks || [],
5543
+ logicHooks: convoStep?.logicHooks || [],
5397
5544
  convo,
5398
5545
  convoStep,
5399
5546
  scriptingMemory,
@@ -5408,7 +5555,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5408
5555
  }) => {
5409
5556
  return this._createLogicHookPromises({
5410
5557
  hookType: 'onMePrepare',
5411
- logicHooks: convoStep.logicHooks || [],
5558
+ logicHooks: convoStep?.logicHooks || [],
5412
5559
  convo,
5413
5560
  convoStep,
5414
5561
  scriptingMemory,
@@ -5423,7 +5570,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5423
5570
  }) => {
5424
5571
  return this._createLogicHookPromises({
5425
5572
  hookType: 'onMeEnd',
5426
- logicHooks: convoStep.logicHooks || [],
5573
+ logicHooks: convoStep?.logicHooks || [],
5427
5574
  convo,
5428
5575
  convoStep,
5429
5576
  scriptingMemory,
@@ -5438,7 +5585,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5438
5585
  }) => {
5439
5586
  return this._createLogicHookPromises({
5440
5587
  hookType: 'onBotStart',
5441
- logicHooks: convoStep.logicHooks || [],
5588
+ logicHooks: convoStep?.logicHooks || [],
5442
5589
  convo,
5443
5590
  convoStep,
5444
5591
  scriptingMemory,
@@ -5453,7 +5600,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5453
5600
  }) => {
5454
5601
  return this._createLogicHookPromises({
5455
5602
  hookType: 'onBotPrepare',
5456
- logicHooks: convoStep.logicHooks || [],
5603
+ logicHooks: convoStep?.logicHooks || [],
5457
5604
  convo,
5458
5605
  convoStep,
5459
5606
  scriptingMemory,
@@ -5468,7 +5615,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5468
5615
  }) => {
5469
5616
  return this._createLogicHookPromises({
5470
5617
  hookType: 'onBotEnd',
5471
- logicHooks: convoStep.logicHooks || [],
5618
+ logicHooks: convoStep?.logicHooks || [],
5472
5619
  convo,
5473
5620
  convoStep,
5474
5621
  scriptingMemory,
@@ -5483,7 +5630,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5483
5630
  }) => {
5484
5631
  return this._createAsserterPromises({
5485
5632
  asserterType: 'assertConvoBegin',
5486
- asserters: convo.beginAsserter || [],
5633
+ asserters: convo?.beginAsserter || [],
5487
5634
  convo,
5488
5635
  convoStep,
5489
5636
  scriptingMemory,
@@ -5498,7 +5645,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5498
5645
  }) => {
5499
5646
  return this._createAsserterPromises({
5500
5647
  asserterType: 'assertConvoStep',
5501
- asserters: convoStep.asserters || [],
5648
+ asserters: convoStep?.asserters || [],
5502
5649
  convo,
5503
5650
  convoStep,
5504
5651
  scriptingMemory,
@@ -5513,7 +5660,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5513
5660
  }) => {
5514
5661
  return this._createAsserterPromises({
5515
5662
  asserterType: 'assertConvoEnd',
5516
- asserters: convo.endAsserter || [],
5663
+ asserters: convo?.endAsserter || [],
5517
5664
  convo,
5518
5665
  convoStep,
5519
5666
  scriptingMemory,
@@ -5542,12 +5689,13 @@ var ScriptingProvider_1 = class ScriptingProvider {
5542
5689
  resolveEmptyIfUnknown
5543
5690
  });
5544
5691
  },
5545
- assertBotResponse: (botresponse, tomatch, stepTag, meMsg) => {
5692
+ assertBotResponse: (botresponse, tomatch, stepTag, meMsg, convoStepParameters) => {
5546
5693
  if (!lodash.isArray(tomatch)) {
5547
5694
  tomatch = [tomatch];
5548
5695
  }
5549
5696
  debug$9(`assertBotResponse ${stepTag} ${meMsg ? `(${meMsg}) ` : ''}BOT: ${botresponse} = ${tomatch} ...`);
5550
- const found = lodash.find(tomatch, utt => this.matchFn(botresponse, utt, this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS]));
5697
+ const matchFn = convoStepParameters.matchingMode ? getMatchFunction(convoStepParameters.matchingMode) || this.matchFn : this.matchFn;
5698
+ const found = lodash.find(tomatch, utt => matchFn(botresponse, utt, this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS]));
5551
5699
  const asserterType = this.caps[Capabilities.SCRIPTING_MATCHING_MODE] === 'wer' ? 'Word Error Rate Asserter' : 'Text Match Asserter';
5552
5700
  if (lodash.isNil(found)) {
5553
5701
  if (this.caps[Capabilities.SCRIPTING_MATCHING_MODE] === 'wer') {
@@ -5597,12 +5745,13 @@ var ScriptingProvider_1 = class ScriptingProvider {
5597
5745
  }
5598
5746
  }
5599
5747
  },
5600
- assertBotNotResponse: (botresponse, nottomatch, stepTag, meMsg) => {
5748
+ assertBotNotResponse: (botresponse, nottomatch, stepTag, meMsg, convoStepParameters) => {
5601
5749
  if (!lodash.isArray(nottomatch)) {
5602
5750
  nottomatch = [nottomatch];
5603
5751
  }
5604
5752
  debug$9(`assertBotNotResponse ${stepTag} ${meMsg ? `(${meMsg}) ` : ''}BOT: ${botresponse} != ${nottomatch} ...`);
5605
- const found = lodash.find(nottomatch, utt => this.matchFn(botresponse, utt, this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS]));
5753
+ const matchFn = convoStepParameters.matchingMode ? getMatchFunction(convoStepParameters.matchingMode) || this.matchFn : this.matchFn;
5754
+ const found = lodash.find(nottomatch, utt => matchFn(botresponse, utt, this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS]));
5606
5755
  const asserterType = this.caps[Capabilities.SCRIPTING_MATCHING_MODE] === 'wer' ? 'Word Error Rate Asserter' : 'Text Match Asserter';
5607
5756
  if (!lodash.isNil(found)) {
5608
5757
  if (this.caps[Capabilities.SCRIPTING_MATCHING_MODE] === 'wer') {
@@ -5676,6 +5825,22 @@ var ScriptingProvider_1 = class ScriptingProvider {
5676
5825
  assertConvoStep: 'assertNotConvoStep',
5677
5826
  assertConvoEnd: 'assertNotConvoEnd'
5678
5827
  };
5828
+ const updateExceptionContext = (promise, asserter) => {
5829
+ const updateError = err => {
5830
+ if (err instanceof BotiumError$1) {
5831
+ if (!err.context) {
5832
+ err.context = {};
5833
+ }
5834
+ err.context.asserter = asserter.name;
5835
+ throw err;
5836
+ } else {
5837
+ throw botiumErrorFromErr(lodash.isString(err) ? err : err.message, err, {
5838
+ asserter: asserter.name
5839
+ });
5840
+ }
5841
+ };
5842
+ return promise.catch(err => updateError(err));
5843
+ };
5679
5844
  const callAsserter = (asserterSpec, asserter, params) => {
5680
5845
  if (asserterSpec.not) {
5681
5846
  const notAsserterType = mapNot[asserterType];
@@ -5699,24 +5864,36 @@ var ScriptingProvider_1 = class ScriptingProvider {
5699
5864
  return p(this.retryHelperAsserter, () => asserter[asserterType](params));
5700
5865
  }
5701
5866
  };
5702
- const convoAsserter = asserters.filter(a => this.asserters[a.name][asserterType]).map(a => callAsserter(a, this.asserters[a.name], {
5703
- convo,
5704
- convoStep,
5705
- scriptingMemory,
5706
- container,
5707
- args: ScriptingMemory.applyToArgs(a.args, scriptingMemory, container.caps, rest.botMsg),
5708
- isGlobal: false,
5709
- ...rest
5710
- }));
5711
- const globalAsserter = Object.values(this.globalAsserter).filter(a => a[asserterType]).map(a => p(this.retryHelperAsserter, () => a[asserterType]({
5712
- convo,
5713
- convoStep,
5714
- scriptingMemory,
5715
- container,
5716
- args: [],
5717
- isGlobal: true,
5718
- ...rest
5719
- })));
5867
+ const convoAsserter = asserters.filter(a => this.asserters[a.name][asserterType]).map(a => ({
5868
+ asserter: a,
5869
+ promise: callAsserter(a, this.asserters[a.name], {
5870
+ convo,
5871
+ convoStep,
5872
+ scriptingMemory,
5873
+ container,
5874
+ args: ScriptingMemory.applyToArgs(a.args, scriptingMemory, container.caps, rest.botMsg),
5875
+ isGlobal: false,
5876
+ ...rest
5877
+ })
5878
+ })).map(({
5879
+ promise,
5880
+ asserter
5881
+ }) => updateExceptionContext(promise, asserter));
5882
+ const globalAsserter = Object.values(this.globalAsserter).filter(a => a[asserterType]).map(a => ({
5883
+ asserter: a,
5884
+ promise: p(this.retryHelperAsserter, () => a[asserterType]({
5885
+ convo,
5886
+ convoStep,
5887
+ scriptingMemory,
5888
+ container,
5889
+ args: [],
5890
+ isGlobal: true,
5891
+ ...rest
5892
+ }))
5893
+ })).map(({
5894
+ promise,
5895
+ asserter
5896
+ }) => updateExceptionContext(promise, asserter));
5720
5897
  const allPromises = [...convoAsserter, ...globalAsserter];
5721
5898
  if (this.caps[Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS]) {
5722
5899
  return Promise.allSettled(allPromises).then(results => {
@@ -5742,7 +5919,8 @@ var ScriptingProvider_1 = class ScriptingProvider {
5742
5919
  if (hookType !== 'onMeStart' && hookType !== 'onMePrepare' && hookType !== 'onMeEnd' && hookType !== 'onBotStart' && hookType !== 'onBotPrepare' && hookType !== 'onBotEnd' && hookType !== 'onConvoBegin' && hookType !== 'onConvoEnd') {
5743
5920
  throw Error(`Unknown hookType ${hookType}`);
5744
5921
  }
5745
- const convoStepPromises = (logicHooks || []).filter(l => this.logicHooks[l.name][hookType]).map(l => p(this.retryHelperLogicHook, () => this.logicHooks[l.name][hookType]({
5922
+ const localHooks = (logicHooks || []).filter(l => this.logicHooks[l.name][hookType]);
5923
+ const convoStepPromises = localHooks.map(l => p(this.retryHelperLogicHook, () => this.logicHooks[l.name][hookType]({
5746
5924
  convo,
5747
5925
  convoStep,
5748
5926
  scriptingMemory,
@@ -5751,7 +5929,8 @@ var ScriptingProvider_1 = class ScriptingProvider {
5751
5929
  isGlobal: false,
5752
5930
  ...rest
5753
5931
  })));
5754
- const globalPromises = Object.values(this.globalLogicHook).filter(l => l[hookType]).map(l => p(this.retryHelperLogicHook, () => l[hookType]({
5932
+ const globalHooks = Object.values(this.globalLogicHook).filter(l => l[hookType]);
5933
+ const globalPromises = globalHooks.map(l => p(this.retryHelperLogicHook, () => l[hookType]({
5755
5934
  convo,
5756
5935
  convoStep,
5757
5936
  scriptingMemory,
@@ -5761,7 +5940,14 @@ var ScriptingProvider_1 = class ScriptingProvider {
5761
5940
  ...rest
5762
5941
  })));
5763
5942
  const allPromises = [...convoStepPromises, ...globalPromises];
5764
- if (allPromises.length > 0) return Promise.all(allPromises).then(() => true);
5943
+ if (allPromises.length > 0) {
5944
+ return Promise.all(allPromises).then(() => {
5945
+ return {
5946
+ // just returning some humanreadable
5947
+ hooks: [...localHooks, ...globalHooks].map(h => h.name || h.context?.ref || JSON.stringify(h))
5948
+ };
5949
+ });
5950
+ }
5765
5951
  return Promise.resolve(false);
5766
5952
  }
5767
5953
  _createUserInputPromises({
@@ -5771,7 +5957,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5771
5957
  container,
5772
5958
  ...rest
5773
5959
  }) {
5774
- const convoStepPromises = (convoStep.userInputs || []).filter(ui => this.userInputs[ui.name]).map(ui => p(this.retryHelperUserInput, () => this.userInputs[ui.name].setUserInput({
5960
+ const convoStepPromises = (convoStep?.userInputs || []).filter(ui => this.userInputs[ui.name]).map(ui => p(this.retryHelperUserInput, () => this.userInputs[ui.name].setUserInput({
5775
5961
  convo,
5776
5962
  convoStep,
5777
5963
  scriptingMemory,
@@ -5833,6 +6019,11 @@ var ScriptingProvider_1 = class ScriptingProvider {
5833
6019
  }
5834
6020
  };
5835
6021
  }
6022
+
6023
+ // Livechat, and crawler using logichooks too. So they need script context
6024
+ BuildScriptContext() {
6025
+ return this._buildScriptContext();
6026
+ }
5836
6027
  Build() {
5837
6028
  const CompilerXlsx = CompilerXlsx_1;
5838
6029
  this.compilers[Constants.SCRIPTING_FORMAT_XSLX] = new CompilerXlsx(this._buildScriptContext(), this.caps);