text: odoo.define('mail.systray.MessagingMenuTests', function (require) {
  "use strict";
  
  var DocumentThread = require('mail.model.DocumentThread');
  var MessagingMenu = require('mail.systray.MessagingMenu');
  var mailTestUtils = require('mail.testUtils');
  
  var testUtils = require('web.test_utils');
  
  QUnit.module('mail', {}, function () {
  QUnit.module('MessagingMenu', {
      beforeEach: function () {
          // patch _.debounce and _.throttle to be fast and synchronous
          this.underscoreDebounce = _.debounce;
          this.underscoreThrottle = _.throttle;
          _.debounce = _.identity;
          _.throttle = _.identity;
  
          this.data = {
              'mail.channel': {
                  fields: {
                      name: {
                          string: "Name",
                          type: "char",
                          required: true,
                      },
                      channel_type: {
                          string: "Channel Type",
                          type: "selection",
                      },
                      channel_message_ids: {
                          string: "Messages",
                          type: "many2many",
                          relation: 'mail.message'
                      },
                  },
                  records: [{
                      id: 1,
                      name: "general",
                      channel_type: "channel",
                      channel_message_ids: [1],
                  }],
              },
              'mail.message': {
                  fields: {
                      body: {
                          string: "Contents",
                          type: 'html',
                      },
                      author_id: {
                          string: "Author",
                          type: 'many2one',
                          relation: 'res.partner',
                      },
                      channel_ids: {
                          string: "Channels",
                          type: 'many2many',
                          relation: 'mail.channel',
                      },
                      starred: {
                          string: "Starred",
                          type: 'boolean',
                      },
                      needaction: {
                        string: "Need Action",
                        type: 'boolean',
                      },
                      needaction_partner_ids: {
                          string: "Needaction partner IDs",
                          type: 'one2many',
                          relation: 'res.partner',
                      },
                      starred_partner_ids: {
                        string: "partner ids",
                        type: 'integer',
                      }
                  },
                  records: [{
                      id: 1,
                      author_id: ['1', 'Me'],
                      body: '<p>test</p>',
                      channel_ids: [1],
                  }],
              },
              initMessaging: {
                  channel_slots: {
                      channel_channel: [{
                          id: 1,
                          channel_type: "channel",
                          name: "general",
                      }],
                  },
              },
          };
          this.services = mailTestUtils.getMailServices();
      },
      afterEach: function () {
          // unpatch _.debounce and _.throttle
          _.debounce = this.underscoreDebounce;
          _.throttle = this.underscoreThrottle;
      }
  });
  
  QUnit.test('messaging menu widget: menu with no records', async function (assert) {
      assert.expect(1);
  
      var messagingMenu = new MessagingMenu();
      testUtils.mock.addMockEnvironment(messagingMenu, {
              services: this.services,
              mockRPC: function (route, args) {
                  if (args.method === 'message_fetch') {
                      return Promise.resolve([]);
                  }
                  return this._super.apply(this, arguments);
              }
          });
      await messagingMenu.appendTo($('#qunit-fixture'));
      await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
      assert.hasClass(messagingMenu.$('.o_no_activity'),'o_no_activity', "should not have instance of widget");
      messagingMenu.destroy();
  });
  
  QUnit.test('messaging menu widget: messaging not ready', async function (assert) {
      assert.expect(8);
  
      const messagingReadyProm = testUtils.makeTestPromise();
  
      const messagingMenu = new MessagingMenu();
      testUtils.mock.addMockEnvironment(messagingMenu, {
          services: this.services,
          async mockRPC(route, args) {
              if (route === '/mail/init_messaging') {
                  const _super = this._super.bind(this, ...arguments); // limitation of class.js
                  assert.step('/mail/init_messaging:pending');
                  await messagingReadyProm;
                  assert.step('/mail/init_messaging:resolved');
                  return _super();
              }
              if (args.method === 'message_fetch') {
                  return [];
              }
              return this._super(...arguments);
          },
      });
      await messagingMenu.appendTo($('#qunit-fixture'));
      assert.verifySteps(['/mail/init_messaging:pending']);
      assert.ok(
          messagingMenu.el,
          "messaging menu should be rendered");
      assert.strictEqual(
          document.querySelector('.o_mail_systray_item'),
          messagingMenu.el,
          "should display messaging menu even when messaging is not ready");
      assert.hasClass(
          messagingMenu.$('.o_mail_messaging_menu_icon'),
          'fa-spinner',
          "Messaging menu icon should be a spinner when messaging menu is not ready");
  
      messagingReadyProm.resolve();
      await testUtils.nextTick();
      assert.verifySteps(['/mail/init_messaging:resolved']);
      assert.doesNotHaveClass(
          messagingMenu.$('.o_mail_messaging_menu_icon'),
          'fa-spinner',
          "Messaging menu icon should no longer be a spinner when messaging menu is ready");
  
      messagingMenu.destroy();
  });
  
  QUnit.test('messaging menu widget: messaging menu with 1 record', async function (assert) {
      assert.expect(3);
      var messagingMenu = new MessagingMenu();
      testUtils.mock.addMockEnvironment(messagingMenu, {
          services: this.services,
          data: this.data,
      });
      await messagingMenu.appendTo($('#qunit-fixture'));
  
      await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
  
      assert.containsOnce(messagingMenu, '.o_mail_preview',
          "should display a preview");
      assert.strictEqual(messagingMenu.$('.o_preview_name').text().trim(), "general",
          "should display correct name of channel in preview");
  
      // remove any space-character inside text
      var lastMessagePreviewText =
          messagingMenu.$('.o_last_message_preview').text().replace(/\s/g, "");
      assert.strictEqual(lastMessagePreviewText,
          "Me:test",
          "should display correct last message preview in channel preview");
  
      messagingMenu.destroy();
  });
  
  QUnit.test('messaging menu widget: open inbox for needaction not linked to any document', async function (assert) {
      assert.expect(4);
  
      var messagingMenu = new MessagingMenu();
      testUtils.mock.addMockEnvironment(messagingMenu, {
          services: this.services,
          data: this.data,
          session: {
              partner_id: 1,
          },
      });
      await messagingMenu.appendTo($('#qunit-fixture'));
  
      // Simulate received needaction message without associated document,
      // so that we have a message in inbox without a model and a resID
      var message = {
          id: 2,
          author_id: [1, "Me"],
          body: '<p>test</p>',
          channel_ids: [],
          needaction_partner_ids: [1],
      };
      var notifications = [
          [['myDB', 'ir.needaction'], message]
      ];
      messagingMenu.call('bus_service', 'trigger', 'notification', notifications);
  
      // Open messaging menu
      await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
  
      var $firstChannelPreview =
          messagingMenu.$('.o_mail_preview').first();
  
      assert.strictEqual($firstChannelPreview.length, 1,
          "should have at least one channel preview");
      assert.strictEqual($firstChannelPreview.data('preview-id'),
          'mailbox_inbox',
          "should be a preview from channel inbox");
  
      testUtils.mock.intercept(messagingMenu, 'do_action', function (ev) {
          if (ev.data.action === 'mail.action_discuss') {
              assert.step('do_action:' + ev.data.action + ':' + ev.data.options.active_id);
          }
      }, true);
      await testUtils.dom.click($firstChannelPreview);
      assert.verifySteps(
          ['do_action:mail.action_discuss:mailbox_inbox'],
          "should open Discuss with Inbox");
  
      messagingMenu.destroy();
  });
  
  QUnit.test("messaging menu widget: mark as read on thread preview", async function ( assert ) {
      assert.expect(8);
  
      testUtils.mock.patch(DocumentThread, {
              markAsRead: function () {
                  if (
                      this.getDocumentModel() === 'crm.lead' &&
                      this.getDocumentID() === 126
                  ) {
                      assert.step('markedAsRead');
                  }
              },
          });
  
      this.data['mail.message'].records = [{
          id: 10,
          channel_ids: ['mailbox_inbox'],
          res_id: 126,
          needaction: true,
          module_icon: "/crm/static/description/icon.png",
          date: "2018-04-05 06:37:26",
          subject: "Re: Interest in your Graphic Design Project",
          model: "crm.lead",
          body: "<span>Testing Messaging</span>"
      }];
  
      var messagingMenu = new MessagingMenu();
      testUtils.mock.addMockEnvironment(messagingMenu, {
          services: this.services,
          data: this.data,
      });
  
      await messagingMenu.appendTo($('#qunit-fixture'));
      await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
      assert.hasClass(messagingMenu.$el,'o_mail_systray_item',
          'should be the instance of widget');
      assert.hasClass(messagingMenu.$el,'show',
          'MessagingMenu should be open');
  
      var $preview = messagingMenu.$('.o_mail_preview.o_preview_unread');
      assert.strictEqual($preview.length, 1,
          "should have one unread preview");
      assert.strictEqual($preview.data('document-model'), 'crm.lead',
          "should preview be linked to correct document model");
      assert.strictEqual($preview.data('document-id'), 126,
          "should preview be linked to correct document ID");
      assert.containsOnce(messagingMenu, '.o_mail_preview_mark_as_read',
          "should have mark as read icon next to preview");
  
      await testUtils.dom.click(messagingMenu.$(".o_mail_preview_mark_as_read"));
  
      assert.verifySteps(['markedAsRead'],
          "the document thread should be marked as read");
  
      testUtils.mock.unpatch(DocumentThread);
      messagingMenu.destroy();
  });
  
  QUnit.test('needaction messages in channels should appear, in addition to channel preview', async function (assert) {
      // Let's suppose a channel whose before-last message (msg1) is a needaction,
      // but not the last message (msg2). In that case, the systray messaging
      // menu should display the needaction message msg1 in the preview, and the
      // channel preview with the msg2.
      assert.expect(3);
  
      // simulate a needaction (mention) message in channel 'general'
      var partnerID = 44;
      var needactionMessage = {
          author_id: [1, "Demo"],
          body: "<p>@Administrator: ping</p>",
          channel_ids: [1],
          id: 3,
          model: 'mail.channel',
          needaction: true,
          needaction_partner_ids: [partnerID],
          record_name: 'general',
          res_id: 1,
      };
      var lastMessage = {
          author_id: [2, "Other"],
          body: "<p>last message content</p>",
          channel_ids: [1],
          id: 4,
          model: 'mail.channel',
          record_name: 'general',
          res_id: 1,
      };
      this.data['mail.message'].records = [needactionMessage, lastMessage];
  
      var messagingMenu = new MessagingMenu();
      testUtils.mock.addMockEnvironment(messagingMenu, {
          services: this.services,
          data: this.data,
          session: {
              partner_id: partnerID,
          },
      });
      await messagingMenu.appendTo($('#qunit-fixture'));
  
      await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
  
      assert.containsN(messagingMenu, '.o_mail_preview', 2,
          "should display two previews");
      var $preview1 = messagingMenu.$('.o_mail_preview').eq(0);
      var $preview2 = messagingMenu.$('.o_mail_preview').eq(1);
  
      assert.strictEqual($preview1.find('.o_last_message_preview').text().replace(/\s/g, ""),
          "Demo:@Administrator:ping",
          "1st preview (needaction preview) should display needaction message");
      assert.strictEqual($preview2.find('.o_last_message_preview').text().replace(/\s/g, ""),
          "Other:lastmessagecontent",
          "2nd preview (channel preview) should display last message preview");
  
      messagingMenu.destroy();
  });
  
  QUnit.test('preview of message on a document + mark as read', async function (assert) {
      assert.expect(7);
  
      this.data.initMessaging = {
          needaction_inbox_counter: 1,
      };
  
      var partnerID = 44;
      var needactionMessage = {
          author_id: [1, "Demo"],
          body: "<p>*MessageOnDocument*</p>",
          id: 689,
          model: 'some.res.model',
          needaction: true,
          needaction_partner_ids: [partnerID],
          record_name: "Some Record",
          res_id: 1,
      };
      this.data['some.res.model'] = {
          fields: {
              message_ids: {string: 'Messages', type: 'one2many'},
          },
          records: [{
              id: 1,
              message_ids: [689],
          }],
      };
      this.data['mail.message'].records.push(needactionMessage);
  
      var messagingMenu = new MessagingMenu();
      testUtils.mock.addMockEnvironment(messagingMenu, {
          services: this.services,
          data: this.data,
          session: {
              partner_id: partnerID,
          },
      });
      await messagingMenu.appendTo($('#qunit-fixture'));
      assert.strictEqual(messagingMenu.$('.o_notification_counter').text(), '1',
          "should display a counter of 1 on the messaging menu icon");
  
      await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
  
      assert.containsOnce(messagingMenu, '.o_mail_preview',
          "should display one preview");
      assert.strictEqual(messagingMenu.$('.o_mail_preview').data('preview-id'),
          "some.res.model_1",
          "preview should be the document thread preview");
      assert.hasClass(messagingMenu.$('.o_mail_preview:first'),'o_preview_unread',
          "document thread preview should be marked as unread");
      assert.strictEqual(messagingMenu.$('.o_preview_unread .o_last_message_preview').text().replace(/\s/g, ''),
          "Demo:*MessageOnDocument*", "should correctly display the preview");
  
      await testUtils.dom.click(messagingMenu.$('.o_mail_preview_mark_as_read'));
      assert.strictEqual(messagingMenu.$('.o_notification_counter').text(), '0',
          "should display a counter of 0 on the messaging menu icon");
      assert.containsNone(messagingMenu, '.o_mail_preview',
          "should not display any preview");
  
      messagingMenu.destroy();
  });
  
  QUnit.test('update messaging preview on receiving a new message in channel preview', async function (assert) {
      assert.expect(8);
  
      var messagingMenu = new MessagingMenu();
      testUtils.mock.addMockEnvironment(messagingMenu, {
          services: this.services,
          data: this.data,
      });
      await messagingMenu.appendTo($('#qunit-fixture'));
  
      await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
  
      assert.containsOnce(messagingMenu, '.o_mail_preview',
          "should display a single channel preview");
      assert.strictEqual(messagingMenu.$('.o_preview_name').text().trim(), "general",
          "should display channel preview of 'general' channel");
      assert.strictEqual(messagingMenu.$('.o_preview_counter').text().trim(), "",
          "should have unread counter of 0 for 'general' channel (no unread messages)");
      // remove any space-character inside text
      var lastMessagePreviewText =
          messagingMenu.$('.o_last_message_preview').text().replace(/\s/g, "");
      assert.strictEqual(lastMessagePreviewText,
          "Me:test",
          "should display author name and inline body of currently last message in the channel");
  
      // simulate receiving a new message on the channel 'general' (id 1)
      var data = {
          id: 100,
          author_id: [42, "Someone"],
          body: "<p>A new message content</p>",
          channel_ids: [1],
      };
      var notification = [[false, 'mail.channel', 1], data];
      messagingMenu.call('bus_service', 'trigger', 'notification', [notification]);
      await testUtils.nextTick();
      assert.containsOnce(messagingMenu, '.o_mail_preview',
          "should still display a single channel preview");
      assert.strictEqual(messagingMenu.$('.o_preview_name').text().trim(), "general",
          "should still display channel preview of 'general' channel");
      assert.strictEqual(messagingMenu.$('.o_preview_counter').text().trim(), "(1)",
          "should have incremented unread counter of 'general' channel");
      // remove any space-character inside text
      lastMessagePreviewText =
          messagingMenu.$('.o_last_message_preview').text().replace(/\s/g, "");
      assert.strictEqual(lastMessagePreviewText,
          "Someone:Anewmessagecontent",
          "should display author name and inline body of newly received message");
  
      messagingMenu.destroy();
  });
  
  QUnit.test('new message of type "notification" are not considered as unread messages', async function (assert) {
      assert.expect(8);
  
      const messagingMenu = new MessagingMenu();
      testUtils.mock.addMockEnvironment(messagingMenu, {
          data: this.data,
          services: this.services,
      });
      await messagingMenu.appendTo($('#qunit-fixture'));
      // open messaging menu
      await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
      assert.containsOnce(
          messagingMenu,
          '.o_mail_preview',
          "should display a single channel preview");
      assert.strictEqual(
          messagingMenu
              .$('.o_preview_name')
              .text()
              .trim(),
          "general",
          "should display channel preview of 'general' channel");
      assert.strictEqual(
          messagingMenu
              .$('.o_preview_counter')
              .text()
              .trim(),
          "",
          "should have unread counter of 0 for 'general' channel (no unread messages)");
      assert.strictEqual(
          messagingMenu
              .$('.o_last_message_preview')
              .text()
              .replace(/\s/g, ""),
          "Me:test",
          "should display author name and inline body of currently last message in the channel");
      // close messaging menu
      await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
  
      // simulate receiving a new message on the channel 'general' (id 1)
      // of type 'notification'
      const data = {
          id: 100,
          author_id: [42, "Someone"],
          body: "<p>Left #general</p>",
          channel_ids: [1],
          message_type: 'notification',
      };
      const notification = [[false, 'mail.channel', 1], data];
      messagingMenu.call('bus_service', 'trigger', 'notification', [notification]);
      await testUtils.nextTick();
      // open messaging menu
      await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
      assert.containsOnce(
          messagingMenu,
          '.o_mail_preview',
          "should still display a single channel preview");
      assert.strictEqual(
          messagingMenu
              .$('.o_preview_name')
              .text()
              .trim(),
          "general",
          "should still display channel preview of 'general' channel");
      assert.strictEqual(
          messagingMenu
              .$('.o_preview_counter')
              .text()
              .trim(),
          "",
          "should still have unread counter of 0 for 'general' channel (no unread messages)");
      assert.strictEqual(
          messagingMenu
              .$('.o_last_message_preview')
              .text()
              .replace(/\s/g, ""),
          "Someone:Left#general",
          "should display author name and inline body of notification message (last message of channel)");
  
      messagingMenu.destroy();
  });
  
  QUnit.test('preview of inbox message not linked to document + mark as read', async function (assert) {
      assert.expect(17);
  
      this.data.initMessaging = {
          needaction_inbox_counter: 2,
      };
  
      var needactionMessages = [{
          author_id: [1, "Demo"],
          body: "<p>*Message1*</p>",
          id: 689,
          needaction: true,
          needaction_partner_ids: [44],
      }, {
          author_id: [1, "Demo"],
          body: "<p>*Message2*</p>",
          id: 690,
          needaction: true,
          needaction_partner_ids: [44],
      }];
      this.data['mail.message'].records =
          this.data['mail.message'].records.concat(needactionMessages);
  
      var messagingMenu = new MessagingMenu();
      testUtils.mock.addMockEnvironment(messagingMenu, {
          services: this.services,
          data: this.data,
          session: {
              partner_id: 44,
          },
          mockRPC: function (route, args) {
              if (args.method === 'set_message_done') {
                  assert.step('method: set_message_done, messageIDs: ' + JSON.stringify(args.args[0]));
              }
              return this._super.apply(this, arguments);
          },
      });
      await messagingMenu.appendTo($('#qunit-fixture'));
      assert.strictEqual(messagingMenu.$('.o_notification_counter').text(), '2',
          "should display a counter of 2 on the messaging menu icon");
  
      await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
  
      assert.strictEqual(messagingMenu.$('.o_mail_preview').length, 2,
          "should display two previews");
  
      var $preview1 = messagingMenu.$('.o_mail_preview').eq(0);
      var $preview2 = messagingMenu.$('.o_mail_preview').eq(1);
  
      assert.strictEqual($preview1.data('preview-id'),
          "mailbox_inbox",
          "1st preview should be from the mailbox inbox");
      assert.strictEqual($preview2.data('preview-id'),
          "mailbox_inbox",
          "2nd preview should also be from the mailbox inbox");
      assert.ok($preview1.hasClass('o_preview_unread'),
          "1st preview should be marked as unread");
      assert.ok($preview2.hasClass('o_preview_unread'),
          "2nd preview should also be marked as unread");
      assert.strictEqual($preview1.find('.o_last_message_preview').text().replace(/\s/g, ''),
          "Demo:*Message1*", "should correctly display the 1st preview");
      assert.strictEqual($preview2.find('.o_last_message_preview').text().replace(/\s/g, ''),
          "Demo:*Message2*", "should correctly display the 2nd preview");
  
      await testUtils.dom.click($preview1.find('.o_mail_preview_mark_as_read'));
      assert.verifySteps(['method: set_message_done, messageIDs: [689]'],
          "should mark 1st preview as read");
      assert.strictEqual(messagingMenu.$('.o_notification_counter').text(), '1',
          "should display a counter of 1 on the messaging menu icon after marking one preview as read");
      assert.strictEqual(messagingMenu.$('.o_mail_preview').length, 1,
          "should display a single preview remaining");
      assert.strictEqual(messagingMenu.$('.o_mail_preview .o_last_message_preview').text().replace(/\s/g, ''),
          "Demo:*Message2*", "preview 2 should be the remaining one");
  
      $preview2 = messagingMenu.$('.o_mail_preview');
      await testUtils.dom.click($preview2.find('.o_mail_preview_mark_as_read'));
      assert.verifySteps(['method: set_message_done, messageIDs: [690]'],
          "should mark 2nd preview as read");
      assert.strictEqual(messagingMenu.$('.o_notification_counter').text(), '0',
          "should display a counter of 0 on the messaging menu icon after marking both previews as read");
      assert.strictEqual(messagingMenu.$('.o_mail_preview').length, 0,
          "should display no preview remaining");
  
      messagingMenu.destroy();
  });
  
  QUnit.test('grouped preview for needaction messages linked to same document', async function (assert) {
      assert.expect(5);
  
      // simulate two (read) needaction (mention) messages in channel 'general'
      var needactionMessage1 = {
          author_id: [1, "Demo"],
          body: "<p>@Administrator: ping</p>",
          channel_ids: [1],
          id: 3,
          model: 'mail.channel',
          needaction: true,
          needaction_partner_ids: [44],
          record_name: 'general',
          res_id: 1,
      };
      var needactionMessage2 = {
          author_id: [2, "Other"],
          body: "<p>@Administrator: pong</p>",
          channel_ids: [1],
          id: 4,
          model: 'mail.channel',
          needaction: true,
          needaction_partner_ids: [44],
          record_name: 'general',
          res_id: 1,
      };
      this.data['mail.message'].records = [needactionMessage1, needactionMessage2];
      this.data.initMessaging.channel_slots.channel_channel[0].message_unread_counter = 0;
      this.data.initMessaging.needaction_inbox_counter = 2;
  
      var messagingMenu = new MessagingMenu();
      testUtils.mock.addMockEnvironment(messagingMenu, {
          services: this.services,
          data: this.data,
          session: {
              partner_id: 44,
          },
      });
      await messagingMenu.appendTo($('#qunit-fixture'));
  
      await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
      var $previews = messagingMenu.$('.o_mail_preview');
  
      assert.strictEqual($previews.length, 2,
          "should display two previews (one for needaction, one for channel)");
  
      var $needactionPreview = $previews.eq(0);
      var $channelPreview = $previews.eq(1);
  
      assert.strictEqual($needactionPreview.find('.o_preview_counter').text().trim(), "(2)",
          "should show two needaction messages in the needaction preview");
      assert.strictEqual($needactionPreview.find('.o_last_message_preview').text().replace(/\s/g, ""),
          "Other:@Administrator:pong",
          "should display last needaction message on needaction preview");
      assert.strictEqual($channelPreview.find('.o_preview_counter').text().trim(), "",
          "should show no unread messages in the channel preview");
      assert.strictEqual($channelPreview.find('.o_last_message_preview').text().replace(/\s/g, ""),
          "Other:@Administrator:pong",
          "should display last needaction message on channel preview");
  
      messagingMenu.destroy();
  });
  
  QUnit.test("messaging menu widget: channel seen notification", async function (assert) {
      assert.expect(4);
  
      this.data.initMessaging.channel_slots = {
          channel_direct_message: [{
              id: 1,
              channel_type: "chat",
              direct_partner: [{ id: 2, name: 'Someone', im_status: '' }],
              name: 'DM',
              message_unread_counter: 1,
          }],
      };
  
      var messagingMenu = new MessagingMenu();
      testUtils.mock.addMockEnvironment(messagingMenu, {
          services: this.services,
          data: this.data,
          session: { partner_id: 3 },
      });
  
      await messagingMenu.appendTo($('#qunit-fixture'));
      await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
  
      assert.strictEqual(messagingMenu.$('.o_notification_counter').text(), '1',
          "should have correct messaging menu counter (1 unread message in channel)");
      assert.strictEqual(messagingMenu.$('.o_preview_counter').text().replace(/\s/g, ''),
          '(1)',
          "should display 1 unread message on DM channel preview");
  
      // Simulate received channel seen notification
      var message = {
          info: 'channel_seen',
          last_message_id: 1,
          partner_id: 3,
      };
      var notifications = [
          [['myDB', 'mail.channel', 1], message]
      ];
      messagingMenu.call('bus_service', 'trigger', 'notification', notifications);
      await testUtils.nextTick();
      assert.strictEqual(messagingMenu.$('.o_notification_counter').text(), '0',
          "should no longer have a messaging menu counter (no unread message in channel)");
      assert.strictEqual(messagingMenu.$('.o_preview_counter').text().replace(/\s/g, ''), '',
          "should no longer display unread message on DM channel preview");
  
      messagingMenu.destroy();
  });
  
  QUnit.test("messaging menu widget: no traceback when receiving channel_fetched notification with blank thread window", async function (assert) {
      assert.expect(3);
  
      this.data.initMessaging.channel_slots = {
          channel_direct_message: [{
              id: 1,
              channel_type: "chat",
              direct_partner: [{ id: 2, name: 'Someone', im_status: '' }],
              name: 'DM',
              message_unread_counter: 1,
          }],
      };
  
      var messagingMenu = new MessagingMenu();
      testUtils.mock.addMockEnvironment(messagingMenu, {
          services: this.services,
          data: this.data,
          session: { partner_id: 3 },
      });
  
      await messagingMenu.appendTo($('#qunit-fixture'));
      await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
      assert.containsOnce(messagingMenu, '.o_new_message',
          "should have button to open blank thread window");
  
      await testUtils.dom.click(messagingMenu.$('.o_new_message'));
      var $threadWindow = $('.o_thread_window');
      assert.strictEqual($threadWindow.length, 1, "should have an open thread window");
      assert.ok($threadWindow.hasClass('o_thread_less'), "should be a blank thread window");
  
      // Simulate received channel fetched notification
      var message = {
          info: 'channel_fetched',
          last_message_id: 1,
          partner_id: 2,
      };
      var notifications = [
          [['myDB', 'mail.channel', 1], message]
      ];
      messagingMenu.call('bus_service', 'trigger', 'notification', notifications);
      await testUtils.nextTick();
  
      messagingMenu.destroy();
  });
  
  QUnit.test("messaging menu widget: preview with no message should be undated", async function ( assert ) {
      assert.expect(2);
  
      // remove any message on the channel
      this.data['mail.channel'].records[0].channel_message_ids = [];
      this.data['mail.message'].records[0].channel_ids = [];
  
      var messagingMenu = new MessagingMenu();
      testUtils.mock.addMockEnvironment(messagingMenu, {
          services: this.services,
          data: this.data,
      });
  
      await messagingMenu.appendTo($('#qunit-fixture'));
      await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
  
      assert.strictEqual(messagingMenu.$('.o_mail_preview').length, 1,
          "should have two previews in messaging menu");
  
      assert.ok(_.isEmpty(messagingMenu.$('.o_mail_preview .o_last_message_date').text().trim()),
          "should have preview as undated");
  
      messagingMenu.destroy();
  });
  
  QUnit.test("messaging menu widget: sort dated previews before undated previews", async function ( assert ) {
      assert.expect(5);
  
      var dm = {
          id: 2,
          name: "DM",
          channel_type: "chat",
      };
  
      this.data['mail.channel'].records.push(dm);
      this.data.initMessaging.channel_slots.direct_message = [dm];
  
      var messagingMenu = new MessagingMenu();
      testUtils.mock.addMockEnvironment(messagingMenu, {
          services: this.services,
          data: this.data,
      });
  
      await messagingMenu.appendTo($('#qunit-fixture'));
      await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
  
      assert.strictEqual(messagingMenu.$('.o_mail_preview').length, 2,
          "should have two previews in messaging menu");
  
      var $preview1 = messagingMenu.$('.o_mail_preview').eq(0);
      var $preview2 = messagingMenu.$('.o_mail_preview').eq(1);
  
      assert.strictEqual($preview1.data('preview-id'), 1,
          "should have channel preview as the 1st preview (dated preview)");
      assert.notOk(_.isEmpty($preview1.find('.o_last_message_date').text().trim()),
          "should have channel preview as dated");
      assert.strictEqual($preview2.data('preview-id'), 2,
          "should have DM preview as the 1st preview (non-dated preview)");
      assert.ok(_.isEmpty($preview2.find('.o_last_message_date').text().trim()),
          "should have DM preview as undated");
  
      messagingMenu.destroy();
  });
  
  QUnit.test('global counter with channel previews', async function (assert) {
      assert.expect(5);
  
      var channels = [{
          id: 1,
          name: "channel1",
          channel_type: "channel",
          channel_message_ids: [1, 2],
          message_unread_counter: 2,
      }, {
          id: 2,
          name: "channel2",
          channel_type: "channel",
          channel_message_ids: [3],
          message_unread_counter: 1,
      }, {
          id: 3,
          name: "channel3",
          channel_type: "channel",
          channel_message_ids: [4],
          message_unread_counter: 0,
      }];
  
      var messages = [{
          author_id: [1, "Demo"],
          body: "<p>message 1</p>",
          channel_ids: [1],
          id: 1,
          model: 'mail.channel',
          record_name: 'channel1',
          res_id: 1,
      }, {
          author_id: [1, "Demo"],
          body: "<p>message 2</p>",
          channel_ids: [1],
          id: 2,
          model: 'mail.channel',
          record_name: 'channel1',
          res_id: 1,
      }, {
          author_id: [1, "Demo"],
          body: "<p>message 3</p>",
          channel_ids: [2],
          id: 3,
          model: 'mail.channel',
          record_name: 'channel2',
          res_id: 2,
      }, {
          author_id: [1, "Demo"],
          body: "<p>message 4</p>",
          channel_ids: [3],
          id: 4,
          model: 'mail.channel',
          record_name: 'channel3',
          res_id: 3,
      }];
  
      this.data['mail.channel'].records = channels;
      this.data.initMessaging.channel_slots.channel_channel = channels;
      this.data['mail.message'].records = messages;
  
      var messagingMenu = new MessagingMenu();
      testUtils.mock.addMockEnvironment(messagingMenu, {
          services: this.services,
          data: this.data,
          session: {
              partner_id: 3,
          },
      });
      await messagingMenu.appendTo($('#qunit-fixture'));
      assert.strictEqual(messagingMenu.$('.o_notification_counter').text(), '2',
          "should display a counter of 2 on the messaging menu icon");
      await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
  
      assert.strictEqual(messagingMenu.$('.o_mail_preview').length, 3,
          "should display 3 previews");
      var $previewChannel1 = messagingMenu.$('.o_mail_preview[data-preview-id=1]');
      var $previewChannel2 = messagingMenu.$('.o_mail_preview[data-preview-id=2]');
      var $previewChannel3 = messagingMenu.$('.o_mail_preview[data-preview-id=3]');
  
      assert.strictEqual($previewChannel1.data('unread-counter'), 2,
          "preview of channel 1 should have unread counter of 2");
      assert.strictEqual($previewChannel2.data('unread-counter'), 1,
          "preview of channel 2 should have unread counter of 1");
      assert.strictEqual($previewChannel3.data('unread-counter'), 0,
          "preview of channel 3 should have unread counter of 0");
  
      messagingMenu.destroy();
  });
  
  QUnit.test('messaging menu widget: do not open chat window twice on preview clicked', async function (assert) {
      // This test assumes that a condition for opening chat window is to
      // successfully fetch messages beforehand.
      assert.expect(4);
  
      var self = this;
      // Used to pause `message_fetch` after opening the messaging menu.
      // This is necessary `message_fetch` on mailbox_inbox is required to
      // display the previews.
      var lockMessageFetch = false;
      var messageFetchDef = testUtils.makeTestPromise();
  
      var messagingMenu = new MessagingMenu();
      testUtils.addMockEnvironment(messagingMenu, {
          services: this.services,
          data: this.data,
          session: { partner_id: 1 },
          mockRPC: function (route, args) {
              if (args.method === 'message_fetch' && lockMessageFetch) {
                  var _super = this._super.bind(this);
                  return messageFetchDef.then(function () {
                      assert.step('message_fetch');
                      return _super(route, args);
                  });
              }
              if (args.method === 'channel_minimize') {
                  assert.step('channel_minimize');
                  // called to detach thread in chat window
                  // simulate longpolling response with new chat window state
                  var channelInfo = _.extend({}, self.data['mail.channel'].records[0], {
                      is_minimized: true,
                      state: 'open',
                  });
                  var notifications = [ [['myDB', 'res.partner'], channelInfo] ];
                  messagingMenu.call('bus_service', 'trigger', 'notification', notifications);
              }
              return this._super.apply(this, arguments);
          },
      });
      await messagingMenu.appendTo($('#qunit-fixture'));
  
      // Opening chat window from messaging menu (pending from `messageFetchDef`)
      await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
      lockMessageFetch = true;
      await testUtils.dom.click(messagingMenu.$('.o_mail_preview'));
      messageFetchDef.resolve();
  
      await testUtils.nextTick();
      assert.strictEqual($('.o_thread_window').length, 1,
          "should only display a single chat window");
      assert.verifySteps([
          'message_fetch',
          'channel_minimize',
      ], "should have fetched messages only once");
  
      messagingMenu.destroy();
  });
  
  QUnit.test('messaging menu widget: expand on thread preview', async function (assert) {
      assert.expect(4);
  
      this.data['mail.message'].records.push({
          author_id: [1, "Demo"],
          body: "<p>Office Design Project</p>",
          res_id: 123,
          model: 'some.res.model',
          needaction: true,
          needaction_partner_ids: [44],
      });
  
      var messagingMenu = new MessagingMenu();
      testUtils.mock.addMockEnvironment(messagingMenu, {
          services: this.services,
          data: this.data,
          session: { partner_id: 44 },
          intercepts: {
              do_action: function (ev) {
                  const { res_model, res_id } = ev.data.action;
                  if (res_model === 'some.res.model' && res_id === 123) {
                      assert.step('open_document');
                  }
              },
          },
      });
      await messagingMenu.appendTo($('#qunit-fixture'));
  
      await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
      var $preview = messagingMenu.$('.o_mail_preview');
      assert.containsOnce(messagingMenu, '.o_thread_window_expand', "should display one preview");
      assert.strictEqual($preview.data('document-model'), "some.res.model", "preview should be from some.res.model");
  
      await testUtils.dom.click($preview.find('.o_thread_window_expand'));
      assert.verifySteps(['open_document']);
  
      messagingMenu.destroy();
  });
  
  QUnit.test('messaging menu widget: click twice preview on slow message_fetch should open chat window once', async function (assert) {
      // This test assumes that a condition for opening chat window is to
      // successfully fetch messages beforehand.
      assert.expect(1);
  
      const self = this;
      // Used to pause `message_fetch` after opening the messaging menu.
      // This is necessary `message_fetch` on mailbox_inbox is required to
      // display the previews.
      let lockMessageFetch = false;
      const messageFetchProm = testUtils.makeTestPromise();
  
      const messagingMenu = new MessagingMenu();
      testUtils.addMockEnvironment(messagingMenu, {
          services: this.services,
          data: this.data,
          session: { partner_id: 1 },
          async mockRPC(route, args) {
              if (args.method === 'message_fetch' && lockMessageFetch) {
                  const _super = this._super.bind(this);
                  await messageFetchProm;
                  return _super(route, args);
              }
              if (args.method === 'channel_minimize') {
                  // called to detach thread in chat window
                  // simulate longpolling response with new chat window state
                  const channelInfo = {
                      ...self.data['mail.channel'].records[0],
                      is_minimized: true,
                      state: 'open',
                  };
                  const notifications = [ [['myDB', 'res.partner'], channelInfo] ];
                  messagingMenu.call('bus_service', 'trigger', 'notification', notifications);
              }
              return this._super(...arguments);
          },
      });
      await messagingMenu.appendTo($('#qunit-fixture'));
  
      // Opening chat window 1st time from messaging menu (pending from `messageFetchDef`)
      await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
      lockMessageFetch = true;
      await testUtils.dom.click(messagingMenu.$('.o_mail_preview'));
      // Click again on preview to open chat window
      await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
      await testUtils.dom.click(messagingMenu.$('.o_mail_preview'));
      messageFetchProm.resolve();
      await testUtils.nextTick();
  
      assert.containsOnce(
          $,
          '.o_thread_window',
          "should only display a single chat window");
  
      messagingMenu.destroy();
  });
  
  QUnit.test('no code injection in message body preview', async function (assert) {
      assert.expect(4);
  
      this.data['mail.message'].records[0].body =
          `<p><script>throw new Error('CodeInjectionError');</script></p>`;
  
      const messagingMenu = new MessagingMenu();
      testUtils.mock.addMockEnvironment(messagingMenu, {
          services: this.services,
          data: this.data,
      });
      await messagingMenu.appendTo($('#qunit-fixture'));
      await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
      assert.containsOnce(
          messagingMenu,
          '.o_mail_preview',
          "should display a preview"
      );
      assert.strictEqual(
          messagingMenu.$('.o_preview_name').text().trim(),
          "general",
          "should display correct name of channel in preview"
      );
      assert.strictEqual(
          messagingMenu.$('.o_last_message_preview').text().replace(/\s/g, ""),
          "Me:thrownewError('CodeInjectionError');",
          "should display correct uninjected last message inline content"
      );
      assert.containsNone(
          messagingMenu.$('.o_last_message_preview'),
          'script',
          "last message inline content should not have any code injection"
      );
  
      messagingMenu.destroy();
  });
  
  });
  });
  