botium-core 1.13.4 → 1.13.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "botium-core",
3
- "version": "1.13.4",
3
+ "version": "1.13.5",
4
4
  "description": "The Selenium for Chatbots",
5
5
  "main": "index.js",
6
6
  "module": "dist/botium-es.js",
@@ -32,7 +32,7 @@
32
32
  },
33
33
  "homepage": "https://www.botium.ai",
34
34
  "dependencies": {
35
- "@babel/runtime": "^7.18.6",
35
+ "@babel/runtime": "^7.19.0",
36
36
  "async": "^3.2.4",
37
37
  "body-parser": "^1.20.0",
38
38
  "boolean": "^3.2.0",
@@ -42,7 +42,7 @@
42
42
  "esprima": "^4.0.1",
43
43
  "express": "^4.18.1",
44
44
  "globby": "11.0.4",
45
- "ioredis": "^5.1.0",
45
+ "ioredis": "^5.2.3",
46
46
  "is-class": "^0.0.9",
47
47
  "is-json": "^2.0.1",
48
48
  "jsonpath": "^1.1.1",
@@ -59,13 +59,13 @@
59
59
  "rimraf": "^3.0.2",
60
60
  "sanitize-filename": "^1.6.3",
61
61
  "slugify": "^1.6.5",
62
- "socket.io": "^4.5.1",
63
- "socket.io-client": "^4.5.1",
62
+ "socket.io": "^4.5.2",
63
+ "socket.io-client": "^4.5.2",
64
64
  "socketio-auth": "^0.1.1",
65
- "swagger-jsdoc": "^6.2.1",
66
- "swagger-ui-express": "^4.4.0",
67
- "uuid": "^8.3.2",
68
- "vm2": "^3.9.10",
65
+ "swagger-jsdoc": "^6.2.5",
66
+ "swagger-ui-express": "^4.5.0",
67
+ "uuid": "^9.0.0",
68
+ "vm2": "^3.9.11",
69
69
  "word-error-rate": "0.0.7",
70
70
  "write-yaml": "^1.0.0",
71
71
  "xlsx": "^0.18.5",
@@ -73,27 +73,27 @@
73
73
  "yaml": "^2.1.1"
74
74
  },
75
75
  "devDependencies": {
76
- "@babel/core": "^7.18.6",
77
- "@babel/node": "^7.18.6",
78
- "@babel/plugin-transform-runtime": "^7.18.6",
79
- "@babel/preset-env": "^7.18.6",
76
+ "@babel/core": "^7.19.3",
77
+ "@babel/node": "^7.19.1",
78
+ "@babel/plugin-transform-runtime": "^7.19.1",
79
+ "@babel/preset-env": "^7.19.3",
80
80
  "chai": "^4.3.6",
81
81
  "chai-as-promised": "^7.1.1",
82
82
  "cross-env": "^7.0.3",
83
- "eslint": "^8.19.0",
83
+ "eslint": "^8.24.0",
84
84
  "eslint-config-standard": "^17.0.0",
85
85
  "eslint-plugin-import": "^2.26.0",
86
86
  "eslint-plugin-mocha": "^10.1.0",
87
- "eslint-plugin-n": "^15.2.4",
88
- "eslint-plugin-promise": "^6.0.0",
87
+ "eslint-plugin-n": "^15.3.0",
88
+ "eslint-plugin-promise": "^6.0.1",
89
89
  "eslint-plugin-standard": "^4.1.0",
90
90
  "license-checker": "^25.0.1",
91
91
  "license-compatibility-checker": "^0.3.5",
92
92
  "mocha": "^10.0.0",
93
- "nock": "^13.2.8",
94
- "npm-check-updates": "^15.2.6",
93
+ "nock": "^13.2.9",
94
+ "npm-check-updates": "^16.3.4",
95
95
  "nyc": "^15.1.0",
96
- "rollup": "^2.76.0",
96
+ "rollup": "^2.79.1",
97
97
  "rollup-plugin-babel": "^4.4.0",
98
98
  "rollup-plugin-commonjs": "^10.1.0",
99
99
  "rollup-plugin-json": "^4.0.0",
@@ -256,6 +256,7 @@ module.exports = class CompilerXlsx extends CompilerBase {
256
256
  if (!convo.header.name) {
257
257
  convo.header.name = `${convo.header.sheetname}-${this.colnames[convo.header.colindex]}${formatRowIndex(convo.header.rowindex)}`
258
258
  }
259
+ // it is not used anymore?
259
260
  convo.header.sort = convo.header.name
260
261
  scriptResults.push(convo)
261
262
  })
@@ -73,6 +73,32 @@ const SCRIPTING_FUNCTIONS_RAW = {
73
73
  return Date.now()
74
74
  },
75
75
 
76
+ $tomorrow: (pattern) => {
77
+ if (pattern) {
78
+ return moment().add(1, 'day').format(pattern)
79
+ }
80
+ return moment().add(1, 'day').toDate().toLocaleDateString()
81
+ },
82
+ $yesterday: (pattern) => {
83
+ if (pattern) {
84
+ return moment().subtract(1, 'day').format(pattern)
85
+ }
86
+ return moment().subtract(1, 'day').toDate().toLocaleDateString()
87
+ },
88
+
89
+ $date_add: (amount, unit, pattern) => {
90
+ if (pattern) {
91
+ return moment().add(amount, unit).format(pattern)
92
+ }
93
+ return moment().add(amount, unit).toDate().toLocaleDateString()
94
+ },
95
+ $date_subtract: (amount, unit, pattern) => {
96
+ if (pattern) {
97
+ return moment().subtract(amount, unit).format(pattern)
98
+ }
99
+ return moment().subtract(amount, unit).toDate().toLocaleDateString()
100
+ },
101
+
76
102
  $year: () => {
77
103
  return new Date().getFullYear()
78
104
  },
@@ -168,7 +194,8 @@ const SCRIPTING_FUNCTIONS_RAW = {
168
194
  require: false,
169
195
  env: caps[Capabilities.SECURITY_ALLOW_UNSAFE] ? process.env : {},
170
196
  sandbox: {
171
- caps
197
+ caps,
198
+ moment
172
199
  }
173
200
  })
174
201
  return vm.run(`module.exports = (${code})`)
@@ -246,7 +273,19 @@ const _apply = (scriptingMemory, str, caps, mockMsg) => {
246
273
  for (const match of matches) {
247
274
  if (match.indexOf('(') > 0) {
248
275
  const arg = match.substring(match.indexOf('(') + 1, match.lastIndexOf(')')).replace(/\\\)/g, ')')
249
- str = str.replace(match, SCRIPTING_FUNCTIONS[key].handler(caps, arg, mockMsg))
276
+ let args = [arg]
277
+ if (SCRIPTING_FUNCTIONS[key].numberOfArguments > 1) {
278
+ args = arg.split(',')
279
+ }
280
+ args = args.map(arg => {
281
+ arg = arg.trim()
282
+ if (arg.startsWith('"') && arg.endsWith('"')) {
283
+ return arg.substring(1, arg.length - 1)
284
+ } else {
285
+ return arg
286
+ }
287
+ })
288
+ str = str.replace(match, SCRIPTING_FUNCTIONS[key].handler(caps, ...args, mockMsg))
250
289
  } else {
251
290
  str = str.replace(match, SCRIPTING_FUNCTIONS[key].handler(caps))
252
291
  }
@@ -868,27 +868,68 @@ module.exports = class ScriptingProvider {
868
868
  this._sortConvos()
869
869
  }
870
870
 
871
- ExpandConvos () {
871
+ ExpandConvos (options = {}) {
872
+ options = Object.assign({
873
+ // use skip and keep, or justHeader
874
+ justHeader: false,
875
+ // drop unwanted convos
876
+ convoFilter: null
877
+ }, options)
878
+ const context = { count: 0 }
872
879
  const expandedConvos = []
873
880
  debug(`ExpandConvos - Using utterances expansion mode: ${this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE]}`)
874
881
  this.convos.forEach((convo) => {
875
882
  convo.expandPartialConvos()
876
- this._expandConvo(expandedConvos, convo)
883
+ for (const expanded of this._expandConvo(convo, options, context)) {
884
+ expanded.header.assertionCount = this.GetAssertionCount(expanded)
885
+ if (options.justHeader) {
886
+ const ConvoWithOnlyHeader = {
887
+ header: {
888
+ name: expanded.header.name,
889
+ assertionCount: expanded.header.assertionCount
890
+ }
891
+ }
892
+ expandedConvos.push(ConvoWithOnlyHeader)
893
+ } else {
894
+ expandedConvos.push(expanded)
895
+ }
896
+ }
877
897
  })
878
898
  this.convos = expandedConvos
879
- this._sortConvos()
899
+ if (!options.justHeader) {
900
+ this._sortConvos()
901
+ } else {
902
+ this._updateConvos()
903
+ }
904
+ }
905
+
906
+ ExpandConvosIterable (options = {}) {
907
+ options = Object.assign({
908
+ // drop unwanted convos
909
+ convoFilter: null
910
+ }, options)
911
+ debug(`ExpandConvos - Using utterances expansion mode: ${this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE]}`)
912
+ // creating a nested generator, calling the other.
913
+ // We hope this.convos does not changes while this iterator is used
914
+ const _convosIterable = function * (options) {
915
+ const context = { count: 0 }
916
+ for (const convo of this.convos) {
917
+ convo.expandPartialConvos()
918
+ yield * this._expandConvo(convo, options, context)
919
+ }
920
+ }.bind(this)
921
+
922
+ this.convosIterable = _convosIterable(options)
880
923
  }
881
924
 
882
925
  /**
883
- *
884
- * @param expandedConvos
926
+ * This is a generator function with yield
885
927
  * @param currentConvo
886
928
  * @param convoStepIndex
887
929
  * @param convoStepsStack list of ConvoSteps
888
- * @param context {width: }
889
930
  * @private
890
931
  */
891
- _expandConvo (expandedConvos, currentConvo, convoStepIndex = 0, convoStepsStack = [], context = {}) {
932
+ * _expandConvo (currentConvo, options, context, convoStepIndex = 0, convoStepsStack = []) {
892
933
  const utterancePostfix = (lineTag, uttOrUserInput) => {
893
934
  const naming = this.caps[Capabilities.SCRIPTING_UTTEXPANSION_NAMING_MODE] || Defaults.capabilities[Capabilities.SCRIPTING_UTTEXPANSION_NAMING_MODE]
894
935
  if (naming === 'justLineTag') {
@@ -908,7 +949,7 @@ module.exports = class ScriptingProvider {
908
949
  if (currentStep.sender === 'bot' || currentStep.sender === 'begin' || currentStep.sender === 'end') {
909
950
  const currentStepsStack = convoStepsStack.slice()
910
951
  currentStepsStack.push(_.cloneDeep(currentStep))
911
- this._expandConvo(expandedConvos, currentConvo, convoStepIndex + 1, currentStepsStack, context)
952
+ yield * this._expandConvo(currentConvo, options, context, convoStepIndex + 1, currentStepsStack)
912
953
  } else if (currentStep.sender === 'me') {
913
954
  let useUnexpanded = true
914
955
  if (currentStep.messageText) {
@@ -925,28 +966,32 @@ module.exports = class ScriptingProvider {
925
966
  }
926
967
  if (this.utterances[uttName]) {
927
968
  const allutterances = this.utterances[uttName].utterances
928
- const processSampleUtterances = (sampleutterances, myContext) => {
929
- sampleutterances.forEach((utt, index) => {
930
- processSampleUtterance(utt, sampleutterances.length, index, Object.assign({ indexExpansionModeIndex: index }, myContext || context))
931
- })
969
+ const processSampleUtterances = function * (sampleutterances, myContext) {
970
+ for (let index = 0; index < sampleutterances.length; index++) {
971
+ yield * processSampleUtterance(sampleutterances[index], sampleutterances.length, index, Object.assign({ indexExpansionModeIndex: index }, myContext || context))
972
+ }
932
973
  }
933
- const processSampleUtterance = (sampleutterance, length, index, myContext) => {
934
- const lineTag = `${index + 1}`.padStart(`${length}`.length, '0')
974
+ const processSampleUtterance = function * (sampleutterance, length, index, myContext) {
935
975
  const currentStepsStack = convoStepsStack.slice()
936
976
  if (uttArgs) {
937
977
  sampleutterance = util.format(sampleutterance, ...uttArgs)
938
978
  }
939
979
  currentStepsStack.push(Object.assign(_.cloneDeep(currentStep), { messageText: sampleutterance }))
940
980
  const currentConvoLabeled = _.cloneDeep(currentConvo)
941
- Object.assign(currentConvoLabeled.header, { name: `${currentConvo.header.name}/${uttName}-${utterancePostfix(lineTag, sampleutterance)}` })
981
+ if (length > 1) {
982
+ const lineTag = `${index + 1}`.padStart(`${length}`.length, '0')
983
+ Object.assign(currentConvoLabeled.header, { name: `${currentConvo.header.name}/${uttName}-${utterancePostfix(lineTag, sampleutterance)}` })
984
+ }
942
985
  if (!currentConvoLabeled.sourceTag) currentConvoLabeled.sourceTag = {}
943
986
  if (!currentConvoLabeled.sourceTag.origConvoName) currentConvoLabeled.sourceTag.origConvoName = currentConvo.header.name
944
- this._expandConvo(expandedConvos, currentConvoLabeled, convoStepIndex + 1, currentStepsStack, myContext || context)
945
- }
946
- if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'index') {
987
+ yield * this._expandConvo(currentConvoLabeled, options, myContext || context, convoStepIndex + 1, currentStepsStack)
988
+ }.bind(this)
989
+ if (allutterances.length === 1) {
990
+ yield * processSampleUtterances([allutterances[0]], context)
991
+ } else if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'index') {
947
992
  if (_.isNil(context.indexExpansionModeWidth)) {
948
993
  // executed for the first found utterance
949
- processSampleUtterances(allutterances, Object.assign({}, context, { indexExpansionModeWidth: allutterances.length }))
994
+ yield * processSampleUtterances(allutterances, Object.assign({}, context, { indexExpansionModeWidth: allutterances.length }))
950
995
  } else {
951
996
  if (_.isNil(context.indexExpansionModeIndex)) {
952
997
  throw new Error('indexExpansionModeIndex must be set!')
@@ -957,58 +1002,67 @@ module.exports = class ScriptingProvider {
957
1002
  debug(`While expanding convos by index found in utterance "${uttName}" less examples (${allutterances.length}) as expected (${context.indexExpansionModeWidth})`)
958
1003
  }
959
1004
  const myContext = Object.assign({}, context, { indexExpansionModeWidth: Math.max(allutterances.length, context.indexExpansionModeWidth) })
960
- processSampleUtterance(allutterances[localIndex], allutterances.length, localIndex, myContext)
1005
+ yield * processSampleUtterance(allutterances[localIndex], allutterances.length, localIndex, myContext)
961
1006
  if (allutterances.length > context.indexExpansionModeWidth && context.indexExpansionModeIndex + 1 === context.indexExpansionModeWidth) {
962
1007
  debug(`While expanding convos by index found in utterance "${uttName}" more examples (${allutterances.length}) as expected (${context.indexExpansionModeWidth})`)
963
1008
  for (let i = context.indexExpansionModeWidth; i < allutterances.length; i++) {
964
1009
  // if we found a utterance with more examples as any utterances before, we have to start new 'thread'
965
1010
  const myContext = Object.assign({}, context, { indexExpansionModeWidth: allutterances.length, indexExpansionModeIndex: i })
966
- processSampleUtterance(allutterances[i], allutterances.length, i, myContext)
1011
+ yield * processSampleUtterance(allutterances[i], allutterances.length, i, myContext)
967
1012
  }
968
1013
  }
969
1014
  }
970
1015
  } else {
971
1016
  if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'first') {
972
- processSampleUtterances([allutterances[0]])
1017
+ yield * processSampleUtterances([allutterances[0]], context)
973
1018
  } else if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'random') {
974
- processSampleUtterances(allutterances
1019
+ yield * processSampleUtterances(allutterances
975
1020
  .map(x => ({ x, r: Math.random() }))
976
1021
  .sort((a, b) => a.r - b.r)
977
1022
  .map(a => a.x)
978
- .slice(0, this.caps[Capabilities.SCRIPTING_UTTEXPANSION_RANDOM_COUNT]))
1023
+ .slice(0, this.caps[Capabilities.SCRIPTING_UTTEXPANSION_RANDOM_COUNT]), context)
979
1024
  } else {
980
- processSampleUtterances(allutterances)
1025
+ yield * processSampleUtterances(allutterances, context)
981
1026
  }
982
1027
  }
983
1028
  useUnexpanded = false
984
1029
  }
985
1030
  }
986
1031
  if (currentStep.userInputs && currentStep.userInputs.length > 0) {
987
- currentStep.userInputs.forEach((ui, uiIndex) => {
1032
+ for (let uiIndex = 0; uiIndex < currentStep.userInputs.length; uiIndex++) {
1033
+ const ui = currentStep.userInputs[uiIndex]
988
1034
  const userInput = this.userInputs[ui.name]
989
1035
  if (userInput && userInput.expandConvo) {
990
1036
  const expandedUserInputs = userInput.expandConvo({ convo: currentConvo, convoStep: currentStep, args: ui.args })
991
1037
  if (expandedUserInputs && expandedUserInputs.length > 0) {
992
1038
  // let sampleinputs = expandedUserInputs
993
- const processSampleInputs = (sampleinputs, myContext, uiIndex) => {
994
- sampleinputs.forEach((input, index) => {
995
- processSampleInput(input, sampleinputs.length, index, Object.assign({ indexExpansionModeIndex: index }, myContext || context), uiIndex)
996
- })
1039
+ const processSampleInputs = function * (sampleinputs, myContext, uiIndex) {
1040
+ for (let index = 0; index < sampleinputs.length; index++) {
1041
+ yield * processSampleInput(sampleinputs[index], sampleinputs.length, index, Object.assign({ indexExpansionModeIndex: index }, myContext || context), uiIndex)
1042
+ }
997
1043
  }
998
- const processSampleInput = (sampleinput, length, index, myContext, uiIndex) => {
999
- const lineTag = `${index + 1}`.padStart(`${length}`.length, '0')
1044
+ const processSampleInput = function * (sampleinput, length, index, myContext, uiIndex) {
1000
1045
  const currentStepsStack = convoStepsStack.slice()
1001
1046
  const currentStepMod = _.cloneDeep(currentStep)
1002
1047
  currentStepMod.userInputs[uiIndex] = sampleinput
1003
1048
 
1004
1049
  currentStepsStack.push(currentStepMod)
1005
1050
  const currentConvoLabeled = _.cloneDeep(currentConvo)
1006
- Object.assign(currentConvoLabeled.header, { name: `${currentConvo.header.name}/${ui.name}-${utterancePostfix(lineTag, (sampleinput.args && sampleinput.args.length) ? sampleinput.args.join(', ') : 'no-args')}` })
1007
- this._expandConvo(expandedConvos, currentConvoLabeled, convoStepIndex + 1, currentStepsStack, myContext || context)
1008
- }
1009
- if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'index') {
1051
+ if (length > 1) {
1052
+ if (sampleinput.convoPostfix) {
1053
+ Object.assign(currentConvoLabeled.header, { name: `${currentConvo.header.name}/${ui.name}-${sampleinput.convoPostfix}` })
1054
+ } else {
1055
+ const lineTag = `${index + 1}`.padStart(`${length}`.length, '0')
1056
+ Object.assign(currentConvoLabeled.header, { name: `${currentConvo.header.name}/${ui.name}-${utterancePostfix(lineTag, (sampleinput.args && sampleinput.args.length) ? sampleinput.args.join(', ') : 'no-args')}` })
1057
+ }
1058
+ }
1059
+ yield * this._expandConvo(currentConvoLabeled, options, myContext || context, convoStepIndex + 1, currentStepsStack)
1060
+ }.bind(this)
1061
+ if (expandedUserInputs.length === 1) {
1062
+ yield * processSampleInputs([expandedUserInputs[0]], context, uiIndex)
1063
+ } else if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'index') {
1010
1064
  if (_.isNil(context.indexExpansionModeWidth)) {
1011
- processSampleInputs(expandedUserInputs, Object.assign({}, context, { indexExpansionModeWidth: expandedUserInputs.length }), uiIndex)
1065
+ yield * processSampleInputs(expandedUserInputs, Object.assign({}, context, { indexExpansionModeWidth: expandedUserInputs.length }), uiIndex)
1012
1066
  } else {
1013
1067
  if (_.isNil(context.indexExpansionModeIndex)) {
1014
1068
  throw new Error('indexExpansionModeIndex must be set!')
@@ -1019,20 +1073,20 @@ module.exports = class ScriptingProvider {
1019
1073
  debug(`While expanding convos by index found user input "${ui.name}, ${ui.args}" less examples (${expandedUserInputs.length}) as expected (${context.indexExpansionModeWidth})`)
1020
1074
  }
1021
1075
  const myContext = Object.assign({}, context, { indexExpansionModeWidth: Math.max(expandedUserInputs.length, context.indexExpansionModeWidth) })
1022
- processSampleInput(expandedUserInputs[localIndex], expandedUserInputs.length, localIndex, myContext, uiIndex)
1076
+ yield * processSampleInput(expandedUserInputs[localIndex], expandedUserInputs.length, localIndex, myContext, uiIndex)
1023
1077
  if (expandedUserInputs.length > context.indexExpansionModeWidth && context.indexExpansionModeIndex + 1 === context.indexExpansionModeWidth) {
1024
1078
  debug(`While expanding convos by index found user input "${ui.name}, ${ui.args}" more examples (${expandedUserInputs.length}) as expected (${context.indexExpansionModeWidth})`)
1025
1079
  for (let i = context.indexExpansionModeWidth; i < expandedUserInputs.length; i++) {
1026
1080
  const myContext = Object.assign({}, context, { indexExpansionModeWidth: expandedUserInputs.length, indexExpansionModeIndex: i })
1027
- processSampleInput(expandedUserInputs[i], expandedUserInputs.length, i, myContext, uiIndex)
1081
+ yield * processSampleInput(expandedUserInputs[i], expandedUserInputs.length, i, myContext, uiIndex)
1028
1082
  }
1029
1083
  }
1030
1084
  }
1031
1085
  } else {
1032
1086
  if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'first') {
1033
- processSampleInputs([expandedUserInputs[0]], context, uiIndex)
1087
+ yield * processSampleInputs([expandedUserInputs[0]], context, uiIndex)
1034
1088
  } else if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'random') {
1035
- processSampleInputs(expandedUserInputs
1089
+ yield * processSampleInputs(expandedUserInputs
1036
1090
  .map(x => ({
1037
1091
  x,
1038
1092
  r: Math.random()
@@ -1041,35 +1095,49 @@ module.exports = class ScriptingProvider {
1041
1095
  .map(a => a.x)
1042
1096
  .slice(0, this.caps[Capabilities.SCRIPTING_UTTEXPANSION_RANDOM_COUNT]), context, uiIndex)
1043
1097
  } else {
1044
- processSampleInputs(expandedUserInputs, context, uiIndex)
1098
+ yield * processSampleInputs(expandedUserInputs, context, uiIndex)
1045
1099
  }
1046
1100
  }
1047
1101
  useUnexpanded = false
1048
1102
  }
1049
1103
  }
1050
- })
1104
+ }
1051
1105
  }
1052
1106
  if (useUnexpanded) {
1053
1107
  const currentStepsStack = convoStepsStack.slice()
1054
1108
  currentStepsStack.push(_.cloneDeep(currentStep))
1055
- this._expandConvo(expandedConvos, currentConvo, convoStepIndex + 1, currentStepsStack, context)
1109
+ yield * this._expandConvo(currentConvo, options, context, convoStepIndex + 1, currentStepsStack)
1056
1110
  }
1057
1111
  }
1058
1112
  } else {
1059
- expandedConvos.push(Object.assign(_.cloneDeep(currentConvo), { conversation: _.cloneDeep(convoStepsStack) }))
1113
+ const expanded = Object.assign(_.cloneDeep(currentConvo), { conversation: _.cloneDeep(convoStepsStack) })
1114
+ if (!options.convoFilter || options.convoFilter(expanded)) {
1115
+ context.count++
1116
+ const logPerEntry = context.count < 10 ? 1 : context.count < 100 ? 10 : context.count < 1000 ? 100 : context.count < 10000 ? 1000 : 10000
1117
+ if (context.count % logPerEntry === 0) {
1118
+ debug(`Convo #${context.count} expanded (${expanded.header.name})`)
1119
+ }
1120
+ yield expanded
1121
+ }
1060
1122
  }
1061
1123
  }
1062
1124
 
1063
1125
  _sortConvos () {
1064
1126
  this.convos = _.sortBy(this.convos, [(convo) => convo.header.sort || convo.header.name])
1127
+ this._updateConvos()
1128
+ }
1129
+
1130
+ _updateConvos () {
1065
1131
  let i = 0
1066
1132
  this.convos.forEach((convo) => {
1067
- convo.header.order = ++i
1068
- if (!convo.header.projectname) {
1069
- convo.header.projectname = this.caps[Capabilities.PROJECTNAME]
1070
- }
1071
- if (!convo.header.testsessionname) {
1072
- convo.header.testsessionname = this.caps[Capabilities.TESTSESSIONNAME]
1133
+ if (convo) {
1134
+ convo.header.order = ++i
1135
+ if (!convo.header.projectname) {
1136
+ convo.header.projectname = this.caps[Capabilities.PROJECTNAME]
1137
+ }
1138
+ if (!convo.header.testsessionname) {
1139
+ convo.header.testsessionname = this.caps[Capabilities.TESTSESSIONNAME]
1140
+ }
1073
1141
  }
1074
1142
  })
1075
1143
  }
@@ -1080,7 +1148,11 @@ module.exports = class ScriptingProvider {
1080
1148
  } else if (convos) {
1081
1149
  this.convos.push(convos)
1082
1150
  }
1083
- this._sortConvos()
1151
+ if (this.convos.filter(c => _.isNil(c))) {
1152
+ this._updateConvos()
1153
+ } else {
1154
+ this._sortConvos()
1155
+ }
1084
1156
  }
1085
1157
 
1086
1158
  AddUtterances (utterances) {
@@ -1334,4 +1406,30 @@ module.exports = class ScriptingProvider {
1334
1406
  ...lines,
1335
1407
  '}'].join('\r\n')
1336
1408
  }
1409
+
1410
+ GetAssertionCount (convo) {
1411
+ if (!convo) {
1412
+ return 0
1413
+ }
1414
+ let counter = 0
1415
+ for (const step of convo.conversation) {
1416
+ if (step.sender === 'bot') {
1417
+ let stepCounter = step.asserters ? step.asserters.length : 0
1418
+ if (step.messageText) {
1419
+ stepCounter++
1420
+ }
1421
+ stepCounter = stepCounter === 0 ? 1 : stepCounter
1422
+ counter += stepCounter
1423
+ }
1424
+ }
1425
+
1426
+ if (convo.convoBegin && convo.convoBegin.asserters) {
1427
+ counter += convo.convoBegin.asserters.length
1428
+ }
1429
+
1430
+ if (convo.convoEnd && convo.convoEnd.asserters) {
1431
+ counter += convo.convoEnd.asserters.length
1432
+ }
1433
+ return counter === 0 ? 1 : counter
1434
+ }
1337
1435
  }
@@ -437,6 +437,8 @@ const convoStepToLines = (step) => {
437
437
  lines.push('MEDIA ' + step.media[0].mediaUri)
438
438
  } else if (step.messageText) {
439
439
  lines.push(step.messageText)
440
+ } else if (step.sourceData) {
441
+ lines.push(JSON.stringify(step.sourceData, null, 2))
440
442
  }
441
443
  step.userInputs && step.userInputs.forEach((userInput) => {
442
444
  lines.push(userInput.name + _formatAppendArgs(userInput.args))
@@ -165,7 +165,8 @@ module.exports = class MediaInput {
165
165
  mediaFiles.forEach(mf => {
166
166
  e.push({
167
167
  name: 'MEDIA',
168
- args: [mf]
168
+ args: [mf],
169
+ convoPostfix: _.last(mf.split('/'))
169
170
  })
170
171
  })
171
172
  } else {
@@ -15,8 +15,15 @@ const buildContext = () => {
15
15
  AddConvos: (c) => {
16
16
  result.convos = result.convos.concat(c)
17
17
  },
18
- AddUtterances: (u) => {
19
- result.utterances = result.utterances.concat(u)
18
+ AddUtterances: (utteranceStructsToAdd) => {
19
+ for (const utteranceStructToAdd of utteranceStructsToAdd) {
20
+ const existing = result.utterances.find(entry => entry.name === utteranceStructToAdd.name)
21
+ if (existing) {
22
+ existing.utterances = existing.utterances.concat(utteranceStructToAdd.utterances)
23
+ } else {
24
+ result.utterances.push(utteranceStructToAdd)
25
+ }
26
+ }
20
27
  },
21
28
  convos: [],
22
29
  utterances: []
@@ -1,3 +1,3 @@
1
- variable_row_len,goodbye, sdf, sdf, sdf,
1
+ variable_row_len,goodbye, col1, col2, col3,
2
2
  hello,goodbye
3
3
  hi,,sd,fs d,f ,sdf ,ds, fsd,f ,sdf, sd,f sd, f,sd f,
@@ -1,4 +1,5 @@
1
1
  const path = require('path')
2
+ const moment = require('moment')
2
3
  const assert = require('chai').assert
3
4
  const BotDriver = require('../../').BotDriver
4
5
  const Capabilities = require('../../').Capabilities
@@ -695,6 +696,39 @@ describe('convo.fillAndApplyScriptingMemory', function () {
695
696
  assert(result.length === 13, '$timestap is invalid')
696
697
  })
697
698
 
699
+ it('tomorrow without format', async function () {
700
+ const result = ScriptingMemory.apply(
701
+ { caps: CAPS_ENABLE_SCRIPTING_MEMORY },
702
+ {},
703
+ '$tomorrow'
704
+ )
705
+ assert.equal(result, moment().add(1, 'day').toDate().toLocaleDateString())
706
+ })
707
+ it('tomorrow with format', async function () {
708
+ const result = ScriptingMemory.apply(
709
+ { caps: CAPS_ENABLE_SCRIPTING_MEMORY },
710
+ {},
711
+ '$tomorrow(YYYY.MM.DD)'
712
+ )
713
+ assert.equal(result, moment().add(1, 'day').format('YYYY.MM.DD'))
714
+ })
715
+ it('yesterday without format', async function () {
716
+ const result = ScriptingMemory.apply(
717
+ { caps: CAPS_ENABLE_SCRIPTING_MEMORY },
718
+ {},
719
+ '$yesterday'
720
+ )
721
+ assert.equal(result, moment().subtract(1, 'day').toDate().toLocaleDateString())
722
+ })
723
+ it('yesterday with format', async function () {
724
+ const result = ScriptingMemory.apply(
725
+ { caps: CAPS_ENABLE_SCRIPTING_MEMORY },
726
+ {},
727
+ '$yesterday(YYYY.MM.DD)'
728
+ )
729
+ assert.equal(result, moment().subtract(1, 'day').format('YYYY.MM.DD'))
730
+ })
731
+
698
732
  it('year', async function () {
699
733
  const result = ScriptingMemory.apply(
700
734
  { caps: CAPS_ENABLE_SCRIPTING_MEMORY },
@@ -703,7 +737,7 @@ describe('convo.fillAndApplyScriptingMemory', function () {
703
737
  )
704
738
 
705
739
  const year = parseInt(result)
706
- assert(year >= 2019 && year <= 2219, '$year invalid')
740
+ assert.equal(year, new Date().getFullYear(), '$year invalid')
707
741
  })
708
742
 
709
743
  it('month', async function () {
@@ -712,8 +746,7 @@ describe('convo.fillAndApplyScriptingMemory', function () {
712
746
  {},
713
747
  '$month'
714
748
  )
715
-
716
- assert(result.length >= 2 && result.length <= 10, '$month invalid')
749
+ assert.equal(result, moment().format('MMMM'), '$month invalid')
717
750
  })
718
751
  it('month_MM', async function () {
719
752
  const result = ScriptingMemory.apply(
@@ -733,7 +766,7 @@ describe('convo.fillAndApplyScriptingMemory', function () {
733
766
  )
734
767
 
735
768
  const dayOfMonth = parseInt(result)
736
- assert(dayOfMonth >= 1 && dayOfMonth <= 35, 'day_of_month invalid')
769
+ assert(dayOfMonth >= 1 && dayOfMonth <= 35, '$day_of_month invalid')
737
770
  })
738
771
 
739
772
  it('day_of_week', async function () {
@@ -746,6 +779,25 @@ describe('convo.fillAndApplyScriptingMemory', function () {
746
779
  assert(result.length >= 2 && result.length <= 20, '$day_of_week invalid')
747
780
  })
748
781
 
782
+ it('date_add', async function () {
783
+ const result = ScriptingMemory.apply(
784
+ { caps: CAPS_ENABLE_SCRIPTING_MEMORY },
785
+ {},
786
+ '$date_add(1, "day", YYYY.MM.DD)'
787
+ )
788
+
789
+ assert.equal(result, moment().add(1, 'day').format('YYYY.MM.DD'))
790
+ })
791
+ it('date_subtract', async function () {
792
+ const result = ScriptingMemory.apply(
793
+ { caps: CAPS_ENABLE_SCRIPTING_MEMORY },
794
+ {},
795
+ '$date_subtract(1, "month", YYYY.MM.DD)'
796
+ )
797
+
798
+ assert.equal(result, moment().subtract(1, 'month').format('YYYY.MM.DD'))
799
+ })
800
+
749
801
  it('random', async function () {
750
802
  const result = ScriptingMemory.apply(
751
803
  { caps: CAPS_ENABLE_SCRIPTING_MEMORY },
@@ -813,6 +865,14 @@ describe('convo.fillAndApplyScriptingMemory', function () {
813
865
  )
814
866
  assert.equal(result, 'botium')
815
867
  })
868
+ it('func with moment', async function () {
869
+ const result = ScriptingMemory.apply(
870
+ { caps: CAPS_ENABLE_SCRIPTING_MEMORY },
871
+ {},
872
+ '$func(moment(\\).subtract(1, "month"\\).startOf("month"\\).format("DD.MM.YYYY"\\))'
873
+ )
874
+ assert.equal(result, moment().subtract(1, 'month').startOf('month').format('DD.MM.YYYY'))
875
+ })
816
876
  it('environment variable', async function () {
817
877
  process.env.MY_VAR_VALUE = 'botium'
818
878
  const result = ScriptingMemory.apply(