|
|
@@ -0,0 +1,290 @@ |
|
|
|
// Test Flow - a multiple intent test script for Alexa Lambda code |
|
|
|
// Launch from a Terminal Prompt. Examples: |
|
|
|
// node testflow |
|
|
|
// node testflow staterequest.txt |
|
|
|
|
|
|
|
// Originally taken from https://github.com/alexa/alexa-cookbook/blob/master/testing/TestFlow/testflow.js |
|
|
|
|
|
|
|
// Toggle on or off various debugging outputs |
|
|
|
const options = { |
|
|
|
speechOutput: true, |
|
|
|
slots: true, |
|
|
|
stdout: false, // standard output / console.log() in your code |
|
|
|
requestEvent: false, // show the full request JSON sent to your code |
|
|
|
reprompt: false, |
|
|
|
delay: 1.0 // seconds between requests |
|
|
|
} |
|
|
|
|
|
|
|
var locale = 'en-US' |
|
|
|
|
|
|
|
var fs = require('fs') |
|
|
|
var MyLambdaFunction = require('../src/index.js') // Your Lambda source with exports.handler |
|
|
|
|
|
|
|
var MyDialog = './test/dialogs/default.txt' |
|
|
|
var appId = '' // 'amzn1.ask.skill.d60ceb98-befe-4f49-a627-1f20f6e98d94' |
|
|
|
var slotArray = [] |
|
|
|
|
|
|
|
if (process.argv[2]) { |
|
|
|
MyDialog = './dialogs/' + process.argv[2] |
|
|
|
} |
|
|
|
|
|
|
|
console.log() |
|
|
|
console.log('================================================================================') |
|
|
|
console.log('Running test sequence from dialog file : ', MyDialog) |
|
|
|
console.log() |
|
|
|
|
|
|
|
const OriginalConsoleLog = console.log |
|
|
|
|
|
|
|
var slotname = '' |
|
|
|
var slotvalue = '' |
|
|
|
var sa = {} |
|
|
|
var currentLine = 1 |
|
|
|
var lineArray = [] |
|
|
|
var Intent = '' |
|
|
|
var prompt = false |
|
|
|
|
|
|
|
var context = { |
|
|
|
'succeed': function (data) { |
|
|
|
if (data.response.shouldEndSession) { |
|
|
|
sa = {} |
|
|
|
} else { |
|
|
|
sa = data.sessionAttributes |
|
|
|
} |
|
|
|
|
|
|
|
console.log = OriginalConsoleLog |
|
|
|
var textToSay = data.response.outputSpeech.ssml |
|
|
|
|
|
|
|
if (options.speechOutput) { |
|
|
|
console.log('\n\x1b[36m%s\x1b[0m', textToSay) |
|
|
|
} |
|
|
|
|
|
|
|
if (data.response.reprompt && data.response.reprompt.outputSpeech && data.response.reprompt.outputSpeech.ssml) { |
|
|
|
var textReprompt = data.response.reprompt.outputSpeech.ssml |
|
|
|
if (options.reprompt) { |
|
|
|
// console.log('%s \x1b[33m\x1b[1m%s\x1b[0m \x1b[2m%s\x1b[0m', currentLine+1, Intent, sdkState) |
|
|
|
console.log('\x1b[36m \x1b[2m%s\x1b[0m ', textReprompt) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (data.response.shouldEndSession) { |
|
|
|
console.log('\n================= Session Ended =================') |
|
|
|
} |
|
|
|
|
|
|
|
if (currentLine < lineArray.length) { |
|
|
|
console.log() |
|
|
|
runSingleTest(lineArray, currentLine++, sa) |
|
|
|
} else { |
|
|
|
console.log('') |
|
|
|
process.exit() |
|
|
|
} |
|
|
|
}, |
|
|
|
'fail': function (err) { |
|
|
|
console.log('context.fail occurred') |
|
|
|
console.log(JSON.stringify(err, null, '\t')) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fs.readFile(MyDialog, function (err, data) { // open dialog sequence file and read Intents |
|
|
|
// var newSession = true |
|
|
|
if (err) {} |
|
|
|
lineArray = cleanArray(data.toString().split('\n')) // remove empty or comment lines (# or //) |
|
|
|
runSingleTest(lineArray, 0, {}) |
|
|
|
}) |
|
|
|
|
|
|
|
function runSingleTest (myLineArray, currentLine, sa) { |
|
|
|
// console.log('--------------------------------------------------------------------------------') |
|
|
|
// console.log('testing line ', currentLine) |
|
|
|
// console.log('testing line values ', myLineArray[currentLine]) |
|
|
|
prompt = false |
|
|
|
var newSession = true |
|
|
|
if (currentLine > 0) { |
|
|
|
newSession = false |
|
|
|
} |
|
|
|
|
|
|
|
var tokenArray = myLineArray[currentLine].split(' ') |
|
|
|
|
|
|
|
if (tokenArray[0].replace('\r', '') === '?') { // pause and prompt the user to confirm |
|
|
|
prompt = true |
|
|
|
// console.log(' ----------------- > prompt') |
|
|
|
tokenArray.shift() // removes first item |
|
|
|
} |
|
|
|
|
|
|
|
var requestType = tokenArray[0].replace('\r', '') |
|
|
|
tokenArray.shift() |
|
|
|
|
|
|
|
if (requestType === 'stop') { |
|
|
|
console.log('') |
|
|
|
process.exit() |
|
|
|
} |
|
|
|
|
|
|
|
Intent = requestType |
|
|
|
// slotArray = [] |
|
|
|
|
|
|
|
var sdkState = '' |
|
|
|
if (sa['STATE']) { |
|
|
|
sdkState = sa['STATE'] |
|
|
|
} |
|
|
|
|
|
|
|
console.log('%s \x1b[33m\x1b[1m%s\x1b[0m \x1b[2m%s\x1b[0m', currentLine + 1, Intent, sdkState) |
|
|
|
processArray(tokenArray, function (request) { |
|
|
|
prepareTestRequest(sa, newSession, request) |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
function processArray (arr, cb) { |
|
|
|
if (arr.length > 0) { |
|
|
|
var equalsPosition = arr[0].indexOf('=') |
|
|
|
slotname = arr[0].substr(0, equalsPosition) |
|
|
|
slotvalue = decodeURI(arr[0].substr(equalsPosition + 1, 300)).replace('\r', '') |
|
|
|
|
|
|
|
promptForSlot(prompt, slotname, slotvalue, (newValue) => { |
|
|
|
// console.log('slotname, slotvalue, newValue') |
|
|
|
// console.log(slotname, slotvalue, newValue) |
|
|
|
|
|
|
|
var answer = newValue.toString().trim() |
|
|
|
|
|
|
|
// console.log('answer = ' + answer) |
|
|
|
|
|
|
|
if (answer === '') { |
|
|
|
answer = slotvalue |
|
|
|
} |
|
|
|
|
|
|
|
if (answer !== '') { |
|
|
|
slotArray.push('"' + slotname + '": {"name":"' + slotname + '","value":"' + answer + '"}') |
|
|
|
} |
|
|
|
|
|
|
|
arr.shift() |
|
|
|
processArray(arr, cb) // RECURSION |
|
|
|
}) |
|
|
|
} else { // nothing left in slot array |
|
|
|
var slotArrayString = '{' + slotArray.toString() + '}' |
|
|
|
|
|
|
|
var slotObj = JSON.parse(slotArrayString) |
|
|
|
|
|
|
|
var req = { |
|
|
|
'type': 'IntentRequest', |
|
|
|
'intent': { |
|
|
|
'name': Intent, |
|
|
|
'slots': slotObj |
|
|
|
}, |
|
|
|
'locale': locale |
|
|
|
} |
|
|
|
cb(req) |
|
|
|
// process.exit() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
function prepareTestRequest (sa, newSession, request) { |
|
|
|
var eventJSON = { |
|
|
|
'session': { |
|
|
|
'sessionId': 'SessionId.f9e6dcbb-b7da-4b47-905c.etc.etc', |
|
|
|
'application': { |
|
|
|
'applicationId': appId |
|
|
|
}, |
|
|
|
'attributes': sa, |
|
|
|
'user': { |
|
|
|
'userId': 'amzn1.ask.account.VO3PVTGF563MOPBY.etc.etc' |
|
|
|
}, |
|
|
|
'new': newSession |
|
|
|
}, |
|
|
|
request, |
|
|
|
'version': '1.0' |
|
|
|
} |
|
|
|
|
|
|
|
if (options.requestEvent) { |
|
|
|
console.log(JSON.stringify(request, null, 2)) |
|
|
|
} |
|
|
|
|
|
|
|
// blocking pause |
|
|
|
var waitTill = new Date(new Date().getTime() + options.delay * 1000) |
|
|
|
while (waitTill > new Date()) {} |
|
|
|
|
|
|
|
// call the function |
|
|
|
if (options.stdout) { |
|
|
|
MyLambdaFunction['handler'](eventJSON, context, callback) |
|
|
|
} else { |
|
|
|
// console.log('setting log to {}') |
|
|
|
console.log = function () {} |
|
|
|
// console.log('set log to {}') |
|
|
|
|
|
|
|
MyLambdaFunction['handler'](eventJSON, context, callback) |
|
|
|
console.log = OriginalConsoleLog |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
function promptForSlot (prompt, slotname, slotvalue, callback) { |
|
|
|
if (prompt) { |
|
|
|
process.stdout.write('\x1b[34m' + slotname + ' \x1b[0m\x1b[32m [' + slotvalue + ']\x1b[0m: ') |
|
|
|
|
|
|
|
// console.log('\x1b[34m%s :\x1b[0m\x1b[32m %s\x1b[0m ', slotname, slotvalue ) |
|
|
|
|
|
|
|
process.stdin.once('data', function (data) { |
|
|
|
var answer = data.toString().trim() |
|
|
|
// console.log(answer) |
|
|
|
if (answer === '') { |
|
|
|
if (slotvalue === '') { |
|
|
|
// no default, user must type something |
|
|
|
console.error('Error: No default slot value defined, user must type a slot value.') |
|
|
|
process.exit() |
|
|
|
} else { |
|
|
|
answer = slotvalue |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
callback(answer) |
|
|
|
}) |
|
|
|
} else { |
|
|
|
if (options.slots) { |
|
|
|
console.log('\x1b[34m%s :\x1b[0m\x1b[32m %s\x1b[0m ', slotname, slotvalue) |
|
|
|
} |
|
|
|
|
|
|
|
callback(slotvalue) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
function callback (error, data) { |
|
|
|
if (error) { |
|
|
|
console.log('error: ' + error) |
|
|
|
} else { |
|
|
|
console.log(data) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
function cleanArray (myArray) { |
|
|
|
var cleanedArray = [] |
|
|
|
|
|
|
|
for (var i = 0; i < myArray.length; i++) { |
|
|
|
if (myArray[i] !== '' && myArray[i].substring(0, 1) !== '#' && myArray[i].substring(0, 2) !== '//') { |
|
|
|
cleanedArray.push(myArray[i]) |
|
|
|
} |
|
|
|
} |
|
|
|
return cleanedArray |
|
|
|
} |
|
|
|
// |
|
|
|
// const fontcolor = { |
|
|
|
// Reset = '\x1b[0m', |
|
|
|
// Bright = '\x1b[1m', |
|
|
|
// Dim = '\x1b[2m', |
|
|
|
// Underscore = '\x1b[4m', |
|
|
|
// Blink = '\x1b[5m', |
|
|
|
// Reverse = '\x1b[7m', |
|
|
|
// Hidden = '\x1b[8m', |
|
|
|
// |
|
|
|
// FgBlack = '\x1b[30m', |
|
|
|
// FgRed = '\x1b[31m', |
|
|
|
// FgGreen = '\x1b[32m', |
|
|
|
// FgYellow = '\x1b[33m', |
|
|
|
// FgBlue = '\x1b[34m', |
|
|
|
// FgMagenta = '\x1b[35m', |
|
|
|
// FgCyan = '\x1b[36m', |
|
|
|
// FgWhite = '\x1b[37m', |
|
|
|
// |
|
|
|
// BgBlack = '\x1b[40m', |
|
|
|
// BgRed = '\x1b[41m', |
|
|
|
// BgGreen = '\x1b[42m', |
|
|
|
// BgYellow = '\x1b[43m', |
|
|
|
// BgBlue = '\x1b[44m', |
|
|
|
// BgMagenta = '\x1b[45m', |
|
|
|
// BgCyan = '\x1b[46m', |
|
|
|
// BgWhite = '\x1b[47m' |
|
|
|
// } |