diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/mail/static/src/components/chatter | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/mail/static/src/components/chatter')
5 files changed, 1137 insertions, 0 deletions
diff --git a/addons/mail/static/src/components/chatter/chatter.js b/addons/mail/static/src/components/chatter/chatter.js new file mode 100644 index 00000000..3f6ca7dc --- /dev/null +++ b/addons/mail/static/src/components/chatter/chatter.js @@ -0,0 +1,150 @@ +odoo.define('mail/static/src/components/chatter/chatter.js', function (require) { +'use strict'; + +const components = { + ActivityBox: require('mail/static/src/components/activity_box/activity_box.js'), + AttachmentBox: require('mail/static/src/components/attachment_box/attachment_box.js'), + ChatterTopbar: require('mail/static/src/components/chatter_topbar/chatter_topbar.js'), + Composer: require('mail/static/src/components/composer/composer.js'), + ThreadView: require('mail/static/src/components/thread_view/thread_view.js'), +}; +const useShouldUpdateBasedOnProps = require('mail/static/src/component_hooks/use_should_update_based_on_props/use_should_update_based_on_props.js'); +const useStore = require('mail/static/src/component_hooks/use_store/use_store.js'); +const useUpdate = require('mail/static/src/component_hooks/use_update/use_update.js'); + +const { Component } = owl; +const { useRef } = owl.hooks; + +class Chatter extends Component { + + /** + * @override + */ + constructor(...args) { + super(...args); + useShouldUpdateBasedOnProps(); + useStore(props => { + const chatter = this.env.models['mail.chatter'].get(props.chatterLocalId); + const thread = chatter ? chatter.thread : undefined; + let attachments = []; + if (thread) { + attachments = thread.allAttachments; + } + return { + attachments: attachments.map(attachment => attachment.__state), + chatter: chatter ? chatter.__state : undefined, + composer: thread && thread.composer, + thread, + threadActivitiesLength: thread && thread.activities.length, + }; + }, { + compareDepth: { + attachments: 1, + }, + }); + useUpdate({ func: () => this._update() }); + /** + * Reference of the composer. Useful to focus it. + */ + this._composerRef = useRef('composer'); + /** + * Reference of the scroll Panel (Real scroll element). Useful to pass the Scroll element to + * child component to handle proper scrollable element. + */ + this._scrollPanelRef = useRef('scrollPanel'); + /** + * Reference of the message list. Useful to trigger the scroll event on it. + */ + this._threadRef = useRef('thread'); + this.getScrollableElement = this.getScrollableElement.bind(this); + } + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * @returns {mail.chatter} + */ + get chatter() { + return this.env.models['mail.chatter'].get(this.props.chatterLocalId); + } + + /** + * @returns {Element|undefined} Scrollable Element + */ + getScrollableElement() { + if (!this._scrollPanelRef.el) { + return; + } + return this._scrollPanelRef.el; + } + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @private + */ + _notifyRendered() { + this.trigger('o-chatter-rendered', { + attachments: this.chatter.thread.allAttachments, + thread: this.chatter.thread.localId, + }); + } + + /** + * @private + */ + _update() { + if (!this.chatter) { + return; + } + if (this.chatter.thread) { + this._notifyRendered(); + } + if (this.chatter.isDoFocus) { + this.chatter.update({ isDoFocus: false }); + const composer = this._composerRef.comp; + if (composer) { + composer.focus(); + } + } + } + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * @private + */ + _onComposerMessagePosted() { + this.chatter.update({ isComposerVisible: false }); + } + + /** + * @private + * @param {MouseEvent} ev + */ + _onScrollPanelScroll(ev) { + if (!this._threadRef.comp) { + return; + } + this._threadRef.comp.onScroll(ev); + } + +} + +Object.assign(Chatter, { + components, + props: { + chatterLocalId: String, + }, + template: 'mail.Chatter', +}); + +return Chatter; + +}); diff --git a/addons/mail/static/src/components/chatter/chatter.scss b/addons/mail/static/src/components/chatter/chatter.scss new file mode 100644 index 00000000..d722e03b --- /dev/null +++ b/addons/mail/static/src/components/chatter/chatter.scss @@ -0,0 +1,42 @@ +// ------------------------------------------------------------------ +// Layout +// ------------------------------------------------------------------ + +.o_Chatter { + position: relative; + display: flex; + flex: 1 1 auto; + flex-direction: column; + width: map-get($sizes, 100); +} + +.o_Chatter_composer { + border-bottom: $border-width solid; + + &.o-bordered { + border-left: $border-width solid; + border-right: $border-width solid; + } +} + +.o_Chatter_scrollPanel { + overflow-y: auto; +} + +// ------------------------------------------------------------------ +// Style +// ------------------------------------------------------------------ + +.o_Chatter { + background-color: white; + border-color: $border-color; +} + +.o_Chatter_composer { + border-bottom-color: $border-color; + + &.o-bordered { + border-left-color: $border-color; + border-right-color: $border-color; + } +} diff --git a/addons/mail/static/src/components/chatter/chatter.xml b/addons/mail/static/src/components/chatter/chatter.xml new file mode 100644 index 00000000..d9cf20b4 --- /dev/null +++ b/addons/mail/static/src/components/chatter/chatter.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<templates xml:space="preserve"> + + <t t-name="mail.Chatter" owl="1"> + <div class="o_Chatter"> + <t t-if="chatter"> + <div class="o_Chatter_fixedPanel"> + <ChatterTopbar + class="o_Chatter_topbar" + chatterLocalId="chatter.localId" + /> + <t t-if="chatter.threadView and chatter.isComposerVisible"> + <Composer + class="o_Chatter_composer" + t-att-class="{ 'o-bordered': chatter.hasExternalBorder }" + composerLocalId="chatter.thread.composer.localId" + hasFollowers="true" + hasMentionSuggestionsBelowPosition="true" + isCompact="false" + isExpandable="true" + textInputSendShortcuts="['ctrl-enter', 'meta-enter']" + t-on-o-message-posted="_onComposerMessagePosted" + t-ref="composer" + /> + </t> + </div> + <div class="o_Chatter_scrollPanel" t-on-scroll="_onScrollPanelScroll" t-ref="scrollPanel"> + <t t-if="chatter.isAttachmentBoxVisible"> + <AttachmentBox + class="o_Chatter_attachmentBox" + threadLocalId="chatter.thread.localId" + /> + </t> + <t t-if="chatter.thread and chatter.hasActivities and chatter.thread.activities.length > 0"> + <ActivityBox + class="o_Chatter_activityBox" + chatterLocalId="chatter.localId" + /> + </t> + <t t-if="chatter.threadView"> + <ThreadView + class="o_Chatter_thread" + getScrollableElement="getScrollableElement" + hasComposer="false" + hasScrollAdjust="chatter.hasMessageListScrollAdjust" + order="'desc'" + threadViewLocalId="chatter.threadView.localId" + t-ref="thread" + /> + </t> + </div> + </t> + </div> + </t> + +</templates> diff --git a/addons/mail/static/src/components/chatter/chatter_suggested_recipient_tests.js b/addons/mail/static/src/components/chatter/chatter_suggested_recipient_tests.js new file mode 100644 index 00000000..9cb57d83 --- /dev/null +++ b/addons/mail/static/src/components/chatter/chatter_suggested_recipient_tests.js @@ -0,0 +1,420 @@ +odoo.define('mail/static/src/components/chatter/chatter_suggested_recipient_tests', function (require) { +'use strict'; + +const components = { + Chatter: require('mail/static/src/components/chatter/chatter.js'), + Composer: require('mail/static/src/components/composer/composer.js'), +}; +const { + afterEach, + afterNextRender, + beforeEach, + createRootComponent, + start, +} = require('mail/static/src/utils/test_utils.js'); + +QUnit.module('mail', {}, function () { +QUnit.module('components', {}, function () { +QUnit.module('chatter', {}, function () { +QUnit.module('chatter_suggested_recipients_tests.js', { + beforeEach() { + beforeEach(this); + + this.createChatterComponent = async ({ chatter }, otherProps) => { + const props = Object.assign({ chatterLocalId: chatter.localId }, otherProps); + await createRootComponent(this, components.Chatter, { + props, + target: this.widget.el, + }); + }; + + this.start = async params => { + const { env, widget } = await start(Object.assign({}, params, { + data: this.data, + })); + this.env = env; + this.widget = widget; + }; + }, + afterEach() { + afterEach(this); + }, +}); + +QUnit.test("suggest recipient on 'Send message' composer", async function (assert) { + assert.expect(1); + + this.data['res.partner'].records.push({ + display_name: "John Jane", + email: "john@jane.be", + id: 100, + }); + this.data['res.fake'].records.push({ + id: 10, + email_cc: "john@test.be", + partner_ids: [100], + }); + await this.start (); + const chatter = this.env.models['mail.chatter'].create({ + threadId: 10, + threadModel: 'res.fake', + }); + await this.createChatterComponent({ chatter }); + await afterNextRender(() => + document.querySelector(`.o_ChatterTopbar_buttonSendMessage`).click() + ); + assert.containsOnce( + document.body, + '.o_ComposerSuggestedRecipientList', + "Should display a list of suggested recipients after opening the composer from 'Send message' button" + ); +}); + +QUnit.test("with 3 or less suggested recipients: no 'show more' button", async function (assert) { + assert.expect(1); + + this.data['res.partner'].records.push({ + display_name: "John Jane", + email: "john@jane.be", + id: 100, + }); + this.data['res.fake'].records.push({ + id: 10, + email_cc: "john@test.be", + partner_ids: [100], + }); + await this.start (); + const chatter = this.env.models['mail.chatter'].create({ + threadId: 10, + threadModel: 'res.fake', + }); + await this.createChatterComponent({ chatter }); + await afterNextRender(() => + document.querySelector(`.o_ChatterTopbar_buttonSendMessage`).click() + ); + assert.containsNone( + document.body, + '.o_ComposerSuggestedRecipientList_showMore', + "should not display 'show more' button with 3 or less suggested recipients" + ); +}); + +QUnit.test("display reason for suggested recipient on mouse over", async function (assert) { + assert.expect(1); + + this.data['res.partner'].records.push({ + display_name: "John Jane", + email: "john@jane.be", + id: 100, + }); + this.data['res.fake'].records.push({ + id: 10, + partner_ids: [100], + }); + await this.start(); + const chatter = this.env.models['mail.chatter'].create({ + threadId: 10, + threadModel: 'res.fake', + }); + await this.createChatterComponent({ chatter }); + await afterNextRender(() => + document.querySelector(`.o_ChatterTopbar_buttonSendMessage`).click() + ); + const partnerTitle = document.querySelector('.o_ComposerSuggestedRecipient[data-partner-id="100"]').getAttribute('title'); + assert.strictEqual( + partnerTitle, + "Add as recipient and follower (reason: Email partner)", + "must display reason for suggested recipient on mouse over", + ); +}); + +QUnit.test("suggested recipient without partner are unchecked by default", async function (assert) { + assert.expect(1); + + this.data['res.fake'].records.push({ + id: 10, + email_cc: "john@test.be", + }); + await this.start(); + const chatter = this.env.models['mail.chatter'].create({ + threadId: 10, + threadModel: 'res.fake', + }); + await this.createChatterComponent({ chatter }); + await afterNextRender(() => + document.querySelector(`.o_ChatterTopbar_buttonSendMessage`).click() + ); + const checkboxUnchecked = document.querySelector('.o_ComposerSuggestedRecipient:not([data-partner-id]) input[type=checkbox]'); + assert.notOk( + checkboxUnchecked.checked, + "suggested recipient without partner must be unchecked by default", + ); +}); + +QUnit.test("suggested recipient with partner are checked by default", async function (assert) { + assert.expect(1); + + this.data['res.partner'].records.push({ + display_name: "John Jane", + email: "john@jane.be", + id: 100, + }); + this.data['res.fake'].records.push({ + id: 10, + partner_ids: [100], + }); + await this.start(); + const chatter = this.env.models['mail.chatter'].create({ + threadId: 10, + threadModel: 'res.fake', + }); + await this.createChatterComponent({ chatter }); + await afterNextRender(() => + document.querySelector(`.o_ChatterTopbar_buttonSendMessage`).click() + ); + const checkboxChecked = document.querySelector('.o_ComposerSuggestedRecipient[data-partner-id="100"] input[type=checkbox]'); + assert.ok( + checkboxChecked.checked, + "suggested recipient with partner must be checked by default", + ); +}); + +QUnit.test("more than 3 suggested recipients: display only 3 and 'show more' button", async function (assert) { + assert.expect(1); + + this.data['res.partner'].records.push({ + display_name: "John Jane", + email: "john@jane.be", + id: 100, + }); + this.data['res.partner'].records.push({ + display_name: "Jack Jone", + email: "jack@jone.be", + id: 1000, + }); + this.data['res.partner'].records.push({ + display_name: "jolly Roger", + email: "Roger@skullflag.com", + id: 1001, + }); + this.data['res.partner'].records.push({ + display_name: "jack sparrow", + email: "jsparrow@blackpearl.bb", + id: 1002, + }); + this.data['res.fake'].records.push({ + id: 10, + partner_ids: [100, 1000, 1001, 1002], + }); + await this.start (); + const chatter = this.env.models['mail.chatter'].create({ + threadId: 10, + threadModel: 'res.fake', + }); + await this.createChatterComponent({ chatter }); + + await afterNextRender(() => + document.querySelector(`.o_ChatterTopbar_buttonSendMessage`).click() + ); + assert.containsOnce( + document.body, + '.o_ComposerSuggestedRecipientList_showMore', + "more than 3 suggested recipients display 'show more' button" + ); +}); + +QUnit.test("more than 3 suggested recipients: show all of them on click 'show more' button", async function (assert) { + assert.expect(1); + + this.data['res.partner'].records.push({ + display_name: "John Jane", + email: "john@jane.be", + id: 100, + }); + this.data['res.partner'].records.push({ + display_name: "Jack Jone", + email: "jack@jone.be", + id: 1000, + }); + this.data['res.partner'].records.push({ + display_name: "jolly Roger", + email: "Roger@skullflag.com", + id: 1001, + }); + this.data['res.partner'].records.push({ + display_name: "jack sparrow", + email: "jsparrow@blackpearl.bb", + id: 1002, + }); + this.data['res.fake'].records.push({ + id: 10, + partner_ids: [100, 1000, 1001, 1002], + }); + await this.start (); + const chatter = this.env.models['mail.chatter'].create({ + threadId: 10, + threadModel: 'res.fake', + }); + await this.createChatterComponent({ chatter }); + + await afterNextRender(() => + document.querySelector(`.o_ChatterTopbar_buttonSendMessage`).click() + ); + await afterNextRender(() => + document.querySelector(`.o_ComposerSuggestedRecipientList_showMore`).click() + ); + assert.containsN( + document.body, + '.o_ComposerSuggestedRecipient', + 4, + "more than 3 suggested recipients: show all of them on click 'show more' button" + ); +}); + +QUnit.test("more than 3 suggested recipients -> click 'show more' -> 'show less' button", async function (assert) { + assert.expect(1); + + this.data['res.partner'].records.push({ + display_name: "John Jane", + email: "john@jane.be", + id: 100, + }); + this.data['res.partner'].records.push({ + display_name: "Jack Jone", + email: "jack@jone.be", + id: 1000, + }); + this.data['res.partner'].records.push({ + display_name: "jolly Roger", + email: "Roger@skullflag.com", + id: 1001, + }); + this.data['res.partner'].records.push({ + display_name: "jack sparrow", + email: "jsparrow@blackpearl.bb", + id: 1002, + }); + this.data['res.fake'].records.push({ + id: 10, + partner_ids: [100, 1000, 1001, 1002], + }); + await this.start (); + const chatter = this.env.models['mail.chatter'].create({ + threadId: 10, + threadModel: 'res.fake', + }); + await this.createChatterComponent({ chatter }); + + await afterNextRender(() => + document.querySelector(`.o_ChatterTopbar_buttonSendMessage`).click() + ); + await afterNextRender(() => + document.querySelector(`.o_ComposerSuggestedRecipientList_showMore`).click() + ); + assert.containsOnce( + document.body, + '.o_ComposerSuggestedRecipientList_showLess', + "more than 3 suggested recipients -> click 'show more' -> 'show less' button" + ); +}); + +QUnit.test("suggested recipients list display 3 suggested recipient and 'show more' button when 'show less' button is clicked", async function (assert) { + assert.expect(2); + + this.data['res.partner'].records.push({ + display_name: "John Jane", + email: "john@jane.be", + id: 100, + }); + this.data['res.partner'].records.push({ + display_name: "Jack Jone", + email: "jack@jone.be", + id: 1000, + }); + this.data['res.partner'].records.push({ + display_name: "jolly Roger", + email: "Roger@skullflag.com", + id: 1001, + }); + this.data['res.partner'].records.push({ + display_name: "jack sparrow", + email: "jsparrow@blackpearl.bb", + id: 1002, + }); + this.data['res.fake'].records.push({ + id: 10, + partner_ids: [100, 1000, 1001, 1002], + }); + await this.start (); + const chatter = this.env.models['mail.chatter'].create({ + threadId: 10, + threadModel: 'res.fake', + }); + await this.createChatterComponent({ chatter }); + + await afterNextRender(() => + document.querySelector(`.o_ChatterTopbar_buttonSendMessage`).click() + ); + await afterNextRender(() => + document.querySelector(`.o_ComposerSuggestedRecipientList_showMore`).click() + ); + await afterNextRender(() => + document.querySelector(`.o_ComposerSuggestedRecipientList_showLess`).click() + ); + assert.containsN( + document.body, + '.o_ComposerSuggestedRecipient', + 3, + "suggested recipient list should display 3 suggested recipients after clicking on 'show less'." + ); + assert.containsOnce( + document.body, + '.o_ComposerSuggestedRecipientList_showMore', + "suggested recipient list should containt a 'show More' button after clicking on 'show less'." + ); +}); + +QUnit.test("suggested recipients should not be notified when posting an internal note", async function (assert) { + assert.expect(1); + + this.data['res.partner'].records.push({ + display_name: "John Jane", + email: "john@jane.be", + id: 100, + }); + this.data['res.fake'].records.push({ + id: 10, + partner_ids: [100], + }); + await this.start({ + async mockRPC(route, args) { + if (args.model === 'res.fake' && args.method === 'message_post') { + assert.strictEqual( + args.kwargs.partner_ids.length, + 0, + "message_post should not contain suggested recipients when posting an internal note" + ); + } + return this._super(...arguments); + }, + }); + const chatter = this.env.models['mail.chatter'].create({ + threadId: 10, + threadModel: 'res.fake', + }); + await this.createChatterComponent({ chatter }); + await afterNextRender(() => + document.querySelector(`.o_ChatterTopbar_buttonLogNote`).click() + ); + document.querySelector('.o_ComposerTextInput_textarea').focus(); + await afterNextRender(() => document.execCommand('insertText', false, "Dummy Message")); + await afterNextRender(() => { + document.querySelector('.o_Composer_buttonSend').click(); + }); +}); + +}); +}); +}); + +}); diff --git a/addons/mail/static/src/components/chatter/chatter_tests.js b/addons/mail/static/src/components/chatter/chatter_tests.js new file mode 100644 index 00000000..15163e85 --- /dev/null +++ b/addons/mail/static/src/components/chatter/chatter_tests.js @@ -0,0 +1,469 @@ +odoo.define('mail/static/src/components/chatter/chatter_tests', function (require) { +'use strict'; + +const components = { + Chatter: require('mail/static/src/components/chatter/chatter.js'), + Composer: require('mail/static/src/components/composer/composer.js'), +}; +const { + afterEach, + afterNextRender, + beforeEach, + createRootComponent, + nextAnimationFrame, + start, +} = require('mail/static/src/utils/test_utils.js'); + +QUnit.module('mail', {}, function () { +QUnit.module('components', {}, function () { +QUnit.module('chatter', {}, function () { +QUnit.module('chatter_tests.js', { + beforeEach() { + beforeEach(this); + + this.createChatterComponent = async ({ chatter }, otherProps) => { + const props = Object.assign({ chatterLocalId: chatter.localId }, otherProps); + await createRootComponent(this, components.Chatter, { + props, + target: this.widget.el, + }); + }; + + this.createComposerComponent = async (composer, otherProps) => { + const props = Object.assign({ composerLocalId: composer.localId }, otherProps); + await createRootComponent(this, components.Composer, { + props, + target: this.widget.el, + }); + }; + + this.start = async params => { + const { env, widget } = await start(Object.assign({}, params, { + data: this.data, + })); + this.env = env; + this.widget = widget; + }; + }, + afterEach() { + afterEach(this); + }, +}); + +QUnit.test('base rendering when chatter has no attachment', async function (assert) { + assert.expect(6); + + this.data['res.partner'].records.push({ id: 100 }); + for (let i = 0; i < 60; i++) { + this.data['mail.message'].records.push({ + body: "not empty", + model: 'res.partner', + res_id: 100, + }); + } + await this.start(); + const chatter = this.env.models['mail.chatter'].create({ + threadId: 100, + threadModel: 'res.partner', + }); + await this.createChatterComponent({ chatter }); + assert.strictEqual( + document.querySelectorAll(`.o_Chatter`).length, + 1, + "should have a chatter" + ); + assert.strictEqual( + document.querySelectorAll(`.o_ChatterTopbar`).length, + 1, + "should have a chatter topbar" + ); + assert.strictEqual( + document.querySelectorAll(`.o_Chatter_attachmentBox`).length, + 0, + "should not have an attachment box in the chatter" + ); + assert.strictEqual( + document.querySelectorAll(`.o_Chatter_thread`).length, + 1, + "should have a thread in the chatter" + ); + assert.strictEqual( + document.querySelector(`.o_Chatter_thread`).dataset.threadLocalId, + this.env.models['mail.thread'].findFromIdentifyingData({ + id: 100, + model: 'res.partner', + }).localId, + "thread should have the right thread local id" + ); + assert.strictEqual( + document.querySelectorAll(`.o_Message`).length, + 30, + "the first 30 messages of thread should be loaded" + ); +}); + +QUnit.test('base rendering when chatter has no record', async function (assert) { + assert.expect(8); + + await this.start(); + const chatter = this.env.models['mail.chatter'].create({ + threadModel: 'res.partner', + }); + await this.createChatterComponent({ chatter }); + assert.strictEqual( + document.querySelectorAll(`.o_Chatter`).length, + 1, + "should have a chatter" + ); + assert.strictEqual( + document.querySelectorAll(`.o_ChatterTopbar`).length, + 1, + "should have a chatter topbar" + ); + assert.strictEqual( + document.querySelectorAll(`.o_Chatter_attachmentBox`).length, + 0, + "should not have an attachment box in the chatter" + ); + assert.strictEqual( + document.querySelectorAll(`.o_Chatter_thread`).length, + 1, + "should have a thread in the chatter" + ); + assert.ok( + chatter.thread.isTemporary, + "thread should have a temporary thread linked to chatter" + ); + assert.strictEqual( + document.querySelectorAll(`.o_Message`).length, + 1, + "should have a message" + ); + assert.strictEqual( + document.querySelector(`.o_Message_content`).textContent, + "Creating a new record...", + "should have the 'Creating a new record ...' message" + ); + assert.containsNone( + document.body, + '.o_MessageList_loadMore', + "should not have the 'load more' button" + ); +}); + +QUnit.test('base rendering when chatter has attachments', async function (assert) { + assert.expect(3); + + this.data['res.partner'].records.push({ id: 100 }); + this.data['ir.attachment'].records.push( + { + mimetype: 'text/plain', + name: 'Blah.txt', + res_id: 100, + res_model: 'res.partner', + }, + { + mimetype: 'text/plain', + name: 'Blu.txt', + res_id: 100, + res_model: 'res.partner', + } + ); + await this.start(); + const chatter = this.env.models['mail.chatter'].create({ + threadId: 100, + threadModel: 'res.partner', + }); + await this.createChatterComponent({ chatter }); + assert.strictEqual( + document.querySelectorAll(`.o_Chatter`).length, + 1, + "should have a chatter" + ); + assert.strictEqual( + document.querySelectorAll(`.o_ChatterTopbar`).length, + 1, + "should have a chatter topbar" + ); + assert.strictEqual( + document.querySelectorAll(`.o_Chatter_attachmentBox`).length, + 0, + "should not have an attachment box in the chatter" + ); +}); + +QUnit.test('show attachment box', async function (assert) { + assert.expect(6); + + this.data['res.partner'].records.push({ id: 100 }); + this.data['ir.attachment'].records.push( + { + mimetype: 'text/plain', + name: 'Blah.txt', + res_id: 100, + res_model: 'res.partner', + }, + { + mimetype: 'text/plain', + name: 'Blu.txt', + res_id: 100, + res_model: 'res.partner', + } + ); + await this.start(); + const chatter = this.env.models['mail.chatter'].create({ + threadId: 100, + threadModel: 'res.partner', + }); + await this.createChatterComponent({ chatter }); + assert.strictEqual( + document.querySelectorAll(`.o_Chatter`).length, + 1, + "should have a chatter" + ); + assert.strictEqual( + document.querySelectorAll(`.o_ChatterTopbar`).length, + 1, + "should have a chatter topbar" + ); + assert.strictEqual( + document.querySelectorAll(`.o_ChatterTopbar_buttonAttachments`).length, + 1, + "should have an attachments button in chatter topbar" + ); + assert.strictEqual( + document.querySelectorAll(`.o_ChatterTopbar_buttonAttachmentsCount`).length, + 1, + "attachments button should have a counter" + ); + assert.strictEqual( + document.querySelectorAll(`.o_Chatter_attachmentBox`).length, + 0, + "should not have an attachment box in the chatter" + ); + + await afterNextRender(() => + document.querySelector(`.o_ChatterTopbar_buttonAttachments`).click() + ); + assert.strictEqual( + document.querySelectorAll(`.o_Chatter_attachmentBox`).length, + 1, + "should have an attachment box in the chatter" + ); +}); + +QUnit.test('composer show/hide on log note/send message [REQUIRE FOCUS]', async function (assert) { + assert.expect(10); + + this.data['res.partner'].records.push({ id: 100 }); + await this.start(); + const chatter = this.env.models['mail.chatter'].create({ + threadId: 100, + threadModel: 'res.partner', + }); + await this.createChatterComponent({ chatter }); + assert.strictEqual( + document.querySelectorAll(`.o_ChatterTopbar_buttonSendMessage`).length, + 1, + "should have a send message button" + ); + assert.strictEqual( + document.querySelectorAll(`.o_ChatterTopbar_buttonLogNote`).length, + 1, + "should have a log note button" + ); + assert.strictEqual( + document.querySelectorAll(`.o_Chatter_composer`).length, + 0, + "should not have a composer" + ); + + await afterNextRender(() => + document.querySelector(`.o_ChatterTopbar_buttonSendMessage`).click() + ); + assert.strictEqual( + document.querySelectorAll(`.o_Chatter_composer`).length, + 1, + "should have a composer" + ); + assert.hasClass( + document.querySelector('.o_Chatter_composer'), + 'o-focused', + "composer 'send message' in chatter should have focus just after being displayed" + ); + + await afterNextRender(() => + document.querySelector(`.o_ChatterTopbar_buttonLogNote`).click() + ); + assert.strictEqual( + document.querySelectorAll(`.o_Chatter_composer`).length, + 1, + "should still have a composer" + ); + assert.hasClass( + document.querySelector('.o_Chatter_composer'), + 'o-focused', + "composer 'log note' in chatter should have focus just after being displayed" + ); + + await afterNextRender(() => + document.querySelector(`.o_ChatterTopbar_buttonLogNote`).click() + ); + assert.strictEqual( + document.querySelectorAll(`.o_Chatter_composer`).length, + 0, + "should have no composer anymore" + ); + + await afterNextRender(() => + document.querySelector(`.o_ChatterTopbar_buttonSendMessage`).click() + ); + assert.strictEqual( + document.querySelectorAll(`.o_Chatter_composer`).length, + 1, + "should have a composer" + ); + + await afterNextRender(() => + document.querySelector(`.o_ChatterTopbar_buttonSendMessage`).click() + ); + assert.strictEqual( + document.querySelectorAll(`.o_Chatter_composer`).length, + 0, + "should have no composer anymore" + ); +}); + +QUnit.test('should not display user notification messages in chatter', async function (assert) { + assert.expect(1); + + this.data['res.partner'].records.push({ id: 100 }); + this.data['mail.message'].records.push({ + id: 102, + message_type: 'user_notification', + model: 'res.partner', + res_id: 100, + }); + await this.start(); + const chatter = this.env.models['mail.chatter'].create({ + threadId: 100, + threadModel: 'res.partner', + }); + await this.createChatterComponent({ chatter }); + + assert.containsNone( + document.body, + '.o_Message', + "should display no messages" + ); +}); + +QUnit.test('post message with "CTRL-Enter" keyboard shortcut', async function (assert) { + assert.expect(2); + + this.data['res.partner'].records.push({ id: 100 }); + await this.start(); + const chatter = this.env.models['mail.chatter'].create({ + threadId: 100, + threadModel: 'res.partner', + }); + await this.createChatterComponent({ chatter }); + assert.containsNone( + document.body, + '.o_Message', + "should not have any message initially in chatter" + ); + + await afterNextRender(() => + document.querySelector('.o_ChatterTopbar_buttonSendMessage').click() + ); + await afterNextRender(() => { + document.querySelector(`.o_ComposerTextInput_textarea`).focus(); + document.execCommand('insertText', false, "Test"); + }); + await afterNextRender(() => { + const kevt = new window.KeyboardEvent('keydown', { ctrlKey: true, key: "Enter" }); + document.querySelector('.o_ComposerTextInput_textarea').dispatchEvent(kevt); + }); + assert.containsOnce( + document.body, + '.o_Message', + "should now have single message in chatter after posting message from pressing 'CTRL-Enter' in text input of composer" + ); +}); + +QUnit.test('post message with "META-Enter" keyboard shortcut', async function (assert) { + assert.expect(2); + + this.data['res.partner'].records.push({ id: 100 }); + await this.start(); + const chatter = this.env.models['mail.chatter'].create({ + threadId: 100, + threadModel: 'res.partner', + }); + await this.createChatterComponent({ chatter }); + assert.containsNone( + document.body, + '.o_Message', + "should not have any message initially in chatter" + ); + + await afterNextRender(() => + document.querySelector('.o_ChatterTopbar_buttonSendMessage').click() + ); + await afterNextRender(() => { + document.querySelector(`.o_ComposerTextInput_textarea`).focus(); + document.execCommand('insertText', false, "Test"); + }); + await afterNextRender(() => { + const kevt = new window.KeyboardEvent('keydown', { key: "Enter", metaKey: true }); + document.querySelector('.o_ComposerTextInput_textarea').dispatchEvent(kevt); + }); + assert.containsOnce( + document.body, + '.o_Message', + "should now have single message in channel after posting message from pressing 'META-Enter' in text input of composer" + ); +}); + +QUnit.test('do not post message with "Enter" keyboard shortcut', async function (assert) { + // Note that test doesn't assert Enter makes a newline, because this + // default browser cannot be simulated with just dispatching + // programmatically crafted events... + assert.expect(2); + + this.data['res.partner'].records.push({ id: 100 }); + await this.start(); + const chatter = this.env.models['mail.chatter'].create({ + threadId: 100, + threadModel: 'res.partner', + }); + await this.createChatterComponent({ chatter }); + assert.containsNone( + document.body, + '.o_Message', + "should not have any message initially in chatter" + ); + + await afterNextRender(() => + document.querySelector('.o_ChatterTopbar_buttonSendMessage').click() + ); + await afterNextRender(() => { + document.querySelector(`.o_ComposerTextInput_textarea`).focus(); + document.execCommand('insertText', false, "Test"); + }); + const kevt = new window.KeyboardEvent('keydown', { key: "Enter" }); + document.querySelector('.o_ComposerTextInput_textarea').dispatchEvent(kevt); + await nextAnimationFrame(); + assert.containsNone( + document.body, + '.o_Message', + "should still not have any message in mailing channel after pressing 'Enter' in text input of composer" + ); +}); + +}); +}); +}); + +}); |
