From 31e705714441f71eb624aa3a80f8daee2d1f264c Mon Sep 17 00:00:00 2001 From: BinHong Lee Date: Thu, 10 Aug 2017 01:38:19 -0700 Subject: [PATCH] Added testing modules --- .travis.yml | 14 ++ README.md | 14 +- package.json | 8 +- src/index.js | 3 +- test/dialogs/default.txt | 7 + test/testflow.js | 290 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 328 insertions(+), 8 deletions(-) create mode 100644 .travis.yml create mode 100644 test/dialogs/default.txt create mode 100644 test/testflow.js diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..61d788c --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +language: node_js +node_js: + - "node" + - "7" + - "6" + - "5" + - "4" + - "lts/*" +before_script: + - npm install + - npm install-test +script: npm test +env: + CODECLIMATE_REPO_TOKEN: a174b03227cdc953c831f45bebcf0af357ec1b8a4b8477f36444c16f084a409f diff --git a/README.md b/README.md index ded8f9f..d553004 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,16 @@ # Dota 2 Random - Alexa Skill -Skill is now available from the [Alexa Skill Store](https://www.amazon.com/binhonglee-Dota2-Random/dp/B073W4GDLS)! +[![Build Status](https://travis-ci.org/binhonglee/dota2-random.svg?branch=master)](https://travis-ci.org/binhonglee/dota2-random) [![Test Coverage](https://codeclimate.com/github/binhonglee/dota2-random/badges/coverage.svg)](https://codeclimate.com/github/binhonglee/dota2-random/coverage) [![Dependency Status](https://gemnasium.com/badges/github.com/binhonglee/dota2-random.svg)](https://gemnasium.com/github.com/binhonglee/dota2-random) -#### Dependency -- alexa-sdk +## Skill is now available from the [Alexa Skill Store](https://www.amazon.com/binhonglee-Dota2-Random/dp/B073W4GDLS)! + +### Dependency - node.js +- alexa-sdk -## Documentations +### Documentations -### Intents +#### Intents | Intents | Description | |:---------|:------------| @@ -20,7 +22,7 @@ Skill is now available from the [Alexa Skill Store](https://www.amazon.com/binho | `AMAZON.HelpIntent` | Provide examples on how to use the skill | | `Stop` | Stop the skill | -### ROLE_TYPES (Custom slot) +#### ROLE_TYPES (Custom slot) - melee - ranged - carry diff --git a/package.json b/package.json index 46d5c4e..004c94a 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,11 @@ "dependencies": { "alexa-sdk": "^1.0.7" }, + "scripts": { + "install-test": "npm install -g standard", + "test": "standard && node test/testflow.js", + "remove-test": "npm uninstall -g standard" + }, "author": "binhonglee", "license": "MIT", "repository": { @@ -15,5 +20,6 @@ "bugs": { "url": "https://github.com/binhonglee/dota2-alexa-skill/issues" }, - "homepage": "https://github.com/binhonglee/dota2-alexa-skill#readme" + "homepage": "https://github.com/binhonglee/dota2-alexa-skill#readme", + "devDependencies": {} } diff --git a/src/index.js b/src/index.js index ee979bb..e9b47ad 100644 --- a/src/index.js +++ b/src/index.js @@ -81,7 +81,8 @@ function randomHero (categories, callback) { } if (!possible) { - callback('Random failed') + var errorMessage = 'Random failed' + callback(errorMessage) return } diff --git a/test/dialogs/default.txt b/test/dialogs/default.txt new file mode 100644 index 0000000..87f3ad5 --- /dev/null +++ b/test/dialogs/default.txt @@ -0,0 +1,7 @@ +LaunchRequest +AMAZON.HelpIntent +AnyRequest +RequestIntent Role=agility +RequestTwoIntent RoleOne=melee RoleTwo=ranged +RequestThreeIntent RoleOne=melee RoleTwo=intelligence RoleThree=support +Stop diff --git a/test/testflow.js b/test/testflow.js new file mode 100644 index 0000000..ec5d7dd --- /dev/null +++ b/test/testflow.js @@ -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' +// }