botium-core 1.12.3 → 1.12.6

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.3",
3
+ "version": "1.12.6",
4
4
  "description": "The Selenium for Chatbots",
5
5
  "main": "index.js",
6
6
  "module": "dist/botium-es.js",
@@ -32,25 +32,25 @@
32
32
  },
33
33
  "homepage": "https://www.botium.ai",
34
34
  "dependencies": {
35
- "@babel/runtime": "^7.17.2",
35
+ "@babel/runtime": "^7.17.9",
36
36
  "async": "^3.2.3",
37
- "body-parser": "^1.19.1",
38
- "boolean": "^3.1.4",
37
+ "body-parser": "^1.20.0",
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.5",
45
+ "ioredis": "^5.0.4",
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
- "moment": "^2.29.1",
53
+ "moment": "^2.29.3",
54
54
  "mustache": "^4.2.0",
55
55
  "promise-retry": "^2.0.1",
56
56
  "promise.allsettled": "^1.0.5",
@@ -62,36 +62,36 @@
62
62
  "socket.io": "^4.4.1",
63
63
  "socket.io-client": "^4.4.1",
64
64
  "socketio-auth": "^0.1.1",
65
- "swagger-jsdoc": "^6.1.0",
65
+ "swagger-jsdoc": "^6.2.1",
66
66
  "swagger-ui-express": "^4.3.0",
67
67
  "uuid": "^8.3.2",
68
- "vm2": "^3.9.7",
68
+ "vm2": "^3.9.9",
69
69
  "write-yaml": "^1.0.0",
70
- "xlsx": "^0.18.2",
70
+ "xlsx": "^0.18.5",
71
71
  "xregexp": "^5.1.0",
72
- "yaml": "^1.10.2"
72
+ "yaml": "^2.0.1"
73
73
  },
74
74
  "devDependencies": {
75
- "@babel/core": "^7.17.2",
75
+ "@babel/core": "^7.17.9",
76
76
  "@babel/node": "^7.16.8",
77
77
  "@babel/plugin-transform-runtime": "^7.17.0",
78
78
  "@babel/preset-env": "^7.16.11",
79
79
  "chai": "^4.3.6",
80
80
  "chai-as-promised": "^7.1.1",
81
81
  "cross-env": "^7.0.3",
82
- "eslint": "^8.9.0",
83
- "eslint-config-standard": "^16.0.3",
84
- "eslint-plugin-import": "^2.25.4",
85
- "eslint-plugin-node": "^11.1.0",
82
+ "eslint": "^8.13.0",
83
+ "eslint-config-standard": "^17.0.0",
84
+ "eslint-plugin-import": "^2.26.0",
85
+ "eslint-plugin-n": "^15.1.0",
86
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.2.0",
90
+ "mocha": "^9.2.2",
91
91
  "nock": "^13.2.4",
92
- "npm-check-updates": "^12.3.0",
92
+ "npm-check-updates": "^12.5.9",
93
93
  "nyc": "^15.1.0",
94
- "rollup": "^2.67.2",
94
+ "rollup": "^2.70.2",
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
@@ -115,6 +115,7 @@ module.exports = class BotDriver {
115
115
  this.eventEmitter.emit(Events.CONTAINER_BUILDING)
116
116
 
117
117
  return new Promise((resolve, reject) => {
118
+ let tempDirectory = null
118
119
  let repo = null
119
120
  let container = null
120
121
 
@@ -126,9 +127,19 @@ module.exports = class BotDriver {
126
127
  .catch(driverValidated)
127
128
  },
128
129
 
130
+ (tempDirectoryCreated) => {
131
+ tempDirectory = path.resolve(process.cwd(), this.caps[Capabilities.TEMPDIR], sanitize(`${this.caps[Capabilities.PROJECTNAME]} ${moment().format('YYYYMMDD HHmmss')} ${randomize('Aa0', 5)}`))
132
+ try {
133
+ mkdirp.sync(tempDirectory)
134
+ tempDirectoryCreated()
135
+ } catch (err) {
136
+ tempDirectoryCreated(new Error(`Unable to create temp directory ${tempDirectory}: ${err.message}`))
137
+ }
138
+ },
139
+
129
140
  (repoValidated) => {
130
141
  try {
131
- repo = this._getRepo()
142
+ repo = this._getRepo(tempDirectory)
132
143
  } catch (err) {
133
144
  return repoValidated(err)
134
145
  }
@@ -141,7 +152,7 @@ module.exports = class BotDriver {
141
152
 
142
153
  (containerValidated) => {
143
154
  try {
144
- container = this._getContainer(repo)
155
+ container = this._getContainer(tempDirectory, repo)
145
156
  } catch (err) {
146
157
  return containerValidated(err)
147
158
  }
@@ -156,9 +167,9 @@ module.exports = class BotDriver {
156
167
  if (err) {
157
168
  debug(`BotDriver Build error: ${err}`)
158
169
  this.eventEmitter.emit(Events.CONTAINER_BUILD_ERROR, err)
159
- if (this.tempDirectory) {
160
- rimraf(this.tempDirectory, (err) => {
161
- if (err) debug(`Cleanup temp dir ${this.tempDirectory} failed: ${util.inspect(err)}`)
170
+ if (tempDirectory) {
171
+ rimraf(tempDirectory, (err) => {
172
+ if (err) debug(`Cleanup temp dir ${tempDirectory} failed: ${util.inspect(err)}`)
162
173
  })
163
174
  }
164
175
  return reject(err)
@@ -311,12 +322,6 @@ module.exports = class BotDriver {
311
322
  throw new Error(`Capability '${Capabilities.CONTAINERMODE}' or '${Capabilities.BOTIUMGRIDURL}' missing`)
312
323
  }
313
324
 
314
- this.tempDirectory = path.resolve(process.cwd(), this.caps[Capabilities.TEMPDIR], sanitize(`${this.caps[Capabilities.PROJECTNAME]} ${moment().format('YYYYMMDD HHmmss')} ${randomize('Aa0', 5)}`))
315
- try {
316
- mkdirp.sync(this.tempDirectory)
317
- } catch (err) {
318
- throw new Error(`Unable to create temp directory ${this.tempDirectory}: ${err}`)
319
- }
320
325
  resolve(this)
321
326
  } catch (err) {
322
327
  reject(err)
@@ -324,35 +329,35 @@ module.exports = class BotDriver {
324
329
  })
325
330
  }
326
331
 
327
- _getRepo () {
332
+ _getRepo (tempDirectory) {
328
333
  if (this.caps[Capabilities.BOTIUMGRIDURL]) {
329
334
  const NoRepo = require('./repos/NoRepo')
330
- return new NoRepo(this.tempDirectory, this.sources)
335
+ return new NoRepo(tempDirectory, this.sources)
331
336
  }
332
337
  if (this.sources[Source.GITURL]) {
333
338
  const GitRepo = require('./repos/GitRepo')
334
- return new GitRepo(this.tempDirectory, this.sources)
339
+ return new GitRepo(tempDirectory, this.sources)
335
340
  }
336
341
  if (this.sources[Source.LOCALPATH]) {
337
342
  const LocalRepo = require('./repos/LocalRepo')
338
- return new LocalRepo(this.tempDirectory, this.sources)
343
+ return new LocalRepo(tempDirectory, this.sources)
339
344
  }
340
345
  throw new Error(`No Repo provider found for Sources ${util.inspect(this.sources)}`)
341
346
  }
342
347
 
343
- _getContainer (repo) {
348
+ _getContainer (tempDirectory, repo) {
344
349
  if (this.caps[Capabilities.BOTIUMGRIDURL]) {
345
350
  const GridContainer = require('./containers/GridContainer')
346
- return new GridContainer(this.eventEmitter, this.tempDirectory, repo, this.caps, this.envs)
351
+ return new GridContainer(this.eventEmitter, tempDirectory, repo, this.caps, this.envs)
347
352
  }
348
353
  if (!this.caps[Capabilities.CONTAINERMODE]) {
349
354
  throw new Error(`Capability '${Capabilities.CONTAINERMODE}' missing`)
350
355
  }
351
356
  if (this.caps[Capabilities.CONTAINERMODE] === 'inprocess') {
352
357
  const InProcessContainer = require('./containers/InProcessContainer')
353
- return new InProcessContainer(this.eventEmitter, this.tempDirectory, repo, this.caps, this.envs)
358
+ return new InProcessContainer(this.eventEmitter, tempDirectory, repo, this.caps, this.envs)
354
359
  }
355
360
  const PluginConnectorContainer = require('./containers/PluginConnectorContainer')
356
- return new PluginConnectorContainer(this.eventEmitter, this.tempDirectory, repo, this.caps, this.envs)
361
+ return new PluginConnectorContainer(this.eventEmitter, tempDirectory, repo, this.caps, this.envs)
357
362
  }
358
363
  }
@@ -91,6 +91,7 @@ module.exports = {
91
91
  SIMPLEREST_REDIS_TOPIC: 'SIMPLEREST_REDIS_TOPIC',
92
92
  SIMPLEREST_INBOUND_ORDER_UNSETTLED_EVENTS_JSONPATH: 'SIMPLEREST_INBOUND_ORDER_UNSETTLED_EVENTS_JSONPATH',
93
93
  SIMPLEREST_INBOUND_DEBOUNCE_TIMEOUT: 'SIMPLEREST_INBOUND_DEBOUNCE_TIMEOUT',
94
+ SIMPLEREST_COOKIE_REPLICATION: 'SIMPLEREST_COOKIE_REPLICATION',
94
95
  // Script Compiler
95
96
  SCRIPTING_TXT_EOL: 'SCRIPTING_TXT_EOL',
96
97
  // ROW_PER_MESSAGE or QUESTION_ANSWER
package/src/Defaults.js CHANGED
@@ -34,6 +34,7 @@ module.exports = {
34
34
  [Capabilities.SIMPLEREST_STRICT_SSL]: true,
35
35
  [Capabilities.SIMPLEREST_INBOUND_UPDATE_CONTEXT]: true,
36
36
  [Capabilities.SIMPLEREST_CONTEXT_MERGE_OR_REPLACE]: 'MERGE',
37
+ [Capabilities.SIMPLEREST_COOKIE_REPLICATION]: true,
37
38
  [Capabilities.SCRIPTING_TXT_EOL]: '\n',
38
39
  [Capabilities.SCRIPTING_XLSX_EOL_WRITE]: '\r\n',
39
40
  [Capabilities.SCRIPTING_XLSX_HASHEADERS]: true,
@@ -28,6 +28,7 @@ module.exports = class SimpleRestContainer {
28
28
  this.bottleneck = bottleneck || ((fn) => fn())
29
29
  this.processInbound = false
30
30
  this.redisTopic = this.caps[Capabilities.SIMPLEREST_REDIS_TOPIC] || 'SIMPLEREST_INBOUND_SUBSCRIPTION'
31
+ this.cookies = {}
31
32
 
32
33
  if (this.caps[Capabilities.SIMPLEREST_INBOUND_ORDER_UNSETTLED_EVENTS_JSONPATH]) {
33
34
  const debounceTimeout = this.caps[Capabilities.SIMPLEREST_INBOUND_DEBOUNCE_TIMEOUT] || 500
@@ -451,7 +452,7 @@ module.exports = class SimpleRestContainer {
451
452
 
452
453
  if (body) {
453
454
  debug(`got response code: ${response.statusCode}, body: ${botiumUtils.shortenJsonString(body)}`)
454
-
455
+ this._storeCookiesFromResponse(response)
455
456
  try {
456
457
  body = await this._parseResponseBody(body)
457
458
  } catch (err) {
@@ -560,8 +561,8 @@ module.exports = class SimpleRestContainer {
560
561
  }
561
562
  }
562
563
  this._addRequestOptions(requestOptions)
563
-
564
564
  await executeHook(this.caps, this.requestHook, Object.assign({ requestOptions }, this.view))
565
+ this._addRequestCookies(requestOptions)
565
566
 
566
567
  return requestOptions
567
568
  }
@@ -593,6 +594,7 @@ module.exports = class SimpleRestContainer {
593
594
  await timeout(pingConfig.timeout)
594
595
  } else {
595
596
  debug(`_waitForUrlResponse success on url check ${pingConfig.uri}: ${response.statusCode}/${response.statusMessage}`)
597
+ this._storeCookiesFromResponse(response)
596
598
  if (debug.enabled && body) {
597
599
  debug(botiumUtils.shortenJsonString(body))
598
600
  }
@@ -753,9 +755,9 @@ module.exports = class SimpleRestContainer {
753
755
  const timeout = this._getCapValue(Capabilities.SIMPLEREST_POLL_TIMEOUT)
754
756
  const pollConfig = {
755
757
  method: verb,
756
- uri: uri,
758
+ uri,
757
759
  followAllRedirects: true,
758
- timeout: timeout
760
+ timeout
759
761
  }
760
762
  if (this.caps[Capabilities.SIMPLEREST_POLL_HEADERS]) {
761
763
  try {
@@ -783,6 +785,8 @@ module.exports = class SimpleRestContainer {
783
785
  debug(`_runPolling: exeucting request hook failed - (${err.message})`)
784
786
  return
785
787
  }
788
+ this._addRequestCookies(pollConfig)
789
+
786
790
  request(pollConfig, async (err, response, body) => {
787
791
  if (err) {
788
792
  debug(`_runPolling: rest request failed: ${err.message}, request: ${JSON.stringify(pollConfig)}`)
@@ -794,7 +798,7 @@ module.exports = class SimpleRestContainer {
794
798
  }
795
799
  } else if (body) {
796
800
  debug(`_runPolling: got response code: ${response.statusCode}, body: ${botiumUtils.shortenJsonString(body)}`)
797
-
801
+ this._storeCookiesFromResponse(response)
798
802
  try {
799
803
  body = await this._parseResponseBody(body)
800
804
  } catch (err) {
@@ -835,9 +839,9 @@ module.exports = class SimpleRestContainer {
835
839
  const timeout = this._getCapValue(`${capPrefix}_TIMEOUT`) || this._getCapValue(Capabilities.SIMPLEREST_TIMEOUT)
836
840
  const httpConfig = {
837
841
  method: verb,
838
- uri: uri,
842
+ uri,
839
843
  followAllRedirects: true,
840
- timeout: timeout
844
+ timeout
841
845
  }
842
846
  if (this.caps[`${capPrefix}_HEADERS`]) {
843
847
  try {
@@ -856,8 +860,8 @@ module.exports = class SimpleRestContainer {
856
860
  }
857
861
  }
858
862
  this._addRequestOptions(httpConfig)
859
-
860
863
  await executeHook(this.caps, this.requestHooks[capPrefix], Object.assign({ requestOptions: httpConfig }, this.view))
864
+ this._addRequestCookies(httpConfig)
861
865
 
862
866
  const retries = this._getCapValue(`${capPrefix}_RETRIES`)
863
867
  debug(`_makeCall(${capPrefix}): rest request: ${JSON.stringify(httpConfig)}`)
@@ -874,4 +878,34 @@ module.exports = class SimpleRestContainer {
874
878
  _.merge(httpConfig, this.caps[Capabilities.SIMPLEREST_EXTRA_OPTIONS])
875
879
  }
876
880
  }
881
+
882
+ _addRequestCookies (requestOptions) {
883
+ if (!this.caps[Capabilities.SIMPLEREST_COOKIE_REPLICATION] || !requestOptions) {
884
+ return
885
+ }
886
+ const url = new URL(requestOptions.uri)
887
+ if (!requestOptions.headers) {
888
+ requestOptions.headers = {}
889
+ }
890
+ requestOptions.headers.Cookie = requestOptions.headers.Cookie ? `${requestOptions.headers.Cookie}; ${this.cookies[url.host]}` : this.cookies[url.host]
891
+ }
892
+
893
+ _storeCookiesFromResponse (response) {
894
+ if (!this.caps[Capabilities.SIMPLEREST_COOKIE_REPLICATION] || !response) {
895
+ return
896
+ }
897
+ const responseCookies = response.headers['set-cookie']
898
+ if (!responseCookies) {
899
+ return
900
+ }
901
+ const host = _.get(response, 'request.uri.host')
902
+ let cookie
903
+ responseCookies.forEach(cookieString => {
904
+ cookie = cookie ? `${cookie}; ${cookieString}` : cookieString
905
+ })
906
+
907
+ if (cookie) {
908
+ this.cookies[host] = cookie
909
+ }
910
+ }
877
911
  }
@@ -69,7 +69,7 @@ const tryLoadPlugin = (containermode, modulepath, args) => {
69
69
  source: 'src/containers/plugins/index.js',
70
70
  cause: {
71
71
  SECURITY_ALLOW_UNSAFE: caps[Capabilities.SECURITY_ALLOW_UNSAFE],
72
- mode: mode,
72
+ mode,
73
73
  ...cause
74
74
  }
75
75
  }
@@ -46,7 +46,7 @@ const toJsonWeak = (stringOrNot) => {
46
46
 
47
47
  const optionalJson = (json) => {
48
48
  const body = isJson(json)
49
- return body ? { 'content-type': 'application/json', body: body } : { 'content-type': 'text/plain', body: json }
49
+ return body ? { 'content-type': 'application/json', body } : { 'content-type': 'text/plain', body: json }
50
50
  }
51
51
 
52
52
  const shortenJsonString = (obj) => {
@@ -339,7 +339,7 @@ module.exports = class CompilerXlsx extends CompilerBase {
339
339
  agg[varName] = varValues[varIndex][caseIndex] || null
340
340
  return agg
341
341
  }, {})
342
- scriptResults.push({ header: { name: caseName }, values: values })
342
+ scriptResults.push({ header: { name: caseName }, values })
343
343
  }
344
344
  } else {
345
345
  const variableNames = []
@@ -370,7 +370,7 @@ module.exports = class CompilerXlsx extends CompilerBase {
370
370
  }
371
371
  rowindex += 1
372
372
 
373
- scriptResults.push({ header: { name: caseName }, values: values })
373
+ scriptResults.push({ header: { name: caseName }, values })
374
374
  } else {
375
375
  break
376
376
  }
@@ -235,14 +235,14 @@ class Convo {
235
235
  await this.runConversation(container, scriptingMemory, transcript)
236
236
  await this._checkBotRepliesConsumed(container)
237
237
  try {
238
- await this.scriptingEvents.onConvoEnd({ convo: this, convoStep: { stepTag: '#end' }, container, transcript, scriptingMemory: scriptingMemory })
238
+ await this.scriptingEvents.onConvoEnd({ convo: this, convoStep: { stepTag: '#end' }, container, transcript, scriptingMemory })
239
239
  } catch (err) {
240
240
  throw new TranscriptError(botiumErrorFromErr(`${this.header.name}: ${err.message}`, err), transcript)
241
241
  }
242
242
  if (transcript.err && container.caps[Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS]) {
243
243
  let assertConvoEndErr = null
244
244
  try {
245
- await this.scriptingEvents.assertConvoEnd({ convo: this, convoStep: { stepTag: '#end' }, container, transcript, scriptingMemory: scriptingMemory })
245
+ await this.scriptingEvents.assertConvoEnd({ convo: this, convoStep: { stepTag: '#end' }, container, transcript, scriptingMemory })
246
246
  } catch (err) {
247
247
  assertConvoEndErr = botiumErrorFromErr(`${this.header.name}: ${err.message}`, err)
248
248
  }
@@ -257,7 +257,7 @@ class Convo {
257
257
  throw new TranscriptError(transcript.err, transcript)
258
258
  }
259
259
  try {
260
- await this.scriptingEvents.assertConvoEnd({ convo: this, convoStep: { stepTag: '#end' }, container, transcript, scriptingMemory: scriptingMemory })
260
+ await this.scriptingEvents.assertConvoEnd({ convo: this, convoStep: { stepTag: '#end' }, container, transcript, scriptingMemory })
261
261
  } catch (err) {
262
262
  transcript.err = botiumErrorFromErr(`${this.header.name}: ${err.message}`, err)
263
263
  throw new TranscriptError(transcript.err, transcript)
@@ -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
 
@@ -535,7 +535,7 @@ class Convo {
535
535
  if (container.caps[Capabilities.SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS]) {
536
536
  const transcriptStepErrs = transcript.steps.filter(s => s.err).map(s => s.err)
537
537
  if (transcriptStepErrs && transcriptStepErrs.length > 0) {
538
- transcript.err = botiumErrorFromList([transcriptStepErrs, transcript.err].filter(e => e), {})
538
+ transcript.err = botiumErrorFromList([...transcriptStepErrs.filter(err => err !== transcript.err), transcript.err].filter(e => e), {})
539
539
  }
540
540
  }
541
541
  }
@@ -1192,7 +1192,7 @@ module.exports = class ScriptingProvider {
1192
1192
  const node = {
1193
1193
  sender: convoNode.sender,
1194
1194
  key: randomize('0', 20),
1195
- hash: hash,
1195
+ hash,
1196
1196
  convoNodes: convoNodeValues,
1197
1197
  convos: [_.cloneDeep(convoNodeHeader)],
1198
1198
  childNodes: []
@@ -77,6 +77,13 @@ const flatString = (str) => {
77
77
  return str ? str.split('\n').map(s => s.trim()).join(' ') : ''
78
78
  }
79
79
 
80
+ const _formatAppendArgs = (args) => {
81
+ return args ? ` ${args.map(a => _.isString(a) ? a.replace(/\|/g, '\\|') : `${a}`).join('|')}` : ''
82
+ }
83
+ const _parseArgs = (str) => {
84
+ return (str && str.length > 0 && str.replace(/\\\|/g, '###ESCAPESPLIT###').split('|').map(s => s.replace(/###ESCAPESPLIT###/g, '|').trim())) || []
85
+ }
86
+
80
87
  const linesToConvoStep = (lines, sender, context, eol, singleLineMode = false) => {
81
88
  if (!validateSender(sender)) throw new Error(`Failed to parse conversation. Section "${sender}" unknown.`)
82
89
 
@@ -106,14 +113,14 @@ const linesToConvoStep = (lines, sender, context, eol, singleLineMode = false) =
106
113
  }
107
114
  const name = logicLine.split(' ')[0]
108
115
  if (sender !== 'me' && context.IsAsserterValid(name)) {
109
- const args = (logicLine.length > name.length ? logicLine.substr(name.length + 1).split('|').map(a => a.trim()) : [])
116
+ const args = (logicLine.length > name.length ? _parseArgs(logicLine.substr(name.length + 1)) : [])
110
117
  convoStep.asserters.push({ name, args, not, optional })
111
118
  } else if (sender === 'me' && context.IsUserInputValid(name)) {
112
- const args = (logicLine.length > name.length ? logicLine.substr(name.length + 1).split('|').map(a => a.trim()) : [])
119
+ const args = (logicLine.length > name.length ? _parseArgs(logicLine.substr(name.length + 1)) : [])
113
120
  convoStep.userInputs.push({ name, args })
114
121
  textLinesAccepted = false
115
122
  } else if (context.IsLogicHookValid(name)) {
116
- const args = (logicLine.length > name.length ? logicLine.substr(name.length + 1).split('|').map(a => a.trim()) : [])
123
+ const args = (logicLine.length > name.length ? _parseArgs(logicLine.substr(name.length + 1)) : [])
117
124
  convoStep.logicHooks.push({ name, args })
118
125
  textLinesAccepted = false
119
126
  } else {
@@ -422,7 +429,7 @@ const convoStepToLines = (step) => {
422
429
  const lines = []
423
430
  if (step.sender === 'me') {
424
431
  step.forms && step.forms.filter(form => form.value).forEach((form) => {
425
- lines.push(`FORM ${form.name}|${form.value}`)
432
+ lines.push(`FORM${_formatAppendArgs([form.name, form.value])}`)
426
433
  })
427
434
  if (step.buttons && step.buttons.length > 0) {
428
435
  lines.push('BUTTON ' + _decompileButton(step.buttons[0]))
@@ -432,34 +439,34 @@ const convoStepToLines = (step) => {
432
439
  lines.push(step.messageText)
433
440
  }
434
441
  step.userInputs && step.userInputs.forEach((userInput) => {
435
- lines.push(userInput.name + (userInput.args ? ' ' + userInput.args.join('|') : ''))
442
+ lines.push(userInput.name + _formatAppendArgs(userInput.args))
436
443
  })
437
444
  step.logicHooks && step.logicHooks.forEach((logicHook) => {
438
- lines.push(logicHook.name + (logicHook.args ? ' ' + logicHook.args.join('|') : ''))
445
+ lines.push(logicHook.name + _formatAppendArgs(logicHook.args))
439
446
  })
440
447
  } else {
441
448
  if (step.messageText) {
442
449
  lines.push((step.optional ? '?' : '') + (step.not ? '!' : '') + step.messageText)
443
450
  }
444
- if (step.buttons && step.buttons.length > 0) lines.push('BUTTONS ' + step.buttons.filter(b => b.text).map(b => flatString(b.text)).join('|'))
445
- if (step.media && step.media.length > 0) lines.push('MEDIA ' + step.media.filter(m => !m.buffer && m.mediaUri).map(m => m.mediaUri).join('|'))
451
+ if (step.buttons && step.buttons.length > 0) lines.push('BUTTONS' + _formatAppendArgs(step.buttons.filter(b => b.text).map(b => flatString(b.text))))
452
+ if (step.media && step.media.length > 0) lines.push('MEDIA' + _formatAppendArgs(step.media.filter(m => !m.buffer && m.mediaUri).map(m => m.mediaUri)))
446
453
  if (step.cards && step.cards.length > 0) {
447
454
  step.cards.forEach(c => {
448
455
  let cardTexts = []
449
456
  if (c.text) cardTexts = cardTexts.concat(_.isArray(c.text) ? c.text : [c.text])
450
457
  if (c.subtext) cardTexts = cardTexts.concat(_.isArray(c.subtext) ? c.subtext : [c.subtext])
451
458
  if (c.content) cardTexts = cardTexts.concat(_.isArray(c.content) ? c.content : [c.content])
452
- if (cardTexts.length > 0) lines.push('CARDS ' + cardTexts.map(c => flatString(c)).join('|'))
459
+ if (cardTexts.length > 0) lines.push('CARDS' + _formatAppendArgs(cardTexts.map(c => flatString(c))))
453
460
 
454
- if (c.buttons && c.buttons.length > 0) lines.push('BUTTONS ' + c.buttons.filter(b => b.text).map(b => flatString(b.text)).join('|'))
461
+ if (c.buttons && c.buttons.length > 0) lines.push('BUTTONS' + _formatAppendArgs(c.buttons.filter(b => b.text).map(b => flatString(b.text))))
455
462
  if (c.image && !c.image.buffer && c.image.mediaUri) lines.push('MEDIA ' + c.image.mediaUri)
456
463
  })
457
464
  }
458
465
  step.asserters && step.asserters.forEach((asserter) => {
459
- lines.push((asserter.optional ? '?' : '') + (asserter.not ? '!' : '') + asserter.name + (asserter.args ? ' ' + asserter.args.join('|') : ''))
466
+ lines.push((asserter.optional ? '?' : '') + (asserter.not ? '!' : '') + asserter.name + _formatAppendArgs(asserter.args))
460
467
  })
461
468
  step.logicHooks && step.logicHooks.forEach((logicHook) => {
462
- lines.push(logicHook.name + (logicHook.args ? ' ' + logicHook.args.join('|') : ''))
469
+ lines.push(logicHook.name + _formatAppendArgs(logicHook.args))
463
470
  })
464
471
  }
465
472
  return lines.map(l => l.trim())
@@ -494,7 +501,7 @@ const linesToScriptingMemories = (lines, columnMode = null) => {
494
501
  agg[varName] = varValues[varIndex][caseIndex] || null
495
502
  return agg
496
503
  }, {})
497
- const scriptingMemory = { header: { name: caseName }, values: values }
504
+ const scriptingMemory = { header: { name: caseName }, values }
498
505
  scriptingMemories.push(scriptingMemory)
499
506
  }
500
507
  } else {
@@ -28,7 +28,7 @@ module.exports = class BaseCountAsserter {
28
28
  args
29
29
  },
30
30
  cause: {
31
- not: not,
31
+ not,
32
32
  expected: check,
33
33
  actual: count
34
34
  }
@@ -1,5 +1,7 @@
1
+ const { SCRIPTING_NORMALIZE_TEXT } = require('../../../Capabilities')
1
2
  const { BotiumError } = require('../../BotiumError')
2
3
  const { buttonsFromMsg } = require('../helpers')
4
+ const { normalizeText } = require('../../helper')
3
5
 
4
6
  module.exports = class ButtonsAsserter {
5
7
  constructor (context, caps = {}) {
@@ -9,14 +11,14 @@ module.exports = class ButtonsAsserter {
9
11
  }
10
12
 
11
13
  _evalButtons (args, botMsg) {
12
- const allButtons = buttonsFromMsg(botMsg, true).map(b => b.text)
14
+ const allButtons = buttonsFromMsg(botMsg, true).map(b => b.text).filter(b => b).map(b => normalizeText(b, !!this.caps[SCRIPTING_NORMALIZE_TEXT]))
13
15
  if (!args || args.length === 0) {
14
16
  return { allButtons, buttonsNotFound: [], buttonsFound: allButtons }
15
17
  }
16
18
  const buttonsNotFound = []
17
19
  const buttonsFound = []
18
20
  for (let i = 0; i < (args || []).length; i++) {
19
- if (allButtons.findIndex(b => this.context.Match(b, args[i])) < 0) {
21
+ if (allButtons.findIndex(b => this.context.Match(b, normalizeText(args[i], !!this.caps[SCRIPTING_NORMALIZE_TEXT]))) < 0) {
20
22
  buttonsNotFound.push(args[i])
21
23
  } else {
22
24
  buttonsFound.push(args[i])
@@ -1,5 +1,7 @@
1
+ const { SCRIPTING_NORMALIZE_TEXT } = require('../../../Capabilities')
1
2
  const { BotiumError } = require('../../BotiumError')
2
3
  const { cardsFromMsg } = require('../helpers')
4
+ const { normalizeText } = require('../../helper')
3
5
 
4
6
  module.exports = class CardsAsserter {
5
7
  constructor (context, caps = {}) {
@@ -9,14 +11,14 @@ module.exports = class CardsAsserter {
9
11
  }
10
12
 
11
13
  _evalCards (args, botMsg) {
12
- const allCards = cardsFromMsg(botMsg, true).reduce((acc, mc) => acc.concat([mc.text, mc.subtext, mc.content].filter(t => t)), [])
14
+ const allCards = cardsFromMsg(botMsg, true).reduce((acc, mc) => acc.concat([mc.text, mc.subtext, mc.content].filter(t => t).map(t => normalizeText(t, !!this.caps[SCRIPTING_NORMALIZE_TEXT]))), [])
13
15
  if (!args || args.length === 0) {
14
16
  return { allCards, cardsNotFound: [], cardsFound: allCards }
15
17
  }
16
18
  const cardsNotFound = []
17
19
  const cardsFound = []
18
20
  for (let i = 0; i < (args || []).length; i++) {
19
- if (allCards.findIndex(c => this.context.Match(c, args[i])) < 0) {
21
+ if (allCards.findIndex(c => this.context.Match(c, normalizeText(args[i], !!this.caps[SCRIPTING_NORMALIZE_TEXT]))) < 0) {
20
22
  cardsNotFound.push(args[i])
21
23
  } else {
22
24
  cardsFound.push(args[i])
@@ -308,5 +308,18 @@ describe('compiler.compilertxt', function () {
308
308
  assert.equal(convo.conversation[0].asserters.length, 1)
309
309
  assert.deepEqual(convo.conversation[0].asserters[0], { name: 'BUTTONS', args: ['Test1', 'Test2'], not: true, optional: false })
310
310
  })
311
+ it('should allow escape pipe for args', async function () {
312
+ const scriptBuffer = fs.readFileSync(path.resolve(__dirname, CONVOS_DIR, 'convos_args_escape.convo.txt'))
313
+ const context = buildContextWithPause()
314
+ const caps = {
315
+ }
316
+ const compiler = new Compiler(context, Object.assign({}, DefaultCapabilities, caps))
317
+
318
+ compiler.Compile(scriptBuffer, 'SCRIPTING_TYPE_CONVO')
319
+ const convo = context.convos[0]
320
+ assert.equal(convo.conversation.length, 1)
321
+ assert.equal(convo.conversation[0].asserters.length, 1)
322
+ assert.deepEqual(convo.conversation[0].asserters[0], { name: 'BUTTONS', args: ['Test|1', 'Test|2'], not: false, optional: false })
323
+ })
311
324
  })
312
325
  })
@@ -0,0 +1,2 @@
1
+ #bot
2
+ BUTTONS Test\|1|Test\|2