botium-core 1.14.1 → 1.14.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/dist/botium-cjs.js +341 -126
- package/dist/botium-cjs.js.map +1 -1
- package/dist/botium-es.js +359 -144
- package/dist/botium-es.js.map +1 -1
- package/package.json +11 -11
- package/src/Capabilities.js +2 -0
- package/src/scripting/BotiumError.js +40 -3
- package/src/scripting/CompilerMarkdown.js +2 -1
- package/src/scripting/CompilerTxt.js +1 -4
- package/src/scripting/Convo.js +113 -19
- package/src/scripting/MatchFunctions.js +30 -8
- package/src/scripting/ScriptingMemory.js +7 -0
- package/src/scripting/ScriptingProvider.js +87 -36
- package/src/scripting/helper.js +3 -2
- package/src/scripting/logichook/LogicHookConsts.js +5 -2
- package/src/scripting/logichook/LogicHookUtils.js +8 -6
- package/src/scripting/logichook/logichooks/ConvoStepParametersLogicHook.js +6 -0
- package/src/scripting/logichook/logichooks/OrderedListToButtonLogicHook.js +37 -0
- package/test/compiler/compilermarkdown.spec.js +3 -3
- package/test/compiler/compilertxt.spec.js +1 -1
- package/test/compiler/convos/txt/convos_emptyrow_just_emptyrow.convo.txt +1 -1
- package/test/convo/fillAndApplyScriptingMemory.spec.js +11 -0
- package/test/logichooks/orderedListToButton.spec.js +35 -0
- package/test/scripting/asserters/convoStepParameters.spec.js +151 -0
- package/test/scripting/asserters/convos/TEXT_GOOD.convo.txt +6 -0
- package/test/scripting/asserters/convos/convo_step_parameter_matchmode_failed.convo.txt +8 -0
- package/test/scripting/asserters/convos/convo_step_parameter_matchmode_failed_wer.convo.txt +9 -0
- package/test/scripting/asserters/convos/convo_step_parameter_retry_asserters_all_good.convo.txt +9 -0
- package/test/scripting/asserters/convos/convo_step_parameter_retry_asserters_botium_timeout.convo.txt +9 -0
- package/test/scripting/asserters/convos/convo_step_parameter_retry_asserters_good.convo.txt +9 -0
- package/test/scripting/asserters/convos/convo_step_parameter_retry_asserters_good_global.convo.txt +9 -0
- package/test/scripting/asserters/convos/convo_step_parameter_retry_main_and_asserter.convo.txt +10 -0
- package/test/scripting/asserters/convos/convo_step_parameter_retry_main_botium_timeout.convo.txt +9 -0
- package/test/scripting/asserters/convos/convo_step_parameter_retry_main_but_no_button.convo.txt +10 -0
- package/test/scripting/asserters/convos/convo_step_parameter_retry_main_good.convo.txt +9 -0
- package/test/scripting/asserters/convos/convo_step_parameter_retry_main_good_begin.convo.txt +11 -0
- package/test/scripting/logichooks/convos/scripting_memory_overwrite_and_check.convo.txt +2 -2
- package/test/scripting/matching/matchingmode.spec.js +4 -1
- package/test/scripting/scriptingProvider.spec.js +38 -12
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "botium-core",
|
|
3
|
-
"version": "1.14.
|
|
3
|
+
"version": "1.14.4",
|
|
4
4
|
"description": "The Selenium for Chatbots",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "dist/botium-es.js",
|
|
@@ -31,12 +31,12 @@
|
|
|
31
31
|
},
|
|
32
32
|
"homepage": "https://www.botium.ai",
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@babel/runtime": "^7.23.
|
|
34
|
+
"@babel/runtime": "^7.23.5",
|
|
35
35
|
"async": "^3.2.5",
|
|
36
36
|
"body-parser": "^1.20.2",
|
|
37
37
|
"boolean": "^3.2.0",
|
|
38
38
|
"bottleneck": "^2.19.5",
|
|
39
|
-
"csv-parse": "^5.5.
|
|
39
|
+
"csv-parse": "^5.5.3",
|
|
40
40
|
"debug": "^4.3.4",
|
|
41
41
|
"express": "^4.18.2",
|
|
42
42
|
"globby": "11.0.4",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"is-json": "^2.0.1",
|
|
46
46
|
"jsonpath": "^1.1.1",
|
|
47
47
|
"lodash": "^4.17.21",
|
|
48
|
-
"markdown-it": "^
|
|
48
|
+
"markdown-it": "^14.0.0",
|
|
49
49
|
"mime-types": "^2.1.35",
|
|
50
50
|
"mkdirp": "^3.0.1",
|
|
51
51
|
"moment": "^2.29.4",
|
|
@@ -71,23 +71,23 @@
|
|
|
71
71
|
"yaml": "^2.3.4"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
|
-
"@babel/core": "^7.23.
|
|
74
|
+
"@babel/core": "^7.23.5",
|
|
75
75
|
"@babel/node": "^7.22.19",
|
|
76
|
-
"@babel/plugin-transform-runtime": "^7.23.
|
|
77
|
-
"@babel/preset-env": "^7.23.
|
|
76
|
+
"@babel/plugin-transform-runtime": "^7.23.4",
|
|
77
|
+
"@babel/preset-env": "^7.23.5",
|
|
78
78
|
"chai": "^4.3.10",
|
|
79
79
|
"chai-as-promised": "^7.1.1",
|
|
80
80
|
"cross-env": "^7.0.3",
|
|
81
|
-
"eslint": "^8.
|
|
81
|
+
"eslint": "^8.55.0",
|
|
82
82
|
"eslint-config-standard": "^17.1.0",
|
|
83
83
|
"eslint-plugin-import": "^2.29.0",
|
|
84
84
|
"eslint-plugin-mocha": "^10.2.0",
|
|
85
|
-
"eslint-plugin-n": "^16.
|
|
85
|
+
"eslint-plugin-n": "^16.4.0",
|
|
86
86
|
"eslint-plugin-promise": "^6.1.1",
|
|
87
87
|
"eslint-plugin-standard": "^4.1.0",
|
|
88
88
|
"mocha": "^10.2.0",
|
|
89
|
-
"nock": "^13.
|
|
90
|
-
"npm-check-updates": "^16.14.
|
|
89
|
+
"nock": "^13.4.0",
|
|
90
|
+
"npm-check-updates": "^16.14.11",
|
|
91
91
|
"nyc": "^15.1.0",
|
|
92
92
|
"rollup": "2.79.1",
|
|
93
93
|
"rollup-plugin-babel": "^4.4.0",
|
package/src/Capabilities.js
CHANGED
|
@@ -157,6 +157,8 @@ module.exports = {
|
|
|
157
157
|
// varnames, testcasenames
|
|
158
158
|
SCRIPTING_MEMORY_COLUMN_MODE: 'SCRIPTING_MEMORY_COLUMN_MODE',
|
|
159
159
|
// Botium Lifecycle Hooks
|
|
160
|
+
SCRIPTING_CONVO_STEP_PARAMETERS: 'SCRIPTING_CONVO_STEP_PARAMETERS',
|
|
161
|
+
// Botium Lifecycle Hooks
|
|
160
162
|
CUSTOMHOOK_ONBUILD: 'CUSTOMHOOK_ONBUILD',
|
|
161
163
|
CUSTOMHOOK_ONSTART: 'CUSTOMHOOK_ONSTART',
|
|
162
164
|
CUSTOMHOOK_ONUSERSAYS: 'CUSTOMHOOK_ONUSERSAYS',
|
|
@@ -90,6 +90,43 @@ const BotiumError = class BotiumError extends Error {
|
|
|
90
90
|
return null
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
|
+
|
|
94
|
+
hasError ({ type, source }) {
|
|
95
|
+
if (this.context) {
|
|
96
|
+
const errArr = _.isArray(this.context) ? this.context : [this.context]
|
|
97
|
+
for (const err of errArr) {
|
|
98
|
+
if (err.type === 'list') {
|
|
99
|
+
for (const internal of err.errors) {
|
|
100
|
+
if ((!type || internal.type === type) && (!source || internal.source === source)) {
|
|
101
|
+
return true
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if ((!type || err.type === type) && (!source || err.source === source)) {
|
|
106
|
+
return true
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
return false
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
toArray () {
|
|
115
|
+
if (this.context) {
|
|
116
|
+
let result = []
|
|
117
|
+
const errArr = _.isArray(this.context) ? this.context : [this.context]
|
|
118
|
+
for (const err of errArr) {
|
|
119
|
+
if (err.type === 'list') {
|
|
120
|
+
result = result.concat(err.errors)
|
|
121
|
+
} else {
|
|
122
|
+
result.push(err)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return result
|
|
126
|
+
} else {
|
|
127
|
+
return []
|
|
128
|
+
}
|
|
129
|
+
}
|
|
93
130
|
}
|
|
94
131
|
|
|
95
132
|
const _getChildErrorsFromContext = (context) => {
|
|
@@ -99,11 +136,11 @@ const _getChildErrorsFromContext = (context) => {
|
|
|
99
136
|
return false
|
|
100
137
|
}
|
|
101
138
|
|
|
102
|
-
const botiumErrorFromErr = (message, err) => {
|
|
139
|
+
const botiumErrorFromErr = (message, err, context = {}) => {
|
|
103
140
|
if (err instanceof BotiumError) {
|
|
104
|
-
return new BotiumError(message, err.context, true)
|
|
141
|
+
return new BotiumError(message, { ...err.context, ...context }, true)
|
|
105
142
|
} else {
|
|
106
|
-
return new BotiumError(message, { err }, true)
|
|
143
|
+
return new BotiumError(message, { err, ...context }, true)
|
|
107
144
|
}
|
|
108
145
|
}
|
|
109
146
|
|
|
@@ -89,7 +89,8 @@ module.exports = class CompilerMarkdown extends CompilerBase {
|
|
|
89
89
|
stepTag: 'Line ' + (step.map[0] + 1)
|
|
90
90
|
},
|
|
91
91
|
linesToConvoStep(step.children.map(child => child.content +
|
|
92
|
-
(child.children ? ' ' + child.children.map(child => child.content).join('|') : '')
|
|
92
|
+
(child.children && child.children.length > 0 ? ' ' + child.children.map(child => child.content).join('|') : '')
|
|
93
|
+
), sender, this.context, this.eol)
|
|
93
94
|
))
|
|
94
95
|
} else {
|
|
95
96
|
debug(`Expected sender ${validSenders.map(s => `'${s}'`).join(' or ')} but found ${sender}`)
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
const _ = require('lodash')
|
|
2
|
-
|
|
3
1
|
const Capabilities = require('../Capabilities')
|
|
4
2
|
const Constants = require('./Constants')
|
|
5
3
|
const CompilerBase = require('./CompilerBase')
|
|
@@ -37,7 +35,7 @@ module.exports = class CompilerTxt extends CompilerBase {
|
|
|
37
35
|
let scriptData = scriptBuffer
|
|
38
36
|
if (Buffer.isBuffer(scriptBuffer)) scriptData = scriptData.toString()
|
|
39
37
|
|
|
40
|
-
const lines =
|
|
38
|
+
const lines = scriptData.split(this.eol)
|
|
41
39
|
|
|
42
40
|
if (scriptType === Constants.SCRIPTING_TYPE_CONVO) {
|
|
43
41
|
return this._compileConvo(lines, false)
|
|
@@ -94,7 +92,6 @@ module.exports = class CompilerTxt extends CompilerBase {
|
|
|
94
92
|
|
|
95
93
|
lines.forEach((line) => {
|
|
96
94
|
currentLineIndex++
|
|
97
|
-
line = line.trim()
|
|
98
95
|
if (isValidTagLine(line)) {
|
|
99
96
|
pushPrev()
|
|
100
97
|
|
package/src/scripting/Convo.js
CHANGED
|
@@ -277,8 +277,55 @@ class Convo {
|
|
|
277
277
|
let skipTranscriptStep = false
|
|
278
278
|
let conditionalGroupId = null
|
|
279
279
|
let conditionMetInGroup = false
|
|
280
|
-
|
|
280
|
+
let globalConvoStepParameters = container.caps[Capabilities.SCRIPTING_CONVO_STEP_PARAMETERS] || {}
|
|
281
|
+
let retryBotMessageTimeoutEnd = null
|
|
282
|
+
let retryBotMessageConvoId = null
|
|
283
|
+
let retryBotMessageDropBotResponse = false
|
|
284
|
+
for (let i = 0; i < this.conversation.length; i = (retryBotMessageDropBotResponse ? i : i + 1)) {
|
|
285
|
+
retryBotMessageDropBotResponse = false
|
|
281
286
|
const convoStep = this.conversation[i]
|
|
287
|
+
const rawConvoStepParameters = convoStep.logicHooks.find(lh => lh.name === 'CONVO_STEP_PARAMETERS')?.args
|
|
288
|
+
let convoStepParameters = {}
|
|
289
|
+
if (rawConvoStepParameters && rawConvoStepParameters.length) {
|
|
290
|
+
let params
|
|
291
|
+
if (rawConvoStepParameters[0].trim().startsWith('{')) {
|
|
292
|
+
try {
|
|
293
|
+
params = JSON.parse(rawConvoStepParameters[0])
|
|
294
|
+
} catch (e) {
|
|
295
|
+
debug(`${this.header.name}/${convoStep.stepTag}: Failed to parse convo step parameters from JSON ${rawConvoStepParameters[0]}`)
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
if (!params || !Object.keys(params).length) {
|
|
299
|
+
params = {}
|
|
300
|
+
for (const param of rawConvoStepParameters) {
|
|
301
|
+
const semicolon = param.indexOf(':')
|
|
302
|
+
if (semicolon) {
|
|
303
|
+
try {
|
|
304
|
+
const name = param.substring(0, semicolon)
|
|
305
|
+
const value = param.substring(semicolon + 1)
|
|
306
|
+
params[name] = value
|
|
307
|
+
} catch (e) {
|
|
308
|
+
debug(`${this.header.name}/${convoStep.stepTag}: Failed to parse convo step parameter from arg ${param}`)
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (convoStep.sender === 'begin') {
|
|
315
|
+
globalConvoStepParameters = Object.assign({}, globalConvoStepParameters || {}, params)
|
|
316
|
+
} else {
|
|
317
|
+
convoStepParameters = Object.assign({}, globalConvoStepParameters || {}, params)
|
|
318
|
+
}
|
|
319
|
+
} else {
|
|
320
|
+
if (convoStep.sender !== 'begin') {
|
|
321
|
+
convoStepParameters = globalConvoStepParameters
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (Object.keys(convoStepParameters).length) {
|
|
326
|
+
debug(`${this.header.name}: using convo step parameters ${JSON.stringify(convoStepParameters)}`)
|
|
327
|
+
}
|
|
328
|
+
|
|
282
329
|
const currentStepIndex = i
|
|
283
330
|
container.eventEmitter.emit(Events.CONVO_STEP_NEXT, container, convoStep, i)
|
|
284
331
|
skipTranscriptStep = false
|
|
@@ -319,8 +366,8 @@ class Convo {
|
|
|
319
366
|
const coreMsg = _.omit(removeBuffers(meMsg), ['sourceData'])
|
|
320
367
|
debug(`${this.header.name}/${convoStep.stepTag}: user says (cleaned by binary and base64 data and sourceData) ${JSON.stringify(coreMsg, null, 2)}`)
|
|
321
368
|
await new Promise(resolve => {
|
|
322
|
-
if (container.caps.SIMULATE_WRITING_SPEED && meMsg.messageText && meMsg.messageText.length) {
|
|
323
|
-
setTimeout(() => resolve(), container.caps.SIMULATE_WRITING_SPEED * meMsg.messageText.length)
|
|
369
|
+
if (container.caps[Capabilities.SIMULATE_WRITING_SPEED] && meMsg.messageText && meMsg.messageText.length) {
|
|
370
|
+
setTimeout(() => resolve(), container.caps[Capabilities.SIMULATE_WRITING_SPEED] * meMsg.messageText.length)
|
|
324
371
|
} else {
|
|
325
372
|
resolve()
|
|
326
373
|
}
|
|
@@ -453,11 +500,32 @@ class Convo {
|
|
|
453
500
|
}
|
|
454
501
|
const isErrorHandledWithOptionConvoStep = (err) => {
|
|
455
502
|
const nextConvoStep = this.conversation[i + 1]
|
|
503
|
+
const retryConfig = convoStepParameters?.ignoreNotMatchedBotResponses
|
|
504
|
+
const retryOn = convoStep.sender === 'bot' && retryConfig && retryConfig.timeout && retryConfig.mainAsserter
|
|
456
505
|
if (convoStep.optional && nextConvoStep && nextConvoStep.sender === 'bot') {
|
|
506
|
+
if (retryOn) {
|
|
507
|
+
debug(`${this.header.name}/${convoStep.stepTag}: Retry failed asserter is ignored on optional convo`)
|
|
508
|
+
}
|
|
457
509
|
waitForBotSays = false
|
|
458
510
|
skipTranscriptStep = true
|
|
459
511
|
return true
|
|
512
|
+
} else if (retryOn) {
|
|
513
|
+
if (!retryBotMessageTimeoutEnd || retryBotMessageConvoId !== convoStep.stepTag) {
|
|
514
|
+
retryBotMessageTimeoutEnd = transcriptStep.stepBegin.getTime() + +retryConfig.timeout
|
|
515
|
+
retryBotMessageConvoId = convoStep.stepTag
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
const now = new Date().getTime()
|
|
519
|
+
const timeoutRemaining = retryBotMessageTimeoutEnd - now
|
|
520
|
+
if (timeoutRemaining > 0) {
|
|
521
|
+
debug(`${this.header.name}/${convoStep.stepTag}: Convo step retry on, timeout remaining: ${timeoutRemaining}, error: "${err.message}"`)
|
|
522
|
+
retryBotMessageDropBotResponse = true
|
|
523
|
+
return false
|
|
524
|
+
} else {
|
|
525
|
+
debug(`${this.header.name}/${convoStep.stepTag}: Convo step retry on, but timeout is over. error: "${err.message}"`)
|
|
526
|
+
}
|
|
460
527
|
}
|
|
528
|
+
|
|
461
529
|
if (container.caps[Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS]) {
|
|
462
530
|
assertErrors.push(err)
|
|
463
531
|
return false
|
|
@@ -474,7 +542,7 @@ class Convo {
|
|
|
474
542
|
const tomatch = this._resolveUtterancesToMatch(container, Object.assign({}, scriptingMemoryUpdate, scriptingMemory), messageText, botMsg)
|
|
475
543
|
if (convoStep.not) {
|
|
476
544
|
try {
|
|
477
|
-
this.scriptingEvents.assertBotNotResponse(response, tomatch, `${this.header.name}/${convoStep.stepTag}`, lastMeConvoStep)
|
|
545
|
+
this.scriptingEvents.assertBotNotResponse(response, tomatch, `${this.header.name}/${convoStep.stepTag}`, lastMeConvoStep, convoStepParameters)
|
|
478
546
|
} catch (err) {
|
|
479
547
|
if (isErrorHandledWithOptionConvoStep(err)) {
|
|
480
548
|
continue
|
|
@@ -482,7 +550,7 @@ class Convo {
|
|
|
482
550
|
}
|
|
483
551
|
} else {
|
|
484
552
|
try {
|
|
485
|
-
this.scriptingEvents.assertBotResponse(response, tomatch, `${this.header.name}/${convoStep.stepTag}`, lastMeConvoStep)
|
|
553
|
+
this.scriptingEvents.assertBotResponse(response, tomatch, `${this.header.name}/${convoStep.stepTag}`, lastMeConvoStep, convoStepParameters)
|
|
486
554
|
} catch (err) {
|
|
487
555
|
if (isErrorHandledWithOptionConvoStep(err)) {
|
|
488
556
|
continue
|
|
@@ -491,7 +559,7 @@ class Convo {
|
|
|
491
559
|
}
|
|
492
560
|
} else if (convoStep.sourceData) {
|
|
493
561
|
try {
|
|
494
|
-
this._compareObject(container, scriptingMemory, convoStep, botMsg.sourceData, convoStep.sourceData, botMsg)
|
|
562
|
+
this._compareObject(container, scriptingMemory, convoStep, botMsg.sourceData, convoStep.sourceData, botMsg, convoStepParameters)
|
|
495
563
|
} catch (err) {
|
|
496
564
|
if (isErrorHandledWithOptionConvoStep(err)) {
|
|
497
565
|
continue
|
|
@@ -509,20 +577,46 @@ class Convo {
|
|
|
509
577
|
skipTranscriptStep = true
|
|
510
578
|
continue
|
|
511
579
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
580
|
+
|
|
581
|
+
const errors = err.toArray ? err.toArray() : []
|
|
582
|
+
const retryConfig = convoStepParameters?.ignoreNotMatchedBotResponses
|
|
583
|
+
const retryOn =
|
|
584
|
+
convoStep.sender === 'bot' &&
|
|
585
|
+
retryConfig &&
|
|
586
|
+
retryConfig.timeout &&
|
|
587
|
+
errors.length &&
|
|
588
|
+
errors.filter(({ type, source, asserter }) => type === 'asserter' && (retryConfig.allAsserters || (retryConfig.asserters && retryConfig.asserters.includes(asserter)))).length
|
|
589
|
+
if (retryOn && (!retryBotMessageTimeoutEnd || retryBotMessageConvoId !== convoStep.stepTag)) {
|
|
590
|
+
retryBotMessageTimeoutEnd = transcriptStep.stepBegin.getTime() + +retryConfig.timeout
|
|
591
|
+
retryBotMessageConvoId = convoStep.stepTag
|
|
517
592
|
}
|
|
518
|
-
|
|
519
|
-
|
|
593
|
+
|
|
594
|
+
const now = new Date().getTime()
|
|
595
|
+
const timeoutRemaining = retryOn && (retryBotMessageTimeoutEnd - now)
|
|
596
|
+
if (retryOn && timeoutRemaining > 0) {
|
|
597
|
+
debug(`${this.header.name}/${convoStep.stepTag}: Convo step retry on, timeout remaining: ${timeoutRemaining}, error: "${err.message}"`)
|
|
598
|
+
retryBotMessageDropBotResponse = true
|
|
520
599
|
} else {
|
|
521
|
-
|
|
600
|
+
if (retryOn && timeoutRemaining <= 0) {
|
|
601
|
+
debug(`${this.header.name}/${convoStep.stepTag}: Convo step retry on, but timeout is over. error: "${err.message}"`)
|
|
602
|
+
}
|
|
603
|
+
const failErr = botiumErrorFromErr(`${this.header.name}/${convoStep.stepTag}: assertion error - ${err.message || err}`, err)
|
|
604
|
+
debug(failErr)
|
|
605
|
+
try {
|
|
606
|
+
this.scriptingEvents.fail && this.scriptingEvents.fail(failErr, lastMeConvoStep)
|
|
607
|
+
} catch (failErr) {
|
|
608
|
+
}
|
|
609
|
+
if (container.caps[Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS] && err instanceof BotiumError) {
|
|
610
|
+
assertErrors.push(err)
|
|
611
|
+
} else {
|
|
612
|
+
throw failErr
|
|
613
|
+
}
|
|
522
614
|
}
|
|
523
615
|
}
|
|
524
616
|
if (container.caps[Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS]) {
|
|
525
617
|
if (assertErrors.length > 0) {
|
|
618
|
+
// this has no effect, but logically it has to be false
|
|
619
|
+
retryBotMessageDropBotResponse = false
|
|
526
620
|
throw botiumErrorFromList(assertErrors, {})
|
|
527
621
|
}
|
|
528
622
|
} else {
|
|
@@ -580,7 +674,7 @@ class Convo {
|
|
|
580
674
|
}
|
|
581
675
|
}
|
|
582
676
|
|
|
583
|
-
_compareObject (container, scriptingMemory, convoStep, result, expected, botMsg) {
|
|
677
|
+
_compareObject (container, scriptingMemory, convoStep, result, expected, botMsg, convoStepParameters) {
|
|
584
678
|
if (expected === null || expected === undefined) return
|
|
585
679
|
|
|
586
680
|
if (_.isArray(expected)) {
|
|
@@ -591,12 +685,12 @@ class Convo {
|
|
|
591
685
|
throw new BotiumError(`${this.header.name}/${convoStep.stepTag}: bot response expected array length ${expected.length}, got ${result.length}`)
|
|
592
686
|
}
|
|
593
687
|
for (let i = 0; i < expected.length; i++) {
|
|
594
|
-
this._compareObject(container, scriptingMemory, convoStep, result[i], expected[i])
|
|
688
|
+
this._compareObject(container, scriptingMemory, convoStep, result[i], expected[i], null, convoStepParameters)
|
|
595
689
|
}
|
|
596
690
|
} else if (_.isObject(expected)) {
|
|
597
691
|
_.forOwn(expected, (value, key) => {
|
|
598
692
|
if (Object.prototype.hasOwnProperty.call(result, key)) {
|
|
599
|
-
this._compareObject(container, scriptingMemory, convoStep, result[key], expected[key])
|
|
693
|
+
this._compareObject(container, scriptingMemory, convoStep, result[key], expected[key], null, convoStepParameters)
|
|
600
694
|
} else {
|
|
601
695
|
throw new BotiumError(`${this.header.name}/${convoStep.stepTag}: bot response "${result}" missing expected property: ${key}`)
|
|
602
696
|
}
|
|
@@ -605,7 +699,7 @@ class Convo {
|
|
|
605
699
|
ScriptingMemory.fill(container, scriptingMemory, result, expected, this.scriptingEvents)
|
|
606
700
|
const response = this._checkNormalizeText(container, result)
|
|
607
701
|
const tomatch = this._resolveUtterancesToMatch(container, scriptingMemory, expected, botMsg)
|
|
608
|
-
this.scriptingEvents.assertBotResponse(response, tomatch, `${this.header.name}/${convoStep.stepTag}
|
|
702
|
+
this.scriptingEvents.assertBotResponse(response, tomatch, `${this.header.name}/${convoStep.stepTag}`, null, convoStepParameters)
|
|
609
703
|
}
|
|
610
704
|
}
|
|
611
705
|
|
|
@@ -673,7 +767,7 @@ class Convo {
|
|
|
673
767
|
}
|
|
674
768
|
|
|
675
769
|
_checkBotRepliesConsumed (container) {
|
|
676
|
-
if (container.caps.SCRIPTING_FORCE_BOT_CONSUMED) {
|
|
770
|
+
if (container.caps[Capabilities.SCRIPTING_FORCE_BOT_CONSUMED]) {
|
|
677
771
|
const queueLength = container._QueueLength()
|
|
678
772
|
if (queueLength === 1) {
|
|
679
773
|
throw new Error('There is an unread bot reply in queue')
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
const _ = require('lodash')
|
|
2
|
+
const debug = require('debug')('botium-core-MatchFunctions')
|
|
2
3
|
|
|
3
4
|
const { toString, quoteRegexpString, calculateWer } = require('./helper')
|
|
4
5
|
|
|
5
|
-
const _normalize = (botresponse) => {
|
|
6
|
+
const _normalize = (botresponse, args, convoStepParameters) => {
|
|
7
|
+
if (!convoStepParameters) {
|
|
8
|
+
debug('Convo step parameters might be missing!')
|
|
9
|
+
}
|
|
6
10
|
if (_.isUndefined(botresponse) || _.isNil(botresponse)) return ''
|
|
7
11
|
if (_.isObject(botresponse) && _.has(botresponse, 'messageText')) {
|
|
8
12
|
return toString(botresponse.messageText) || ''
|
|
@@ -10,7 +14,10 @@ const _normalize = (botresponse) => {
|
|
|
10
14
|
return toString(botresponse)
|
|
11
15
|
}
|
|
12
16
|
|
|
13
|
-
const regexp = (ignoreCase) => (botresponse, utterance) => {
|
|
17
|
+
const regexp = (ignoreCase) => (botresponse, utterance, args, convoStepParameters) => {
|
|
18
|
+
if (!convoStepParameters) {
|
|
19
|
+
debug('Convo step parameters might be missing!')
|
|
20
|
+
}
|
|
14
21
|
if (_.isUndefined(botresponse)) return false
|
|
15
22
|
utterance = toString(utterance)
|
|
16
23
|
botresponse = _normalize(botresponse)
|
|
@@ -19,7 +26,10 @@ const regexp = (ignoreCase) => (botresponse, utterance) => {
|
|
|
19
26
|
return regexp.test(botresponse)
|
|
20
27
|
}
|
|
21
28
|
|
|
22
|
-
const wildcard = (ignoreCase) => (botresponse, utterance) => {
|
|
29
|
+
const wildcard = (ignoreCase) => (botresponse, utterance, args, convoStepParameters) => {
|
|
30
|
+
if (!convoStepParameters) {
|
|
31
|
+
debug('Convo step parameters might be missing!')
|
|
32
|
+
}
|
|
23
33
|
if (_.isUndefined(botresponse)) {
|
|
24
34
|
if (utterance.trim() === '*') return true
|
|
25
35
|
else return false
|
|
@@ -37,7 +47,10 @@ const wildcard = (ignoreCase) => (botresponse, utterance) => {
|
|
|
37
47
|
return regexp.test(botresponse)
|
|
38
48
|
}
|
|
39
49
|
|
|
40
|
-
const wildcardExact = (ignoreCase) => (botresponse, utterance) => {
|
|
50
|
+
const wildcardExact = (ignoreCase) => (botresponse, utterance, args, convoStepParameters) => {
|
|
51
|
+
if (!convoStepParameters) {
|
|
52
|
+
debug('Convo step parameters might be missing!')
|
|
53
|
+
}
|
|
41
54
|
if (_.isUndefined(botresponse)) {
|
|
42
55
|
if (utterance.trim() === '*') return true
|
|
43
56
|
else return false
|
|
@@ -55,7 +68,10 @@ const wildcardExact = (ignoreCase) => (botresponse, utterance) => {
|
|
|
55
68
|
return regexp.test(botresponse)
|
|
56
69
|
}
|
|
57
70
|
|
|
58
|
-
const include = (ignoreCase) => (botresponse, utterance) => {
|
|
71
|
+
const include = (ignoreCase) => (botresponse, utterance, args, convoStepParameters) => {
|
|
72
|
+
if (!convoStepParameters) {
|
|
73
|
+
debug('Convo step parameters might be missing!')
|
|
74
|
+
}
|
|
59
75
|
if (_.isUndefined(botresponse)) return false
|
|
60
76
|
utterance = toString(utterance)
|
|
61
77
|
botresponse = _normalize(botresponse)
|
|
@@ -67,7 +83,10 @@ const include = (ignoreCase) => (botresponse, utterance) => {
|
|
|
67
83
|
return botresponse.indexOf(utterance) >= 0
|
|
68
84
|
}
|
|
69
85
|
|
|
70
|
-
const equals = (ignoreCase) => (botresponse, utterance) => {
|
|
86
|
+
const equals = (ignoreCase) => (botresponse, utterance, args, convoStepParameters) => {
|
|
87
|
+
if (!convoStepParameters) {
|
|
88
|
+
debug('Convo step parameters might be missing!')
|
|
89
|
+
}
|
|
71
90
|
if (_.isUndefined(botresponse)) return false
|
|
72
91
|
utterance = toString(utterance)
|
|
73
92
|
botresponse = _normalize(botresponse)
|
|
@@ -79,11 +98,14 @@ const equals = (ignoreCase) => (botresponse, utterance) => {
|
|
|
79
98
|
return botresponse === utterance
|
|
80
99
|
}
|
|
81
100
|
|
|
82
|
-
const wer = () => (botresponse, utterance, args) => {
|
|
101
|
+
const wer = () => (botresponse, utterance, args, convoStepParameters) => {
|
|
102
|
+
if (!convoStepParameters) {
|
|
103
|
+
debug('Convo step parameters might be missing!')
|
|
104
|
+
}
|
|
83
105
|
botresponse = _normalize(botresponse || '')
|
|
84
106
|
utterance = toString(utterance || '')
|
|
85
107
|
|
|
86
|
-
const threshold = ([',', '.'].find(p => `${args[0]}`.includes(p)) ? parseFloat(args[0]) : parseInt(args[0]) / 100)
|
|
108
|
+
const threshold = !_.isNil(convoStepParameters?.matchingModeWer) ? (convoStepParameters?.matchingModeWer / 100) : ([',', '.'].find(p => `${args[0]}`.includes(p)) ? parseFloat(args[0]) : parseInt(args[0]) / 100)
|
|
87
109
|
return calculateWer(botresponse, utterance) <= threshold
|
|
88
110
|
}
|
|
89
111
|
|
|
@@ -261,6 +261,13 @@ const _apply = (scriptingMemory, str, caps, mockMsg) => {
|
|
|
261
261
|
return arg
|
|
262
262
|
}
|
|
263
263
|
})
|
|
264
|
+
args = args.map(arg => {
|
|
265
|
+
const argStr = `${arg}`
|
|
266
|
+
if (argStr.startsWith('$')) {
|
|
267
|
+
return scriptingMemory[argStr.substring(1)] || arg
|
|
268
|
+
}
|
|
269
|
+
return arg
|
|
270
|
+
})
|
|
264
271
|
str = str.replace(match, SCRIPTING_FUNCTIONS[key].handler(caps, ...args, mockMsg))
|
|
265
272
|
} else {
|
|
266
273
|
str = str.replace(match, SCRIPTING_FUNCTIONS[key].handler(caps))
|