botium-core 1.14.3 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "botium-core",
3
- "version": "1.14.3",
3
+ "version": "1.14.4",
4
4
  "description": "The Selenium for Chatbots",
5
5
  "main": "index.js",
6
6
  "module": "dist/botium-es.js",
@@ -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('|') : '')), sender, this.context, this.eol)
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 = _.map(scriptData.split(this.eol), (line) => line.trim())
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
 
@@ -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
 
@@ -130,19 +130,20 @@ module.exports = class ScriptingProvider {
130
130
  resolveUtterance: ({ utterance, resolveEmptyIfUnknown }) => {
131
131
  return this._resolveUtterance({ utterance, resolveEmptyIfUnknown })
132
132
  },
133
- assertBotResponse: (botresponse, tomatch, stepTag, meMsg, convoStepParameters) => {
133
+ assertBotResponse: (botresponse, tomatch, stepTag, meMsg, convoStepParameters = {}) => {
134
134
  if (!_.isArray(tomatch)) {
135
135
  tomatch = [tomatch]
136
136
  }
137
137
  debug(`assertBotResponse ${stepTag} ${meMsg ? `(${meMsg}) ` : ''}BOT: ${botresponse} = ${tomatch} ...`)
138
138
  const matchFn = convoStepParameters.matchingMode ? (getMatchFunction(convoStepParameters.matchingMode) || this.matchFn) : this.matchFn
139
- const found = _.find(tomatch, (utt) => matchFn(botresponse, utt, this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS]))
139
+ const found = _.find(tomatch, (utt) => matchFn(botresponse, utt, this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS], convoStepParameters))
140
140
  const asserterType = this.caps[Capabilities.SCRIPTING_MATCHING_MODE] === 'wer' ? 'Word Error Rate Asserter' : 'Text Match Asserter'
141
141
  if (_.isNil(found)) {
142
- if (this.caps[Capabilities.SCRIPTING_MATCHING_MODE] === 'wer') {
142
+ const matchingMode = convoStepParameters.matchingMode || this.caps[Capabilities.SCRIPTING_MATCHING_MODE]
143
+ if (matchingMode === 'wer') {
143
144
  const wer = calculateWer(botresponse, tomatch[0])
144
145
  const werArgs = this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS]
145
- const threshold = ([',', '.'].find(p => `${werArgs[0]}`.includes(p)) ? parseFloat(werArgs[0]) : parseInt(werArgs[0]) / 100)
146
+ const threshold = !_.isNil(convoStepParameters.matchingModeWer) ? (convoStepParameters.matchingModeWer / 100) : ([',', '.'].find(p => `${werArgs[0]}`.includes(p)) ? parseFloat(werArgs[0]) : parseInt(werArgs[0]) / 100)
146
147
  const message = `${stepTag}: Word Error Rate (${toPercent(wer)}) higher than accepted (${toPercent(threshold)})`
147
148
  throw new BotiumError(
148
149
  message,
@@ -150,7 +151,7 @@ module.exports = class ScriptingProvider {
150
151
  type: 'asserter',
151
152
  source: asserterType,
152
153
  params: {
153
- matchingMode: this.caps[Capabilities.SCRIPTING_MATCHING_MODE],
154
+ matchingMode,
154
155
  args: this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS] || null
155
156
  },
156
157
  context: {
@@ -192,19 +193,20 @@ module.exports = class ScriptingProvider {
192
193
  }
193
194
  }
194
195
  },
195
- assertBotNotResponse: (botresponse, nottomatch, stepTag, meMsg, convoStepParameters) => {
196
+ assertBotNotResponse: (botresponse, nottomatch, stepTag, meMsg, convoStepParameters = {}) => {
196
197
  if (!_.isArray(nottomatch)) {
197
198
  nottomatch = [nottomatch]
198
199
  }
199
200
  debug(`assertBotNotResponse ${stepTag} ${meMsg ? `(${meMsg}) ` : ''}BOT: ${botresponse} != ${nottomatch} ...`)
200
201
  const matchFn = convoStepParameters.matchingMode ? (getMatchFunction(convoStepParameters.matchingMode) || this.matchFn) : this.matchFn
201
- const found = _.find(nottomatch, (utt) => matchFn(botresponse, utt, this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS]))
202
+ const found = _.find(nottomatch, (utt) => matchFn(botresponse, utt, this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS], convoStepParameters))
202
203
  const asserterType = this.caps[Capabilities.SCRIPTING_MATCHING_MODE] === 'wer' ? 'Word Error Rate Asserter' : 'Text Match Asserter'
203
204
  if (!_.isNil(found)) {
204
- if (this.caps[Capabilities.SCRIPTING_MATCHING_MODE] === 'wer') {
205
+ const matchingMode = convoStepParameters.matchingMode || this.caps[Capabilities.SCRIPTING_MATCHING_MODE]
206
+ if (matchingMode === 'wer') {
205
207
  const wer = calculateWer(botresponse, nottomatch[0])
206
208
  const werArgs = this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS]
207
- const threshold = ([',', '.'].find(p => `${werArgs[0]}`.includes(p)) ? parseFloat(werArgs[0]) : parseInt(werArgs[0]) / 100)
209
+ const threshold = !_.isNil(convoStepParameters.matchingModeWer) ? (convoStepParameters.matchingModeWer / 100) : ([',', '.'].find(p => `${werArgs[0]}`.includes(p)) ? parseFloat(werArgs[0]) : parseInt(werArgs[0]) / 100)
208
210
  const message = `${stepTag}: Word Error Rate (${toPercent(wer)}) lower than accepted (${toPercent(threshold)})`
209
211
  throw new BotiumError(
210
212
  message,
@@ -212,7 +214,7 @@ module.exports = class ScriptingProvider {
212
214
  type: 'asserter',
213
215
  source: asserterType,
214
216
  params: {
215
- matchingMode: this.caps[Capabilities.SCRIPTING_MATCHING_MODE],
217
+ matchingMode,
216
218
  args: this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS] || null
217
219
  },
218
220
  context: {
@@ -4,6 +4,7 @@ const speechScorer = require('word-error-rate')
4
4
  const debug = require('debug')('botium-core-scripting-helper')
5
5
 
6
6
  const { E_SCRIPTING_MEMORY_COLUMN_MODE } = require('../Enums')
7
+ const WHITE_SPACES_EXCEPT_SPACE_CHAR_AT_THE_END = /[\n\t\r]+$/
7
8
 
8
9
  const normalizeText = (str, doCleanup) => {
9
10
  if (str && _.isArray(str)) {
@@ -247,7 +248,7 @@ const linesToConvoStep = (lines, sender, context, eol, singleLineMode = false) =
247
248
  if (eol === null) {
248
249
  throw new Error('eol cant be null')
249
250
  }
250
- convoStep.messageText = textLines.join(eol).trim()
251
+ convoStep.messageText = textLines.join(eol).replace(WHITE_SPACES_EXCEPT_SPACE_CHAR_AT_THE_END, '')
251
252
  }
252
253
  }
253
254
  } else {
@@ -473,7 +474,7 @@ const convoStepToLines = (step) => {
473
474
  lines.push(logicHook.name + _formatAppendArgs(logicHook.args))
474
475
  })
475
476
  }
476
- return lines.map(l => l.trim())
477
+ return lines
477
478
  }
478
479
 
479
480
  const linesToScriptingMemories = (lines, columnMode = null) => {
@@ -161,7 +161,7 @@ describe('compiler.compilermarkdown', function () {
161
161
  const compiler = new Compiler(context, Object.assign({}, DefaultCapabilities, caps))
162
162
 
163
163
  compiler.Compile(scriptBuffer, 'SCRIPTING_TYPE_CONVO')
164
- assert.equal(context.convos[0].conversation[1].messageText, 'hello meat bag \n!hello2')
164
+ assert.equal(context.convos[0].conversation[1].messageText, 'hello meat bag\n!hello2')
165
165
  assert.equal(context.convos[0].conversation[1].not, true)
166
166
  })
167
167
  })
@@ -191,7 +191,7 @@ describe('compiler.compilermarkdown', function () {
191
191
  compiler.Compile(scriptBuffer, 'SCRIPTING_TYPE_CONVO')
192
192
  assert.fail('expected error')
193
193
  } catch (err) {
194
- assert.equal(err.message, 'Failed to parse conversation. All element in convo step has to be optional or not optional: ["?hello meat bag ","BUTTONS checkbutton|checkbutton2 "]')
194
+ assert.equal(err.message, 'Failed to parse conversation. All element in convo step has to be optional or not optional: ["?hello meat bag","BUTTONS checkbutton|checkbutton2"]')
195
195
  }
196
196
  })
197
197
  it('should read ?? as ?', async function () {
@@ -225,7 +225,7 @@ describe('compiler.compilermarkdown', function () {
225
225
  const compiler = new Compiler(context, Object.assign({}, DefaultCapabilities, caps))
226
226
 
227
227
  compiler.Compile(scriptBuffer, 'SCRIPTING_TYPE_CONVO')
228
- assert.equal(context.convos[0].conversation[1].messageText, 'hello meat bag \n?hello2')
228
+ assert.equal(context.convos[0].conversation[1].messageText, 'hello meat bag\n?hello2')
229
229
  assert.equal(context.convos[0].conversation[1].optional, true)
230
230
  })
231
231
  })
@@ -236,7 +236,7 @@ describe('compiler.compilertxt', function () {
236
236
  assert.equal(convo.conversation.length, 2)
237
237
  assert.equal(convo.conversation[0].messageText, '')
238
238
  assert.equal(convo.conversation[0].logicHooks.length, 0)
239
- assert.equal(convo.conversation[1].messageText, 'Hi')
239
+ assert.equal(convo.conversation[1].messageText, 'Hi ')
240
240
  })
241
241
  it('should read nothing if there is nothing (even no separator)', async function () {
242
242
  const scriptBuffer = fs.readFileSync(path.resolve(__dirname, CONVOS_DIR, 'convos_emptyrow_no_separator_row.convo.txt'))
@@ -64,6 +64,17 @@ describe('scripting.asserters.convoStepParametersForAssert', function () {
64
64
  assert.isTrue(err.message.indexOf('"You said Hello" expected to match "Hello"') >= 0)
65
65
  }
66
66
  })
67
+ it('should not accept bad chatbot response on exact match defined on step even with WER asserter', async function () {
68
+ this.compiler.ReadScript(path.resolve(__dirname, 'convos'), 'convo_step_parameter_matchmode_failed_wer.convo.txt')
69
+ assert.equal(this.compiler.convos.length, 1)
70
+
71
+ try {
72
+ await this.compiler.convos[0].Run(this.container)
73
+ assert.fail('should have failed')
74
+ } catch (err) {
75
+ assert.isTrue(err.message.indexOf('Word Error Rate (50%) higher than accepted (25%)') >= 0)
76
+ }
77
+ })
67
78
  })
68
79
 
69
80
  describe('scripting.asserters.convoStepParametersForAssert.retry', function () {
@@ -0,0 +1,9 @@
1
+ convo_step_parameter_matchmode_failed_wer
2
+
3
+ #me
4
+ Hello1
5
+
6
+ #bot
7
+ You said Hello1 Hello2 Hello3 Hello4
8
+ CONVO_STEP_PARAMETERS {"matchingMode":"wer","matchingModeWer":25}
9
+
@@ -7,7 +7,7 @@ this is a variable: VARVALUE1
7
7
  this is a variable: $myvar
8
8
 
9
9
  #me
10
- this is a variable: VARVALUE2
10
+ this is a variable: VARVALUE2
11
11
 
12
12
  #bot
13
- this is a variable: $myvar
13
+ this is a variable: $myvar