botium-core 1.15.6 → 1.15.9

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.15.6",
3
+ "version": "1.15.9",
4
4
  "description": "The Selenium for Chatbots",
5
5
  "main": "index.js",
6
6
  "module": "dist/botium-es.js",
package/src/BotDriver.js CHANGED
@@ -241,8 +241,8 @@ module.exports = class BotDriver {
241
241
  if (_.isString(newCaps[capKey])) {
242
242
  try {
243
243
  caps[capKey] = JSON.parse(newCaps[capKey])
244
- if (_.isFinite(caps[capKey])) {
245
- caps[capKey] = caps[capKey].toString()
244
+ if (_.isFinite(Number(newCaps[capKey]))) {
245
+ caps[capKey] = newCaps[capKey].toString()
246
246
  }
247
247
  } catch (err) {
248
248
  caps[capKey] = newCaps[capKey]
@@ -111,6 +111,15 @@ module.exports = {
111
111
  SIMPLEREST_NLP_LIST_JSONPATH: 'SIMPLEREST_NLP_LIST_JSONPATH',
112
112
  SIMPLEREST_NLP_LIST_INTENT_JSONPATH: 'SIMPLEREST_NLP_LIST_INTENT_JSONPATH',
113
113
  SIMPLEREST_NLP_LIST_CONFIDENCE_JSONPATH: 'SIMPLEREST_NLP_LIST_CONFIDENCE_JSONPATH',
114
+ /**
115
+ * Single response can contain a list of messages. This capability defines how to merge the list into a single message.
116
+ * It can be 'OFF' (default), 'MERGE_TEXT'
117
+ * @type {string}
118
+ * @default 'OFF'
119
+ * @description Merge message list into single message
120
+ */
121
+ SIMPLEREST_MESSAGE_LIST_MERGE: 'SIMPLEREST_MESSAGE_LIST_MERGE',
122
+
114
123
  // Script Compiler
115
124
  SCRIPTING_TXT_EOL: 'SCRIPTING_TXT_EOL',
116
125
  // ROW_PER_MESSAGE or QUESTION_ANSWER
@@ -369,7 +369,7 @@ module.exports = class SimpleRestContainer {
369
369
  return
370
370
  }
371
371
 
372
- const result = []
372
+ let result = []
373
373
  if (isFromUser) {
374
374
  const _extractFrom = (root, jsonPaths, acceptFn = null) => {
375
375
  const result = []
@@ -402,6 +402,11 @@ module.exports = class SimpleRestContainer {
402
402
  jsonPathRoots.push(body)
403
403
  }
404
404
 
405
+ if (jsonPathRoots.length === 0 && !this.caps[Capabilities.SIMPLEREST_IGNORE_EMPTY]) {
406
+ debug(`found empty body, and processed because of SIMPLEREST_IGNORE_EMPTY capability: ${util.inspect(body)}`)
407
+ result.push({ messageText: '', sourceData: body })
408
+ }
409
+
405
410
  for (const jsonPathRoot of jsonPathRoots) {
406
411
  const _retrieveMedia = (jsonPathMediaRoot, jsonPathsMedia) => {
407
412
  const retrievedMedia = []
@@ -590,8 +595,13 @@ module.exports = class SimpleRestContainer {
590
595
  const beforeHookKeys = Object.keys(botMsg)
591
596
  await executeHook(this.caps, this.responseHook, Object.assign({ botMsg, botMsgRoot: jsonPathRoot }, this.view))
592
597
  const afterHookKeys = Object.keys(botMsg)
593
- if (beforeHookKeys.length !== afterHookKeys.length || !!(botMsg.messageText && botMsg.messageText.length > 0) || botMsg.media.length > 0 || botMsg.buttons.length > 0 || botMsg.cards.length > 0 || botMsg.nlp || !this.caps[Capabilities.SIMPLEREST_IGNORE_EMPTY]) {
598
+ if (beforeHookKeys.length !== afterHookKeys.length || !!(botMsg.messageText && botMsg.messageText.length > 0) || botMsg.media.length > 0 || botMsg.buttons.length > 0 || botMsg.cards.length > 0 || botMsg.nlp) {
599
+ result.push(botMsg)
600
+ } else if (!this.caps[Capabilities.SIMPLEREST_IGNORE_EMPTY]) {
601
+ debug(`found empty message, and processed because of SIMPLEREST_IGNORE_EMPTY capability: ${util.inspect(botMsg)}`)
594
602
  result.push(botMsg)
603
+ } else {
604
+ debug(`found empty message, and ignored because of SIMPLEREST_IGNORE_EMPTY capability: ${util.inspect(botMsg)}`)
595
605
  }
596
606
  }
597
607
  }
@@ -604,6 +614,23 @@ module.exports = class SimpleRestContainer {
604
614
 
605
615
  setTimeout(() => this._doRequest({ messageText: '' }, true, true), 0)
606
616
  }
617
+
618
+ if (this.caps[Capabilities.SIMPLEREST_MESSAGE_LIST_MERGE] === 'MERGE_TEXT') {
619
+ const isTextMsg = (msg) => msg.messageText && (!msg.media || msg.media.length === 0) && (!msg.buttons || msg.buttons.length === 0) && (!msg.cards || msg.cards.length === 0)
620
+
621
+ result = result.reduce((acc, currentMsg) => {
622
+ if (acc.length > 0) {
623
+ const last = acc[acc.length - 1]
624
+ if (isTextMsg(last)) {
625
+ currentMsg.messageText = [last.messageText, currentMsg.messageText].filter(t => t).join('\n')
626
+ acc[acc.length - 1] = currentMsg
627
+ return acc
628
+ }
629
+ }
630
+ acc.push(currentMsg)
631
+ return acc
632
+ }, [])
633
+ }
607
634
  return result
608
635
  }
609
636
 
@@ -620,8 +647,7 @@ module.exports = class SimpleRestContainer {
620
647
  fetch(requestOptions.uri, requestOptions).then(async (bodyRaw) => {
621
648
  let body
622
649
  try {
623
- const contentType = bodyRaw.headers.get('content-type')
624
- if (contentType && contentType.includes('application/json')) {
650
+ if (bodyRaw?.headers?.get('content-type') && bodyRaw.headers.get('content-type').includes('application/json')) {
625
651
  try {
626
652
  body = await bodyRaw.json()
627
653
  } catch (err) {
@@ -377,7 +377,15 @@ module.exports = class ScriptingProvider {
377
377
  throw Error(`Unknown hookType ${hookType}`)
378
378
  }
379
379
 
380
- const localHooks = (logicHooks || []).filter(l => this.logicHooks[l.name][hookType])
380
+ let localHooks = (logicHooks || []).filter(l => this.logicHooks[l.name][hookType])
381
+ // Scripting memory file are injected via SET_SCRIPTING_MEMORY in the BEGIN step
382
+ // But there might be other logic hooks that need the scripting memory variables
383
+ // Order is important (SET_SCRIPTING_MEMORY in begin can be because the user added it,
384
+ // or because the scripting memory file added it. User one has to be the last one.
385
+ localHooks = [
386
+ ...localHooks.filter(l => l.name === 'SET_SCRIPTING_MEMORY'),
387
+ ...localHooks.filter(l => l.name !== 'SET_SCRIPTING_MEMORY')
388
+ ]
381
389
 
382
390
  const convoStepPromises = localHooks
383
391
  .map(l => p(this.retryHelperLogicHook, () => this.logicHooks[l.name][hookType]({
@@ -385,6 +393,7 @@ module.exports = class ScriptingProvider {
385
393
  convoStep,
386
394
  scriptingMemory,
387
395
  container,
396
+ // Do this more sensitve for SET_SCRIPTING_MEMORY? It can have scripting variables in the args
388
397
  args: ScriptingMemory.applyToArgs(l.args, scriptingMemory, container.caps, rest.botMsg),
389
398
  isGlobal: false,
390
399
  ...rest
@@ -810,6 +810,7 @@ describe('connectors.simplerest', function () {
810
810
 
811
811
  await container.Clean()
812
812
  })
813
+
813
814
  it('should parse jsonmessage from sourcedata if it enabled', async function () {
814
815
  const msgJSON = {
815
816
  sourceData: {
@@ -837,6 +838,7 @@ describe('connectors.simplerest', function () {
837
838
 
838
839
  await container.Clean()
839
840
  })
841
+
840
842
  it('should fall back to text message if using jsonmessage from sourcedata is enabled, but sourcedata is not set', async function () {
841
843
  const myCaps = Object.assign({}, myCapsPost)
842
844
  myCaps[Capabilities.SIMPLEREST_BODY_FROM_JSON] = true
@@ -855,6 +857,7 @@ describe('connectors.simplerest', function () {
855
857
 
856
858
  await container.Clean()
857
859
  })
860
+
858
861
  it('should handle somehow if jsonmessage from sourcedata is enabled, and booth json, and text are set in the user message (impossible state)', async function () {
859
862
  // this is not a valid state. In case there is a json as message in a convo.txt, text parser parses it into the sourceData field, and keeps messageText empty
860
863
  const msgTextAndJSONIllegal = {
@@ -903,6 +906,81 @@ describe('connectors.simplerest', function () {
903
906
  await container.Clean()
904
907
  })
905
908
 
909
+ it('should merge text responses', async function () {
910
+ const myCaps = Object.assign({}, myCapsGet, {
911
+ [Capabilities.SIMPLEREST_BODY_JSONPATH]: '$.responses[*]',
912
+ [Capabilities.SIMPLEREST_RESPONSE_JSONPATH]: '$.text',
913
+ [Capabilities.SIMPLEREST_BUTTONS_JSONPATH]: '$.buttons[*]',
914
+ [Capabilities.SIMPLEREST_BUTTONS_TEXT_SUBJSONPATH]: '$.text',
915
+ [Capabilities.SIMPLEREST_MEDIA_JSONPATH]: '$.media[*]',
916
+ [Capabilities.SIMPLEREST_MESSAGE_LIST_MERGE]: 'MERGE_TEXT'
917
+ })
918
+ const driver = new BotDriver(myCaps)
919
+ const container = await driver.Build()
920
+ await container.Start()
921
+ const msgs = await container.pluginInstance._processBodyAsyncImpl({
922
+ responses: [
923
+ { text: 'text 1' },
924
+ { text: 'text 2' },
925
+ { text: 'text 3', buttons: [{ text: 'button' }] },
926
+ { text: 'text 4' },
927
+ { media: ['some.jpg'] },
928
+ { text: 'text 5' },
929
+ { text: 'text 6' }
930
+ ]
931
+ }, {}, true)
932
+
933
+ assert.exists(msgs)
934
+ assert.equal(msgs.length, 3)
935
+ assert.equal(msgs[0].messageText, 'text 1\ntext 2\ntext 3')
936
+ assert.exists(msgs[0].buttons)
937
+ assert.equal(msgs[0].buttons.length, 1)
938
+ assert.equal(msgs[0].media.length, 0)
939
+ assert.equal(msgs[1].messageText, 'text 4')
940
+ assert.equal(msgs[1].buttons.length, 0)
941
+ assert.equal(msgs[1].media.length, 1)
942
+ assert.equal(msgs[2].messageText, 'text 5\ntext 6')
943
+ assert.equal(msgs[2].buttons.length, 0)
944
+ assert.equal(msgs[2].media.length, 0)
945
+ await container.Clean()
946
+ })
947
+
948
+ it('should not merge text responses', async function () {
949
+ const myCaps = Object.assign({}, myCapsGet, {
950
+ [Capabilities.SIMPLEREST_BODY_JSONPATH]: '$.responses[*]',
951
+ [Capabilities.SIMPLEREST_RESPONSE_JSONPATH]: '$.text',
952
+ [Capabilities.SIMPLEREST_BUTTONS_JSONPATH]: '$.buttons[*]',
953
+ [Capabilities.SIMPLEREST_BUTTONS_TEXT_SUBJSONPATH]: '$.text',
954
+ [Capabilities.SIMPLEREST_MEDIA_JSONPATH]: '$.media[*]'
955
+ })
956
+ const driver = new BotDriver(myCaps)
957
+ const container = await driver.Build()
958
+ await container.Start()
959
+ const msgs = await container.pluginInstance._processBodyAsyncImpl({
960
+ responses: [
961
+ { text: 'text 1' },
962
+ { text: 'text 2' },
963
+ { text: 'text 3', buttons: [{ text: 'button' }] },
964
+ { text: 'text 4' },
965
+ { media: ['some.jpg'] },
966
+ { text: 'text 5' },
967
+ { text: 'text 6' }
968
+ ]
969
+ }, {}, true)
970
+
971
+ assert.exists(msgs)
972
+ assert.equal(msgs.length, 7)
973
+ assert.equal(msgs[0].messageText, 'text 1')
974
+ assert.equal(msgs[1].messageText, 'text 2')
975
+ assert.equal(msgs[2].messageText, 'text 3')
976
+ assert.equal(msgs[3].messageText, 'text 4')
977
+ assert.equal(msgs[4].messageText, '')
978
+ assert.equal(msgs[5].messageText, 'text 5')
979
+ assert.equal(msgs[6].messageText, 'text 6')
980
+
981
+ await container.Clean()
982
+ })
983
+
906
984
  it('should ignore empty response', async function () {
907
985
  const myCaps = Object.assign({}, myCapsGet, {
908
986
  [Capabilities.SIMPLEREST_BODY_JSONPATH]: '$.responses[*]',
@@ -75,6 +75,15 @@ describe('driver.capabilities', function () {
75
75
  assert.isString(driver.caps.CAP_STRING_1)
76
76
  assert.isString(driver.caps.CAP_STRING_2)
77
77
  })
78
+ it('should merge string caps when there are numbers', function () {
79
+ const myCaps = {
80
+ CAP_STRING_1: 'Test',
81
+ CAP_STRING_2: '1234567892343434344'
82
+ }
83
+ const driver = new BotDriver(myCaps)
84
+ assert.strictEqual(driver.caps.CAP_STRING_1, myCaps.CAP_STRING_1)
85
+ assert.strictEqual(driver.caps.CAP_STRING_2, myCaps.CAP_STRING_2)
86
+ })
78
87
  it('should merge boolean envs', function () {
79
88
  process.env.BOTIUM_SIMPLEREST_PING_PROCESS_RESPONSE = 'NO'
80
89
  const driver = new BotDriver()