Repository: microsoft/botbuilder-python

Issue:
[PORT] Add RecognizeOrdinals and RecognizeNumbers flags to FindChoicesOptions
> Port this change from botbuilder-dotnet/master branch:
https://github.com/microsoft/botbuilder-dotnet/pull/3216

Fixes https://github.com/microsoft/botbuilder-dotnet/issues/2956

and

https://github.com/microsoft/botbuilder-dotnet/issues/3039


Discussion:

Resolving patch:
diff --git a/libraries/botbuilder-dialogs/botbuilder/dialogs/choices/choice_recognizers.py b/libraries/botbuilder-dialogs/botbuilder/dialogs/choices/choice_recognizers.py
--- a/libraries/botbuilder-dialogs/botbuilder/dialogs/choices/choice_recognizers.py
+++ b/libraries/botbuilder-dialogs/botbuilder/dialogs/choices/choice_recognizers.py
@@ -1,140 +1,141 @@
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-
-from typing import List, Union
-from recognizers_number import NumberModel, NumberRecognizer, OrdinalModel
-from recognizers_text import Culture
-
-
-from .choice import Choice
-from .find import Find
-from .find_choices_options import FindChoicesOptions
-from .found_choice import FoundChoice
-from .model_result import ModelResult
-
-
-class ChoiceRecognizers:
-    """ Contains methods for matching user input against a list of choices. """
-
-    @staticmethod
-    def recognize_choices(
-        utterance: str,
-        choices: List[Union[str, Choice]],
-        options: FindChoicesOptions = None,
-    ) -> List[ModelResult]:
-        """
-        Matches user input against a list of choices.
-
-        This is layered above the `Find.find_choices()` function, and adds logic to let the user specify
-        their choice by index (they can say "one" to pick `choice[0]`) or ordinal position
-         (they can say "the second one" to pick `choice[1]`.)
-        The user's utterance is recognized in the following order:
-
-        - By name using `find_choices()`
-        - By 1's based ordinal position.
-        - By 1's based index position.
-
-        Parameters:
-        -----------
-
-        utterance: The input.
-
-        choices: The list of choices.
-
-        options: (Optional) Options to control the recognition strategy.
-
-        Returns:
-        --------
-        A list of found choices, sorted by most relevant first.
-        """
-        if utterance is None:
-            utterance = ""
-
-        # Normalize list of choices
-        choices_list = [
-            Choice(value=choice) if isinstance(choice, str) else choice
-            for choice in choices
-        ]
-
-        # Try finding choices by text search first
-        # - We only want to use a single strategy for returning results to avoid issues where utterances
-        #   like the "the third one" or "the red one" or "the first division book" would miss-recognize as
-        #   a numerical index or ordinal as well.
-        locale = options.locale if (options and options.locale) else Culture.English
-        matched = Find.find_choices(utterance, choices_list, options)
-        if not matched:
-            # Next try finding by ordinal
-            matches = ChoiceRecognizers._recognize_ordinal(utterance, locale)
-
-            if matches:
-                for match in matches:
-                    ChoiceRecognizers._match_choice_by_index(
-                        choices_list, matched, match
-                    )
-            else:
-                # Finally try by numerical index
-                matches = ChoiceRecognizers._recognize_number(utterance, locale)
-
-                for match in matches:
-                    ChoiceRecognizers._match_choice_by_index(
-                        choices_list, matched, match
-                    )
-
-            # Sort any found matches by their position within the utterance.
-            # - The results from find_choices() are already properly sorted so we just need this
-            #   for ordinal & numerical lookups.
-            matched = sorted(matched, key=lambda model_result: model_result.start)
-
-        return matched
-
-    @staticmethod
-    def _recognize_ordinal(utterance: str, culture: str) -> List[ModelResult]:
-        model: OrdinalModel = NumberRecognizer(culture).get_ordinal_model(culture)
-
-        return list(
-            map(ChoiceRecognizers._found_choice_constructor, model.parse(utterance))
-        )
-
-    @staticmethod
-    def _match_choice_by_index(
-        choices: List[Choice], matched: List[ModelResult], match: ModelResult
-    ):
-        try:
-            index: int = int(match.resolution.value) - 1
-            if 0 <= index < len(choices):
-                choice = choices[index]
-
-                matched.append(
-                    ModelResult(
-                        start=match.start,
-                        end=match.end,
-                        type_name="choice",
-                        text=match.text,
-                        resolution=FoundChoice(
-                            value=choice.value, index=index, score=1.0
-                        ),
-                    )
-                )
-        except:
-            # noop here, as in dotnet/node repos
-            pass
-
-    @staticmethod
-    def _recognize_number(utterance: str, culture: str) -> List[ModelResult]:
-        model: NumberModel = NumberRecognizer(culture).get_number_model(culture)
-
-        return list(
-            map(ChoiceRecognizers._found_choice_constructor, model.parse(utterance))
-        )
-
-    @staticmethod
-    def _found_choice_constructor(value_model: ModelResult) -> ModelResult:
-        return ModelResult(
-            start=value_model.start,
-            end=value_model.end,
-            type_name="choice",
-            text=value_model.text,
-            resolution=FoundChoice(
-                value=value_model.resolution["value"], index=0, score=1.0
-            ),
-        )
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License.
+
+from typing import List, Union
+from recognizers_number import NumberModel, NumberRecognizer, OrdinalModel
+from recognizers_text import Culture
+
+
+from .choice import Choice
+from .find import Find
+from .find_choices_options import FindChoicesOptions
+from .found_choice import FoundChoice
+from .model_result import ModelResult
+
+
+class ChoiceRecognizers:
+    """ Contains methods for matching user input against a list of choices. """
+
+    @staticmethod
+    def recognize_choices(
+        utterance: str,
+        choices: List[Union[str, Choice]],
+        options: FindChoicesOptions = None,
+    ) -> List[ModelResult]:
+        """
+        Matches user input against a list of choices.
+
+        This is layered above the `Find.find_choices()` function, and adds logic to let the user specify
+        their choice by index (they can say "one" to pick `choice[0]`) or ordinal position
+         (they can say "the second one" to pick `choice[1]`.)
+        The user's utterance is recognized in the following order:
+
+        - By name using `find_choices()`
+        - By 1's based ordinal position.
+        - By 1's based index position.
+
+        Parameters:
+        -----------
+
+        utterance: The input.
+
+        choices: The list of choices.
+
+        options: (Optional) Options to control the recognition strategy.
+
+        Returns:
+        --------
+        A list of found choices, sorted by most relevant first.
+        """
+        if utterance is None:
+            utterance = ""
+
+        # Normalize list of choices
+        choices_list = [
+            Choice(value=choice) if isinstance(choice, str) else choice
+            for choice in choices
+        ]
+
+        # Try finding choices by text search first
+        # - We only want to use a single strategy for returning results to avoid issues where utterances
+        #   like the "the third one" or "the red one" or "the first division book" would miss-recognize as
+        #   a numerical index or ordinal as well.
+        locale = options.locale if (options and options.locale) else Culture.English
+        matched = Find.find_choices(utterance, choices_list, options)
+        if not matched:
+            matches = []
+
+            if not options or options.recognize_ordinals:
+                # Next try finding by ordinal
+                matches = ChoiceRecognizers._recognize_ordinal(utterance, locale)
+                for match in matches:
+                    ChoiceRecognizers._match_choice_by_index(
+                        choices_list, matched, match
+                    )
+
+            if not matches and (not options or options.recognize_numbers):
+                # Then try by numerical index
+                matches = ChoiceRecognizers._recognize_number(utterance, locale)
+                for match in matches:
+                    ChoiceRecognizers._match_choice_by_index(
+                        choices_list, matched, match
+                    )
+
+            # Sort any found matches by their position within the utterance.
+            # - The results from find_choices() are already properly sorted so we just need this
+            #   for ordinal & numerical lookups.
+            matched = sorted(matched, key=lambda model_result: model_result.start)
+
+        return matched
+
+    @staticmethod
+    def _recognize_ordinal(utterance: str, culture: str) -> List[ModelResult]:
+        model: OrdinalModel = NumberRecognizer(culture).get_ordinal_model(culture)
+
+        return list(
+            map(ChoiceRecognizers._found_choice_constructor, model.parse(utterance))
+        )
+
+    @staticmethod
+    def _match_choice_by_index(
+        choices: List[Choice], matched: List[ModelResult], match: ModelResult
+    ):
+        try:
+            index: int = int(match.resolution.value) - 1
+            if 0 <= index < len(choices):
+                choice = choices[index]
+
+                matched.append(
+                    ModelResult(
+                        start=match.start,
+                        end=match.end,
+                        type_name="choice",
+                        text=match.text,
+                        resolution=FoundChoice(
+                            value=choice.value, index=index, score=1.0
+                        ),
+                    )
+                )
+        except:
+            # noop here, as in dotnet/node repos
+            pass
+
+    @staticmethod
+    def _recognize_number(utterance: str, culture: str) -> List[ModelResult]:
+        model: NumberModel = NumberRecognizer(culture).get_number_model(culture)
+
+        return list(
+            map(ChoiceRecognizers._found_choice_constructor, model.parse(utterance))
+        )
+
+    @staticmethod
+    def _found_choice_constructor(value_model: ModelResult) -> ModelResult:
+        return ModelResult(
+            start=value_model.start,
+            end=value_model.end,
+            type_name="choice",
+            text=value_model.text,
+            resolution=FoundChoice(
+                value=value_model.resolution["value"], index=0, score=1.0
+            ),
+        )
diff --git a/libraries/botbuilder-dialogs/botbuilder/dialogs/choices/find_choices_options.py b/libraries/botbuilder-dialogs/botbuilder/dialogs/choices/find_choices_options.py
--- a/libraries/botbuilder-dialogs/botbuilder/dialogs/choices/find_choices_options.py
+++ b/libraries/botbuilder-dialogs/botbuilder/dialogs/choices/find_choices_options.py
@@ -1,23 +1,38 @@
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-
-from .find_values_options import FindValuesOptions
-
-
-class FindChoicesOptions(FindValuesOptions):
-    """ Contains options to control how input is matched against a list of choices """
-
-    def __init__(self, no_value: bool = None, no_action: bool = None, **kwargs):
-        """
-        Parameters:
-        -----------
-
-        no_value: (Optional) If `True`, the choices `value` field will NOT be search over. Defaults to `False`.
-
-        no_action: (Optional) If `True`, the choices `action.title` field will NOT be searched over.
-         Defaults to `False`.
-        """
-
-        super().__init__(**kwargs)
-        self.no_value = no_value
-        self.no_action = no_action
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License.
+
+from .find_values_options import FindValuesOptions
+
+
+class FindChoicesOptions(FindValuesOptions):
+    """ Contains options to control how input is matched against a list of choices """
+
+    def __init__(
+        self,
+        no_value: bool = None,
+        no_action: bool = None,
+        recognize_numbers: bool = True,
+        recognize_ordinals: bool = True,
+        **kwargs,
+    ):
+        """
+        Parameters:
+        -----------
+
+        no_value: (Optional) If `True`, the choices `value` field will NOT be search over. Defaults to `False`.
+
+        no_action: (Optional) If `True`, the choices `action.title` field will NOT be searched over.
+         Defaults to `False`.
+
+        recognize_numbers: (Optional) Indicates whether the recognizer should check for Numbers using the
+        NumberRecognizer's NumberModel.
+
+        recognize_ordinals: (Options) Indicates whether the recognizer should check for Ordinal Numbers using
+        the NumberRecognizer's OrdinalModel.
+        """
+
+        super().__init__(**kwargs)
+        self.no_value = no_value
+        self.no_action = no_action
+        self.recognize_numbers = recognize_numbers
+        self.recognize_ordinals = recognize_ordinals


Test patch:
diff --git a/libraries/botbuilder-dialogs/tests/test_choice_prompt.py b/libraries/botbuilder-dialogs/tests/test_choice_prompt.py
--- a/libraries/botbuilder-dialogs/tests/test_choice_prompt.py
+++ b/libraries/botbuilder-dialogs/tests/test_choice_prompt.py
@@ -1,695 +1,717 @@
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-
-from typing import List
-
-import aiounittest
-from recognizers_text import Culture
-
-from botbuilder.core import CardFactory, ConversationState, MemoryStorage, TurnContext
-from botbuilder.core.adapters import TestAdapter
-from botbuilder.dialogs import DialogSet, DialogTurnResult, DialogTurnStatus
-from botbuilder.dialogs.choices import Choice, ListStyle
-from botbuilder.dialogs.prompts import (
-    ChoicePrompt,
-    PromptOptions,
-    PromptValidatorContext,
-)
-from botbuilder.schema import Activity, ActivityTypes
-
-_color_choices: List[Choice] = [
-    Choice(value="red"),
-    Choice(value="green"),
-    Choice(value="blue"),
-]
-
-_answer_message: Activity = Activity(text="red", type=ActivityTypes.message)
-_invalid_message: Activity = Activity(text="purple", type=ActivityTypes.message)
-
-
-class ChoicePromptTest(aiounittest.AsyncTestCase):
-    def test_choice_prompt_with_empty_id_should_fail(self):
-        empty_id = ""
-
-        with self.assertRaises(TypeError):
-            ChoicePrompt(empty_id)
-
-    def test_choice_prompt_with_none_id_should_fail(self):
-        none_id = None
-
-        with self.assertRaises(TypeError):
-            ChoicePrompt(none_id)
-
-    async def test_should_call_choice_prompt_using_dc_prompt(self):
-        async def exec_test(turn_context: TurnContext):
-            dialog_context = await dialogs.create_context(turn_context)
-
-            results: DialogTurnResult = await dialog_context.continue_dialog()
-
-            if results.status == DialogTurnStatus.Empty:
-                options = PromptOptions(
-                    prompt=Activity(
-                        type=ActivityTypes.message, text="Please choose a color."
-                    ),
-                    choices=_color_choices,
-                )
-                await dialog_context.prompt("ChoicePrompt", options)
-            elif results.status == DialogTurnStatus.Complete:
-                selected_choice = results.result
-                await turn_context.send_activity(selected_choice.value)
-
-            await convo_state.save_changes(turn_context)
-
-        # Initialize TestAdapter.
-        adapter = TestAdapter(exec_test)
-
-        # Create new ConversationState with MemoryStorage and register the state as middleware.
-        convo_state = ConversationState(MemoryStorage())
-
-        # Create a DialogState property, DialogSet, and ChoicePrompt.
-        dialog_state = convo_state.create_property("dialogState")
-        dialogs = DialogSet(dialog_state)
-        choice_prompt = ChoicePrompt("ChoicePrompt")
-        dialogs.add(choice_prompt)
-
-        step1 = await adapter.send("hello")
-        step2 = await step1.assert_reply(
-            "Please choose a color. (1) red, (2) green, or (3) blue"
-        )
-        step3 = await step2.send(_answer_message)
-        await step3.assert_reply("red")
-
-    async def test_should_call_choice_prompt_with_custom_validator(self):
-        async def exec_test(turn_context: TurnContext):
-            dialog_context = await dialogs.create_context(turn_context)
-
-            results: DialogTurnResult = await dialog_context.continue_dialog()
-
-            if results.status == DialogTurnStatus.Empty:
-                options = PromptOptions(
-                    prompt=Activity(
-                        type=ActivityTypes.message, text="Please choose a color."
-                    ),
-                    choices=_color_choices,
-                )
-                await dialog_context.prompt("prompt", options)
-            elif results.status == DialogTurnStatus.Complete:
-                selected_choice = results.result
-                await turn_context.send_activity(selected_choice.value)
-
-            await convo_state.save_changes(turn_context)
-
-        adapter = TestAdapter(exec_test)
-
-        convo_state = ConversationState(MemoryStorage())
-        dialog_state = convo_state.create_property("dialogState")
-        dialogs = DialogSet(dialog_state)
-
-        async def validator(prompt: PromptValidatorContext) -> bool:
-            assert prompt
-
-            return prompt.recognized.succeeded
-
-        choice_prompt = ChoicePrompt("prompt", validator)
-
-        dialogs.add(choice_prompt)
-
-        step1 = await adapter.send("Hello")
-        step2 = await step1.assert_reply(
-            "Please choose a color. (1) red, (2) green, or (3) blue"
-        )
-        step3 = await step2.send(_invalid_message)
-        step4 = await step3.assert_reply(
-            "Please choose a color. (1) red, (2) green, or (3) blue"
-        )
-        step5 = await step4.send(_answer_message)
-        await step5.assert_reply("red")
-
-    async def test_should_send_custom_retry_prompt(self):
-        async def exec_test(turn_context: TurnContext):
-            dialog_context = await dialogs.create_context(turn_context)
-
-            results: DialogTurnResult = await dialog_context.continue_dialog()
-
-            if results.status == DialogTurnStatus.Empty:
-                options = PromptOptions(
-                    prompt=Activity(
-                        type=ActivityTypes.message, text="Please choose a color."
-                    ),
-                    retry_prompt=Activity(
-                        type=ActivityTypes.message,
-                        text="Please choose red, blue, or green.",
-                    ),
-                    choices=_color_choices,
-                )
-                await dialog_context.prompt("prompt", options)
-            elif results.status == DialogTurnStatus.Complete:
-                selected_choice = results.result
-                await turn_context.send_activity(selected_choice.value)
-
-            await convo_state.save_changes(turn_context)
-
-        adapter = TestAdapter(exec_test)
-
-        convo_state = ConversationState(MemoryStorage())
-        dialog_state = convo_state.create_property("dialogState")
-        dialogs = DialogSet(dialog_state)
-        choice_prompt = ChoicePrompt("prompt")
-        dialogs.add(choice_prompt)
-
-        step1 = await adapter.send("Hello")
-        step2 = await step1.assert_reply(
-            "Please choose a color. (1) red, (2) green, or (3) blue"
-        )
-        step3 = await step2.send(_invalid_message)
-        step4 = await step3.assert_reply(
-            "Please choose red, blue, or green. (1) red, (2) green, or (3) blue"
-        )
-        step5 = await step4.send(_answer_message)
-        await step5.assert_reply("red")
-
-    async def test_should_send_ignore_retry_prompt_if_validator_replies(self):
-        async def exec_test(turn_context: TurnContext):
-            dialog_context = await dialogs.create_context(turn_context)
-
-            results: DialogTurnResult = await dialog_context.continue_dialog()
-
-            if results.status == DialogTurnStatus.Empty:
-                options = PromptOptions(
-                    prompt=Activity(
-                        type=ActivityTypes.message, text="Please choose a color."
-                    ),
-                    retry_prompt=Activity(
-                        type=ActivityTypes.message,
-                        text="Please choose red, blue, or green.",
-                    ),
-                    choices=_color_choices,
-                )
-                await dialog_context.prompt("prompt", options)
-            elif results.status == DialogTurnStatus.Complete:
-                selected_choice = results.result
-                await turn_context.send_activity(selected_choice.value)
-
-            await convo_state.save_changes(turn_context)
-
-        adapter = TestAdapter(exec_test)
-
-        convo_state = ConversationState(MemoryStorage())
-        dialog_state = convo_state.create_property("dialogState")
-        dialogs = DialogSet(dialog_state)
-
-        async def validator(prompt: PromptValidatorContext) -> bool:
-            assert prompt
-
-            if not prompt.recognized.succeeded:
-                await prompt.context.send_activity("Bad input.")
-
-            return prompt.recognized.succeeded
-
-        choice_prompt = ChoicePrompt("prompt", validator)
-
-        dialogs.add(choice_prompt)
-
-        step1 = await adapter.send("Hello")
-        step2 = await step1.assert_reply(
-            "Please choose a color. (1) red, (2) green, or (3) blue"
-        )
-        step3 = await step2.send(_invalid_message)
-        step4 = await step3.assert_reply("Bad input.")
-        step5 = await step4.send(_answer_message)
-        await step5.assert_reply("red")
-
-    async def test_should_use_default_locale_when_rendering_choices(self):
-        async def exec_test(turn_context: TurnContext):
-            dialog_context = await dialogs.create_context(turn_context)
-
-            results: DialogTurnResult = await dialog_context.continue_dialog()
-
-            if results.status == DialogTurnStatus.Empty:
-                options = PromptOptions(
-                    prompt=Activity(
-                        type=ActivityTypes.message, text="Please choose a color."
-                    ),
-                    choices=_color_choices,
-                )
-                await dialog_context.prompt("prompt", options)
-            elif results.status == DialogTurnStatus.Complete:
-                selected_choice = results.result
-                await turn_context.send_activity(selected_choice.value)
-
-            await convo_state.save_changes(turn_context)
-
-        adapter = TestAdapter(exec_test)
-
-        convo_state = ConversationState(MemoryStorage())
-        dialog_state = convo_state.create_property("dialogState")
-        dialogs = DialogSet(dialog_state)
-
-        async def validator(prompt: PromptValidatorContext) -> bool:
-            assert prompt
-
-            if not prompt.recognized.succeeded:
-                await prompt.context.send_activity("Bad input.")
-
-            return prompt.recognized.succeeded
-
-        choice_prompt = ChoicePrompt(
-            "prompt", validator, default_locale=Culture.Spanish
-        )
-
-        dialogs.add(choice_prompt)
-
-        step1 = await adapter.send(Activity(type=ActivityTypes.message, text="Hello"))
-        step2 = await step1.assert_reply(
-            "Please choose a color. (1) red, (2) green, o (3) blue"
-        )
-        step3 = await step2.send(_invalid_message)
-        step4 = await step3.assert_reply("Bad input.")
-        step5 = await step4.send(Activity(type=ActivityTypes.message, text="red"))
-        await step5.assert_reply("red")
-
-    async def test_should_use_context_activity_locale_when_rendering_choices(self):
-        async def exec_test(turn_context: TurnContext):
-            dialog_context = await dialogs.create_context(turn_context)
-
-            results: DialogTurnResult = await dialog_context.continue_dialog()
-
-            if results.status == DialogTurnStatus.Empty:
-                options = PromptOptions(
-                    prompt=Activity(
-                        type=ActivityTypes.message, text="Please choose a color."
-                    ),
-                    choices=_color_choices,
-                )
-                await dialog_context.prompt("prompt", options)
-            elif results.status == DialogTurnStatus.Complete:
-                selected_choice = results.result
-                await turn_context.send_activity(selected_choice.value)
-
-            await convo_state.save_changes(turn_context)
-
-        adapter = TestAdapter(exec_test)
-
-        convo_state = ConversationState(MemoryStorage())
-        dialog_state = convo_state.create_property("dialogState")
-        dialogs = DialogSet(dialog_state)
-
-        async def validator(prompt: PromptValidatorContext) -> bool:
-            assert prompt
-
-            if not prompt.recognized.succeeded:
-                await prompt.context.send_activity("Bad input.")
-
-            return prompt.recognized.succeeded
-
-        choice_prompt = ChoicePrompt("prompt", validator)
-        dialogs.add(choice_prompt)
-
-        step1 = await adapter.send(
-            Activity(type=ActivityTypes.message, text="Hello", locale=Culture.Spanish)
-        )
-        step2 = await step1.assert_reply(
-            "Please choose a color. (1) red, (2) green, o (3) blue"
-        )
-        step3 = await step2.send(_answer_message)
-        await step3.assert_reply("red")
-
-    async def test_should_use_context_activity_locale_over_default_locale_when_rendering_choices(
-        self,
-    ):
-        async def exec_test(turn_context: TurnContext):
-            dialog_context = await dialogs.create_context(turn_context)
-
-            results: DialogTurnResult = await dialog_context.continue_dialog()
-
-            if results.status == DialogTurnStatus.Empty:
-                options = PromptOptions(
-                    prompt=Activity(
-                        type=ActivityTypes.message, text="Please choose a color."
-                    ),
-                    choices=_color_choices,
-                )
-                await dialog_context.prompt("prompt", options)
-            elif results.status == DialogTurnStatus.Complete:
-                selected_choice = results.result
-                await turn_context.send_activity(selected_choice.value)
-
-            await convo_state.save_changes(turn_context)
-
-        adapter = TestAdapter(exec_test)
-
-        convo_state = ConversationState(MemoryStorage())
-        dialog_state = convo_state.create_property("dialogState")
-        dialogs = DialogSet(dialog_state)
-
-        async def validator(prompt: PromptValidatorContext) -> bool:
-            assert prompt
-
-            if not prompt.recognized.succeeded:
-                await prompt.context.send_activity("Bad input.")
-
-            return prompt.recognized.succeeded
-
-        choice_prompt = ChoicePrompt(
-            "prompt", validator, default_locale=Culture.Spanish
-        )
-        dialogs.add(choice_prompt)
-
-        step1 = await adapter.send(
-            Activity(type=ActivityTypes.message, text="Hello", locale=Culture.English)
-        )
-        step2 = await step1.assert_reply(
-            "Please choose a color. (1) red, (2) green, or (3) blue"
-        )
-        step3 = await step2.send(_answer_message)
-        await step3.assert_reply("red")
-
-    async def test_should_not_render_choices_if_list_style_none_is_specified(self):
-        async def exec_test(turn_context: TurnContext):
-            dialog_context = await dialogs.create_context(turn_context)
-
-            results: DialogTurnResult = await dialog_context.continue_dialog()
-
-            if results.status == DialogTurnStatus.Empty:
-                options = PromptOptions(
-                    prompt=Activity(
-                        type=ActivityTypes.message, text="Please choose a color."
-                    ),
-                    choices=_color_choices,
-                    style=ListStyle.none,
-                )
-                await dialog_context.prompt("prompt", options)
-            elif results.status == DialogTurnStatus.Complete:
-                selected_choice = results.result
-                await turn_context.send_activity(selected_choice.value)
-
-            await convo_state.save_changes(turn_context)
-
-        adapter = TestAdapter(exec_test)
-
-        convo_state = ConversationState(MemoryStorage())
-        dialog_state = convo_state.create_property("dialogState")
-        dialogs = DialogSet(dialog_state)
-
-        choice_prompt = ChoicePrompt("prompt")
-
-        dialogs.add(choice_prompt)
-
-        step1 = await adapter.send("Hello")
-        step2 = await step1.assert_reply("Please choose a color.")
-        step3 = await step2.send(_answer_message)
-        await step3.assert_reply("red")
-
-    async def test_should_create_prompt_with_inline_choices_when_specified(self):
-        async def exec_test(turn_context: TurnContext):
-            dialog_context = await dialogs.create_context(turn_context)
-
-            results: DialogTurnResult = await dialog_context.continue_dialog()
-
-            if results.status == DialogTurnStatus.Empty:
-                options = PromptOptions(
-                    prompt=Activity(
-                        type=ActivityTypes.message, text="Please choose a color."
-                    ),
-                    choices=_color_choices,
-                )
-                await dialog_context.prompt("prompt", options)
-            elif results.status == DialogTurnStatus.Complete:
-                selected_choice = results.result
-                await turn_context.send_activity(selected_choice.value)
-
-            await convo_state.save_changes(turn_context)
-
-        adapter = TestAdapter(exec_test)
-
-        convo_state = ConversationState(MemoryStorage())
-        dialog_state = convo_state.create_property("dialogState")
-        dialogs = DialogSet(dialog_state)
-
-        choice_prompt = ChoicePrompt("prompt")
-        choice_prompt.style = ListStyle.in_line
-
-        dialogs.add(choice_prompt)
-
-        step1 = await adapter.send("Hello")
-        step2 = await step1.assert_reply(
-            "Please choose a color. (1) red, (2) green, or (3) blue"
-        )
-        step3 = await step2.send(_answer_message)
-        await step3.assert_reply("red")
-
-    async def test_should_create_prompt_with_list_choices_when_specified(self):
-        async def exec_test(turn_context: TurnContext):
-            dialog_context = await dialogs.create_context(turn_context)
-
-            results: DialogTurnResult = await dialog_context.continue_dialog()
-
-            if results.status == DialogTurnStatus.Empty:
-                options = PromptOptions(
-                    prompt=Activity(
-                        type=ActivityTypes.message, text="Please choose a color."
-                    ),
-                    choices=_color_choices,
-                )
-                await dialog_context.prompt("prompt", options)
-            elif results.status == DialogTurnStatus.Complete:
-                selected_choice = results.result
-                await turn_context.send_activity(selected_choice.value)
-
-            await convo_state.save_changes(turn_context)
-
-        adapter = TestAdapter(exec_test)
-
-        convo_state = ConversationState(MemoryStorage())
-        dialog_state = convo_state.create_property("dialogState")
-        dialogs = DialogSet(dialog_state)
-
-        choice_prompt = ChoicePrompt("prompt")
-        choice_prompt.style = ListStyle.list_style
-
-        dialogs.add(choice_prompt)
-
-        step1 = await adapter.send("Hello")
-        step2 = await step1.assert_reply(
-            "Please choose a color.\n\n   1. red\n   2. green\n   3. blue"
-        )
-        step3 = await step2.send(_answer_message)
-        await step3.assert_reply("red")
-
-    async def test_should_create_prompt_with_suggested_action_style_when_specified(
-        self,
-    ):
-        async def exec_test(turn_context: TurnContext):
-            dialog_context = await dialogs.create_context(turn_context)
-
-            results: DialogTurnResult = await dialog_context.continue_dialog()
-
-            if results.status == DialogTurnStatus.Empty:
-                options = PromptOptions(
-                    prompt=Activity(
-                        type=ActivityTypes.message, text="Please choose a color."
-                    ),
-                    choices=_color_choices,
-                    style=ListStyle.suggested_action,
-                )
-                await dialog_context.prompt("prompt", options)
-            elif results.status == DialogTurnStatus.Complete:
-                selected_choice = results.result
-                await turn_context.send_activity(selected_choice.value)
-
-            await convo_state.save_changes(turn_context)
-
-        adapter = TestAdapter(exec_test)
-
-        convo_state = ConversationState(MemoryStorage())
-        dialog_state = convo_state.create_property("dialogState")
-        dialogs = DialogSet(dialog_state)
-
-        choice_prompt = ChoicePrompt("prompt")
-
-        dialogs.add(choice_prompt)
-
-        step1 = await adapter.send("Hello")
-        step2 = await step1.assert_reply("Please choose a color.")
-        step3 = await step2.send(_answer_message)
-        await step3.assert_reply("red")
-
-    async def test_should_create_prompt_with_auto_style_when_specified(self):
-        async def exec_test(turn_context: TurnContext):
-            dialog_context = await dialogs.create_context(turn_context)
-
-            results: DialogTurnResult = await dialog_context.continue_dialog()
-
-            if results.status == DialogTurnStatus.Empty:
-                options = PromptOptions(
-                    prompt=Activity(
-                        type=ActivityTypes.message, text="Please choose a color."
-                    ),
-                    choices=_color_choices,
-                    style=ListStyle.auto,
-                )
-                await dialog_context.prompt("prompt", options)
-            elif results.status == DialogTurnStatus.Complete:
-                selected_choice = results.result
-                await turn_context.send_activity(selected_choice.value)
-
-            await convo_state.save_changes(turn_context)
-
-        adapter = TestAdapter(exec_test)
-
-        convo_state = ConversationState(MemoryStorage())
-        dialog_state = convo_state.create_property("dialogState")
-        dialogs = DialogSet(dialog_state)
-
-        choice_prompt = ChoicePrompt("prompt")
-
-        dialogs.add(choice_prompt)
-
-        step1 = await adapter.send("Hello")
-        step2 = await step1.assert_reply(
-            "Please choose a color. (1) red, (2) green, or (3) blue"
-        )
-        step3 = await step2.send(_answer_message)
-        await step3.assert_reply("red")
-
-    async def test_should_recognize_valid_number_choice(self):
-        async def exec_test(turn_context: TurnContext):
-            dialog_context = await dialogs.create_context(turn_context)
-
-            results: DialogTurnResult = await dialog_context.continue_dialog()
-
-            if results.status == DialogTurnStatus.Empty:
-                options = PromptOptions(
-                    prompt=Activity(
-                        type=ActivityTypes.message, text="Please choose a color."
-                    ),
-                    choices=_color_choices,
-                )
-                await dialog_context.prompt("prompt", options)
-            elif results.status == DialogTurnStatus.Complete:
-                selected_choice = results.result
-                await turn_context.send_activity(selected_choice.value)
-
-            await convo_state.save_changes(turn_context)
-
-        adapter = TestAdapter(exec_test)
-
-        convo_state = ConversationState(MemoryStorage())
-        dialog_state = convo_state.create_property("dialogState")
-        dialogs = DialogSet(dialog_state)
-
-        choice_prompt = ChoicePrompt("prompt")
-
-        dialogs.add(choice_prompt)
-
-        step1 = await adapter.send("Hello")
-        step2 = await step1.assert_reply(
-            "Please choose a color. (1) red, (2) green, or (3) blue"
-        )
-        step3 = await step2.send("1")
-        await step3.assert_reply("red")
-
-    async def test_should_display_choices_on_hero_card(self):
-        size_choices = ["large", "medium", "small"]
-
-        async def exec_test(turn_context: TurnContext):
-            dialog_context = await dialogs.create_context(turn_context)
-
-            results: DialogTurnResult = await dialog_context.continue_dialog()
-
-            if results.status == DialogTurnStatus.Empty:
-                options = PromptOptions(
-                    prompt=Activity(
-                        type=ActivityTypes.message, text="Please choose a size."
-                    ),
-                    choices=size_choices,
-                )
-                await dialog_context.prompt("prompt", options)
-            elif results.status == DialogTurnStatus.Complete:
-                selected_choice = results.result
-                await turn_context.send_activity(selected_choice.value)
-
-            await convo_state.save_changes(turn_context)
-
-        def assert_expected_activity(
-            activity: Activity, description
-        ):  # pylint: disable=unused-argument
-            assert len(activity.attachments) == 1
-            assert (
-                activity.attachments[0].content_type
-                == CardFactory.content_types.hero_card
-            )
-            assert activity.attachments[0].content.text == "Please choose a size."
-
-        adapter = TestAdapter(exec_test)
-
-        convo_state = ConversationState(MemoryStorage())
-        dialog_state = convo_state.create_property("dialogState")
-        dialogs = DialogSet(dialog_state)
-
-        choice_prompt = ChoicePrompt("prompt")
-
-        # Change the ListStyle of the prompt to ListStyle.none.
-        choice_prompt.style = ListStyle.hero_card
-
-        dialogs.add(choice_prompt)
-
-        step1 = await adapter.send("Hello")
-        step2 = await step1.assert_reply(assert_expected_activity)
-        step3 = await step2.send("1")
-        await step3.assert_reply(size_choices[0])
-
-    async def test_should_display_choices_on_hero_card_with_additional_attachment(self):
-        size_choices = ["large", "medium", "small"]
-        card = CardFactory.adaptive_card(
-            {
-                "type": "AdaptiveCard",
-                "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
-                "version": "1.2",
-                "body": [],
-            }
-        )
-        card_activity = Activity(attachments=[card])
-
-        async def exec_test(turn_context: TurnContext):
-            dialog_context = await dialogs.create_context(turn_context)
-
-            results: DialogTurnResult = await dialog_context.continue_dialog()
-
-            if results.status == DialogTurnStatus.Empty:
-                options = PromptOptions(prompt=card_activity, choices=size_choices)
-                await dialog_context.prompt("prompt", options)
-            elif results.status == DialogTurnStatus.Complete:
-                selected_choice = results.result
-                await turn_context.send_activity(selected_choice.value)
-
-            await convo_state.save_changes(turn_context)
-
-        def assert_expected_activity(
-            activity: Activity, description
-        ):  # pylint: disable=unused-argument
-            assert len(activity.attachments) == 2
-            assert (
-                activity.attachments[0].content_type
-                == CardFactory.content_types.adaptive_card
-            )
-            assert (
-                activity.attachments[1].content_type
-                == CardFactory.content_types.hero_card
-            )
-
-        adapter = TestAdapter(exec_test)
-
-        convo_state = ConversationState(MemoryStorage())
-        dialog_state = convo_state.create_property("dialogState")
-        dialogs = DialogSet(dialog_state)
-
-        choice_prompt = ChoicePrompt("prompt")
-
-        # Change the ListStyle of the prompt to ListStyle.none.
-        choice_prompt.style = ListStyle.hero_card
-
-        dialogs.add(choice_prompt)
-
-        step1 = await adapter.send("Hello")
-        await step1.assert_reply(assert_expected_activity)
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License.
+
+from typing import List
+
+import aiounittest
+from recognizers_text import Culture
+
+from botbuilder.core import CardFactory, ConversationState, MemoryStorage, TurnContext
+from botbuilder.core.adapters import TestAdapter
+from botbuilder.dialogs import (
+    DialogSet,
+    DialogTurnResult,
+    DialogTurnStatus,
+    ChoiceRecognizers,
+    FindChoicesOptions,
+)
+from botbuilder.dialogs.choices import Choice, ListStyle
+from botbuilder.dialogs.prompts import (
+    ChoicePrompt,
+    PromptOptions,
+    PromptValidatorContext,
+)
+from botbuilder.schema import Activity, ActivityTypes
+
+_color_choices: List[Choice] = [
+    Choice(value="red"),
+    Choice(value="green"),
+    Choice(value="blue"),
+]
+
+_answer_message: Activity = Activity(text="red", type=ActivityTypes.message)
+_invalid_message: Activity = Activity(text="purple", type=ActivityTypes.message)
+
+
+class ChoicePromptTest(aiounittest.AsyncTestCase):
+    def test_choice_prompt_with_empty_id_should_fail(self):
+        empty_id = ""
+
+        with self.assertRaises(TypeError):
+            ChoicePrompt(empty_id)
+
+    def test_choice_prompt_with_none_id_should_fail(self):
+        none_id = None
+
+        with self.assertRaises(TypeError):
+            ChoicePrompt(none_id)
+
+    async def test_should_call_choice_prompt_using_dc_prompt(self):
+        async def exec_test(turn_context: TurnContext):
+            dialog_context = await dialogs.create_context(turn_context)
+
+            results: DialogTurnResult = await dialog_context.continue_dialog()
+
+            if results.status == DialogTurnStatus.Empty:
+                options = PromptOptions(
+                    prompt=Activity(
+                        type=ActivityTypes.message, text="Please choose a color."
+                    ),
+                    choices=_color_choices,
+                )
+                await dialog_context.prompt("ChoicePrompt", options)
+            elif results.status == DialogTurnStatus.Complete:
+                selected_choice = results.result
+                await turn_context.send_activity(selected_choice.value)
+
+            await convo_state.save_changes(turn_context)
+
+        # Initialize TestAdapter.
+        adapter = TestAdapter(exec_test)
+
+        # Create new ConversationState with MemoryStorage and register the state as middleware.
+        convo_state = ConversationState(MemoryStorage())
+
+        # Create a DialogState property, DialogSet, and ChoicePrompt.
+        dialog_state = convo_state.create_property("dialogState")
+        dialogs = DialogSet(dialog_state)
+        choice_prompt = ChoicePrompt("ChoicePrompt")
+        dialogs.add(choice_prompt)
+
+        step1 = await adapter.send("hello")
+        step2 = await step1.assert_reply(
+            "Please choose a color. (1) red, (2) green, or (3) blue"
+        )
+        step3 = await step2.send(_answer_message)
+        await step3.assert_reply("red")
+
+    async def test_should_call_choice_prompt_with_custom_validator(self):
+        async def exec_test(turn_context: TurnContext):
+            dialog_context = await dialogs.create_context(turn_context)
+
+            results: DialogTurnResult = await dialog_context.continue_dialog()
+
+            if results.status == DialogTurnStatus.Empty:
+                options = PromptOptions(
+                    prompt=Activity(
+                        type=ActivityTypes.message, text="Please choose a color."
+                    ),
+                    choices=_color_choices,
+                )
+                await dialog_context.prompt("prompt", options)
+            elif results.status == DialogTurnStatus.Complete:
+                selected_choice = results.result
+                await turn_context.send_activity(selected_choice.value)
+
+            await convo_state.save_changes(turn_context)
+
+        adapter = TestAdapter(exec_test)
+
+        convo_state = ConversationState(MemoryStorage())
+        dialog_state = convo_state.create_property("dialogState")
+        dialogs = DialogSet(dialog_state)
+
+        async def validator(prompt: PromptValidatorContext) -> bool:
+            assert prompt
+
+            return prompt.recognized.succeeded
+
+        choice_prompt = ChoicePrompt("prompt", validator)
+
+        dialogs.add(choice_prompt)
+
+        step1 = await adapter.send("Hello")
+        step2 = await step1.assert_reply(
+            "Please choose a color. (1) red, (2) green, or (3) blue"
+        )
+        step3 = await step2.send(_invalid_message)
+        step4 = await step3.assert_reply(
+            "Please choose a color. (1) red, (2) green, or (3) blue"
+        )
+        step5 = await step4.send(_answer_message)
+        await step5.assert_reply("red")
+
+    async def test_should_send_custom_retry_prompt(self):
+        async def exec_test(turn_context: TurnContext):
+            dialog_context = await dialogs.create_context(turn_context)
+
+            results: DialogTurnResult = await dialog_context.continue_dialog()
+
+            if results.status == DialogTurnStatus.Empty:
+                options = PromptOptions(
+                    prompt=Activity(
+                        type=ActivityTypes.message, text="Please choose a color."
+                    ),
+                    retry_prompt=Activity(
+                        type=ActivityTypes.message,
+                        text="Please choose red, blue, or green.",
+                    ),
+                    choices=_color_choices,
+                )
+                await dialog_context.prompt("prompt", options)
+            elif results.status == DialogTurnStatus.Complete:
+                selected_choice = results.result
+                await turn_context.send_activity(selected_choice.value)
+
+            await convo_state.save_changes(turn_context)
+
+        adapter = TestAdapter(exec_test)
+
+        convo_state = ConversationState(MemoryStorage())
+        dialog_state = convo_state.create_property("dialogState")
+        dialogs = DialogSet(dialog_state)
+        choice_prompt = ChoicePrompt("prompt")
+        dialogs.add(choice_prompt)
+
+        step1 = await adapter.send("Hello")
+        step2 = await step1.assert_reply(
+            "Please choose a color. (1) red, (2) green, or (3) blue"
+        )
+        step3 = await step2.send(_invalid_message)
+        step4 = await step3.assert_reply(
+            "Please choose red, blue, or green. (1) red, (2) green, or (3) blue"
+        )
+        step5 = await step4.send(_answer_message)
+        await step5.assert_reply("red")
+
+    async def test_should_send_ignore_retry_prompt_if_validator_replies(self):
+        async def exec_test(turn_context: TurnContext):
+            dialog_context = await dialogs.create_context(turn_context)
+
+            results: DialogTurnResult = await dialog_context.continue_dialog()
+
+            if results.status == DialogTurnStatus.Empty:
+                options = PromptOptions(
+                    prompt=Activity(
+                        type=ActivityTypes.message, text="Please choose a color."
+                    ),
+                    retry_prompt=Activity(
+                        type=ActivityTypes.message,
+                        text="Please choose red, blue, or green.",
+                    ),
+                    choices=_color_choices,
+                )
+                await dialog_context.prompt("prompt", options)
+            elif results.status == DialogTurnStatus.Complete:
+                selected_choice = results.result
+                await turn_context.send_activity(selected_choice.value)
+
+            await convo_state.save_changes(turn_context)
+
+        adapter = TestAdapter(exec_test)
+
+        convo_state = ConversationState(MemoryStorage())
+        dialog_state = convo_state.create_property("dialogState")
+        dialogs = DialogSet(dialog_state)
+
+        async def validator(prompt: PromptValidatorContext) -> bool:
+            assert prompt
+
+            if not prompt.recognized.succeeded:
+                await prompt.context.send_activity("Bad input.")
+
+            return prompt.recognized.succeeded
+
+        choice_prompt = ChoicePrompt("prompt", validator)
+
+        dialogs.add(choice_prompt)
+
+        step1 = await adapter.send("Hello")
+        step2 = await step1.assert_reply(
+            "Please choose a color. (1) red, (2) green, or (3) blue"
+        )
+        step3 = await step2.send(_invalid_message)
+        step4 = await step3.assert_reply("Bad input.")
+        step5 = await step4.send(_answer_message)
+        await step5.assert_reply("red")
+
+    async def test_should_use_default_locale_when_rendering_choices(self):
+        async def exec_test(turn_context: TurnContext):
+            dialog_context = await dialogs.create_context(turn_context)
+
+            results: DialogTurnResult = await dialog_context.continue_dialog()
+
+            if results.status == DialogTurnStatus.Empty:
+                options = PromptOptions(
+                    prompt=Activity(
+                        type=ActivityTypes.message, text="Please choose a color."
+                    ),
+                    choices=_color_choices,
+                )
+                await dialog_context.prompt("prompt", options)
+            elif results.status == DialogTurnStatus.Complete:
+                selected_choice = results.result
+                await turn_context.send_activity(selected_choice.value)
+
+            await convo_state.save_changes(turn_context)
+
+        adapter = TestAdapter(exec_test)
+
+        convo_state = ConversationState(MemoryStorage())
+        dialog_state = convo_state.create_property("dialogState")
+        dialogs = DialogSet(dialog_state)
+
+        async def validator(prompt: PromptValidatorContext) -> bool:
+            assert prompt
+
+            if not prompt.recognized.succeeded:
+                await prompt.context.send_activity("Bad input.")
+
+            return prompt.recognized.succeeded
+
+        choice_prompt = ChoicePrompt(
+            "prompt", validator, default_locale=Culture.Spanish
+        )
+
+        dialogs.add(choice_prompt)
+
+        step1 = await adapter.send(Activity(type=ActivityTypes.message, text="Hello"))
+        step2 = await step1.assert_reply(
+            "Please choose a color. (1) red, (2) green, o (3) blue"
+        )
+        step3 = await step2.send(_invalid_message)
+        step4 = await step3.assert_reply("Bad input.")
+        step5 = await step4.send(Activity(type=ActivityTypes.message, text="red"))
+        await step5.assert_reply("red")
+
+    async def test_should_use_context_activity_locale_when_rendering_choices(self):
+        async def exec_test(turn_context: TurnContext):
+            dialog_context = await dialogs.create_context(turn_context)
+
+            results: DialogTurnResult = await dialog_context.continue_dialog()
+
+            if results.status == DialogTurnStatus.Empty:
+                options = PromptOptions(
+                    prompt=Activity(
+                        type=ActivityTypes.message, text="Please choose a color."
+                    ),
+                    choices=_color_choices,
+                )
+                await dialog_context.prompt("prompt", options)
+            elif results.status == DialogTurnStatus.Complete:
+                selected_choice = results.result
+                await turn_context.send_activity(selected_choice.value)
+
+            await convo_state.save_changes(turn_context)
+
+        adapter = TestAdapter(exec_test)
+
+        convo_state = ConversationState(MemoryStorage())
+        dialog_state = convo_state.create_property("dialogState")
+        dialogs = DialogSet(dialog_state)
+
+        async def validator(prompt: PromptValidatorContext) -> bool:
+            assert prompt
+
+            if not prompt.recognized.succeeded:
+                await prompt.context.send_activity("Bad input.")
+
+            return prompt.recognized.succeeded
+
+        choice_prompt = ChoicePrompt("prompt", validator)
+        dialogs.add(choice_prompt)
+
+        step1 = await adapter.send(
+            Activity(type=ActivityTypes.message, text="Hello", locale=Culture.Spanish)
+        )
+        step2 = await step1.assert_reply(
+            "Please choose a color. (1) red, (2) green, o (3) blue"
+        )
+        step3 = await step2.send(_answer_message)
+        await step3.assert_reply("red")
+
+    async def test_should_use_context_activity_locale_over_default_locale_when_rendering_choices(
+        self,
+    ):
+        async def exec_test(turn_context: TurnContext):
+            dialog_context = await dialogs.create_context(turn_context)
+
+            results: DialogTurnResult = await dialog_context.continue_dialog()
+
+            if results.status == DialogTurnStatus.Empty:
+                options = PromptOptions(
+                    prompt=Activity(
+                        type=ActivityTypes.message, text="Please choose a color."
+                    ),
+                    choices=_color_choices,
+                )
+                await dialog_context.prompt("prompt", options)
+            elif results.status == DialogTurnStatus.Complete:
+                selected_choice = results.result
+                await turn_context.send_activity(selected_choice.value)
+
+            await convo_state.save_changes(turn_context)
+
+        adapter = TestAdapter(exec_test)
+
+        convo_state = ConversationState(MemoryStorage())
+        dialog_state = convo_state.create_property("dialogState")
+        dialogs = DialogSet(dialog_state)
+
+        async def validator(prompt: PromptValidatorContext) -> bool:
+            assert prompt
+
+            if not prompt.recognized.succeeded:
+                await prompt.context.send_activity("Bad input.")
+
+            return prompt.recognized.succeeded
+
+        choice_prompt = ChoicePrompt(
+            "prompt", validator, default_locale=Culture.Spanish
+        )
+        dialogs.add(choice_prompt)
+
+        step1 = await adapter.send(
+            Activity(type=ActivityTypes.message, text="Hello", locale=Culture.English)
+        )
+        step2 = await step1.assert_reply(
+            "Please choose a color. (1) red, (2) green, or (3) blue"
+        )
+        step3 = await step2.send(_answer_message)
+        await step3.assert_reply("red")
+
+    async def test_should_not_render_choices_if_list_style_none_is_specified(self):
+        async def exec_test(turn_context: TurnContext):
+            dialog_context = await dialogs.create_context(turn_context)
+
+            results: DialogTurnResult = await dialog_context.continue_dialog()
+
+            if results.status == DialogTurnStatus.Empty:
+                options = PromptOptions(
+                    prompt=Activity(
+                        type=ActivityTypes.message, text="Please choose a color."
+                    ),
+                    choices=_color_choices,
+                    style=ListStyle.none,
+                )
+                await dialog_context.prompt("prompt", options)
+            elif results.status == DialogTurnStatus.Complete:
+                selected_choice = results.result
+                await turn_context.send_activity(selected_choice.value)
+
+            await convo_state.save_changes(turn_context)
+
+        adapter = TestAdapter(exec_test)
+
+        convo_state = ConversationState(MemoryStorage())
+        dialog_state = convo_state.create_property("dialogState")
+        dialogs = DialogSet(dialog_state)
+
+        choice_prompt = ChoicePrompt("prompt")
+
+        dialogs.add(choice_prompt)
+
+        step1 = await adapter.send("Hello")
+        step2 = await step1.assert_reply("Please choose a color.")
+        step3 = await step2.send(_answer_message)
+        await step3.assert_reply("red")
+
+    async def test_should_create_prompt_with_inline_choices_when_specified(self):
+        async def exec_test(turn_context: TurnContext):
+            dialog_context = await dialogs.create_context(turn_context)
+
+            results: DialogTurnResult = await dialog_context.continue_dialog()
+
+            if results.status == DialogTurnStatus.Empty:
+                options = PromptOptions(
+                    prompt=Activity(
+                        type=ActivityTypes.message, text="Please choose a color."
+                    ),
+                    choices=_color_choices,
+                )
+                await dialog_context.prompt("prompt", options)
+            elif results.status == DialogTurnStatus.Complete:
+                selected_choice = results.result
+                await turn_context.send_activity(selected_choice.value)
+
+            await convo_state.save_changes(turn_context)
+
+        adapter = TestAdapter(exec_test)
+
+        convo_state = ConversationState(MemoryStorage())
+        dialog_state = convo_state.create_property("dialogState")
+        dialogs = DialogSet(dialog_state)
+
+        choice_prompt = ChoicePrompt("prompt")
+        choice_prompt.style = ListStyle.in_line
+
+        dialogs.add(choice_prompt)
+
+        step1 = await adapter.send("Hello")
+        step2 = await step1.assert_reply(
+            "Please choose a color. (1) red, (2) green, or (3) blue"
+        )
+        step3 = await step2.send(_answer_message)
+        await step3.assert_reply("red")
+
+    async def test_should_create_prompt_with_list_choices_when_specified(self):
+        async def exec_test(turn_context: TurnContext):
+            dialog_context = await dialogs.create_context(turn_context)
+
+            results: DialogTurnResult = await dialog_context.continue_dialog()
+
+            if results.status == DialogTurnStatus.Empty:
+                options = PromptOptions(
+                    prompt=Activity(
+                        type=ActivityTypes.message, text="Please choose a color."
+                    ),
+                    choices=_color_choices,
+                )
+                await dialog_context.prompt("prompt", options)
+            elif results.status == DialogTurnStatus.Complete:
+                selected_choice = results.result
+                await turn_context.send_activity(selected_choice.value)
+
+            await convo_state.save_changes(turn_context)
+
+        adapter = TestAdapter(exec_test)
+
+        convo_state = ConversationState(MemoryStorage())
+        dialog_state = convo_state.create_property("dialogState")
+        dialogs = DialogSet(dialog_state)
+
+        choice_prompt = ChoicePrompt("prompt")
+        choice_prompt.style = ListStyle.list_style
+
+        dialogs.add(choice_prompt)
+
+        step1 = await adapter.send("Hello")
+        step2 = await step1.assert_reply(
+            "Please choose a color.\n\n   1. red\n   2. green\n   3. blue"
+        )
+        step3 = await step2.send(_answer_message)
+        await step3.assert_reply("red")
+
+    async def test_should_create_prompt_with_suggested_action_style_when_specified(
+        self,
+    ):
+        async def exec_test(turn_context: TurnContext):
+            dialog_context = await dialogs.create_context(turn_context)
+
+            results: DialogTurnResult = await dialog_context.continue_dialog()
+
+            if results.status == DialogTurnStatus.Empty:
+                options = PromptOptions(
+                    prompt=Activity(
+                        type=ActivityTypes.message, text="Please choose a color."
+                    ),
+                    choices=_color_choices,
+                    style=ListStyle.suggested_action,
+                )
+                await dialog_context.prompt("prompt", options)
+            elif results.status == DialogTurnStatus.Complete:
+                selected_choice = results.result
+                await turn_context.send_activity(selected_choice.value)
+
+            await convo_state.save_changes(turn_context)
+
+        adapter = TestAdapter(exec_test)
+
+        convo_state = ConversationState(MemoryStorage())
+        dialog_state = convo_state.create_property("dialogState")
+        dialogs = DialogSet(dialog_state)
+
+        choice_prompt = ChoicePrompt("prompt")
+
+        dialogs.add(choice_prompt)
+
+        step1 = await adapter.send("Hello")
+        step2 = await step1.assert_reply("Please choose a color.")
+        step3 = await step2.send(_answer_message)
+        await step3.assert_reply("red")
+
+    async def test_should_create_prompt_with_auto_style_when_specified(self):
+        async def exec_test(turn_context: TurnContext):
+            dialog_context = await dialogs.create_context(turn_context)
+
+            results: DialogTurnResult = await dialog_context.continue_dialog()
+
+            if results.status == DialogTurnStatus.Empty:
+                options = PromptOptions(
+                    prompt=Activity(
+                        type=ActivityTypes.message, text="Please choose a color."
+                    ),
+                    choices=_color_choices,
+                    style=ListStyle.auto,
+                )
+                await dialog_context.prompt("prompt", options)
+            elif results.status == DialogTurnStatus.Complete:
+                selected_choice = results.result
+                await turn_context.send_activity(selected_choice.value)
+
+            await convo_state.save_changes(turn_context)
+
+        adapter = TestAdapter(exec_test)
+
+        convo_state = ConversationState(MemoryStorage())
+        dialog_state = convo_state.create_property("dialogState")
+        dialogs = DialogSet(dialog_state)
+
+        choice_prompt = ChoicePrompt("prompt")
+
+        dialogs.add(choice_prompt)
+
+        step1 = await adapter.send("Hello")
+        step2 = await step1.assert_reply(
+            "Please choose a color. (1) red, (2) green, or (3) blue"
+        )
+        step3 = await step2.send(_answer_message)
+        await step3.assert_reply("red")
+
+    async def test_should_recognize_valid_number_choice(self):
+        async def exec_test(turn_context: TurnContext):
+            dialog_context = await dialogs.create_context(turn_context)
+
+            results: DialogTurnResult = await dialog_context.continue_dialog()
+
+            if results.status == DialogTurnStatus.Empty:
+                options = PromptOptions(
+                    prompt=Activity(
+                        type=ActivityTypes.message, text="Please choose a color."
+                    ),
+                    choices=_color_choices,
+                )
+                await dialog_context.prompt("prompt", options)
+            elif results.status == DialogTurnStatus.Complete:
+                selected_choice = results.result
+                await turn_context.send_activity(selected_choice.value)
+
+            await convo_state.save_changes(turn_context)
+
+        adapter = TestAdapter(exec_test)
+
+        convo_state = ConversationState(MemoryStorage())
+        dialog_state = convo_state.create_property("dialogState")
+        dialogs = DialogSet(dialog_state)
+
+        choice_prompt = ChoicePrompt("prompt")
+
+        dialogs.add(choice_prompt)
+
+        step1 = await adapter.send("Hello")
+        step2 = await step1.assert_reply(
+            "Please choose a color. (1) red, (2) green, or (3) blue"
+        )
+        step3 = await step2.send("1")
+        await step3.assert_reply("red")
+
+    async def test_should_display_choices_on_hero_card(self):
+        size_choices = ["large", "medium", "small"]
+
+        async def exec_test(turn_context: TurnContext):
+            dialog_context = await dialogs.create_context(turn_context)
+
+            results: DialogTurnResult = await dialog_context.continue_dialog()
+
+            if results.status == DialogTurnStatus.Empty:
+                options = PromptOptions(
+                    prompt=Activity(
+                        type=ActivityTypes.message, text="Please choose a size."
+                    ),
+                    choices=size_choices,
+                )
+                await dialog_context.prompt("prompt", options)
+            elif results.status == DialogTurnStatus.Complete:
+                selected_choice = results.result
+                await turn_context.send_activity(selected_choice.value)
+
+            await convo_state.save_changes(turn_context)
+
+        def assert_expected_activity(
+            activity: Activity, description
+        ):  # pylint: disable=unused-argument
+            assert len(activity.attachments) == 1
+            assert (
+                activity.attachments[0].content_type
+                == CardFactory.content_types.hero_card
+            )
+            assert activity.attachments[0].content.text == "Please choose a size."
+
+        adapter = TestAdapter(exec_test)
+
+        convo_state = ConversationState(MemoryStorage())
+        dialog_state = convo_state.create_property("dialogState")
+        dialogs = DialogSet(dialog_state)
+
+        choice_prompt = ChoicePrompt("prompt")
+
+        # Change the ListStyle of the prompt to ListStyle.none.
+        choice_prompt.style = ListStyle.hero_card
+
+        dialogs.add(choice_prompt)
+
+        step1 = await adapter.send("Hello")
+        step2 = await step1.assert_reply(assert_expected_activity)
+        step3 = await step2.send("1")
+        await step3.assert_reply(size_choices[0])
+
+    async def test_should_display_choices_on_hero_card_with_additional_attachment(self):
+        size_choices = ["large", "medium", "small"]
+        card = CardFactory.adaptive_card(
+            {
+                "type": "AdaptiveCard",
+                "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
+                "version": "1.2",
+                "body": [],
+            }
+        )
+        card_activity = Activity(attachments=[card])
+
+        async def exec_test(turn_context: TurnContext):
+            dialog_context = await dialogs.create_context(turn_context)
+
+            results: DialogTurnResult = await dialog_context.continue_dialog()
+
+            if results.status == DialogTurnStatus.Empty:
+                options = PromptOptions(prompt=card_activity, choices=size_choices)
+                await dialog_context.prompt("prompt", options)
+            elif results.status == DialogTurnStatus.Complete:
+                selected_choice = results.result
+                await turn_context.send_activity(selected_choice.value)
+
+            await convo_state.save_changes(turn_context)
+
+        def assert_expected_activity(
+            activity: Activity, description
+        ):  # pylint: disable=unused-argument
+            assert len(activity.attachments) == 2
+            assert (
+                activity.attachments[0].content_type
+                == CardFactory.content_types.adaptive_card
+            )
+            assert (
+                activity.attachments[1].content_type
+                == CardFactory.content_types.hero_card
+            )
+
+        adapter = TestAdapter(exec_test)
+
+        convo_state = ConversationState(MemoryStorage())
+        dialog_state = convo_state.create_property("dialogState")
+        dialogs = DialogSet(dialog_state)
+
+        choice_prompt = ChoicePrompt("prompt")
+
+        # Change the ListStyle of the prompt to ListStyle.none.
+        choice_prompt.style = ListStyle.hero_card
+
+        dialogs.add(choice_prompt)
+
+        step1 = await adapter.send("Hello")
+        await step1.assert_reply(assert_expected_activity)
+
+    async def test_should_not_find_a_choice_in_an_utterance_by_ordinal(self):
+        found = ChoiceRecognizers.recognize_choices(
+            "the first one please",
+            _color_choices,
+            FindChoicesOptions(recognize_numbers=False, recognize_ordinals=False),
+        )
+        assert not found
+
+    async def test_should_not_find_a_choice_in_an_utterance_by_numerical_index(self):
+        found = ChoiceRecognizers.recognize_choices(
+            "one",
+            _color_choices,
+            FindChoicesOptions(recognize_numbers=False, recognize_ordinals=False),
+        )
+        assert not found