Repository: microsoft/botbuilder-python

Issue:
ChoiceFactory forces usage of hardcoded ChoiceFactoryOptions; can therefore only use en-us locale
## Version
python v4.4

## Describe the bug
* `ChoiceFactory.inline()` is broken, in that it does not use the passed in `options` parameter, and only uses `opt`, the hard-coded "or" separators
* this in turn means that English is the only locale that prompts are able to use
* also, due to https://github.com/microsoft/botbuilder-python/issues/240, we can't move away from inline styling, therefore we can't move away from `en-us` locale

## Expected behavior
To use the locale specified, and not only stick to `en-us`



Discussion:

Resolving patch:
diff --git a/libraries/botbuilder-dialogs/botbuilder/dialogs/prompts/choice_prompt.py b/libraries/botbuilder-dialogs/botbuilder/dialogs/prompts/choice_prompt.py
--- a/libraries/botbuilder-dialogs/botbuilder/dialogs/prompts/choice_prompt.py
+++ b/libraries/botbuilder-dialogs/botbuilder/dialogs/prompts/choice_prompt.py
@@ -30,28 +30,52 @@ class ChoicePrompt(Prompt):
 
     _default_choice_options: Dict[str, ChoiceFactoryOptions] = {
         Culture.Spanish: ChoiceFactoryOptions(
-            inline_separator=", ", inline_or=" o ", include_numbers=True
+            inline_separator=", ",
+            inline_or=" o ",
+            inline_or_more=", o ",
+            include_numbers=True,
         ),
         Culture.Dutch: ChoiceFactoryOptions(
-            inline_separator=", ", inline_or=" of ", include_numbers=True
+            inline_separator=", ",
+            inline_or=" of ",
+            inline_or_more=", of ",
+            include_numbers=True,
         ),
         Culture.English: ChoiceFactoryOptions(
-            inline_separator=", ", inline_or=" or ", include_numbers=True
+            inline_separator=", ",
+            inline_or=" or ",
+            inline_or_more=", or ",
+            include_numbers=True,
         ),
         Culture.French: ChoiceFactoryOptions(
-            inline_separator=", ", inline_or=" ou ", include_numbers=True
+            inline_separator=", ",
+            inline_or=" ou ",
+            inline_or_more=", ou ",
+            include_numbers=True,
         ),
         "de-de": ChoiceFactoryOptions(
-            inline_separator=", ", inline_or=" oder ", include_numbers=True
+            inline_separator=", ",
+            inline_or=" oder ",
+            inline_or_more=", oder ",
+            include_numbers=True,
         ),
         Culture.Japanese: ChoiceFactoryOptions(
-            inline_separator="、 ", inline_or=" または ", include_numbers=True
+            inline_separator="、 ",
+            inline_or=" または ",
+            inline_or_more="、 または ",
+            include_numbers=True,
         ),
         Culture.Portuguese: ChoiceFactoryOptions(
-            inline_separator=", ", inline_or=" ou ", include_numbers=True
+            inline_separator=", ",
+            inline_or=" ou ",
+            inline_or_more=", ou ",
+            include_numbers=True,
         ),
         Culture.Chinese: ChoiceFactoryOptions(
-            inline_separator="， ", inline_or=" 要么 ", include_numbers=True
+            inline_separator="， ",
+            inline_or=" 要么 ",
+            inline_or_more="， 要么 ",
+            include_numbers=True,
         ),
     }
 
diff --git a/libraries/botbuilder-dialogs/botbuilder/dialogs/prompts/prompt.py b/libraries/botbuilder-dialogs/botbuilder/dialogs/prompts/prompt.py
--- a/libraries/botbuilder-dialogs/botbuilder/dialogs/prompts/prompt.py
+++ b/libraries/botbuilder-dialogs/botbuilder/dialogs/prompts/prompt.py
@@ -2,7 +2,7 @@
 # Licensed under the MIT License.
 
 import copy
-from typing import Dict
+from typing import Dict, List
 from .prompt_options import PromptOptions
 from .prompt_validator_context import PromptValidatorContext
 from ..dialog_reason import DialogReason
@@ -12,7 +12,12 @@
 from ..dialog_context import DialogContext
 from botbuilder.core.turn_context import TurnContext
 from botbuilder.schema import InputHints, ActivityTypes
-from botbuilder.dialogs.choices import ChoiceFactory
+from botbuilder.dialogs.choices import (
+    Choice,
+    ChoiceFactory,
+    ChoiceFactoryOptions,
+    ListStyle,
+)
 
 from abc import abstractmethod
 from botbuilder.schema import Activity
@@ -142,16 +147,13 @@ async def on_recognize(
     ):
         pass
 
-    # TODO: Fix choices to use Choice object when ported.
-    # TODO: Fix style to use ListStyle when ported.
-    # TODO: Fix options to use ChoiceFactoryOptions object when ported.
     def append_choices(
         self,
         prompt: Activity,
         channel_id: str,
-        choices: object,
-        style: object,
-        options: object = None,
+        choices: List[Choice],
+        style: ListStyle,
+        options: ChoiceFactoryOptions = None,
     ) -> Activity:
         """
         Helper function to compose an output activity containing a set of choices.
@@ -187,23 +189,24 @@ def hero_card() -> Activity:
             return ChoiceFactory.hero_card(choices, text)
 
         def list_style_none() -> Activity:
-            activity = Activity()
+            activity = Activity(type=ActivityTypes.message)
             activity.text = text
             return activity
 
         def default() -> Activity:
             return ChoiceFactory.for_channel(channel_id, choices, text, None, options)
 
+        # Maps to values in ListStyle Enum
         switcher = {
-            # ListStyle.inline
-            1: inline,
-            2: list_style,
-            3: suggested_action,
-            4: hero_card,
-            5: list_style_none,
+            0: list_style_none,
+            1: default,
+            2: inline,
+            3: list_style,
+            4: suggested_action,
+            5: hero_card,
         }
 
-        msg = switcher.get(style, default)()
+        msg = switcher.get(int(style.value), default)()
 
         # Update prompt with text, actions and attachments
         if not prompt:


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
@@ -267,11 +267,8 @@ async def validator(prompt: PromptValidatorContext) -> bool:
         dialogs.add(choice_prompt)
 
         step1 = await adapter.send(Activity(type=ActivityTypes.message, text="Hello"))
-        # TODO ChoiceFactory.inline() is broken, where it only uses hard-coded English locale.
-        # commented out the CORRECT assertion below, until .inline() is fixed to use proper locale
-        # step2 = await step1.assert_reply('Please choose a color. (1) red, (2) green, o (3) blue')
         step2 = await step1.assert_reply(
-            "Please choose a color. (1) red, (2) green, or (3) blue"
+            "Please choose a color. (1) red, (2) green, o (3) blue"
         )
         step3 = await step2.send(_invalid_message)
         step4 = await step3.assert_reply("Bad input.")
@@ -318,11 +315,8 @@ async def validator(prompt: PromptValidatorContext) -> bool:
         step1 = await adapter.send(
             Activity(type=ActivityTypes.message, text="Hello", locale=Culture.Spanish)
         )
-        # TODO ChoiceFactory.inline() is broken, where it only uses hard-coded English locale.
-        # commented out the CORRECT assertion below, until .inline() is fixed to use proper locale
-        # step2 = await step1.assert_reply('Please choose a color. (1) red, (2) green, o (3) blue')
         step2 = await step1.assert_reply(
-            "Please choose a color. (1) red, (2) green, or (3) blue"
+            "Please choose a color. (1) red, (2) green, o (3) blue"
         )
         step3 = await step2.send(_answer_message)
         await step3.assert_reply("red")
@@ -377,9 +371,7 @@ async def validator(prompt: PromptValidatorContext) -> bool:
         step3 = await step2.send(_answer_message)
         await step3.assert_reply("red")
 
-    async def test_should_not_render_choices_and_not_blow_up_if_choices_are_not_passed_in(
-        self
-    ):
+    async def test_should_not_render_choices_if_list_style_none_is_specified(self):
         async def exec_test(turn_context: TurnContext):
             dc = await dialogs.create_context(turn_context)
 
@@ -390,7 +382,8 @@ async def exec_test(turn_context: TurnContext):
                     prompt=Activity(
                         type=ActivityTypes.message, text="Please choose a color."
                     ),
-                    choices=None,
+                    choices=_color_choices,
+                    style=ListStyle.none,
                 )
                 await dc.prompt("prompt", options)
             elif results.status == DialogTurnStatus.Complete:
@@ -406,18 +399,15 @@ async def exec_test(turn_context: TurnContext):
         dialogs = DialogSet(dialog_state)
 
         choice_prompt = ChoicePrompt("prompt")
-        choice_prompt.style = ListStyle.none
 
         dialogs.add(choice_prompt)
 
         step1 = await adapter.send("Hello")
-        await step1.assert_reply("Please choose a color.")
+        step2 = await step1.assert_reply("Please choose a color.")
+        step3 = await step2.send(_answer_message)
+        await step3.assert_reply("red")
 
-    # TODO to create parity with JS, need to refactor this so that it does not blow up when choices are None
-    # Possibly does not work due to the side effect of list styles not applying
-    # Note: step2 only appears to pass as ListStyle.none, probably because choices is None, and therefore appending
-    # nothing to the prompt text
-    async def test_should_not_recognize_if_choices_are_not_passed_in(self):
+    async def test_should_create_prompt_with_inline_choices_when_specified(self):
         async def exec_test(turn_context: TurnContext):
             dc = await dialogs.create_context(turn_context)
 
@@ -428,7 +418,7 @@ async def exec_test(turn_context: TurnContext):
                     prompt=Activity(
                         type=ActivityTypes.message, text="Please choose a color."
                     ),
-                    choices=None,
+                    choices=_color_choices,
                 )
                 await dc.prompt("prompt", options)
             elif results.status == DialogTurnStatus.Complete:
@@ -444,17 +434,18 @@ async def exec_test(turn_context: TurnContext):
         dialogs = DialogSet(dialog_state)
 
         choice_prompt = ChoicePrompt("prompt")
-        choice_prompt.style = ListStyle.none
+        choice_prompt.style = ListStyle.in_line
 
         dialogs.add(choice_prompt)
 
         step1 = await adapter.send("Hello")
-        step2 = await step1.assert_reply("Please choose a color.")
-        # TODO uncomment when styling is fixed for prompts - assertions should pass
-        # step3 = await step2.send('hello')
-        # await step3.assert_reply('Please choose a color.')
+        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_inline_choices_when_specified(self):
+    async def test_should_create_prompt_with_list_choices_when_specified(self):
         async def exec_test(turn_context: TurnContext):
             dc = await dialogs.create_context(turn_context)
 
@@ -481,20 +472,20 @@ async def exec_test(turn_context: TurnContext):
         dialogs = DialogSet(dialog_state)
 
         choice_prompt = ChoicePrompt("prompt")
-        choice_prompt.style = ListStyle.in_line
+        choice_prompt.style = ListStyle.list_style
 
         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"
+            "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")
 
-    # TODO fix test to actually test for list_style instead of inline
-    # currently bug where all styling is ignored and only does inline styling for prompts
-    async def test_should_create_prompt_with_list_choices_when_specified(self):
+    async def test_should_create_prompt_with_suggested_action_style_when_specified(
+        self
+    ):
         async def exec_test(turn_context: TurnContext):
             dc = await dialogs.create_context(turn_context)
 
@@ -506,6 +497,43 @@ async def exec_test(turn_context: TurnContext):
                         type=ActivityTypes.message, text="Please choose a color."
                     ),
                     choices=_color_choices,
+                    style=ListStyle.suggested_action,
+                )
+                await dc.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):
+            dc = await dialogs.create_context(turn_context)
+
+            results: DialogTurnResult = await dc.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 dc.prompt("prompt", options)
             elif results.status == DialogTurnStatus.Complete:
@@ -521,14 +549,10 @@ async def exec_test(turn_context: TurnContext):
         dialogs = DialogSet(dialog_state)
 
         choice_prompt = ChoicePrompt("prompt")
-        choice_prompt.style = ListStyle.list_style
 
         dialogs.add(choice_prompt)
 
         step1 = await adapter.send("Hello")
-        # TODO uncomment assertion when prompt styling has been fixed - assertion should pass with list_style
-        # Also be sure to remove inline assertion currently being tested below
-        # step2 = await step1.assert_reply('Please choose a color.\n\n   1. red\n   2. green\n   3. blue')
         step2 = await step1.assert_reply(
             "Please choose a color. (1) red, (2) green, or (3) blue"
         )