botium-core 1.13.16 → 1.13.17

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.
@@ -6,7 +6,7 @@ var util = require('util');
6
6
  var fs = require('fs');
7
7
  var path = require('path');
8
8
  var async = require('async');
9
- var rimraf = require('rimraf');
9
+ var rimraf$2 = require('rimraf');
10
10
  var mkdirp = require('mkdirp');
11
11
  var sanitizeFilename = require('sanitize-filename');
12
12
  var moment = require('moment');
@@ -46,7 +46,7 @@ var util__default = /*#__PURE__*/_interopDefaultLegacy(util);
46
46
  var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
47
47
  var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
48
48
  var async__default = /*#__PURE__*/_interopDefaultLegacy(async);
49
- var rimraf__default = /*#__PURE__*/_interopDefaultLegacy(rimraf);
49
+ var rimraf__default = /*#__PURE__*/_interopDefaultLegacy(rimraf$2);
50
50
  var mkdirp__default = /*#__PURE__*/_interopDefaultLegacy(mkdirp);
51
51
  var sanitizeFilename__default = /*#__PURE__*/_interopDefaultLegacy(sanitizeFilename);
52
52
  var moment__default = /*#__PURE__*/_interopDefaultLegacy(moment);
@@ -81,7 +81,7 @@ var express__default = /*#__PURE__*/_interopDefaultLegacy(express);
81
81
  var bodyParser__default = /*#__PURE__*/_interopDefaultLegacy(bodyParser);
82
82
 
83
83
  var name = "botium-core";
84
- var version$1 = "1.13.16";
84
+ var version$1 = "1.13.17";
85
85
  var description = "The Selenium for Chatbots";
86
86
  var main = "index.js";
87
87
  var module$1 = "dist/botium-es.js";
@@ -113,66 +113,66 @@ var bugs = {
113
113
  };
114
114
  var homepage = "https://www.botium.ai";
115
115
  var dependencies = {
116
- "@babel/runtime": "^7.20.6",
116
+ "@babel/runtime": "^7.21.5",
117
117
  async: "^3.2.4",
118
- "body-parser": "^1.20.1",
118
+ "body-parser": "^1.20.2",
119
119
  boolean: "^3.2.0",
120
120
  bottleneck: "^2.19.5",
121
- "csv-parse": "^5.3.3",
121
+ "csv-parse": "^5.3.10",
122
122
  debug: "^4.3.4",
123
123
  esprima: "^4.0.1",
124
124
  express: "^4.18.2",
125
125
  globby: "11.0.4",
126
- ioredis: "^5.2.4",
126
+ ioredis: "^5.3.2",
127
127
  "is-class": "^0.0.9",
128
128
  "is-json": "^2.0.1",
129
129
  jsonpath: "^1.1.1",
130
130
  lodash: "^4.17.21",
131
131
  "markdown-it": "^13.0.1",
132
132
  "mime-types": "^2.1.35",
133
- mkdirp: "^1.0.4",
133
+ mkdirp: "^3.0.1",
134
134
  moment: "^2.29.4",
135
135
  mustache: "^4.2.0",
136
136
  "promise-retry": "^2.0.1",
137
137
  "promise.allsettled": "^1.0.6",
138
138
  randomatic: "^3.1.1",
139
139
  request: "^2.88.2",
140
- rimraf: "^3.0.2",
140
+ rimraf: "^5.0.0",
141
141
  "sanitize-filename": "^1.6.3",
142
- slugify: "^1.6.5",
143
- "socket.io": "^4.5.4",
144
- "socket.io-client": "^4.5.4",
142
+ slugify: "^1.6.6",
143
+ "socket.io": "^4.6.1",
144
+ "socket.io-client": "^4.6.1",
145
145
  "socketio-auth": "^0.1.1",
146
- "swagger-jsdoc": "^6.2.5",
147
- "swagger-ui-express": "^4.6.0",
146
+ "swagger-jsdoc": "^6.2.8",
147
+ "swagger-ui-express": "^4.6.3",
148
148
  uuid: "^9.0.0",
149
- vm2: "^3.9.13",
149
+ vm2: "^3.9.17",
150
150
  "word-error-rate": "0.0.7",
151
151
  "write-yaml": "^1.0.0",
152
152
  xlsx: "^0.18.5",
153
153
  xregexp: "^5.1.1",
154
- yaml: "^2.1.3"
154
+ yaml: "^2.2.2"
155
155
  };
156
156
  var devDependencies = {
157
- "@babel/core": "^7.20.5",
158
- "@babel/node": "^7.20.5",
159
- "@babel/plugin-transform-runtime": "^7.19.6",
160
- "@babel/preset-env": "^7.20.2",
157
+ "@babel/core": "^7.21.8",
158
+ "@babel/node": "^7.20.7",
159
+ "@babel/plugin-transform-runtime": "^7.21.4",
160
+ "@babel/preset-env": "^7.21.5",
161
161
  chai: "^4.3.7",
162
162
  "chai-as-promised": "^7.1.1",
163
163
  "cross-env": "^7.0.3",
164
- eslint: "^8.29.0",
164
+ eslint: "^8.40.0",
165
165
  "eslint-config-standard": "^17.0.0",
166
- "eslint-plugin-import": "^2.26.0",
166
+ "eslint-plugin-import": "^2.27.5",
167
167
  "eslint-plugin-mocha": "^10.1.0",
168
- "eslint-plugin-n": "^15.6.0",
168
+ "eslint-plugin-n": "^15.7.0",
169
169
  "eslint-plugin-promise": "^6.1.1",
170
170
  "eslint-plugin-standard": "^4.1.0",
171
171
  "license-checker": "^25.0.1",
172
172
  "license-compatibility-checker": "^0.3.5",
173
173
  mocha: "^10.2.0",
174
- nock: "^13.2.9",
175
- "npm-check-updates": "^16.5.6",
174
+ nock: "^13.3.1",
175
+ "npm-check-updates": "^16.10.12",
176
176
  nyc: "^15.1.0",
177
177
  rollup: "2.79.1",
178
178
  "rollup-plugin-babel": "^4.4.0",
@@ -383,9 +383,7 @@ var Capabilities = {
383
383
  RATELIMIT_USERSAYS_MINTIME: 'RATELIMIT_USERSAYS_MINTIME',
384
384
  RATELIMIT_BOTTLENECK_FN: 'RATELIMIT_BOTTLENECK_FN',
385
385
  SECURITY_ALLOW_UNSAFE: 'SECURITY_ALLOW_UNSAFE',
386
- PRECOMPILERS: 'PRECOMPILERS',
387
- // RETRY
388
- RETRY_CONVO_ASYNC: 'RETRY_CONVO_ASYNC'
386
+ PRECOMPILERS: 'PRECOMPILERS'
389
387
  };
390
388
  Capabilities.PROJECTNAME;
391
389
  Capabilities.TESTSESSIONNAME;
@@ -538,7 +536,6 @@ Capabilities.RATELIMIT_USERSAYS_MINTIME;
538
536
  Capabilities.RATELIMIT_BOTTLENECK_FN;
539
537
  Capabilities.SECURITY_ALLOW_UNSAFE;
540
538
  Capabilities.PRECOMPILERS;
541
- Capabilities.RETRY_CONVO_ASYNC;
542
539
 
543
540
  var Source = {
544
541
  LOCALPATH: 'LOCALPATH',
@@ -859,6 +856,7 @@ var Events = {
859
856
  CONTAINER_CLEANED: 'CONTAINER_CLEANED',
860
857
  CONTAINER_CLEAN_ERROR: 'CONTAINER_CLEAN_ERROR',
861
858
  BOT_CONNECTED: 'BOT_CONNECTED',
859
+ CONVO_STEP_NEXT: 'CONVO_STEP_NEXT',
862
860
  // Chatbot Events
863
861
  MESSAGE_SENTTOBOT: 'MESSAGE_SENTTOBOT',
864
862
  MESSAGE_SENDTOBOT_ERROR: 'MESSAGE_SENDTOBOT_ERROR',
@@ -882,6 +880,7 @@ Events.CONTAINER_CLEANING;
882
880
  Events.CONTAINER_CLEANED;
883
881
  Events.CONTAINER_CLEAN_ERROR;
884
882
  Events.BOT_CONNECTED;
883
+ Events.CONVO_STEP_NEXT;
885
884
  Events.MESSAGE_SENTTOBOT;
886
885
  Events.MESSAGE_SENDTOBOT_ERROR;
887
886
  Events.MESSAGE_RECEIVEDFROMBOT;
@@ -2225,7 +2224,7 @@ const linesToScriptingMemories$2 = (lines, columnMode = null) => {
2225
2224
  }
2226
2225
  return scriptingMemories;
2227
2226
  };
2228
- const calculateWer$1 = (str, pattern) => {
2227
+ const calculateWer$2 = (str, pattern) => {
2229
2228
  const _prepareString = (str, remWildcard = false) => {
2230
2229
  if (remWildcard) return str.replace(/[.,/#!$%^&;:*{}=\-_`~()]/g, '').toLowerCase();
2231
2230
  return str.replace(/[.,/#!$%^&;:{}=\-_`~()]/g, '').toLowerCase();
@@ -2248,6 +2247,11 @@ const calculateWer$1 = (str, pattern) => {
2248
2247
  const botMessage = _prepareString(str);
2249
2248
  const botMessageWords = botMessage.split(' ').map(bm => bm.trim());
2250
2249
  const utt = _prepareString(utterance);
2250
+
2251
+ // if no wildcards, just calculate WER
2252
+ if (utt.indexOf('*') === -1) return wordErrorRate__default["default"].wordErrorRate(botMessage, utt).toFixed(2);
2253
+
2254
+ // if there are wildcards, calculate WER for each wildcard part
2251
2255
  const errors = [];
2252
2256
  for (let wildcardPart of utt.split('*')) {
2253
2257
  let wer = 1;
@@ -2267,7 +2271,7 @@ const calculateWer$1 = (str, pattern) => {
2267
2271
  }
2268
2272
  }
2269
2273
  if (lodash__default["default"].isNil(subsetPhraseFound)) {
2270
- throw new Error('Word Error Asserter: Something went wrong here, please try to modify your assertion!');
2274
+ throw new Error('Word Error Asserter: When using wild cards, please make sure that the length of the asserter text is smaller than the bot message!');
2271
2275
  }
2272
2276
  errors.push(_getErrors(_getWords(wildcardPart), _getWords(subsetPhraseFound)));
2273
2277
  }
@@ -2280,6 +2284,7 @@ const calculateWer$1 = (str, pattern) => {
2280
2284
  debug$l(`Word Error Rate Asserter - Compared Bot Message '${botMessage}' / '${utt}': ${(errCount / allCount).toFixed(2)}`);
2281
2285
  return (errCount / allCount).toFixed(2);
2282
2286
  };
2287
+ const toPercent$1 = s => `${(s * 100).toFixed(0)}%`;
2283
2288
  var helper = {
2284
2289
  normalizeText: normalizeText$1,
2285
2290
  splitStringInNonEmptyLines: splitStringInNonEmptyLines$1,
@@ -2294,7 +2299,8 @@ var helper = {
2294
2299
  validateSender: validateSender$1,
2295
2300
  validateConvo: validateConvo$2,
2296
2301
  linesToScriptingMemories: linesToScriptingMemories$2,
2297
- calculateWer: calculateWer$1
2302
+ calculateWer: calculateWer$2,
2303
+ toPercent: toPercent$1
2298
2304
  };
2299
2305
 
2300
2306
  const debug$k = debug__default["default"]('botium-core-ScriptingMemory');
@@ -2634,45 +2640,7 @@ ScriptingMemory.extractVarNames;
2634
2640
  ScriptingMemory.RESERVED_WORDS;
2635
2641
  ScriptingMemory.SCRIPTING_FUNCTIONS;
2636
2642
 
2637
- const debug$j = debug__default["default"]('botium-core-RetryHelper');
2638
- var RetryHelper_1 = class RetryHelper {
2639
- constructor(caps, section, options = {}) {
2640
- this.retryErrorPatterns = [];
2641
- const onErrorRegexp = caps[`RETRY_${section.toUpperCase()}_ONERROR_REGEXP`] || [];
2642
- if (onErrorRegexp) {
2643
- if (lodash__default["default"].isArray(onErrorRegexp)) {
2644
- onErrorRegexp.forEach(r => {
2645
- if (lodash__default["default"].isString(r)) this.retryErrorPatterns.push(new RegExp(r, 'i'));else this.retryErrorPatterns.push(r);
2646
- });
2647
- } else if (lodash__default["default"].isString(onErrorRegexp)) {
2648
- this.retryErrorPatterns.push(new RegExp(onErrorRegexp, 'i'));
2649
- } else {
2650
- this.retryErrorPatterns.push(onErrorRegexp);
2651
- }
2652
- }
2653
-
2654
- // to turn on retries, NUMRETRIES or ONERROR_REGEXP has to be set
2655
- this.retrySettings = {
2656
- retries: caps[`RETRY_${section.toUpperCase()}_NUMRETRIES`] || (!lodash__default["default"].isNil(options.numRetries) ? options.numRetries : this.retryErrorPatterns.length === 0 ? 0 : 1),
2657
- factor: caps[`RETRY_${section.toUpperCase()}_FACTOR`] || (lodash__default["default"].isNil(options.factor) ? 1 : options.factor),
2658
- minTimeout: caps[`RETRY_${section.toUpperCase()}_MINTIMEOUT`] || (lodash__default["default"].isNil(options.minTimeout) ? 1000 : options.minTimeout)
2659
- };
2660
- if (this.retrySettings.retries > 0) {
2661
- debug$j(`Retry for ${section} is enabled. Settings: ${JSON.stringify(this.retrySettings)} Patterns: ${JSON.stringify(this.retryErrorPatterns.map(r => r.toString()))}`);
2662
- }
2663
- }
2664
- shouldRetry(err) {
2665
- if (!err) return false;
2666
- if (this.retryErrorPatterns.length === 0) return true;
2667
- const errString = util__default["default"].inspect(err);
2668
- for (const re of this.retryErrorPatterns) {
2669
- if (errString.match(re)) return true;
2670
- }
2671
- return false;
2672
- }
2673
- };
2674
-
2675
- const debug$i = debug__default["default"]('botium-core-Convo');
2643
+ const debug$j = debug__default["default"]('botium-core-Convo');
2676
2644
  const {
2677
2645
  BotiumError: BotiumError$4,
2678
2646
  botiumErrorFromErr: botiumErrorFromErr$1,
@@ -2826,10 +2794,10 @@ class TranscriptError extends Error {
2826
2794
  class Convo$6 {
2827
2795
  constructor(context, fromJson = {}) {
2828
2796
  if (fromJson instanceof Convo$6) {
2829
- debug$i('Illegal state!!! Parameter should be a JSON, but it is a Convo');
2797
+ debug$j('Illegal state!!! Parameter should be a JSON, but it is a Convo');
2830
2798
  } else if (fromJson.beginAsserter) {
2831
2799
  // beginAsserter is one of the fields which are lost
2832
- debug$i('Illegal state!!! Parameter should be a native JSON, but looks as a Convo converted to JSON');
2800
+ debug$j('Illegal state!!! Parameter should be a native JSON, but looks as a Convo converted to JSON');
2833
2801
  }
2834
2802
  this.scriptingEvents = context.scriptingEvents;
2835
2803
  this.context = context;
@@ -2874,30 +2842,6 @@ class Convo$6 {
2874
2842
  return this.header.toString() + (this.sourceTag ? ` (${util__default["default"].inspect(this.sourceTag)})` : '') + ': ' + this.conversation.map(c => c.toString()).join(' | ');
2875
2843
  }
2876
2844
  async Run(container) {
2877
- if (container.caps.RETRY_CONVO_ASYNC) {
2878
- return this.RunImpl(container).catch(err => {
2879
- debug$i(`Convo failed with error "${err.message || JSON.stringify(err)}".`);
2880
- throw err;
2881
- });
2882
- } else {
2883
- const retryHelper = new RetryHelper_1(container.caps, 'CONVO');
2884
- return promiseRetry__default["default"](async (retry, number) => {
2885
- const retryRemaining = retryHelper.retrySettings.retries - number + 1;
2886
- return this.RunImpl(container).catch(err => {
2887
- if (retryHelper.shouldRetry(err)) {
2888
- debug$i(`Convo failed with error "${err.message || JSON.stringify(err)}". Retry ${retryRemaining > 0 ? 'enabled' : 'disabled'} (remaining #${retryRemaining}/${retryHelper.retrySettings.retries}, criterion matches)`);
2889
- retry(err);
2890
- } else {
2891
- if (retryHelper.retryErrorPatterns.length > 0) {
2892
- debug$i(`Convo failed with error "${err.message || JSON.stringify(err)}". Retry 'disabled' (remaining (#${retryRemaining}/${retryHelper.retrySettings.retries}), criterion does not match)`);
2893
- }
2894
- throw err;
2895
- }
2896
- });
2897
- }, retryHelper.retrySettings);
2898
- }
2899
- }
2900
- async RunImpl(container) {
2901
2845
  const transcript = new Transcript({
2902
2846
  steps: [],
2903
2847
  attachments: [],
@@ -3003,6 +2947,7 @@ class Convo$6 {
3003
2947
  for (let i = 0; i < this.conversation.length; i++) {
3004
2948
  const convoStep = this.conversation[i];
3005
2949
  const currentStepIndex = i;
2950
+ container.eventEmitter.emit(Events.CONVO_STEP_NEXT, container, convoStep, i);
3006
2951
  skipTranscriptStep = false;
3007
2952
  const transcriptStep = new TranscriptStep({
3008
2953
  expected: new BotiumMockMessage_1(convoStep),
@@ -3062,7 +3007,7 @@ class Convo$6 {
3062
3007
  });
3063
3008
  await this._checkBotRepliesConsumed(container);
3064
3009
  const coreMsg = lodash__default["default"].omit(removeBuffers(meMsg), ['sourceData']);
3065
- debug$i(`${this.header.name}/${convoStep.stepTag}: user says (cleaned by binary and base64 data and sourceData) ${JSON.stringify(coreMsg, null, 2)}`);
3010
+ debug$j(`${this.header.name}/${convoStep.stepTag}: user says (cleaned by binary and base64 data and sourceData) ${JSON.stringify(coreMsg, null, 2)}`);
3066
3011
  await new Promise(resolve => {
3067
3012
  if (container.caps.SIMULATE_WRITING_SPEED && meMsg.messageText && meMsg.messageText.length) {
3068
3013
  setTimeout(() => resolve(), container.caps.SIMULATE_WRITING_SPEED * meMsg.messageText.length);
@@ -3098,7 +3043,7 @@ class Convo$6 {
3098
3043
  });
3099
3044
  continue;
3100
3045
  } else {
3101
- debug$i(`${this.header.name}/${convoStep.stepTag}: message not found in #me section, message not sent to container ${util__default["default"].inspect(convoStep)}`);
3046
+ debug$j(`${this.header.name}/${convoStep.stepTag}: message not found in #me section, message not sent to container ${util__default["default"].inspect(convoStep)}`);
3102
3047
  transcriptStep.botEnd = new Date();
3103
3048
  await this.scriptingEvents.onMeEnd({
3104
3049
  convo: this,
@@ -3114,7 +3059,7 @@ class Convo$6 {
3114
3059
  } catch (err) {
3115
3060
  transcriptStep.botEnd = new Date();
3116
3061
  const failErr = botiumErrorFromErr$1(`${this.header.name}/${convoStep.stepTag}: error sending to bot - ${err.message || err}`, err);
3117
- debug$i(failErr);
3062
+ debug$j(failErr);
3118
3063
  try {
3119
3064
  this.scriptingEvents.fail && this.scriptingEvents.fail(failErr);
3120
3065
  } catch (failErr) {}
@@ -3127,7 +3072,7 @@ class Convo$6 {
3127
3072
  waitForBotSays = true;
3128
3073
  }
3129
3074
  try {
3130
- debug$i(`${this.header.name} wait for bot ${convoStep.channel || ''}`);
3075
+ debug$j(`${this.header.name} wait for bot ${convoStep.channel || ''}`);
3131
3076
  await this.scriptingEvents.onBotStart({
3132
3077
  convo: this,
3133
3078
  convoStep,
@@ -3143,11 +3088,11 @@ class Convo$6 {
3143
3088
  transcriptStep.botEnd = new Date();
3144
3089
  transcriptStep.actual = new BotiumMockMessage_1(botMsg);
3145
3090
  const coreMsg = lodash__default["default"].omit(removeBuffers(botMsg), ['sourceData']);
3146
- debug$i(`${this.header.name}: bot says (cleaned by binary and base64 data and sourceData) ${JSON.stringify(coreMsg, null, 2)}`);
3091
+ debug$j(`${this.header.name}: bot says (cleaned by binary and base64 data and sourceData) ${JSON.stringify(coreMsg, null, 2)}`);
3147
3092
  } catch (err) {
3148
3093
  transcriptStep.botEnd = new Date();
3149
3094
  const failErr = botiumErrorFromErr$1(`${this.header.name}/${convoStep.stepTag}: error waiting for bot - ${err.message}`, err);
3150
- debug$i(failErr);
3095
+ debug$j(failErr);
3151
3096
  try {
3152
3097
  this.scriptingEvents.fail && this.scriptingEvents.fail(failErr, lastMeConvoStep);
3153
3098
  } catch (failErr) {}
@@ -3166,11 +3111,11 @@ class Convo$6 {
3166
3111
  if (prepared) {
3167
3112
  transcriptStep.actual = new BotiumMockMessage_1(botMsg);
3168
3113
  const coreMsg = lodash__default["default"].omit(removeBuffers(botMsg), ['sourceData']);
3169
- debug$i(`${this.header.name}: onBotPrepare (cleaned by binary and base64 data and sourceData) ${JSON.stringify(coreMsg, null, 2)}`);
3114
+ debug$j(`${this.header.name}: onBotPrepare (cleaned by binary and base64 data and sourceData) ${JSON.stringify(coreMsg, null, 2)}`);
3170
3115
  }
3171
3116
  } catch (err) {
3172
3117
  const failErr = botiumErrorFromErr$1(`${this.header.name}/${convoStep.stepTag}: onBotPrepare error - ${err.message || err}`, err);
3173
- debug$i(failErr);
3118
+ debug$j(failErr);
3174
3119
  try {
3175
3120
  this.scriptingEvents.fail && this.scriptingEvents.fail(failErr, lastMeConvoStep);
3176
3121
  } catch (failErr) {}
@@ -3178,7 +3123,7 @@ class Convo$6 {
3178
3123
  }
3179
3124
  if (!botMsg || !botMsg.messageText && !botMsg.media && !botMsg.buttons && !botMsg.cards && !botMsg.sourceData && !botMsg.nlp) {
3180
3125
  const failErr = new BotiumError$4(`${this.header.name}/${convoStep.stepTag}: bot says nothing`);
3181
- debug$i(failErr);
3126
+ debug$j(failErr);
3182
3127
  try {
3183
3128
  this.scriptingEvents.fail && this.scriptingEvents.fail(failErr, lastMeConvoStep);
3184
3129
  } catch (failErr) {}
@@ -3259,7 +3204,7 @@ class Convo$6 {
3259
3204
  continue;
3260
3205
  }
3261
3206
  const failErr = botiumErrorFromErr$1(`${this.header.name}/${convoStep.stepTag}: assertion error - ${err.message || err}`, err);
3262
- debug$i(failErr);
3207
+ debug$j(failErr);
3263
3208
  try {
3264
3209
  this.scriptingEvents.fail && this.scriptingEvents.fail(failErr, lastMeConvoStep);
3265
3210
  } catch (failErr) {}
@@ -3280,7 +3225,7 @@ class Convo$6 {
3280
3225
  }
3281
3226
  } else {
3282
3227
  const failErr = new BotiumError$4(`${this.header.name}/${convoStep.stepTag}: invalid sender - ${util__default["default"].inspect(convoStep.sender)}`);
3283
- debug$i(failErr);
3228
+ debug$j(failErr);
3284
3229
  try {
3285
3230
  this.scriptingEvents.fail && this.scriptingEvents.fail(failErr);
3286
3231
  } catch (failErr) {}
@@ -3486,7 +3431,7 @@ class Convo$6 {
3486
3431
  throw new BotiumError$4(`Cant find partial convo with name ${includeLogicHook} (available partial convos: ${Object.keys(partialConvos).join(',')})`);
3487
3432
  }
3488
3433
  _getEffectiveConversationRecursive(partialConvo.conversation, [...parentPConvos, includeLogicHook], result, true);
3489
- debug$i(`Partial convo ${includeLogicHook} included`);
3434
+ debug$j(`Partial convo ${includeLogicHook} included`);
3490
3435
  });
3491
3436
  });
3492
3437
  return result;
@@ -3507,10 +3452,48 @@ var Convo_1 = {
3507
3452
  TranscriptError
3508
3453
  };
3509
3454
 
3455
+ const debug$i = debug__default["default"]('botium-core-RetryHelper');
3456
+ var RetryHelper_1 = class RetryHelper {
3457
+ constructor(caps, section, options = {}) {
3458
+ this.retryErrorPatterns = [];
3459
+ const onErrorRegexp = caps[`RETRY_${section.toUpperCase()}_ONERROR_REGEXP`] || [];
3460
+ if (onErrorRegexp) {
3461
+ if (lodash__default["default"].isArray(onErrorRegexp)) {
3462
+ onErrorRegexp.forEach(r => {
3463
+ if (lodash__default["default"].isString(r)) this.retryErrorPatterns.push(new RegExp(r, 'i'));else this.retryErrorPatterns.push(r);
3464
+ });
3465
+ } else if (lodash__default["default"].isString(onErrorRegexp)) {
3466
+ this.retryErrorPatterns.push(new RegExp(onErrorRegexp, 'i'));
3467
+ } else {
3468
+ this.retryErrorPatterns.push(onErrorRegexp);
3469
+ }
3470
+ }
3471
+
3472
+ // to turn on retries, NUMRETRIES or ONERROR_REGEXP has to be set
3473
+ this.retrySettings = {
3474
+ retries: caps[`RETRY_${section.toUpperCase()}_NUMRETRIES`] || (!lodash__default["default"].isNil(options.numRetries) ? options.numRetries : this.retryErrorPatterns.length === 0 ? 0 : 1),
3475
+ factor: caps[`RETRY_${section.toUpperCase()}_FACTOR`] || (lodash__default["default"].isNil(options.factor) ? 1 : options.factor),
3476
+ minTimeout: caps[`RETRY_${section.toUpperCase()}_MINTIMEOUT`] || (lodash__default["default"].isNil(options.minTimeout) ? 1000 : options.minTimeout)
3477
+ };
3478
+ if (this.retrySettings.retries > 0) {
3479
+ debug$i(`Retry for ${section} is enabled. Settings: ${JSON.stringify(this.retrySettings)} Patterns: ${JSON.stringify(this.retryErrorPatterns.map(r => r.toString()))}`);
3480
+ }
3481
+ }
3482
+ shouldRetry(err) {
3483
+ if (!err) return false;
3484
+ if (this.retryErrorPatterns.length === 0) return true;
3485
+ const errString = util__default["default"].inspect(err);
3486
+ for (const re of this.retryErrorPatterns) {
3487
+ if (errString.match(re)) return true;
3488
+ }
3489
+ return false;
3490
+ }
3491
+ };
3492
+
3510
3493
  const {
3511
3494
  toString,
3512
3495
  quoteRegexpString,
3513
- calculateWer
3496
+ calculateWer: calculateWer$1
3514
3497
  } = helper;
3515
3498
  const _normalize = botresponse => {
3516
3499
  if (lodash__default["default"].isUndefined(botresponse) || lodash__default["default"].isNil(botresponse)) return '';
@@ -3578,7 +3561,7 @@ const wer = () => (botresponse, utterance, args) => {
3578
3561
  botresponse = _normalize(botresponse || '');
3579
3562
  utterance = toString(utterance || '');
3580
3563
  const threshold = [',', '.'].find(p => `${args[0]}`.includes(p)) ? parseFloat(args[0]) : parseInt(args[0]) / 100;
3581
- return calculateWer(botresponse, utterance) <= threshold;
3564
+ return calculateWer$1(botresponse, utterance) <= threshold;
3582
3565
  };
3583
3566
  const getMatchFunction$1 = matchingMode => {
3584
3567
  if (matchingMode === 'regexp' || matchingMode === 'regexpIgnoreCase') {
@@ -5372,6 +5355,10 @@ const {
5372
5355
  const {
5373
5356
  getMatchFunction
5374
5357
  } = MatchFunctions;
5358
+ const {
5359
+ calculateWer,
5360
+ toPercent
5361
+ } = helper;
5375
5362
  const globPattern = '**/+(*.convo.txt|*.utterances.txt|*.pconvo.txt|*.scriptingmemory.txt|*.xlsx|*.xlsm|*.convo.csv|*.pconvo.csv|*.utterances.csv|*.yaml|*.yml|*.json|*.md|*.markdown)';
5376
5363
  const skipPattern = /^skip[.\-_]/i;
5377
5364
  const p = (retryHelper, fn) => {
@@ -5632,29 +5619,51 @@ var ScriptingProvider_1 = class ScriptingProvider {
5632
5619
  const found = lodash__default["default"].find(tomatch, utt => this.matchFn(botresponse, utt, this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS]));
5633
5620
  const asserterType = this.caps[Capabilities.SCRIPTING_MATCHING_MODE] === 'wer' ? 'Word Error Rate Asserter' : 'Text Match Asserter';
5634
5621
  if (lodash__default["default"].isNil(found)) {
5635
- let message = `${stepTag}: Bot response `;
5636
- message += meMsg ? `(on ${meMsg}) ` : '';
5637
- message += botresponse ? '"' + botresponse + '"' : '<no response>';
5638
- message += ' expected to match ';
5639
- message += tomatch && tomatch.length > 1 ? 'one of ' : '';
5640
- message += `${tomatch.map(e => e ? '"' + e + '"' : '<any response>').join(', ')}`;
5641
- throw new BotiumError$2(message, {
5642
- type: 'asserter',
5643
- source: asserterType,
5644
- params: {
5645
- matchingMode: this.caps[Capabilities.SCRIPTING_MATCHING_MODE],
5646
- args: this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS] || null
5647
- },
5648
- context: {
5649
- stepTag
5650
- },
5651
- cause: {
5652
- expected: tomatch,
5653
- actual: botresponse,
5654
- matchingMode: this.caps[Capabilities.SCRIPTING_MATCHING_MODE],
5655
- args: this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS] || null
5656
- }
5657
- });
5622
+ if (this.caps[Capabilities.SCRIPTING_MATCHING_MODE] === 'wer') {
5623
+ const wer = calculateWer(botresponse, tomatch[0]);
5624
+ const werArgs = this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS];
5625
+ const threshold = [',', '.'].find(p => `${werArgs[0]}`.includes(p)) ? parseFloat(werArgs[0]) : parseInt(werArgs[0]) / 100;
5626
+ const message = `${stepTag}: Word Error Rate (${toPercent(wer)}) higher than accepted (${toPercent(threshold)})`;
5627
+ throw new BotiumError$2(message, {
5628
+ type: 'asserter',
5629
+ source: asserterType,
5630
+ params: {
5631
+ matchingMode: this.caps[Capabilities.SCRIPTING_MATCHING_MODE],
5632
+ args: this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS] || null
5633
+ },
5634
+ context: {
5635
+ stepTag
5636
+ },
5637
+ cause: {
5638
+ expected: `<=${toPercent(threshold)} (${tomatch})`,
5639
+ actual: `${toPercent(wer)} (${botresponse})`
5640
+ }
5641
+ });
5642
+ } else {
5643
+ let message = `${stepTag}: Bot response `;
5644
+ message += meMsg ? `(on ${meMsg}) ` : '';
5645
+ message += botresponse ? '"' + botresponse + '"' : '<no response>';
5646
+ message += ' expected to match ';
5647
+ message += tomatch && tomatch.length > 1 ? 'one of ' : '';
5648
+ message += `${tomatch.map(e => e ? '"' + e + '"' : '<any response>').join(', ')}`;
5649
+ throw new BotiumError$2(message, {
5650
+ type: 'asserter',
5651
+ source: asserterType,
5652
+ params: {
5653
+ matchingMode: this.caps[Capabilities.SCRIPTING_MATCHING_MODE],
5654
+ args: this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS] || null
5655
+ },
5656
+ context: {
5657
+ stepTag
5658
+ },
5659
+ cause: {
5660
+ expected: tomatch,
5661
+ actual: botresponse,
5662
+ matchingMode: this.caps[Capabilities.SCRIPTING_MATCHING_MODE],
5663
+ args: this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS] || null
5664
+ }
5665
+ });
5666
+ }
5658
5667
  }
5659
5668
  },
5660
5669
  assertBotNotResponse: (botresponse, nottomatch, stepTag, meMsg) => {
@@ -5665,30 +5674,52 @@ var ScriptingProvider_1 = class ScriptingProvider {
5665
5674
  const found = lodash__default["default"].find(nottomatch, utt => this.matchFn(botresponse, utt, this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS]));
5666
5675
  const asserterType = this.caps[Capabilities.SCRIPTING_MATCHING_MODE] === 'wer' ? 'Word Error Rate Asserter' : 'Text Match Asserter';
5667
5676
  if (!lodash__default["default"].isNil(found)) {
5668
- let message = `${stepTag}: Bot response `;
5669
- message += meMsg ? `(on ${meMsg}) ` : '';
5670
- message += botresponse ? '"' + botresponse + '"' : '<no response>';
5671
- message += ' expected NOT to match ';
5672
- message += nottomatch && nottomatch.length > 1 ? 'one of ' : '';
5673
- message += `${nottomatch.map(e => e ? '"' + e + '"' : '<any response>').join(', ')}`;
5674
- throw new BotiumError$2(message, {
5675
- type: 'asserter',
5676
- source: asserterType,
5677
- params: {
5678
- matchingMode: this.caps[Capabilities.SCRIPTING_MATCHING_MODE],
5679
- args: this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS] || null
5680
- },
5681
- context: {
5682
- stepTag
5683
- },
5684
- cause: {
5685
- not: true,
5686
- expected: nottomatch,
5687
- actual: botresponse,
5688
- matchingMode: this.caps[Capabilities.SCRIPTING_MATCHING_MODE],
5689
- args: this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS] || null
5690
- }
5691
- });
5677
+ if (this.caps[Capabilities.SCRIPTING_MATCHING_MODE] === 'wer') {
5678
+ const wer = calculateWer(botresponse, nottomatch[0]);
5679
+ const werArgs = this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS];
5680
+ const threshold = [',', '.'].find(p => `${werArgs[0]}`.includes(p)) ? parseFloat(werArgs[0]) : parseInt(werArgs[0]) / 100;
5681
+ const message = `${stepTag}: Word Error Rate (${toPercent(wer)}) lower than accepted (${toPercent(threshold)})`;
5682
+ throw new BotiumError$2(message, {
5683
+ type: 'asserter',
5684
+ source: asserterType,
5685
+ params: {
5686
+ matchingMode: this.caps[Capabilities.SCRIPTING_MATCHING_MODE],
5687
+ args: this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS] || null
5688
+ },
5689
+ context: {
5690
+ stepTag
5691
+ },
5692
+ cause: {
5693
+ expected: `>=${toPercent(threshold)} (${nottomatch})`,
5694
+ actual: `${toPercent(wer)} (${botresponse})`
5695
+ }
5696
+ });
5697
+ } else {
5698
+ let message = `${stepTag}: Bot response `;
5699
+ message += meMsg ? `(on ${meMsg}) ` : '';
5700
+ message += botresponse ? '"' + botresponse + '"' : '<no response>';
5701
+ message += ' expected NOT to match ';
5702
+ message += nottomatch && nottomatch.length > 1 ? 'one of ' : '';
5703
+ message += `${nottomatch.map(e => e ? '"' + e + '"' : '<any response>').join(', ')}`;
5704
+ throw new BotiumError$2(message, {
5705
+ type: 'asserter',
5706
+ source: asserterType,
5707
+ params: {
5708
+ matchingMode: this.caps[Capabilities.SCRIPTING_MATCHING_MODE],
5709
+ args: this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS] || null
5710
+ },
5711
+ context: {
5712
+ stepTag
5713
+ },
5714
+ cause: {
5715
+ not: true,
5716
+ expected: nottomatch,
5717
+ actual: botresponse,
5718
+ matchingMode: this.caps[Capabilities.SCRIPTING_MATCHING_MODE],
5719
+ args: this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS] || null
5720
+ }
5721
+ });
5722
+ }
5692
5723
  }
5693
5724
  },
5694
5725
  fail: null
@@ -7159,6 +7190,9 @@ var Queue_1 = class Queue {
7159
7190
  }
7160
7191
  };
7161
7192
 
7193
+ const {
7194
+ rimraf: rimraf$1
7195
+ } = rimraf__default["default"];
7162
7196
  const debug$7 = debug__default["default"]('botium-connector-BaseContainer');
7163
7197
  const {
7164
7198
  executeHook: executeHook$1,
@@ -7291,10 +7325,7 @@ var BaseContainer_1 = class BaseContainer {
7291
7325
  }, rimraffed => {
7292
7326
  if (this.caps[Capabilities.CLEANUPTEMPDIR]) {
7293
7327
  debug$7(`Cleanup rimrafing temp dir ${this.tempDirectory}`);
7294
- rimraf__default["default"](this.tempDirectory, err => {
7295
- if (err) debug$7(`Cleanup temp dir ${this.tempDirectory} failed: ${util__default["default"].inspect(err)}`);
7296
- rimraffed();
7297
- });
7328
+ rimraf$1(this.tempDirectory).catch(err => debug$7(`Cleanup temp dir ${this.tempDirectory} failed: ${util__default["default"].inspect(err)}`)).finally(() => rimraffed());
7298
7329
  } else {
7299
7330
  rimraffed();
7300
7331
  }
@@ -8685,13 +8716,9 @@ const {
8685
8716
  var PluginConnectorContainer_1 = class PluginConnectorContainer extends BaseContainer_1 {
8686
8717
  async Validate() {
8687
8718
  await super.Validate();
8688
- const setAsync = isAsync => {
8689
- this.caps.RETRY_CONVO_ASYNC = isAsync;
8690
- };
8691
8719
  this.pluginInstance = tryLoadPlugin(this.caps[Capabilities.CONTAINERMODE], this.caps[Capabilities.PLUGINMODULEPATH], {
8692
8720
  container: this,
8693
8721
  queueBotSays: msg => this._QueueBotSays(msg),
8694
- setAsync: isAsync => setAsync(isAsync),
8695
8722
  bottleneck: this.bottleneck,
8696
8723
  eventEmitter: this.eventEmitter,
8697
8724
  caps: this.caps,
@@ -8822,15 +8849,21 @@ var PluginConnectorContainer_1 = class PluginConnectorContainer extends BaseCont
8822
8849
  }
8823
8850
  };
8824
8851
 
8825
- var require$$2 = getCjsExportFromNamespace(_package$1);
8852
+ var require$$4 = getCjsExportFromNamespace(_package$1);
8826
8853
 
8854
+ const {
8855
+ rimraf
8856
+ } = rimraf__default["default"];
8857
+ const {
8858
+ mkdirpSync
8859
+ } = mkdirp__default["default"];
8827
8860
  const {
8828
8861
  boolean
8829
8862
  } = boolean__default["default"];
8830
8863
  const debug$1 = debug__default["default"]('botium-core-BotDriver');
8831
8864
  const {
8832
8865
  version
8833
- } = require$$2;
8866
+ } = require$$4;
8834
8867
  var BotDriver_1 = class BotDriver {
8835
8868
  constructor(caps = {}, sources = {}, envs = {}) {
8836
8869
  this.eventEmitter = new events__default["default"]();
@@ -8920,7 +8953,7 @@ var BotDriver_1 = class BotDriver {
8920
8953
  }, tempDirectoryCreated => {
8921
8954
  tempDirectory = path__default["default"].resolve(process.cwd(), this.caps[Capabilities.TEMPDIR], sanitizeFilename__default["default"](`${this.caps[Capabilities.PROJECTNAME]} ${moment__default["default"]().format('YYYYMMDD HHmmss')} ${randomatic__default["default"]('Aa0', 5)}`));
8922
8955
  try {
8923
- mkdirp__default["default"].sync(tempDirectory);
8956
+ mkdirpSync(tempDirectory);
8924
8957
  tempDirectoryCreated();
8925
8958
  } catch (err) {
8926
8959
  tempDirectoryCreated(new Error(`Unable to create temp directory ${tempDirectory}: ${err.message}`));
@@ -8948,9 +8981,7 @@ var BotDriver_1 = class BotDriver {
8948
8981
  debug$1(`BotDriver Build error: ${err}`);
8949
8982
  this.eventEmitter.emit(Events.CONTAINER_BUILD_ERROR, err);
8950
8983
  if (tempDirectory) {
8951
- rimraf__default["default"](tempDirectory, err => {
8952
- if (err) debug$1(`Cleanup temp dir ${tempDirectory} failed: ${util__default["default"].inspect(err)}`);
8953
- });
8984
+ rimraf(tempDirectory).catch(err => debug$1(`Cleanup temp dir ${tempDirectory} failed: ${util__default["default"].inspect(err)}`));
8954
8985
  }
8955
8986
  return reject(err);
8956
8987
  }