// 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' // }