Skip to content

Commit ae81a86

Browse files
author
Rob McCauley
committed
TestFlow updates
1 parent 4f34740 commit ae81a86

File tree

4 files changed

+73
-59
lines changed

4 files changed

+73
-59
lines changed

testing/TestFlow/README.md

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
## TestFlow <a id="title"></a>
22

3-
In the main [testing](../testing) folder, we see several examples of how to test your code with one specific event.
3+
Are you developing a conversational skill? Maybe you are building a game, or a questionnaire, that requires several steps.
4+
You have probably seen how session attributes are set and get to allow the skill to remember things and keep track of the state of the skill.
5+
Sometimes it is difficult to visualize how your skill behaves as a "state machine" through many sequences of events.
6+
It can get tedious to verbally test a skill with several steps.
7+
8+
In the main [testing](../testing) folder, we see several examples of how to test your code with one specific, discrete event.
49
This is useful if you have a specific test state you need to reproduce and debug.
510

6-
Many skills are designed to have a conversation with the user involving multiple steps.
711
A skill may prompt the user for inputs early in the conversation, store the responses in session attributes, and use the values to look up data or perform an action.
812
Game skills will keep track of user names, current scores, high scores, etc.
9-
As a developer, in order to visualize the state of session attributes throughout a long skill session, it helps to be able to run a pre-defined sequence of events and observe the state of the attributes at each point.
13+
As a developer, in order to visualize the state of session attributes throughout a long skill session, it helps to be able to run a pre-defined sequence of events and observe everthing that is happening at each point.
14+
It is also very useful if you can override the unit test with a custom response, for example to test a multiple-choice quiz game where the correct answer depends on a random question.
1015

11-
This tutorial will show you an easy way to define, run, and view test sequences against your skill code.
16+
This tutorial will show you an easy way to define, run, and view test sequences against your skill code from your local command line.
1217

1318
<img src="https://m.media-amazon.com/images/G/01/cookbook/testflow_default._TTH_.png" alt="TestFlow" width="411" height="245">
1419

@@ -21,32 +26,33 @@ This tutorial will show you an easy way to define, run, and view test sequences
2126
Define a text file with your skill's input events.
2227
Put one Request or Intent per line. This corresponds to each of the Intent requests your code expects to receive from the Alexa service.
2328

24-
For example: *default.txt*
29+
For example: *dialogs/default.txt*
2530

2631
```
2732
LaunchRequest
2833
AMAZON.HelpIntent
29-
AMAZON.CancelIntent
3034
AMAZON.StopIntent
3135
```
3236

33-
Another example: *staterequest.txt*
37+
Another example: *dialogs/staterequest.txt*
3438

3539
```
3640
LaunchRequest
3741
StateRequestIntent usstate=Vermont
3842
StateRequestIntent usstate=New%20York
3943
ISeeIntent animal=bear color=brown
40-
AMAZON.HelpIntent
44+
# AMAZON.HelpIntent
4145
AMAZON.StopIntent
4246
MyNameIsIntent myName=
4347
MyNameIsIntent myName=Madeline
44-
StateRequestIntent usstate=Texas
48+
? StateRequestIntent usstate=Texas
4549
RecapIntent
4650
AMAZON.StopIntent
4751
```
4852

49-
Notice that slot values with spaces need to be encoded. Just insert ```%20``` to replace any white spaces, such as in ```usstate=New%20York```
53+
* Notice that slot values with spaces need to be encoded. Just insert ```%20``` to replace any white spaces, such as in ```usstate=New%20York```
54+
* You can prompt the user to confirm, or type in, a slot value by adding a leading ```? ``` to your Intent
55+
* You can comment out a line with a pound #
5056

5157
#### Running the test
5258

@@ -70,10 +76,20 @@ You may change any of these to ```true``` or ```false```.
7076
const options = {
7177
speechOutput : true, // the cyan text you hear the Echo say
7278
slots : true, // key/value pairs shown in blue and green
73-
attributes : true, // session.attributes shown in magenta
79+
attributes : true, // session.attributes shown in magenta. You can also name one particular attribute to watch instead of the boolean
7480
stdout : false // console.log() messages or errors, shown in white
81+
requestEvent : false, // the full request JSON sent to your code
82+
reprompt : false, // show the reprompt below the speechOutput
83+
delay : 1.0 // delay N seconds between requests
7584
};
7685
```
86+
#### AWS Calls
87+
If your code makes calls to AWS Services such as S3 or IOT, you should be able to test these from your local command prompt, too.
88+
Be sure you have the AWS CLI (command line interface) [installed](http://docs.aws.amazon.com/cli/latest/userguide/installing.html) and [configured](https://developer.amazon.com/blogs/post/Tx1UE9W1NQ0GYII/publishing-your-skill-code-to-lambda-via-the-command-line-interface).
89+
90+
**Note:** the ```alexa-sdk``` feature to automatically persist session attributes to a DynamoDB table has not been successfully tested with TestFlow.
91+
92+
``` //alexa.dynamoDBTableName = 'YourTableName';```
7793

7894
#### Session Attributes
7995
Notice in magenta (purple) the session attributes that store values your skill is designed to remember.
@@ -82,10 +98,11 @@ By default, this object is empty ```{}```
8298
Each new request uses the session attributes object from the previous session's output.
8399
The skill code may add or modify the session attributes. Look for any changes in the attributes after each Intent.
84100
If your skill causes the session to end, such as when an ```AMAZON.StopIntent``` handler calls ```this.emit(':tell' )```, the session attributes will be lost.
85-
If your skill uses an AWS DynamoDB table, however, you can expect the session attributes to be saved and re-loaded after each session ends, within the actual AWS Lambda environment.
86101

87102

88-
#### Try it on your code
103+
#### Installation Steps
104+
To try TestFlow on your own skill code, follow these steps:
105+
89106
1. Copy and paste the ```testflow.js``` file and ```/dialogs``` folder to your project folder, next to your ```/src``` folder.
90107
1. Customize the settings within the top of the ```testflow.js``` file
91108
1. Create a new dialog sequence file with your Intents in sequence, such as ```mytest.txt```

testing/TestFlow/dialogs/default.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
LaunchRequest
22
AMAZON.HelpIntent
3-
AMAZON.CancelIntent
43
AMAZON.StopIntent

testing/TestFlow/dialogs/staterequest.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ LaunchRequest
22
StateRequestIntent usstate=Vermont
33
StateRequestIntent usstate=New%20York
44
ISeeIntent animal=bear color=brown
5-
AMAZON.HelpIntent
5+
# AMAZON.HelpIntent
66
AMAZON.StopIntent
77
MyNameIsIntent myName=
88
MyNameIsIntent myName=Madeline
9-
StateRequestIntent usstate=Texas
9+
? StateRequestIntent usstate=Texas
1010
RecapIntent
1111
AMAZON.StopIntent

testing/TestFlow/testflow.js

Lines changed: 41 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
// node testflow
44
// node testflow staterequest.txt
55

6-
76
// Toggle on or off various debugging outputs
87
const options = {
98
speechOutput : true,
109
slots : true,
11-
attributes : false, // true, false, or a string with the name of an attribute
12-
stdout : true, // standard output / console.log() in your code
13-
delay : 0.8 // seconds between requests
10+
attributes : true, // true, false, or a string with the name of an attribute
11+
stdout : false, // standard output / console.log() in your code
12+
requestEvent : false, // show the full request JSON sent to your code
13+
reprompt : false,
14+
delay : 1.0 // seconds between requests
1415
};
1516

1617
var locale = 'en-US';
@@ -19,6 +20,7 @@ var fs = require("fs");
1920
var MyLambdaFunction = require('./src/index.js'); // Your Lambda source with exports.handler
2021

2122
var MyDialog = './dialogs/default.txt';
23+
var appId = ''; // 'amzn1.ask.skill.d60ceb98-befe-4f49-a627-1f20f6e98d94';
2224

2325
if (process.argv[2]) {
2426
MyDialog = './dialogs/' + process.argv[2];
@@ -80,6 +82,26 @@ var context = {
8082
console.log('\x1b[36m%s\x1b[0m ', textToSay);
8183
}
8284

85+
if (data.response.reprompt && data.response.reprompt.outputSpeech && data.response.reprompt.outputSpeech.ssml) {
86+
87+
var textReprompt = data.response.reprompt.outputSpeech.ssml;
88+
textReprompt = textReprompt.replace('<speak>', ' ');
89+
textReprompt = textReprompt.replace('</speak>', '');
90+
91+
if (options.reprompt) {
92+
console.log = OriginalConsoleLog;
93+
94+
// console.log('%s \x1b[33m\x1b[1m%s\x1b[0m \x1b[2m%s\x1b[0m', currentLine+1, Intent, sdkState);
95+
96+
console.log('\x1b[36m \x1b[2m%s\x1b[0m ', textReprompt);
97+
}
98+
}
99+
100+
101+
if (data.response.shouldEndSession) {
102+
// console.log('================ Session Ended');
103+
}
104+
83105

84106
// =====================
85107

@@ -94,11 +116,12 @@ var context = {
94116
runSingleTest(lineArray, current_line++, sa);
95117

96118
} else {
119+
console.log('');
120+
97121
process.exit();
98122

99123
}
100124

101-
102125
},
103126
'fail': function (err) {
104127
console.log('context.fail occurred');
@@ -142,6 +165,11 @@ function runSingleTest(myLineArray, currentLine, sa) {
142165
var requestType = tokenArray[0].replace('\r','');
143166
tokenArray.shift();
144167

168+
if (requestType == 'stop') {
169+
console.log('');
170+
process.exit();
171+
}
172+
145173
if (requestType =='LaunchRequest') {
146174
request = {
147175
"type": requestType,
@@ -164,53 +192,17 @@ function runSingleTest(myLineArray, currentLine, sa) {
164192
sdkState = sa['STATE'];
165193
}
166194

167-
// console.log(' ========== %s. Intent \x1b[33m\x1b[1m%s\x1b[0m', currentLine+1, Intent);
168195
console.log('%s \x1b[33m\x1b[1m%s\x1b[0m \x1b[2m%s\x1b[0m', currentLine+1, Intent, sdkState);
169196

170-
171-
// for(j = 0; j < tokenArray.length; j++) {
172-
//
173-
// var equalsPosition = tokenArray[j].indexOf('=');
174-
// slotname = tokenArray[j].substr(0, equalsPosition);
175-
// slotvalue = decodeURI(tokenArray[j].substr(equalsPosition+1, 300)).replace('\r','');
176-
//
177-
// if (options.slots) {
178-
// console.log('\x1b[34m%s :\x1b[0m\x1b[32m %s\x1b[0m ', slotname, slotvalue );
179-
// }
180-
//
181-
// if (slotvalue != '') {
182-
// slotArray.push('"' + slotname + '": {"name":"' + slotname + '","value":"' + slotvalue + '"}');
183-
//
184-
// }
185-
//
186-
// }
187197
processArray(tokenArray, function(request) {
188198
prepareTestRequest(sa, newSession, request);
189199

190200
});
191201

192-
// blocking pause
193-
// var waitTill = new Date(new Date().getTime() + options.delay * 1000);
194-
// while(waitTill > new Date()){}
195-
196-
// request = {
197-
// "type": "IntentRequest",
198-
// "intent": {
199-
// "name": Intent,
200-
// "slots" : slotObj
201-
// },
202-
// "locale": locale
203-
// };
204-
205-
// prepareTestRequest(sa, newSession, request);
206202

207203
}
208204

209205

210-
// if (currentLine < myLineArray.length - 1) {
211-
// console.log();
212-
// runSingleTest(myLineArray, currentLine + 1, sa);
213-
// }
214206
}
215207

216208
slotArray = [];
@@ -278,7 +270,7 @@ function prepareTestRequest(sa, newSession, request){
278270
"session": {
279271
"sessionId": "SessionId.f9e6dcbb-b7da-4b47-905c.etc.etc",
280272
"application": {
281-
"applicationId": "amzn1.echo-sdk-ams.app.1234"
273+
"applicationId": appId
282274
},
283275
"attributes": sa,
284276
"user": {
@@ -290,6 +282,10 @@ function prepareTestRequest(sa, newSession, request){
290282
"version": "1.0"
291283
};
292284

285+
if (options.requestEvent) {
286+
console.log(JSON.stringify(request, null, 2));
287+
}
288+
293289
// blocking pause
294290
var waitTill = new Date(new Date().getTime() + options.delay * 1000);
295291
while(waitTill > new Date()){}
@@ -299,8 +295,10 @@ function prepareTestRequest(sa, newSession, request){
299295
MyLambdaFunction['handler'] (eventJSON, context, callback);
300296

301297
} else {
302-
298+
//console.log('setting log to {}');
303299
console.log = function() {};
300+
//console.log('set log to {}');
301+
304302
MyLambdaFunction['handler'] (eventJSON, context, callback);
305303
console.log = OriginalConsoleLog;
306304
}

0 commit comments

Comments
 (0)