botium-core 1.11.16 → 1.12.3

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 (42) hide show
  1. package/dist/botium-cjs.js +493 -202
  2. package/dist/botium-cjs.js.map +1 -1
  3. package/dist/botium-es.js +484 -194
  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/BotDriver.js +3 -3
  10. package/src/Capabilities.js +9 -0
  11. package/src/Defaults.js +1 -1
  12. package/src/Enums.js +6 -0
  13. package/src/containers/BaseContainer.js +20 -10
  14. package/src/containers/PluginConnectorContainer.js +1 -0
  15. package/src/containers/plugins/SimpleRestContainer.js +98 -30
  16. package/src/scripting/BotiumError.js +21 -0
  17. package/src/scripting/CompilerCsv.js +1 -1
  18. package/src/scripting/CompilerObjectBase.js +4 -14
  19. package/src/scripting/CompilerTxt.js +4 -15
  20. package/src/scripting/CompilerXlsx.js +81 -25
  21. package/src/scripting/Convo.js +16 -4
  22. package/src/scripting/ScriptingProvider.js +6 -0
  23. package/src/scripting/helper.js +54 -1
  24. package/src/scripting/logichook/LogicHookUtils.js +2 -0
  25. package/src/scripting/logichook/asserter/JsonPathAsserter.js +1 -1
  26. package/src/scripting/logichook/logichooks/ClearQueueLogicHook.js +1 -1
  27. package/src/scripting/logichook/userinput/MediaInput.js +14 -2
  28. package/test/connectors/convos/hello.convo.txt +6 -0
  29. package/test/connectors/simplerest.spec.js +106 -2
  30. package/test/convo/convos/continuefailing.convo.txt +19 -0
  31. package/test/convo/transcript.spec.js +34 -0
  32. package/test/scripting/scriptingProvider.spec.js +4 -4
  33. package/test/scripting/scriptingmemory/convosMultiMemorySameCols/buy.convo.txt +6 -0
  34. package/test/scripting/scriptingmemory/convosMultiMemorySameCols/products1.scriptingmemory.txt +2 -0
  35. package/test/scripting/scriptingmemory/convosMultiMemorySameCols/products2.scriptingmemory.txt +2 -0
  36. package/test/scripting/scriptingmemory/convosSimpleCols/buy.convo.txt +8 -0
  37. package/test/scripting/scriptingmemory/convosSimpleCols/product.scriptingmemory.txt +3 -0
  38. package/test/scripting/scriptingmemory/convosTwoTablesCols/buy.convo.txt +6 -0
  39. package/test/scripting/scriptingmemory/convosTwoTablesCols/customer.xlsx +0 -0
  40. package/test/scripting/scriptingmemory/convosTwoTablesCols/product.xlsx +0 -0
  41. package/test/scripting/scriptingmemory/fillScriptingMemoryFromFile.spec.js +45 -0
  42. package/test/scripting/userinputs/mediaInputConvos.spec.js +53 -2
@@ -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
  }
@@ -240,6 +240,8 @@ module.exports = class LogicHookUtils {
240
240
  return CheckClass({ ref, ...this.buildScriptContext }, this.caps, args)
241
241
  } else if (isClass(CheckClass.PluginClass)) {
242
242
  return new CheckClass.PluginClass({ ref, ...this.buildScriptContext }, this.caps, args)
243
+ } else if (_.isFunction(CheckClass.PluginClass)) {
244
+ return CheckClass.PluginClass({ ref, ...this.buildScriptContext }, this.caps, args)
243
245
  } else {
244
246
  throw new Error(`${src} class or function expected`)
245
247
  }
@@ -118,7 +118,7 @@ module.exports = class JsonPathAsserter {
118
118
  }
119
119
  },
120
120
  cause: {
121
- expected: assert || (args && `any element for ${args.join('|')}`) || (!args && `any element in ${path}`),
121
+ expected: assert || ((args && args.length > 0) && `any element for ${args.join('|')}`) || ((!args || args.length === 0) && `any element in ${path}`),
122
122
  actual: null,
123
123
  path
124
124
  }
@@ -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,6 @@
1
+ hello
2
+
3
+ #me
4
+ hello
5
+
6
+ #bot
@@ -22,6 +22,12 @@ const myCapsPost = {
22
22
  [Capabilities.SIMPLEREST_BODY_TEMPLATE]: { BODY1: 'BODY1VALUE', BODY2: '{{msg.messageText}}' },
23
23
  [Capabilities.SIMPLEREST_RESPONSE_JSONPATH]: ['$']
24
24
  }
25
+ const myCapsFormPost = {
26
+ [Capabilities.CONTAINERMODE]: 'simplerest',
27
+ [Capabilities.SIMPLEREST_URL]: 'http://my-host.com/api/endpoint/{{msg.messageText}}',
28
+ [Capabilities.SIMPLEREST_METHOD]: 'POST',
29
+ [Capabilities.SIMPLEREST_RESPONSE_JSONPATH]: ['$']
30
+ }
25
31
 
26
32
  const myCapsScriptingMemory = {
27
33
  [Capabilities.CONTAINERMODE]: 'simplerest',
@@ -448,7 +454,7 @@ describe('connectors.simplerest.build', function () {
448
454
  assert.isTrue(err.message.includes('Cant load hook, syntax is not valid'))
449
455
  }
450
456
  })
451
- it('should query params from UPDATE_CUSTOM (without "?")', async function () {
457
+ it('should add query params from UPDATE_CUSTOM (without "?")', async function () {
452
458
  const myCaps = Object.assign({}, myCapsGet)
453
459
  const myMsg = Object.assign({}, msg)
454
460
  myMsg.ADD_QUERY_PARAM = {
@@ -467,7 +473,7 @@ describe('connectors.simplerest.build', function () {
467
473
 
468
474
  await container.Clean()
469
475
  })
470
- it('should query params from UPDATE_CUSTOM (with "?")', async function () {
476
+ it('should add query params from UPDATE_CUSTOM (with "?")', async function () {
471
477
  const myCaps = Object.assign({}, myCapsGet)
472
478
  myCaps.SIMPLEREST_URL = 'http://my-host.com/api/endpoint/messageText?const1=const1'
473
479
  const myMsg = Object.assign({}, msg)
@@ -510,6 +516,26 @@ describe('connectors.simplerest.build', function () {
510
516
 
511
517
  await container.Clean()
512
518
  })
519
+ it('should add form params from UPDATE_CUSTOM (without "?")', async function () {
520
+ const myCaps = Object.assign({}, myCapsFormPost)
521
+ const myMsg = Object.assign({}, msg)
522
+ myMsg.ADD_FORM_PARAM = {
523
+ formparam1: 'valueparam1',
524
+ formparam2: '{{msg.messageText}}'
525
+ }
526
+
527
+ const driver = new BotDriver(myCaps)
528
+ const container = await driver.Build()
529
+ assert.equal(container.pluginInstance.constructor.name, 'SimpleRestContainer')
530
+
531
+ await container.Start()
532
+ const request = await container.pluginInstance._buildRequest(myMsg)
533
+ assert.isObject(request.form)
534
+ assert.equal(request.form.formparam1, 'valueparam1')
535
+ assert.equal(request.form.formparam2, 'messageText')
536
+
537
+ await container.Clean()
538
+ })
513
539
  it('should add header from UPDATE_CUSTOM', async function () {
514
540
  const myCaps = Object.assign({}, myCapsGet)
515
541
  const myMsg = Object.assign({}, msg)
@@ -734,6 +760,70 @@ describe('connectors.simplerest.processBody', function () {
734
760
  assert.equal(msgs[2].messageText, 'text 3')
735
761
  assert.equal(msgs[2].media[0].mediaUri, 'http://botium.at/3.jpg')
736
762
 
763
+ await container.Clean()
764
+ })
765
+ it('should process card responses', async function () {
766
+ const myCaps = Object.assign({}, myCapsGet, {
767
+ [Capabilities.SIMPLEREST_RESPONSE_JSONPATH]: '$.text',
768
+ [Capabilities.SIMPLEREST_CARDS_JSONPATH]: '$.cards',
769
+ [Capabilities.SIMPLEREST_CARD_TEXT_JSONPATH]: '$.title',
770
+ [Capabilities.SIMPLEREST_CARD_SUBTEXT_JSONPATH]: '$.subTitle',
771
+ [Capabilities.SIMPLEREST_CARD_ATTACHMENTS_JSONPATH]: '$.media',
772
+ [Capabilities.SIMPLEREST_CARD_BUTTONS_JSONPATH]: '$.buttons[*].text'
773
+ })
774
+ const driver = new BotDriver(myCaps)
775
+ const container = await driver.Build()
776
+ assert.equal(container.pluginInstance.constructor.name, 'SimpleRestContainer')
777
+
778
+ await container.Start()
779
+ const msgs = await container.pluginInstance._processBodyAsyncImpl({
780
+ cards: [
781
+ {
782
+ title: 'card1',
783
+ subTitle: 'card1 sub',
784
+ media: 'http://botium.at/1.jpg',
785
+ buttons: [
786
+ {
787
+ text: 'c1b1'
788
+ },
789
+ {
790
+ text: 'c1b2'
791
+ }
792
+ ]
793
+ },
794
+ {
795
+ title: 'card2',
796
+ subTitle: 'card2 sub',
797
+ media: 'http://botium.at/2.jpg',
798
+ buttons: [
799
+ {
800
+ text: 'c2b1'
801
+ }
802
+ ]
803
+ }
804
+ ]
805
+ }, true)
806
+
807
+ assert.exists(msgs)
808
+ assert.equal(msgs.length, 1)
809
+ assert.equal(msgs[0].cards.length, 2)
810
+ assert.equal(msgs[0].cards[0].text, 'card1')
811
+ assert.equal(msgs[0].cards[0].subtext, 'card1 sub')
812
+ assert.equal(msgs[0].cards[0].media.length, 1)
813
+ assert.equal(msgs[0].cards[0].media[0].mediaUri, 'http://botium.at/1.jpg')
814
+ assert.equal(msgs[0].cards[0].media[0].mimeType, 'image/jpeg')
815
+ assert.equal(msgs[0].cards[0].buttons.length, 2)
816
+ assert.equal(msgs[0].cards[0].buttons[0].text, 'c1b1')
817
+ assert.equal(msgs[0].cards[0].buttons[1].text, 'c1b2')
818
+
819
+ assert.equal(msgs[0].cards[1].text, 'card2')
820
+ assert.equal(msgs[0].cards[1].subtext, 'card2 sub')
821
+ assert.equal(msgs[0].cards[1].media.length, 1)
822
+ assert.equal(msgs[0].cards[1].media[0].mediaUri, 'http://botium.at/2.jpg')
823
+ assert.equal(msgs[0].cards[1].media[0].mimeType, 'image/jpeg')
824
+ assert.equal(msgs[0].cards[1].buttons.length, 1)
825
+ assert.equal(msgs[0].cards[1].buttons[0].text, 'c2b1')
826
+
737
827
  await container.Clean()
738
828
  })
739
829
  })
@@ -789,6 +879,7 @@ describe('connectors.simplerest.useresponse', function () {
789
879
  .get('/startencoded').reply(200, { text: JSON.stringify({ prop: 'response from start' }) })
790
880
  .get('/startstring').reply(200, 'response from start')
791
881
  .get('/msg').reply(200, { text: 'response from msg' })
882
+ .get('/msgfail').reply(400, { error: 'failure text' })
792
883
  .persist()
793
884
 
794
885
  const myCaps = Object.assign({
@@ -914,6 +1005,19 @@ describe('connectors.simplerest.useresponse', function () {
914
1005
  assert.equal(transcript.steps.length, 1)
915
1006
  assert.equal(transcript.steps[0].actual.messageText, 'response from start')
916
1007
  })
1008
+
1009
+ it('should use error body content', async function () {
1010
+ await this.init({
1011
+ [Capabilities.SIMPLEREST_URL]: 'https://mock.com/msgfail'
1012
+ })
1013
+
1014
+ this.compiler.ReadScript(path.resolve(__dirname, 'convos'), 'hello.convo.txt')
1015
+ try {
1016
+ await this.compiler.convos[0].Run(this.container)
1017
+ } catch (err) {
1018
+ assert.isTrue(err.message.indexOf('failure text') >= 0)
1019
+ }
1020
+ })
917
1021
  })
918
1022
 
919
1023
  describe('connectors.simplerest.inbound', function () {
@@ -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
  })
@@ -508,11 +508,11 @@ describe('scriptingProvider.ExpandUtterancesToConvos', function () {
508
508
  assert.equal(scriptingProvider.convos[0].conversation.length, 2)
509
509
  assert.equal(scriptingProvider.convos[0].header.name, 'utt1/utt1-L1')
510
510
  assert.equal(scriptingProvider.convos[0].conversation[0].messageText, 'TEXT1')
511
- assert.equal(scriptingProvider.convos[0].toString(), '1 utt1/utt1-L1 (Expanded Utterances - utt1) ({ origUttName: \'utt1\', origConvoName: \'utt1\' }): Step 1 - tell utterance: #me - TEXT1 | Step 2 - check bot response: #bot - ')
511
+ assert.equal(scriptingProvider.convos[0].toString(), '1 utt1/utt1-L1 (Expanded Utterances - utt1) ({ origUttName: \'utt1\', origConvoName: \'utt1\' }): Step 1 - tell utterance: #me - TEXT1 SKIP_BOT_UNCONSUMED(no args) | Step 2 - check bot response: #bot - ')
512
512
  assert.equal(scriptingProvider.convos[1].conversation.length, 2)
513
513
  assert.equal(scriptingProvider.convos[1].header.name, 'utt1/utt1-L2')
514
514
  assert.equal(scriptingProvider.convos[1].conversation[0].messageText, 'TEXT2')
515
- assert.equal(scriptingProvider.convos[1].toString(), '2 utt1/utt1-L2 (Expanded Utterances - utt1) ({ origUttName: \'utt1\', origConvoName: \'utt1\' }): Step 1 - tell utterance: #me - TEXT2 | Step 2 - check bot response: #bot - ')
515
+ assert.equal(scriptingProvider.convos[1].toString(), '2 utt1/utt1-L2 (Expanded Utterances - utt1) ({ origUttName: \'utt1\', origConvoName: \'utt1\' }): Step 1 - tell utterance: #me - TEXT2 SKIP_BOT_UNCONSUMED(no args) | Step 2 - check bot response: #bot - ')
516
516
  })
517
517
  it('should add leading zeros for utterance tags', async function () {
518
518
  const scriptingProvider = new ScriptingProvider(DefaultCapabilities)
@@ -529,11 +529,11 @@ describe('scriptingProvider.ExpandUtterancesToConvos', function () {
529
529
  assert.equal(scriptingProvider.convos[0].conversation.length, 2)
530
530
  assert.equal(scriptingProvider.convos[0].header.name, 'utt1/utt1-L00001')
531
531
  assert.equal(scriptingProvider.convos[0].conversation[0].messageText, 'TEXT1')
532
- assert.equal(scriptingProvider.convos[0].toString(), '1 utt1/utt1-L00001 (Expanded Utterances - utt1) ({ origUttName: \'utt1\', origConvoName: \'utt1\' }): Step 1 - tell utterance: #me - TEXT1 | Step 2 - check bot response: #bot - ')
532
+ assert.equal(scriptingProvider.convos[0].toString(), '1 utt1/utt1-L00001 (Expanded Utterances - utt1) ({ origUttName: \'utt1\', origConvoName: \'utt1\' }): Step 1 - tell utterance: #me - TEXT1 SKIP_BOT_UNCONSUMED(no args) | Step 2 - check bot response: #bot - ')
533
533
  assert.equal(scriptingProvider.convos[1].conversation.length, 2)
534
534
  assert.equal(scriptingProvider.convos[1].header.name, 'utt1/utt1-L00002')
535
535
  assert.equal(scriptingProvider.convos[1].conversation[0].messageText, 'TEXT2')
536
- assert.equal(scriptingProvider.convos[1].toString(), '2 utt1/utt1-L00002 (Expanded Utterances - utt1) ({ origUttName: \'utt1\', origConvoName: \'utt1\' }): Step 1 - tell utterance: #me - TEXT2 | Step 2 - check bot response: #bot - ')
536
+ assert.equal(scriptingProvider.convos[1].toString(), '2 utt1/utt1-L00002 (Expanded Utterances - utt1) ({ origUttName: \'utt1\', origConvoName: \'utt1\' }): Step 1 - tell utterance: #me - TEXT2 SKIP_BOT_UNCONSUMED(no args) | Step 2 - check bot response: #bot - ')
537
537
  })
538
538
  it('should build incomprehension convos for utterance', async function () {
539
539
  const scriptingProvider = new ScriptingProvider(Object.assign({}, DefaultCapabilities, { SCRIPTING_UTTEXPANSION_INCOMPREHENSION: 'INCOMPREHENSION' }))
@@ -0,0 +1,6 @@
1
+ buy
2
+
3
+ #me
4
+ Ok, then send me some $productName
5
+
6
+ #bot
@@ -0,0 +1,2 @@
1
+ | productBread | productBeer | productEE
2
+ $productName | Bread | Beer | Everything Else
@@ -0,0 +1,2 @@
1
+ | productHamburger
2
+ $productName | Hamburger
@@ -0,0 +1,8 @@
1
+ simple text
2
+
3
+ #begin
4
+
5
+ #me
6
+ $productName $customer
7
+
8
+ #bot
@@ -0,0 +1,3 @@
1
+ |product1
2
+ $productName |Wiener Schnitzel
3
+ $customer |Joe
@@ -0,0 +1,6 @@
1
+ two table
2
+
3
+ #me
4
+ buy $productName as $customerName
5
+
6
+ #bot