botium-core 1.11.14 → 1.12.1
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 +853 -579
- package/dist/botium-cjs.js.map +1 -1
- package/dist/botium-es.js +471 -198
- package/dist/botium-es.js.map +1 -1
- package/index.js +1 -0
- package/package.json +29 -29
- package/samples/connectors/custom/botium-connector-myapi.js +3 -3
- package/samples/extensions/asserterHooks/DummyAsserter.js +3 -3
- package/src/Capabilities.js +4 -1
- package/src/Defaults.js +1 -0
- package/src/Enums.js +6 -0
- package/src/containers/plugins/SimpleRestContainer.js +32 -1
- package/src/scripting/BotiumError.js +21 -0
- package/src/scripting/CompilerCsv.js +1 -1
- package/src/scripting/CompilerObjectBase.js +4 -14
- package/src/scripting/CompilerTxt.js +4 -15
- package/src/scripting/CompilerXlsx.js +81 -25
- package/src/scripting/Convo.js +16 -4
- package/src/scripting/MatchFunctions.js +21 -0
- package/src/scripting/ScriptingProvider.js +55 -40
- package/src/scripting/helper.js +57 -4
- package/src/scripting/logichook/LogicHookConsts.js +4 -0
- package/src/scripting/logichook/LogicHookUtils.js +2 -0
- package/src/scripting/logichook/asserter/JsonPathAsserter.js +1 -1
- package/src/scripting/logichook/asserter/TextWildcardExactAllAsserter.js +8 -0
- package/src/scripting/logichook/asserter/TextWildcardExactAllICAsserter.js +8 -0
- package/src/scripting/logichook/asserter/TextWildcardExactAnyAsserter.js +8 -0
- package/src/scripting/logichook/asserter/TextWildcardExactAnyICAsserter.js +8 -0
- package/src/scripting/logichook/logichooks/ClearQueueLogicHook.js +1 -1
- package/src/scripting/logichook/userinput/MediaInput.js +14 -2
- package/test/connectors/convos/hello.convo.txt +6 -0
- package/test/connectors/simplerest.spec.js +42 -2
- package/test/convo/convos/continuefailing.convo.txt +19 -0
- package/test/convo/transcript.spec.js +34 -0
- package/test/scripting/asserters/convos/text_wildcardexact_all_nok.yml +7 -0
- package/test/scripting/asserters/convos/text_wildcardexact_all_ok.yml +7 -0
- package/test/scripting/asserters/convos/text_wildcardexact_any_nok.yml +7 -0
- package/test/scripting/asserters/convos/text_wildcardexact_any_ok.yml +7 -0
- package/test/scripting/asserters/textWildcardExactAllAsserter.spec.js +51 -0
- package/test/scripting/asserters/textWildcardExactAnyAsserter.spec.js +51 -0
- package/test/scripting/matching/matchingmode.spec.js +43 -0
- package/test/scripting/scriptingProvider.spec.js +4 -4
- package/test/scripting/scriptingmemory/convosMultiMemorySameCols/buy.convo.txt +6 -0
- package/test/scripting/scriptingmemory/convosMultiMemorySameCols/products1.scriptingmemory.txt +2 -0
- package/test/scripting/scriptingmemory/convosMultiMemorySameCols/products2.scriptingmemory.txt +2 -0
- package/test/scripting/scriptingmemory/convosSimpleCols/buy.convo.txt +8 -0
- package/test/scripting/scriptingmemory/convosSimpleCols/product.scriptingmemory.txt +3 -0
- package/test/scripting/scriptingmemory/convosTwoTablesCols/buy.convo.txt +6 -0
- package/test/scripting/scriptingmemory/convosTwoTablesCols/customer.xlsx +0 -0
- package/test/scripting/scriptingmemory/convosTwoTablesCols/product.xlsx +0 -0
- package/test/scripting/scriptingmemory/fillScriptingMemoryFromFile.spec.js +45 -0
- package/test/scripting/userinputs/mediaInputConvos.spec.js +53 -2
|
@@ -464,11 +464,33 @@ module.exports = class ScriptingProvider {
|
|
|
464
464
|
return { convos: dirConvos, utterances: dirUtterances, pconvos: dirPartialConvos, scriptingMemories: dirScriptingMemories }
|
|
465
465
|
}
|
|
466
466
|
|
|
467
|
+
ReadScriptFromBuffer (scriptBuffer, scriptingFormat, scriptingTypes = null) {
|
|
468
|
+
if (_.isString(scriptingTypes)) scriptingTypes = [scriptingTypes]
|
|
469
|
+
if (_.isArray(scriptingTypes) && scriptingTypes.length === 0) scriptingTypes = null
|
|
470
|
+
|
|
471
|
+
const result = {
|
|
472
|
+
convos: [],
|
|
473
|
+
utterances: [],
|
|
474
|
+
pconvos: [],
|
|
475
|
+
scriptingMemories: []
|
|
476
|
+
}
|
|
477
|
+
if (!scriptingTypes || scriptingTypes.includes(Constants.SCRIPTING_TYPE_UTTERANCES)) {
|
|
478
|
+
result.utterances = this.Compile(scriptBuffer, scriptingFormat, Constants.SCRIPTING_TYPE_UTTERANCES)
|
|
479
|
+
}
|
|
480
|
+
if (!scriptingTypes || scriptingTypes.includes(Constants.SCRIPTING_TYPE_PCONVO)) {
|
|
481
|
+
result.pconvos = this.Compile(scriptBuffer, scriptingFormat, Constants.SCRIPTING_TYPE_PCONVO)
|
|
482
|
+
}
|
|
483
|
+
if (!scriptingTypes || scriptingTypes.includes(Constants.SCRIPTING_TYPE_CONVO)) {
|
|
484
|
+
result.convos = this.Compile(scriptBuffer, scriptingFormat, Constants.SCRIPTING_TYPE_CONVO)
|
|
485
|
+
}
|
|
486
|
+
if (!scriptingTypes || scriptingTypes.includes(Constants.SCRIPTING_TYPE_SCRIPTING_MEMORY)) {
|
|
487
|
+
result.scriptingMemories = this.Compile(scriptBuffer, scriptingFormat, Constants.SCRIPTING_TYPE_SCRIPTING_MEMORY)
|
|
488
|
+
}
|
|
489
|
+
return result
|
|
490
|
+
}
|
|
491
|
+
|
|
467
492
|
ReadScript (convoDir, filename) {
|
|
468
|
-
let
|
|
469
|
-
let fileUtterances = []
|
|
470
|
-
let filePartialConvos = []
|
|
471
|
-
let fileScriptingMemories = []
|
|
493
|
+
let result = {}
|
|
472
494
|
|
|
473
495
|
try {
|
|
474
496
|
let scriptBuffer = fs.readFileSync(path.resolve(convoDir, filename))
|
|
@@ -487,36 +509,25 @@ module.exports = class ScriptingProvider {
|
|
|
487
509
|
}
|
|
488
510
|
|
|
489
511
|
if (filename.endsWith('.xlsx') || filename.endsWith('.xlsm')) {
|
|
490
|
-
|
|
491
|
-
filePartialConvos = this.Compile(scriptBuffer, Constants.SCRIPTING_FORMAT_XSLX, Constants.SCRIPTING_TYPE_PCONVO)
|
|
492
|
-
fileConvos = this.Compile(scriptBuffer, Constants.SCRIPTING_FORMAT_XSLX, Constants.SCRIPTING_TYPE_CONVO)
|
|
493
|
-
fileScriptingMemories = this.Compile(scriptBuffer, Constants.SCRIPTING_FORMAT_XSLX, Constants.SCRIPTING_TYPE_SCRIPTING_MEMORY)
|
|
512
|
+
result = this.ReadScriptFromBuffer(scriptBuffer, Constants.SCRIPTING_FORMAT_XSLX, [Constants.SCRIPTING_TYPE_UTTERANCES, Constants.SCRIPTING_TYPE_PCONVO, Constants.SCRIPTING_TYPE_CONVO, Constants.SCRIPTING_TYPE_SCRIPTING_MEMORY])
|
|
494
513
|
} else if (filename.endsWith('.convo.txt')) {
|
|
495
|
-
|
|
514
|
+
result = this.ReadScriptFromBuffer(scriptBuffer, Constants.SCRIPTING_FORMAT_TXT, Constants.SCRIPTING_TYPE_CONVO)
|
|
496
515
|
} else if (filename.endsWith('.pconvo.txt')) {
|
|
497
|
-
|
|
516
|
+
result = this.ReadScriptFromBuffer(scriptBuffer, Constants.SCRIPTING_FORMAT_TXT, Constants.SCRIPTING_TYPE_PCONVO)
|
|
498
517
|
} else if (filename.endsWith('.utterances.txt')) {
|
|
499
|
-
|
|
518
|
+
result = this.ReadScriptFromBuffer(scriptBuffer, Constants.SCRIPTING_FORMAT_TXT, Constants.SCRIPTING_TYPE_UTTERANCES)
|
|
500
519
|
} else if (filename.endsWith('.scriptingmemory.txt')) {
|
|
501
|
-
|
|
520
|
+
result = this.ReadScriptFromBuffer(scriptBuffer, Constants.SCRIPTING_FORMAT_TXT, Constants.SCRIPTING_TYPE_SCRIPTING_MEMORY)
|
|
502
521
|
} else if (filename.endsWith('.convo.csv')) {
|
|
503
|
-
|
|
522
|
+
result = this.ReadScriptFromBuffer(scriptBuffer, Constants.SCRIPTING_FORMAT_CSV, Constants.SCRIPTING_TYPE_CONVO)
|
|
504
523
|
} else if (filename.endsWith('.pconvo.csv')) {
|
|
505
|
-
|
|
524
|
+
result = this.ReadScriptFromBuffer(scriptBuffer, Constants.SCRIPTING_FORMAT_CSV, Constants.SCRIPTING_TYPE_PCONVO)
|
|
506
525
|
} else if (filename.endsWith('.yaml') || filename.endsWith('.yml')) {
|
|
507
|
-
|
|
508
|
-
filePartialConvos = this.Compile(scriptBuffer, Constants.SCRIPTING_FORMAT_YAML, Constants.SCRIPTING_TYPE_PCONVO)
|
|
509
|
-
fileConvos = this.Compile(scriptBuffer, Constants.SCRIPTING_FORMAT_YAML, Constants.SCRIPTING_TYPE_CONVO)
|
|
510
|
-
fileScriptingMemories = this.Compile(scriptBuffer, Constants.SCRIPTING_FORMAT_YAML, Constants.SCRIPTING_TYPE_SCRIPTING_MEMORY)
|
|
526
|
+
result = this.ReadScriptFromBuffer(scriptBuffer, Constants.SCRIPTING_FORMAT_YAML, [Constants.SCRIPTING_TYPE_UTTERANCES, Constants.SCRIPTING_TYPE_PCONVO, Constants.SCRIPTING_TYPE_CONVO, Constants.SCRIPTING_TYPE_SCRIPTING_MEMORY])
|
|
511
527
|
} else if (filename.endsWith('.json')) {
|
|
512
|
-
|
|
513
|
-
filePartialConvos = this.Compile(scriptBuffer, Constants.SCRIPTING_FORMAT_JSON, Constants.SCRIPTING_TYPE_PCONVO)
|
|
514
|
-
fileConvos = this.Compile(scriptBuffer, Constants.SCRIPTING_FORMAT_JSON, Constants.SCRIPTING_TYPE_CONVO)
|
|
515
|
-
fileScriptingMemories = this.Compile(scriptBuffer, Constants.SCRIPTING_FORMAT_JSON, Constants.SCRIPTING_TYPE_SCRIPTING_MEMORY)
|
|
528
|
+
result = this.ReadScriptFromBuffer(scriptBuffer, Constants.SCRIPTING_FORMAT_JSON, [Constants.SCRIPTING_TYPE_UTTERANCES, Constants.SCRIPTING_TYPE_PCONVO, Constants.SCRIPTING_TYPE_CONVO, Constants.SCRIPTING_TYPE_SCRIPTING_MEMORY])
|
|
516
529
|
} else if (filename.endsWith('.markdown') || filename.endsWith('.md')) {
|
|
517
|
-
|
|
518
|
-
fileConvos = this.Compile(scriptBuffer, Constants.SCRIPTING_FORMAT_MARKDOWN, Constants.SCRIPTING_TYPE_CONVO)
|
|
519
|
-
filePartialConvos = this.Compile(scriptBuffer, Constants.SCRIPTING_FORMAT_MARKDOWN, Constants.SCRIPTING_TYPE_PCONVO)
|
|
530
|
+
result = this.ReadScriptFromBuffer(scriptBuffer, Constants.SCRIPTING_FORMAT_MARKDOWN, [Constants.SCRIPTING_TYPE_UTTERANCES, Constants.SCRIPTING_TYPE_PCONVO, Constants.SCRIPTING_TYPE_CONVO])
|
|
520
531
|
} else {
|
|
521
532
|
debug(`ReadScript - dropped file: ${filename}, filename not supported`)
|
|
522
533
|
}
|
|
@@ -526,42 +537,40 @@ module.exports = class ScriptingProvider {
|
|
|
526
537
|
}
|
|
527
538
|
|
|
528
539
|
// Compilers saved the convos, and we alter here the saved version too
|
|
529
|
-
if (
|
|
530
|
-
|
|
540
|
+
if (result.convos && result.convos.length > 0) {
|
|
541
|
+
result.convos.forEach((fileConvo) => {
|
|
531
542
|
fileConvo.sourceTag = { convoDir, filename }
|
|
532
543
|
if (!fileConvo.header.name) {
|
|
533
544
|
fileConvo.header.name = filename
|
|
534
545
|
}
|
|
535
546
|
})
|
|
536
547
|
const isSkip = (c) => c.header.name && skipPattern.test(c.header.name.toLowerCase())
|
|
537
|
-
|
|
538
|
-
|
|
548
|
+
result.convos.filter(c => isSkip(c)).forEach(c => debug(`ReadScript - skipping convo '${c.header.name}'`))
|
|
549
|
+
result.convos = result.convos.filter(c => !isSkip(c))
|
|
539
550
|
}
|
|
540
|
-
if (
|
|
541
|
-
|
|
551
|
+
if (result.pconvos && result.pconvos.length > 0) {
|
|
552
|
+
result.pconvos.forEach((filePartialConvo) => {
|
|
542
553
|
filePartialConvo.sourceTag = { convoDir, filename }
|
|
543
554
|
if (!filePartialConvo.header.name) {
|
|
544
555
|
filePartialConvo.header.name = filename
|
|
545
556
|
}
|
|
546
557
|
})
|
|
547
558
|
}
|
|
548
|
-
if (
|
|
549
|
-
|
|
559
|
+
if (result.scriptingMemories && result.scriptingMemories.length > 0) {
|
|
560
|
+
result.scriptingMemories.forEach((scriptingMemory) => {
|
|
550
561
|
scriptingMemory.sourceTag = { filename }
|
|
551
562
|
})
|
|
552
563
|
}
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
this.fileUtterances = this._tagAndCleanupUtterances(fileUtterances, convoDir, filename)
|
|
564
|
+
if (result.utterances) {
|
|
565
|
+
result.utterances = this._tagAndCleanupUtterances(result.utterances, convoDir, filename)
|
|
556
566
|
}
|
|
557
|
-
return { convos:
|
|
567
|
+
return { convos: result.convos || [], utterances: result.utterances || [], pconvos: result.pconvos || [], scriptingMemories: result.scriptingMemories || [] }
|
|
558
568
|
}
|
|
559
569
|
|
|
560
570
|
_tagAndCleanupUtterances (utteranceFiles, convoDir, filename) {
|
|
561
571
|
return utteranceFiles.map((fileUtt) => {
|
|
562
572
|
fileUtt.sourceTag = { convoDir, filename }
|
|
563
|
-
fileUtt.utterances = fileUtt.utterances
|
|
564
|
-
.filter(u => u)
|
|
573
|
+
fileUtt.utterances = fileUtt.utterances.filter(u => u)
|
|
565
574
|
return fileUtt
|
|
566
575
|
})
|
|
567
576
|
}
|
|
@@ -752,6 +761,7 @@ module.exports = class ScriptingProvider {
|
|
|
752
761
|
|
|
753
762
|
debug(`ExpandScriptingMemoryToConvos - ${convosExpandedAll.length} convo expanded, added to convos (${this.convos.length}). Result ${convosExpandedAll.length + this.convos.length} convo`)
|
|
754
763
|
this.convos = this.convos.concat(convosExpandedAll)
|
|
764
|
+
this._sortConvos()
|
|
755
765
|
}
|
|
756
766
|
|
|
757
767
|
ExpandUtterancesToConvos ({ useNameAsIntent, incomprehensionUtt } = {}) {
|
|
@@ -787,6 +797,11 @@ module.exports = class ScriptingProvider {
|
|
|
787
797
|
conversation: [
|
|
788
798
|
{
|
|
789
799
|
sender: 'me',
|
|
800
|
+
logicHooks: [
|
|
801
|
+
{
|
|
802
|
+
name: 'SKIP_BOT_UNCONSUMED'
|
|
803
|
+
}
|
|
804
|
+
],
|
|
790
805
|
messageText: utt.name,
|
|
791
806
|
stepTag: 'Step 1 - tell utterance'
|
|
792
807
|
},
|
|
@@ -944,7 +959,7 @@ module.exports = class ScriptingProvider {
|
|
|
944
959
|
}
|
|
945
960
|
}
|
|
946
961
|
} else {
|
|
947
|
-
expandedConvos.push(Object.assign(_.cloneDeep(currentConvo), { conversation: convoStepsStack }))
|
|
962
|
+
expandedConvos.push(Object.assign(_.cloneDeep(currentConvo), { conversation: _.cloneDeep(convoStepsStack) }))
|
|
948
963
|
}
|
|
949
964
|
}
|
|
950
965
|
|
package/src/scripting/helper.js
CHANGED
|
@@ -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(' ')
|
|
@@ -72,7 +74,7 @@ const toString = (value) => {
|
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
const flatString = (str) => {
|
|
75
|
-
return str.split('\n').map(s => s.trim()).join(' ')
|
|
77
|
+
return str ? str.split('\n').map(s => s.trim()).join(' ') : ''
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
const linesToConvoStep = (lines, sender, context, eol, singleLineMode = false) => {
|
|
@@ -439,7 +441,7 @@ const convoStepToLines = (step) => {
|
|
|
439
441
|
if (step.messageText) {
|
|
440
442
|
lines.push((step.optional ? '?' : '') + (step.not ? '!' : '') + step.messageText)
|
|
441
443
|
}
|
|
442
|
-
if (step.buttons && step.buttons.length > 0) lines.push('BUTTONS ' + step.buttons.map(b => flatString(b.text)).join('|'))
|
|
444
|
+
if (step.buttons && step.buttons.length > 0) lines.push('BUTTONS ' + step.buttons.filter(b => b.text).map(b => flatString(b.text)).join('|'))
|
|
443
445
|
if (step.media && step.media.length > 0) lines.push('MEDIA ' + step.media.filter(m => !m.buffer && m.mediaUri).map(m => m.mediaUri).join('|'))
|
|
444
446
|
if (step.cards && step.cards.length > 0) {
|
|
445
447
|
step.cards.forEach(c => {
|
|
@@ -449,7 +451,7 @@ const convoStepToLines = (step) => {
|
|
|
449
451
|
if (c.content) cardTexts = cardTexts.concat(_.isArray(c.content) ? c.content : [c.content])
|
|
450
452
|
if (cardTexts.length > 0) lines.push('CARDS ' + cardTexts.map(c => flatString(c)).join('|'))
|
|
451
453
|
|
|
452
|
-
if (c.buttons && c.buttons.length > 0) lines.push('BUTTONS ' + c.buttons.map(b => flatString(b.text)).join('|'))
|
|
454
|
+
if (c.buttons && c.buttons.length > 0) lines.push('BUTTONS ' + c.buttons.filter(b => b.text).map(b => flatString(b.text)).join('|'))
|
|
453
455
|
if (c.image && !c.image.buffer && c.image.mediaUri) lines.push('MEDIA ' + c.image.mediaUri)
|
|
454
456
|
})
|
|
455
457
|
}
|
|
@@ -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
|
}
|
|
@@ -33,6 +33,10 @@ module.exports = {
|
|
|
33
33
|
{ name: 'TEXT_WILDCARD_ANY_IC', className: 'TextWildcardAnyICAsserter' },
|
|
34
34
|
{ name: 'TEXT_WILDCARD_ALL', className: 'TextWildcardAllAsserter' },
|
|
35
35
|
{ name: 'TEXT_WILDCARD_ALL_IC', className: 'TextWildcardAllICAsserter' },
|
|
36
|
+
{ name: 'TEXT_WILDCARDEXACT_ANY', className: 'TextWildcardExactAnyAsserter' },
|
|
37
|
+
{ name: 'TEXT_WILDCARDEXACT_ANY_IC', className: 'TextWildcardExactAnyICAsserter' },
|
|
38
|
+
{ name: 'TEXT_WILDCARDEXACT_ALL', className: 'TextWildcardExactAllAsserter' },
|
|
39
|
+
{ name: 'TEXT_WILDCARDEXACT_ALL_IC', className: 'TextWildcardExactAllICAsserter' },
|
|
36
40
|
{ name: 'TEXT_REGEXP_ANY', className: 'TextRegexpAnyAsserter' },
|
|
37
41
|
{ name: 'TEXT_REGEXP_ANY_IC', className: 'TextRegexpAnyICAsserter' },
|
|
38
42
|
{ name: 'TEXT_REGEXP_ALL', className: 'TextRegexpAllAsserter' },
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
const BaseTextAsserter = require('./BaseTextAsserter')
|
|
2
|
+
const MatchFunctions = require('../../MatchFunctions')
|
|
3
|
+
|
|
4
|
+
module.exports = class TextWildcardExactAnyAsserter extends BaseTextAsserter {
|
|
5
|
+
constructor (context, caps = {}) {
|
|
6
|
+
super(context, caps, MatchFunctions.wildcardExact(false), 'all')
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
const BaseTextAsserter = require('./BaseTextAsserter')
|
|
2
|
+
const MatchFunctions = require('../../MatchFunctions')
|
|
3
|
+
|
|
4
|
+
module.exports = class TextWildcardExactAnyAsserter extends BaseTextAsserter {
|
|
5
|
+
constructor (context, caps = {}) {
|
|
6
|
+
super(context, caps, MatchFunctions.wildcardExact(true), 'all')
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
const BaseTextAsserter = require('./BaseTextAsserter')
|
|
2
|
+
const MatchFunctions = require('../../MatchFunctions')
|
|
3
|
+
|
|
4
|
+
module.exports = class TextWildcardExactAnyAsserter extends BaseTextAsserter {
|
|
5
|
+
constructor (context, caps = {}) {
|
|
6
|
+
super(context, caps, MatchFunctions.wildcardExact(false), 'any')
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
const BaseTextAsserter = require('./BaseTextAsserter')
|
|
2
|
+
const MatchFunctions = require('../../MatchFunctions')
|
|
3
|
+
|
|
4
|
+
module.exports = class TextWildcardExactAnyAsserter extends BaseTextAsserter {
|
|
5
|
+
constructor (context, caps = {}) {
|
|
6
|
+
super(context, caps, MatchFunctions.wildcardExact(true), 'any')
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -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:
|
|
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:
|
|
209
|
+
mimeType: this._mime(args[0]),
|
|
198
210
|
buffer: args[1]
|
|
199
211
|
}))
|
|
200
212
|
}
|
|
@@ -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)
|
|
@@ -789,6 +815,7 @@ describe('connectors.simplerest.useresponse', function () {
|
|
|
789
815
|
.get('/startencoded').reply(200, { text: JSON.stringify({ prop: 'response from start' }) })
|
|
790
816
|
.get('/startstring').reply(200, 'response from start')
|
|
791
817
|
.get('/msg').reply(200, { text: 'response from msg' })
|
|
818
|
+
.get('/msgfail').reply(400, { error: 'failure text' })
|
|
792
819
|
.persist()
|
|
793
820
|
|
|
794
821
|
const myCaps = Object.assign({
|
|
@@ -914,6 +941,19 @@ describe('connectors.simplerest.useresponse', function () {
|
|
|
914
941
|
assert.equal(transcript.steps.length, 1)
|
|
915
942
|
assert.equal(transcript.steps[0].actual.messageText, 'response from start')
|
|
916
943
|
})
|
|
944
|
+
|
|
945
|
+
it('should use error body content', async function () {
|
|
946
|
+
await this.init({
|
|
947
|
+
[Capabilities.SIMPLEREST_URL]: 'https://mock.com/msgfail'
|
|
948
|
+
})
|
|
949
|
+
|
|
950
|
+
this.compiler.ReadScript(path.resolve(__dirname, 'convos'), 'hello.convo.txt')
|
|
951
|
+
try {
|
|
952
|
+
await this.compiler.convos[0].Run(this.container)
|
|
953
|
+
} catch (err) {
|
|
954
|
+
assert.isTrue(err.message.indexOf('failure text') >= 0)
|
|
955
|
+
}
|
|
956
|
+
})
|
|
917
957
|
})
|
|
918
958
|
|
|
919
959
|
describe('connectors.simplerest.inbound', function () {
|
|
@@ -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
|
})
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const assert = require('chai').assert
|
|
2
|
+
const path = require('path')
|
|
3
|
+
|
|
4
|
+
const BotDriver = require('../../..').BotDriver
|
|
5
|
+
const Capabilities = require('../../..').Capabilities
|
|
6
|
+
|
|
7
|
+
const echoConnector = ({ queueBotSays }) => {
|
|
8
|
+
return {
|
|
9
|
+
UserSays (msg) {
|
|
10
|
+
const botMsg = { sender: 'bot', sourceData: msg.sourceData, messageText: msg.messageText }
|
|
11
|
+
queueBotSays(botMsg)
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
describe('scripting.asserters.textWildcardExactAllAsserter', function () {
|
|
17
|
+
beforeEach(async function () {
|
|
18
|
+
const myCaps = {
|
|
19
|
+
[Capabilities.PROJECTNAME]: 'scripting.asserters.textWildcardExactAllAsserter',
|
|
20
|
+
[Capabilities.CONTAINERMODE]: echoConnector
|
|
21
|
+
}
|
|
22
|
+
const driver = new BotDriver(myCaps)
|
|
23
|
+
this.compiler = driver.BuildCompiler()
|
|
24
|
+
this.container = await driver.Build()
|
|
25
|
+
})
|
|
26
|
+
afterEach(async function () {
|
|
27
|
+
this.container && await this.container.Clean()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('ok', async function () {
|
|
31
|
+
this.compiler.ReadScriptsFromDirectory(path.resolve(__dirname, 'convos', 'text_wildcardexact_all_ok.yml'))
|
|
32
|
+
|
|
33
|
+
this.compiler.ExpandScriptingMemoryToConvos()
|
|
34
|
+
assert.equal(this.compiler.convos.length, 1)
|
|
35
|
+
await this.compiler.convos[0].Run(this.container)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('nok', async function () {
|
|
39
|
+
this.compiler.ReadScriptsFromDirectory(path.resolve(__dirname, 'convos', 'text_wildcardexact_all_nok.yml'))
|
|
40
|
+
|
|
41
|
+
this.compiler.ExpandScriptingMemoryToConvos()
|
|
42
|
+
assert.equal(this.compiler.convos.length, 1)
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
await this.compiler.convos[0].Run(this.container)
|
|
46
|
+
assert.fail('expected error')
|
|
47
|
+
} catch (err) {
|
|
48
|
+
assert.equal(err.message, 'text_wildcardexact_all_nok/Line 2: assertion error - Line 2: Expected text(s) in response "Im Joe, my number is 12345, and my ID is id2_*3"')
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
})
|