├── .eslintrc.json
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── index.js
├── lib
└── index.js
├── package.json
└── spec
├── message-builder-spec.js
└── support
├── jasmine-runner.js
└── jasmine.json
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "defaults",
3 | "env": {
4 | "node": true,
5 | "es6": true
6 | },
7 | "parserOptions": {
8 | "ecmaVersion": 6,
9 | "sourceType": "module"
10 | },
11 | "rules": {
12 | "no-console": "off",
13 | "semi": ["error", "never"],
14 | "indent": ["error", 2],
15 | "quotes": ["error", "single", {"avoidEscape": true, "allowTemplateLiterals": true}],
16 | "prefer-arrow-callback": "error"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 4
4 | - 4.3.2
5 | - 5
6 | - 6
7 | - 7
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 Slobodan Stojanović
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Alexa Message Builder
2 |
3 | [](https://travis-ci.org/stojanovic/alexa-message-builder)
4 | [](https://www.npmjs.com/package/alexa-message-builder)
5 | [](https://github.com/claudiajs/alexa-message-builder/blob/master/LICENSE)
6 |
7 | Simple message builder for Alexa response.
8 |
9 | ## Installation
10 |
11 | Alexa Message Builder is available as a node module on NPM.
12 |
13 | Install it by running:
14 |
15 | ```shell
16 | npm install alexa-message-builder --save
17 | ```
18 |
19 | ## Usage
20 |
21 | After installing the package, require it in your code:
22 |
23 | ```javascript
24 | const AlexaMessageBuilder = require('alexa-message-builder')
25 | ```
26 |
27 | or with `import`* syntax:
28 |
29 | ```javascript
30 | import AlexaMessageBuilder from 'alexa-message-builder'
31 | ```
32 |
33 | \* `import` syntax is not supported in Node.js, you need to use additional library like Babel to make it work.
34 |
35 | After requiring it, you simply need to initialize the class, use any of available methods from the [documentation](#documentation) below and call `.get()` in the end. For Example:
36 |
37 | ```javascript
38 | const AlexaMessageBuilder = require('alexa-message-builder')
39 |
40 | const message = new AlexaMessageBuilder()
41 | .addText('Hello from Alexa')
42 | .get()
43 | ```
44 |
45 | will return:
46 |
47 | ```json
48 | {
49 | "version": "1.0",
50 | "response": {
51 | "shouldEndSession": false,
52 | "outputSpeech": {
53 | "type": "PlainText",
54 | "ssml": "Hello from Alexa"
55 | }
56 | }
57 | }
58 | ```
59 |
60 | ## Motivation
61 |
62 | Building JSON responses manually is not fun and hard to read for a big JSON files. The main motivation for this message builder is to replace them with a simple and readable syntax. For example, instead of this JSON:
63 | ```json
64 | {
65 | "version": "1.0",
66 | "response": {
67 | "shouldEndSession": false,
68 | "outputSpeech" : {
69 | "type": "PlainText",
70 | "text": "Alexa message builder is a simple message builder for Alexa responses"
71 | },
72 | "card": {
73 | "type": "Standard",
74 | "title": "Alexa Message Builder",
75 | "text": "Alexa message builder description",
76 | "image": {
77 | "smallImageUrl": "http://example.com/small-image-url.png",
78 | "largeImageUrl": "http://example.com/large-image-url.png"
79 | }
80 | }
81 | }
82 | }
83 | ```
84 |
85 | You can write following JavaScript code:
86 | ```javascript
87 | new AlexaMessageBuilder()
88 | .addText('Alexa message builder is a simple message builder for Alexa responses')
89 | .addStandardCard('Alexa Message Builder', 'Alexa message builder description', {
90 | smallImageUrl: 'http://example.com/small-image-url.png',
91 | largeImageUrl: 'http://example.com/large-image-url.png'
92 | })
93 | .keepSession()
94 | .get()
95 | ```
96 |
97 | Package can work with any Node.js project for building Alexa app. For example, it works perfectly with [Claudia Bot Builder](https://github.com/claudiajs/claudia-bot-builder):
98 |
99 | ```javascript
100 | const BotBuilder = require('claudia-bot-builder'),
101 | AlexaMessageBuilder = require('alexa-message-builder')
102 |
103 | module.exports = botBuilder(message => {
104 | return new AlexaMessageBuilder()
105 | .addText('Hello from Alexa')
106 | .get()
107 | }, {
108 | platforms: ['alexa']
109 | })
110 | ```
111 |
112 | ## Documentation
113 |
114 | Alexa Message Builder is still not covering 100% of Alexa JSON response, but it covers the big part of it. Here's how it works:
115 |
116 | Require the package you previously installed from NPM:
117 |
118 | ```javascript
119 | const AlexaMessageBuilder = require('alexa-message-builder')
120 | ```
121 |
122 | or with `import`* syntax:
123 |
124 | ```javascript
125 | import AlexaMessageBuilder from 'alexa-message-builder'
126 | ```
127 |
128 | \* `import` syntax is not supported in Node.js, you need to use additional library like Babel to make it work.
129 |
130 | After requiring it, you simply need to initialize the class, use any of available methods from the [documentation](#documentation) below and call `.get()` in the end. For Example:
131 |
132 | ```javascript
133 | const AlexaMessageBuilder = require('alexa-message-builder')
134 |
135 | const message = new AlexaMessageBuilder()
136 | .addText('Hello from Alexa')
137 | .get()
138 | ```
139 |
140 | will return:
141 |
142 | ```json
143 | {
144 | "version": "1.0",
145 | "response": {
146 | "shouldEndSession": false,
147 | "outputSpeech": {
148 | "type": "PlainText",
149 | "text": "Hello from Alexa"
150 | }
151 | }
152 | }
153 | ```
154 |
155 | ### Add output speech
156 |
157 | This generates the speech that Alexa will say as a reply to your question or command. It can be used as a response to a [LaunchRequest or IntentRequest](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/custom-standard-request-types-reference).
158 |
159 | You can send either plain text or [Speech Synthesis Markup Language (SSML)](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/speech-synthesis-markup-language-ssml-reference).
160 |
161 | #### Available methods
162 |
163 | - addText
164 | - addSSML
165 |
166 | **addText** method can receive a plain text and it returns a reference to `this` for chaining.
167 |
168 | Example:
169 |
170 | ```javascript
171 | new AlexaMessageBuilder()
172 | .addText('A text that Alexa will use as a response')
173 | .get()
174 | ```
175 |
176 | This method will throw an error if `text` is not provided.
177 |
178 | **addSSML** method can receive a SSML message as a string and it returns a reference to `this` for chaining.
179 |
180 | Example:
181 |
182 | ```javascript
183 | new AlexaMessageBuilder()
184 | .addSSML('This output speech uses SSML.')
185 | .get()
186 | ```
187 |
188 | This method will throw an error if `ssmlMessage` is not provided.
189 |
190 | ### Add reprompt
191 |
192 | Similar to the output speech, reprompt supports both text and SSML, and it can be used as a response to a [LaunchRequest or IntentRequest](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/custom-standard-request-types-reference).
193 |
194 | #### Available methods
195 |
196 | - addText
197 | - addSSML
198 |
199 | **addRepromptText** method can receive a plain text and it returns a reference to `this` for chaining.
200 |
201 | Example:
202 |
203 | ```javascript
204 | new AlexaMessageBuilder()
205 | .addRepromptText('A reprompt text that Alexa will use as a response')
206 | .get()
207 | ```
208 |
209 | This method will throw an error if `text` is not provided.
210 |
211 | **addRepromptSSML** method can receive a SSML message as a string and it returns a reference to `this` for chaining.
212 |
213 | Example:
214 |
215 | ```javascript
216 | new AlexaMessageBuilder()
217 | .addRepromptSSML('This reprompt speech uses SSML.')
218 | .get()
219 | ```
220 |
221 | This method will throw an error if `ssmlMessage` is not provided.
222 |
223 | ### Add cards
224 |
225 | Alexa supports 3 different types of the cards: Simple, Standard and LinkAccount. First two types are supported by this library.
226 |
227 | Cards can only be included when sending a response to a [LaunchRequest or IntentRequest](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/custom-standard-request-types-reference).
228 |
229 | #### Add Simple card
230 |
231 | Simple card is a card that contains a title and plain text content.
232 |
233 | **addSimpleCard** method can receive title and text and it returns a reference to `this` for chaining.
234 |
235 | Example:
236 |
237 | ```javascript
238 | new AlexaMessageBuilder()
239 | .addText('A text that Alexa will use as a response')
240 | .addSimpleCard('Card title', 'Card text')
241 | .get()
242 | ```
243 |
244 | This method will throw an error if both `title` and `text` are not provided.
245 |
246 | #### Add Standard card
247 |
248 | Standard card is a card that contains a title, text content, and an image to display.
249 |
250 | **addStandardCard** method can receive title, text and image object, and it returns a reference to `this` for chaining.
251 |
252 | Example:
253 |
254 | ```javascript
255 | new AlexaMessageBuilder()
256 | .addText('A text that Alexa will use as a response')
257 | .addStandardCard('Card title', 'Card text', {
258 | smallImageUrl: 'http://example.com/small-image-url.png',
259 | largeImageUrl: 'http://example.com/large-image-url.png'
260 | })
261 | .get()
262 | ```
263 |
264 | This method will throw an error if `title`, `text` and `imageObject` are not provided.
265 |
266 | ### Keep the session opened
267 |
268 | Alexa session will be closed by default, if you want to keep it opened use `.keepSession()` method.
269 |
270 | **keepSession** method will keep the session opened. It doesn't require any params.
271 |
272 | Example:
273 |
274 | ```javascript
275 | new AlexaMessageBuilder()
276 | .addText('A text that Alexa will use as a response, and session will not be closed')
277 | .keepSession()
278 | .get()
279 | ```
280 |
281 | ### Add session attributes
282 |
283 | Alexa also allows you to store some session attributes while the session is opened. To do so with a message builder use `.addSessionAttribute(key, value)` method.
284 |
285 | **addSessionAttribute** method can receive key and value and it returns a reference to `this` for chaining. Key needs to be a string and value can be in other types too.
286 |
287 | Example:
288 |
289 | ```javascript
290 | new AlexaMessageBuilder()
291 | .addText('A text that Alexa will use as a response, and session will not be closed')
292 | .addSessionAttribute('someKey', 1)
293 | .keepSession()
294 | .get()
295 | ```
296 |
297 | ## TODO
298 |
299 | - [ ] Add directives
300 | - [ ] Add LinkAccount cards
301 | - [ ] Check for limits
302 |
303 | ## Contribute
304 |
305 | ### Folder structure
306 |
307 | The main body of code is in the [lib](lib) directory.
308 |
309 | The tests are in the [spec](spec) directory, and should follow the structure of the corresponding source files. All executable test file names should end with `-spec`, so they will be automatically picked up by `npm test`. Any additional project files, helper classes etc that must not be directly executed by the test runner should not end with `-spec`. You can use the [spec/helpers](spec/helpers) directory to store Jasmine helpers, that will be loaded before any test is executed.
310 |
311 | ### Running tests
312 |
313 | We use [Jasmine](https://jasmine.github.io/) for unit and integration tests. Unless there is a very compelling reason to use something different, please continue using Jasmine for tests. The existing tests are in the [spec](spec) folder. Here are some useful command shortcuts:
314 |
315 | Run all the tests:
316 |
317 | ```bash
318 | npm test
319 | ```
320 |
321 | Run only some tests:
322 |
323 | ```bash
324 | npm test -- filter=prefix
325 | ```
326 |
327 | Get detailed hierarchical test name reporting:
328 |
329 | ```bash
330 | npm test -- full
331 | ```
332 |
333 | We use [ESLint](http://eslint.org/) for syntax consistency, and the linting rules are included in this repository. Running `npm test` will check the linting rules as well. Please make sure your code has no linting errors before submitting a pull request.
334 |
335 | ## License
336 |
337 | MIT - See [LICENSE](LICENSE)
338 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib')
2 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | class AlexaMessageBuilder {
4 | constructor() {
5 | this.template = {
6 | version: '1.0',
7 | response: {
8 | shouldEndSession: true
9 | }
10 | }
11 | }
12 |
13 | addVersion(versionString) {
14 | if (typeof versionString !== 'string')
15 | throw new Error('You need to provide version as a string for addVersion method, ie. "1.0"')
16 |
17 | this.template.version = versionString
18 |
19 | return this
20 | }
21 |
22 | addSessionAttribute(key, value) {
23 | if (typeof key !== 'string' || typeof value === 'undefined')
24 | throw new Error('You need to provide both key and value for addSessionAttribute method')
25 |
26 | if (!this.template.sessionAttributes)
27 | this.template.sessionAttributes = {}
28 |
29 | this.template.sessionAttributes[key] = value
30 |
31 | return this
32 | }
33 |
34 | addOutputSpeech(type, text, isReprompt) {
35 | if (['PlainText', 'SSML'].indexOf(type) < 0)
36 | throw new Error('You need to provide type and it can be either "PlainText" or "SSML" for addOutputSpeech method')
37 |
38 | if (typeof text !== 'string')
39 | throw new Error('You need to provide text as a string for addText, addSSML and addOutputSpeech methods')
40 |
41 | if (typeof this.template.response.outputSpeech === 'object' && !isReprompt)
42 | throw new Error('You can call addText or addSSML only once')
43 |
44 | let obj = this.template.response
45 | if (isReprompt) {
46 | this.template.response.reprompt = {}
47 | obj = obj.reprompt
48 | }
49 |
50 | obj.outputSpeech = {
51 | type: type
52 | }
53 |
54 | obj.outputSpeech[type === 'SSML' ? 'ssml' : 'text'] = text
55 |
56 | return this
57 | }
58 |
59 | addText(text) {
60 | return this.addOutputSpeech('PlainText', text)
61 | }
62 |
63 | addSSML(ssmlString) {
64 | return this.addOutputSpeech('SSML', ssmlString)
65 | }
66 |
67 | addRepromptText(text) {
68 | return this.addOutputSpeech('PlainText', text, true)
69 | }
70 |
71 | addRepromptSSML(ssmlString) {
72 | return this.addOutputSpeech('SSML', ssmlString, true)
73 | }
74 |
75 | addSimpleCard(title, content) {
76 | if (typeof title !== 'string' || typeof content !== 'string')
77 | throw new Error('You need to provide title and content as strings for addSimpleCard method')
78 |
79 | this.template.response.card = {
80 | type: 'Simple',
81 | title: title,
82 | content: content
83 | }
84 |
85 | return this
86 | }
87 |
88 | addStandardCard(title, text, imageObject) {
89 | this.template.response.card = {
90 | type: 'Standard',
91 | title: title,
92 | text: text
93 | }
94 |
95 | if (typeof imageObject === 'object' && (imageObject.smallImageUrl || imageObject.largeImageUrl))
96 | this.template.response.card.image = imageObject
97 |
98 | return this
99 | }
100 |
101 | addDialogDelegate(updatedIntent) {
102 | if (!this.template.response.directives) {
103 | this.template.response.directives = []
104 | }
105 |
106 | const directive = {
107 | type: 'Dialog.Delegate'
108 | }
109 |
110 | if (updatedIntent) {
111 | directive.updatedIntent = updatedIntent
112 | }
113 |
114 | this.template.response.directives.push(directive)
115 |
116 | return this
117 | }
118 |
119 | addDialogElicitSlot(slot, updatedIntent) {
120 | if (!this.template.response.directives) {
121 | this.template.response.directives = []
122 | }
123 |
124 | const directive = {
125 | type: 'Dialog.ElicitSlot',
126 | slotToElicit: slot
127 | }
128 |
129 | if (updatedIntent) {
130 | directive.updatedIntent = updatedIntent
131 | }
132 |
133 | this.template.response.directives.push(directive)
134 |
135 | return this
136 | }
137 |
138 | addDialogConfirmSlot(slot, updatedIntent) {
139 | if (!this.template.response.directives) {
140 | this.template.response.directives = []
141 | }
142 |
143 | const directive = {
144 | type: 'Dialog.ConfirmSlot',
145 | slotToConfirm: slot
146 | }
147 |
148 | if (updatedIntent) {
149 | directive.updatedIntent = updatedIntent
150 | }
151 |
152 | this.template.response.directives.push(directive)
153 |
154 | return this
155 | }
156 |
157 | addDialogConfirmIntent(updatedIntent) {
158 | if (!this.template.response.directives) {
159 | this.template.response.directives = []
160 | }
161 |
162 | const directive = {
163 | type: 'Dialog.ConfirmIntent'
164 | }
165 |
166 | if (updatedIntent) {
167 | directive.updatedIntent = updatedIntent
168 | }
169 |
170 | this.template.response.directives.push(directive)
171 |
172 | return this
173 | }
174 |
175 | keepSession() {
176 | this.template.response.shouldEndSession = false
177 |
178 | return this
179 | }
180 |
181 |
182 | get() {
183 | return this.template
184 | }
185 | }
186 |
187 | module.exports = AlexaMessageBuilder
188 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "alexa-message-builder",
3 | "version": "1.1.0",
4 | "description": "Simple builder for Alexa replies.",
5 | "main": "index.js",
6 | "scripts": {
7 | "pretest": "eslint lib spec *.js",
8 | "test": "node spec/support/jasmine-runner.js",
9 | "debug": "node debug spec/support/jasmine-runner.js"
10 | },
11 | "keywords": [
12 | "alexa",
13 | "conversation",
14 | "message",
15 | "builder",
16 | "echo",
17 | "amazon"
18 | ],
19 | "author": "Slobodan Stojanovic (http://slobodan.me/)",
20 | "license": "MIT",
21 | "devDependencies": {
22 | "eslint": "^3.15.0",
23 | "eslint-config-defaults": "^9.0.0",
24 | "jasmine": "^2.5.3",
25 | "jasmine-spec-reporter": "^3.2.0"
26 | },
27 | "repository": {
28 | "type": "git",
29 | "url": "https://github.com/stojanovic/alexa-message-builder.git"
30 | },
31 | "bugs": {
32 | "url": "https://github.com/stojanovic/alexa-message-builder/issues"
33 | },
34 | "homepage": "https://github.com/stojanovic/alexa-message-builder"
35 | }
36 |
--------------------------------------------------------------------------------
/spec/message-builder-spec.js:
--------------------------------------------------------------------------------
1 | /* global describe, it, expect */
2 | 'use strict'
3 |
4 | const AlexaMessageBuilder = require('../lib')
5 |
6 | describe('Alexa Message Builder', () => {
7 | it('should be a class', () => {
8 | const message = new AlexaMessageBuilder()
9 | expect(typeof AlexaMessageBuilder).toBe('function')
10 | expect(message instanceof AlexaMessageBuilder).toBeTruthy()
11 | })
12 |
13 | it('should export an object when you invoke get method', () => {
14 | const message = new AlexaMessageBuilder().get()
15 | expect(message).toEqual({
16 | version: '1.0',
17 | response: {
18 | shouldEndSession: true
19 | }
20 | })
21 | })
22 |
23 | describe('addVersion', () => {
24 | it('should be a function', () => {
25 | const message = new AlexaMessageBuilder()
26 | expect(typeof message.addVersion).toBe('function')
27 | })
28 |
29 | it('should throw an error if version is not provided or not string', () => {
30 | const message = new AlexaMessageBuilder()
31 |
32 | expect(() => message.addVersion()).toThrowError('You need to provide version as a string for addVersion method, ie. "1.0"')
33 | expect(() => message.addVersion(1)).toThrowError('You need to provide version as a string for addVersion method, ie. "1.0"')
34 | expect(() => message.addVersion(1.0)).toThrowError('You need to provide version as a string for addVersion method, ie. "1.0"')
35 | expect(() => message.addVersion({})).toThrowError('You need to provide version as a string for addVersion method, ie. "1.0"')
36 | })
37 |
38 | it('should set a version if it is provided', () => {
39 | const message = new AlexaMessageBuilder()
40 |
41 | expect(message.addVersion('2.0') instanceof AlexaMessageBuilder).toBeTruthy()
42 | expect(message.addVersion('2.0').get()).toEqual({
43 | version: '2.0',
44 | response: {
45 | shouldEndSession: true
46 | }
47 | })
48 | })
49 | })
50 |
51 | describe('addSessionAttribute', () => {
52 | it('should be a function', () => {
53 | const message = new AlexaMessageBuilder()
54 | expect(typeof message.addSessionAttribute).toBe('function')
55 | })
56 |
57 | it('should throw an error if key is not string or value is not provided', () => {
58 | const message = new AlexaMessageBuilder()
59 | expect(() => message.addSessionAttribute()).toThrowError('You need to provide both key and value for addSessionAttribute method')
60 | expect(() => message.addSessionAttribute(1)).toThrowError('You need to provide both key and value for addSessionAttribute method')
61 | expect(() => message.addSessionAttribute('key')).toThrowError('You need to provide both key and value for addSessionAttribute method')
62 | })
63 |
64 | it('should add a session attribute', () => {
65 | const message = new AlexaMessageBuilder().addSessionAttribute('key', 'value')
66 | expect(message instanceof AlexaMessageBuilder).toBeTruthy()
67 | expect(message.get()).toEqual({
68 | version: '1.0',
69 | response: {
70 | shouldEndSession: true
71 | },
72 | sessionAttributes: {
73 | key: 'value'
74 | }
75 | })
76 | })
77 |
78 | it('should add multiple session attributes', () => {
79 | const message = new AlexaMessageBuilder()
80 | .addSessionAttribute('key1', 'value1')
81 | .addSessionAttribute('key2', 'value2')
82 |
83 | expect(message instanceof AlexaMessageBuilder).toBeTruthy()
84 | expect(message.get()).toEqual({
85 | version: '1.0',
86 | response: {
87 | shouldEndSession: true
88 | },
89 | sessionAttributes: {
90 | key1: 'value1',
91 | key2: 'value2'
92 | }
93 | })
94 | })
95 |
96 | it('should keep only the session attribute if they have the same key', () => {
97 | const message = new AlexaMessageBuilder()
98 | .addSessionAttribute('key', 'value1')
99 | .addSessionAttribute('key', 'value2')
100 |
101 | expect(message instanceof AlexaMessageBuilder).toBeTruthy()
102 | expect(message.get()).toEqual({
103 | version: '1.0',
104 | response: {
105 | shouldEndSession: true
106 | },
107 | sessionAttributes: {
108 | key: 'value2'
109 | }
110 | })
111 | })
112 | })
113 |
114 | describe('addOutputSpeech', () => {
115 | it('should be a function', () => {
116 | const message = new AlexaMessageBuilder()
117 | expect(typeof message.addOutputSpeech).toBe('function')
118 | })
119 |
120 | it('should throw an error if type is not PlainText, or SSML or value is not provided', () => {
121 | const message = new AlexaMessageBuilder()
122 | expect(() => message.addOutputSpeech()).toThrowError('You need to provide type and it can be either "PlainText" or "SSML" for addOutputSpeech method')
123 | expect(() => message.addOutputSpeech('test')).toThrowError('You need to provide type and it can be either "PlainText" or "SSML" for addOutputSpeech method')
124 | expect(() => message.addOutputSpeech(5)).toThrowError('You need to provide type and it can be either "PlainText" or "SSML" for addOutputSpeech method')
125 | })
126 |
127 | it('should throw an error if text is not stringr or value is not provided', () => {
128 | const message = new AlexaMessageBuilder()
129 | expect(() => message.addOutputSpeech('SSML')).toThrowError('You need to provide text as a string for addText, addSSML and addOutputSpeech methods')
130 | expect(() => message.addOutputSpeech('SSML', 5)).toThrowError('You need to provide text as a string for addText, addSSML and addOutputSpeech methods')
131 | })
132 |
133 | it('should throw an error if you call addText or addSSML more then once', () => {
134 | const message = new AlexaMessageBuilder()
135 | expect(() => message.addOutputSpeech('SSML', 'text').addOutputSpeech('SSML', 'text')).toThrowError('You can call addText or addSSML only once')
136 | })
137 |
138 | it('with reprompt', () => {
139 | const message = new AlexaMessageBuilder().addOutputSpeech('SSML', 'some text', true)
140 | expect(message.get()).toEqual({
141 | version: '1.0',
142 | response: {
143 | shouldEndSession: true,
144 | reprompt: {
145 | outputSpeech : {
146 | type: 'SSML',
147 | ssml: 'some text'
148 | }
149 | }
150 | }
151 | })
152 | })
153 |
154 | it('without reprompt', () => {
155 | const message = new AlexaMessageBuilder().addOutputSpeech('SSML', 'some text', false)
156 | expect(message.get()).toEqual({
157 | version: '1.0',
158 | response: {
159 | shouldEndSession: true,
160 | outputSpeech : {
161 | type: 'SSML',
162 | ssml: 'some text'
163 | }
164 | }
165 | })
166 | })
167 |
168 | it('before reprompt', () => {
169 | const message = new AlexaMessageBuilder().addOutputSpeech('SSML', 'some text', false).addRepromptSSML('some reprompt text')
170 | expect(message.get()).toEqual({
171 | version: '1.0',
172 | response: {
173 | shouldEndSession: true,
174 | outputSpeech : {
175 | type: 'SSML',
176 | ssml: 'some text'
177 | },
178 | reprompt: {
179 | outputSpeech : {
180 | type: 'SSML',
181 | ssml: 'some reprompt text'
182 | }
183 | }
184 | }
185 | })
186 | })
187 |
188 | })
189 |
190 | describe('addSimpleCard', () => {
191 | it('should be a function', () => {
192 | const message = new AlexaMessageBuilder()
193 | expect(typeof message.addSimpleCard).toBe('function')
194 | })
195 |
196 | it('should return error if title or content are not provided or if they are not a string', () => {
197 | const message = new AlexaMessageBuilder()
198 | expect(() => message.addSimpleCard()).toThrowError('You need to provide title and content as strings for addSimpleCard method')
199 | expect(() => message.addSimpleCard(5)).toThrowError('You need to provide title and content as strings for addSimpleCard method')
200 | expect(() => message.addSimpleCard(5, [])).toThrowError('You need to provide title and content as strings for addSimpleCard method')
201 | expect(() => message.addSimpleCard({}, 6)).toThrowError('You need to provide title and content as strings for addSimpleCard method')
202 | })
203 |
204 | it('should add simple card', () => {
205 | const message = new AlexaMessageBuilder().addSimpleCard('some title', 'some content')
206 | expect(message.get()).toEqual({
207 | version: '1.0',
208 | response: {
209 | shouldEndSession: true,
210 | card: {
211 | type: 'Simple',
212 | title: 'some title',
213 | content: 'some content'
214 | }
215 | }
216 | })
217 | })
218 | })
219 |
220 | describe('addStandardCard', () => {
221 | it('should be a function', () => {
222 | const message = new AlexaMessageBuilder()
223 | expect(typeof message.addStandardCard).toBe('function')
224 | })
225 |
226 | it('should add standard card', () => {
227 | const message = new AlexaMessageBuilder().addStandardCard('some title', 'some text', { smallImageUrl: 'http://example.com/small-image-url.png', largeImageUrl: 'http://example.com/large-image-url.png' })
228 | expect(message.get()).toEqual({
229 | version: '1.0',
230 | response: {
231 | shouldEndSession: true,
232 | card: {
233 | type: 'Standard',
234 | title: 'some title',
235 | text: 'some text',
236 | image: {smallImageUrl: 'http://example.com/small-image-url.png', largeImageUrl: 'http://example.com/large-image-url.png'}
237 | }
238 | }
239 | })
240 | })
241 | })
242 |
243 | describe('dialog directives', () => {
244 | const intent = {
245 | name: 'TestIntent',
246 | confirmationStatus: 'None',
247 | slots: { }
248 | }
249 |
250 | describe('addDialogDelegate', () => {
251 | it('should be a function', () => {
252 | const message = new AlexaMessageBuilder()
253 | expect(typeof message.addDialogDelegate).toBe('function')
254 | })
255 |
256 | it('should add a delegate directive', () => {
257 | const message = new AlexaMessageBuilder().addDialogDelegate(intent)
258 | expect(message.get()).toEqual({
259 | version: '1.0',
260 | response: {
261 | shouldEndSession: true,
262 | directives: [
263 | {
264 | type: 'Dialog.Delegate',
265 | updatedIntent: intent
266 | }
267 | ]
268 | }
269 | })
270 | })
271 | })
272 |
273 | describe('addDialogElicitSlot', () => {
274 | it('should be a function', () => {
275 | const message = new AlexaMessageBuilder()
276 | expect(typeof message.addDialogElicitSlot).toBe('function')
277 | })
278 |
279 | it('should add a elicit slot directive', () => {
280 | const message = new AlexaMessageBuilder().addDialogElicitSlot('slotname', intent)
281 | expect(message.get()).toEqual({
282 | version: '1.0',
283 | response: {
284 | shouldEndSession: true,
285 | directives: [
286 | {
287 | type: 'Dialog.ElicitSlot',
288 | slotToElicit: 'slotname',
289 | updatedIntent: intent
290 | }
291 | ]
292 | }
293 | })
294 | })
295 | })
296 |
297 | describe('addDialogConfirmSlot', () => {
298 | it('should be a function', () => {
299 | const message = new AlexaMessageBuilder()
300 | expect(typeof message.addDialogConfirmSlot).toBe('function')
301 | })
302 |
303 | it('should add a confirm slot directive', () => {
304 | const message = new AlexaMessageBuilder().addDialogConfirmSlot('slotname', intent)
305 | expect(message.get()).toEqual({
306 | version: '1.0',
307 | response: {
308 | shouldEndSession: true,
309 | directives: [
310 | {
311 | type: 'Dialog.ConfirmSlot',
312 | slotToConfirm: 'slotname',
313 | updatedIntent: intent
314 | }
315 | ]
316 | }
317 | })
318 | })
319 | })
320 |
321 | describe('addDialogConfirmIntent', () => {
322 | it('should be a function', () => {
323 | const message = new AlexaMessageBuilder()
324 | expect(typeof message.addDialogConfirmIntent).toBe('function')
325 | })
326 |
327 | it('should add a delegate directive', () => {
328 | const message = new AlexaMessageBuilder().addDialogConfirmIntent(intent)
329 | expect(message.get()).toEqual({
330 | version: '1.0',
331 | response: {
332 | shouldEndSession: true,
333 | directives: [
334 | {
335 | type: 'Dialog.ConfirmIntent',
336 | updatedIntent: intent
337 | }
338 | ]
339 | }
340 | })
341 | })
342 | })
343 | })
344 | })
345 |
--------------------------------------------------------------------------------
/spec/support/jasmine-runner.js:
--------------------------------------------------------------------------------
1 | /*global jasmine, require, process*/
2 | var Jasmine = require('jasmine'),
3 | SpecReporter = require('jasmine-spec-reporter').SpecReporter,
4 | noop = function () {},
5 | jrunner = new Jasmine(),
6 | filter
7 | process.argv.slice(2).forEach(option => {
8 | 'use strict'
9 | if (option === 'full') {
10 | jrunner.configureDefaultReporter({ print: noop }) // remove default reporter logs
11 | jasmine.getEnv().addReporter(new SpecReporter()) // add jasmine-spec-reporter
12 | }
13 | if (option.match('^filter=')) {
14 | filter = option.match('^filter=(.*)')[1]
15 | }
16 | })
17 | jrunner.loadConfigFile() // load jasmine.json configuration
18 | jrunner.execute(undefined, filter)
19 |
--------------------------------------------------------------------------------
/spec/support/jasmine.json:
--------------------------------------------------------------------------------
1 | {
2 | "spec_dir": "spec",
3 | "spec_files": [
4 | "**/*[sS]pec.js"
5 | ],
6 | "helpers": [
7 | "helpers/**/*.js"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------