Skip to content

Commit

Permalink
Suggestion for on-responded on prompts
Browse files Browse the repository at this point in the history
On issue #3947, relying on the responded flag todecide whether to show the prompt interferes with logic that could be happening either

a. Concurrently
b. On some middleware
c. As part of how we reached the prompt

I believe the *intention* is to prevent sending a prompt twice to the user, using a symbol may be enough for that, without relying on global status
  • Loading branch information
alexrecuenco committed Nov 16, 2021
1 parent a5ae17e commit 1463e9a
Show file tree
Hide file tree
Showing 7 changed files with 35 additions and 13 deletions.
5 changes: 4 additions & 1 deletion libraries/botbuilder-dialogs/src/prompts/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ export abstract class Prompt<T> extends Dialog {
return Dialog.EndOfTurn;
}

public static shouldNotReprompt = Symbol('shouldNotReprompt')

/**
* Called when a prompt dialog is the active dialog and the user replied with a new activity.
* @param dc The [DialogContext](xref:botbuilder-dialogs.DialogContext) for the current turn of conversation.
Expand Down Expand Up @@ -238,6 +240,7 @@ export abstract class Prompt<T> extends Dialog {

// Validate the return value
let isValid = false;

if (this.validator) {
if (state.state['attemptCount'] === undefined) {
state.state['attemptCount'] = 0;
Expand All @@ -257,7 +260,7 @@ export abstract class Prompt<T> extends Dialog {
if (isValid) {
return await dc.endDialog(recognized.value);
} else {
if (!dc.context.responded) {
if (!dc.context.turnState.get(Prompt.shouldNotReprompt)) {
await this.onPrompt(dc.context, state.state, state.options, true);
}

Expand Down
3 changes: 2 additions & 1 deletion libraries/botbuilder-dialogs/tests/attachmentPrompt.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ describe('AttachmentPrompt', function () {
.startTest();
});

it('should send ignore retryPrompt if validator replies.', async function () {
it('should send retryPrompt even if validator replies.', async function () {
const adapter = new TestAdapter(async (turnContext) => {
const dc = await dialogs.createContext(turnContext);

Expand Down Expand Up @@ -150,6 +150,7 @@ describe('AttachmentPrompt', function () {
.assertReply('Please send an attachment.')
.send(invalidMessage)
.assertReply('Bad input.')
.assertReply('Please try again.')
.send(answerMessage)
.assertReply('test1')
.startTest();
Expand Down
6 changes: 4 additions & 2 deletions libraries/botbuilder-dialogs/tests/choicePrompt.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ describe('ChoicePrompt', function () {
.startTest();
});

it('should send ignore retryPrompt if validator replies.', async function () {
it('should send retryPrompt even if validator replies.', async function () {
const adapter = new TestAdapter(async (turnContext) => {
const dc = await dialogs.createContext(turnContext);

Expand Down Expand Up @@ -255,6 +255,7 @@ describe('ChoicePrompt', function () {
.assertReply('Please choose a color.')
.send(invalidMessage)
.assertReply('bad input.')
.assertReply('Please choose red, blue, or green.')
.send(answerMessage)
.assertReply('red')
.startTest();
Expand All @@ -266,7 +267,7 @@ describe('ChoicePrompt', function () {

const results = await dc.continueDialog();
if (results.status === DialogTurnStatus.empty) {
await dc.prompt('prompt', { prompt: 'Please choose a color.', choices: stringChoices });
await dc.prompt('prompt', { prompt: 'Please choose a color.', choices: stringChoices, retryPrompt: 'Try again.' });
} else if (results.status === DialogTurnStatus.complete) {
const selectedChoice = results.result;
await turnContext.sendActivity(selectedChoice.value);
Expand Down Expand Up @@ -297,6 +298,7 @@ describe('ChoicePrompt', function () {
})
.send(invalidMessage)
.assertReply('bad input.')
.assertReply((a)=> (assert(a.text.startsWith('Try again.'))))
.send({ text: 'red', type: ActivityTypes.Message, locale: undefined })
.assertReply('red')
.startTest();
Expand Down
17 changes: 12 additions & 5 deletions libraries/botbuilder-dialogs/tests/confirmPrompt.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { ActivityTypes, ConversationState, MemoryStorage, TestAdapter } = require('botbuilder-core');
const { ConfirmPrompt, DialogSet, DialogTurnStatus, ListStyle } = require('../');
const { ConfirmPrompt, DialogSet, DialogTurnStatus, ListStyle, Prompt } = require('../');
const assert = require('assert');

const beginMessage = { text: `begin`, type: 'message' };
Expand Down Expand Up @@ -139,7 +139,7 @@ describe('ConfirmPrompt', function () {
.startTest();
});

it('should ignore retryPrompt if validator replies.', async function () {
it('should ignore retryPrompt if validator sets no-reply flag.', async function () {
const adapter = new TestAdapter(async (turnContext) => {
const dc = await dialogs.createContext(turnContext);

Expand All @@ -163,20 +163,26 @@ describe('ConfirmPrompt', function () {
assert(prompt, `PromptValidatorContext not found.`);
if (!prompt.recognized.succeeded) {
await prompt.context.sendActivity('The correct response is either yes or no. Please choose one.')
prompt.context.turnState.set(Prompt.shouldNotReprompt, true)
console.log('prompt.context.turnState', prompt.context.turnState.get(Prompt.shouldNotReprompt, true))
}
return prompt.recognized.succeeded;
});
confirmPrompt.style = ListStyle.none;
dialogs.add(confirmPrompt);

console.log('hellooo');


await adapter.send('Hello')
.assertReply('Please confirm. Yes or No')
.send('what?')
.assertReply('The correct response is either yes or no. Please choose one.')
.send('no')
.assertReply(`The result found is 'false'.`)
//.assertReply(`The result found is 'false'.`)
.startTest();

console.log(adapter.activeQueue)
});

it('should not send any retryPrompt if no prompt is specified.', async function () {
Expand Down Expand Up @@ -310,11 +316,12 @@ describe('ConfirmPrompt', function () {
}, 'ja-jp');
dialogs.add(confirmPrompt);

await adapter.send({ text: 'Hello', type: ActivityTypes.Message })
await adapter.send('Hello')
.assertReply('Please confirm. (1) はい または (2) いいえ')
.send(invalidMessage)
.assertReply('bad input.')
.send({ text: 'はい', type: ActivityTypes.Message })
.assertReply((a)=> (assert(a.text.startsWith('Please confirm.'))))
.send('はい')
.assertReply('true')
.startTest();
});
Expand Down
3 changes: 2 additions & 1 deletion libraries/botbuilder-dialogs/tests/datetimePrompt.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ describe('DatetimePrompt', function () {
.startTest();
});

it('should send ignore retryPrompt if validator replies.', async function () {
it('should send retryPrompt even if validator replies.', async function () {
const adapter = new TestAdapter(async (turnContext) => {
const dc = await dialogs.createContext(turnContext);

Expand Down Expand Up @@ -156,6 +156,7 @@ describe('DatetimePrompt', function () {
.assertReply('Please say something.')
.send(invalidMessage)
.assertReply('That was a bad date.')
.assertReply('Please provide a valid datetime.')
.send(answerMessage2)
.assertReply('2012-09-02')
.startTest();
Expand Down
9 changes: 8 additions & 1 deletion libraries/botbuilder-dialogs/tests/numberPrompt.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ describe('NumberPrompt', function () {
.startTest();
});

it('should send ignore retryPrompt if validator replies.', async function () {
it('should send retryPrompt even if validator replies.', async function () {
const adapter = createAdapter(
(dialogContext) =>
dialogContext.prompt('prompt', {
Expand All @@ -110,6 +110,7 @@ describe('NumberPrompt', function () {
.assertReply('Please send a number.')
.send('-1')
.assertReply('out of range')
.assertReply('Please send a number between 1 and 100.')
.send('67')
.assertReply('67')
.startTest();
Expand Down Expand Up @@ -174,20 +175,26 @@ describe('NumberPrompt', function () {
.assertReply('Send me a zero')
.send('100')
.assertReply('attemptCount 1')
.assertReply('Send 0 or zero')
.send('200')
.assertReply('attemptCount 2')
.assertReply('Send 0 or zero')
.send('300')
.assertReply('attemptCount 3')
.assertReply('Send 0 or zero')
.send('0')
.assertReply('ok')
.send('Another!')
.assertReply('Send me a zero')
.send('100')
.assertReply('attemptCount 1')
.assertReply('Send 0 or zero')
.send('200')
.assertReply('attemptCount 2')
.assertReply('Send 0 or zero')
.send('300')
.assertReply('attemptCount 3')
.assertReply('Send 0 or zero')
.send('0')
.assertReply('ok')
.startTest();
Expand Down
5 changes: 3 additions & 2 deletions libraries/botbuilder-dialogs/tests/textPrompt.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,13 +130,13 @@ describe('TextPrompt', function() {
.startTest();
});

it('should send ignore retryPrompt if validator replies.', async function() {
it('should send retryPrompt even if validator replies.', async function() {
const adapter = new TestAdapter(async (turnContext) => {
const dc = await dialogs.createContext(turnContext);

const results = await dc.continueDialog();
if (results.status === DialogTurnStatus.empty) {
await dc.prompt('prompt', { prompt: 'Please say something.', retryPrompt: 'Text is required.' });
await dc.prompt('prompt', { prompt: 'Please say something.', retryPrompt: 'Try again.' });
} else if (results.status === DialogTurnStatus.complete) {
const reply = results.result;
await turnContext.sendActivity(reply);
Expand All @@ -161,6 +161,7 @@ describe('TextPrompt', function() {
.assertReply('Please say something.')
.send('i')
.assertReply('too short')
.assertReply('Try again.')
.send('test')
.assertReply('test')
.startTest();
Expand Down

0 comments on commit 1463e9a

Please sign in to comment.