Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ async def assert_reply(
:param timeout:
:return:
"""

# TODO: refactor method so expected can take a Callable[[Activity], None]
def default_inspector(reply, description=None):
if isinstance(expected, Activity):
validate_activity(reply, expected)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

from typing import List
from typing import List, Union

from botbuilder.core import CardFactory, MessageFactory
from botbuilder.schema import ActionTypes, Activity, CardAction, HeroCard, InputHints
Expand All @@ -17,7 +17,7 @@ class ChoiceFactory:
@staticmethod
def for_channel(
channel_id: str,
choices: List[Choice],
choices: List[Union[str, Choice]],
text: str = None,
speak: str = None,
options: ChoiceFactoryOptions = None,
Expand All @@ -36,8 +36,7 @@ def for_channel(
if channel_id is None:
channel_id = ""

if choices is None:
choices = []
choices = ChoiceFactory._to_choices(choices)

# Find maximum title length
max_title_length = 0
Expand Down Expand Up @@ -74,7 +73,7 @@ def for_channel(

@staticmethod
def inline(
choices: List[Choice],
choices: List[Union[str, Choice]],
text: str = None,
speak: str = None,
options: ChoiceFactoryOptions = None,
Expand All @@ -89,8 +88,7 @@ def inline(
speak: (Optional) SSML. Text to be spoken by your bot on a speech-enabled channel.
options: (Optional) The formatting options to use to tweak rendering of list.
"""
if choices is None:
choices = []
choices = ChoiceFactory._to_choices(choices)

if options is None:
options = ChoiceFactoryOptions()
Expand Down Expand Up @@ -134,7 +132,7 @@ def inline(

@staticmethod
def list_style(
choices: List[Choice],
choices: List[Union[str, Choice]],
text: str = None,
speak: str = None,
options: ChoiceFactoryOptions = None,
Expand All @@ -153,8 +151,7 @@ def list_style(

options: (Optional) The formatting options to use to tweak rendering of list.
"""
if choices is None:
choices = []
choices = ChoiceFactory._to_choices(choices)
if options is None:
options = ChoiceFactoryOptions()

Expand Down Expand Up @@ -206,7 +203,7 @@ def suggested_action(

@staticmethod
def hero_card(
choices: List[Choice], text: str = None, speak: str = None
choices: List[Union[Choice, str]], text: str = None, speak: str = None
) -> Activity:
"""
Creates a message activity that includes a lsit of coices that have been added as `HeroCard`'s
Expand All @@ -221,18 +218,22 @@ def hero_card(
)

@staticmethod
def _to_choices(choices: List[str]) -> List[Choice]:
def _to_choices(choices: List[Union[str, Choice]]) -> List[Choice]:
"""
Takes a list of strings and returns them as [`Choice`].
"""
if choices is None:
return []
return [Choice(value=choice.value) for choice in choices]
return [
Choice(value=choice) if isinstance(choice, str) else choice
for choice in choices
]

@staticmethod
def _extract_actions(choices: List[Choice]) -> List[CardAction]:
def _extract_actions(choices: List[Union[str, Choice]]) -> List[CardAction]:
if choices is None:
choices = []
choices = ChoiceFactory._to_choices(choices)
card_actions: List[CardAction] = []
for choice in choices:
if choice.action is not None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,11 @@ def default() -> Activity:
):
prompt.suggested_actions = msg.suggested_actions

if msg.attachments is not None and msg.attachments:
prompt.attachments = msg.attachments
if msg.attachments:
if prompt.attachments:
prompt.attachments.extend(msg.attachments)
else:
prompt.attachments = msg.attachments

return prompt

Expand Down
107 changes: 106 additions & 1 deletion libraries/botbuilder-dialogs/tests/test_choice_prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import aiounittest
from recognizers_text import Culture

from botbuilder.core import ConversationState, MemoryStorage, TurnContext
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
Expand Down Expand Up @@ -588,3 +588,108 @@ async def exec_test(turn_context: TurnContext):
)
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)