With some Javascript development know-how, you can extend Botium with your own logic hooks.
Logic Hooks
There are currently nine types of logic hook events where you can add your custom logic:
-
onConvoBegin: before a convo begins
-
Conversation step, me section:
-
onMeBegin: before a me message is sent.
-
onMeEnd: after a me message is sent
-
onMe: same as onMeBegin/onMeEnd, but the position (executed before, or after) depends on the order in the script. Use this instead of onMeBegin/onMeEnd if it has sense to execute a logichook before, and after sending the me message, like Pause Logic Hook
-
-
Conversation step, bot section:
-
onBotBegin: before a bot message received
-
onBotPrepare: after a bot message received but before assertions are done
-
onBotEnd: after a bot message received
-
onBot: same as onBegin/onMeEnd, but the position (executed before, or after) depends on the order in the script. Use this instead of onMeBegin/onMeEnd if it has sense to execute a logichook before, and after asserting the bot response, like Pause Logic Hook
-
-
onConvoEnd: after convo ends
The logic hooks are implemented as Javascript functions. Implementation of the hooks is optional, you just have to implement the hooks required by your logic.
When adding a global logic hook to Botium (Register as global scripting component), the logic hooks will be called on every convo and every convo step.
Please keep in mind, that the logic hooks are blocking the test.
Some tips for better performance:
-
Put your time consuming initialization into constructor, or into the onConvoBegin function.
-
Execute write processes (which have no influence on the test process) parallel.
Code Sample - Logic Hook Class Skeleton
Best practice is to place your custom logic hook code in a Javascript file and export a Javascript class definition. As an example, place this in a file called MyCustomLogicHook.js:
module.exports = class MyCustomLogicHook {
constructor (context, caps, globalArgs) {
this.context = context
this.caps = caps
this.globalArgs = globalArgs
console.log(`MyCustomLogicHook constructor, globalArgs: ${utils.inspect(globalArgs)}`)
}
onConvoBegin ({ convo, args }) {
console.log(`MyCustomLogicHook onConvoBegin: ${convo.header.name}`)
}
onConvoEnd ({ convo, transcript, args }) {
console.log(`MyCustomLogicHook onConvoEnd ${convo.header.name}, conversation length: ${transcript.steps.length} steps`)
}
onMeStart ({ convo, convoStep, args }) {
console.log(`MyCustomLogicHook onMeStart ${convo.header.name}/${convoStep.stepTag}, args: ${utils.inspect(args)}, meMessage: ${convoStep.messageText}`)
}
onMeEnd ({ convo, convoStep, args }) {
console.log(`MyCustomLogicHook onMeEnd ${convo.header.name}/${convoStep.stepTag}, args: ${utils.inspect(args)}, meMessage: ${convoStep.messageText}`)
}
onMe ({ convo, convoStep, args }) {
console.log(`MyCustomLogicHook onMe ${convo.header.name}/${convoStep.stepTag}, args: ${utils.inspect(args)}, meMessage: ${convoStep.messageText}`)
}
onBotStart ({ convo, convoStep, args }) {
console.log(`MyCustomLogicHook onBotStart ${convo.header.name}/${convoStep.stepTag}, args: ${utils.inspect(args)}, expected: ${convoStep.messageText}`)
}
onBotPrepare ({ convo, convoStep, args }) {
console.log(`MyCustomLogicHook onBotPrepare ${convo.header.name}/${convoStep.stepTag}, args: ${utils.inspect(args)}, expected: ${convoStep.messageText}`)
}
onBotEnd ({ convo, convoStep, botMsg, args }) {
console.log(`MyCustomLogicHook onBotEnd ${convo.header.name}/${convoStep.stepTag}, args: ${utils.inspect(args)}, expected: ${convoStep.messageText}, meMessage: ${botMsg.messageText}`)
}
onBot ({ convo, convoStep, botMsg, args }) {
console.log(`MyCustomLogicHook onBot ${convo.header.name}/${convoStep.stepTag}, args: ${utils.inspect(args)}, expected: ${convoStep.messageText}, meMessage: ${botMsg ? botMsg.messageText : null}`)
}
}
Code Sample - Logic Hook File Code
For short logic hook it is possible to place the Javascript code directly in the component configuration:
{
"botium": {
"Capabilities": {
...
"LOGIC_HOOKS": [
{
"ref": "MY-LOGICHOOK-NAME",
"src": {
"onMeStart": "meMsg.messageText += ' from logichook'"
},
"global": false,
"args": {
"my-arg-1": "something"
}
}
]
}
}
}
Logic Hook API
Class Constructor Arguments (only for class definitions)
-
context - Scripting Context with several Botium internal functions
-
caps - Botium capabilities
-
globalArgs - the component configuration (from the Component Configuration field in Botium, or from the args field in botium.json) as JSON structure
onConvoBegin Arguments
-
convo - current convo
-
args - the logic hook args from the convo file (as JSON array - split by “|”)
-
scriptingMemory - scripting memory variables (a JSON object)
-
isGlobal - wether the logic hook has been registered globally or not
-
container - Botium Connector container (provides access to internal connector data)
onMeStart/onMeEnd/onMe
-
convo - see above
-
convoStep - the logic hook args from the convo file (as JSON array - split by “|”)
-
args - see above
-
scriptingMemory - see above
-
isGlobal - see above
-
container - see above
-
meMsg - request from the user
onBotStart Arguments
-
convo - see above
-
convoStep - the logic hook args from the convo file (as JSON array - split by “|”)
-
args - see above
-
scriptingMemory - see above
-
isGlobal - see above
-
container - see above
onBotPrepare Arguments
-
convo - see above
-
convoStep -see above
-
args - see above
-
scriptingMemory - see above
-
isGlobal - see above
-
container - see above
-
botMsg - the message from the bot
onBotEnd Arguments
-
convo - see above
-
convoStep -see above
-
args - see above
-
scriptingMemory - see above
-
isGlobal - see above
-
container - see above
-
botMsg - the message from the bot
onBot Arguments
-
convo - see above
-
convoStep -see above
-
args - see above
-
scriptingMemory - see above
-
isGlobal - see above
-
container - see above
-
botMsg - the message from the bot. CAN BE NULL DEPENDING ON ORDER IN SCRIPT.
onConvoEnd Arguments
-
convo - see above
-
args - see above
-
scriptingMemory - see above
-
isGlobal - see above
-
container - see above
-
transcript - the full conversation transcript
Deployment to Botium
There are two ways to deploy your custom logic hook to Botium.
- Registering works the same as any other asserter
- In both cases you can use botium-logichook- prefix to automatically register your logic hook.
- In both cases the logic hook is loaded if plug-ins are enabled.
-
NPM package: Build an NPM package, exporting a Javascript class holding your
logic. You have to add the NPM package to your Botium server - unpack the NPM package
to the resources directory - the package.json of your NPM package is in the
resources/your-logichook-name directory - and run “npm install” there.Note: An NPM package is nothing more than a ZIP-File. For Botium logic hooks hosted on Github, you can download the package as ZIP-file from Github (Clone or Download => Download as ZIP) and unpack them - make sure to rename the resulting directory to match the logic hook package name (Github adds the -master suffix to the downloaded files)Now you can register your custom logic hook, using your-logichook-name as Component Source.
-
Shared Javascript file: You can just tell Botium to load the logic hook code
from a Javascript file. The Javascript file has to be available on all Botium Box
servers and agents, obviously, by placing it in the resources directory.
Now you can use the Registered Components view to register your custom logic hook, using your-logichook-filename.js as Component Source (the relative path in the resources directory).
Deployment to botium.json
When using botium-cli or botium-bindings, you have to register your logic hook in the botium.json file:
{
"botium": {
"Capabilities": {
...
"LOGIC_HOOKS": [
{
"ref": "MY-LOGICHOOK-NAME",
"src": "path-to-my-javascript-file-or-npm-package-name",
"global": false,
"args": {
"my-arg-1": "something"
}
}
]
}
}
}
Using the Logic Hook
You trigger the logic hook call by using the defined Component Ref
Code of the logic hook (in our example below: DUMMY_LOG_TESTCASE
and
DUMMY_INSERT_TO_SCRIPTING_MEMORY
). Everything after Component Ref
Code are args separated by |
-
begin: Before the conversation starts (use onConvoBegin hook)
-
me: before and after me request (use onMeStart/onMeEnd, or booth hooks)
-
bot: before and after bot response (use onBotStart/onBotEnd, or booth hooks)
-
end: After the conversation finished
restaurant
#begin
DUMMY_LOG_TESTCASE dbUrl | dbPassword
#me
hi
#bot
Hi. It looks like a nice drive today. What would you like me to do?
#me
where is the next restaurant
#bot
I understand you want me to find a location. I can find restaurants, gas stations and restrooms nearby.
#me
find my a restaurant
#bot
Of course. Do you have a specific cuisine in mind?
#me
DUMMY_INSERT_TO_SCRIPTING_MEMORY CUISINE1
$cuisine
#bot
Super! I've found 5 locations for you. Which one would you like to drive to?
#me
DUMMY_INSERT_TO_SCRIPTING_MEMORY CUISINE1 | CHOICE1
$choice
#bot
Sure! That restaurant gets great reviews.
#end
DUMMY_LOG_TESTCASE dbUrl | dbPassword