botium-core 1.11.14 → 1.12.1

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 (52) hide show
  1. package/dist/botium-cjs.js +853 -579
  2. package/dist/botium-cjs.js.map +1 -1
  3. package/dist/botium-es.js +471 -198
  4. package/dist/botium-es.js.map +1 -1
  5. package/index.js +1 -0
  6. package/package.json +29 -29
  7. package/samples/connectors/custom/botium-connector-myapi.js +3 -3
  8. package/samples/extensions/asserterHooks/DummyAsserter.js +3 -3
  9. package/src/Capabilities.js +4 -1
  10. package/src/Defaults.js +1 -0
  11. package/src/Enums.js +6 -0
  12. package/src/containers/plugins/SimpleRestContainer.js +32 -1
  13. package/src/scripting/BotiumError.js +21 -0
  14. package/src/scripting/CompilerCsv.js +1 -1
  15. package/src/scripting/CompilerObjectBase.js +4 -14
  16. package/src/scripting/CompilerTxt.js +4 -15
  17. package/src/scripting/CompilerXlsx.js +81 -25
  18. package/src/scripting/Convo.js +16 -4
  19. package/src/scripting/MatchFunctions.js +21 -0
  20. package/src/scripting/ScriptingProvider.js +55 -40
  21. package/src/scripting/helper.js +57 -4
  22. package/src/scripting/logichook/LogicHookConsts.js +4 -0
  23. package/src/scripting/logichook/LogicHookUtils.js +2 -0
  24. package/src/scripting/logichook/asserter/JsonPathAsserter.js +1 -1
  25. package/src/scripting/logichook/asserter/TextWildcardExactAllAsserter.js +8 -0
  26. package/src/scripting/logichook/asserter/TextWildcardExactAllICAsserter.js +8 -0
  27. package/src/scripting/logichook/asserter/TextWildcardExactAnyAsserter.js +8 -0
  28. package/src/scripting/logichook/asserter/TextWildcardExactAnyICAsserter.js +8 -0
  29. package/src/scripting/logichook/logichooks/ClearQueueLogicHook.js +1 -1
  30. package/src/scripting/logichook/userinput/MediaInput.js +14 -2
  31. package/test/connectors/convos/hello.convo.txt +6 -0
  32. package/test/connectors/simplerest.spec.js +42 -2
  33. package/test/convo/convos/continuefailing.convo.txt +19 -0
  34. package/test/convo/transcript.spec.js +34 -0
  35. package/test/scripting/asserters/convos/text_wildcardexact_all_nok.yml +7 -0
  36. package/test/scripting/asserters/convos/text_wildcardexact_all_ok.yml +7 -0
  37. package/test/scripting/asserters/convos/text_wildcardexact_any_nok.yml +7 -0
  38. package/test/scripting/asserters/convos/text_wildcardexact_any_ok.yml +7 -0
  39. package/test/scripting/asserters/textWildcardExactAllAsserter.spec.js +51 -0
  40. package/test/scripting/asserters/textWildcardExactAnyAsserter.spec.js +51 -0
  41. package/test/scripting/matching/matchingmode.spec.js +43 -0
  42. package/test/scripting/scriptingProvider.spec.js +4 -4
  43. package/test/scripting/scriptingmemory/convosMultiMemorySameCols/buy.convo.txt +6 -0
  44. package/test/scripting/scriptingmemory/convosMultiMemorySameCols/products1.scriptingmemory.txt +2 -0
  45. package/test/scripting/scriptingmemory/convosMultiMemorySameCols/products2.scriptingmemory.txt +2 -0
  46. package/test/scripting/scriptingmemory/convosSimpleCols/buy.convo.txt +8 -0
  47. package/test/scripting/scriptingmemory/convosSimpleCols/product.scriptingmemory.txt +3 -0
  48. package/test/scripting/scriptingmemory/convosTwoTablesCols/buy.convo.txt +6 -0
  49. package/test/scripting/scriptingmemory/convosTwoTablesCols/customer.xlsx +0 -0
  50. package/test/scripting/scriptingmemory/convosTwoTablesCols/product.xlsx +0 -0
  51. package/test/scripting/scriptingmemory/fillScriptingMemoryFromFile.spec.js +45 -0
  52. package/test/scripting/userinputs/mediaInputConvos.spec.js +53 -2
package/index.js CHANGED
@@ -4,6 +4,7 @@ module.exports = {
4
4
  ScriptingConstants: require('./src/scripting/Constants'),
5
5
  Capabilities: require('./src/Capabilities'),
6
6
  Defaults: require('./src/Defaults'),
7
+ Enums: require('./src/Enums'),
7
8
  Source: require('./src/Source'),
8
9
  Events: require('./src/Events'),
9
10
  Plugins: require('./src/Plugins'),
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "botium-core",
3
- "version": "1.11.14",
3
+ "version": "1.12.1",
4
4
  "description": "The Selenium for Chatbots",
5
5
  "main": "index.js",
6
6
  "module": "dist/botium-es.js",
7
7
  "engines": {
8
- "node": ">=10.0.0"
8
+ "node": ">=14.0.0"
9
9
  },
10
10
  "scripts": {
11
11
  "postinstall": "node ./report.js",
@@ -32,66 +32,66 @@
32
32
  },
33
33
  "homepage": "https://www.botium.ai",
34
34
  "dependencies": {
35
- "@babel/runtime": "^7.15.4",
36
- "async": "^3.2.1",
37
- "body-parser": "^1.19.0",
35
+ "@babel/runtime": "^7.16.7",
36
+ "async": "^3.2.3",
37
+ "body-parser": "^1.19.1",
38
38
  "boolean": "^3.1.4",
39
39
  "bottleneck": "^2.19.5",
40
- "csv-parse": "^4.16.3",
41
- "debug": "^4.3.2",
40
+ "csv-parse": "^5.0.4",
41
+ "debug": "^4.3.3",
42
42
  "esprima": "^4.0.1",
43
- "express": "^4.17.1",
43
+ "express": "^4.17.2",
44
44
  "globby": "11.0.4",
45
- "ioredis": "^4.27.9",
45
+ "ioredis": "^4.28.3",
46
46
  "is-class": "^0.0.9",
47
47
  "is-json": "^2.0.1",
48
48
  "jsonpath": "^1.1.1",
49
49
  "lodash": "^4.17.21",
50
- "markdown-it": "^12.2.0",
51
- "mime-types": "^2.1.32",
50
+ "markdown-it": "^12.3.2",
51
+ "mime-types": "^2.1.34",
52
52
  "mkdirp": "^1.0.4",
53
53
  "moment": "^2.29.1",
54
54
  "mustache": "^4.2.0",
55
55
  "promise-retry": "^2.0.1",
56
- "promise.allsettled": "^1.0.4",
56
+ "promise.allsettled": "^1.0.5",
57
57
  "randomatic": "^3.1.1",
58
58
  "request": "^2.88.2",
59
59
  "rimraf": "^3.0.2",
60
60
  "sanitize-filename": "^1.6.3",
61
- "slugify": "^1.6.0",
62
- "socket.io": "^4.2.0",
63
- "socket.io-client": "^4.2.0",
61
+ "slugify": "^1.6.5",
62
+ "socket.io": "^4.4.1",
63
+ "socket.io-client": "^4.4.1",
64
64
  "socketio-auth": "^0.1.1",
65
65
  "swagger-jsdoc": "^6.1.0",
66
- "swagger-ui-express": "^4.1.6",
66
+ "swagger-ui-express": "^4.3.0",
67
67
  "uuid": "^8.3.2",
68
- "vm2": "^3.9.3",
68
+ "vm2": "^3.9.5",
69
69
  "write-yaml": "^1.0.0",
70
- "xlsx": "^0.17.1",
70
+ "xlsx": "^0.17.5",
71
71
  "xregexp": "^5.1.0",
72
72
  "yaml": "^1.10.2"
73
73
  },
74
74
  "devDependencies": {
75
- "@babel/core": "^7.15.5",
76
- "@babel/node": "^7.15.4",
77
- "@babel/plugin-transform-runtime": "^7.15.0",
78
- "@babel/preset-env": "^7.15.6",
75
+ "@babel/core": "^7.16.12",
76
+ "@babel/node": "^7.16.8",
77
+ "@babel/plugin-transform-runtime": "^7.16.10",
78
+ "@babel/preset-env": "^7.16.11",
79
79
  "chai": "^4.3.4",
80
80
  "chai-as-promised": "^7.1.1",
81
81
  "cross-env": "^7.0.3",
82
- "eslint": "^7.32.0",
82
+ "eslint": "^8.7.0",
83
83
  "eslint-config-standard": "^16.0.3",
84
- "eslint-plugin-import": "^2.24.2",
84
+ "eslint-plugin-import": "^2.25.4",
85
85
  "eslint-plugin-node": "^11.1.0",
86
- "eslint-plugin-promise": "^5.1.0",
86
+ "eslint-plugin-promise": "^6.0.0",
87
87
  "eslint-plugin-standard": "^4.1.0",
88
88
  "license-checker": "^25.0.1",
89
89
  "license-compatibility-checker": "^0.3.5",
90
- "mocha": "^9.1.1",
91
- "nock": "^13.1.3",
92
- "npm-check-updates": "^11.8.5",
90
+ "mocha": "^9.2.0",
91
+ "nock": "^13.2.2",
92
+ "npm-check-updates": "^12.2.1",
93
93
  "nyc": "^15.1.0",
94
- "rollup": "^2.56.3",
94
+ "rollup": "^2.66.0",
95
95
  "rollup-plugin-babel": "^4.4.0",
96
96
  "rollup-plugin-commonjs": "^10.1.0",
97
97
  "rollup-plugin-json": "^4.0.0",
@@ -23,13 +23,13 @@ class BotiumConnectorMyApi {
23
23
  [CoreCapabilities.SIMPLEREST_URL]: this.caps[Capabilities.MYAPI_URL],
24
24
  [CoreCapabilities.SIMPLEREST_METHOD]: 'POST',
25
25
  [CoreCapabilities.SIMPLEREST_RESPONSE_JSONPATH]: '$.reply',
26
- [CoreCapabilities.SIMPLEREST_BODY_TEMPLATE]: JSON.stringify({
26
+ [CoreCapabilities.SIMPLEREST_BODY_TEMPLATE]: JSON.stringify({
27
27
  username: 'botium',
28
28
  message: '{{msg.messageText}}',
29
29
  session: '{{botium.conversationId}}',
30
30
  startsession: false,
31
31
  quickreply: null
32
- })
32
+ })
33
33
  }
34
34
  if (this.caps[Capabilities.MYAPI_TOKEN]) {
35
35
  this.delegateCaps[CoreCapabilities.SIMPLEREST_HEADERS_TEMPLATE] = `{ "Authorization": "Token ${this.caps[Capabilities.MYAPI_TOKEN]}"}`
@@ -104,4 +104,4 @@ module.exports = {
104
104
  }
105
105
  ]
106
106
  }
107
- }
107
+ }
@@ -6,17 +6,17 @@ module.exports = class DummyAsserter {
6
6
  this.caps = caps
7
7
  }
8
8
 
9
- assertConvoBegin ({convo, container, args}) {
9
+ assertConvoBegin ({ convo, container, args }) {
10
10
  console.log(`Dummy asserter Begin started with those args: ${utils.inspect(args)}`)
11
11
  return Promise.resolve()
12
12
  }
13
13
 
14
- assertConvoStep ({convo, convoStep, args, botMsg}) {
14
+ assertConvoStep ({ convo, convoStep, args, botMsg }) {
15
15
  console.log(`ConvoStep dummy assertion with those args: ${utils.inspect(args)}, botMessage: ${utils.inspect(botMsg)} ...`)
16
16
  return Promise.resolve()
17
17
  }
18
18
 
19
- assertConvoEnd ({convo, container, transcript, args}) {
19
+ assertConvoEnd ({ convo, container, transcript, args }) {
20
20
  console.log(`ConvoEnd dummy assertion with those args: ${utils.inspect(args)}, transcript: ${utils.inspect(transcript)} ...`)
21
21
  return Promise.resolve()
22
22
  }
@@ -111,8 +111,9 @@ module.exports = {
111
111
  SCRIPTING_NORMALIZE_TEXT: 'SCRIPTING_NORMALIZE_TEXT',
112
112
  SCRIPTING_ENABLE_MEMORY: 'SCRIPTING_ENABLE_MEMORY',
113
113
  SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS: 'SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS',
114
+ SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS: 'SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS',
114
115
  SCRIPTING_FORCE_BOT_CONSUMED: 'SCRIPTING_FORCE_BOT_CONSUMED',
115
- // regexp, regexpIgnoreCase, wildcard, wildcardIgnoreCase, include, includeIgnoreCase, equals, equalsIgnoreCase
116
+ // regexp, regexpIgnoreCase, wildcard, wildcardIgnoreCase, wildcardExact, wildcardExactIgnoreCase, include, includeIgnoreCase, equals, equalsIgnoreCase
116
117
  SCRIPTING_MATCHING_MODE: 'SCRIPTING_MATCHING_MODE',
117
118
  // all, first, random
118
119
  SCRIPTING_UTTEXPANSION_MODE: 'SCRIPTING_UTTEXPANSION_MODE',
@@ -126,6 +127,8 @@ module.exports = {
126
127
  SCRIPTING_MEMORYEXPANSION_KEEP_ORIG: 'SCRIPTING_MEMORYEXPANSION_KEEP_ORIG',
127
128
  // word, non_whitespace, joker
128
129
  SCRIPTING_MEMORY_MATCHING_MODE: 'SCRIPTING_MEMORY_MATCHING_MODE',
130
+ // varnames, testcasenames
131
+ SCRIPTING_MEMORY_COLUMN_MODE: 'SCRIPTING_MEMORY_COLUMN_MODE',
129
132
  // Botium Lifecycle Hooks
130
133
  CUSTOMHOOK_ONBUILD: 'CUSTOMHOOK_ONBUILD',
131
134
  CUSTOMHOOK_ONSTART: 'CUSTOMHOOK_ONSTART',
package/src/Defaults.js CHANGED
@@ -50,6 +50,7 @@ module.exports = {
50
50
  [Capabilities.SCRIPTING_UTTEXPANSION_NAMING_MODE]: 'justLineTag',
51
51
  [Capabilities.SCRIPTING_UTTEXPANSION_NAMING_UTTERANCE_MAX]: '16',
52
52
  [Capabilities.SCRIPTING_MEMORYEXPANSION_KEEP_ORIG]: false,
53
+ [Capabilities.SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS]: false,
53
54
  [Capabilities.SCRIPTING_FORCE_BOT_CONSUMED]: false,
54
55
  [Capabilities.ASSERTERS]: [],
55
56
  [Capabilities.LOGIC_HOOKS]: [],
package/src/Enums.js ADDED
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ E_SCRIPTING_MEMORY_COLUMN_MODE: {
3
+ VARNAMES: 'varnames',
4
+ TESTCASENAMES: 'testcasenames'
5
+ }
6
+ }
@@ -17,6 +17,7 @@ const Defaults = require('../../Defaults').Capabilities
17
17
  const { SCRIPTING_FUNCTIONS } = require('../../scripting/ScriptingMemory')
18
18
  const { getHook, executeHook } = require('../../helpers/HookUtils')
19
19
  const { escapeJSONString } = require('../../helpers/Utils')
20
+ const { BotiumError } = require('../../scripting/BotiumError')
20
21
 
21
22
  Mustache.escape = s => s
22
23
 
@@ -384,6 +385,18 @@ module.exports = class SimpleRestContainer {
384
385
  } else {
385
386
  if (response.statusCode >= 400) {
386
387
  debug(`got error response: ${response.statusCode}/${response.statusMessage}`)
388
+ if (debug.enabled && body) {
389
+ debug(botiumUtils.shortenJsonString(body))
390
+ }
391
+ if (body) {
392
+ const jsonBody = botiumUtils.toJsonWeak(body)
393
+ const errKey = Object.keys(jsonBody).find(k => k.startsWith('err') || k.startsWith('fail'))
394
+ if (errKey) {
395
+ return reject(new BotiumError(`got error response: ${response.statusCode}/${response.statusMessage} - ${jsonBody[errKey]}`, {
396
+ message: botiumUtils.shortenJsonString(body)
397
+ }))
398
+ }
399
+ }
387
400
  return reject(new Error(`got error response: ${response.statusCode}/${response.statusMessage}`))
388
401
  }
389
402
 
@@ -475,6 +488,15 @@ module.exports = class SimpleRestContainer {
475
488
  requestOptions.uri = `${requestOptions.uri}?${appendToUri}`
476
489
  }
477
490
  }
491
+ if (msg.ADD_FORM_PARAM && Object.keys(msg.ADD_FORM_PARAM).length > 0) {
492
+ requestOptions.form = {}
493
+ for (const formKey of Object.keys(msg.ADD_FORM_PARAM)) {
494
+ const formValue = this._getMustachedVal(
495
+ _.isString(msg.ADD_FORM_PARAM[formKey]) ? msg.ADD_FORM_PARAM[formKey] : JSON.stringify(msg.ADD_FORM_PARAM[formKey]),
496
+ false)
497
+ requestOptions.form[formKey] = formValue
498
+ }
499
+ }
478
500
  if (msg.ADD_HEADER && Object.keys(msg.ADD_HEADER).length > 0) {
479
501
  requestOptions.headers = requestOptions.headers || {}
480
502
 
@@ -516,9 +538,15 @@ module.exports = class SimpleRestContainer {
516
538
  await timeout(pingConfig.timeout)
517
539
  } else if (response.statusCode >= 400) {
518
540
  debug(`_waitForUrlResponse on url check ${pingConfig.uri} got error response: ${response.statusCode}/${response.statusMessage}`)
541
+ if (debug.enabled && body) {
542
+ debug(botiumUtils.shortenJsonString(body))
543
+ }
519
544
  await timeout(pingConfig.timeout)
520
545
  } else {
521
- debug(`_waitForUrlResponse success on url check ${pingConfig.uri}`)
546
+ debug(`_waitForUrlResponse success on url check ${pingConfig.uri}: ${response.statusCode}/${response.statusMessage}`)
547
+ if (debug.enabled && body) {
548
+ debug(botiumUtils.shortenJsonString(body))
549
+ }
522
550
  return body
523
551
  }
524
552
  }
@@ -712,6 +740,9 @@ module.exports = class SimpleRestContainer {
712
740
  } else {
713
741
  if (response.statusCode >= 400) {
714
742
  debug(`_runPolling: got error response: ${response.statusCode}/${response.statusMessage}, request: ${JSON.stringify(pollConfig)}`)
743
+ if (debug.enabled && body) {
744
+ debug(botiumUtils.shortenJsonString(body))
745
+ }
715
746
  } else if (body) {
716
747
  debug(`_runPolling: got response code: ${response.statusCode}, body: ${botiumUtils.shortenJsonString(body)}`)
717
748
 
@@ -26,6 +26,27 @@ const BotiumError = class BotiumError extends Error {
26
26
  this.context.message = message.message || message
27
27
  }
28
28
 
29
+ isAsserterError () {
30
+ if (this.context) {
31
+ const errArr = _.isArray(this.context) ? this.context : [this.context]
32
+ const hasNotAsserterError = errArr.findIndex(errDetail => {
33
+ if (errDetail.type === 'list') {
34
+ if (errDetail.errors) {
35
+ return errDetail.errors.findIndex(e => e.type !== 'asserter') >= 0
36
+ } else {
37
+ return true
38
+ }
39
+ } else {
40
+ return errDetail.type !== 'asserter'
41
+ }
42
+ }) >= 0
43
+ if (hasNotAsserterError) return false
44
+ return true
45
+ } else {
46
+ return false
47
+ }
48
+ }
49
+
29
50
  prettify (includeJson) {
30
51
  const lines = []
31
52
  if (this.context) {
@@ -1,4 +1,4 @@
1
- const parse = require('csv-parse/lib/sync')
1
+ const { parse } = require('csv-parse/sync')
2
2
  const _ = require('lodash')
3
3
  const debug = require('debug')('botium-core-CompilerCsv')
4
4
 
@@ -6,7 +6,7 @@ const CompilerBase = require('./CompilerBase')
6
6
  const Constants = require('./Constants')
7
7
  const Utterance = require('./Utterance')
8
8
  const { Convo } = require('./Convo')
9
- const { linesToConvoStep, validSenders } = require('./helper')
9
+ const { linesToConvoStep, validSenders, linesToScriptingMemories } = require('./helper')
10
10
 
11
11
  module.exports = class CompilerObjectBase extends CompilerBase {
12
12
  constructor (context, caps = {}) {
@@ -109,20 +109,10 @@ module.exports = class CompilerObjectBase extends CompilerBase {
109
109
  if (lines && lines.length > 0) {
110
110
  if (_.isString(lines[0])) {
111
111
  if (lines.length > 1) {
112
- const names = lines[0].split('|').map((name) => name.trim()).slice(1)
113
- const scriptingMemories = []
114
- for (let row = 1; row < lines.length; row++) {
115
- const rawRow = lines[row].split('|').map((name) => name.trim())
116
- const caseName = rawRow[0]
117
- const values = rawRow.slice(1)
118
- const json = {}
119
- for (let col = 0; col < names.length; col++) {
120
- json[names[col]] = values[col]
121
- }
122
- const scriptingMemory = { header: { name: caseName }, values: json }
123
- scriptingMemories.push(scriptingMemory)
112
+ const scriptingMemories = linesToScriptingMemories(lines, this.caps[Capabilities.SCRIPTING_MEMORY_COLUMN_MODE])
113
+ if (scriptingMemories && scriptingMemories.length > 0) {
114
+ this.context.AddScriptingMemories(scriptingMemories)
124
115
  }
125
- this.context.AddScriptingMemories(scriptingMemories)
126
116
  return scriptingMemories
127
117
  }
128
118
  } else {
@@ -5,7 +5,7 @@ const Constants = require('./Constants')
5
5
  const CompilerBase = require('./CompilerBase')
6
6
  const Utterance = require('./Utterance')
7
7
  const { ConvoHeader, Convo } = require('./Convo')
8
- const { linesToConvoStep, convoStepToLines, validateConvo, validSenders } = require('./helper')
8
+ const { linesToConvoStep, convoStepToLines, validateConvo, validSenders, linesToScriptingMemories } = require('./helper')
9
9
 
10
10
  module.exports = class CompilerTxt extends CompilerBase {
11
11
  constructor (context, caps = {}) {
@@ -131,21 +131,10 @@ module.exports = class CompilerTxt extends CompilerBase {
131
131
 
132
132
  _compileScriptingMemory (lines) {
133
133
  if (lines && lines.length > 1) {
134
- const names = lines[0].split('|').map((name) => name.trim()).slice(1)
135
- const scriptingMemories = []
136
- for (let row = 1; row < lines.length; row++) {
137
- if (!lines[row] || lines[row].length === 0) continue
138
- const rawRow = lines[row].split('|').map((name) => name.trim())
139
- const caseName = rawRow[0]
140
- const values = rawRow.slice(1)
141
- const json = {}
142
- for (let col = 0; col < names.length; col++) {
143
- json[names[col]] = values[col]
144
- }
145
- const scriptingMemory = { header: { name: caseName }, values: json }
146
- scriptingMemories.push(scriptingMemory)
134
+ const scriptingMemories = linesToScriptingMemories(lines, this.caps[Capabilities.SCRIPTING_MEMORY_COLUMN_MODE])
135
+ if (scriptingMemories && scriptingMemories.length > 0) {
136
+ this.context.AddScriptingMemories(scriptingMemories)
147
137
  }
148
- this.context.AddScriptingMemories(scriptingMemories)
149
138
  return scriptingMemories
150
139
  }
151
140
  }
@@ -4,6 +4,7 @@ const _ = require('lodash')
4
4
  const debug = require('debug')('botium-core-CompilerXlsx')
5
5
 
6
6
  const Capabilities = require('../Capabilities')
7
+ const { E_SCRIPTING_MEMORY_COLUMN_MODE } = require('../Enums')
7
8
  const CompilerBase = require('./CompilerBase')
8
9
  const Constants = require('./Constants')
9
10
  const Utterance = require('./Utterance')
@@ -286,38 +287,93 @@ module.exports = class CompilerXlsx extends CompilerBase {
286
287
  }
287
288
 
288
289
  if (scriptType === Constants.SCRIPTING_TYPE_SCRIPTING_MEMORY) {
289
- const variableNames = []
290
- let colindexTemp = colindex + 1
291
- while (true) {
292
- const variableNameCell = this.colnames[colindexTemp] + rowindex
293
- if (sheet[variableNameCell] && sheet[variableNameCell].v) {
294
- variableNames.push(sheet[variableNameCell].v)
295
- } else {
296
- break
290
+ const guessScriptingMemoryColumnMode = () => {
291
+ const line1Cell = this.colnames[colindex] + (rowindex + 1)
292
+ if (sheet[line1Cell] && sheet[line1Cell].v) {
293
+ if (sheet[line1Cell].v.startsWith('$')) return E_SCRIPTING_MEMORY_COLUMN_MODE.TESTCASENAMES
297
294
  }
298
-
299
- colindexTemp++
295
+ return E_SCRIPTING_MEMORY_COLUMN_MODE.VARNAMES
300
296
  }
297
+ const columnMode = this.caps[Capabilities.SCRIPTING_MEMORY_COLUMN_MODE] || guessScriptingMemoryColumnMode()
298
+
299
+ if (columnMode === E_SCRIPTING_MEMORY_COLUMN_MODE.TESTCASENAMES) {
300
+ const caseNames = []
301
+ let colindexTemp = colindex + 1
302
+ while (true) {
303
+ const caseNameCell = this.colnames[colindexTemp] + rowindex
304
+ if (sheet[caseNameCell] && sheet[caseNameCell].v) {
305
+ caseNames.push(sheet[caseNameCell].v)
306
+ } else {
307
+ break
308
+ }
309
+ colindexTemp++
310
+ }
301
311
 
302
- rowindex += 1
303
- while (true) {
304
- const caseNameCell = this.colnames[colindex] + rowindex
305
- if (sheet[caseNameCell] && sheet[caseNameCell].v) {
306
- const caseName = sheet[caseNameCell].v
307
- const values = {}
308
- for (let i = 0; i < variableNames.length; i++) {
309
- const variableValueCell = this.colnames[colindex + 1 + i] + rowindex
310
- if (sheet[variableValueCell] && sheet[variableValueCell].v) {
311
- values[variableNames[i]] = sheet[variableValueCell].v.toString()
312
- } else {
313
- values[variableNames[i]] = null
312
+ const varNames = []
313
+ const varValues = []
314
+
315
+ rowindex += 1
316
+ while (true) {
317
+ const varNameCell = this.colnames[colindex] + rowindex
318
+ if (sheet[varNameCell] && sheet[varNameCell].v) {
319
+ varNames.push(sheet[varNameCell].v)
320
+ const values = []
321
+ for (let i = 0; i < caseNames.length; i++) {
322
+ const variableValueCell = this.colnames[colindex + 1 + i] + rowindex
323
+ if (sheet[variableValueCell] && sheet[variableValueCell].v) {
324
+ values.push(sheet[variableValueCell].v.toString())
325
+ } else {
326
+ values.push(null)
327
+ }
314
328
  }
329
+ varValues.push(values)
330
+ rowindex += 1
331
+ } else {
332
+ break
315
333
  }
316
- rowindex += 1
334
+ }
335
+ for (let caseIndex = 0; caseIndex < caseNames.length; caseIndex++) {
336
+ const caseName = caseNames[caseIndex]
317
337
 
338
+ const values = varNames.reduce((agg, varName, varIndex) => {
339
+ agg[varName] = varValues[varIndex][caseIndex] || null
340
+ return agg
341
+ }, {})
318
342
  scriptResults.push({ header: { name: caseName }, values: values })
319
- } else {
320
- break
343
+ }
344
+ } else {
345
+ const variableNames = []
346
+ let colindexTemp = colindex + 1
347
+ while (true) {
348
+ const variableNameCell = this.colnames[colindexTemp] + rowindex
349
+ if (sheet[variableNameCell] && sheet[variableNameCell].v) {
350
+ variableNames.push(sheet[variableNameCell].v)
351
+ } else {
352
+ break
353
+ }
354
+ colindexTemp++
355
+ }
356
+
357
+ rowindex += 1
358
+ while (true) {
359
+ const caseNameCell = this.colnames[colindex] + rowindex
360
+ if (sheet[caseNameCell] && sheet[caseNameCell].v) {
361
+ const caseName = sheet[caseNameCell].v
362
+ const values = {}
363
+ for (let i = 0; i < variableNames.length; i++) {
364
+ const variableValueCell = this.colnames[colindex + 1 + i] + rowindex
365
+ if (sheet[variableValueCell] && sheet[variableValueCell].v) {
366
+ values[variableNames[i]] = sheet[variableValueCell].v.toString()
367
+ } else {
368
+ values[variableNames[i]] = null
369
+ }
370
+ }
371
+ rowindex += 1
372
+
373
+ scriptResults.push({ header: { name: caseName }, values: values })
374
+ } else {
375
+ break
376
+ }
321
377
  }
322
378
  }
323
379
  }
@@ -36,7 +36,7 @@ class ConvoStepAssert {
36
36
  }
37
37
 
38
38
  toString () {
39
- return (this.optional ? '?' : '') + (this.not ? '!' : '') + this.name + '(' + (this.args ? this.args.join(',') : 'no args') + ')'
39
+ return (this.optional ? '?' : '') + (this.not ? '!' : '') + this.name + '(' + (this.args ? this.args.map(a => _.truncate(a, { length: 200 })).join(',') : 'no args') + ')'
40
40
  }
41
41
  }
42
42
 
@@ -47,7 +47,7 @@ class ConvoStepLogicHook {
47
47
  }
48
48
 
49
49
  toString () {
50
- return this.name + '(' + (this.args ? this.args.join(',') : 'no args') + ')'
50
+ return this.name + '(' + (this.args ? this.args.map(a => _.truncate(a, { length: 200 })).join(',') : 'no args') + ')'
51
51
  }
52
52
  }
53
53
 
@@ -58,7 +58,7 @@ class ConvoStepUserInput {
58
58
  }
59
59
 
60
60
  toString () {
61
- return this.name + '(' + (this.args ? this.args.join(',') : 'no args') + ')'
61
+ return this.name + '(' + (this.args ? this.args.map(a => _.truncate(a, { length: 200 })).join(',') : 'no args') + ')'
62
62
  }
63
63
  }
64
64
 
@@ -511,7 +511,13 @@ class Convo {
511
511
  }
512
512
  }
513
513
  transcriptStep.err = err
514
- throw err
514
+ if (err instanceof BotiumError && container.caps[Capabilities.SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS]) {
515
+ if (!err.isAsserterError()) {
516
+ throw err
517
+ }
518
+ } else {
519
+ throw err
520
+ }
515
521
  } finally {
516
522
  if (convoStep.sender !== 'begin' && convoStep.sender !== 'end' && !skipTranscriptStep) {
517
523
  transcriptStep.scriptingMemory = Object.assign({}, scriptingMemory)
@@ -526,6 +532,12 @@ class Convo {
526
532
  transcript.steps = transcriptSteps.filter(s => s)
527
533
  transcript.scriptingMemory = scriptingMemory
528
534
  transcript.convoEnd = new Date()
535
+ if (container.caps[Capabilities.SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS]) {
536
+ const transcriptStepErrs = transcript.steps.filter(s => s.err).map(s => s.err)
537
+ if (transcriptStepErrs && transcriptStepErrs.length > 0) {
538
+ transcript.err = botiumErrorFromList([transcriptStepErrs, transcript.err].filter(e => e), {})
539
+ }
540
+ }
529
541
  }
530
542
  }
531
543
 
@@ -37,6 +37,24 @@ const wildcard = (ignoreCase) => (botresponse, utterance) => {
37
37
  return regexp.test(botresponse)
38
38
  }
39
39
 
40
+ const wildcardExact = (ignoreCase) => (botresponse, utterance) => {
41
+ if (_.isUndefined(botresponse)) {
42
+ if (utterance.trim() === '*') return true
43
+ else return false
44
+ }
45
+ utterance = toString(utterance)
46
+ botresponse = _normalize(botresponse)
47
+
48
+ const numWildcards = utterance.split('*').length - 1
49
+ if (numWildcards > 10) {
50
+ throw new Error('Maximum number of 10 wildcards supported.')
51
+ }
52
+ const utteranceRe = '^' + quoteRegexpString(utterance).replace(/\\\*/g, '(.*)') + '$'
53
+
54
+ const regexp = ignoreCase ? (new RegExp(utteranceRe, 'i')) : (new RegExp(utteranceRe, ''))
55
+ return regexp.test(botresponse)
56
+ }
57
+
40
58
  const include = (ignoreCase) => (botresponse, utterance) => {
41
59
  if (_.isUndefined(botresponse)) return false
42
60
  utterance = toString(utterance)
@@ -66,6 +84,8 @@ const getMatchFunction = (matchingMode) => {
66
84
  return regexp(matchingMode === 'regexpIgnoreCase')
67
85
  } else if (matchingMode === 'wildcard' || matchingMode === 'wildcardIgnoreCase' || matchingMode === 'wildcardLowerCase') {
68
86
  return wildcard(matchingMode === 'wildcardIgnoreCase' || matchingMode === 'wildcardLowerCase')
87
+ } else if (matchingMode === 'wildcardExact' || matchingMode === 'wildcardExactIgnoreCase') {
88
+ return wildcardExact(matchingMode === 'wildcardExactIgnoreCase')
69
89
  } else if (matchingMode === 'include' || matchingMode === 'includeIgnoreCase' || matchingMode === 'includeLowerCase') {
70
90
  return include(matchingMode === 'includeIgnoreCase' || matchingMode === 'includeLowerCase')
71
91
  } else if (matchingMode === 'equals' || matchingMode === 'equalsIgnoreCase') {
@@ -78,6 +98,7 @@ const getMatchFunction = (matchingMode) => {
78
98
  module.exports = {
79
99
  regexp,
80
100
  wildcard,
101
+ wildcardExact,
81
102
  include,
82
103
  equals,
83
104
  getMatchFunction