[IMP] website_livechat : add various tests on livechat and visitors
Adds Python tests and javascripts tours on livechat (website and visitor integration). Because breaking livechat every two days in rush periods (or even not) is getting quite annoying. Those tests are testing : - The client side flow (open livechat, send messages, send rating and close the livechat session) - The channel and message author naming, visitor page view history - Chat request flow (complete chat request flow, open empty operator's chat request and cancel due to visitor's new chat session) Task ID : 2079087 PR #40052 Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
@@ -184,9 +184,9 @@ class ImLivechatChannel(models.Model):
|
||||
FROM mail_channel c
|
||||
LEFT OUTER JOIN mail_message_mail_channel_rel r ON c.id = r.mail_channel_id
|
||||
LEFT OUTER JOIN mail_message m ON r.mail_message_id = m.id
|
||||
WHERE m.create_date > ((now() at time zone 'UTC') - interval '30 minutes')
|
||||
AND c.channel_type = 'livechat'
|
||||
WHERE c.channel_type = 'livechat'
|
||||
AND c.livechat_operator_id in %s
|
||||
AND m.create_date > ((now() at time zone 'UTC') - interval '30 minutes')
|
||||
GROUP BY c.livechat_operator_id
|
||||
ORDER BY COUNT(DISTINCT c.id) asc""", (tuple(operators.mapped('partner_id').ids),))
|
||||
active_channels = self.env.cr.dictfetchall()
|
||||
|
||||
@@ -482,7 +482,7 @@ var Feedback = Widget.extend({
|
||||
if (reason) {
|
||||
content += " \n" + reason;
|
||||
}
|
||||
self.trigger('send_message', { content: content });
|
||||
self.trigger('send_message', { content: content, isFeedback: true });
|
||||
});
|
||||
},
|
||||
/**
|
||||
|
||||
@@ -12,6 +12,7 @@ Allow website visitors to chat with the collaborators. This module also brings a
|
||||
'application': False,
|
||||
'auto_install': True,
|
||||
'data': [
|
||||
'views/assets.xml',
|
||||
'views/website_livechat.xml',
|
||||
'views/res_config_settings_views.xml',
|
||||
'views/website_livechat_view.xml',
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import main
|
||||
from . import test
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.http import Controller, request, route
|
||||
|
||||
|
||||
class TestBusController(Controller):
|
||||
"""
|
||||
This controller is only useful for test purpose. Bus is unavailable in test mode, but there is no way to know,
|
||||
at client side, if we are running in test mode or not. This route can be called while running tours to mock
|
||||
some behaviour in function of the test mode status (activated or not).
|
||||
|
||||
E.g. : To test the livechat and to check there is no duplicates in message displayed in the chatter,
|
||||
in test mode, we need to mock a 'message added' notification that is normally triggered by the bus.
|
||||
In Normal mode, the bus triggers itself the notification.
|
||||
"""
|
||||
@route('/bus/test_mode_activated', type="json", auth="public")
|
||||
def is_test_mode_activated(self):
|
||||
return request.registry.in_test_mode()
|
||||
@@ -15,6 +15,7 @@ class ImLivechatChannel(models.Model):
|
||||
if not user_id:
|
||||
mail_channel_vals['anonymous_name'] = visitor_sudo.display_name + (' (%s)' % visitor_sudo.country_id.name if visitor_sudo.country_id else '')
|
||||
# As chat requested by the visitor, delete the chat requested by an operator if any to avoid conflicts between two flows
|
||||
# TODO DBE : Move this into the proper method (open or init mail channel)
|
||||
chat_request_channel = self.env['mail.channel'].sudo().search([('livechat_visitor_id', '=', visitor_sudo.id), ('livechat_active', '=', True)])
|
||||
for mail_channel in chat_request_channel:
|
||||
mail_channel._close_livechat_session(cancel=True, speaking_with=operator.name)
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
odoo.define('website_livechat.tour_common', function(require) {
|
||||
'use strict';
|
||||
|
||||
var session = require('web.session');
|
||||
var LivechatButton = require('im_livechat.im_livechat').LivechatButton;
|
||||
|
||||
/**
|
||||
* Alter this method for test purposes.
|
||||
*
|
||||
* Fake the notification after sending message
|
||||
* As bus is not available, it's necessary to add the message in the chatter + in livechat.messages
|
||||
*
|
||||
* Add a class to the chatter window after sendFeedback is done
|
||||
* to force the test to wait until feedback is really done
|
||||
* (to check afterwards if the livechat session is set to inactive)
|
||||
*
|
||||
* Note : this asset is loaded for tests only (rpc call done only during tests)
|
||||
*/
|
||||
LivechatButton.include({
|
||||
_sendMessage: function (message) {
|
||||
var self = this;
|
||||
return this._super.apply(this, arguments).then(function () {
|
||||
if (message.isFeedback) {
|
||||
$('div.o_thread_window_header').addClass('feedback_sent');
|
||||
}
|
||||
else {
|
||||
session.rpc('/bus/test_mode_activated', {}).then(function (in_test_mode) {
|
||||
if (in_test_mode) {
|
||||
var notification = [
|
||||
self._livechat.getUUID(),
|
||||
{
|
||||
'id': -1,
|
||||
'author_id': [0, 'Website Visitor Test'],
|
||||
'email_from': 'Website Visitor Test',
|
||||
'body': '<p>' + message.content + '</p>',
|
||||
'is_discussion': true,
|
||||
'subtype_id': [1, "Discussions"],
|
||||
'date': moment().format('YYYY-MM-DD HH:mm:ss'),
|
||||
}
|
||||
]
|
||||
self._handleNotification(notification);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
/*******************************
|
||||
* Common Steps
|
||||
*******************************/
|
||||
|
||||
var startStep = [{
|
||||
content: "click on livechat widget",
|
||||
trigger: "div.o_livechat_button"
|
||||
}, {
|
||||
content: "Say hello!",
|
||||
trigger: "input.o_composer_text_field",
|
||||
run: "text Hello Sir!"
|
||||
}, {
|
||||
content: "Send the message",
|
||||
trigger: "input.o_composer_text_field",
|
||||
run: function() {
|
||||
$('input.o_composer_text_field').trigger($.Event('keydown', {which: $.ui.keyCode.ENTER}));
|
||||
}
|
||||
}, {
|
||||
content: "Verify your message has been typed",
|
||||
trigger: "div.o_thread_message_content>p:contains('Hello Sir!')"
|
||||
}, {
|
||||
content: "Verify there is no duplicates",
|
||||
trigger: "body",
|
||||
run: function () {
|
||||
if ($("div.o_thread_message_content p:contains('Hello Sir!')").length === 1) {
|
||||
$('body').addClass('no_duplicated_message');
|
||||
}
|
||||
}
|
||||
}, {
|
||||
content: "Is your message correctly sent ?",
|
||||
trigger: 'body.no_duplicated_message'
|
||||
}];
|
||||
|
||||
var endDiscussionStep = [{
|
||||
content: "Close the chatter",
|
||||
trigger: "a.o_thread_window_close",
|
||||
run: function() {
|
||||
$('a.o_thread_window_close').click();
|
||||
}
|
||||
}];
|
||||
|
||||
var feedbackStep = [{
|
||||
content: "Type a feedback",
|
||||
trigger: "div.o_livechat_rating_reason > textarea",
|
||||
run: "text ;-) This was really helpful. Thanks ;-)!"
|
||||
}, {
|
||||
content: "Send the feedback",
|
||||
trigger: "input[type='button'].o_rating_submit_button",
|
||||
}, {
|
||||
content: "Check if feedback has been sent",
|
||||
trigger: "div.o_thread_window_header.feedback_sent",
|
||||
}, {
|
||||
content: "Thanks for your feedback",
|
||||
trigger: "div.o_livechat_rating_box:has(div:contains('Thank you for your feedback'))",
|
||||
}];
|
||||
|
||||
var transcriptStep = [{
|
||||
content: "Type your email",
|
||||
trigger: "input[id='o_email']",
|
||||
run: "text deboul@onner.com"
|
||||
}, {
|
||||
content: "Send the conversation to your email address",
|
||||
trigger: "button.o_email_chat_button",
|
||||
}, {
|
||||
content: "Type your email",
|
||||
trigger: "div.o_livechat_email:has(strong:contains('Conversation Sent'))",
|
||||
}];
|
||||
|
||||
var closeStep = [{
|
||||
content: "Close the conversation with the x button",
|
||||
trigger: "a.o_thread_window_close",
|
||||
}, {
|
||||
content: "Check that the chat window is closed",
|
||||
trigger: 'body',
|
||||
run: function () {
|
||||
if ($('div.o_livechat_button').length === 1 && !$('div.o_livechat_button').is(':visible')) {
|
||||
$('body').addClass('tour_success');
|
||||
}
|
||||
}
|
||||
}, {
|
||||
content: "Is the Test succeded ?",
|
||||
trigger: 'body.tour_success'
|
||||
}];
|
||||
|
||||
var goodRatingStep = [{
|
||||
content: "Send Good Rating",
|
||||
trigger: "div.o_livechat_rating_choices > img[data-value=10]",
|
||||
}, {
|
||||
content: "Check if feedback has been sent",
|
||||
trigger: "div.o_thread_window_header.feedback_sent",
|
||||
}, {
|
||||
content: "Thanks for your feedback",
|
||||
trigger: "div.o_livechat_rating_box:has(div:contains('Thank you for your feedback'))"
|
||||
}];
|
||||
|
||||
var okRatingStep = [{
|
||||
content: "Send ok Rating",
|
||||
trigger: "div.o_livechat_rating_choices > img[data-value=5]",
|
||||
}];
|
||||
|
||||
var sadRatingStep = [{
|
||||
content: "Send bad Rating",
|
||||
trigger: "div.o_livechat_rating_choices > img[data-value=1]",
|
||||
}];
|
||||
|
||||
return {
|
||||
'startStep': startStep,
|
||||
'endDiscussionStep': endDiscussionStep,
|
||||
'transcriptStep': transcriptStep,
|
||||
'feedbackStep': feedbackStep,
|
||||
'closeStep': closeStep,
|
||||
'goodRatingStep': goodRatingStep,
|
||||
'okRatingStep': okRatingStep,
|
||||
'sadRatingStep': sadRatingStep,
|
||||
};
|
||||
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
odoo.define('website_livechat.tour', function(require) {
|
||||
'use strict';
|
||||
|
||||
var commonSteps = require("website_livechat.tour_common");
|
||||
var tour = require("web_tour.tour");
|
||||
|
||||
tour.register('website_livechat_complete_flow_tour', {
|
||||
test: true,
|
||||
url: '/',
|
||||
}, [].concat(commonSteps.startStep, commonSteps.endDiscussionStep, commonSteps.okRatingStep, commonSteps.feedbackStep, commonSteps.transcriptStep, commonSteps.closeStep));
|
||||
|
||||
tour.register('website_livechat_happy_rating_tour', {
|
||||
test: true,
|
||||
url: '/',
|
||||
}, [].concat(commonSteps.startStep, commonSteps.endDiscussionStep, commonSteps.goodRatingStep));
|
||||
|
||||
tour.register('website_livechat_ok_rating_tour', {
|
||||
test: true,
|
||||
url: '/',
|
||||
}, [].concat(commonSteps.startStep, commonSteps.endDiscussionStep, commonSteps.okRatingStep, commonSteps.feedbackStep));
|
||||
|
||||
tour.register('website_livechat_sad_rating_tour', {
|
||||
test: true,
|
||||
url: '/',
|
||||
}, [].concat(commonSteps.startStep, commonSteps.endDiscussionStep, commonSteps.sadRatingStep, commonSteps.feedbackStep));
|
||||
|
||||
tour.register('website_livechat_no_rating_tour', {
|
||||
test: true,
|
||||
url: '/',
|
||||
}, [].concat(commonSteps.startStep, commonSteps.endDiscussionStep, commonSteps.transcriptStep, commonSteps.closeStep));
|
||||
|
||||
tour.register('website_livechat_no_rating_no_close_tour', {
|
||||
test: true,
|
||||
url: '/',
|
||||
}, [].concat(commonSteps.startStep));
|
||||
|
||||
return {};
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
odoo.define('website_livechat.chat_request_tour', function(require) {
|
||||
'use strict';
|
||||
|
||||
var commonSteps = require("website_livechat.tour_common");
|
||||
var tour = require("web_tour.tour");
|
||||
|
||||
|
||||
var stepWithChatRequestStep = [{
|
||||
content: "Answer the chat request!",
|
||||
trigger: "input.o_composer_text_field",
|
||||
run: "text Hi ! What a coincidence! I need your help indeed."
|
||||
}, {
|
||||
content: "Send the message",
|
||||
trigger: "input.o_composer_text_field",
|
||||
run: function() {
|
||||
$('input.o_composer_text_field').trigger($.Event('keydown', {which: $.ui.keyCode.ENTER}));
|
||||
}
|
||||
}, {
|
||||
content: "Verify your message has been typed",
|
||||
trigger: "div.o_thread_message_content>p:contains('Hi ! What a coincidence! I need your help indeed.')"
|
||||
}, {
|
||||
content: "Verify there is no duplicates",
|
||||
trigger: "body",
|
||||
run: function () {
|
||||
if ($("div.o_thread_message_content p:contains('Hi ! What a coincidence! I need your help indeed.')").length === 1) {
|
||||
$('body').addClass('no_duplicated_message');
|
||||
}
|
||||
}
|
||||
}, {
|
||||
content: "Is your message correctly sent ?",
|
||||
trigger: 'body.no_duplicated_message'
|
||||
}];
|
||||
|
||||
|
||||
tour.register('website_livechat_chat_request_part_1_no_close_tour', {
|
||||
test: true,
|
||||
url: '/',
|
||||
}, [].concat(stepWithChatRequestStep));
|
||||
|
||||
tour.register('website_livechat_chat_request_part_2_end_session_tour', {
|
||||
test: true,
|
||||
url: '/',
|
||||
}, [].concat(commonSteps.endDiscussionStep, commonSteps.okRatingStep, commonSteps.feedbackStep, commonSteps.transcriptStep, commonSteps.closeStep));
|
||||
|
||||
return {};
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import common
|
||||
from . import test_ui
|
||||
from . import test_livechat_basic_flow
|
||||
from . import test_livechat_request
|
||||
@@ -0,0 +1,87 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, tests
|
||||
|
||||
|
||||
class TestLivechatCommon(tests.TransactionCase):
|
||||
def setUp(self):
|
||||
super(TestLivechatCommon, self).setUp()
|
||||
self.base_datetime = fields.Datetime.from_string("2019-11-11 21:30:00")
|
||||
|
||||
self.operator = self.env['res.users'].create({
|
||||
'name': 'Operator Michel',
|
||||
'login': 'operator',
|
||||
'email': 'operator@example.com',
|
||||
'password': "ideboulonate",
|
||||
'livechat_username': 'El Deboulonnator',
|
||||
})
|
||||
|
||||
self.livechat_channel = self.env['im_livechat.channel'].create({
|
||||
'name': 'The basic channel',
|
||||
'user_ids': [(6, 0, [self.operator.id])]
|
||||
})
|
||||
|
||||
self.max_sessions_per_operator = 5
|
||||
visitor_vals = {
|
||||
'lang_id': self.env.ref('base.lang_en').id,
|
||||
'country_id': self.env.ref('base.be').id,
|
||||
'website_id': self.env.ref('website.default_website').id,
|
||||
}
|
||||
self.visitors = self.env['website.visitor'].create([{
|
||||
'lang_id': self.env.ref('base.lang_en').id,
|
||||
'country_id': self.env.ref('base.de').id,
|
||||
'website_id': self.env.ref('website.default_website').id,
|
||||
'partner_id': self.env.ref('base.user_demo').partner_id.id,
|
||||
}] + [visitor_vals]*self.max_sessions_per_operator)
|
||||
self.visitor_demo, self.visitor = self.visitors[0], self.visitors[1]
|
||||
|
||||
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
||||
|
||||
self.open_chat_url = base_url + "/im_livechat/get_session"
|
||||
self.open_chat_params = {'params': {
|
||||
'channel_id': self.livechat_channel.id,
|
||||
'anonymous_name': "Wrong Name"
|
||||
}}
|
||||
|
||||
self.send_feedback_url = base_url + "/im_livechat/feedback"
|
||||
self.leave_session_url = base_url + "/im_livechat/visitor_leave_session"
|
||||
|
||||
# override the get_available_users to return only Michel as available
|
||||
operators = self.operator
|
||||
def get_available_users(self):
|
||||
return operators
|
||||
self.patch(type(self.env['im_livechat.channel']), '_get_available_users', get_available_users)
|
||||
|
||||
# override the _get_visitor_from_request to return self.visitor
|
||||
self.target_visitor = self.visitor
|
||||
def get_visitor_from_request(self_mock, **kwargs):
|
||||
return self.target_visitor
|
||||
self.patch(type(self.env['website.visitor']), '_get_visitor_from_request', get_visitor_from_request)
|
||||
|
||||
def _send_message(self, channel, email_from, body, author_id=False):
|
||||
# As bus is unavailable in test mode, we cannot call /mail/chat_post route to post a message.
|
||||
# Instead, we post directly the message on the given channel.
|
||||
channel.with_context(mail_create_nosubscribe=True) \
|
||||
.message_post(author_id=author_id, email_from=email_from, body=body,
|
||||
message_type='comment', subtype_id=self.env.ref('mail.mt_comment').id)
|
||||
|
||||
def _send_rating(self, channel, visitor, rating_value, reason=False):
|
||||
channel_messages_count = len(channel.message_ids)
|
||||
|
||||
rating_to_emoji = {1: "😞", 5: "😐", 10: "😊"}
|
||||
self.opener.post(url=self.send_feedback_url, json={'params': {
|
||||
'uuid': channel.uuid,
|
||||
'rate': rating_value,
|
||||
'reason': reason,
|
||||
}})
|
||||
res_model_id = self.env['ir.model'].sudo().search([('model', '=', channel._name)], limit=1).id
|
||||
rating = self.env['rating.rating'].search([('res_id', '=', channel.id), ('res_model_id', '=', res_model_id)])
|
||||
self.assertEqual(rating.rating, rating_value, "The rating is not correct.")
|
||||
|
||||
message = "Rating: %s" % rating_to_emoji[rating_value]
|
||||
if reason:
|
||||
message += " \n%s" % reason
|
||||
|
||||
self._send_message(channel, visitor.display_name, message, author_id=False)
|
||||
self.assertEqual(len(channel.message_ids), channel_messages_count + 1)
|
||||
@@ -0,0 +1,113 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import datetime
|
||||
|
||||
from odoo import tests, _
|
||||
from odoo.addons.website_livechat.tests.common import TestLivechatCommon
|
||||
|
||||
|
||||
@tests.tagged('post_install', '-at_install')
|
||||
class TestLivechatBasicFlowHttpCase(tests.HttpCase, TestLivechatCommon):
|
||||
def test_visitor_banner_history(self):
|
||||
# create visitor history
|
||||
self.env['website.track'].create([{
|
||||
'page_id': self.env.ref('website.homepage_page').id,
|
||||
'visitor_id': self.visitor.id,
|
||||
'visit_datetime': self.base_datetime,
|
||||
}, {
|
||||
'page_id': self.env.ref('website.contactus_page').id,
|
||||
'visitor_id': self.visitor.id,
|
||||
'visit_datetime': self.base_datetime - datetime.timedelta(minutes=10),
|
||||
}, {
|
||||
'page_id': self.env.ref('website.aboutus_page').id,
|
||||
'visitor_id': self.visitor.id,
|
||||
'visit_datetime': self.base_datetime - datetime.timedelta(minutes=20),
|
||||
}, {
|
||||
'page_id': self.env.ref('website.contactus_page').id,
|
||||
'visitor_id': self.visitor.id,
|
||||
'visit_datetime': self.base_datetime - datetime.timedelta(minutes=30),
|
||||
}])
|
||||
|
||||
handmade_history = "%s (21:10) → %s (21:20) → %s (21:30)" % (
|
||||
self.env.ref('website.aboutus_page').name,
|
||||
self.env.ref('website.contactus_page').name,
|
||||
self.env.ref('website.homepage_page').name
|
||||
)
|
||||
history = self.env['mail.channel']._get_visitor_history(self.visitor)
|
||||
|
||||
self.assertEqual(history, handmade_history)
|
||||
|
||||
def test_livechat_username(self):
|
||||
# Open a new live chat
|
||||
res = self.opener.post(url=self.open_chat_url, json=self.open_chat_params)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
channel_1 = self.env['mail.channel'].search([('livechat_visitor_id', '=', self.visitor.id), ('livechat_active', '=', True)], limit=1)
|
||||
|
||||
# Check Channel naming
|
||||
self.assertEqual(channel_1.name, "%s, %s" % (self.visitor.display_name, self.operator.livechat_username))
|
||||
channel_1.unlink()
|
||||
|
||||
# Remove livechat_username
|
||||
self.operator.livechat_username = False
|
||||
|
||||
# Open a new live chat
|
||||
res = self.opener.post(url=self.open_chat_url, json=self.open_chat_params)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
channel_2 = self.env['mail.channel'].search([('livechat_visitor_id', '=', self.visitor.id), ('livechat_active', '=', True)], limit=1)
|
||||
|
||||
# Check Channel naming
|
||||
self.assertEqual(channel_2.name, "%s, %s" % (self.visitor.display_name, self.operator.name))
|
||||
|
||||
def test_basic_flow_with_rating(self):
|
||||
channel = self._common_basic_flow()
|
||||
|
||||
self._send_rating(channel, self.visitor, 5, "This deboulonnage was fine but not topitop.")
|
||||
|
||||
channel._close_livechat_session()
|
||||
|
||||
self.assertEqual(len(channel.message_ids), 4)
|
||||
self.assertEqual(channel.message_ids[0].author_id, self.env.ref('base.partner_root'), "Odoobot must be the sender of the 'has left the conversation' message.")
|
||||
self.assertEqual(channel.message_ids[0].body, "<p>%s has left the conversation.</p>" % self.visitor.display_name)
|
||||
self.assertEqual(channel.livechat_active, False, "The livechat session must be inactive as the visitor sent his feedback.")
|
||||
|
||||
def test_basic_flow_without_rating(self):
|
||||
channel = self._common_basic_flow()
|
||||
|
||||
# has left the conversation
|
||||
channel._close_livechat_session()
|
||||
self.assertEqual(len(channel.message_ids), 3)
|
||||
self.assertEqual(channel.message_ids[0].author_id, self.env.ref('base.partner_root'), "Odoobot must be the author the message.")
|
||||
self.assertEqual(channel.message_ids[0].body, "<p>%s has left the conversation.</p>" % self.visitor.display_name)
|
||||
self.assertEqual(channel.livechat_active, False, "The livechat session must be inactive since visitor has left the conversation.")
|
||||
|
||||
def _common_basic_flow(self):
|
||||
# Open a new live chat
|
||||
res = self.opener.post(url=self.open_chat_url, json=self.open_chat_params)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
channel = self.env['mail.channel'].search([('livechat_visitor_id', '=', self.visitor.id), ('livechat_active', '=', True)], limit=1)
|
||||
|
||||
# Check Channel and Visitor naming
|
||||
self.assertEqual(self.visitor.display_name, "%s #%s" % (_("Website Visitor"), self.visitor.id))
|
||||
self.assertEqual(channel.name, "%s, %s" % (self.visitor.display_name, self.operator.livechat_username))
|
||||
|
||||
# Post Message from visitor
|
||||
self._send_message(channel, self.visitor.display_name, "Message from Visitor")
|
||||
|
||||
self.assertEqual(len(channel.message_ids), 1)
|
||||
self.assertEqual(channel.message_ids[0].author_id.id, False, "The author of the message is not a partner.")
|
||||
self.assertEqual(channel.message_ids[0].email_from, self.visitor.display_name, "The sender's email should be the visitor's email.")
|
||||
self.assertEqual(channel.message_ids[0].body, "<p>Message from Visitor</p>")
|
||||
self.assertEqual(channel.livechat_active, True, "The livechat session must be active as the visitor did not left the conversation yet.")
|
||||
|
||||
# Post message from operator
|
||||
self._send_message(channel, self.operator.email, "Message from Operator", author_id=self.operator.partner_id.id)
|
||||
|
||||
self.assertEqual(len(channel.message_ids), 2)
|
||||
self.assertEqual(channel.message_ids[0].author_id, self.operator.partner_id, "The author of the message should be the operator.")
|
||||
self.assertEqual(channel.message_ids[0].email_from, self.operator.email, "The sender's email should be the operator's email.")
|
||||
self.assertEqual(channel.message_ids[0].body, "<p>Message from Operator</p>")
|
||||
self.assertEqual(channel.livechat_active, True, "The livechat session must be active as the visitor did not left the conversation yet.")
|
||||
|
||||
return channel
|
||||
@@ -0,0 +1,79 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import tests
|
||||
from odoo.addons.website_livechat.tests.common import TestLivechatCommon
|
||||
|
||||
|
||||
@tests.tagged('post_install', '-at_install')
|
||||
class TestLivechatRequestHttpCase(tests.HttpCase, TestLivechatCommon):
|
||||
def test_livechat_request_complete_flow(self):
|
||||
self._clean_livechat_sessions()
|
||||
|
||||
# Send first chat request - Open chat from operator side
|
||||
channel_1 = self._common_chat_request_flow()
|
||||
# Visitor Rates the conversation (Good)
|
||||
self._send_rating(channel_1, self.visitor, 10)
|
||||
|
||||
# Operator Re-Send a chat request
|
||||
channel_2 = self._common_chat_request_flow()
|
||||
# Visitor Rates the conversation (Bad)
|
||||
self._send_rating(channel_2, self.visitor, 1, "Stop bothering me! I hate you </3 !")
|
||||
|
||||
def test_cancel_chat_request_on_visitor_demand(self):
|
||||
self._clean_livechat_sessions()
|
||||
|
||||
self.operator_b = self.env['res.users'].create({
|
||||
'name': 'Operator Marc',
|
||||
'login': 'operator_b',
|
||||
'email': 'operatormarc@example.com',
|
||||
'password': "operatormarc",
|
||||
'livechat_username': "Marco'r El",
|
||||
})
|
||||
|
||||
# Open Chat Request
|
||||
self.visitor.with_user(self.operator_b).action_send_chat_request()
|
||||
chat_request = self.env['mail.channel'].search([('livechat_visitor_id', '=', self.visitor.id), ('livechat_active', '=', True)])
|
||||
self.assertEqual(chat_request.livechat_operator_id, self.operator_b.partner_id, "Operator for active livechat session must be Operator Marc")
|
||||
|
||||
# Click on livechatbutton at client side
|
||||
res = self.opener.post(url=self.open_chat_url, json=self.open_chat_params)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
channel = self.env['mail.channel'].search([('livechat_visitor_id', '=', self.visitor.id),
|
||||
('livechat_active', '=', True)])
|
||||
|
||||
# Check that the chat request has been canceled.
|
||||
chat_request.invalidate_cache()
|
||||
self.assertEqual(chat_request.livechat_active, False, "The livechat request must be inactive as the visitor started himself a livechat session.")
|
||||
self.assertEqual(len(channel), 1)
|
||||
self.assertEqual(channel.livechat_operator_id, self.operator.partner_id, "Operator for active livechat session must be Michel Operator")
|
||||
|
||||
def _common_chat_request_flow(self):
|
||||
self.visitor.with_user(self.operator).action_send_chat_request()
|
||||
channel = self.env['mail.channel'].search([('livechat_visitor_id', '=', self.visitor.id), ('livechat_active', '=', True)])
|
||||
self.assertEqual(len(channel), 1)
|
||||
self.assertEqual(channel.livechat_operator_id, self.operator.partner_id, "Michel Operator should be the operator of this channel.")
|
||||
self.assertEqual(len(channel.message_ids), 0)
|
||||
|
||||
# Operator Sends message
|
||||
self._send_message(channel, self.operator.email, "Hello Again !", author_id=self.operator.partner_id.id)
|
||||
self.assertEqual(len(channel.message_ids), 1)
|
||||
|
||||
# Visitor Answers
|
||||
self._send_message(channel, self.visitor.display_name, "Answer from Visitor")
|
||||
self.assertEqual(len(channel.message_ids), 2)
|
||||
|
||||
# Visitor Leave the conversation
|
||||
channel._close_livechat_session()
|
||||
self.assertEqual(len(channel.message_ids), 3)
|
||||
self.assertEqual(channel.message_ids[0].author_id, self.env.ref('base.partner_root'), "Odoobot must be the sender of the 'has left the conversation' message.")
|
||||
self.assertEqual(channel.message_ids[0].body, "<p>%s has left the conversation.</p>" % self.visitor.display_name)
|
||||
self.assertEqual(channel.livechat_active, False, "The livechat session must be inactive as the visitor sent his feedback.")
|
||||
|
||||
return channel
|
||||
|
||||
def _clean_livechat_sessions(self):
|
||||
# clean every possible mail channel linked to the visitor
|
||||
active_channels = self.env['mail.channel'].search([('livechat_visitor_id', '=', self.visitor.id), ('livechat_active', '=', True)])
|
||||
for active_channel in active_channels:
|
||||
active_channel._close_livechat_session()
|
||||
@@ -0,0 +1,87 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import tests, _
|
||||
from odoo.addons.website_livechat.tests.common import TestLivechatCommon
|
||||
|
||||
|
||||
@tests.tagged('post_install', '-at_install')
|
||||
class TestLivechatUI(tests.HttpCase, TestLivechatCommon):
|
||||
def setUp(self):
|
||||
super(TestLivechatUI, self).setUp()
|
||||
self.visitor_tour = self.env['website.visitor'].create({
|
||||
'name': 'Visitor Tour',
|
||||
'website_id': self.env.ref('website.default_website').id,
|
||||
})
|
||||
self.target_visitor = self.visitor_tour
|
||||
|
||||
def test_complete_rating_flow_ui(self):
|
||||
self.start_tour("/", 'website_livechat_complete_flow_tour')
|
||||
self._check_end_of_rating_tours()
|
||||
|
||||
def test_happy_rating_flow_ui(self):
|
||||
self.start_tour("/", 'website_livechat_happy_rating_tour')
|
||||
self._check_end_of_rating_tours()
|
||||
|
||||
def test_ok_rating_flow_ui(self):
|
||||
self.start_tour("/", 'website_livechat_ok_rating_tour')
|
||||
self._check_end_of_rating_tours()
|
||||
|
||||
def test_bad_rating_flow_ui(self):
|
||||
self.start_tour("/", 'website_livechat_sad_rating_tour')
|
||||
self._check_end_of_rating_tours()
|
||||
|
||||
def test_no_rating_flow_ui(self):
|
||||
self.start_tour("/", 'website_livechat_no_rating_tour')
|
||||
channel = self.env['mail.channel'].search([('livechat_visitor_id', '=', self.visitor_tour.id)])
|
||||
self.assertEqual(len(channel), 1, "There can only be one channel created for 'Visitor Tour'.")
|
||||
self.assertEqual(channel.livechat_active, False, 'Livechat must be inactive after closing the chat window.')
|
||||
|
||||
def test_no_rating_no_close_flow_ui(self):
|
||||
self.start_tour("/", 'website_livechat_no_rating_no_close_tour')
|
||||
channel = self.env['mail.channel'].search([('livechat_visitor_id', '=', self.visitor_tour.id)])
|
||||
self.assertEqual(len(channel), 1, "There can only be one channel created for 'Visitor Tour'.")
|
||||
self.assertEqual(channel.livechat_active, True, 'Livechat must be active while the chat window is not closed.')
|
||||
|
||||
def test_empty_chat_request_flow_no_rating_no_close_ui(self):
|
||||
# Open an empty chat request
|
||||
self.visitor_tour.with_user(self.operator).action_send_chat_request()
|
||||
chat_request = self.env['mail.channel'].search([('livechat_visitor_id', '=', self.visitor_tour.id), ('livechat_active', '=', True)])
|
||||
|
||||
# Visitor ask a new livechat session before the operator start to send message in chat request session
|
||||
self.start_tour("/", 'website_livechat_no_rating_no_close_tour')
|
||||
|
||||
# Visitor's session must be active (gets the priority)
|
||||
channel = self.env['mail.channel'].search([('livechat_visitor_id', '=', self.visitor_tour.id), ('livechat_active', '=', True)])
|
||||
self.assertEqual(len(channel), 1, "There can only be one channel created for 'Visitor Tour'.")
|
||||
self.assertEqual(channel.livechat_active, True, 'Livechat must be active while the chat window is not closed.')
|
||||
|
||||
# Check that the chat request has been canceled.
|
||||
chat_request.invalidate_cache()
|
||||
self.assertEqual(chat_request.livechat_active, False, "The livechat request must be inactive as the visitor started himself a livechat session.")
|
||||
|
||||
def test_chat_request_flow_with_rating_ui(self):
|
||||
# Open a chat request
|
||||
self.visitor_tour.with_user(self.operator).action_send_chat_request()
|
||||
chat_request = self.env['mail.channel'].search([('livechat_visitor_id', '=', self.visitor_tour.id), ('livechat_active', '=', True)])
|
||||
|
||||
# Operator send a message to the visitor
|
||||
self._send_message(chat_request, self.operator.email, "Hello my friend !", author_id=self.operator.partner_id.id)
|
||||
self.assertEqual(len(chat_request.message_ids), 1, "Number of messages incorrect.")
|
||||
|
||||
# Visitor comes to the website and receives the chat request
|
||||
self.start_tour("/", 'website_livechat_chat_request_part_1_no_close_tour')
|
||||
|
||||
# Check that the current session is the chat request
|
||||
channel = self.env['mail.channel'].search([('livechat_visitor_id', '=', self.visitor_tour.id), ('livechat_active', '=', True)])
|
||||
self.assertEqual(len(channel), 1, "There can only be one channel created for 'Visitor Tour'.")
|
||||
self.assertEqual(channel, chat_request, "The active livechat session must be the chat request one.")
|
||||
|
||||
# Visitor reload the page and continues the chat with the operator normally
|
||||
self.start_tour("/", 'website_livechat_chat_request_part_2_end_session_tour')
|
||||
self._check_end_of_rating_tours()
|
||||
|
||||
def _check_end_of_rating_tours(self):
|
||||
channel = self.env['mail.channel'].search([('livechat_visitor_id', '=', self.visitor_tour.id)])
|
||||
self.assertEqual(len(channel), 1, "There can only be one channel created for 'Visitor Tour'.")
|
||||
self.assertEqual(channel.livechat_active, False, 'Livechat must be inactive after rating.')
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<template id="assets_tests" name="Website Livechat Assets Tests" inherit_id="web.assets_tests">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/website_livechat/static/tests/tours/website_livechat_common.js"></script>
|
||||
<script type="text/javascript" src="/website_livechat/static/tests/tours/website_livechat_rating.js"></script>
|
||||
<script type="text/javascript" src="/website_livechat/static/tests/tours/website_livechat_request.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</data>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user