diff --git a/index.js b/index.js index 611f07f..36b999e 100644 --- a/index.js +++ b/index.js @@ -3,5 +3,6 @@ const { Convo } = require('./src/convo'); const { ConvoApp } = require('./src/convo-app'); const { Say } = require('./src/say'); const { ConvoStorage } = require('./src/convo-storage'); +const { ConvoTest } = require('./src/convo-test'); -module.exports = { Convo, ConvoApp, Say, ConvoStorage }; +module.exports = { Convo, ConvoApp, Say, ConvoStorage, ConvoTest }; diff --git a/src/convo-test.js b/src/convo-test.js new file mode 100644 index 0000000..afd79d1 --- /dev/null +++ b/src/convo-test.js @@ -0,0 +1,34 @@ + +class ConvoTest { + static containsResponseType(requests, types) { + if (!requests) { + throw new Error('Requests were not supplied.'); + } + if (!types || types.length === 0) { + return false; + } + return types + .map(type => requests.filter(request => request.payload.type === type).length > 0) + .filter(typeVal => !typeVal) + .length === 0; + } + static isConversationClose(requests) { + return requests[0] && requests[0].action === 'close'; + } + static testConversation(conversation, done) { + if (!conversation || !conversation.then) { + throw new Error('You must pass a conversational promise.'); + } + if (!done) { + throw new Error('You must pass a done function.'); + } + let isDone = false; + conversation.catch(err => { + isDone = true; + done(err); + }) + .then(() => !isDone && done()); + } +} + +module.exports = { ConvoTest }; diff --git a/test/convo-app.js b/test/convo-app.js index 47c5197..f1714fb 100644 --- a/test/convo-app.js +++ b/test/convo-app.js @@ -1,5 +1,6 @@ let { Convo } = require('../src/convo'); let { ConvoApp } = require('../src/convo-app'); +let { ConvoTest } = require('../src/convo-test'); let assert = require('assert'); describe('ConvoApp', () => { @@ -261,8 +262,7 @@ describe('ConvoApp', () => { } } - let didError = false; - new MyApp() + ConvoTest.testConversation(new MyApp() .intent(new Convo(), 'applyList') .then(({ app, convo, requests }) => app.intent(new Convo(convo), 'list_repeat')) .then(({ app, convo, requests }) => { @@ -275,11 +275,7 @@ describe('ConvoApp', () => { assert(listLength === 5); return { app, convo, requests }; }) - .catch(err => { - didError = true; - return done(err); - }) - .then(() => !didError && done()); + , done); }); it('Should be able to page through list with list_next and have it loop back.', (done) => { let list = ['test 1', 'test 2', 'test 3', 'test 4', 'test 5']; @@ -301,7 +297,7 @@ describe('ConvoApp', () => { } } - new MyApp() + ConvoTest.testConversation(new MyApp() .intent(new Convo(), 'applyList') .then(({ app, convo, requests }) => { assert(requests && requests.length > 0); @@ -346,7 +342,7 @@ describe('ConvoApp', () => { assert(listLength === 5); return { app, convo, requests }; }) - .then(() => done()); + , done); }); it('Should be able to page through list with list_prev and have it loop back.', (done) => { let list = ['test 1', 'test 2', 'test 3', 'test 4', 'test 5']; @@ -368,8 +364,7 @@ describe('ConvoApp', () => { } } - let didError = false; - new MyApp() + ConvoTest.testConversation(new MyApp() .intent(new Convo(), 'applyList') .then(({ app, convo, requests }) => { assert(requests && requests.length > 0); @@ -414,11 +409,7 @@ describe('ConvoApp', () => { assert(listLength === 5); return { app, convo, requests }; }) - .catch(err => { - didError = true; - return done(err); - }) - .then(() => !didError && done()); + , done); }); it('Should be able to page through list with list_next and have it loop back while changing page sizes.', (done) => { let list = ['test 1', 'test 2', 'test 3', 'test 4', 'test 5']; @@ -440,7 +431,7 @@ describe('ConvoApp', () => { } } - new MyApp() + ConvoTest.testConversation(new MyApp() .intent(new Convo(), 'applyList') .then(({ app, convo, requests }) => { assert(requests && requests.length > 0); @@ -485,7 +476,7 @@ describe('ConvoApp', () => { assert(listLength === 5); return { app, convo, requests }; }) - .then(() => done()); + , done); }); it('Should be able to page through list with list_prev and have it loop back while changing page sizes.', (done) => { let list = ['test 1', 'test 2', 'test 3', 'test 4', 'test 5']; @@ -507,8 +498,7 @@ describe('ConvoApp', () => { } } - let didError = false; - new MyApp() + ConvoTest.testConversation(new MyApp() .intent(new Convo(), 'applyList') .then(({ app, convo, requests }) => { assert(requests && requests.length > 0); @@ -553,11 +543,7 @@ describe('ConvoApp', () => { assert(listLength === 5); return { app, convo, requests }; }) - .catch(err => { - didError = true; - return done(err); - }) - .then(() => !didError && done()); + , done); }); it('Should be able to use list_all to move from a paged list to full list.', (done) => { let list = ['test 1', 'test 2', 'test 3', 'test 4', 'test 5']; @@ -579,8 +565,7 @@ describe('ConvoApp', () => { } } - let didError = false; - new MyApp() + ConvoTest.testConversation(new MyApp() .intent(new Convo(), 'applyList') .then(({ app, convo, requests }) => { assert(requests && requests.length > 0); @@ -603,11 +588,7 @@ describe('ConvoApp', () => { assert(listLength === 5); return { app, convo, requests }; }) - .catch(err => { - didError = true; - return done(err); - }) - .then(() => !didError && done()); + , done); }); it('Should be able to use list_select to select an item in the full list.', (done) => { let list = ['test 1', 'test 2', 'test 3', 'test 4', 'test 5']; @@ -634,8 +615,7 @@ describe('ConvoApp', () => { })); } } - let didError = false; - new MyApp() + ConvoTest.testConversation(new MyApp() .intent(new Convo(), 'applyList') .then(({ app, convo, requests }) => app.intent(new Convo(convo), 'list_select', { index: 2 })) .then(({ app, convo, requests }) => { @@ -645,11 +625,7 @@ describe('ConvoApp', () => { assert(item === 'test 2', `Was actually ${item}`); return { app, convo, requests }; }) - .catch(err => { - didError = true; - return done(err); - }) - .then(() => !didError && done()); + , done); }); it('Should be able to use list_select_ui to select an item in the full list.', (done) => { let list = ['test 1', 'test 2', 'test 3', 'test 4', 'test 5']; @@ -676,8 +652,7 @@ describe('ConvoApp', () => { })); } } - let didError = false; - new MyApp() + ConvoTest.testConversation(new MyApp() .intent(new Convo(), 'applyList') .then(({ app, convo, requests }) => app.intent(new Convo(convo), 'list_select_ui', null, 'item_1')) .then(({ app, convo, requests }) => { @@ -687,11 +662,7 @@ describe('ConvoApp', () => { assert(item === 'test 2', `Was actually ${item}`); return { app, convo, requests }; }) - .catch(err => { - didError = true; - return done(err); - }) - .then(() => !didError && done()); + , done); }); it('Should be able to use list_select to select an item in on a list page.', (done) => { let list = ['test 1', 'test 2', 'test 3', 'test 4', 'test 5']; @@ -718,8 +689,7 @@ describe('ConvoApp', () => { })); } } - let didError = false; - new MyApp() + ConvoTest.testConversation(new MyApp() .intent(new Convo(), 'applyList') .then(({ app, convo, requests }) => app.intent(new Convo(convo), 'list_select', { index: 2 })) .then(({ app, convo, requests }) => { @@ -729,11 +699,7 @@ describe('ConvoApp', () => { assert(item === 'test 4', `Was actually ${item}`); return { app, convo, requests }; }) - .catch(err => { - didError = true; - return done(err); - }) - .then(() => !didError && done()); + , done); }); it('Should be able select the next item after a currently select one with list_select_next and have it loop.', (done) => { let list = ['test 1', 'test 2', 'test 3', 'test 4', 'test 5']; @@ -760,8 +726,7 @@ describe('ConvoApp', () => { })); } } - let didError = false; - new MyApp() + ConvoTest.testConversation(new MyApp() .intent(new Convo(), 'applyList') .then(({ app, convo, requests }) => app.intent(new Convo(convo), 'list_select', { index: 2 })) .then(({ app, convo, requests }) => { @@ -803,11 +768,7 @@ describe('ConvoApp', () => { assert(item === 'test 1', `Was actually ${item}`); return { app, convo, requests }; }) - .catch(err => { - didError = true; - return done(err); - }) - .then(() => !didError && done()); + , done); }); it('Should be able select the previous item before a currently select one with list_select_prev and have it loop.', (done) => { let list = ['test 1', 'test 2', 'test 3', 'test 4', 'test 5']; @@ -834,8 +795,8 @@ describe('ConvoApp', () => { })); } } - let didError = false; - new MyApp() + + ConvoTest.testConversation(new MyApp() .intent(new Convo(), 'applyList') .then(({ app, convo, requests }) => app.intent(new Convo(convo), 'list_select', { index: 2 })) .then(({ app, convo, requests }) => { @@ -877,11 +838,7 @@ describe('ConvoApp', () => { assert(item === 'test 3', `Was actually ${item}`); return { app, convo, requests }; }) - .catch(err => { - didError = true; - return done(err); - }) - .then(() => !didError && done()); + , done); }); }); @@ -921,19 +878,14 @@ describe('ConvoApp', () => { ]; } } - let didError = false; - new MyApp() + ConvoTest.testConversation(new MyApp() .intent(new Convo(), 'help') .then(({ app, convo, requests }) => { assert(requests && requests.length > 0); assert(convo.hasList('help')); return { app, convo, requests }; }) - .catch(err => { - didError = true; - return done(err); - }) - .then(() => !didError && done()); + , done); }); }); diff --git a/test/convo-test.js b/test/convo-test.js new file mode 100644 index 0000000..edfd08e --- /dev/null +++ b/test/convo-test.js @@ -0,0 +1,111 @@ +let { ConvoTest } = require('../src/convo-test'); +let { ConvoApp } = require('../src/convo-app'); +let { Convo } = require('../src/convo'); +let assert = require('assert'); + +describe('ConvoTest', () => { + describe('#containsResponseType', () => { + it('Should return true if requests contain the supplied type.', () => { + let requests = [ + { action: 'ask', payload: { type: 'SimpleResponse' } }, + { action: 'ask', payload: { type: 'List' } } + ]; + assert(ConvoTest.containsResponseType(requests, ['SimpleResponse'])); + assert(ConvoTest.containsResponseType(requests, ['List'])); + assert(ConvoTest.containsResponseType(requests, ['SimpleResponse', 'List'])); + }); + it('Should return false if a named type does not exist in the requests.', () => { + let requests = [ + { action: 'ask', payload: { type: 'SimpleResponse' } }, + { action: 'ask', payload: { type: 'List' } } + ]; + assert(!ConvoTest.containsResponseType(requests, ['Card'])); + assert(!ConvoTest.containsResponseType(requests, ['SimpleResponse', 'Card'])); + assert(!ConvoTest.containsResponseType(requests, ['List', 'Card'])); + }); + it('Should throw error if responses are null', () => { + assert.throws(() => ConvoTest.containsResponseType(null, ['SimpleResponse'])); + }); + it('Should return false if requests are empty', () => { + assert(!ConvoTest.containsResponseType([], ['SimpleResponse'])); + }); + it('Should return false if no types are supplied', () => { + let requests = [ + { action: 'ask', payload: { type: 'SimpleResponse' } }, + { action: 'ask', payload: { type: 'List' } } + ]; + assert(!ConvoTest.containsResponseType(requests, [])); + assert(!ConvoTest.containsResponseType(requests)); + }); + }); + describe('#isConversationClose', () => { + it('Should return false if the requests are asks.', () => { + let requests = [ + { action: 'ask', payload: { type: 'SimpleResponse' } }, + { action: 'ask', payload: { type: 'List' } } + ]; + assert(!ConvoTest.isConversationClose(requests)); + }); + it('Should return true if the requests are close.', () => { + let requests = [ + { action: 'close', payload: { type: 'SimpleResponse' } } + ]; + assert(ConvoTest.isConversationClose(requests)); + }); + it('Should return true if the first request is close.', () => { + let requests = [ + { action: 'close', payload: { type: 'SimpleResponse' } }, + { action: 'ask', payload: { type: 'List' } } + ]; + assert(ConvoTest.isConversationClose(requests)); + }); + it('Should throw error if you don\'t supply requests.', () => { + assert.throws(() => !ConvoTest.isConversationClose()); + }); + it('Should return false if requests are empty.', () => { + assert(!ConvoTest.isConversationClose([])); + }); + }); + describe('#testConversation', () => { + it('Should complete when an intent is simulated', (done) => { + let app = new ConvoApp() + .registerIntent('welcome', ( convo, params, option ) => {Convo.ask(convo.speak('Hi'));}); + ConvoTest.testConversation( + app.intent(new Convo(), 'welcome'), + done + ); + }); + it('Should complete when multiple intents are simulated', (done) => { + let app = new ConvoApp() + .registerIntent('welcome', ( convo, params, option ) => {Convo.ask(convo.speak('Hi'));}) + .registerIntent('another_intent', ( convo, params, option ) => {Convo.ask(convo.speak('Hi'));}); + ConvoTest.testConversation( + app.intent(new Convo(), 'welcome') + .then(({ app, convo }) => app.intent(new Convo(convo), 'another_intent')), + done + ); + }); + it('Should fail when an error is thrown', (done) => { + let app = new ConvoApp() + .registerIntent('welcome', ( convo, params, option ) => {Convo.ask(convo.speak('Hi'));}); + ConvoTest.testConversation( + app.intent(new Convo(), 'welcome') + .then(() => { + throw new Error('Test Error'); + }), + (potentialError) => {potentialError ? done() : done('We did not catch the error that was thrown.');} + ); + }); + it('Should throw error if conversation promise not passed.', () => { + assert.throws(() => ConvoTest.testConversation(null, (error) => {})); + assert.throws(() => ConvoTest.testConversation({}, (error) => {})); + }); + it('Should throw error if done function not passed', () => { + let app = new ConvoApp() + .registerIntent('welcome', ( convo, params, option ) => {Convo.ask(convo.speak('Hi'));}); + assert.throws(() => ConvoTest.testConversation( + app.intent(new Convo(), 'welcome') + )); + }); + }); +});