botium-core 1.12.0 → 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 (31) hide show
  1. package/dist/botium-cjs.js +290 -115
  2. package/dist/botium-cjs.js.map +1 -1
  3. package/dist/botium-es.js +281 -107
  4. package/dist/botium-es.js.map +1 -1
  5. package/index.js +1 -0
  6. package/package.json +21 -21
  7. package/src/Capabilities.js +3 -0
  8. package/src/Defaults.js +1 -0
  9. package/src/Enums.js +6 -0
  10. package/src/scripting/BotiumError.js +21 -0
  11. package/src/scripting/CompilerObjectBase.js +4 -14
  12. package/src/scripting/CompilerTxt.js +4 -15
  13. package/src/scripting/CompilerXlsx.js +81 -25
  14. package/src/scripting/Convo.js +16 -4
  15. package/src/scripting/ScriptingProvider.js +6 -0
  16. package/src/scripting/helper.js +54 -1
  17. package/src/scripting/logichook/logichooks/ClearQueueLogicHook.js +1 -1
  18. package/src/scripting/logichook/userinput/MediaInput.js +14 -2
  19. package/test/convo/convos/continuefailing.convo.txt +19 -0
  20. package/test/convo/transcript.spec.js +34 -0
  21. package/test/scripting/scriptingProvider.spec.js +4 -4
  22. package/test/scripting/scriptingmemory/convosMultiMemorySameCols/buy.convo.txt +6 -0
  23. package/test/scripting/scriptingmemory/convosMultiMemorySameCols/products1.scriptingmemory.txt +2 -0
  24. package/test/scripting/scriptingmemory/convosMultiMemorySameCols/products2.scriptingmemory.txt +2 -0
  25. package/test/scripting/scriptingmemory/convosSimpleCols/buy.convo.txt +8 -0
  26. package/test/scripting/scriptingmemory/convosSimpleCols/product.scriptingmemory.txt +3 -0
  27. package/test/scripting/scriptingmemory/convosTwoTablesCols/buy.convo.txt +6 -0
  28. package/test/scripting/scriptingmemory/convosTwoTablesCols/customer.xlsx +0 -0
  29. package/test/scripting/scriptingmemory/convosTwoTablesCols/product.xlsx +0 -0
  30. package/test/scripting/scriptingmemory/fillScriptingMemoryFromFile.spec.js +45 -0
  31. 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,6 +1,6 @@
1
1
  {
2
2
  "name": "botium-core",
3
- "version": "1.12.0",
3
+ "version": "1.12.1",
4
4
  "description": "The Selenium for Chatbots",
5
5
  "main": "index.js",
6
6
  "module": "dist/botium-es.js",
@@ -32,22 +32,22 @@
32
32
  },
33
33
  "homepage": "https://www.botium.ai",
34
34
  "dependencies": {
35
- "@babel/runtime": "^7.16.5",
36
- "async": "^3.2.2",
35
+ "@babel/runtime": "^7.16.7",
36
+ "async": "^3.2.3",
37
37
  "body-parser": "^1.19.1",
38
38
  "boolean": "^3.1.4",
39
39
  "bottleneck": "^2.19.5",
40
- "csv-parse": "^5.0.3",
40
+ "csv-parse": "^5.0.4",
41
41
  "debug": "^4.3.3",
42
42
  "esprima": "^4.0.1",
43
43
  "express": "^4.17.2",
44
44
  "globby": "11.0.4",
45
- "ioredis": "^4.28.2",
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.3.0",
50
+ "markdown-it": "^12.3.2",
51
51
  "mime-types": "^2.1.34",
52
52
  "mkdirp": "^1.0.4",
53
53
  "moment": "^2.29.1",
@@ -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.4",
62
- "socket.io": "^4.4.0",
63
- "socket.io-client": "^4.4.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
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.4",
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.5",
76
- "@babel/node": "^7.16.5",
77
- "@babel/plugin-transform-runtime": "^7.16.5",
78
- "@babel/preset-env": "^7.16.5",
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.4.1",
82
+ "eslint": "^8.7.0",
83
83
  "eslint-config-standard": "^16.0.3",
84
- "eslint-plugin-import": "^2.25.3",
84
+ "eslint-plugin-import": "^2.25.4",
85
85
  "eslint-plugin-node": "^11.1.0",
86
- "eslint-plugin-promise": "^5.2.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.3",
91
- "nock": "^13.2.1",
92
- "npm-check-updates": "^12.0.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.61.1",
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",
@@ -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',
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
+ }
@@ -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) {
@@ -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
 
@@ -761,6 +761,7 @@ module.exports = class ScriptingProvider {
761
761
 
762
762
  debug(`ExpandScriptingMemoryToConvos - ${convosExpandedAll.length} convo expanded, added to convos (${this.convos.length}). Result ${convosExpandedAll.length + this.convos.length} convo`)
763
763
  this.convos = this.convos.concat(convosExpandedAll)
764
+ this._sortConvos()
764
765
  }
765
766
 
766
767
  ExpandUtterancesToConvos ({ useNameAsIntent, incomprehensionUtt } = {}) {
@@ -796,6 +797,11 @@ module.exports = class ScriptingProvider {
796
797
  conversation: [
797
798
  {
798
799
  sender: 'me',
800
+ logicHooks: [
801
+ {
802
+ name: 'SKIP_BOT_UNCONSUMED'
803
+ }
804
+ ],
799
805
  messageText: utt.name,
800
806
  stepTag: 'Step 1 - tell utterance'
801
807
  },
@@ -1,6 +1,8 @@
1
1
  const _ = require('lodash')
2
2
  const isJSON = require('is-json')
3
3
 
4
+ const { E_SCRIPTING_MEMORY_COLUMN_MODE } = require('../Enums')
5
+
4
6
  const normalizeText = (str, doCleanup) => {
5
7
  if (str && _.isArray(str)) {
6
8
  str = str.join(' ')
@@ -463,6 +465,56 @@ const convoStepToLines = (step) => {
463
465
  return lines.map(l => l.trim())
464
466
  }
465
467
 
468
+ const linesToScriptingMemories = (lines, columnMode = null) => {
469
+ const guessScriptingMemoryColumnMode = (lines) => {
470
+ if (lines && lines.length > 1) {
471
+ if (lines[1].trim().startsWith('$')) return E_SCRIPTING_MEMORY_COLUMN_MODE.TESTCASENAMES
472
+ }
473
+ return E_SCRIPTING_MEMORY_COLUMN_MODE.VARNAMES
474
+ }
475
+ columnMode = columnMode || guessScriptingMemoryColumnMode(lines)
476
+
477
+ const scriptingMemories = []
478
+
479
+ if (columnMode === E_SCRIPTING_MEMORY_COLUMN_MODE.TESTCASENAMES) {
480
+ const caseNames = lines[0].split('|').map((name) => name.trim()).slice(1)
481
+
482
+ const varNames = []
483
+ const varValues = []
484
+ for (let row = 1; row < lines.length; row++) {
485
+ if (!lines[row] || lines[row].length === 0) continue
486
+ const rawRow = lines[row].split('|').map((name) => name.trim())
487
+ varNames.push(rawRow[0])
488
+ varValues.push(rawRow.slice(1))
489
+ }
490
+ for (let caseIndex = 0; caseIndex < caseNames.length; caseIndex++) {
491
+ const caseName = caseNames[caseIndex]
492
+
493
+ const values = varNames.reduce((agg, varName, varIndex) => {
494
+ agg[varName] = varValues[varIndex][caseIndex] || null
495
+ return agg
496
+ }, {})
497
+ const scriptingMemory = { header: { name: caseName }, values: values }
498
+ scriptingMemories.push(scriptingMemory)
499
+ }
500
+ } else {
501
+ const varNames = lines[0].split('|').map((name) => name.trim()).slice(1)
502
+ for (let row = 1; row < lines.length; row++) {
503
+ if (!lines[row] || lines[row].length === 0) continue
504
+ const rawRow = lines[row].split('|').map((name) => name.trim())
505
+ const caseName = rawRow[0]
506
+ const values = rawRow.slice(1)
507
+ const varValues = {}
508
+ for (let varIndex = 0; varIndex < varNames.length; varIndex++) {
509
+ varValues[varNames[varIndex]] = values[varIndex]
510
+ }
511
+ const scriptingMemory = { header: { name: caseName }, values: varValues }
512
+ scriptingMemories.push(scriptingMemory)
513
+ }
514
+ }
515
+ return scriptingMemories
516
+ }
517
+
466
518
  module.exports = {
467
519
  normalizeText,
468
520
  splitStringInNonEmptyLines,
@@ -475,5 +527,6 @@ module.exports = {
475
527
  convoStepToObject,
476
528
  validSenders,
477
529
  validateSender,
478
- validateConvo
530
+ validateConvo,
531
+ linesToScriptingMemories
479
532
  }
@@ -9,7 +9,7 @@ module.exports = class ClearQueueLogicHook {
9
9
  container._EmptyQueue()
10
10
  }
11
11
 
12
- onMeEnd ({ container }) {
12
+ onMeStart ({ container }) {
13
13
  container._EmptyQueue()
14
14
  }
15
15
 
@@ -20,6 +20,9 @@ module.exports = class MediaInput {
20
20
  }
21
21
 
22
22
  _getResolvedUri (uri, convo) {
23
+ if (uri.startsWith('data:')) {
24
+ return new url.URL(uri)
25
+ }
23
26
  if (this.globalArgs && this.globalArgs.baseUris) {
24
27
  const baseUrisSelector = _.get(convo, this.globalArgs.baseSelector || DEFAULT_BASE_SELECTOR)
25
28
  if (baseUrisSelector && this.globalArgs.baseUris[baseUrisSelector]) {
@@ -134,6 +137,8 @@ module.exports = class MediaInput {
134
137
  }
135
138
  })
136
139
  })
140
+ } else if (uri.protocol === 'data:') {
141
+ return Buffer.from(uri.href.split(',')[1], 'base64')
137
142
  }
138
143
  }
139
144
  }
@@ -142,6 +147,13 @@ module.exports = class MediaInput {
142
147
  return arg.indexOf('*') >= 0
143
148
  }
144
149
 
150
+ _mime (arg) {
151
+ if (arg.startsWith('data:')) {
152
+ return arg.substring(arg.indexOf(':') + 1, arg.indexOf(';'))
153
+ }
154
+ return mime.lookup(arg)
155
+ }
156
+
145
157
  expandConvo ({ convo, convoStep, args }) {
146
158
  const hasWildcard = args.findIndex(a => this._isWildcard(a)) >= 0
147
159
 
@@ -187,14 +199,14 @@ module.exports = class MediaInput {
187
199
  meMsg.media.push(new BotiumMockMedia({
188
200
  mediaUri: args[0],
189
201
  downloadUri: uri.toString(),
190
- mimeType: mime.lookup(args[0]),
202
+ mimeType: this._mime(args[0]),
191
203
  buffer
192
204
  }))
193
205
  }
194
206
  } else if (args.length === 2) {
195
207
  meMsg.media.push(new BotiumMockMedia({
196
208
  mediaUri: args[0],
197
- mimeType: mime.lookup(args[0]),
209
+ mimeType: this._mime(args[0]),
198
210
  buffer: args[1]
199
211
  }))
200
212
  }
@@ -0,0 +1,19 @@
1
+ continuefailing
2
+
3
+ #me
4
+ Hello
5
+
6
+ #bot
7
+ Hello
8
+
9
+ #me
10
+ Hello again!
11
+
12
+ #bot
13
+ Hello again FAILING!
14
+
15
+ #me
16
+ working again
17
+
18
+ #bot
19
+ working again
@@ -42,12 +42,25 @@ describe('convo.transcript', function () {
42
42
  this.compilerMultipleAssertErrors = this.driverMultipleAssertErrors.BuildCompiler()
43
43
  this.containerMultipleAssertErrors = await this.driverMultipleAssertErrors.Build()
44
44
  await this.containerMultipleAssertErrors.Start()
45
+
46
+ const myCapsSkipAssertErrors = {
47
+ [Capabilities.PROJECTNAME]: 'convo.transcript',
48
+ [Capabilities.CONTAINERMODE]: echoConnector,
49
+ [Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS]: true,
50
+ [Capabilities.SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS]: true
51
+ }
52
+ this.driverSkipAssertErrors = new BotDriver(myCapsSkipAssertErrors)
53
+ this.compilerSkipAssertErrors = this.driverSkipAssertErrors.BuildCompiler()
54
+ this.containerSkipAssertErrors = await this.driverSkipAssertErrors.Build()
55
+ await this.containerSkipAssertErrors.Start()
45
56
  })
46
57
  afterEach(async function () {
47
58
  await this.container.Stop()
48
59
  await this.container.Clean()
49
60
  await this.containerMultipleAssertErrors.Stop()
50
61
  await this.containerMultipleAssertErrors.Clean()
62
+ await this.containerSkipAssertErrors.Stop()
63
+ await this.containerSkipAssertErrors.Clean()
51
64
  })
52
65
  it('should provide transcript steps on success', async function () {
53
66
  this.compiler.ReadScript(path.resolve(__dirname, 'convos'), '2steps.convo.txt')
@@ -347,4 +360,25 @@ describe('convo.transcript', function () {
347
360
  assert.equal(this.compilerMultipleAssertErrors.convos.length, 1)
348
361
  await this.compilerMultipleAssertErrors.convos[0].Run(this.containerMultipleAssertErrors)
349
362
  })
363
+ it('should continue on failing assertion', async function () {
364
+ this.compilerSkipAssertErrors.ReadScript(path.resolve(__dirname, 'convos'), 'continuefailing.convo.txt')
365
+ assert.equal(this.compilerSkipAssertErrors.convos.length, 1)
366
+
367
+ try {
368
+ await this.compilerSkipAssertErrors.convos[0].Run(this.containerSkipAssertErrors)
369
+ assert.fail('expected error')
370
+ } catch (err) {
371
+ assert.isDefined(err.transcript)
372
+ assert.equal(err.transcript.steps.length, 6)
373
+ assert.equal(err.transcript.steps[0].actual.messageText, this.compilerSkipAssertErrors.convos[0].conversation[0].messageText)
374
+ assert.equal(err.transcript.steps[1].actual.messageText, this.compilerSkipAssertErrors.convos[0].conversation[1].messageText)
375
+ assert.equal(err.transcript.steps[2].actual.messageText, this.compilerSkipAssertErrors.convos[0].conversation[2].messageText)
376
+ assert.equal(err.transcript.steps[3].expected.messageText, this.compilerSkipAssertErrors.convos[0].conversation[3].messageText)
377
+ assert.notEqual(err.transcript.steps[3].actual.messageText, this.compilerSkipAssertErrors.convos[0].conversation[3].messageText)
378
+ assert.isDefined(err.transcript.steps[3].err)
379
+ assert.equal(err.transcript.steps[4].expected.messageText, this.compilerSkipAssertErrors.convos[0].conversation[4].messageText)
380
+ assert.equal(err.transcript.steps[5].expected.messageText, this.compilerSkipAssertErrors.convos[0].conversation[5].messageText)
381
+ assert.isNull(err.transcript.steps[5].err)
382
+ }
383
+ })
350
384
  })