async def test_do_not_propagate_previous_message_to_the_loop(): trigger_hi_again = SimpleTrigger() with answer.Talk() as talk: story = talk.story @story.on('hi!') def one_job(): @story.part() def show_do_job(ctx): pass @story.loop() def job_scope(): @story.on('hi!') def hi_again(): @story.part() def store_hi_again(): trigger_hi_again.passed() @story.on('?') def local_help_story(): @story.part() def show_local_help(ctx): pass @story.on('work') def job_story(): @story.part() def do_some_job(ctx): pass await talk.pure_text('hi!') assert trigger_hi_again.is_triggered is not True
async def test_match_fblike_sticker(): trigger_any = utils.SimpleTrigger() trigger_like = utils.SimpleTrigger() with answer.Talk() as talk: story = talk.story @story.on(sticker.Like()) def like_sticker_story(): @story.part() def then(ctx): trigger_like.passed() @story.on(sticker.Any()) def any_sticker_story(): @story.part() def then(ctx): trigger_any.passed() await talk.ask({ 'sticker_id': sticker.LIKE_STICKERS[0], }) assert trigger_like.is_triggered await talk.ask({ 'sticker_id': '1', }) assert trigger_like.is_triggered
async def test_ask_location(mock_interface): with answer.Talk() as talk: story = talk.story story.use(mock_interface) @story.on('SOS!') def one_story(): @story.part() async def then(message): await story.ask_location('Hey, bro! Where is your rocket?', message['user']) await talk.pure_text('SOS!') mock_interface.send_text_message.assert_called_once_with( talk.user, text='Hey, bro! Where is your rocket?')
async def test_match_any_sticker(): trigger = utils.SimpleTrigger() with answer.Talk() as talk: story = talk.story @story.on(sticker.Any()) def sticker_story(): @story.part() def then(ctx): trigger.passed() await talk.ask({ 'sticker_id': sticker.LIKE_STICKERS[0], }) assert trigger.is_triggered
async def test_loop_inside_of_loop(): trigger_global_action1 = SimpleTrigger() trigger_inner_action1 = SimpleTrigger() with answer.Talk() as talk: story = talk.story @story.on('action1') def global_action1(): @story.part() def trigger_action1(ctx): trigger_global_action1.passed() @story.on('action2') def global_action2(): @story.loop() def outer_loop(): @story.on('action1') def outer_action1(): @story.loop() def inner_loop(): @story.on('action1') def inner_action1(): @story.part() def inner_action1_part(ctx): trigger_inner_action1.passed() @story.on('action2') def inner_action2(): @story.part() def inner_action2_part(ctx): pass @story.on('action2') def outer_action2(): @story.part() def do_some_job(ctx): pass await talk.pure_text('action2') await talk.pure_text('action1') await talk.pure_text('action1') assert trigger_inner_action1.is_triggered is True assert trigger_global_action1.is_triggered is not True
async def test_should_say(mock_interface): with answer.Talk() as talk: story = talk.story story.use(mock_interface) @story.on('hi there!') def one_story(): @story.part() async def then(message): await story.say('Nice to see you!', message['user']) await talk.pure_text('hi there!') mock_interface.send_text_message.assert_called_once_with( recipient=talk.user, text='Nice to see you!', options=None, )
async def test_break_loop_on_unmatched_message(): action1_trigger = SimpleTrigger(0) action2_after_part_trigger = SimpleTrigger(0) with answer.Talk() as talk: story = talk.story @story.on('action1') def action1(): @story.part() def action1_part(ctx): pass @story.on('action2') def action2(): @story.part() def action2_part(ctx): pass @story.loop() def action2_loop(): @story.on('action1') def action2_loop_action1(): @story.part() def action2_loop_action1_part(ctx): action1_trigger.inc() @story.on('action2') def action2_loop_action2(): @story.part() def action2_loop_action2_part(ctx): pass @story.part() def action2_after_part(ctx): action2_after_part_trigger.inc() await talk.pure_text('action2') await talk.pure_text('action1') await talk.pure_text('action1') await talk.pure_text('action3') assert action1_trigger.value == 2 assert action2_after_part_trigger.value == 1
async def test_prevent_message_propagation_from_outer_loop_to_an_inner_loop(): trigger_1 = SimpleTrigger(0) trigger_2 = SimpleTrigger(0) trigger_3 = SimpleTrigger(0) with answer.Talk() as talk: story = talk.story @story.on('action1') # 1 def global_action1(): @story.part() def pre_1(ctx): trigger_1.inc() @story.loop() def outer_loop(): @story.on('action1') # 2, 4 def outer_action1(): @story.part() def pre_2(ctx): trigger_2.inc() @story.loop() def inner_loop(): @story.on('action1') # 3 def inner_action1(): @story.part() def inner_action1_part(ctx): trigger_3.inc() return loop.BreakLoop() @story.part() def pre_3(ctx): pass await talk.pure_text('action1') await talk.pure_text('action1') await talk.pure_text('action1') await talk.pure_text('action1') assert trigger_1.value == 1 assert trigger_2.value == 2 assert trigger_3.value == 1
async def test_breaking_the_loop(): trigger_show_global_help = SimpleTrigger() trigger_show_local_help = SimpleTrigger(0) with answer.Talk() as talk: story = talk.story @story.on('?') def global_help_story(): @story.part() def show_global_help(ctx): trigger_show_global_help.passed() @story.on('start job') def one_job(): @story.part() def show_do_job(ctx): pass @story.loop() def job_scope(): @story.on('?') def local_help_story(): @story.part() def show_local_help(ctx): trigger_show_local_help.inc() @story.on('work') def job_story(): @story.part() def do_some_job(ctx): return loop.BreakLoop() await talk.pure_text('start job') await talk.pure_text('?') await talk.pure_text('work') await talk.pure_text('?') assert trigger_show_local_help.value == 1 assert trigger_show_global_help.is_triggered is True
async def test_get_location_as_result_of_asking_of_location(mock_interface): trigger = SimpleTrigger() with answer.Talk() as talk: story = talk.story story.use(mock_interface) @story.on('SOS!') def one_story(): @story.part() def then(ctx): return story.ask_location('Hey, bro! Where is your rocket?', ctx['user']) @story.part() def then(ctx): trigger.receive(location.get_location(ctx)) await talk.pure_text('SOS!') await talk.location('somewhere') assert trigger.result() == 'somewhere'
async def test_match_different_size_of_fblike(match_size, message_size): trigger_any = utils.SimpleTrigger() trigger_likes = { size: utils.SimpleTrigger() for size in list(sticker.LIKE_STICKERS) } with answer.Talk() as talk: story = talk.story @story.on(sticker.Like(sticker.SMALL_LIKE)) def small_like_sticker_story(): @story.part() def then(ctx): trigger_likes[sticker.SMALL_LIKE].passed() @story.on(sticker.Like(sticker.MEDIUM_LIKE)) def medium_like_sticker_story(): @story.part() def then(ctx): trigger_likes[sticker.MEDIUM_LIKE].passed() @story.on(sticker.Like(sticker.BIG_LIKE)) def big_like_sticker_story(): @story.part() def then(ctx): trigger_likes[sticker.BIG_LIKE].passed() @story.on(sticker.Any()) def any_like_sticker_story(): @story.part() def then(ctx): trigger_any.passed() await talk.ask({ 'sticker_id': message_size, }) assert trigger_likes[match_size].is_triggered
async def test_jump_in_a_loop(): trigger_show_global_help = SimpleTrigger() trigger_show_local_help = SimpleTrigger() with answer.Talk() as talk: story = talk.story @story.on('?') def global_help_story(): @story.part() def show_global_help(ctx): trigger_show_global_help.passed() @story.on('start job') def one_job(): @story.part() def show_do_job(ctx): pass @story.loop() def job_scope(): @story.on('?') def local_help_story(): @story.part() def show_local_help(ctx): trigger_show_local_help.passed() @story.on('work') def job_story(): @story.part() def do_some_job(ctx): pass await talk.pure_text('start job') await talk.pure_text('?') assert trigger_show_global_help.is_triggered is not True assert trigger_show_local_help.is_triggered is True
async def test_send_list(): with answer.Talk() as talk: story = talk.story fb_interface = story.use( messenger.FBInterface(page_access_token='qwerty1')) mock_http = story.use(mockhttp.MockHttpInterface()) await story.start() await fb_interface.send_list( recipient=talk.user, elements=[ { 'title': 'Classic T-Shirt Collection', # (*) required 'image_url': 'https://peterssendreceiveapp.ngrok.io/img/collection.png', 'subtitle': 'See all our colors', 'default_action': { 'type': 'web_url', 'url': 'https://peterssendreceiveapp.ngrok.io/shop_collection', 'messenger_extensions': True, 'webview_height_ratio': 'tall', 'fallback_url': 'https://peterssendreceiveapp.ngrok.io/' }, 'buttons': [{ 'title': 'View', 'type': 'web_url', 'url': 'https://peterssendreceiveapp.ngrok.io/collection', 'messenger_extensions': True, 'webview_height_ratio': 'tall', 'fallback_url': 'https://peterssendreceiveapp.ngrok.io/' }] }, { 'title': 'Classic White T-Shirt', 'image_url': 'https://peterssendreceiveapp.ngrok.io/img/white-t-shirt.png', 'subtitle': '100% Cotton, 200% Comfortable', 'default_action': { 'type': 'web_url', 'url': 'https://peterssendreceiveapp.ngrok.io/view?item=100', 'messenger_extensions': True, 'webview_height_ratio': 'tall', 'fallback_url': 'https://peterssendreceiveapp.ngrok.io/' }, 'buttons': [{ 'title': 'Shop Now', 'type': 'web_url', 'url': 'https://peterssendreceiveapp.ngrok.io/shop?item=100', 'messenger_extensions': True, 'webview_height_ratio': 'tall', 'fallback_url': 'https://peterssendreceiveapp.ngrok.io/' }] }, { 'title': 'Classic Blue T-Shirt', 'image_url': 'https://peterssendreceiveapp.ngrok.io/img/blue-t-shirt.png', 'subtitle': '100% Cotton, 200% Comfortable', 'default_action': { 'type': 'web_url', 'url': 'https://peterssendreceiveapp.ngrok.io/view?item=101', 'messenger_extensions': True, 'webview_height_ratio': 'tall', 'fallback_url': 'https://peterssendreceiveapp.ngrok.io/' }, 'buttons': [{ 'title': 'Shop Now', 'type': 'web_url', 'url': 'https://peterssendreceiveapp.ngrok.io/shop?item=101', 'messenger_extensions': True, 'webview_height_ratio': 'tall', 'fallback_url': 'https://peterssendreceiveapp.ngrok.io/' }] }, { 'title': 'Classic Black T-Shirt', 'image_url': 'https://peterssendreceiveapp.ngrok.io/img/black-t-shirt.png', 'subtitle': '100% Cotton, 200% Comfortable', 'default_action': { 'type': 'web_url', 'url': 'https://peterssendreceiveapp.ngrok.io/view?item=102', 'messenger_extensions': True, 'webview_height_ratio': 'tall', 'fallback_url': 'https://peterssendreceiveapp.ngrok.io/' }, 'buttons': [{ 'title': 'Shop Now', 'type': 'web_url', 'url': 'https://peterssendreceiveapp.ngrok.io/shop?item=102', 'messenger_extensions': True, 'webview_height_ratio': 'tall', 'fallback_url': 'https://peterssendreceiveapp.ngrok.io/' }] } ], buttons=[{ 'title': 'View More', 'payload': 'payload', }]) mock_http.post.assert_called_with( 'https://graph.facebook.com/v2.6/me/messages/', params={ 'access_token': 'qwerty1', }, json={ 'message': { 'attachment': { 'type': 'template', 'payload': { 'template_type': 'list', 'top_element_style': 'large', 'elements': [ { 'title': 'Classic T-Shirt Collection', # (*) required 'image_url': 'https://peterssendreceiveapp.ngrok.io/img/collection.png', 'subtitle': 'See all our colors', 'default_action': { 'type': 'web_url', 'url': 'https://peterssendreceiveapp.ngrok.io/shop_collection', 'messenger_extensions': True, 'webview_height_ratio': 'tall', 'fallback_url': 'https://peterssendreceiveapp.ngrok.io/' }, 'buttons': [{ 'title': 'View', 'type': 'web_url', 'url': 'https://peterssendreceiveapp.ngrok.io/collection', 'messenger_extensions': True, 'webview_height_ratio': 'tall', 'fallback_url': 'https://peterssendreceiveapp.ngrok.io/' }] }, { 'title': 'Classic White T-Shirt', 'image_url': 'https://peterssendreceiveapp.ngrok.io/img/white-t-shirt.png', 'subtitle': '100% Cotton, 200% Comfortable', 'default_action': { 'type': 'web_url', 'url': 'https://peterssendreceiveapp.ngrok.io/view?item=100', 'messenger_extensions': True, 'webview_height_ratio': 'tall', 'fallback_url': 'https://peterssendreceiveapp.ngrok.io/' }, 'buttons': [{ 'title': 'Shop Now', 'type': 'web_url', 'url': 'https://peterssendreceiveapp.ngrok.io/shop?item=100', 'messenger_extensions': True, 'webview_height_ratio': 'tall', 'fallback_url': 'https://peterssendreceiveapp.ngrok.io/' }] }, { 'title': 'Classic Blue T-Shirt', 'image_url': 'https://peterssendreceiveapp.ngrok.io/img/blue-t-shirt.png', 'subtitle': '100% Cotton, 200% Comfortable', 'default_action': { 'type': 'web_url', 'url': 'https://peterssendreceiveapp.ngrok.io/view?item=101', 'messenger_extensions': True, 'webview_height_ratio': 'tall', 'fallback_url': 'https://peterssendreceiveapp.ngrok.io/' }, 'buttons': [{ 'title': 'Shop Now', 'type': 'web_url', 'url': 'https://peterssendreceiveapp.ngrok.io/shop?item=101', 'messenger_extensions': True, 'webview_height_ratio': 'tall', 'fallback_url': 'https://peterssendreceiveapp.ngrok.io/' }] }, { 'title': 'Classic Black T-Shirt', 'image_url': 'https://peterssendreceiveapp.ngrok.io/img/black-t-shirt.png', 'subtitle': '100% Cotton, 200% Comfortable', 'default_action': { 'type': 'web_url', 'url': 'https://peterssendreceiveapp.ngrok.io/view?item=102', 'messenger_extensions': True, 'webview_height_ratio': 'tall', 'fallback_url': 'https://peterssendreceiveapp.ngrok.io/' }, 'buttons': [{ 'title': 'Shop Now', 'type': 'web_url', 'url': 'https://peterssendreceiveapp.ngrok.io/shop?item=102', 'messenger_extensions': True, 'webview_height_ratio': 'tall', 'fallback_url': 'https://peterssendreceiveapp.ngrok.io/' }] } ], 'buttons': [{ 'title': 'View More', 'type': 'postback', 'payload': 'payload' }] } } }, 'recipient': { 'id': talk.user['facebook_user_id'], }, })
async def test_should_send_tempalate_based_message(mock_interface): with answer.Talk() as talk: story = talk.story story.use(mock_interface) payload = { 'template_type': 'receipt', 'recipient_name': 'Stephane Crozatier', 'order_number': '12345678902', 'currency': 'USD', 'payment_method': 'Visa 2345', 'order_url': 'http://petersapparel.parseapp.com/order?order_id=123456', 'timestamp': '1428444852', 'elements': [{ 'title': 'Classic White T-Shirt', 'subtitle': '100% Soft and Luxurious Cotton', 'quantity': 2, 'price': 50, 'currency': 'USD', 'image_url': 'http://petersapparel.parseapp.com/img/whiteshirt.png' }, { 'title': 'Classic Gray T-Shirt', 'subtitle': '100% Soft and Luxurious Cotton', 'quantity': 1, 'price': 25, 'currency': 'USD', 'image_url': 'http://petersapparel.parseapp.com/img/grayshirt.png' }], 'address': { 'street_1': '1 Hacker Way', 'street_2': '', 'city': 'Menlo Park', 'postal_code': '94025', 'state': 'CA', 'country': 'US' }, 'summary': { 'subtotal': 75.00, 'shipping_cost': 4.95, 'total_tax': 6.19, 'total_cost': 56.14 }, 'adjustments': [{ 'name': 'New Customer Discount', 'amount': 20 }, { 'name': '$10 Off Coupon', 'amount': 10 }] } await story.send_template(payload, user=talk.user) mock_interface.send_template.assert_called_once_with( recipient=talk.user, payload=payload)
async def test_should_list_elements(mock_interface): with answer.Talk() as talk: story = talk.story story.use(mock_interface) @story.on('hi there!') def one_story(): @story.part() async def then(ctx): await story.list_elements( elements=[ { 'title': 'Classic T-Shirt Collection', # (*) required 'image_url': 'https://peterssendreceiveapp.ngrok.io/img/collection.png', 'subtitle': 'See all our colors', 'default_action': { 'type': 'web_url', 'url': 'https://peterssendreceiveapp.ngrok.io/shop_collection', 'messenger_extensions': True, 'webview_height_ratio': 'tall', 'fallback_url': 'https://peterssendreceiveapp.ngrok.io/' }, 'buttons': [{ 'title': 'View', 'type': 'web_url', 'url': 'https://peterssendreceiveapp.ngrok.io/collection', 'messenger_extensions': True, 'webview_height_ratio': 'tall', 'fallback_url': 'https://peterssendreceiveapp.ngrok.io/' }] }, { 'title': 'Classic White T-Shirt', 'image_url': 'https://peterssendreceiveapp.ngrok.io/img/white-t-shirt.png', 'subtitle': '100% Cotton, 200% Comfortable', 'default_action': { 'type': 'web_url', 'url': 'https://peterssendreceiveapp.ngrok.io/view?item=100', 'messenger_extensions': True, 'webview_height_ratio': 'tall', 'fallback_url': 'https://peterssendreceiveapp.ngrok.io/' }, 'buttons': [{ 'title': 'Shop Now', 'type': 'web_url', 'url': 'https://peterssendreceiveapp.ngrok.io/shop?item=100', 'messenger_extensions': True, 'webview_height_ratio': 'tall', 'fallback_url': 'https://peterssendreceiveapp.ngrok.io/' }] } ], buttons=[{ 'title': 'View More', 'payload': 'payload', }], user=ctx['user']) await talk.ask('hi there!') mock_interface.send_list.assert_called_once_with( recipient=talk.user, elements=[ { 'title': 'Classic T-Shirt Collection', # (*) required 'image_url': 'https://peterssendreceiveapp.ngrok.io/img/collection.png', 'subtitle': 'See all our colors', 'default_action': { 'type': 'web_url', 'url': 'https://peterssendreceiveapp.ngrok.io/shop_collection', 'messenger_extensions': True, 'webview_height_ratio': 'tall', 'fallback_url': 'https://peterssendreceiveapp.ngrok.io/' }, 'buttons': [{ 'title': 'View', 'type': 'web_url', 'url': 'https://peterssendreceiveapp.ngrok.io/collection', 'messenger_extensions': True, 'webview_height_ratio': 'tall', 'fallback_url': 'https://peterssendreceiveapp.ngrok.io/' }] }, { 'title': 'Classic White T-Shirt', 'image_url': 'https://peterssendreceiveapp.ngrok.io/img/white-t-shirt.png', 'subtitle': '100% Cotton, 200% Comfortable', 'default_action': { 'type': 'web_url', 'url': 'https://peterssendreceiveapp.ngrok.io/view?item=100', 'messenger_extensions': True, 'webview_height_ratio': 'tall', 'fallback_url': 'https://peterssendreceiveapp.ngrok.io/' }, 'buttons': [{ 'title': 'Shop Now', 'type': 'web_url', 'url': 'https://peterssendreceiveapp.ngrok.io/shop?item=100', 'messenger_extensions': True, 'webview_height_ratio': 'tall', 'fallback_url': 'https://peterssendreceiveapp.ngrok.io/' }] } ], buttons=[{ 'title': 'View More', 'payload': 'payload', }], options=None, )
async def test_should_send_template_based_message(): with answer.Talk() as talk: story = talk.story fb_interface = story.use( messenger.FBInterface(page_access_token='qwerty1')) mock_http = story.use(mockhttp.MockHttpInterface()) await story.start() payload = { 'template_type': 'receipt', 'recipient_name': 'Stephane Crozatier', 'order_number': '12345678902', 'currency': 'USD', 'payment_method': 'Visa 2345', 'order_url': 'http://petersapparel.parseapp.com/order?order_id=123456', 'timestamp': '1428444852', 'elements': [{ 'title': 'Classic White T-Shirt', 'subtitle': '100% Soft and Luxurious Cotton', 'quantity': 2, 'price': 50, 'currency': 'USD', 'image_url': 'http://petersapparel.parseapp.com/img/whiteshirt.png' }, { 'title': 'Classic Gray T-Shirt', 'subtitle': '100% Soft and Luxurious Cotton', 'quantity': 1, 'price': 25, 'currency': 'USD', 'image_url': 'http://petersapparel.parseapp.com/img/grayshirt.png' }], 'address': { 'street_1': '1 Hacker Way', 'street_2': '', 'city': 'Menlo Park', 'postal_code': '94025', 'state': 'CA', 'country': 'US' }, 'summary': { 'subtotal': 75.00, 'shipping_cost': 4.95, 'total_tax': 6.19, 'total_cost': 56.14 }, 'adjustments': [{ 'name': 'New Customer Discount', 'amount': 20 }, { 'name': '$10 Off Coupon', 'amount': 10 }] } await fb_interface.send_template(talk.user, payload) mock_http.post.assert_called_with( 'https://graph.facebook.com/v2.6/me/messages/', params={ 'access_token': 'qwerty1', }, json={ 'message': { 'attachment': { 'type': 'template', 'payload': payload, } }, 'recipient': { 'id': talk.user['facebook_user_id'], }, })
async def test_breaking_the_loop_inside_of_other_loop(): trigger_global = SimpleTrigger() trigger_inner = SimpleTrigger() trigger_outer = SimpleTrigger() with answer.Talk() as talk: story = talk.story @story.on('action1') def global_action1(): @story.part() def trigger_action1(ctx): pass @story.on('action2') # 1 def global_action2(): @story.loop() def outer_loop(): @story.on('action1') # 2 def outer_action1(): @story.loop() def inner_loop(): @story.on('action1') # 3 def inner_action1(): @story.part() def inner_action1_part(ctx): return loop.BreakLoop() @story.on('action2') def inner_action2(): @story.part() def inner_action2_part(ctx): pass @story.on('action3') # 4 (wrong) def inner_action3(): @story.part() def inner_action3_part(ctx): trigger_inner.passed() @story.on('action2') def outer_action2(): @story.part() def do_some_job(ctx): pass @story.on('action3') # 4 (correct) def outer_action3(): @story.part() def do_some_job(ctx): trigger_outer.passed() @story.on('action3') # 4 (wrong) def global_action3(): @story.part() def do_some_job(ctx): trigger_global.passed() await talk.pure_text('action2') await talk.pure_text('action1') await talk.pure_text('action1') await talk.pure_text('action3') assert trigger_global.is_triggered is False assert trigger_inner.is_triggered is False assert trigger_outer.is_triggered is True