botium-core 1.12.1 → 1.12.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "botium-core",
3
- "version": "1.12.1",
3
+ "version": "1.12.4",
4
4
  "description": "The Selenium for Chatbots",
5
5
  "main": "index.js",
6
6
  "module": "dist/botium-es.js",
@@ -32,23 +32,23 @@
32
32
  },
33
33
  "homepage": "https://www.botium.ai",
34
34
  "dependencies": {
35
- "@babel/runtime": "^7.16.7",
35
+ "@babel/runtime": "^7.17.8",
36
36
  "async": "^3.2.3",
37
- "body-parser": "^1.19.1",
38
- "boolean": "^3.1.4",
37
+ "body-parser": "^1.19.2",
38
+ "boolean": "^3.2.0",
39
39
  "bottleneck": "^2.19.5",
40
40
  "csv-parse": "^5.0.4",
41
- "debug": "^4.3.3",
41
+ "debug": "^4.3.4",
42
42
  "esprima": "^4.0.1",
43
- "express": "^4.17.2",
43
+ "express": "^4.17.3",
44
44
  "globby": "11.0.4",
45
- "ioredis": "^4.28.3",
45
+ "ioredis": "^4.28.5",
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
50
  "markdown-it": "^12.3.2",
51
- "mime-types": "^2.1.34",
51
+ "mime-types": "^2.1.35",
52
52
  "mkdirp": "^1.0.4",
53
53
  "moment": "^2.29.1",
54
54
  "mustache": "^4.2.0",
@@ -65,21 +65,21 @@
65
65
  "swagger-jsdoc": "^6.1.0",
66
66
  "swagger-ui-express": "^4.3.0",
67
67
  "uuid": "^8.3.2",
68
- "vm2": "^3.9.5",
68
+ "vm2": "^3.9.9",
69
69
  "write-yaml": "^1.0.0",
70
- "xlsx": "^0.17.5",
70
+ "xlsx": "^0.18.4",
71
71
  "xregexp": "^5.1.0",
72
72
  "yaml": "^1.10.2"
73
73
  },
74
74
  "devDependencies": {
75
- "@babel/core": "^7.16.12",
75
+ "@babel/core": "^7.17.8",
76
76
  "@babel/node": "^7.16.8",
77
- "@babel/plugin-transform-runtime": "^7.16.10",
77
+ "@babel/plugin-transform-runtime": "^7.17.0",
78
78
  "@babel/preset-env": "^7.16.11",
79
- "chai": "^4.3.4",
79
+ "chai": "^4.3.6",
80
80
  "chai-as-promised": "^7.1.1",
81
81
  "cross-env": "^7.0.3",
82
- "eslint": "^8.7.0",
82
+ "eslint": "^8.11.0",
83
83
  "eslint-config-standard": "^16.0.3",
84
84
  "eslint-plugin-import": "^2.25.4",
85
85
  "eslint-plugin-node": "^11.1.0",
@@ -87,11 +87,11 @@
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.2.0",
91
- "nock": "^13.2.2",
92
- "npm-check-updates": "^12.2.1",
90
+ "mocha": "^9.2.2",
91
+ "nock": "^13.2.4",
92
+ "npm-check-updates": "^12.5.4",
93
93
  "nyc": "^15.1.0",
94
- "rollup": "^2.66.0",
94
+ "rollup": "^2.70.1",
95
95
  "rollup-plugin-babel": "^4.4.0",
96
96
  "rollup-plugin-commonjs": "^10.1.0",
97
97
  "rollup-plugin-json": "^4.0.0",
package/src/BotDriver.js CHANGED
@@ -109,9 +109,9 @@ module.exports = class BotDriver {
109
109
 
110
110
  Build () {
111
111
  debug(`Build - Botium Core Version: ${version}`)
112
- debug(`Build - Capabilites: ${util.inspect(this.caps)}`)
113
- debug(`Build - Sources : ${util.inspect(this.sources)}`)
114
- debug(`Build - Envs : ${util.inspect(this.envs)}`)
112
+ debug(`Build - Capabilites: ${JSON.stringify(_.pickBy(this.caps, (value, key) => Defaults.Capabilities[key] !== value), null, 2)}`)
113
+ debug(`Build - Sources: ${JSON.stringify(_.pickBy(this.sources, (value, key) => Defaults.Sources[key] !== value), null, 2)}`)
114
+ debug(`Build - Envs: ${JSON.stringify(_.pickBy(this.envs, (value, key) => Defaults.Envs[key] !== value), null, 2)}`)
115
115
  this.eventEmitter.emit(Events.CONTAINER_BUILDING)
116
116
 
117
117
  return new Promise((resolve, reject) => {
@@ -73,6 +73,11 @@ module.exports = {
73
73
  SIMPLEREST_RESPONSE_HOOK: 'SIMPLEREST_RESPONSE_HOOK',
74
74
  SIMPLEREST_MEDIA_JSONPATH: 'SIMPLEREST_MEDIA_JSONPATH',
75
75
  SIMPLEREST_BUTTONS_JSONPATH: 'SIMPLEREST_BUTTONS_JSONPATH',
76
+ SIMPLEREST_CARDS_JSONPATH: 'SIMPLEREST_CARDS_JSONPATH',
77
+ SIMPLEREST_CARD_TEXT_JSONPATH: 'SIMPLEREST_CARD_TEXT_JSONPATH',
78
+ SIMPLEREST_CARD_SUBTEXT_JSONPATH: 'SIMPLEREST_CARD_SUBTEXT_JSONPATH',
79
+ SIMPLEREST_CARD_BUTTONS_JSONPATH: 'SIMPLEREST_CARD_BUTTONS_JSONPATH',
80
+ SIMPLEREST_CARD_ATTACHMENTS_JSONPATH: 'SIMPLEREST_CARD_ATTACHMENTS_JSONPATH',
76
81
  SIMPLEREST_CONTEXT_JSONPATH: 'SIMPLEREST_CONTEXT_JSONPATH',
77
82
  SIMPLEREST_CONTEXT_MERGE_OR_REPLACE: 'SIMPLEREST_CONTEXT_MERGE_OR_REPLACE',
78
83
  SIMPLEREST_CONVERSATION_ID_TEMPLATE: 'SIMPLEREST_CONVERSATION_ID_TEMPLATE',
@@ -143,6 +148,7 @@ module.exports = {
143
148
  // API Calls Rate Limiting
144
149
  RATELIMIT_USERSAYS_MAXCONCURRENT: 'RATELIMIT_USERSAYS_MAXCONCURRENT',
145
150
  RATELIMIT_USERSAYS_MINTIME: 'RATELIMIT_USERSAYS_MINTIME',
151
+ RATELIMIT_BOTTLENECK_FN: 'RATELIMIT_BOTTLENECK_FN',
146
152
  SECURITY_ALLOW_UNSAFE: 'SECURITY_ALLOW_UNSAFE',
147
153
  PRECOMPILERS: 'PRECOMPILERS'
148
154
  }
package/src/Defaults.js CHANGED
@@ -31,7 +31,6 @@ module.exports = {
31
31
  [Capabilities.SIMPLEREST_METHOD]: 'GET',
32
32
  [Capabilities.SIMPLEREST_IGNORE_EMPTY]: true,
33
33
  [Capabilities.SIMPLEREST_TIMEOUT]: 10000,
34
- [Capabilities.SIMPLEREST_EXTRA_OPTIONS]: {},
35
34
  [Capabilities.SIMPLEREST_STRICT_SSL]: true,
36
35
  [Capabilities.SIMPLEREST_INBOUND_UPDATE_CONTEXT]: true,
37
36
  [Capabilities.SIMPLEREST_CONTEXT_MERGE_OR_REPLACE]: 'MERGE',
@@ -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,
@@ -22,9 +22,10 @@ const { BotiumError } = require('../../scripting/BotiumError')
22
22
  Mustache.escape = s => s
23
23
 
24
24
  module.exports = class SimpleRestContainer {
25
- constructor ({ queueBotSays, caps }) {
25
+ constructor ({ queueBotSays, caps, bottleneck }) {
26
26
  this.queueBotSays = queueBotSays
27
27
  this.caps = Object.assign({}, Defaults, caps)
28
+ this.bottleneck = bottleneck || ((fn) => fn())
28
29
  this.processInbound = false
29
30
  this.redisTopic = this.caps[Capabilities.SIMPLEREST_REDIS_TOPIC] || 'SIMPLEREST_INBOUND_SUBSCRIPTION'
30
31
 
@@ -308,34 +309,82 @@ module.exports = class SimpleRestContainer {
308
309
  }
309
310
 
310
311
  for (const jsonPathRoot of jsonPathRoots) {
311
- const media = []
312
- const buttons = []
313
-
314
- const jsonPathsMedia = getAllCapValues(Capabilities.SIMPLEREST_MEDIA_JSONPATH, this.caps)
315
- jsonPathsMedia.forEach(jsonPath => {
316
- const responseMedia = jp.query(jsonPathRoot, jsonPath)
317
- if (responseMedia) {
318
- (_.isArray(responseMedia) ? _.flattenDeep(responseMedia) : [responseMedia]).forEach(m =>
319
- media.push({
320
- mediaUri: m,
321
- mimeType: mime.lookup(m) || 'application/unknown'
322
- })
323
- )
324
- debug(`found response media: ${util.inspect(media)}`)
312
+ const _retrieveMedia = (jsonPathMediaRoot, jsonPathsMedia) => {
313
+ const retrievedMedia = []
314
+ jsonPathsMedia.forEach(jsonPath => {
315
+ const responseMedia = jp.query(jsonPathMediaRoot, jsonPath)
316
+ if (responseMedia) {
317
+ (_.isArray(responseMedia) ? _.flattenDeep(responseMedia) : [responseMedia]).forEach(m =>
318
+ retrievedMedia.push({
319
+ mediaUri: m,
320
+ mimeType: mime.lookup(m) || 'application/unknown'
321
+ })
322
+ )
323
+ }
324
+ })
325
+ return retrievedMedia
326
+ }
327
+
328
+ const _retrieveButtons = (jsonPathButtonRoot, jsonPathsButtons) => {
329
+ const retrievedButtons = []
330
+ jsonPathsButtons.forEach(jsonPath => {
331
+ const responseButtons = jp.query(jsonPathButtonRoot, jsonPath)
332
+ if (responseButtons) {
333
+ (_.isArray(responseButtons) ? _.flattenDeep(responseButtons) : [responseButtons]).forEach(b =>
334
+ retrievedButtons.push({
335
+ text: b
336
+ })
337
+ )
338
+ }
339
+ })
340
+ return retrievedButtons
341
+ }
342
+
343
+ const _getCardText = (responseCardText) => {
344
+ if (responseCardText) {
345
+ const texts = _.isArray(responseCardText) ? _.flattenDeep(responseCardText) : [responseCardText]
346
+ if (texts.length > 1) {
347
+ debug(`more than one text found for card: ${util.inspect(texts)}`)
348
+ }
349
+ if (texts.length > 0) {
350
+ return texts[0]
351
+ }
325
352
  }
326
- })
327
- const jsonPathsButtons = getAllCapValues(Capabilities.SIMPLEREST_BUTTONS_JSONPATH, this.caps)
328
- jsonPathsButtons.forEach(jsonPath => {
329
- const responseButtons = jp.query(jsonPathRoot, jsonPath)
330
- if (responseButtons) {
331
- (_.isArray(responseButtons) ? _.flattenDeep(responseButtons) : [responseButtons]).forEach(b =>
332
- buttons.push({
333
- text: b
353
+ }
354
+
355
+ const media = _retrieveMedia(jsonPathRoot, getAllCapValues(Capabilities.SIMPLEREST_MEDIA_JSONPATH, this.caps))
356
+ debug(`found response media: ${util.inspect(media)}`)
357
+ const buttons = _retrieveButtons(jsonPathRoot, getAllCapValues(Capabilities.SIMPLEREST_BUTTONS_JSONPATH, this.caps))
358
+ debug(`found response buttons: ${util.inspect(buttons)}`)
359
+ const cards = []
360
+
361
+ const jsonPathsCards = getAllCapValues(Capabilities.SIMPLEREST_CARDS_JSONPATH, this.caps)
362
+ jsonPathsCards.forEach(jsonPath => {
363
+ const responseCards = jp.query(jsonPathRoot, jsonPath)
364
+ if (responseCards) {
365
+ (_.isArray(responseCards) ? _.flattenDeep(responseCards) : [responseCards]).forEach(c => {
366
+ const card = {}
367
+
368
+ const jsonPathsCardText = getAllCapValues(Capabilities.SIMPLEREST_CARD_TEXT_JSONPATH, this.caps)
369
+ jsonPathsCardText.forEach(jsonPath => {
370
+ card.text = _getCardText(jp.query(c, jsonPath))
334
371
  })
335
- )
336
- debug(`found response buttons: ${util.inspect(buttons)}`)
372
+
373
+ const jsonPathsCardSubText = getAllCapValues(Capabilities.SIMPLEREST_CARD_SUBTEXT_JSONPATH, this.caps)
374
+ jsonPathsCardSubText.forEach(jsonPath => {
375
+ card.subtext = _getCardText(jp.query(c, jsonPath))
376
+ })
377
+
378
+ card.buttons = _retrieveButtons(c, getAllCapValues(Capabilities.SIMPLEREST_CARD_BUTTONS_JSONPATH, this.caps))
379
+ card.media = _retrieveMedia(c, getAllCapValues(Capabilities.SIMPLEREST_CARD_ATTACHMENTS_JSONPATH, this.caps))
380
+
381
+ if (_.keys(card).length > 0) {
382
+ cards.push(card)
383
+ }
384
+ })
337
385
  }
338
386
  })
387
+ debug(`found response cards: ${util.inspect(cards)}`)
339
388
 
340
389
  let hasMessageText = false
341
390
  const jsonPathsTexts = getAllCapValues(Capabilities.SIMPLEREST_RESPONSE_JSONPATH, this.caps)
@@ -350,18 +399,18 @@ module.exports = class SimpleRestContainer {
350
399
  if (!messageText) continue
351
400
 
352
401
  hasMessageText = true
353
- const botMsg = { sourceData: body, messageText, media, buttons }
402
+ const botMsg = { sourceData: body, messageText, media, buttons, cards }
354
403
  await executeHook(this.caps, this.responseHook, Object.assign({ botMsg, botMsgRoot: jsonPathRoot, messageTextIndex }, this.view))
355
404
  result.push(botMsg)
356
405
  }
357
406
  }
358
407
 
359
408
  if (!hasMessageText) {
360
- const botMsg = { messageText: '', sourceData: body, media, buttons }
409
+ const botMsg = { messageText: '', sourceData: body, media, buttons, cards }
361
410
  const beforeHookKeys = Object.keys(botMsg)
362
411
  await executeHook(this.caps, this.responseHook, Object.assign({ botMsg, botMsgRoot: jsonPathRoot }, this.view))
363
412
  const afterHookKeys = Object.keys(botMsg)
364
- if (beforeHookKeys.length !== afterHookKeys.length || !!(botMsg.messageText && botMsg.messageText.length > 0) || botMsg.media.length > 0 || botMsg.buttons.length > 0 || !this.caps[Capabilities.SIMPLEREST_IGNORE_EMPTY]) {
413
+ if (beforeHookKeys.length !== afterHookKeys.length || !!(botMsg.messageText && botMsg.messageText.length > 0) || botMsg.media.length > 0 || botMsg.buttons.length > 0 || botMsg.cards.length > 0 || !this.caps[Capabilities.SIMPLEREST_IGNORE_EMPTY]) {
365
414
  result.push(botMsg)
366
415
  }
367
416
  }
@@ -528,11 +577,11 @@ module.exports = class SimpleRestContainer {
528
577
  throw new Error(`Failed to ping bot after ${retries} retries`)
529
578
  }
530
579
  tries++
531
- const { err, response, body } = await new Promise((resolve) => {
580
+ const { err, response, body } = await this.bottleneck(() => new Promise((resolve) => {
532
581
  request(pingConfig, (err, response, body) => {
533
582
  resolve({ err, response, body })
534
583
  })
535
- })
584
+ }))
536
585
  if (err) {
537
586
  debug(`_waitForUrlResponse error on url check ${pingConfig.uri}: ${err}`)
538
587
  await timeout(pingConfig.timeout)
@@ -307,9 +307,9 @@ class Convo {
307
307
  transcriptStep.actual = meMsg
308
308
 
309
309
  try {
310
- await this.scriptingEvents.setUserInput({ convo: this, convoStep, container, scriptingMemory, meMsg, transcript, transcriptStep })
311
- await this.scriptingEvents.onMeStart({ convo: this, convoStep, container, scriptingMemory, meMsg, transcript, transcriptStep })
312
- await this.scriptingEvents.onMePrepare({ convo: this, convoStep, container, scriptingMemory, meMsg, transcript, transcriptStep })
310
+ await this.scriptingEvents.setUserInput({ convo: this, convoStep, container, scriptingMemory, meMsg, transcript, transcriptStep, transcriptSteps })
311
+ await this.scriptingEvents.onMeStart({ convo: this, convoStep, container, scriptingMemory, meMsg, transcript, transcriptStep, transcriptSteps })
312
+ await this.scriptingEvents.onMePrepare({ convo: this, convoStep, container, scriptingMemory, meMsg, transcript, transcriptStep, transcriptSteps })
313
313
 
314
314
  await this._checkBotRepliesConsumed(container)
315
315
 
@@ -760,6 +760,70 @@ describe('connectors.simplerest.processBody', function () {
760
760
  assert.equal(msgs[2].messageText, 'text 3')
761
761
  assert.equal(msgs[2].media[0].mediaUri, 'http://botium.at/3.jpg')
762
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
+
763
827
  await container.Clean()
764
828
  })
765
829
  })
@@ -69,7 +69,7 @@ describe('logichooks.hookfromsrc', function () {
69
69
  await compiler.convos[0].Run(container)
70
70
  assert.fail('it should have failed')
71
71
  } catch (err) {
72
- assert.isTrue(err.message.includes('Line 6: assertion error - Unexpected token'))
72
+ assert.isTrue(err.message.includes('Line 6: assertion error - Unexpected end of input'))
73
73
  }
74
74
  })
75
75
  })