botium-core 1.11.15 → 1.12.2

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 (41) hide show
  1. package/dist/botium-cjs.js +420 -172
  2. package/dist/botium-cjs.js.map +1 -1
  3. package/dist/botium-es.js +411 -164
  4. package/dist/botium-es.js.map +1 -1
  5. package/index.js +1 -0
  6. package/package.json +27 -27
  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 -0
  10. package/src/Defaults.js +1 -0
  11. package/src/Enums.js +6 -0
  12. package/src/containers/BaseContainer.js +20 -10
  13. package/src/containers/PluginConnectorContainer.js +1 -0
  14. package/src/containers/plugins/SimpleRestContainer.js +36 -4
  15. package/src/scripting/BotiumError.js +21 -0
  16. package/src/scripting/CompilerCsv.js +1 -1
  17. package/src/scripting/CompilerObjectBase.js +4 -14
  18. package/src/scripting/CompilerTxt.js +4 -15
  19. package/src/scripting/CompilerXlsx.js +81 -25
  20. package/src/scripting/Convo.js +16 -4
  21. package/src/scripting/ScriptingProvider.js +6 -0
  22. package/src/scripting/helper.js +54 -1
  23. package/src/scripting/logichook/LogicHookUtils.js +2 -0
  24. package/src/scripting/logichook/asserter/JsonPathAsserter.js +1 -1
  25. package/src/scripting/logichook/logichooks/ClearQueueLogicHook.js +1 -1
  26. package/src/scripting/logichook/userinput/MediaInput.js +14 -2
  27. package/test/connectors/convos/hello.convo.txt +6 -0
  28. package/test/connectors/simplerest.spec.js +42 -2
  29. package/test/convo/convos/continuefailing.convo.txt +19 -0
  30. package/test/convo/transcript.spec.js +34 -0
  31. package/test/scripting/scriptingProvider.spec.js +4 -4
  32. package/test/scripting/scriptingmemory/convosMultiMemorySameCols/buy.convo.txt +6 -0
  33. package/test/scripting/scriptingmemory/convosMultiMemorySameCols/products1.scriptingmemory.txt +2 -0
  34. package/test/scripting/scriptingmemory/convosMultiMemorySameCols/products2.scriptingmemory.txt +2 -0
  35. package/test/scripting/scriptingmemory/convosSimpleCols/buy.convo.txt +8 -0
  36. package/test/scripting/scriptingmemory/convosSimpleCols/product.scriptingmemory.txt +3 -0
  37. package/test/scripting/scriptingmemory/convosTwoTablesCols/buy.convo.txt +6 -0
  38. package/test/scripting/scriptingmemory/convosTwoTablesCols/customer.xlsx +0 -0
  39. package/test/scripting/scriptingmemory/convosTwoTablesCols/product.xlsx +0 -0
  40. package/test/scripting/scriptingmemory/fillScriptingMemoryFromFile.spec.js +45 -0
  41. 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.15",
3
+ "version": "1.12.2",
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,23 +32,23 @@
32
32
  },
33
33
  "homepage": "https://www.botium.ai",
34
34
  "dependencies": {
35
- "@babel/runtime": "^7.16.0",
36
- "async": "^3.2.2",
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.28.0",
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.33",
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",
@@ -58,40 +58,40 @@
58
58
  "request": "^2.88.2",
59
59
  "rimraf": "^3.0.2",
60
60
  "sanitize-filename": "^1.6.3",
61
- "slugify": "^1.6.1",
62
- "socket.io": "^4.3.1",
63
- "socket.io-client": "^4.3.2",
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
68
  "vm2": "^3.9.5",
69
69
  "write-yaml": "^1.0.0",
70
- "xlsx": "^0.17.3",
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.16.0",
76
- "@babel/node": "^7.16.0",
77
- "@babel/plugin-transform-runtime": "^7.16.0",
78
- "@babel/preset-env": "^7.16.0",
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": "^8.1.0",
82
+ "eslint": "^8.7.0",
83
83
  "eslint-config-standard": "^16.0.3",
84
- "eslint-plugin-import": "^2.25.2",
84
+ "eslint-plugin-import": "^2.25.4",
85
85
  "eslint-plugin-node": "^11.1.0",
86
- "eslint-plugin-promise": "^5.1.1",
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.3",
91
- "nock": "^13.1.4",
92
- "npm-check-updates": "^12.0.0",
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.59.0",
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,6 +111,7 @@ 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
116
  // regexp, regexpIgnoreCase, wildcard, wildcardIgnoreCase, wildcardExact, wildcardExactIgnoreCase, include, includeIgnoreCase, equals, equalsIgnoreCase
116
117
  SCRIPTING_MATCHING_MODE: 'SCRIPTING_MATCHING_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',
@@ -140,6 +143,7 @@ module.exports = {
140
143
  // API Calls Rate Limiting
141
144
  RATELIMIT_USERSAYS_MAXCONCURRENT: 'RATELIMIT_USERSAYS_MAXCONCURRENT',
142
145
  RATELIMIT_USERSAYS_MINTIME: 'RATELIMIT_USERSAYS_MINTIME',
146
+ RATELIMIT_BOTTLENECK_FN: 'RATELIMIT_BOTTLENECK_FN',
143
147
  SECURITY_ALLOW_UNSAFE: 'SECURITY_ALLOW_UNSAFE',
144
148
  PRECOMPILERS: 'PRECOMPILERS'
145
149
  }
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
+ }
@@ -21,7 +21,7 @@ module.exports = class BaseContainer {
21
21
  this.tempDirectory = tempDirectory
22
22
  this.cleanupTasks = []
23
23
  this.queues = {}
24
- this.userSaysLimiter = null
24
+ this.bottleneck = null
25
25
  }
26
26
 
27
27
  Validate () {
@@ -32,18 +32,28 @@ module.exports = class BaseContainer {
32
32
  this.onStopHook = getHook(this.caps, this.caps[Capabilities.CUSTOMHOOK_ONSTOP])
33
33
  this.onCleanHook = getHook(this.caps, this.caps[Capabilities.CUSTOMHOOK_ONCLEAN])
34
34
 
35
- return Promise.resolve()
36
- }
37
-
38
- Build () {
39
- if (this.caps[Capabilities.RATELIMIT_USERSAYS_MAXCONCURRENT] || this.caps[Capabilities.RATELIMIT_USERSAYS_MINTIME]) {
35
+ if (this.caps[Capabilities.RATELIMIT_BOTTLENECK_FN]) {
36
+ if (_.isFunction(this.caps[Capabilities.RATELIMIT_BOTTLENECK_FN])) {
37
+ this.bottleneck = this.caps[Capabilities.RATELIMIT_BOTTLENECK_FN]
38
+ debug('Validate: Applying userSays rate limits from capability')
39
+ } else {
40
+ const limiter = new Bottleneck(this.caps[Capabilities.RATELIMIT_BOTTLENECK_FN])
41
+ this.bottleneck = (fn) => limiter.schedule(fn)
42
+ debug(`Validate: Applying userSays rate limits ${util.inspect(this.caps[Capabilities.RATELIMIT_BOTTLENECK_FN])}`)
43
+ }
44
+ } else if (this.caps[Capabilities.RATELIMIT_USERSAYS_MAXCONCURRENT] || this.caps[Capabilities.RATELIMIT_USERSAYS_MINTIME]) {
40
45
  const opts = {}
41
46
  if (this.caps[Capabilities.RATELIMIT_USERSAYS_MAXCONCURRENT]) opts.maxConcurrent = this.caps[Capabilities.RATELIMIT_USERSAYS_MAXCONCURRENT]
42
47
  if (this.caps[Capabilities.RATELIMIT_USERSAYS_MINTIME]) opts.minTime = this.caps[Capabilities.RATELIMIT_USERSAYS_MINTIME]
43
- this.userSaysLimiter = new Bottleneck(opts)
44
- debug(`Build: Applying userSays rate limits ${util.inspect(opts)}`)
48
+ const limiter = new Bottleneck(opts)
49
+ this.bottleneck = (fn) => limiter.schedule(fn)
50
+ debug(`Validate: Applying userSays rate limits ${util.inspect(opts)}`)
45
51
  }
46
52
 
53
+ return Promise.resolve()
54
+ }
55
+
56
+ Build () {
47
57
  return new Promise((resolve, reject) => {
48
58
  this._RunCustomHook('onBuild', this.onBuildHook)
49
59
  .then(() => resolve(this))
@@ -69,8 +79,8 @@ module.exports = class BaseContainer {
69
79
  const run = () => this._RunCustomHook('onUserSays', this.onUserSaysHook, { meMsg })
70
80
  .then(() => this.UserSaysImpl(meMsg))
71
81
 
72
- if (this.userSaysLimiter) {
73
- return this.userSaysLimiter.schedule(run)
82
+ if (this.bottleneck) {
83
+ return this.bottleneck(run)
74
84
  } else {
75
85
  return run()
76
86
  }
@@ -17,6 +17,7 @@ module.exports = class PluginConnectorContainer extends BaseContainer {
17
17
  {
18
18
  container: this,
19
19
  queueBotSays: (msg) => this._QueueBotSays(msg),
20
+ bottleneck: this.bottleneck,
20
21
  eventEmitter: this.eventEmitter,
21
22
  caps: this.caps,
22
23
  sources: this.sources,
@@ -17,13 +17,15 @@ 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
 
23
24
  module.exports = class SimpleRestContainer {
24
- constructor ({ queueBotSays, caps }) {
25
+ constructor ({ queueBotSays, caps, bottleneck }) {
25
26
  this.queueBotSays = queueBotSays
26
27
  this.caps = Object.assign({}, Defaults, caps)
28
+ this.bottleneck = bottleneck || ((fn) => fn())
27
29
  this.processInbound = false
28
30
  this.redisTopic = this.caps[Capabilities.SIMPLEREST_REDIS_TOPIC] || 'SIMPLEREST_INBOUND_SUBSCRIPTION'
29
31
 
@@ -384,6 +386,18 @@ module.exports = class SimpleRestContainer {
384
386
  } else {
385
387
  if (response.statusCode >= 400) {
386
388
  debug(`got error response: ${response.statusCode}/${response.statusMessage}`)
389
+ if (debug.enabled && body) {
390
+ debug(botiumUtils.shortenJsonString(body))
391
+ }
392
+ if (body) {
393
+ const jsonBody = botiumUtils.toJsonWeak(body)
394
+ const errKey = Object.keys(jsonBody).find(k => k.startsWith('err') || k.startsWith('fail'))
395
+ if (errKey) {
396
+ return reject(new BotiumError(`got error response: ${response.statusCode}/${response.statusMessage} - ${jsonBody[errKey]}`, {
397
+ message: botiumUtils.shortenJsonString(body)
398
+ }))
399
+ }
400
+ }
387
401
  return reject(new Error(`got error response: ${response.statusCode}/${response.statusMessage}`))
388
402
  }
389
403
 
@@ -475,6 +489,15 @@ module.exports = class SimpleRestContainer {
475
489
  requestOptions.uri = `${requestOptions.uri}?${appendToUri}`
476
490
  }
477
491
  }
492
+ if (msg.ADD_FORM_PARAM && Object.keys(msg.ADD_FORM_PARAM).length > 0) {
493
+ requestOptions.form = {}
494
+ for (const formKey of Object.keys(msg.ADD_FORM_PARAM)) {
495
+ const formValue = this._getMustachedVal(
496
+ _.isString(msg.ADD_FORM_PARAM[formKey]) ? msg.ADD_FORM_PARAM[formKey] : JSON.stringify(msg.ADD_FORM_PARAM[formKey]),
497
+ false)
498
+ requestOptions.form[formKey] = formValue
499
+ }
500
+ }
478
501
  if (msg.ADD_HEADER && Object.keys(msg.ADD_HEADER).length > 0) {
479
502
  requestOptions.headers = requestOptions.headers || {}
480
503
 
@@ -506,19 +529,25 @@ module.exports = class SimpleRestContainer {
506
529
  throw new Error(`Failed to ping bot after ${retries} retries`)
507
530
  }
508
531
  tries++
509
- const { err, response, body } = await new Promise((resolve) => {
532
+ const { err, response, body } = await this.bottleneck(() => new Promise((resolve) => {
510
533
  request(pingConfig, (err, response, body) => {
511
534
  resolve({ err, response, body })
512
535
  })
513
- })
536
+ }))
514
537
  if (err) {
515
538
  debug(`_waitForUrlResponse error on url check ${pingConfig.uri}: ${err}`)
516
539
  await timeout(pingConfig.timeout)
517
540
  } else if (response.statusCode >= 400) {
518
541
  debug(`_waitForUrlResponse on url check ${pingConfig.uri} got error response: ${response.statusCode}/${response.statusMessage}`)
542
+ if (debug.enabled && body) {
543
+ debug(botiumUtils.shortenJsonString(body))
544
+ }
519
545
  await timeout(pingConfig.timeout)
520
546
  } else {
521
- debug(`_waitForUrlResponse success on url check ${pingConfig.uri}`)
547
+ debug(`_waitForUrlResponse success on url check ${pingConfig.uri}: ${response.statusCode}/${response.statusMessage}`)
548
+ if (debug.enabled && body) {
549
+ debug(botiumUtils.shortenJsonString(body))
550
+ }
522
551
  return body
523
552
  }
524
553
  }
@@ -712,6 +741,9 @@ module.exports = class SimpleRestContainer {
712
741
  } else {
713
742
  if (response.statusCode >= 400) {
714
743
  debug(`_runPolling: got error response: ${response.statusCode}/${response.statusMessage}, request: ${JSON.stringify(pollConfig)}`)
744
+ if (debug.enabled && body) {
745
+ debug(botiumUtils.shortenJsonString(body))
746
+ }
715
747
  } else if (body) {
716
748
  debug(`_runPolling: got response code: ${response.statusCode}, body: ${botiumUtils.shortenJsonString(body)}`)
717
749
 
@@ -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