This commit attempts to improve the workflow of the OdooBot onboarding
tutorial, as well as the answer given by the bot while idle.
These changes come from watching how users interact with OdooBot. To
improve the user's experience, the following changes have been made:
1. Move the attachment to the end of the tour, as it is where most
people just close the bot.
2. Remove the quotation marks around commands that OdooBot tells the
user to type, like "/" or ":)", as some users try to actually type
the quotation marks too. Instead, use a light grey background around
the command that the user should type.
3. Some users ask OdooBot questions, but the responses they get are
useless. To solve this, use a message linking to the documentation or the
videos when:
- The user gives wrong answers twice for the same stage of the tour
- The user talks again when the tour is completed
- There's a question mark in the answer
To achieve this a "failed" state field is added on user model, allowing to
distinguish state in the bot workflow from state of answers (failed / not
failed).
Task ID: 2233014
PR #49661
144 lines
6.2 KiB
Python
144 lines
6.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from unittest.mock import patch
|
|
|
|
from odoo.addons.test_mail.tests.common import TestMailCommon, TestRecipients
|
|
from odoo.tests import tagged
|
|
from odoo.tools import mute_logger
|
|
|
|
|
|
@tagged("odoobot")
|
|
class TestOdoobot(TestMailCommon, TestRecipients):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super(TestOdoobot, cls).setUpClass()
|
|
cls.test_record = cls.env['mail.test.simple'].with_context(cls._test_context).create({'name': 'Test', 'email_from': 'ignasse@example.com'})
|
|
|
|
cls.odoobot = cls.env.ref("base.partner_root")
|
|
cls.message_post_default_kwargs = {
|
|
'body': '',
|
|
'attachment_ids': [],
|
|
'message_type': 'comment',
|
|
'partner_ids': [],
|
|
'subtype_xmlid': 'mail.mt_comment'
|
|
}
|
|
cls.odoobot_ping_body = '<a href="http://odoo.com/web#model=res.partner&id=%s" class="o_mail_redirect" data-oe-id="%s" data-oe-model="res.partner" target="_blank">@OdooBot</a>' % (cls.odoobot.id, cls.odoobot.id)
|
|
cls.test_record_employe = cls.test_record.with_user(cls.user_employee)
|
|
|
|
@mute_logger('odoo.addons.mail.models.mail_mail')
|
|
def test_fetch_listener(self):
|
|
channel = self.env['mail.channel'].with_user(self.user_employee).init_odoobot()
|
|
partners = self.env['mail.channel'].channel_fetch_listeners(channel.uuid)
|
|
odoobot = self.env.ref("base.partner_root")
|
|
odoobot_in_fetch_listeners = [partner for partner in partners if partner['id'] == odoobot.id]
|
|
self.assertEqual(len(odoobot_in_fetch_listeners), 1, 'odoobot should appear only once in channel_fetch_listeners')
|
|
|
|
@mute_logger('odoo.addons.mail.models.mail_mail')
|
|
def test_odoobot_ping(self):
|
|
kwargs = self.message_post_default_kwargs.copy()
|
|
kwargs.update({'body': self.odoobot_ping_body, 'partner_ids': [self.odoobot.id, self.user_admin.partner_id.id]})
|
|
|
|
with patch('random.choice', lambda x: x[0]):
|
|
self.assertNextMessage(
|
|
self.test_record_employe.with_context({'mail_post_autofollow': True}).message_post(**kwargs),
|
|
sender=self.odoobot,
|
|
answer=False
|
|
)
|
|
# Odoobot should not be a follower but user_employee and user_admin should
|
|
follower = self.test_record.message_follower_ids.mapped('partner_id')
|
|
self.assertNotIn(self.odoobot, follower)
|
|
self.assertIn(self.user_employee.partner_id, follower)
|
|
self.assertIn(self.user_admin.partner_id, follower)
|
|
|
|
@mute_logger('odoo.addons.mail.models.mail_mail')
|
|
def test_onboarding_flow(self):
|
|
kwargs = self.message_post_default_kwargs.copy()
|
|
channel = self.env['mail.channel'].with_user(self.user_employee).init_odoobot()
|
|
|
|
kwargs['body'] = 'tagada 😊'
|
|
last_message = self.assertNextMessage(
|
|
channel.message_post(**kwargs),
|
|
sender=self.odoobot,
|
|
answer=("help",)
|
|
)
|
|
channel.execute_command(command="help")
|
|
self.assertNextMessage(
|
|
last_message, # no message will be post with command help, use last odoobot message instead
|
|
sender=self.odoobot,
|
|
answer=("@OdooBot",)
|
|
)
|
|
kwargs['body'] = ''
|
|
kwargs['partner_ids'] = [self.env['ir.model.data'].xmlid_to_res_id("base.partner_root")]
|
|
self.assertNextMessage(
|
|
channel.message_post(**kwargs),
|
|
sender=self.odoobot,
|
|
answer=("attachment",)
|
|
)
|
|
kwargs['body'] = ''
|
|
attachment = self.env['ir.attachment'].with_user(self.user_employee).create({
|
|
'datas': 'bWlncmF0aW9uIHRlc3Q=',
|
|
'name': 'picture_of_your_dog.doc',
|
|
'res_model': 'mail.compose.message',
|
|
})
|
|
kwargs['attachment_ids'] = [attachment.id]
|
|
# For the end of the flow, we only test that the state changed, but not to which
|
|
# one since it depends on the intalled apps, which can add more steps (like livechat)
|
|
channel.message_post(**kwargs)
|
|
self.assertNotEqual(self.user_employee.odoobot_state, 'onboarding_attachement')
|
|
|
|
# Test miscellaneous messages
|
|
self.user_employee.odoobot_state = "idle"
|
|
kwargs['partner_ids'] = []
|
|
kwargs['body'] = "I love you"
|
|
self.assertNextMessage(
|
|
channel.message_post(**kwargs),
|
|
sender=self.odoobot,
|
|
answer=("too human for me",)
|
|
)
|
|
kwargs['body'] = "Go fuck yourself"
|
|
self.assertNextMessage(
|
|
channel.message_post(**kwargs),
|
|
sender=self.odoobot,
|
|
answer=("I have feelings",)
|
|
)
|
|
kwargs['body'] = "help me"
|
|
self.assertNextMessage(
|
|
channel.message_post(**kwargs),
|
|
sender=self.odoobot,
|
|
answer=("If you need help",)
|
|
)
|
|
|
|
@mute_logger('odoo.addons.mail.models.mail_mail')
|
|
def test_odoobot_no_default_answer(self):
|
|
kwargs = self.message_post_default_kwargs.copy()
|
|
kwargs.update({'body': "I'm not talking to @odoobot right now", 'partner_ids': []})
|
|
self.assertNextMessage(
|
|
self.test_record_employe.message_post(**kwargs),
|
|
answer=False
|
|
)
|
|
|
|
def assertNextMessage(self, message, answer=None, sender=None):
|
|
last_message = self.env['mail.message'].search([('id', '=', message.id + 1)])
|
|
if last_message:
|
|
body = last_message.body.replace('<p>', '').replace('</p>', '')
|
|
else:
|
|
self.assertFalse(answer, "No last message found when an answer was expect")
|
|
if answer is not None:
|
|
if answer and not last_message:
|
|
self.assertTrue(False, "No last message found")
|
|
if isinstance(answer, list):
|
|
self.assertIn(body, answer)
|
|
elif isinstance(answer, tuple):
|
|
for elem in answer:
|
|
self.assertIn(elem, body)
|
|
elif not answer:
|
|
self.assertFalse(last_message, "No answer should have been post")
|
|
return
|
|
else:
|
|
self.assertEqual(body, answer)
|
|
if sender:
|
|
self.assertEqual(sender, last_message.author_id)
|
|
return last_message
|