toggle_star_status: function (event) { event.stopPropagation(); assert.strictEqual(event.data.message_id, 2, "toggle_star_status should have been triggered for message 2 (twice)"); var msg = _.findWhere(messages, {id: event.data.message_id}); msg.is_starred = !msg.is_starred; bus.trigger('update_message', msg); },
odoo.define('web.core', function (require) { "use strict"; var Bus = require('web.Bus'); var Class = require('web.Class'); var QWeb = require('web.QWeb'); var Registry = require('web.Registry'); var translation = require('web.translation'); /** * Whether the client is currently in "debug" mode * * @type Boolean */ var debug = $.deparam($.param.querystring()).debug !== undefined; var bus = new Bus (); _.each('click,dblclick,keydown,keypress,keyup'.split(','), function(evtype) { $('html').on(evtype, function(ev) { bus.trigger(evtype, ev); }); }); _.each('resize,scroll'.split(','), function(evtype) { $(window).on(evtype, function(ev) { bus.trigger(evtype, ev); }); }); return { debug: debug, qweb: new QWeb(debug), // core classes and functions Class: Class, bus: bus, main_bus: new Bus(), _t: translation._t, _lt: translation._lt, // registries action_registry : new Registry(), crash_registry: new Registry(), form_custom_registry: new Registry(), form_tag_registry: new Registry(), form_widget_registry: new Registry(), list_widget_registry: new Registry(), one2many_view_registry: new Registry(), search_filters_registry: new Registry(), search_widgets_registry: new Registry(), /** * @type {String} */ csrf_token: odoo.csrf_token, a_thing: 42, }; });
post_message: function (event) { event.stopPropagation(); var msg_id = messages[messages.length-1].id + 1; messages.push({ attachment_ids: [], author_id: ["42", "Me"], body: event.data.message.content, date: moment(), // now displayed_author: "Me", id: msg_id, is_note: event.data.message.subtype === 'mail.mt_note', is_starred: false, model: 'partner', res_id: 2, }); bus.trigger('new_message', { id: msg_id, model: event.data.options.model, res_id: event.data.options.res_id, }); },
mockRPC: function (route, args) { if (args.method === 'channel_fetch_listeners') { fetchListenersDef.resolve(); return $.when([ {id: 1, name: 'Admin'}, {id: 2, name: 'TestUser'}, {id: 3, name: 'DemoUser'} ]); } if (args.method === 'message_post') { var data = { author_id: ["42", "Me"], body: args.kwargs.body, channel_ids: [1], }; var notification = [[false, 'mail.channel'], data]; bus.trigger('notification', [notification]); receiveMessageDef.resolve(); return $.when(42); } return this._super.apply(this, arguments); },
$(window).on(evtype, function(ev) { bus.trigger(evtype, ev); });
$('html').on(evtype, function(ev) { bus.trigger(evtype, ev); });
QUnit.test('chatter: post, receive and star messages', function (assert) { var done = assert.async(); assert.expect(27); // Remove the mention throttle to speed up the test var mentionThrottle = BasicComposer.prototype.MENTION_THROTTLE; BasicComposer.prototype.MENTION_THROTTLE = 1; this.data.partner.records[0].message_ids = [1]; var messages = [{ attachment_ids: [], author_id: ["1", "John Doe"], body: "A message", date: moment("2016-12-20 09:35:40"), displayed_author: "John Doe", id: 1, is_note: false, is_starred: false, model: 'partner', res_id: 2, }]; var bus = new Bus(); var getSuggestionsDef = $.Deferred(); var form = createView({ View: FormView, model: 'partner', data: this.data, arch: '<form string="Partners">' + '<sheet>' + '<field name="foo"/>' + '</sheet>' + '<div class="oe_chatter">' + '<field name="message_ids" widget="mail_thread" options="{\'display_log_button\': True}"/>' + '</div>' + '</form>', res_id: 2, mockRPC: function (route, args) { if (args.method === 'message_get_suggested_recipients') { return $.when({2: []}); } if (args.method === 'get_mention_suggestions') { getSuggestionsDef.resolve(); return $.when([{email: "*****@*****.**", id: 1, name: "Test User"}]); } return this._super(route, args); }, session: {}, intercepts: { get_messages: function (event) { event.stopPropagation(); var requested_msgs = _.filter(messages, function (msg) { return _.contains(event.data.options.ids, msg.id); }); event.data.callback($.when(requested_msgs)); }, post_message: function (event) { event.stopPropagation(); var msg_id = messages[messages.length-1].id + 1; messages.push({ attachment_ids: [], author_id: ["42", "Me"], body: event.data.message.content, date: moment(), // now displayed_author: "Me", id: msg_id, is_note: event.data.message.subtype === 'mail.mt_note', is_starred: false, model: 'partner', res_id: 2, }); bus.trigger('new_message', { id: msg_id, model: event.data.options.model, res_id: event.data.options.res_id, }); }, get_bus: function (event) { event.stopPropagation(); event.data.callback(bus); }, toggle_star_status: function (event) { event.stopPropagation(); assert.strictEqual(event.data.message_id, 2, "toggle_star_status should have been triggered for message 2 (twice)"); var msg = _.findWhere(messages, {id: event.data.message_id}); msg.is_starred = !msg.is_starred; bus.trigger('update_message', msg); }, }, }); assert.ok(form.$('.o_chatter_topbar .o_chatter_button_log_note').length, "log note button should be available"); assert.strictEqual(form.$('.o_thread_message').length, 1, "thread should contain one message"); assert.ok(!form.$('.o_thread_message:first() .o_mail_note').length, "the message shouldn't be a note"); assert.ok(form.$('.o_thread_message:first() .o_thread_message_core').text().indexOf('A message') >= 0, "the message's body should be correct"); assert.ok(form.$('.o_thread_message:first() .o_mail_info').text().indexOf('John Doe') >= 0, "the message's author should be correct"); // send a message form.$('.o_chatter_button_new_message').click(); assert.ok(!$('.oe_chatter .o_chat_composer').hasClass('o_hidden'), "chatter should be opened"); form.$('.oe_chatter .o_composer_text_field:first()').val("My first message"); form.$('.oe_chatter .o_composer_button_send').click(); assert.ok($('.oe_chatter .o_chat_composer').hasClass('o_hidden'), "chatter should be closed"); assert.strictEqual(form.$('.o_thread_message').length, 2, "thread should contain two messages"); assert.ok(!form.$('.o_thread_message:first() .o_mail_note').length, "the last message shouldn't be a note"); assert.ok(form.$('.o_thread_message:first() .o_thread_message_core').text().indexOf('My first message') >= 0, "the message's body should be correct"); assert.ok(form.$('.o_thread_message:first() .o_mail_info').text().indexOf('Me') >= 0, "the message's author should be correct"); // log a note form.$('.o_chatter_button_log_note').click(); assert.ok(!$('.oe_chatter .o_chat_composer').hasClass('o_hidden'), "chatter should be opened"); form.$('.oe_chatter .o_composer_text_field:first()').val("My first note"); form.$('.oe_chatter .o_composer_button_send').click(); assert.ok($('.oe_chatter .o_chat_composer').hasClass('o_hidden'), "chatter should be closed"); assert.strictEqual(form.$('.o_thread_message').length, 3, "thread should contain three messages"); assert.ok(form.$('.o_thread_message:first() .o_mail_note').length, "the last message should be a note"); assert.ok(form.$('.o_thread_message:first() .o_thread_message_core').text().indexOf('My first note') >= 0, "the message's body should be correct"); assert.ok(form.$('.o_thread_message:first() .o_mail_info').text().indexOf('Me') >= 0, "the message's author should be correct"); // star message 2 assert.ok(form.$('.o_thread_message[data-message-id=2] .o_thread_message_star.fa-star-o').length, "message 2 should not be starred"); form.$('.o_thread_message[data-message-id=2] .o_thread_message_star').click(); assert.ok(form.$('.o_thread_message[data-message-id=2] .o_thread_message_star.fa-star').length, "message 2 should be starred"); // unstar message 2 form.$('.o_thread_message[data-message-id=2] .o_thread_message_star').click(); assert.ok(form.$('.o_thread_message[data-message-id=2] .o_thread_message_star.fa-star-o').length, "message 2 should not be starred"); // very basic test of mention form.$('.o_chatter_button_new_message').click(); var $input = form.$('.oe_chatter .o_composer_text_field:first()'); $input.val('@'); // the cursor position must be set for the mention manager to detect that we are mentionning $input[0].selectionStart = 1; $input[0].selectionEnd = 1; $input.trigger('keyup'); assert.strictEqual(getSuggestionsDef.state(), "pending", "the mention suggestion RPC should be throttled"); getSuggestionsDef .then(concurrency.delay.bind(concurrency, 0)) .then(function () { assert.strictEqual(form.$('.o_mention_proposition:visible').length, 1, "there should be one mention suggestion"); assert.strictEqual(form.$('.o_mention_proposition').data('id'), 1, "suggestion's id should be correct"); assert.strictEqual(form.$('.o_mention_proposition .o_mention_name').text(), 'Test User', "suggestion should be displayed correctly"); assert.strictEqual(form.$('.o_mention_proposition .o_mention_info').text(), '(test@odoo.com)', "suggestion should be displayed correctly"); BasicComposer.prototype.MENTION_THROTTLE = mentionThrottle; form.destroy(); done(); }); });
QUnit.test('@ mention in channel', function (assert) { assert.expect(34); var done = assert.async(); // Remove throttles to speed up the test var channelThrottle = ChatManager.prototype.CHANNEL_SEEN_THROTTLE; var mentionThrottle = BasicComposer.prototype.MENTION_THROTTLE; ChatManager.prototype.CHANNEL_SEEN_THROTTLE = 1; BasicComposer.prototype.MENTION_THROTTLE = 1; var bus = new Bus(); var BusService = createBusService(bus); var fetchListenersDef = $.Deferred(); var receiveMessageDef = $.Deferred(); this.data.initMessaging = { channel_slots: { channel_channel: [{ id: 1, channel_type: "channel", name: "general", }], }, }; createDiscuss({ id: 1, context: {}, params: {}, data: this.data, services: [ChatManager, BusService], mockRPC: function (route, args) { if (args.method === 'channel_fetch_listeners') { fetchListenersDef.resolve(); return $.when([ {id: 1, name: 'Admin'}, {id: 2, name: 'TestUser'}, {id: 3, name: 'DemoUser'} ]); } if (args.method === 'message_post') { var data = { author_id: ["42", "Me"], body: args.kwargs.body, channel_ids: [1], }; var notification = [[false, 'mail.channel'], data]; bus.trigger('notification', [notification]); receiveMessageDef.resolve(); return $.when(42); } return this._super.apply(this, arguments); }, }) .then(function (discuss) { var $general = discuss.$('.o_mail_chat_sidebar') .find('.o_mail_chat_channel_item[data-channel-id=1]'); assert.strictEqual($general.length, 1, "should have the channel item with id 1"); assert.strictEqual($general.attr('title'), 'general', "should have the title 'general'"); // click on general $general.click(); var $input = discuss.$('.o_composer_input').first(); assert.ok($input.length, "should display a composer input"); // Simulate '@' typed by user with mocked Window.getSelection // Note: focus is needed in order to trigger rpc 'channel_fetch_listeners' $input.focus(); $input.text("@"); var unpatchWindowGetSelection = patchWindowGetSelection(); $input.trigger('keyup'); fetchListenersDef .then(concurrency.delay.bind(concurrency, 0)) .then(function () { assert.strictEqual(discuss.$('.dropup.o_composer_mention_dropdown.open').length, 1, "dropup menu for partner mentions should be open"); var $mentionPropositions = discuss.$('.o_mention_proposition'); assert.strictEqual($mentionPropositions.length, 3, "should display 3 partner mention propositions"); var $mention1 = $mentionPropositions.first(); var $mention2 = $mentionPropositions.first().next(); var $mention3 = $mentionPropositions.first().next().next(); // correct 1st mention proposition assert.ok($mention1.hasClass('active'), "first partner mention should be active"); assert.strictEqual($mention1.data('id'), 1, "first partner mention should link to the correct partner id"); assert.strictEqual($mention1.find('.o_mention_name').text(), "Admin", "first partner mention should display the correct partner name"); // correct 2nd mention proposition assert.notOk($mention2.hasClass('active'), "second partner mention should not be active"); assert.strictEqual($mention2.data('id'), 2, "second partner mention should link to the correct partner id"); assert.strictEqual($mention2.find('.o_mention_name').text(), "TestUser", "second partner mention should display the correct partner name"); // correct 3rd mention proposition assert.notOk($mention3.hasClass('active'), "third partner mention should not be active"); assert.strictEqual($mention3.data('id'), 3, "third partner mention should link to the correct partner id"); assert.strictEqual($mention3.find('.o_mention_name').text(), "DemoUser", "third partner mention should display the correct partner name"); // check DOWN event $input.trigger($.Event('keyup', {which: $.ui.keyCode.DOWN})); assert.notOk($mention1.hasClass('active'), "first partner mention should not be active"); assert.ok($mention2.hasClass('active'), "second partner mention should be active"); assert.notOk($mention3.hasClass('active'), "third partner mention should not be active"); // check UP event $input.trigger($.Event('keyup', {which: $.ui.keyCode.UP})); assert.ok($mention1.hasClass('active'), "first partner mention should be active"); assert.notOk($mention2.hasClass('active'), "second partner mention should not be active"); assert.notOk($mention3.hasClass('active'), "third partner mention should not be active"); // check TAB event (full cycle, hence 3 TABs) $input.trigger($.Event('keyup', {which: $.ui.keyCode.TAB})); assert.notOk($mention1.hasClass('active'), "first partner mention should not be active"); assert.ok($mention2.hasClass('active'), "second partner mention should be active"); assert.notOk($mention3.hasClass('active'), "third partner mention should not be active"); $input.trigger($.Event('keyup', {which: $.ui.keyCode.TAB})); assert.notOk($mention1.hasClass('active'), "first partner mention should not be active"); assert.notOk($mention2.hasClass('active'), "second partner mention should not be active"); assert.ok($mention3.hasClass('active'), "third partner mention should be active"); $input.trigger($.Event('keyup', {which: $.ui.keyCode.TAB})); assert.ok($mention1.hasClass('active'), "first partner mention should be active"); assert.notOk($mention2.hasClass('active'), "second partner mention should not be active"); assert.notOk($mention3.hasClass('active'), "third partner mention should not be active"); // equivalent to $mentionPropositions.find('active').click(); $input.trigger($.Event('keyup', {which: $.ui.keyCode.ENTER})); assert.strictEqual(discuss.$('.o_mention_proposition').length, 0, "should not have any partner mention proposition after ENTER"); assert.strictEqual($input.find('a').text() , "@Admin", "should have the correct mention link in the composer input"); // send message $input.trigger($.Event('keydown', {which: $.ui.keyCode.ENTER})); receiveMessageDef .then(concurrency.delay.bind(concurrency, 0)) .then(function () { assert.strictEqual(discuss.$('.o_thread_message_content').length, 1, "should display one message with some content"); assert.strictEqual(discuss.$('.o_thread_message_content a').length, 1, "should contain a link in the message content"); assert.strictEqual(discuss.$('.o_thread_message_content a').text(), "@Admin", "should have correct mention link in the message content"); // Restore throttles and window.getSelection BasicComposer.prototype.MENTION_THROTTLE = mentionThrottle; ChatManager.prototype.CHANNEL_SEEN_THROTTLE = channelThrottle; unpatchWindowGetSelection(); discuss.destroy(); done(); }); }); }); });