Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "botium-core",
"version": "1.15.15",
"version": "1.15.16",
"description": "The Selenium for Chatbots",
"main": "index.js",
"module": "dist/botium-es.js",
Expand Down
53 changes: 53 additions & 0 deletions src/scripting/ScriptingMemory.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,56 @@ const SCRIPTING_FUNCTIONS = _.mapValues(SCRIPTING_FUNCTIONS_RAW, (funcOrStruct,
})
const RESERVED_WORDS = Object.keys(SCRIPTING_FUNCTIONS)

// #region helper functions
const _convoMemoryPrefix = (convoName) => `$Convo__${convoName}__`

const _shortNameFromConvoMemory = (convoName, varName) => {
if (!convoName || !_.isString(varName)) {
return null
}
const prefix = _convoMemoryPrefix(convoName)
if (!varName.startsWith(prefix)) {
return null
}
const short = varName.slice(prefix.length)
if (!short) {
return null
}
return `$${short}`
}

const convoMemoryAliases = (convoName, varNames) => {
const names = varNames || []
if (!convoName) {
return [...names]
}
const aliased = []
for (const name of names) {
aliased.push(name)
const short = _shortNameFromConvoMemory(convoName, name)
if (short) {
aliased.push(short)
}
}
return _.uniq(aliased)
}

const aliasConvoMemory = (convoName, values) => {
const result = Object.assign({}, values || {})
if (!convoName || !values) {
return result
}
for (const [key, value] of Object.entries(values)) {
const short = _shortNameFromConvoMemory(convoName, key)
if (short) {
result[short] = value
}
}
return result
}
// #endregion

// #region main functions
const apply = (container, scriptingMemory, str, mockMsg) => {
if (container.caps[Capabilities.SCRIPTING_ENABLE_MEMORY]) {
str = _apply(scriptingMemory, str, container.caps, mockMsg)
Expand Down Expand Up @@ -328,12 +378,15 @@ const fill = (container, scriptingMemory, result, utterance, scriptingEvents) =>
debug(`fill end: ${util.inspect(scriptingMemory)}`)
}
}
// #endregion

module.exports = {
apply,
applyToArgs,
fill,
extractVarNames,
convoMemoryAliases,
aliasConvoMemory,
RESERVED_WORDS,
SCRIPTING_FUNCTIONS
}
18 changes: 14 additions & 4 deletions src/scripting/ScriptingProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -876,21 +876,31 @@ module.exports = class ScriptingProvider {
const multipliers = []
for (const [key, scriptingMemories] of variablesToScriptingMemory.entries()) {
const variableNames = JSON.parse(key)
if (_.intersection(variableNames, convoVariables).length) {
const variableNamesForMatch = ScriptingMemory.convoMemoryAliases(convo.header.name, variableNames)
if (_.intersection(variableNamesForMatch, convoVariables).length) {
const convosExpandedVariable = []
multipliers.push(scriptingMemories.length)
scriptingMemories.forEach((scriptingMemory) => {
// Appending the case name to name
for (const convoToExpand of convosToExpand) {
const convoExpanded = _.cloneDeep(convoToExpand)
convoExpanded.header.name = convoToExpand.header.name + '.' + scriptingMemory.header.name
variableNames.forEach((name) => {
const value = scriptingMemory.values[name]
const appendMemoryHook = (name, value) => {
if (value) {
convoExpanded.beginLogicHook.push({ name: 'SET_SCRIPTING_MEMORY', args: [name.substring(1), scriptingMemory.values[name]] })
convoExpanded.beginLogicHook.push({ name: 'SET_SCRIPTING_MEMORY', args: [name.substring(1), value] })
} else {
convoExpanded.beginLogicHook.push({ name: 'CLEAR_SCRIPTING_MEMORY', args: [name.substring(1)] })
}
}
variableNames.forEach((name) => {
appendMemoryHook(name, scriptingMemory.values[name])
})
// scoped $Convo__<thisConvo>__* aliases after originals so they win over a global of the same short name
variableNames.forEach((name) => {
const shortNames = ScriptingMemory.convoMemoryAliases(convo.header.name, [name]).filter(n => n !== name)
shortNames.forEach((shortName) => {
appendMemoryHook(shortName, scriptingMemory.values[name])
})
})
convosExpandedVariable.push(convoExpanded)
}
Expand Down
108 changes: 108 additions & 0 deletions test/scripting/scriptingmemory/convoMemory.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
const path = require('path')
const assert = require('chai').assert
const BotDriver = require('../../..').BotDriver
const Capabilities = require('../../..').Capabilities
const ScriptingMemory = require('../../../src/scripting/ScriptingMemory')

// #region helper functions
const echoConnector = ({ queueBotSays }) => {
return {
UserSays (msg) {
queueBotSays({
sender: 'bot',
sourceData: msg.sourceData || { request: msg.messageText },
messageText: msg.messageText
})
}
}
}

const convoByName = (compiler, name) => compiler.convos.find(c => c.header.name === name)

const meTexts = (transcript) => transcript.steps
.filter(s => s.actual && s.actual.sender !== 'bot')
.map(s => s.actual.messageText)
.filter(t => t)
// #endregion

describe('scripting.scriptingmemory.convoMemory', function () {
describe('alias helpers', function () {
it('aliasConvoMemory copies scoped keys to short names for the current convo', function () {
const values = {
$Convo__Checkout__orderId: '342',
$Convo__Checkout__username: 'Joe',
$Convo__Payment__username: 'Jane',
$token: 'secret'
}
const aliased = ScriptingMemory.aliasConvoMemory('Checkout', values)
assert.equal(aliased.$orderId, '342')
assert.equal(aliased.$username, 'Joe')
assert.equal(aliased.$Convo__Checkout__orderId, '342')
assert.equal(aliased.$Convo__Payment__username, 'Jane')
assert.isUndefined(aliased.$pw)
assert.equal(aliased.$token, 'secret')
assert.equal(values.$orderId, undefined)
})

it('aliasConvoMemory lets scoped names win over a global of the same short name', function () {
const aliased = ScriptingMemory.aliasConvoMemory('Checkout', {
$orderId: 'global',
$Convo__Checkout__orderId: '342'
})
assert.equal(aliased.$orderId, '342')
})

it('convoMemoryAliases adds short names for the current convo prefix only', function () {
const aliased = ScriptingMemory.convoMemoryAliases('Checkout', [
'$Convo__Checkout__orderId',
'$Convo__Payment__username',
'$newPWD'
])
assert.deepEqual(aliased, [
'$Convo__Checkout__orderId',
'$orderId',
'$Convo__Payment__username',
'$newPWD'
])
})
})

describe('expand and run', function () {
beforeEach(async function () {
const driver = new BotDriver({
[Capabilities.PROJECTNAME]: 'scripting.scriptingmemory.convoMemory',
[Capabilities.CONTAINERMODE]: echoConnector,
[Capabilities.SCRIPTING_ENABLE_MEMORY]: true
})
this.compiler = driver.BuildCompiler()
this.container = await driver.Build()
})

afterEach(async function () {
this.container && await this.container.Clean()
})

it('expands Checkout and ChangePassword with case suffix and applies scoped values', async function () {
this.compiler.ReadScriptsFromDirectory(path.resolve(__dirname, 'convosConvoMemory'))
this.compiler.ExpandScriptingMemoryToConvos()
this.compiler.ExpandConvos()

assert.sameMembers(this.compiler.convos.map(c => c.header.name), ['Checkout.One', 'ChangePassword.One'])

await this.container.Start()
const checkout = await convoByName(this.compiler, 'Checkout.One').Run(this.container)
await this.container.Stop()
assert.deepEqual(meTexts(checkout), ['login Joe 555', 'my order is 342'])
assert.equal(checkout.scriptingMemory.$username, 'Joe')
assert.equal(checkout.scriptingMemory.$orderId, '342')
assert.equal(checkout.scriptingMemory.$Convo__Payment__username, 'Jane')

await this.container.Start()
const changePassword = await convoByName(this.compiler, 'ChangePassword.One').Run(this.container)
await this.container.Stop()
assert.deepEqual(meTexts(changePassword), ['login Tom 234', 'change password to 78977'])
assert.equal(changePassword.scriptingMemory.$username, 'Tom')
assert.equal(changePassword.scriptingMemory.$newPWD, '78977')
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Case|$Convo__Payment__username|$Convo__Payment__pw|$Convo__Checkout__username|$Convo__Checkout__pw|$Convo__Checkout__orderId|$Convo__ChangePassword__username|$Convo__ChangePassword__pw|$token|$newPWD
One|Jane|123|Joe|555|342|Tom|234|secret|78977
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
ChangePassword

#me
login $username $pw

#bot
login $username $pw

#me
change password to $newPWD

#bot
change password to $newPWD
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Checkout

#include Login

#me
my order is $orderId

#bot
my order is $orderId
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Login

#me
login $username $pw

#bot
login $username $pw