mirror of
https://github.com/bringout/oca-ocb-mail.git
synced 2026-04-21 00:22:00 +02:00
Initial commit: Mail packages
This commit is contained in:
commit
4e53507711
1948 changed files with 751201 additions and 0 deletions
|
|
@ -0,0 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import chatbot_common
|
||||
from . import test_chatbot_form_ui
|
||||
from . import test_chatbot_internals
|
||||
from . import test_get_mail_channel
|
||||
from . import test_im_livechat_report
|
||||
from . import test_im_livechat_support_page
|
||||
from . import test_message
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.tests import common
|
||||
|
||||
|
||||
class ChatbotCase(common.TransactionCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(ChatbotCase, cls).setUpClass()
|
||||
|
||||
cls.chatbot_script = cls.env['chatbot.script'].create({
|
||||
'title': 'Testing Bot',
|
||||
})
|
||||
|
||||
ChatbotScriptStep = cls.env['chatbot.script.step'].sudo()
|
||||
|
||||
[
|
||||
cls.step_hello,
|
||||
cls.step_welcome,
|
||||
cls.step_dispatch,
|
||||
] = ChatbotScriptStep.create([{
|
||||
'step_type': 'text',
|
||||
'message': "Hello! I'm a bot!",
|
||||
'chatbot_script_id': cls.chatbot_script.id,
|
||||
}, {
|
||||
'step_type': 'text',
|
||||
'message': "I help lost visitors find their way.",
|
||||
'chatbot_script_id': cls.chatbot_script.id,
|
||||
}, {
|
||||
'step_type': 'question_selection',
|
||||
'message': "How can I help you?",
|
||||
'chatbot_script_id': cls.chatbot_script.id,
|
||||
}])
|
||||
|
||||
[
|
||||
cls.step_dispatch_buy_software,
|
||||
cls.step_dispatch_pricing,
|
||||
cls.step_dispatch_operator,
|
||||
] = cls.env['chatbot.script.answer'].sudo().create([{
|
||||
'name': 'I want to buy the software',
|
||||
'script_step_id': cls.step_dispatch.id,
|
||||
}, {
|
||||
'name': 'Pricing Question',
|
||||
'script_step_id': cls.step_dispatch.id,
|
||||
}, {
|
||||
'name': "I want to speak with an operator",
|
||||
'script_step_id': cls.step_dispatch.id,
|
||||
}])
|
||||
|
||||
[
|
||||
cls.step_pricing_contact_us,
|
||||
cls.step_email,
|
||||
cls.step_email_validated,
|
||||
cls.step_forward_operator,
|
||||
cls.step_no_one_available,
|
||||
cls.step_no_operator_dispatch,
|
||||
] = ChatbotScriptStep.create([{
|
||||
'step_type': 'text',
|
||||
'message': 'For any pricing question, feel free ton contact us at pricing@mycompany.com',
|
||||
'triggering_answer_ids': [(4, cls.step_dispatch_pricing.id)],
|
||||
'chatbot_script_id': cls.chatbot_script.id,
|
||||
}, {
|
||||
'step_type': 'question_email',
|
||||
'message': 'Can you give us your email please?',
|
||||
'triggering_answer_ids': [(4, cls.step_dispatch_buy_software.id)],
|
||||
'chatbot_script_id': cls.chatbot_script.id,
|
||||
}, {
|
||||
'step_type': 'text',
|
||||
'message': 'Your email is validated, thank you!',
|
||||
'triggering_answer_ids': [(4, cls.step_dispatch_buy_software.id)],
|
||||
'chatbot_script_id': cls.chatbot_script.id,
|
||||
}, {
|
||||
'step_type': 'forward_operator',
|
||||
'message': 'I will transfer you to a human.',
|
||||
'triggering_answer_ids': [(4, cls.step_dispatch_operator.id)],
|
||||
'chatbot_script_id': cls.chatbot_script.id,
|
||||
}, {
|
||||
'step_type': 'text',
|
||||
'message': 'Sorry, you will have to stay with me for a while',
|
||||
'triggering_answer_ids': [(4, cls.step_dispatch_operator.id)],
|
||||
'chatbot_script_id': cls.chatbot_script.id,
|
||||
}, {
|
||||
'step_type': 'question_selection',
|
||||
'message': 'So... What can I do to help you?',
|
||||
'triggering_answer_ids': [(4, cls.step_dispatch_operator.id)],
|
||||
'chatbot_script_id': cls.chatbot_script.id,
|
||||
}])
|
||||
|
||||
cls.step_no_operator_just_leaving = cls.env['chatbot.script.answer'].sudo().create({
|
||||
'name': 'I will be leaving then',
|
||||
'script_step_id': cls.step_no_operator_dispatch.id,
|
||||
})
|
||||
|
||||
[
|
||||
cls.step_just_leaving,
|
||||
cls.step_pricing_thank_you,
|
||||
cls.step_ask_website,
|
||||
cls.step_ask_feedback,
|
||||
cls.step_goodbye,
|
||||
] = ChatbotScriptStep.create([{
|
||||
'step_type': 'text',
|
||||
'message': "Ok, I'm sorry I was not able to help you",
|
||||
'triggering_answer_ids': [(4, cls.step_no_operator_just_leaving.id)],
|
||||
'chatbot_script_id': cls.chatbot_script.id,
|
||||
}, {
|
||||
'step_type': 'text',
|
||||
'message': 'We will reach back to you as soon as we can!',
|
||||
'triggering_answer_ids': [(4, cls.step_dispatch_pricing.id)],
|
||||
'chatbot_script_id': cls.chatbot_script.id,
|
||||
}, {
|
||||
'step_type': 'free_input_single',
|
||||
'message': 'Would you mind providing your website address?',
|
||||
'sequence': 97, # makes it easier for other modules to add steps before this one
|
||||
'chatbot_script_id': cls.chatbot_script.id,
|
||||
}, {
|
||||
'step_type': 'free_input_multi',
|
||||
'message': 'Great, do you want to leave any feedback for us to improve?',
|
||||
'sequence': 98, # makes it easier for other modules to add steps before this one
|
||||
'chatbot_script_id': cls.chatbot_script.id,
|
||||
}, {
|
||||
'step_type': 'text',
|
||||
'message': "Ok bye!",
|
||||
'sequence': 99, # makes it easier for other modules to add steps before this one
|
||||
'chatbot_script_id': cls.chatbot_script.id,
|
||||
}])
|
||||
|
||||
cls.livechat_channel = cls.env['im_livechat.channel'].create({
|
||||
'name': 'Test Channel',
|
||||
'rule_ids': [(0, 0, {
|
||||
'chatbot_script_id': cls.chatbot_script.id,
|
||||
})]
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def _post_answer_and_trigger_next_step(cls, mail_channel, answer, chatbot_script_answer=False):
|
||||
mail_message = mail_channel.message_post(body=answer)
|
||||
if chatbot_script_answer:
|
||||
cls.env['chatbot.message'].search([
|
||||
('mail_message_id', '=', mail_message.id)
|
||||
], limit=1).user_script_answer_id = chatbot_script_answer.id
|
||||
# sudo: chatbot.script.step - members of a channel can access the current chatbot step
|
||||
next_step = mail_channel.chatbot_current_step_id.sudo()._process_answer(mail_channel, mail_message.body)
|
||||
next_step._process_step(mail_channel)
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.tests.common import TransactionCase
|
||||
|
||||
|
||||
class TestImLivechatCommon(TransactionCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.operators = cls.env['res.users'].create([{
|
||||
'name': 'Michel',
|
||||
'login': 'michel',
|
||||
'livechat_username': "Michel Operator",
|
||||
'email': 'michel@example.com',
|
||||
}, {
|
||||
'name': 'Paul',
|
||||
'login': 'paul'
|
||||
}, {
|
||||
'name': 'Pierre',
|
||||
'login': 'pierre'
|
||||
}, {
|
||||
'name': 'Jean',
|
||||
'login': 'jean'
|
||||
}, {
|
||||
'name': 'Georges',
|
||||
'login': 'georges'
|
||||
}])
|
||||
|
||||
cls.visitor_user = cls.env['res.users'].create({
|
||||
'name': 'Rajesh',
|
||||
'login': 'rajesh',
|
||||
'country_id': cls.env.ref('base.in').id,
|
||||
'email': 'rajesh@example.com',
|
||||
})
|
||||
|
||||
cls.livechat_channel = cls.env['im_livechat.channel'].create({
|
||||
'name': 'The channel',
|
||||
'user_ids': [(6, 0, cls.operators.ids)]
|
||||
})
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
def get_available_users(_):
|
||||
return self.operators
|
||||
|
||||
self.patch(type(self.env['im_livechat.channel']), '_get_available_users', get_available_users)
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import tests
|
||||
from odoo.addons.base.tests.common import HttpCaseWithUserDemo
|
||||
|
||||
|
||||
@tests.tagged('post_install', '-at_install')
|
||||
class TestLivechatChatbotFormUI(HttpCaseWithUserDemo):
|
||||
def test_chatbot_steps_sequence_ui(self):
|
||||
""" As sequences are *critical* for the chatbot_script script, let us a run a little tour that
|
||||
creates a few steps, then verify sequences are properly applied. """
|
||||
|
||||
self.start_tour(
|
||||
'/web',
|
||||
'im_livechat_chatbot_steps_sequence_tour',
|
||||
login='admin',
|
||||
step_delay=1000
|
||||
)
|
||||
|
||||
chatbot_script = self.env['chatbot.script'].search([('title', '=', 'Test Chatbot Sequence')])
|
||||
|
||||
self.assertEqual(len(chatbot_script.script_step_ids), 3)
|
||||
|
||||
self.assertEqual(chatbot_script.script_step_ids[0].message, 'Step 1')
|
||||
self.assertEqual(chatbot_script.script_step_ids[0].sequence, 0)
|
||||
self.assertEqual(chatbot_script.script_step_ids[1].message, 'Step 2')
|
||||
self.assertEqual(chatbot_script.script_step_ids[1].sequence, 1)
|
||||
self.assertEqual(chatbot_script.script_step_ids[2].message, 'Step 3')
|
||||
self.assertEqual(chatbot_script.script_step_ids[2].sequence, 2)
|
||||
|
||||
def test_chatbot_steps_sequence_with_move_ui(self):
|
||||
""" Same as above, with more steps and a drag&drop within the tour.
|
||||
|
||||
It is important to test those separately, as we want proper sequences even if we don't
|
||||
move records around. """
|
||||
|
||||
self.start_tour(
|
||||
'/web',
|
||||
'im_livechat_chatbot_steps_sequence_with_move_tour',
|
||||
login='admin',
|
||||
step_delay=1000
|
||||
)
|
||||
|
||||
chatbot_script = self.env['chatbot.script'].search([('title', '=', 'Test Chatbot Sequence')])
|
||||
|
||||
self.assertEqual(len(chatbot_script.script_step_ids), 6)
|
||||
|
||||
# during the test, we create the steps normally and then move 'Step 5'
|
||||
# in second position -> check order is correct
|
||||
|
||||
self.assertEqual(chatbot_script.script_step_ids[0].message, 'Step 1')
|
||||
self.assertEqual(chatbot_script.script_step_ids[0].sequence, 0)
|
||||
self.assertEqual(chatbot_script.script_step_ids[1].message, 'Step 5')
|
||||
self.assertEqual(chatbot_script.script_step_ids[1].sequence, 1)
|
||||
self.assertEqual(chatbot_script.script_step_ids[2].message, 'Step 2')
|
||||
self.assertEqual(chatbot_script.script_step_ids[2].sequence, 2)
|
||||
self.assertEqual(chatbot_script.script_step_ids[3].message, 'Step 3')
|
||||
self.assertEqual(chatbot_script.script_step_ids[3].sequence, 3)
|
||||
self.assertEqual(chatbot_script.script_step_ids[4].message, 'Step 4')
|
||||
self.assertEqual(chatbot_script.script_step_ids[4].sequence, 4)
|
||||
self.assertEqual(chatbot_script.script_step_ids[5].message, 'Step 6')
|
||||
self.assertEqual(chatbot_script.script_step_ids[5].sequence, 5)
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.addons.im_livechat.tests import chatbot_common
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class ChatbotCase(chatbot_common.ChatbotCase):
|
||||
|
||||
def test_chatbot_duplicate(self):
|
||||
""" In this test we make sure that 'triggering_answer_ids' are correctly duplicated and
|
||||
reference the answers from the copied script steps.
|
||||
See chatbot.script#copy for more details. """
|
||||
|
||||
chatbot_copy = self.chatbot_script.copy()
|
||||
|
||||
step_pricing_contact_us_copy = chatbot_copy.script_step_ids.filtered(
|
||||
lambda step: 'For any pricing question, feel free ton contact us at pricing@mycompany.com' in step.message)
|
||||
|
||||
self.assertNotEqual(step_pricing_contact_us_copy, self.step_pricing_contact_us)
|
||||
self.assertEqual(len(step_pricing_contact_us_copy.triggering_answer_ids), 1)
|
||||
self.assertEqual(step_pricing_contact_us_copy.triggering_answer_ids.name, 'Pricing Question')
|
||||
self.assertNotEqual(step_pricing_contact_us_copy.triggering_answer_ids, self.step_dispatch_pricing)
|
||||
|
||||
step_email_copy = chatbot_copy.script_step_ids.filtered(
|
||||
lambda step: 'Can you give us your email please' in step.message)
|
||||
|
||||
self.assertNotEqual(step_email_copy, self.step_email)
|
||||
self.assertEqual(len(step_email_copy.triggering_answer_ids), 1)
|
||||
self.assertEqual(step_email_copy.triggering_answer_ids.name, 'I want to buy the software')
|
||||
self.assertNotEqual(step_email_copy.triggering_answer_ids, self.step_dispatch_buy_software)
|
||||
|
||||
def test_chatbot_is_forward_operator_child(self):
|
||||
self.assertEqual([step.is_forward_operator_child for step in self.chatbot_script.script_step_ids],
|
||||
[False, False, False, False, False, False, False, True, True, True, False, False, False, False],
|
||||
"Steps 'step_no_one_available', 'step_no_operator_dispatch', 'step_just_leaving'"
|
||||
"should be flagged as forward operator child.")
|
||||
|
||||
self.step_no_operator_dispatch.write({'triggering_answer_ids': [(6, 0, [self.step_dispatch_pricing.id])]})
|
||||
self.chatbot_script.script_step_ids.invalidate_recordset(['is_forward_operator_child'])
|
||||
|
||||
self.assertEqual([step.is_forward_operator_child for step in self.chatbot_script.script_step_ids],
|
||||
[False, False, False, False, False, False, False, True, False, False, False, False, False, False],
|
||||
"Only step 'step_no_one_available' should be flagged as forward operator child.")
|
||||
|
||||
def test_chatbot_steps(self):
|
||||
channel_info = self.livechat_channel._open_livechat_mail_channel(
|
||||
anonymous_name='Test Visitor', chatbot_script=self.chatbot_script)
|
||||
mail_channel = self.env['mail.channel'].browse(channel_info['id'])
|
||||
|
||||
self.assertEqual(mail_channel.chatbot_current_step_id, self.step_dispatch)
|
||||
|
||||
self._post_answer_and_trigger_next_step(
|
||||
mail_channel,
|
||||
self.step_dispatch_buy_software.name,
|
||||
chatbot_script_answer=self.step_dispatch_buy_software
|
||||
)
|
||||
self.assertEqual(mail_channel.chatbot_current_step_id, self.step_email)
|
||||
|
||||
with self.assertRaises(ValidationError, msg="Should raise an error since it's not a valid email"):
|
||||
self._post_answer_and_trigger_next_step(mail_channel, 'test')
|
||||
|
||||
self._post_answer_and_trigger_next_step(mail_channel, 'test@example.com')
|
||||
self.assertEqual(mail_channel.chatbot_current_step_id, self.step_email_validated)
|
||||
|
||||
def test_chatbot_steps_sequence(self):
|
||||
""" Ensure sequence is correct when creating chatbots and adding steps to an existing one.
|
||||
See chatbot.script.step#create for more details. """
|
||||
|
||||
chatbot_1, chatbot_2 = self.env['chatbot.script'].create([{
|
||||
'title': 'Chatbot 1',
|
||||
'script_step_ids': [
|
||||
(0, 0, {'step_type': 'text', 'message': '1'}),
|
||||
(0, 0, {'step_type': 'text', 'message': '2'}),
|
||||
(0, 0, {'step_type': 'text', 'message': '3'}),
|
||||
(0, 0, {'step_type': 'text', 'message': '4'}),
|
||||
(0, 0, {'step_type': 'text', 'message': '5'}),
|
||||
]
|
||||
}, {
|
||||
'title': 'Chatbot 2',
|
||||
'script_step_ids': [
|
||||
(0, 0, {'step_type': 'text', 'message': '1'}),
|
||||
(0, 0, {'step_type': 'text', 'message': '2'}),
|
||||
(0, 0, {'step_type': 'text', 'message': '3'}),
|
||||
]
|
||||
}])
|
||||
|
||||
self.assertEqual([0, 1, 2, 3, 4], chatbot_1.script_step_ids.mapped('sequence'))
|
||||
self.assertEqual([0, 1, 2], chatbot_2.script_step_ids.mapped('sequence'))
|
||||
|
||||
chatbot_1.write({'script_step_ids': [
|
||||
(0, 0, {'step_type': 'text', 'message': '6'}),
|
||||
(0, 0, {'step_type': 'text', 'message': '7'}),
|
||||
]})
|
||||
self.assertEqual([0, 1, 2, 3, 4, 5, 6], chatbot_1.script_step_ids.mapped('sequence'))
|
||||
|
||||
def test_chatbot_welcome_steps(self):
|
||||
""" see '_get_welcome_steps' for more details. """
|
||||
|
||||
welcome_steps = self.chatbot_script._get_welcome_steps()
|
||||
self.assertEqual(len(welcome_steps), 3)
|
||||
self.assertEqual(welcome_steps, self.chatbot_script.script_step_ids[:3])
|
||||
|
||||
self.chatbot_script.script_step_ids[:2].unlink()
|
||||
welcome_steps = self.chatbot_script._get_welcome_steps()
|
||||
self.assertEqual(len(welcome_steps), 1)
|
||||
self.assertEqual(welcome_steps, self.chatbot_script.script_step_ids[0])
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from datetime import timedelta
|
||||
from freezegun import freeze_time
|
||||
|
||||
from odoo import fields
|
||||
from odoo.addons.im_livechat.tests.common import TestImLivechatCommon
|
||||
|
||||
|
||||
class TestGetMailChannel(TestImLivechatCommon):
|
||||
def test_get_mail_channel(self):
|
||||
"""For a livechat with 5 available operators, we open 5 channels 5 times (25 channels total).
|
||||
For every 5 channels opening, we check that all operators were assigned.
|
||||
"""
|
||||
|
||||
for i in range(5):
|
||||
mail_channels = self._open_livechat_mail_channel()
|
||||
channel_operators = [channel_info['operator_pid'] for channel_info in mail_channels]
|
||||
channel_operator_ids = [channel_operator[0] for channel_operator in channel_operators]
|
||||
self.assertTrue(all(partner_id in channel_operator_ids for partner_id in self.operators.mapped('partner_id').ids))
|
||||
|
||||
def test_channel_get_livechat_visitor_info(self):
|
||||
belgium = self.env.ref('base.be')
|
||||
public_user = self.env.ref('base.public_user')
|
||||
test_user = self.env['res.users'].create({'name': 'Roger', 'login': 'roger', 'country_id': belgium.id})
|
||||
|
||||
# ensure visitor info are correct with anonymous
|
||||
operator = self.operators[0]
|
||||
channel_info = self.livechat_channel.with_user(public_user)._open_livechat_mail_channel(anonymous_name='Visitor 22', previous_operator_id=operator.partner_id.id, country_id=belgium.id)
|
||||
self.assertEqual(channel_info['channel']['anonymous_name'], "Visitor 22")
|
||||
self.assertEqual(channel_info['channel']['anonymous_country'], {'code': 'BE', 'id': belgium.id, 'name': 'Belgium'})
|
||||
|
||||
# ensure member info are hidden (in particular email and real name when livechat username is present)
|
||||
# shape of channelMembers is [('insert', data...)], [0][1] accesses the data
|
||||
self.assertEqual(sorted(map(lambda m: m['persona']['partner'], channel_info['channel']['channelMembers'][0][1]), key=lambda m: m['id']), sorted([{
|
||||
'active': True,
|
||||
'country': [('clear',)],
|
||||
'id': operator.partner_id.id,
|
||||
'is_public': False,
|
||||
'user_livechat_username': 'Michel Operator',
|
||||
}, {
|
||||
'active': False,
|
||||
'id': public_user.partner_id.id,
|
||||
'is_public': True,
|
||||
'name': 'Public user',
|
||||
}], key=lambda m: m['id']))
|
||||
|
||||
# ensure visitor info are correct with real user
|
||||
channel_info = self.livechat_channel.with_user(test_user)._open_livechat_mail_channel(anonymous_name='whatever', previous_operator_id=operator.partner_id.id, user_id=test_user.id)
|
||||
self.assertFalse(channel_info['channel']['anonymous_name'])
|
||||
self.assertEqual(channel_info['channel']['anonymous_country'], [('clear',)])
|
||||
self.assertEqual(channel_info['channel']['channelMembers'], [('insert', [
|
||||
{
|
||||
'channel': {'id': channel_info['id']},
|
||||
'id': self.env['mail.channel.member'].search([('channel_id', '=', channel_info['id']), ('partner_id', '=', operator.partner_id.id)]).id,
|
||||
'persona': {
|
||||
'partner': {
|
||||
'active': True,
|
||||
'country': [('clear',)],
|
||||
'id': operator.partner_id.id,
|
||||
'is_public': False,
|
||||
'user_livechat_username': 'Michel Operator',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
'channel': {'id': channel_info['id']},
|
||||
'id': self.env['mail.channel.member'].search([('channel_id', '=', channel_info['id']), ('partner_id', '=', test_user.partner_id.id)]).id,
|
||||
'persona': {
|
||||
'partner': {
|
||||
'active': True,
|
||||
'country': {
|
||||
'code': 'BE',
|
||||
'id': belgium.id,
|
||||
'name': 'Belgium',
|
||||
},
|
||||
'id': test_user.partner_id.id,
|
||||
'is_public': False,
|
||||
'name': 'Roger',
|
||||
},
|
||||
},
|
||||
},
|
||||
])])
|
||||
|
||||
# ensure visitor info are correct when operator is testing themselves
|
||||
operator = self.operators[0]
|
||||
channel_info = self.livechat_channel.with_user(operator)._open_livechat_mail_channel(anonymous_name='whatever', previous_operator_id=operator.partner_id.id, user_id=operator.id)
|
||||
self.assertEqual(channel_info['operator_pid'], (operator.partner_id.id, "Michel Operator"))
|
||||
self.assertFalse(channel_info['channel']['anonymous_name'])
|
||||
self.assertEqual(channel_info['channel']['anonymous_country'], [('clear',)])
|
||||
self.assertEqual(channel_info['channel']['channelMembers'], [('insert', [
|
||||
{
|
||||
'channel': {'id': channel_info['id']},
|
||||
'id': self.env['mail.channel.member'].search([('channel_id', '=', channel_info['id']), ('partner_id', '=', operator.partner_id.id)]).id,
|
||||
'persona': {
|
||||
'partner': {
|
||||
'active': True,
|
||||
'country': [('clear',)],
|
||||
'id': operator.partner_id.id,
|
||||
'is_public': False,
|
||||
'user_livechat_username': 'Michel Operator',
|
||||
},
|
||||
},
|
||||
},
|
||||
])])
|
||||
|
||||
def _open_livechat_mail_channel(self):
|
||||
mail_channels = []
|
||||
|
||||
for i in range(5):
|
||||
mail_channel = self.livechat_channel._open_livechat_mail_channel('Anonymous')
|
||||
mail_channels.append(mail_channel)
|
||||
# send a message to mark this channel as 'active'
|
||||
self.env['mail.channel'].browse(mail_channel['id']).message_post(body='cc')
|
||||
|
||||
return mail_channels
|
||||
|
||||
def test_channel_not_pinned_for_operator_before_first_message(self):
|
||||
public_user = self.env.ref('base.public_user')
|
||||
channel_info = self.livechat_channel.with_user(public_user)._open_livechat_mail_channel(anonymous_name='whatever')
|
||||
operator_channel_member = self.env['mail.channel.member'].search([('channel_id', '=', channel_info['id']), ('partner_id', 'in', self.operators.partner_id.ids)])
|
||||
self.assertEqual(len(operator_channel_member), 1, "operator should be member of channel")
|
||||
self.assertFalse(operator_channel_member.is_pinned, "channel should not be pinned for operator initially")
|
||||
self.env['mail.channel'].browse(channel_info['id']).message_post(body='cc')
|
||||
self.assertTrue(operator_channel_member.is_pinned, "channel should be pinned for operator after visitor sent a message")
|
||||
self.assertIn(channel_info['id'], operator_channel_member.partner_id._get_channels_as_member().ids, "channel should be fetched by operator on new page")
|
||||
|
||||
def test_operator_livechat_username(self):
|
||||
"""Ensures the operator livechat_username is returned by `_channel_fetch_message`, which is
|
||||
the method called by the public route displaying chat history."""
|
||||
public_user = self.env.ref('base.public_user')
|
||||
operator = self.operators[0]
|
||||
operator.write({
|
||||
'email': 'michel@example.com',
|
||||
'livechat_username': 'Michel at your service',
|
||||
})
|
||||
channel_info = self.livechat_channel.with_user(public_user).sudo()._open_livechat_mail_channel(anonymous_name='whatever')
|
||||
channel = self.env['mail.channel'].browse(channel_info['id'])
|
||||
channel.with_user(operator).message_post(body='Hello', message_type='comment', subtype_xmlid='mail.mt_comment')
|
||||
message_formats = channel.with_user(public_user).sudo()._channel_fetch_message()
|
||||
self.assertEqual(len(message_formats), 1)
|
||||
self.assertEqual(message_formats[0]['author']['id'], operator.partner_id.id)
|
||||
self.assertEqual(message_formats[0]['author']['user_livechat_username'], operator.livechat_username)
|
||||
self.assertFalse(message_formats[0].get('email_from'), "should not send email_from to livechat user")
|
||||
|
||||
def test_read_channel_unpined_for_operator_after_one_day(self):
|
||||
public_user = self.env.ref('base.public_user')
|
||||
channel_info = self.livechat_channel.with_user(public_user)._open_livechat_mail_channel(anonymous_name='visitor')
|
||||
member_of_operator = self.env['mail.channel.member'].search([('channel_id', '=', channel_info['id']), ('partner_id', 'in', self.operators.partner_id.ids)])
|
||||
message = self.env['mail.channel'].browse(channel_info['id']).message_post(body='cc')
|
||||
member_of_operator.channel_id.with_user(self.operators.filtered(
|
||||
lambda operator: operator.partner_id == member_of_operator.partner_id
|
||||
))._channel_seen(message.id)
|
||||
with freeze_time(fields.Datetime.to_string(fields.datetime.now() + timedelta(days=1))):
|
||||
member_of_operator._gc_unpin_livechat_sessions()
|
||||
self.assertFalse(member_of_operator.is_pinned, "read channel should be unpinned after one day")
|
||||
|
||||
def test_unread_channel_not_unpined_for_operator_after_autovacuum(self):
|
||||
public_user = self.env.ref('base.public_user')
|
||||
channel_info = self.livechat_channel.with_user(public_user)._open_livechat_mail_channel(anonymous_name='visitor')
|
||||
member_of_operator = self.env['mail.channel.member'].search([('channel_id', '=', channel_info['id']), ('partner_id', 'in', self.operators.partner_id.ids)])
|
||||
self.env['mail.channel'].browse(channel_info['id']).message_post(body='cc')
|
||||
with freeze_time(fields.Datetime.to_string(fields.datetime.now() + timedelta(days=1))):
|
||||
member_of_operator._gc_unpin_livechat_sessions()
|
||||
self.assertTrue(member_of_operator.is_pinned, "unread channel should not be unpinned after autovacuum")
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from odoo.addons.im_livechat.tests.common import TestImLivechatCommon
|
||||
|
||||
|
||||
class TestImLivechatReport(TestImLivechatCommon):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.env['mail.channel'].search([('livechat_channel_id', '!=', False)]).unlink()
|
||||
|
||||
with patch.object(type(cls.env['im_livechat.channel']), '_get_available_users', lambda _: cls.operators):
|
||||
channel_id = cls.livechat_channel._open_livechat_mail_channel('Anonymous')['id']
|
||||
|
||||
channel = cls.env['mail.channel'].browse(channel_id)
|
||||
cls.operator = channel.livechat_operator_id
|
||||
|
||||
cls._create_message(channel, cls.visitor_user.partner_id, '2023-03-17 06:05:54')
|
||||
cls._create_message(channel, cls.operator, '2023-03-17 08:15:54')
|
||||
cls._create_message(channel, cls.operator, '2023-03-17 08:45:54')
|
||||
|
||||
# message with the same record id, but with a different model
|
||||
# should not be taken into account for statistics
|
||||
partner_message = cls._create_message(channel, cls.operator, '2023-03-17 05:05:54')
|
||||
partner_message |= cls._create_message(channel, cls.operator, '2023-03-17 09:15:54')
|
||||
partner_message.model = 'res.partner'
|
||||
cls.env['mail.message'].flush_model()
|
||||
|
||||
def test_im_livechat_report_channel(self):
|
||||
report = self.env['im_livechat.report.channel'].search([('livechat_channel_id', '=', self.livechat_channel.id)])
|
||||
self.assertEqual(len(report), 1, 'Should have one channel report for this live channel')
|
||||
# We have those messages, ordered by creation;
|
||||
# 05:05:54: wrong model
|
||||
# 06:05:54: visitor message
|
||||
# 08:15:54: operator first answer
|
||||
# 08:45:54: operator second answer
|
||||
# 09:15:54: wrong model
|
||||
# So the duration of the session is: (08:45:54 - 06:05:54) = 2h40 = 9600 seconds
|
||||
# The time to answer of this session is: (08:15:54 - 06:05:54) = 2h10 = 7800 seconds
|
||||
self.assertEqual(int(report.time_to_answer), 7800)
|
||||
self.assertEqual(int(report.duration), 9600)
|
||||
|
||||
def test_im_livechat_report_operator(self):
|
||||
result = self.env['im_livechat.report.operator'].read_group([], ['time_to_answer:avg', 'duration:avg'], [])
|
||||
self.assertEqual(len(result), 1)
|
||||
self.assertEqual(int(result[0]['time_to_answer']), 7800)
|
||||
self.assertEqual(int(result[0]['duration']), 9600)
|
||||
|
||||
@classmethod
|
||||
def _create_message(cls, channel, author, date):
|
||||
with patch.object(cls.env.cr, 'now', lambda: date):
|
||||
return channel.message_post(author_id=author.id, body=f'Message {date}')
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import odoo
|
||||
from odoo.tests import HttpCase
|
||||
|
||||
@odoo.tests.tagged('-at_install', 'post_install')
|
||||
class TestImLivechatSupportPage(HttpCase):
|
||||
def test_load_modules(self):
|
||||
"""Checks that all javascript modules load correctly on the livechat support page"""
|
||||
|
||||
# Give some time to the assets to load to prevent fetch
|
||||
# interrupt errors then ensures all the assets are loaded.
|
||||
check_js_modules = """
|
||||
setTimeout(() => {
|
||||
const { missing, failed, unloaded } = odoo.__DEBUG__.jsModules;
|
||||
if ([missing, failed, unloaded].some(arr => arr.length)) {
|
||||
console.error("Couldn't load all JS modules.", JSON.stringify({ missing, failed, unloaded }));
|
||||
} else {
|
||||
console.log("test successful");
|
||||
}
|
||||
Object.assign(console, {
|
||||
log: () => {},
|
||||
error: () => {},
|
||||
warn: () => {},
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
"""
|
||||
self.browser_js("/im_livechat/support/1", code=check_js_modules, ready="odoo.__DEBUG__.didLogInfo")
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import Command
|
||||
from odoo.tests.common import users, tagged
|
||||
from odoo.addons.im_livechat.tests.chatbot_common import ChatbotCase
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestImLivechatMessage(ChatbotCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.users = self.env['res.users'].create([
|
||||
{
|
||||
'email': 'e.e@example.com',
|
||||
'groups_id': [Command.link(self.env.ref('base.group_user').id)],
|
||||
'login': 'emp',
|
||||
'name': 'Ernest Employee',
|
||||
'notification_type': 'inbox',
|
||||
'odoobot_state': 'disabled',
|
||||
'signature': '--\nErnest',
|
||||
},
|
||||
{'name': 'test1', 'login': 'test1', 'email': 'test1@example.com'},
|
||||
])
|
||||
|
||||
@users('emp')
|
||||
def test_chatbot_message_format(self):
|
||||
user = self.env.user
|
||||
channel_info = self.livechat_channel.with_user(user)._open_livechat_mail_channel(
|
||||
anonymous_name='Test Chatbot',
|
||||
previous_operator_id=self.chatbot_script.operator_partner_id.id,
|
||||
chatbot_script=self.chatbot_script,
|
||||
user_id=user.id
|
||||
)
|
||||
mail_channel = self.env['mail.channel'].browse(channel_info['id'])
|
||||
self._post_answer_and_trigger_next_step(
|
||||
mail_channel,
|
||||
self.step_dispatch_buy_software.name,
|
||||
chatbot_script_answer=self.step_dispatch_buy_software
|
||||
)
|
||||
chatbot_message = mail_channel.chatbot_message_ids.mail_message_id[-1:]
|
||||
self.assertEqual(chatbot_message.message_format(), [{
|
||||
'id': chatbot_message.id,
|
||||
'body': '<p>Can you give us your email please?</p>',
|
||||
'date': chatbot_message.date,
|
||||
'message_type': 'comment',
|
||||
'subtype_id': (self.env.ref('mail.mt_comment').id, 'Discussions'),
|
||||
'subject': False,
|
||||
'model': 'mail.channel',
|
||||
'res_id': mail_channel.id,
|
||||
'record_name': 'Testing Bot',
|
||||
'starred_partner_ids': [],
|
||||
'author': {
|
||||
'id': self.chatbot_script.operator_partner_id.id,
|
||||
'name': 'Testing Bot'
|
||||
},
|
||||
'guestAuthor': [('clear',)],
|
||||
'notifications': [],
|
||||
'attachment_ids': [],
|
||||
'trackingValues': [],
|
||||
'linkPreviews': [],
|
||||
'messageReactionGroups': [],
|
||||
'chatbot_script_step_id': self.step_email.id,
|
||||
'needaction_partner_ids': [],
|
||||
'history_partner_ids': [],
|
||||
'is_note': False,
|
||||
'is_discussion': True,
|
||||
'subtype_description': False,
|
||||
'is_notification': False,
|
||||
'recipients': [],
|
||||
'module_icon': '/mail/static/description/icon.png',
|
||||
'sms_ids': []
|
||||
}])
|
||||
|
||||
@users('emp')
|
||||
def test_message_format(self):
|
||||
im_livechat_channel = self.env['im_livechat.channel'].sudo().create({'name': 'support', 'user_ids': [Command.link(self.users[0].id)]})
|
||||
self.users[0].im_status = 'online' # make available for livechat (ignore leave)
|
||||
channel_livechat_1 = self.env['mail.channel'].browse(im_livechat_channel._open_livechat_mail_channel(anonymous_name='anon 1', previous_operator_id=self.users[0].partner_id.id, user_id=self.users[1].id, country_id=self.env.ref('base.in').id)['id'])
|
||||
record_rating = self.env['rating.rating'].create({
|
||||
'res_model_id': self.env['ir.model']._get('mail.channel').id,
|
||||
'res_id': channel_livechat_1.id,
|
||||
'parent_res_model_id': self.env['ir.model']._get('im_livechat.channel').id,
|
||||
'parent_res_id': im_livechat_channel.id,
|
||||
'rated_partner_id': self.users[0].partner_id.id,
|
||||
'partner_id': self.users[1].partner_id.id,
|
||||
'rating': 5,
|
||||
'consumed': True,
|
||||
})
|
||||
message = channel_livechat_1.message_post(
|
||||
author_id=record_rating.partner_id.id,
|
||||
body="<img src='%s' alt=':%s/5' style='width:18px;height:18px;float:left;margin-right: 5px;'/>%s"
|
||||
% (record_rating.rating_image_url, record_rating.rating, record_rating.feedback),
|
||||
rating_id=record_rating.id,
|
||||
)
|
||||
self.assertEqual(message.message_format(), [{
|
||||
'attachment_ids': [],
|
||||
'author': {
|
||||
'id': self.users[1].partner_id.id,
|
||||
'name': "test1",
|
||||
},
|
||||
'body': message.body,
|
||||
'date': message.date,
|
||||
'guestAuthor': [('clear',)],
|
||||
'history_partner_ids': [],
|
||||
'id': message.id,
|
||||
'is_discussion': False,
|
||||
'is_note': True,
|
||||
'is_notification': False,
|
||||
'linkPreviews': [],
|
||||
'message_type': 'notification',
|
||||
'messageReactionGroups': [],
|
||||
'model': 'mail.channel',
|
||||
'module_icon': '/mail/static/description/icon.png',
|
||||
'needaction_partner_ids': [],
|
||||
'notifications': [],
|
||||
'rating': {
|
||||
'id': record_rating.id,
|
||||
'ratingImageUrl': record_rating.rating_image_url,
|
||||
'ratingText': record_rating.rating_text,
|
||||
},
|
||||
'recipients': [],
|
||||
'record_name': "test1 Ernest Employee",
|
||||
'res_id': channel_livechat_1.id,
|
||||
'sms_ids': [],
|
||||
'starred_partner_ids': [],
|
||||
'subject': False,
|
||||
'subtype_description': False,
|
||||
'subtype_id': (self.env.ref('mail.mt_note').id, 'Note'),
|
||||
'trackingValues': [],
|
||||
}])
|
||||
Loading…
Add table
Add a link
Reference in a new issue