From fefc893c43ce00ea4e678400c06f53b7c0a4397c Mon Sep 17 00:00:00 2001 From: Tony MacDonell Date: Mon, 5 Nov 2018 20:53:51 -0500 Subject: [PATCH] added direct selection capabilities --- package-lock.json | 2 +- package.json | 2 +- src/convo-app.js | 9 +++++++ src/convo.js | 34 +++++++++++++++++++++++++-- test/convo-app.js | 31 ++++++++++++++++++++---- test/convo.js | 60 +++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 129 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index b7f1dae..44db3c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@tmtek/convo", - "version": "0.2.19", + "version": "0.2.21", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index d09419b..f15bfe6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@tmtek/convo", - "version": "0.2.21", + "version": "0.2.22", "description": "A uility for building conversational responses in DialogFlow fufillments", "main": "index.js", "scripts": { diff --git a/src/convo-app.js b/src/convo-app.js index 0cbea33..3ebc675 100644 --- a/src/convo-app.js +++ b/src/convo-app.js @@ -139,6 +139,11 @@ class ConvoApp { .forListPage(data => this._onRespondForList(data)); } + presentSelection(convo, type, item) { + return convo.select(type, item) + .forSelection(data => this.onRespondForSelection(data)); + } + onListSelectUI(convo, type, itemName) { return convo.selectFromListPage(ConvoApp.ensureNumber(itemName.split('_')[1])); } @@ -166,6 +171,10 @@ class ConvoApp { } onRespondForListSelection({ convo, type, item }) { + return this.onRespondForSelection({ convo, type, item }); + } + + onRespondForSelection({ convo, type, item }) { return convo.speak('Nothing was selected'); } diff --git a/src/convo.js b/src/convo.js index 48836e9..8a01a76 100644 --- a/src/convo.js +++ b/src/convo.js @@ -414,6 +414,36 @@ class Convo { return this; } + getSelection(type) { + return this.getContext('selection'); + } + + hasSelection(type) { + return this.getContext('selection') && (!type || this.getContext('selection').type === type); + } + + clearSelection() { + let selection = this.getSelection(); + if (selection) { + this.setContext('selection', 0, null); + this.setContext(`selected_${selection.type}`, 0, null); + } + } + + select(type, item) { + this.setContext('selection', 10, { type, item }); + this.setContext(`selected_${type}`, 10, { active: true }); + return this; + } + + forSelection(func) { + if (this.hasSelection()) { + let { item, type } = this.getSelection(); + func({ convo: this, item, type }); + } + return this; + } + selectFromList(index = 0){ if (!this.hasList()) { throw new Error('Can\'t select an item if there\'s no list.'); @@ -427,7 +457,7 @@ class Convo { } listContext.selectedIndex = index; this.setContext('list', 10, listContext); - this.setContext(`list_select_${listContext.type}`, 10, { active: true }); + this.select(listContext.type, listContext.list[listContext.selectedIndex]); return this; } @@ -458,7 +488,7 @@ class Convo { let listContext = this.getContext('list'); if (listContext) { this.getContext('list').selectedIndex = -1; - this.setContext(`list_select_${listContext.type}`, 0, null); + this.clearSelection(); } return this; } diff --git a/test/convo-app.js b/test/convo-app.js index f1714fb..80c6ff1 100644 --- a/test/convo-app.js +++ b/test/convo-app.js @@ -241,6 +241,27 @@ describe('ConvoApp', () => { }); }); + describe('#presentSelection', () => { + it('Should call onRespondForSelection when selection is presented', (done) => { + class MyApp extends ConvoApp { + onRegisterIntents(){ + this.registerIntent('selectThing', (convo, params, option) => Convo.ask( + this.presentSelection(convo, 'thing', { value: 'test' }) + )); + } + onRespondForSelection({ convo, type, item }) { + assert(convo); + assert(type === 'thing'); + assert(item); + assert(item.value === 'test'); + done(); + } + + } + new MyApp().intent(new Convo(), 'selectThing'); + }); + }); + describe('#registerListIntents', () => { it('Should be able to repeat the list with list_repeat', (done) => { let list = ['test 1', 'test 2', 'test 3', 'test 4', 'test 5']; @@ -608,7 +629,7 @@ describe('ConvoApp', () => { listLength: list.length })); } - onRespondForListSelection({ convo, type, item }) { + onRespondForSelection({ convo, type, item }) { return convo.speak(JSON.stringify({ type, item @@ -645,7 +666,7 @@ describe('ConvoApp', () => { listLength: list.length })); } - onRespondForListSelection({ convo, type, item }) { + onRespondForSelection({ convo, type, item }) { return convo.speak(JSON.stringify({ type, item @@ -682,7 +703,7 @@ describe('ConvoApp', () => { listLength: list.length })); } - onRespondForListSelection({ convo, type, item }) { + onRespondForSelection({ convo, type, item }) { return convo.speak(JSON.stringify({ type, item @@ -719,7 +740,7 @@ describe('ConvoApp', () => { listLength: list.length })); } - onRespondForListSelection({ convo, type, item }) { + onRespondForSelection({ convo, type, item }) { return convo.speak(JSON.stringify({ type, item @@ -788,7 +809,7 @@ describe('ConvoApp', () => { listLength: list.length })); } - onRespondForListSelection({ convo, type, item }) { + onRespondForSelection({ convo, type, item }) { return convo.speak(JSON.stringify({ type, item diff --git a/test/convo.js b/test/convo.js index cd46da3..2730754 100644 --- a/test/convo.js +++ b/test/convo.js @@ -610,6 +610,43 @@ describe('Convo', () => { }); }); + describe('#select', () => { + it('Should select an item and report back on it.', () => { + let convo = new Convo().select('thing', { value: 'the thing' }); + assert(convo.hasSelection()); + assert(convo.hasSelection('thing')); + assert(convo.getSelection().item.value === 'the thing'); + }); + it('Should respond back negatively when there is no selection.', () => { + let convo = new Convo(); + assert(!convo.hasSelection()); + assert(!convo.hasSelection('thing')); + assert(convo.getSelection() === null); + }); + it('Should be able to discriminate on types.', () => { + let convo = new Convo().select('thing', { value: 'the thing' }); + assert(convo.hasSelection()); + assert(!convo.hasSelection('thing2')); + }); + it('Should be able to clear selection.', () => { + let convo = new Convo().select('thing', { value: 'the thing' }); + assert(convo.hasSelection()); + assert(convo.hasSelection('thing')); + assert(convo.getSelection().item.value === 'the thing'); + convo.clearSelection(); + assert(!convo.hasSelection()); + assert(!convo.hasSelection('thing')); + assert(convo.getSelection() === null); + }); + it('Should be to present selection.', () => { + let convo = new Convo().select('thing', { value: 'the thing' }); + convo.forSelection(({ type, item }) => { + assert(type === 'thing'); + assert(item.value === 'the thing'); + }); + }); + }); + describe('#setList', () => { it('Should throw an error if a type or a list is not passed.', () => { assert.throws(() => new Convo().setList()); @@ -1157,6 +1194,29 @@ describe('Convo', () => { `there should not be a selection.${JSON.stringify(convo.getListSelection(), null, 2)}` ); }); + it('Should have list selection and selection match when selection is from list.', () => { + let convo = new Convo() + .setList('items', ['item 1', 'item 2', 'item 3']) + .selectFromList(0); + assert( + convo.getListSelection().item === convo.getSelection().item, + `there should not be a selection.${JSON.stringify(convo.getListSelection(), null, 2)}` + ); + }); + it('Should not have list selection and selection match when selection is not from list.', () => { + let convo = new Convo() + .setList('items', ['item 1', 'item 2', 'item 3']) + .selectFromList(0) + .select('thing', 'item 4'); + assert(convo.getListSelection().item !== convo.getSelection().item); + }); + it('Should have list section override a direct selection.', () => { + let convo = new Convo() + .setList('items', ['item 1', 'item 2', 'item 3']) + .select('thing', 'item 4') + .selectFromList(0); + assert(convo.getListSelection().item === convo.getSelection().item); + }); }); describe('#forListSelection', () => {