2024-05-03 12:40:35 +03:00
/* @odoo-module */
import { startServer } from "@bus/../tests/helpers/mock_python_environment" ;
import { makeFakePresenceService } from "@bus/../tests/helpers/mock_services" ;
import { TEST _USER _IDS } from "@bus/../tests/helpers/test_constants" ;
import { waitUntilSubscribe } from "@bus/../tests/helpers/websocket_event_deferred" ;
import { Command } from "@mail/../tests/helpers/command" ;
import { patchUiSize } from "@mail/../tests/helpers/patch_ui_size" ;
import { start } from "@mail/../tests/helpers/test_utils" ;
import {
editInput ,
makeDeferred ,
patchWithCleanup ,
triggerHotkey ,
} from "@web/../tests/helpers/utils" ;
import {
assertSteps ,
click ,
contains ,
createFile ,
focus ,
insertText ,
scroll ,
step ,
} from "@web/../tests/utils" ;
QUnit . module ( "discuss" ) ;
QUnit . test ( "sanity check" , async ( ) => {
const { openDiscuss } = await start ( {
mockRPC ( route , args , originRPC ) {
if ( route . startsWith ( "/mail" ) ) {
step ( route ) ;
}
return originRPC ( route , args ) ;
} ,
} ) ;
await assertSteps ( [ "/mail/init_messaging" , "/mail/load_message_failures" ] ) ;
await openDiscuss ( ) ;
await assertSteps ( [ "/mail/inbox/messages" ] ) ;
await contains ( ".o-mail-DiscussSidebar" ) ;
await contains ( "h4" , { text : "Congratulations, your inbox is empty" } ) ;
} ) ;
QUnit . test ( "can change the thread name of #general [REQUIRE FOCUS]" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
name : "general" ,
channel _type : "channel" ,
create _uid : pyEnv . currentUserId ,
} ) ;
const { openDiscuss } = await start ( {
mockRPC ( route , params ) {
if ( route === "/web/dataset/call_kw/discuss.channel/channel_rename" ) {
step ( route ) ;
}
} ,
} ) ;
await openDiscuss ( channelId ) ;
await contains ( ".o-mail-Composer-input:focus" ) ;
await contains ( "input.o-mail-Discuss-threadName" , { value : "general" } ) ;
await insertText ( "input.o-mail-Discuss-threadName:enabled" , "special" , { replace : true } ) ;
triggerHotkey ( "Enter" ) ;
await assertSteps ( [ "/web/dataset/call_kw/discuss.channel/channel_rename" ] ) ;
await contains ( ".o-mail-DiscussSidebarChannel" , { text : "special" } ) ;
await contains ( "input.o-mail-Discuss-threadName" , { value : "special" } ) ;
} ) ;
QUnit . test ( "can active change thread from messaging menu" , async ( ) => {
const pyEnv = await startServer ( ) ;
const [ , teamId ] = pyEnv [ "discuss.channel" ] . create ( [
{ name : "general" , channel _type : "channel" } ,
{ name : "team" , channel _type : "channel" } ,
] ) ;
const { openDiscuss } = await start ( ) ;
await openDiscuss ( teamId ) ;
await contains ( ".o-mail-DiscussSidebar-item" , { text : "general" } ) ;
await contains ( ".o-mail-DiscussSidebar-item.o-active" , { text : "team" } ) ;
await click ( ".o_main_navbar i[aria-label='Messages']" ) ;
await click ( ".o-mail-DiscussSidebar-item" , { text : "general" } ) ;
await contains ( ".o-mail-DiscussSidebar-item.o-active" , { text : "general" } ) ;
await contains ( ".o-mail-DiscussSidebar-item" , { text : "team" } ) ;
} ) ;
QUnit . test ( "can change the thread description of #general [REQUIRE FOCUS]" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
name : "general" ,
channel _type : "channel" ,
description : "General announcements..." ,
create _uid : pyEnv . currentUserId ,
} ) ;
const { openDiscuss } = await start ( {
mockRPC ( route , params ) {
if ( route === "/web/dataset/call_kw/discuss.channel/channel_change_description" ) {
step ( route ) ;
}
} ,
} ) ;
await openDiscuss ( channelId ) ;
await contains ( ".o-mail-Composer-input:focus" ) ;
await contains ( "input.o-mail-Discuss-threadDescription" , {
value : "General announcements..." ,
} ) ;
await insertText ( "input.o-mail-Discuss-threadDescription:enabled" , "I want a burger today!" , {
replace : true ,
} ) ;
triggerHotkey ( "Enter" ) ;
await assertSteps ( [ "/web/dataset/call_kw/discuss.channel/channel_change_description" ] ) ;
await contains ( "input.o-mail-Discuss-threadDescription" , {
value : "I want a burger today!" ,
} ) ;
} ) ;
QUnit . test ( "Message following a notification should not be squashed" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
name : "general" ,
channel _type : "channel" ,
} ) ;
pyEnv [ "mail.message" ] . create ( {
author _id : pyEnv . currentPartnerId ,
body : '<div class="o_mail_notification">created <a href="#" class="o_channel_redirect">#general</a></div>' ,
model : "discuss.channel" ,
res _id : channelId ,
message _type : "notification" ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await insertText ( ".o-mail-Composer-input" , "Hello world!" ) ;
await click ( ".o-mail-Composer button:enabled" , { text : "Send" } ) ;
await contains ( ".o-mail-Message-sidebar .o-mail-Message-avatarContainer" ) ;
} ) ;
QUnit . test ( "Posting message should transform links." , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
name : "general" ,
channel _type : "channel" ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await insertText ( ".o-mail-Composer-input" , "test https://www.odoo.com/" ) ;
await click ( ".o-mail-Composer-send:enabled" ) ;
await contains ( "a[href='https://www.odoo.com/']" ) ;
} ) ;
QUnit . test ( "Posting message should transform relevant data to emoji." , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
name : "general" ,
channel _type : "channel" ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await insertText ( ".o-mail-Composer-input" , "test :P :laughing:" ) ;
await click ( ".o-mail-Composer-send:enabled" ) ;
await contains ( ".o-mail-Message-body" , { text : "test 😛 😆" } ) ;
} ) ;
QUnit . test (
"posting a message immediately after another one is displayed in 'simple' mode (squashed)" ,
async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
name : "general" ,
channel _type : "channel" ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await insertText ( ".o-mail-Composer-input" , "abc" ) ;
await click ( ".o-mail-Composer button:enabled" , { text : "Send" } ) ;
await contains ( ".o-mail-Message" , { count : 1 } ) ;
await insertText ( ".o-mail-Composer-input" , "def" ) ;
await click ( ".o-mail-Composer button:enabled" , { text : "Send" } ) ;
await contains ( ".o-mail-Message" , { count : 2 } ) ;
await contains ( ".o-mail-Message-header" ) ; // just 1, because 2nd message is squashed
}
) ;
QUnit . test (
"Message of type notification in chatter should not have inline display" ,
async ( assert ) => {
const pyEnv = await startServer ( ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( { name : "testPartner" } ) ;
pyEnv [ "mail.message" ] . create ( {
author _id : pyEnv . currentPartnerId ,
body : "<p>Line 1</p><p>Line 2</p>" ,
model : "res.partner" ,
res _id : partnerId ,
message _type : "notification" ,
} ) ;
const { openFormView } = await start ( ) ;
await openFormView ( "res.partner" , partnerId ) ;
await contains ( ".o-mail-Message-body" ) ;
assert . notOk (
window
. getComputedStyle ( document . querySelector ( ".o-mail-Message-body" ) , null )
. display . includes ( "inline" )
) ;
}
) ;
QUnit . test ( "Click on avatar opens its partner chat window" , async ( ) => {
const pyEnv = await startServer ( ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( { name : "testPartner" } ) ;
pyEnv [ "res.users" ] . create ( {
partner _id : partnerId ,
name : "testPartner" ,
email : "test@partner.com" ,
phone : "+45687468" ,
} ) ;
pyEnv [ "mail.message" ] . create ( {
author _id : partnerId ,
body : "Test" ,
attachment _ids : [ ] ,
model : "res.partner" ,
res _id : partnerId ,
} ) ;
const { openFormView } = await start ( ) ;
await openFormView ( "res.partner" , partnerId ) ;
await contains ( ".o-mail-Message-sidebar .o-mail-Message-avatarContainer img" ) ;
await click ( ".o-mail-Message-sidebar .o-mail-Message-avatarContainer img" ) ;
await contains ( ".o_avatar_card" ) ;
await contains ( ".o_card_user_infos > span" , { text : "testPartner" } ) ;
await contains ( ".o_card_user_infos > a" , { text : "test@partner.com" } ) ;
await contains ( ".o_card_user_infos > a" , { text : "+45687468" } ) ;
} ) ;
QUnit . test ( "Can use channel command /who" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
channel _type : "channel" ,
name : "my-channel" ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await insertText ( ".o-mail-Composer-input" , "/who" ) ;
await click ( ".o-mail-Composer button:enabled" , { text : "Send" } ) ;
await contains ( ".o_mail_notification" , { text : "You are alone in this channel." } ) ;
} ) ;
QUnit . test ( "sidebar: chat im_status rendering" , async ( ) => {
const pyEnv = await startServer ( ) ;
const [ partnerId _1 , partnerId _2 , partnerId _3 ] = pyEnv [ "res.partner" ] . create ( [
{ im _status : "offline" , name : "Partner1" } ,
{ im _status : "online" , name : "Partner2" } ,
{ im _status : "away" , name : "Partner3" } ,
] ) ;
pyEnv [ "discuss.channel" ] . create ( [
{
channel _member _ids : [
Command . create ( { partner _id : pyEnv . currentPartnerId } ) ,
Command . create ( { partner _id : partnerId _1 } ) ,
] ,
channel _type : "chat" ,
} ,
{
channel _member _ids : [
Command . create ( { partner _id : pyEnv . currentPartnerId } ) ,
Command . create ( { partner _id : partnerId _2 } ) ,
] ,
channel _type : "chat" ,
} ,
{
channel _member _ids : [
Command . create ( { partner _id : pyEnv . currentPartnerId } ) ,
Command . create ( { partner _id : partnerId _3 } ) ,
] ,
channel _type : "chat" ,
} ,
] ) ;
const { openDiscuss } = await start ( { hasTimeControl : true } ) ;
openDiscuss ( ) ;
await contains ( ".o-mail-DiscussSidebarChannel-threadIcon" , { count : 3 } ) ;
await contains ( ".o-mail-DiscussSidebarChannel" , {
text : "Partner1" ,
contains : [ ".o-mail-ThreadIcon div[title='Offline']" ] ,
} ) ;
await contains ( ".o-mail-DiscussSidebarChannel" , {
text : "Partner2" ,
contains : [ ".fa-circle.text-success" ] ,
} ) ;
await contains ( ".o-mail-DiscussSidebarChannel" , {
text : "Partner3" ,
contains : [ ".o-mail-ThreadIcon div[title='Away']" ] ,
} ) ;
} ) ;
QUnit . test ( "No load more when fetch below fetch limit of 30" , async ( assert ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "general" } ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( { } ) ;
pyEnv [ "res.partner" ] . create ( { } ) ;
for ( let i = 28 ; i >= 0 ; i -- ) {
pyEnv [ "mail.message" ] . create ( {
author _id : partnerId ,
body : "not empty" ,
date : "2019-04-20 10:00:00" ,
model : "discuss.channel" ,
res _id : channelId ,
} ) ;
}
const { openDiscuss } = await start ( {
async mockRPC ( route , args ) {
if ( route === "/discuss/channel/messages" ) {
assert . strictEqual ( args . limit , 30 ) ;
}
} ,
} ) ;
openDiscuss ( channelId ) ;
await contains ( ".o-mail-Message" , { count : 29 } ) ;
await contains ( "button" , { count : 0 , text : "Load More" } ) ;
} ) ;
QUnit . test ( "show date separator above mesages of similar date" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "general" } ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( { } ) ;
pyEnv [ "res.partner" ] . create ( { } ) ;
for ( let i = 28 ; i >= 0 ; i -- ) {
pyEnv [ "mail.message" ] . create ( {
author _id : partnerId ,
body : "not empty" ,
date : "2019-04-20 10:00:00" ,
model : "discuss.channel" ,
res _id : channelId ,
} ) ;
}
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await contains ( ".o-mail-Message" , {
count : 29 ,
after : [ ".o-mail-DateSection" , { text : "April 20, 2019" } ] ,
} ) ;
} ) ;
QUnit . test ( "sidebar: chat custom name" , async ( ) => {
const pyEnv = await startServer ( ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( { name : "Marc Demo" } ) ;
pyEnv [ "discuss.channel" ] . create ( {
channel _member _ids : [
Command . create ( { custom _channel _name : "Marc" , partner _id : pyEnv . currentPartnerId } ) ,
Command . create ( { partner _id : partnerId } ) ,
] ,
channel _type : "chat" ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( ) ;
await contains ( ".o-mail-DiscussSidebarChannel" , { text : "Marc" } ) ;
} ) ;
QUnit . test ( "reply to message from inbox (message linked to document) [REQUIRE FOCUS]" , async ( ) => {
const pyEnv = await startServer ( ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( { name : "Refactoring" } ) ;
const messageId = pyEnv [ "mail.message" ] . create ( {
body : "<p>Test</p>" ,
date : "2019-04-20 11:00:00" ,
message _type : "comment" ,
needaction : true ,
model : "res.partner" ,
res _id : partnerId ,
} ) ;
pyEnv [ "mail.notification" ] . create ( {
mail _message _id : messageId ,
notification _type : "inbox" ,
res _partner _id : pyEnv . currentPartnerId ,
} ) ;
const { openDiscuss , openFormView } = await start ( ) ;
openDiscuss ( ) ;
await contains ( ".o-mail-Message" ) ;
await contains ( ".o-mail-Message-header small" , { text : "on Refactoring" } ) ;
await click ( "[title='Expand']" ) ;
await click ( "[title='Reply']" ) ;
await contains ( ".o-mail-Message.o-selected" ) ;
await contains ( ".o-mail-Composer" ) ;
await contains ( ".o-mail-Composer-coreHeader" , { text : "on: Refactoring" } ) ;
await insertText ( ".o-mail-Composer-input:focus" , "Hello" ) ;
await click ( ".o-mail-Composer-send:enabled" ) ;
await contains ( ".o-mail-Composer" , { count : 0 } ) ;
await contains ( ".o-mail-Message:not(.o-selected)" ) ;
await contains ( ".o_notification.border-info" , { text : 'Message posted on "Refactoring"' } ) ;
openFormView ( "res.partner" , partnerId ) ;
await contains ( ".o-mail-Message" , { count : 2 } ) ;
await contains ( ".o-mail-Message-content" , { text : "Hello" } ) ;
} ) ;
QUnit . test ( "Can reply to starred message" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "RandomName" } ) ;
pyEnv [ "mail.message" ] . create ( {
body : "not empty" ,
model : "discuss.channel" ,
starred _partner _ids : [ pyEnv . currentPartnerId ] ,
res _id : channelId ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( "mail.box_starred" ) ;
await click ( "[title='Reply']" ) ;
await contains ( ".o-mail-Composer-coreHeader" , { text : "RandomName" } ) ;
await insertText ( ".o-mail-Composer-input" , "abc" ) ;
await click ( ".o-mail-Composer-send:enabled" ) ;
await contains ( ".o-mail-Composer-send" , { count : 0 } ) ;
await contains ( ".o_notification" , { text : 'Message posted on "RandomName"' } ) ;
await click ( ".o-mail-DiscussSidebarChannel" , { text : "RandomName" } ) ;
await contains ( ".o-mail-Message-content" , { text : "abc" } ) ;
} ) ;
QUnit . test ( "Can reply to history message" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "RandomName" } ) ;
const messageId = pyEnv [ "mail.message" ] . create ( {
body : "not empty" ,
model : "discuss.channel" ,
history _partner _ids : [ pyEnv . currentPartnerId ] ,
res _id : channelId ,
} ) ;
pyEnv [ "mail.notification" ] . create ( {
mail _message _id : messageId ,
notification _type : "inbox" ,
res _partner _id : pyEnv . currentPartnerId ,
is _read : true ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( "mail.box_history" ) ;
await click ( "[title='Reply']" ) ;
await contains ( ".o-mail-Composer-coreHeader" , { text : "RandomName" } ) ;
await insertText ( ".o-mail-Composer-input" , "abc" ) ;
await click ( ".o-mail-Composer-send:enabled" ) ;
await contains ( ".o-mail-Composer-send" , { count : 0 } ) ;
await contains ( ".o_notification" , { text : 'Message posted on "RandomName"' } ) ;
await click ( ".o-mail-DiscussSidebarChannel" , { text : "RandomName" } ) ;
await contains ( ".o-mail-Message-content" , { text : "abc" } ) ;
} ) ;
QUnit . test ( "receive new needaction messages" , async ( ) => {
const { openDiscuss , pyEnv } = await start ( ) ;
openDiscuss ( ) ;
await contains ( "button.o-active" , { text : "Inbox" , contains : [ ".badge" , { count : 0 } ] } ) ;
await contains ( ".o-mail-Thread .o-mail-Message" , { count : 0 } ) ;
// simulate receiving a new needaction message
pyEnv [ "bus.bus" ] . _sendone ( pyEnv . currentPartner , "mail.message/inbox" , {
body : "not empty 1" ,
id : 100 ,
needaction _partner _ids : [ pyEnv . currentPartnerId ] ,
model : "res.partner" ,
res _id : 20 ,
} ) ;
await contains ( "button" , { text : "Inbox" , contains : [ ".badge" , { text : "1" } ] } ) ;
await contains ( ".o-mail-Message" ) ;
await contains ( ".o-mail-Message-content" , { text : "not empty 1" } ) ;
// simulate receiving another new needaction message
pyEnv [ "bus.bus" ] . _sendone ( pyEnv . currentPartner , "mail.message/inbox" , {
body : "not empty 2" ,
id : 101 ,
needaction _partner _ids : [ pyEnv . currentPartnerId ] ,
model : "res.partner" ,
res _id : 20 ,
} ) ;
await contains ( "button" , { text : "Inbox" , contains : [ ".badge" , { text : "2" } ] } ) ;
await contains ( ".o-mail-Message" , { count : 2 } ) ;
await contains ( ".o-mail-Message-content" , { text : "not empty 1" } ) ;
await contains ( ".o-mail-Message-content" , { text : "not empty 2" } ) ;
} ) ;
QUnit . test ( "basic rendering" , async ( ) => {
const { openDiscuss } = await start ( ) ;
openDiscuss ( ) ;
await contains ( ".o-mail-DiscussSidebar" ) ;
await contains ( ".o-mail-Discuss-content" ) ;
await contains ( ".o-mail-Discuss-content .o-mail-Thread" ) ;
} ) ;
QUnit . test ( "basic rendering: sidebar" , async ( ) => {
const { openDiscuss } = await start ( ) ;
openDiscuss ( ) ;
await contains ( ".o-mail-DiscussSidebar button" , { text : "Inbox" } ) ;
await contains ( ".o-mail-DiscussSidebar button" , { text : "Starred" } ) ;
await contains ( ".o-mail-DiscussSidebar button" , { text : "History" } ) ;
await contains ( ".o-mail-DiscussSidebarCategory" , { count : 2 } ) ;
await contains ( ".o-mail-DiscussSidebarCategory-channel" , { text : "Channels" } ) ;
await contains ( ".o-mail-DiscussSidebarCategory-chat" , { text : "Direct messages" } ) ;
} ) ;
QUnit . test ( "sidebar: Inbox should have icon" , async ( ) => {
const { openDiscuss } = await start ( ) ;
openDiscuss ( ) ;
await contains ( "button" , { text : "Inbox" , contains : [ ".fa-inbox" ] } ) ;
} ) ;
QUnit . test ( "sidebar: default active inbox" , async ( ) => {
const { openDiscuss } = await start ( ) ;
openDiscuss ( ) ;
await contains ( "button.o-active" , { text : "Inbox" } ) ;
} ) ;
QUnit . test ( "sidebar: change active" , async ( ) => {
const { openDiscuss } = await start ( ) ;
openDiscuss ( ) ;
await contains ( "button.o-active" , { text : "Inbox" } ) ;
await contains ( "button:not(.o-active)" , { text : "Starred" } ) ;
await click ( "button" , { text : "Starred" } ) ;
await contains ( "button:not(.o-active)" , { text : "Inbox" } ) ;
await contains ( "button.o-active" , { text : "Starred" } ) ;
} ) ;
QUnit . test ( "sidebar: basic channel rendering" , async ( ) => {
const pyEnv = await startServer ( ) ;
pyEnv [ "discuss.channel" ] . create ( { name : "General" } ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( ) ;
await contains ( ".o-mail-DiscussSidebarChannel" , { text : "General" } ) ;
await contains ( ".o-mail-DiscussSidebarChannel img[data-alt='Thread Image']" ) ;
await contains ( ".o-mail-DiscussSidebarChannel .o-mail-DiscussSidebarChannel-commands.d-none" ) ;
await contains (
".o-mail-DiscussSidebarChannel .o-mail-DiscussSidebarChannel-commands i[title='Channel settings']"
) ;
await contains (
".o-mail-DiscussSidebarChannel .o-mail-DiscussSidebarChannel-commands div[title='Leave this channel']"
) ;
} ) ;
QUnit . test ( "channel become active" , async ( ) => {
const pyEnv = await startServer ( ) ;
pyEnv [ "discuss.channel" ] . create ( { name : "General" } ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( ) ;
await contains ( ".o-mail-DiscussSidebarChannel" ) ;
await contains ( ".o-mail-DiscussSidebarChannel.o-active" , { count : 0 } ) ;
await click ( ".o-mail-DiscussSidebarChannel" ) ;
await contains ( ".o-mail-DiscussSidebarChannel.o-active" ) ;
} ) ;
QUnit . test ( "channel become active - show composer in discuss content" , async ( ) => {
const pyEnv = await startServer ( ) ;
pyEnv [ "discuss.channel" ] . create ( { name : "General" } ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( ) ;
await click ( ".o-mail-DiscussSidebarChannel" ) ;
await contains ( ".o-mail-Thread" ) ;
await contains ( ".o-mail-Composer" ) ;
} ) ;
QUnit . test ( "sidebar: channel rendering with needaction counter" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "general" } ) ;
const messageId = pyEnv [ "mail.message" ] . create ( {
body : "not empty" ,
model : "discuss.channel" ,
res _id : channelId ,
} ) ;
pyEnv [ "mail.notification" ] . create ( {
mail _message _id : messageId ,
notification _type : "inbox" ,
res _partner _id : pyEnv . currentPartnerId ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( ) ;
await contains ( ".o-mail-DiscussSidebarChannel" , {
contains : [
[ "span" , { text : "general" } ] ,
[ ".badge" , { text : "1" } ] ,
] ,
} ) ;
} ) ;
QUnit . test ( "sidebar: chat rendering with unread counter" , async ( ) => {
const pyEnv = await startServer ( ) ;
pyEnv [ "discuss.channel" ] . create ( {
channel _member _ids : [
Command . create ( { message _unread _counter : 100 , partner _id : pyEnv . currentPartnerId } ) , // weak test, relies on hardcoded value for message_unread_counter but the messages do not actually exist
] ,
channel _type : "chat" ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( ) ;
await contains ( ".o-mail-DiscussSidebarChannel" , {
contains : [
[ ".badge" , { text : "100" } ] ,
[ ".o-mail-DiscussSidebarChannel-commands" , { text : "Unpin Conversation" , count : 0 } ] , // weak test, no guarantee this selector is valid in the first place
] ,
} ) ;
} ) ;
QUnit . test ( "initially load messages from inbox" , async ( assert ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "general" } ) ;
const messageId = pyEnv [ "mail.message" ] . create ( {
body : "not empty" ,
message _type : "comment" ,
model : "discuss.channel" ,
needaction _partner _ids : [ pyEnv . currentPartnerId ] ,
needaction : true ,
res _id : channelId ,
} ) ;
pyEnv [ "mail.notification" ] . create ( {
mail _message _id : messageId ,
notification _status : "sent" ,
notification _type : "inbox" ,
res _partner _id : pyEnv . currentPartnerId ,
} ) ;
const { openDiscuss } = await start ( {
async mockRPC ( route , args ) {
if ( route === "/mail/inbox/messages" ) {
step ( "/discuss/inbox/messages" ) ;
assert . strictEqual ( args . limit , 30 ) ;
}
} ,
} ) ;
openDiscuss ( ) ;
await contains ( ".o-mail-Message" ) ;
await assertSteps ( [ "/discuss/inbox/messages" ] ) ;
} ) ;
QUnit . test ( "default active id on mailbox" , async ( ) => {
const { openDiscuss } = await start ( ) ;
openDiscuss ( "mail.box_starred" ) ;
await contains ( "button.o-active" , { text : "Starred" } ) ;
} ) ;
QUnit . test ( "basic top bar rendering" , async ( ) => {
const pyEnv = await startServer ( ) ;
pyEnv [ "discuss.channel" ] . create ( { name : "General" } ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( ) ;
await contains ( "button:disabled" , { text : "Mark all read" } ) ;
await contains ( ".o-mail-Discuss-threadName" , { value : "Inbox" } ) ;
await click ( "button" , { text : "Starred" } ) ;
await contains ( "button:disabled" , { text : "Unstar all" } ) ;
await contains ( ".o-mail-Discuss-threadName" , { value : "Starred" } ) ;
await click ( ".o-mail-DiscussSidebarChannel" , { text : "General" } ) ;
await contains ( ".o-mail-Discuss-header button[title='Add Users']" ) ;
await contains ( ".o-mail-Discuss-threadName" , { value : "General" } ) ;
} ) ;
QUnit . test ( "rendering of inbox message" , async ( ) => {
const pyEnv = await startServer ( ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( { name : "Refactoring" } ) ;
const messageId = pyEnv [ "mail.message" ] . create ( {
body : "not empty" ,
model : "res.partner" ,
needaction : true ,
needaction _partner _ids : [ pyEnv . currentPartnerId ] ,
res _id : partnerId ,
} ) ;
pyEnv [ "mail.notification" ] . create ( {
mail _message _id : messageId ,
notification _status : "sent" ,
notification _type : "inbox" ,
res _partner _id : pyEnv . currentPartnerId ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( ) ;
await contains ( ".o-mail-Message" ) ;
await contains ( ".o-mail-Message-header small" , { text : "on Refactoring" } ) ;
await contains ( ".o-mail-Message-actions i" , { count : 4 } ) ;
await contains ( "[title='Add a Reaction']" ) ;
await contains ( "[title='Mark as Todo']" ) ;
await contains ( "[title='Mark as Read']" ) ;
await click ( "[title='Expand']" ) ;
await contains ( ".o-mail-Message-actions i" , { count : 5 } ) ;
await contains ( "[title='Reply']" ) ;
await contains ( "[title='Mark as Todo']" ) ;
await contains ( "[title='Mark as Read']" ) ;
await contains ( "[title='Expand']" ) ;
} ) ;
QUnit . test ( "Unfollow message" , async function ( ) {
const pyEnv = await startServer ( ) ;
const currentPartnerId = pyEnv . currentPartnerId ;
const [ threadFollowedId , threadNotFollowedId ] = pyEnv [ "res.partner" ] . create ( [
{
name : "Thread followed" ,
} ,
{
name : "Thread not followed" ,
} ,
] ) ;
pyEnv [ "mail.followers" ] . create ( {
partner _id : currentPartnerId ,
res _id : threadFollowedId ,
res _model : "res.partner" ,
} ) ;
for ( const threadId of [ threadFollowedId , threadFollowedId , threadNotFollowedId ] ) {
const messageId = pyEnv [ "mail.message" ] . create ( {
body : "not empty" ,
model : "res.partner" ,
needaction : true ,
needaction _partner _ids : [ currentPartnerId ] ,
res _id : threadId ,
} ) ;
pyEnv [ "mail.notification" ] . create ( {
mail _message _id : messageId ,
notification _status : "sent" ,
notification _type : "inbox" ,
res _partner _id : currentPartnerId ,
} ) ;
}
const { openDiscuss } = await start ( ) ;
openDiscuss ( ) ;
await contains ( ".o-mail-Message" , { count : 3 } ) ;
await click ( ":nth-child(1 of .o-mail-Message) button[title='Expand']" ) ;
await contains ( ":nth-child(1 of .o-mail-Message)" , {
contains : [
[ ".o-mail-Message-header small" , { text : "on Thread followed" } ] ,
[ ".o-mail-Message-moreMenu" ] ,
[ "span[title='Unfollow']" ] ,
] ,
} ) ;
await click ( ":nth-child(2 of .o-mail-Message) button[title='Expand']" ) ;
await contains ( ":nth-child(2 of .o-mail-Message)" , {
contains : [
[ ".o-mail-Message-header small" , { text : "on Thread followed" } ] ,
[ ".o-mail-Message-moreMenu" ] ,
[ "span[title='Unfollow']" ] ,
] ,
} ) ;
await click ( ":nth-child(3 of .o-mail-Message) button[title='Expand']" ) ;
await contains ( ":nth-child(3 of .o-mail-Message)" , {
contains : [
[ ".o-mail-Message-header small" , { text : "on Thread not followed" } ] ,
[ ".o-mail-Message-moreMenu" ] ,
[ "span[title='Unfollow']" , { count : 0 } ] ,
] ,
} ) ;
await click ( ":nth-child(1 of .o-mail-Message) button[title='Expand']" ) ;
await click ( ":nth-child(1 of .o-mail-Message) span[title='Unfollow']" ) ;
await contains ( ".o-mail-Message" , { count : 2 } ) ; // Unfollowing message 0 marks it as read -> Message removed
await click ( ":nth-child(1 of .o-mail-Message) button[title='Expand']" ) ;
await contains ( ":nth-child(1 of .o-mail-Message)" , {
contains : [
[ ".o-mail-Message-header small" , { text : "on Thread followed" } ] ,
[ ".o-mail-Message-moreMenu" ] ,
[ "span[title='Unfollow']" , { count : 0 } ] ,
] ,
} ) ;
await click ( ":nth-child(2 of .o-mail-Message) button[title='Expand']" ) ;
await contains ( ":nth-child(2 of .o-mail-Message)" , {
contains : [
[ ".o-mail-Message-header small" , { text : "on Thread not followed" } ] ,
[ ".o-mail-Message-moreMenu" ] ,
[ "span[title='Unfollow']" , { count : 0 } ] ,
] ,
} ) ;
} ) ;
QUnit . test ( 'messages marked as read move to "History" mailbox' , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "other-disco" } ) ;
const [ messageId _1 , messageId _2 ] = pyEnv [ "mail.message" ] . create ( [
{
body : "not empty" ,
model : "discuss.channel" ,
needaction : true ,
res _id : channelId ,
} ,
{
body : "not empty" ,
model : "discuss.channel" ,
needaction : true ,
res _id : channelId ,
} ,
] ) ;
pyEnv [ "mail.notification" ] . create ( [
{
mail _message _id : messageId _1 ,
notification _type : "inbox" ,
res _partner _id : pyEnv . currentPartnerId ,
} ,
{
mail _message _id : messageId _2 ,
notification _type : "inbox" ,
res _partner _id : pyEnv . currentPartnerId ,
} ,
] ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( "mail.box_history" ) ;
await contains ( "button.o-active" , { text : "History" } ) ;
await contains ( ".o-mail-Thread h4" , { text : "No history messages" } ) ;
await click ( "button" , { text : "Inbox" } ) ;
await contains ( "button.o-active" , { text : "Inbox" } ) ;
await contains ( ".o-mail-Thread h4" , { count : 0 , text : "Congratulations, your inbox is empty" } ) ;
await contains ( ".o-mail-Thread .o-mail-Message" , { count : 2 } ) ;
await click ( "button" , { text : "Mark all read" } ) ;
await contains ( "button.o-active" , { text : "Inbox" } ) ;
await contains ( ".o-mail-Thread h4" , { text : "Congratulations, your inbox is empty" } ) ;
await click ( "button" , { text : "History" } ) ;
await contains ( "button.o-active" , { text : "History" } ) ;
await contains ( ".o-mail-Thread h4" , { count : 0 , text : "No history messages" } ) ;
await contains ( ".o-mail-Thread .o-mail-Message" , { count : 2 } ) ;
} ) ;
QUnit . test (
'mark a single message as read should only move this message to "History" mailbox' ,
async ( ) => {
const pyEnv = await startServer ( ) ;
const [ messageId _1 , messageId _2 ] = pyEnv [ "mail.message" ] . create ( [
{
body : "not empty 1" ,
needaction : true ,
needaction _partner _ids : [ pyEnv . currentPartnerId ] ,
} ,
{
body : "not empty 2" ,
needaction : true ,
needaction _partner _ids : [ pyEnv . currentPartnerId ] ,
} ,
] ) ;
pyEnv [ "mail.notification" ] . create ( [
{
mail _message _id : messageId _1 ,
notification _type : "inbox" ,
res _partner _id : pyEnv . currentPartnerId ,
} ,
{
mail _message _id : messageId _2 ,
notification _type : "inbox" ,
res _partner _id : pyEnv . currentPartnerId ,
} ,
] ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( "mail.box_history" ) ;
await contains ( "button.o-active" , { text : "History" } ) ;
await contains ( ".o-mail-Thread h4" , { text : "No history messages" } ) ;
await click ( "button" , { text : "Inbox" } ) ;
await contains ( "button.o-active" , { text : "Inbox" } ) ;
await contains ( ".o-mail-Message" , { count : 2 } ) ;
await click ( "[title='Mark as Read']" , {
parent : [ ".o-mail-Message" , { text : "not empty 1" } ] ,
} ) ;
await contains ( ".o-mail-Message" ) ;
await contains ( ".o-mail-Message-content" , { text : "not empty 2" } ) ;
await click ( "button" , { text : "History" } ) ;
await contains ( "button.o-active" , { text : "History" } ) ;
await contains ( ".o-mail-Message" ) ;
await contains ( ".o-mail-Message-content" , { text : "not empty 1" } ) ;
}
) ;
QUnit . test ( 'all messages in "Inbox" in "History" after marked all as read' , async ( ) => {
const pyEnv = await startServer ( ) ;
for ( let i = 0 ; i < 40 ; i ++ ) {
const messageId = pyEnv [ "mail.message" ] . create ( {
body : "not empty" ,
needaction : true ,
} ) ;
pyEnv [ "mail.notification" ] . create ( {
mail _message _id : messageId ,
notification _type : "inbox" ,
res _partner _id : pyEnv . currentPartnerId ,
} ) ;
}
const { openDiscuss } = await start ( ) ;
openDiscuss ( ) ;
await contains ( ".o-mail-Message" , { count : 30 } ) ;
await click ( "button" , { text : "Mark all read" } ) ;
await contains ( ".o-mail-Message" , { count : 0 } ) ;
await click ( "button" , { text : "History" } ) ;
await contains ( ".o-mail-Message" , { count : 30 } ) ;
await contains ( ".o-mail-Thread" , { scroll : "bottom" } ) ;
await scroll ( ".o-mail-Thread" , 0 ) ;
await contains ( ".o-mail-Message" , { count : 40 } ) ;
} ) ;
QUnit . test ( "post a simple message" , async ( assert ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "general" } ) ;
const { openDiscuss } = await start ( {
async mockRPC ( route , args ) {
if ( route === "/mail/message/post" ) {
step ( "message_post" ) ;
assert . strictEqual ( args . thread _model , "discuss.channel" ) ;
assert . strictEqual ( args . thread _id , channelId ) ;
assert . strictEqual ( args . post _data . body , "Test" ) ;
assert . strictEqual ( args . post _data . message _type , "comment" ) ;
assert . strictEqual ( args . post _data . subtype _xmlid , "mail.mt_comment" ) ;
}
} ,
} ) ;
openDiscuss ( channelId ) ;
await contains ( ".o-mail-Thread" , { text : "There are no messages in this conversation." } ) ;
await contains ( ".o-mail-Message" , { count : 0 } ) ;
await insertText ( ".o-mail-Composer-input" , "Test" ) ;
await click ( ".o-mail-Composer-send:enabled" ) ;
await assertSteps ( [ "message_post" ] ) ;
await contains ( ".o-mail-Composer-input" , { value : "" } ) ;
await contains ( ".o-mail-Message-author" , { text : "Mitchell Admin" } ) ;
await contains ( ".o-mail-Message-content" , { text : "Test" } ) ;
} ) ;
QUnit . test ( "starred: unstar all" , async ( ) => {
const pyEnv = await startServer ( ) ;
pyEnv [ "mail.message" ] . create ( [
{ body : "not empty" , starred _partner _ids : [ pyEnv . currentPartnerId ] } ,
{ body : "not empty" , starred _partner _ids : [ pyEnv . currentPartnerId ] } ,
] ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( "mail.box_starred" ) ;
await contains ( ".o-mail-Message" , { count : 2 } ) ;
await contains ( "button" , { text : "Starred" , contains : [ ".badge" , { text : "2" } ] } ) ;
await click ( "button:enabled" , { text : "Unstar all" } ) ;
await contains ( "button" , { text : "Starred" , contains : [ ".badge" , { count : 0 } ] } ) ;
await contains ( ".o-mail-Message" , { count : 0 } ) ;
await contains ( "button:disabled" , { text : "Unstar all" } ) ;
} ) ;
QUnit . test ( "auto-focus composer on opening thread [REQUIRE FOCUS]" , async ( ) => {
const pyEnv = await startServer ( ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( { name : "Demo User" } ) ;
pyEnv [ "discuss.channel" ] . create ( [
{ name : "General" } ,
{
channel _member _ids : [
Command . create ( { partner _id : pyEnv . currentPartnerId } ) ,
Command . create ( { partner _id : partnerId } ) ,
] ,
channel _type : "chat" ,
} ,
] ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( ) ;
await contains ( "button.o-active" , { text : "Inbox" } ) ;
await contains ( ".o-mail-DiscussSidebarChannel:not(.o-active)" , { text : "General" } ) ;
await contains ( ".o-mail-DiscussSidebarChannel:not(.o-active)" , { text : "Demo User" } ) ;
await contains ( ".o-mail-Composer" , { count : 0 } ) ;
await click ( ".o-mail-DiscussSidebarChannel" , { text : "General" } ) ;
await contains ( ".o-mail-DiscussSidebarChannel.o-active" , { text : "General" } ) ;
await contains ( ".o-mail-Composer-input:focus" ) ;
await click ( ".o-mail-DiscussSidebarChannel" , { text : "Demo User" } ) ;
await contains ( ".o-mail-DiscussSidebarChannel.o-active" , { text : "Demo User" } ) ;
await contains ( ".o-mail-Composer-input:focus" ) ;
} ) ;
QUnit . test ( "no out-of-focus notif on non-needaction message in channel" , async ( assert ) => {
const pyEnv = await startServer ( ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( { name : "Dumbledore" } ) ;
const userId = pyEnv [ "res.users" ] . create ( { partner _id : partnerId } ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
channel _member _ids : [
Command . create ( { partner _id : pyEnv . currentPartnerId } ) ,
Command . create ( { partner _id : partnerId } ) ,
] ,
channel _type : "channel" ,
} ) ;
const { env } = await start ( {
services : {
presence : makeFakePresenceService ( { isOdooFocused : ( ) => false } ) ,
} ,
} ) ;
patchWithCleanup ( env . services [ "title" ] , {
setParts ( parts ) {
if ( parts . _chat ) {
step ( "set_title_part" ) ;
}
} ,
} ) ;
await contains ( ".o_menu_systray i[aria-label='Messages']" ) ;
await contains ( ".o-mail-ChatWindow" , { count : 0 } ) ;
// simulate receving new message
pyEnv . withUser ( userId , ( ) =>
env . services . rpc ( "/mail/message/post" , {
post _data : { body : "New message" , message _type : "comment" } ,
thread _id : channelId ,
thread _model : "discuss.channel" ,
} )
) ;
await click ( ".o_menu_systray i[aria-label='Messages']" ) ;
await contains ( ".o-mail-NotificationItem" , { text : "Dumbledore: New message" } ) ;
await contains ( ".o-mail-ChatWindow" , { count : 0 } ) ;
await assertSteps ( [ ] ) ;
} ) ;
QUnit . test ( "out-of-focus notif on needaction message in channel" , async ( assert ) => {
const pyEnv = await startServer ( ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( { name : "Dumbledore" } ) ;
const userId = pyEnv [ "res.users" ] . create ( { partner _id : partnerId } ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
channel _member _ids : [
Command . create ( { partner _id : pyEnv . currentPartnerId } ) ,
Command . create ( { partner _id : partnerId } ) ,
] ,
channel _type : "channel" ,
} ) ;
const { env } = await start ( {
services : {
presence : makeFakePresenceService ( { isOdooFocused : ( ) => false } ) ,
} ,
} ) ;
patchWithCleanup ( env . services [ "title" ] , {
setParts ( parts ) {
if ( parts . _chat ) {
step ( ` set_title_part: ${ parts . _chat } ` ) ;
}
} ,
} ) ;
await contains ( ".o_menu_systray i[aria-label='Messages']" ) ;
await contains ( ".o-mail-ChatWindow" , { count : 0 } ) ;
// simulate receiving a new needaction message with odoo out-of-focused
const currentPartnerId = pyEnv . currentPartnerId ;
pyEnv . withUser ( userId , ( ) =>
env . services . rpc ( "/mail/message/post" , {
post _data : {
body : "@Michell Admin" ,
partner _ids : [ currentPartnerId ] ,
message _type : "comment" ,
} ,
thread _id : channelId ,
thread _model : "discuss.channel" ,
} )
) ;
await contains ( ".o-mail-ChatWindow" ) ;
await assertSteps ( [ "set_title_part:1 Message" ] ) ;
} ) ;
QUnit . test ( "receive new chat message: out of odoo focus (notification, chat)" , async ( assert ) => {
const pyEnv = await startServer ( ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( { name : "Dumbledore" } ) ;
const userId = pyEnv [ "res.users" ] . create ( { partner _id : partnerId } ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
channel _member _ids : [
Command . create ( { partner _id : pyEnv . currentPartnerId } ) ,
Command . create ( { partner _id : partnerId } ) ,
] ,
channel _type : "chat" ,
} ) ;
const { env } = await start ( {
services : {
presence : makeFakePresenceService ( { isOdooFocused : ( ) => false } ) ,
} ,
} ) ;
patchWithCleanup ( env . services [ "title" ] , {
setParts ( parts ) {
if ( parts . _chat ) {
step ( ` set_title_part: ${ parts . _chat } ` ) ;
}
} ,
} ) ;
await contains ( ".o_menu_systray i[aria-label='Messages']" ) ;
await contains ( ".o-mail-ChatWindow" , { count : 0 } ) ;
// simulate receiving a new message with odoo out-of-focused
pyEnv . withUser ( userId , ( ) =>
env . services . rpc ( "/mail/message/post" , {
post _data : {
body : "New message" ,
message _type : "comment" ,
} ,
thread _id : channelId ,
thread _model : "discuss.channel" ,
} )
) ;
await contains ( ".o-mail-ChatWindow" ) ;
await assertSteps ( [ "set_title_part:1 Message" ] ) ;
} ) ;
QUnit . test ( "no out-of-focus notification on receiving self messages in chat" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { channel _type : "chat" } ) ;
const { env } = await start ( {
services : {
presence : makeFakePresenceService ( { isOdooFocused : ( ) => false } ) ,
} ,
} ) ;
patchWithCleanup ( env . services [ "title" ] , {
setParts ( parts ) {
if ( parts . _chat ) {
step ( "set_title_part" ) ;
}
} ,
} ) ;
await contains ( ".o_menu_systray i[aria-label='Messages']" ) ;
await contains ( ".o-mail-ChatWindow" , { count : 0 } ) ;
// simulate receiving a new message of self with odoo out-of-focused
pyEnv . withUser ( pyEnv . currentUserId , ( ) =>
env . services . rpc ( "/mail/message/post" , {
post _data : {
body : "New message" ,
message _type : "comment" ,
} ,
thread _id : channelId ,
thread _model : "discuss.channel" ,
} )
) ;
await click ( ".o_menu_systray i[aria-label='Messages']" ) ;
await contains ( ".o-mail-NotificationItem" , { text : "You: New message" } ) ;
await contains ( ".o-mail-ChatWindow" , { count : 0 } ) ;
assertSteps ( [ ] ) ;
} ) ;
QUnit . test ( "receive new chat messages: out of odoo focus (tab title)" , async ( assert ) => {
let stepCount = 0 ;
const pyEnv = await startServer ( ) ;
const [ channelId _1 , channelId _2 ] = pyEnv [ "discuss.channel" ] . create ( [
{ channel _type : "chat" } ,
{ channel _type : "chat" } ,
] ) ;
const { env , openDiscuss } = await start ( {
services : {
presence : makeFakePresenceService ( { isOdooFocused : ( ) => false } ) ,
} ,
} ) ;
openDiscuss ( ) ;
patchWithCleanup ( env . services [ "title" ] , {
setParts ( parts ) {
if ( ! parts . _chat ) {
return ;
}
stepCount ++ ;
step ( "set_title_part" ) ;
if ( stepCount === 1 ) {
assert . strictEqual ( parts . _chat , "1 Message" ) ;
}
if ( stepCount === 2 ) {
assert . strictEqual ( parts . _chat , "2 Messages" ) ;
}
if ( stepCount === 3 ) {
assert . strictEqual ( parts . _chat , "3 Messages" ) ;
}
} ,
} ) ;
const channel _1 = pyEnv [ "discuss.channel" ] . searchRead ( [ [ "id" , "=" , channelId _1 ] ] ) [ 0 ] ;
// simulate receiving a new message in chat 1 with odoo out-of-focused
pyEnv [ "bus.bus" ] . _sendone ( channel _1 , "discuss.channel/new_message" , {
id : channelId _1 ,
message : {
id : 126 ,
model : "discuss.channel" ,
res _id : channelId _1 ,
} ,
} ) ;
await assertSteps ( [ "set_title_part" ] ) ;
const channel _2 = pyEnv [ "discuss.channel" ] . searchRead ( [ [ "id" , "=" , channelId _2 ] ] ) [ 0 ] ;
// simulate receiving a new message in chat 2 with odoo out-of-focused
pyEnv [ "bus.bus" ] . _sendone ( channel _2 , "discuss.channel/new_message" , {
id : channelId _2 ,
message : {
id : 127 ,
model : "discuss.channel" ,
res _id : channelId _2 ,
} ,
} ) ;
await assertSteps ( [ "set_title_part" ] ) ;
// simulate receiving another new message in chat 2 with odoo focused
pyEnv [ "bus.bus" ] . _sendone ( channel _2 , "discuss.channel/new_message" , {
id : channelId _2 ,
message : {
id : 128 ,
model : "discuss.channel" ,
res _id : channelId _2 ,
} ,
} ) ;
await assertSteps ( [ "set_title_part" ] ) ;
} ) ;
QUnit . test ( "should auto-pin chat when receiving a new DM" , async ( ) => {
const pyEnv = await startServer ( ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( { name : "Demo" } ) ;
const userId = pyEnv [ "res.users" ] . create ( { partner _id : partnerId } ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
channel _member _ids : [
Command . create ( { is _pinned : false , partner _id : pyEnv . currentPartnerId } ) ,
Command . create ( { partner _id : partnerId } ) ,
] ,
channel _type : "chat" ,
} ) ;
const { env , openDiscuss } = await start ( ) ;
openDiscuss ( ) ;
await contains ( ".o-mail-DiscussSidebarCategory-chat" ) ;
await contains ( ".o-mail-DiscussSidebarChannel" , { count : 0 , text : "Demo" } ) ;
// simulate receiving the first message on channel 11
pyEnv . withUser ( userId , ( ) =>
env . services . rpc ( "/mail/message/post" , {
post _data : { body : "new message" , message _type : "comment" } ,
thread _id : channelId ,
thread _model : "discuss.channel" ,
} )
) ;
await contains ( ".o-mail-DiscussSidebarChannel" , { text : "Demo" } ) ;
} ) ;
QUnit . test ( "'Add Users' button should be displayed in the topbar of channels" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
name : "general" ,
channel _type : "channel" ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await contains ( "button[title='Add Users']" ) ;
} ) ;
QUnit . test ( "'Add Users' button should be displayed in the topbar of chats" , async ( ) => {
const pyEnv = await startServer ( ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( { name : "Marc Demo" } ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
channel _member _ids : [
Command . create ( { partner _id : pyEnv . currentPartnerId } ) ,
Command . create ( { partner _id : partnerId } ) ,
] ,
channel _type : "chat" ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await contains ( "button[title='Add Users']" ) ;
} ) ;
QUnit . test ( "'Add Users' button should be displayed in the topbar of groups" , async ( ) => {
const pyEnv = await startServer ( ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( { name : "Demo" } ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
channel _member _ids : [
Command . create ( { partner _id : pyEnv . currentPartnerId } ) ,
Command . create ( { partner _id : partnerId } ) ,
] ,
channel _type : "group" ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await contains ( "button[title='Add Users']" ) ;
} ) ;
QUnit . test ( "'Add Users' button should not be displayed in the topbar of mailboxes" , async ( ) => {
const { openDiscuss } = await start ( ) ;
openDiscuss ( "mail.box_starred" ) ;
await contains ( "button" , { text : "Unstar all" } ) ;
await contains ( "button[title='Add Users']" , { count : 0 } ) ;
} ) ;
QUnit . test (
"Thread avatar image is displayed in top bar of channels of type 'channel' limited to a group" ,
async ( ) => {
const pyEnv = await startServer ( ) ;
const groupId = pyEnv [ "res.groups" ] . create ( { name : "testGroup" } ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
channel _type : "channel" ,
name : "string" ,
group _public _id : groupId ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await contains ( ".o-mail-Discuss-header .o-mail-Discuss-threadAvatar" ) ;
}
) ;
QUnit . test (
"Thread avatar image is displayed in top bar of channels of type 'channel' not limited to any group" ,
async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
channel _type : "channel" ,
name : "string" ,
group _public _id : false ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await contains ( ".o-mail-Discuss-header .o-mail-Discuss-threadAvatar" ) ;
}
) ;
QUnit . test (
"Partner IM status is displayed as thread icon in top bar of channels of type 'chat'" ,
async ( ) => {
const pyEnv = await startServer ( ) ;
const [ partnerId _1 , partnerId _2 , partnerId _3 , partnerId _4 ] = pyEnv [ "res.partner" ] . create ( [
{ im _status : "online" , name : "Michel Online" } ,
{ im _status : "offline" , name : "Jacqueline Offline" } ,
{ im _status : "away" , name : "Nabuchodonosor Idle" } ,
{ im _status : "im_partner" , name : "Robert Fired" } ,
] ) ;
pyEnv [ "discuss.channel" ] . create ( [
{
channel _member _ids : [
Command . create ( { partner _id : pyEnv . currentPartnerId } ) ,
Command . create ( { partner _id : partnerId _1 } ) ,
] ,
channel _type : "chat" ,
} ,
{
channel _member _ids : [
Command . create ( { partner _id : pyEnv . currentPartnerId } ) ,
Command . create ( { partner _id : partnerId _2 } ) ,
] ,
channel _type : "chat" ,
} ,
{
channel _member _ids : [
Command . create ( { partner _id : pyEnv . currentPartnerId } ) ,
Command . create ( { partner _id : partnerId _3 } ) ,
] ,
channel _type : "chat" ,
} ,
{
channel _member _ids : [
Command . create ( { partner _id : pyEnv . currentPartnerId } ) ,
Command . create ( { partner _id : partnerId _4 } ) ,
] ,
channel _type : "chat" ,
} ,
{
channel _member _ids : [
Command . create ( { partner _id : pyEnv . currentPartnerId } ) ,
Command . create ( { partner _id : TEST _USER _IDS . odoobotId } ) ,
] ,
channel _type : "chat" ,
} ,
] ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( ) ;
await click ( ".o-mail-DiscussSidebarChannel" , { text : "Michel Online" } ) ;
await contains ( ".o-mail-Discuss-header .o-mail-ImStatus [title='Online']" ) ;
await click ( ".o-mail-DiscussSidebarChannel" , { text : "Jacqueline Offline" } ) ;
await contains ( ".o-mail-Discuss-header .o-mail-ImStatus [title='Offline']" ) ;
await click ( ".o-mail-DiscussSidebarChannel" , { text : "Nabuchodonosor Idle" } ) ;
await contains ( ".o-mail-Discuss-header .o-mail-ImStatus [title='Idle']" ) ;
await click ( ".o-mail-DiscussSidebarChannel" , { text : "Robert Fired" } ) ;
await contains ( ".o-mail-Discuss-header .o-mail-ImStatus [title='No IM status available']" ) ;
2024-05-08 11:31:09 +03:00
await click ( ".o-mail-DiscussSidebarChannel" , { text : "TalismanBot" } ) ;
2024-05-03 12:40:35 +03:00
await contains ( ".o-mail-Discuss-header .o-mail-ImStatus [title='Bot']" ) ;
}
) ;
QUnit . test ( "Thread avatar image is displayed in top bar of channels of type 'group'" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { channel _type : "group" } ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await contains ( ".o-mail-Discuss-header .o-mail-Discuss-threadAvatar" ) ;
} ) ;
QUnit . test ( "Do not trigger chat name server update when it is unchanged" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { channel _type : "chat" } ) ;
const { openDiscuss } = await start ( {
mockRPC ( route , args , originalRPC ) {
if ( args . method === "channel_set_custom_name" ) {
step ( args . method ) ;
}
return originalRPC ( route , args ) ;
} ,
} ) ;
openDiscuss ( channelId ) ;
await insertText ( "input.o-mail-Discuss-threadName:enabled" , "Mitchell Admin" , {
replace : true ,
} ) ;
triggerHotkey ( "Enter" ) ;
assertSteps ( [ ] ) ;
} ) ;
QUnit . test (
"Do not trigger channel description server update when channel has no description and editing to empty description" ,
async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
create _uid : pyEnv . currentUserId ,
name : "General" ,
} ) ;
const { openDiscuss } = await start ( {
mockRPC ( route , args , originalRPC ) {
if ( args . method === "channel_change_description" ) {
step ( args . method ) ;
}
return originalRPC ( route , args ) ;
} ,
} ) ;
openDiscuss ( channelId ) ;
await insertText ( "input.o-mail-Discuss-threadDescription" , "" ) ;
triggerHotkey ( "Enter" ) ;
assertSteps ( [ ] ) ;
}
) ;
QUnit . test ( "Channel is added to discuss after invitation" , async ( ) => {
const pyEnv = await startServer ( ) ;
const userId = pyEnv [ "res.users" ] . create ( { name : "Harry" } ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( { name : "Harry" , user _ids : [ userId ] } ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
name : "General" ,
channel _member _ids : [ Command . create ( { partner _id : partnerId } ) ] ,
} ) ;
const { env , openDiscuss } = await start ( ) ;
openDiscuss ( ) ;
await contains ( ".o-mail-DiscussSidebarCategory-channel" ) ;
await contains ( ".o-mail-DiscussSidebarChannel" , { count : 0 , text : "General" } ) ;
pyEnv . withUser ( userId , ( ) =>
env . services . orm . call ( "discuss.channel" , "add_members" , [ [ channelId ] ] , {
partner _ids : [ pyEnv . adminPartnerId ] ,
} )
) ;
await contains ( ".o-mail-DiscussSidebarChannel" , { text : "General" } ) ;
await contains ( ".o_notification.border-info" , { text : "You have been invited to #General" } ) ;
} ) ;
QUnit . test ( "select another mailbox" , async ( ) => {
patchUiSize ( { height : 360 , width : 640 } ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( ) ;
await contains ( ".o-mail-Discuss" ) ;
await contains ( ".o-mail-Discuss-threadName" , { value : "Inbox" } ) ;
await click ( "button" , { text : "Starred" } ) ;
await contains ( "button:disabled" , { text : "Unstar all" } ) ;
await contains ( ".o-mail-Discuss-threadName" , { value : "Starred" } ) ;
} ) ;
QUnit . test ( 'auto-select "Inbox nav bar" when discuss had inbox as active thread' , async ( ) => {
patchUiSize ( { height : 360 , width : 640 } ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( ) ;
await contains ( ".o-mail-Discuss-threadName" , { value : "Inbox" } ) ;
await contains ( ".o-mail-MessagingMenu-navbar button.fw-bolder" , { text : "Mailboxes" } ) ;
await contains ( "button.active.o-active" , { text : "Inbox" } ) ;
await contains ( "h4" , { text : "Congratulations, your inbox is empty" } ) ;
} ) ;
QUnit . test (
"composer should be focused automatically after clicking on the send button [REQUIRE FOCUS]" ,
async ( assert ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "test" } ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await insertText ( ".o-mail-Composer-input" , "Dummy Message" ) ;
await click ( ".o-mail-Composer-send:enabled" ) ;
assert . strictEqual ( document . activeElement , $ ( ".o-mail-Composer-input" ) [ 0 ] ) ;
}
) ;
QUnit . test (
"mark channel as seen if last message is visible when switching channels when the previous channel had a more recent last message than the current channel [REQUIRE FOCUS]" ,
async ( ) => {
const pyEnv = await startServer ( ) ;
const [ channelId _1 , channelId _2 ] = pyEnv [ "discuss.channel" ] . create ( [
{
channel _member _ids : [
[
0 ,
0 ,
{
message _unread _counter : 1 ,
partner _id : pyEnv . currentPartnerId ,
} ,
] ,
] ,
name : "Bla" ,
} ,
{
channel _member _ids : [
[
0 ,
0 ,
{
message _unread _counter : 1 ,
partner _id : pyEnv . currentPartnerId ,
} ,
] ,
] ,
name : "Blu" ,
} ,
] ) ;
pyEnv [ "mail.message" ] . create ( [
{
body : "oldest message" ,
model : "discuss.channel" ,
res _id : channelId _1 ,
} ,
{
body : "newest message" ,
model : "discuss.channel" ,
res _id : channelId _2 ,
} ,
] ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId _2 ) ;
await click ( "button" , { text : "Bla" } ) ;
await contains ( ".o-unread" , { count : 0 } ) ;
}
) ;
QUnit . test (
"warning on send with shortcut when attempting to post message with still-uploading attachments" ,
async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "test" } ) ;
const { openDiscuss } = await start ( {
async mockRPC ( route ) {
if ( route === "/mail/attachment/upload" ) {
// simulates attachment is never finished uploading
await new Promise ( ( ) => { } ) ;
}
} ,
} ) ;
openDiscuss ( channelId ) ;
await contains ( ".o-mail-Composer input[type=file]" ) ;
const file = await createFile ( {
content : "hello, world" ,
contentType : "text/plain" ,
name : "text.txt" ,
} ) ;
await editInput ( document . body , ".o-mail-Composer input[type=file]" , [ file ] ) ;
await contains ( ".o-mail-AttachmentCard" ) ;
await contains ( ".o-mail-AttachmentCard.o-isUploading" ) ;
await contains ( ".o-mail-Composer-send:disabled" ) ;
// Try to send message
triggerHotkey ( "Enter" ) ;
await contains ( ".o_notification.border-warning" , {
text : "Please wait while the file is uploading." ,
} ) ;
}
) ;
QUnit . test ( "new messages separator [REQUIRE FOCUS]" , async ( ) => {
// this test requires several messages so that the last message is not
// visible. This is necessary in order to display 'new messages' and not
// remove from DOM right away from seeing last message.
const pyEnv = await startServer ( ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( { name : "Foreigner partner" } ) ;
const userId = pyEnv [ "res.users" ] . create ( {
name : "Foreigner user" ,
partner _id : partnerId ,
} ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
name : "test" ,
channel _member _ids : [
Command . create ( { partner _id : partnerId } ) ,
Command . create ( { partner _id : pyEnv . currentPartnerId } ) ,
] ,
} ) ;
let lastMessageId ;
for ( let i = 1 ; i <= 25 ; i ++ ) {
lastMessageId = pyEnv [ "mail.message" ] . create ( {
body : "not empty" ,
model : "discuss.channel" ,
res _id : channelId ,
} ) ;
}
const [ memberId ] = pyEnv [ "discuss.channel.member" ] . search ( [
[ "channel_id" , "=" , channelId ] ,
[ "partner_id" , "=" , pyEnv . currentPartnerId ] ,
] ) ;
pyEnv [ "discuss.channel.member" ] . write ( [ memberId ] , { seen _message _id : lastMessageId } ) ;
const { env , openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await contains ( ".o-mail-Message" , { count : 25 } ) ;
await contains ( ".o-mail-Thread-newMessage hr + span" , { count : 0 , text : "New messages" } ) ;
await contains ( ".o-mail-Discuss-content .o-mail-Thread" , { scroll : "bottom" } ) ;
await scroll ( ".o-mail-Discuss-content .o-mail-Thread" , 0 ) ;
// composer is focused by default, we remove that focus
$ ( ".o-mail-Composer-input" ) [ 0 ] . blur ( ) ;
// simulate receiving a message
pyEnv . withUser ( userId , ( ) =>
env . services . rpc ( "/mail/message/post" , {
post _data : { body : "hu" , message _type : "comment" } ,
thread _id : channelId ,
thread _model : "discuss.channel" ,
} )
) ;
await contains ( ".o-mail-Message" , { count : 26 } ) ;
await contains ( ".o-mail-Thread-newMessage hr + span" , { text : "New messages" } ) ;
await scroll ( ".o-mail-Discuss-content .o-mail-Thread" , "bottom" ) ;
await contains ( ".o-mail-Thread-newMessage hr + span" , { text : "New messages" } ) ;
await focus ( ".o-mail-Composer-input" ) ;
await contains ( ".o-mail-Thread-newMessage hr + span" , { count : 0 , text : "New messages" } ) ;
} ) ;
QUnit . test ( "failure on loading messages should display error" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
channel _type : "channel" ,
name : "General" ,
} ) ;
const { openDiscuss } = await start ( {
async mockRPC ( route , args ) {
if ( route === "/discuss/channel/messages" ) {
return Promise . reject ( ) ;
}
} ,
} ) ;
openDiscuss ( channelId ) ;
await contains ( ".o-mail-Thread" , { text : "An error occurred while fetching messages." } ) ;
} ) ;
QUnit . test ( "failure on loading messages should prompt retry button" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
channel _type : "channel" ,
name : "General" ,
} ) ;
const { openDiscuss } = await start ( {
async mockRPC ( route , args ) {
if ( route === "/discuss/channel/messages" ) {
return Promise . reject ( ) ;
}
} ,
} ) ;
openDiscuss ( channelId ) ;
await contains ( "button" , { text : "Click here to retry" } ) ;
} ) ;
QUnit . test (
"failure on loading more messages should display error and prompt retry button" ,
async ( ) => {
// first call needs to be successful as it is the initial loading of messages
// second call comes from load more and needs to fail in order to show the error alert
// any later call should work so that retry button and load more clicks would now work
let messageFetchShouldFail = false ;
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
channel _type : "channel" ,
name : "General" ,
} ) ;
pyEnv [ "mail.message" ] . create (
[ ... Array ( 60 ) . keys ( ) ] . map ( ( ) => {
return {
body : "coucou" ,
model : "discuss.channel" ,
res _id : channelId ,
} ;
} )
) ;
const { openDiscuss } = await start ( {
async mockRPC ( route , args ) {
if ( route === "/discuss/channel/messages" && messageFetchShouldFail ) {
return Promise . reject ( ) ;
}
} ,
} ) ;
openDiscuss ( channelId ) ;
await contains ( ".o-mail-Message" , { count : 30 } ) ;
messageFetchShouldFail = true ;
await click ( "button" , { text : "Load More" } ) ;
await contains ( ".o-mail-Thread" , { text : "An error occurred while fetching messages." } ) ;
await contains ( "button" , { text : "Click here to retry" } ) ;
await contains ( "button" , { count : 0 , text : "Load More" } ) ;
}
) ;
QUnit . test (
"Retry loading more messages on failed load more messages should load more messages" ,
async ( ) => {
// first call needs to be successful as it is the initial loading of messages
// second call comes from load more and needs to fail in order to show the error alert
// any later call should work so that retry button and load more clicks would now work
let messageFetchShouldFail = false ;
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
channel _type : "channel" ,
name : "General" ,
} ) ;
pyEnv [ "mail.message" ] . create (
[ ... Array ( 90 ) . keys ( ) ] . map ( ( ) => {
return {
body : "coucou" ,
model : "discuss.channel" ,
res _id : channelId ,
} ;
} )
) ;
const { openDiscuss } = await start ( {
async mockRPC ( route , args ) {
if ( route === "/discuss/channel/messages" ) {
if ( messageFetchShouldFail ) {
return Promise . reject ( ) ;
}
}
} ,
} ) ;
openDiscuss ( channelId ) ;
await contains ( ".o-mail-Message" , { count : 30 } ) ;
messageFetchShouldFail = true ;
await click ( "button" , { text : "Load More" } ) ;
await contains ( "button" , { text : "Click here to retry" } ) ;
messageFetchShouldFail = false ;
await click ( "button" , { text : "Click here to retry" } ) ;
await contains ( ".o-mail-Message" , { count : 60 } ) ;
}
) ;
QUnit . test ( "composer state: attachments save and restore" , async ( ) => {
const pyEnv = await startServer ( ) ;
const [ channelId ] = pyEnv [ "discuss.channel" ] . create ( [ { name : "General" } , { name : "Special" } ] ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await contains (
".o-mail-Composer:has(textarea[placeholder='Message #General…']) input[type=file]"
) ;
// Add attachment in a message for #general
const file = await createFile ( {
content : "hello, world" ,
contentType : "text/plain" ,
name : "text.txt" ,
} ) ;
await editInput (
document . body ,
".o-mail-Composer:has(textarea[placeholder='Message #General…']) input[type=file]" ,
[ file ]
) ;
await contains ( ".o-mail-Composer .o-mail-AttachmentCard:not(.o-isUploading)" ) ;
// Switch to #special
await click ( "button" , { text : "Special" } ) ;
// Attach files in a message for #special
const files = await Promise . all ( [
createFile ( {
content : "hello2, world" ,
contentType : "text/plain" ,
name : "text2.txt" ,
} ) ,
createFile ( {
content : "hello3, world" ,
contentType : "text/plain" ,
name : "text3.txt" ,
} ) ,
createFile ( {
content : "hello4, world" ,
contentType : "text/plain" ,
name : "text4.txt" ,
} ) ,
] ) ;
await contains (
".o-mail-Composer:has(textarea[placeholder='Message #Special…']) input[type=file]"
) ;
await editInput (
document . body ,
".o-mail-Composer:has(textarea[placeholder='Message #Special…']) input[type=file]" ,
files
) ;
await contains ( ".o-mail-Composer .o-mail-AttachmentCard:not(.o-isUploading)" , { count : 3 } ) ;
// Switch back to #general
await click ( "button" , { text : "General" } ) ;
await contains ( ".o-mail-Composer .o-mail-AttachmentCard" ) ;
await contains ( ".o-mail-AttachmentCard" , { text : "text.txt" } ) ;
// Switch back to #special
await click ( "button" , { text : "Special" } ) ;
await contains ( ".o-mail-Composer .o-mail-AttachmentCard" , { count : 3 } ) ;
await contains ( ".o-mail-AttachmentCard" , { text : "text2.txt" } ) ;
await contains ( ".o-mail-AttachmentCard" , { text : "text3.txt" } ) ;
await contains ( ".o-mail-AttachmentCard" , { text : "text4.txt" } ) ;
} ) ;
QUnit . test (
"sidebar: cannot unpin channel group_based_subscription: mandatorily pinned" ,
async ( ) => {
const pyEnv = await startServer ( ) ;
pyEnv [ "discuss.channel" ] . create ( {
name : "General" ,
channel _member _ids : [
Command . create ( { is _pinned : false , partner _id : pyEnv . currentPartnerId } ) ,
] ,
group _based _subscription : true ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( ) ;
await contains ( "button" , { text : "General" } ) ;
await contains ( "div[title='Leave this channel']" , { count : 0 } ) ;
}
) ;
QUnit . test ( "restore thread scroll position" , async ( ) => {
const pyEnv = await startServer ( ) ;
const [ channelId _1 , channelId _2 ] = pyEnv [ "discuss.channel" ] . create ( [
{ name : "Channel1" } ,
{ name : "Channel2" } ,
] ) ;
for ( let i = 1 ; i <= 25 ; i ++ ) {
pyEnv [ "mail.message" ] . create ( {
body : "not empty" ,
model : "discuss.channel" ,
res _id : channelId _1 ,
} ) ;
}
for ( let i = 1 ; i <= 24 ; i ++ ) {
pyEnv [ "mail.message" ] . create ( {
body : "not empty" ,
model : "discuss.channel" ,
res _id : channelId _2 ,
} ) ;
}
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId _1 ) ;
await contains ( ".o-mail-Message" , { count : 25 } ) ;
await contains ( ".o-mail-Thread" , { scroll : "bottom" } ) ;
await scroll ( ".o-mail-Thread" , 0 ) ;
await click ( "button" , { text : "Channel2" } ) ;
await contains ( ".o-mail-Message" , { count : 24 } ) ;
await contains ( ".o-mail-Thread" , { scroll : "bottom" } ) ;
await click ( "button" , { text : "Channel1" } ) ;
await contains ( ".o-mail-Message" , { count : 25 } ) ;
await contains ( ".o-mail-Thread" , { scroll : 0 } ) ;
await click ( "button" , { text : "Channel2" } ) ;
await contains ( ".o-mail-Message" , { count : 24 } ) ;
await contains ( ".o-mail-Thread" , { scroll : "bottom" } ) ;
} ) ;
QUnit . test ( "Message shows up even if channel data is incomplete" , async ( ) => {
const { env , openDiscuss , pyEnv } = await start ( ) ;
openDiscuss ( ) ;
await contains ( ".o-mail-DiscussSidebarCategory-chat" ) ;
await contains ( ".o-mail-DiscussSidebarChannel" , { count : 0 } ) ;
const correspondentUserId = pyEnv [ "res.users" ] . create ( { name : "Albert" } ) ;
const correspondentPartnerId = pyEnv [ "res.partner" ] . create ( {
name : "Albert" ,
user _ids : [ correspondentUserId ] ,
} ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
channel _member _ids : [
[
0 ,
0 ,
{
is _pinned : true ,
partner _id : pyEnv . currentPartnerId ,
} ,
] ,
Command . create ( { partner _id : correspondentPartnerId } ) ,
] ,
channel _type : "chat" ,
} ) ;
env . services [ "bus_service" ] . forceUpdateChannels ( ) ;
await waitUntilSubscribe ( ) ;
await pyEnv . withUser ( correspondentUserId , ( ) =>
env . services . rpc ( "/discuss/channel/notify_typing" , {
is _typing : true ,
channel _id : channelId ,
} )
) ;
await pyEnv . withUser ( correspondentUserId , ( ) =>
env . services . rpc ( "/mail/message/post" , {
post _data : { body : "hello world" , message _type : "comment" } ,
thread _id : channelId ,
thread _model : "discuss.channel" ,
} )
) ;
await click ( ".o-mail-DiscussSidebarChannel" , { text : "Albert" } ) ;
await contains ( ".o-mail-Message-content" , { text : "hello world" } ) ;
} ) ;
QUnit . test ( "Correct breadcrumb when open discuss from chat window then see settings" , async ( ) => {
const pyEnv = await startServer ( ) ;
pyEnv [ "discuss.channel" ] . create ( { name : "General" } ) ;
await start ( ) ;
await click ( ".o_main_navbar i[aria-label='Messages']" ) ;
await click ( ".o-mail-NotificationItem" , { text : "General" } ) ;
await click ( "[title='Open Actions Menu']" ) ;
await click ( "[title='Open in Discuss']" ) ;
await click ( "[title='Channel settings']" , {
parent : [ ".o-mail-DiscussSidebarChannel" , { text : "General" } ] ,
} ) ;
await contains ( ".o_breadcrumb" , { text : "DiscussGeneral" } ) ;
} ) ;
QUnit . test (
"Chatter notification in messaging menu should open the form view even when discuss app is open" ,
async ( ) => {
const pyEnv = await startServer ( ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( { name : "TestPartner" } ) ;
const messageId = pyEnv [ "mail.message" ] . create ( {
model : "res.partner" ,
body : "A needaction message to have it in messaging menu" ,
author _id : pyEnv . odoobotId ,
needaction : true ,
needaction _partner _ids : [ pyEnv . currentPartnerId ] ,
res _id : partnerId ,
} ) ;
pyEnv [ "mail.notification" ] . create ( {
mail _message _id : messageId ,
notification _status : "sent" ,
notification _type : "inbox" ,
res _partner _id : pyEnv . currentPartnerId ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( ) ;
await click ( ".o_main_navbar i[aria-label='Messages']" ) ;
await click ( ".o-mail-NotificationItem" ) ;
await contains ( ".o-mail-Discuss" , { count : 0 } ) ;
await contains ( ".o_form_view .o-mail-Chatter" ) ;
await contains ( ".o_form_view .o_last_breadcrumb_item" , { text : "TestPartner" } ) ;
await contains ( ".o-mail-Chatter .o-mail-Message" , {
text : "A needaction message to have it in messaging menu" ,
} ) ;
}
) ;
QUnit . test (
"Chats input should wait until the previous RPC is done before starting a new one" ,
async ( ) => {
const pyEnv = await startServer ( ) ;
const [ partnerId1 , partnerId2 ] = pyEnv [ "res.partner" ] . create ( [
{ name : "Mario" } ,
{ name : "Mama" } ,
] ) ;
pyEnv [ "res.users" ] . create ( [ { partner _id : partnerId1 } , { partner _id : partnerId2 } ] ) ;
const deferred1 = makeDeferred ( ) ;
const deferred2 = makeDeferred ( ) ;
const { openDiscuss } = await start ( {
async mockRPC ( route , params ) {
if ( route === "/web/dataset/call_kw/res.partner/im_search" ) {
const { args } = params ;
if ( args [ 0 ] === "m" ) {
await deferred1 ;
step ( "First RPC" ) ;
} else if ( args [ 0 ] === "mar" ) {
await deferred2 ;
step ( "Second RPC" ) ;
} else {
throw Error ( ` Unexpected search term: ${ args [ 0 ] } ` ) ;
}
}
} ,
} ) ;
openDiscuss ( ) ;
await click ( ".o-mail-DiscussSidebarCategory-add[title='Start a conversation']" ) ;
await insertText ( ".o-discuss-ChannelSelector input" , "m" ) ;
await contains ( ".o-mail-NavigableList-item" , { text : "Loading" } ) ;
await insertText ( ".o-discuss-ChannelSelector input" , "a" ) ;
await insertText ( ".o-discuss-ChannelSelector input" , "r" ) ;
deferred1 . resolve ( ) ;
await Promise . resolve ( ) ;
await assertSteps ( [ "First RPC" ] ) ;
deferred2 . resolve ( ) ;
await contains ( ".o-discuss-ChannelSelector-suggestion" , { text : "Mario" } ) ;
await contains ( ".o-discuss-ChannelSelector-suggestion" , { count : 0 , text : "Mama" } ) ;
await assertSteps ( [ "Second RPC" ] ) ;
}
) ;
QUnit . test ( "Escape key should close the channel selector and focus the composer" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "General" } ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await click ( "i[title='Add or join a channel']" ) ;
await contains ( ".o-discuss-ChannelSelector" ) ;
triggerHotkey ( "escape" ) ;
await contains ( ".o-discuss-ChannelSelector" , { count : 0 } ) ;
await contains ( ".o-mail-Composer-input:focus" ) ;
} ) ;
QUnit . test ( "Escape key should focus the composer if it's not focused" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "General" } ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await click ( "button[title='Pinned Messages']" ) ;
triggerHotkey ( "escape" ) ;
await contains ( ".o-mail-Composer-input:focus" ) ;
} ) ;
QUnit . test ( "Notification settings: basic rendering" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
name : "Mario Party" ,
channel _type : "channel" ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await click ( "[title='Notification Settings']" ) ;
await contains ( "button" , { text : "All Messages" } ) ;
await contains ( "button" , { text : "Mentions Only" } ) ;
await contains ( "button" , { text : "Nothing" } ) ;
await click ( "[title='Mute Channel']" ) ;
await contains ( "[title='For 15 minutes']" ) ;
await contains ( "[title='For 1 hour']" ) ;
await contains ( "[title='For 3 hours']" ) ;
await contains ( "[title='For 8 hours']" ) ;
await contains ( "[title='For 24 hours']" ) ;
await contains ( "[title='Until I turn it back on']" ) ;
} ) ;
QUnit . test ( "Notification settings: mute channel will change the style of sidebar" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
name : "Mario Party" ,
channel _type : "channel" ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await contains ( ".o-mail-DiscussSidebar-item" , { text : "Mario Party" } ) ;
await contains ( ".o-mail-DiscussSidebar-item[class*='opacity-50']" , {
text : "Mario Party" ,
count : 0 ,
} ) ;
await click ( "[title='Notification Settings']" ) ;
await click ( "[title='Mute Channel']" ) ;
await click ( "[title='For 15 minutes']" ) ;
await contains ( ".o-mail-DiscussSidebar-item" , { text : "Mario Party" } ) ;
await contains ( ".o-mail-DiscussSidebar-item[class*='opacity-50']" , { text : "Mario Party" } ) ;
} ) ;
QUnit . test ( "Notification settings: mute/unmute channel works correctly" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
name : "Mario Party" ,
channel _type : "channel" ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await click ( "[title='Notification Settings']" ) ;
await click ( "[title='Mute Channel']" ) ;
await click ( "[title='For 15 minutes']" ) ;
await click ( "[title='Notification Settings']" ) ;
await contains ( "span" , { text : "Unmute Channel" } ) ;
await click ( "button" , { text : "Unmute Channel" } ) ;
await click ( "[title='Notification Settings']" ) ;
await contains ( "span" , { text : "Unmute Channel" } ) ;
} ) ;
QUnit . test ( "Newly created chat should be at the top of the direct message list" , async ( ) => {
const pyEnv = await startServer ( ) ;
const [ userId1 , userId2 ] = pyEnv [ "res.users" ] . create ( [
{ name : "Jerry Golay" } ,
{ name : "Albert" } ,
] ) ;
const [ partnerId1 ] = pyEnv [ "res.partner" ] . create ( [
{
name : "Albert" ,
user _ids : [ userId2 ] ,
} ,
{
name : "Jerry Golay" ,
user _ids : [ userId1 ] ,
} ,
] ) ;
pyEnv [ "discuss.channel" ] . create ( {
channel _member _ids : [
Command . create ( {
is _pinned : true ,
last _interest _dt : "2021-01-01 10:00:00" ,
partner _id : pyEnv . currentPartnerId ,
} ) ,
Command . create ( { partner _id : partnerId1 } ) ,
] ,
channel _type : "chat" ,
} ) ;
const { openDiscuss } = await start ( ) ;
await openDiscuss ( ) ;
await click ( ".o-mail-DiscussSidebarCategory-add[title='Start a conversation']" ) ;
await insertText ( ".o-discuss-ChannelSelector input" , "Jer" ) ;
await click ( ".o-discuss-ChannelSelector-suggestion" ) ;
await triggerHotkey ( "Enter" ) ;
await contains ( ".o-mail-DiscussSidebar-item" , {
text : "Jerry Golay" ,
before : [ ".o-mail-DiscussSidebar-item" , { text : "Albert" } ] ,
} ) ;
} ) ;
QUnit . test ( "Read of unread chat where new message is deleted should mark as read." , async ( ) => {
const pyEnv = await startServer ( ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( { name : "Marc Demo" } ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
channel _member _ids : [
Command . create ( { partner _id : pyEnv . currentPartnerId } ) ,
Command . create ( { partner _id : partnerId } ) ,
] ,
channel _type : "chat" ,
} ) ;
const messageId = pyEnv [ "mail.message" ] . create ( {
author _id : partnerId ,
body : "Heyo" ,
model : "discuss.channel" ,
res _id : channelId ,
message _type : "comment" ,
} ) ;
const [ memberId ] = pyEnv [ "discuss.channel.member" ] . search ( [
[ "channel_id" , "=" , channelId ] ,
[ "partner_id" , "=" , pyEnv . currentPartnerId ] ,
] ) ;
pyEnv [ "discuss.channel.member" ] . write ( [ memberId ] , {
seen _message _id : messageId ,
message _unread _counter : 1 ,
} ) ;
const { env , openDiscuss } = await start ( ) ;
await openDiscuss ( ) ;
await contains ( "button" , { text : "Marc Demo" , contains : [ ".badge" , { text : "1" } ] } ) ;
// simulate deleted message
await env . services . rpc ( "/mail/message/update_content" , {
message _id : messageId ,
body : "" ,
attachment _ids : [ ] ,
} ) ;
await click ( "button" , { text : "Marc Demo" } ) ;
await contains ( "button" , { text : "Marc Demo" , contains : [ ".badge" , { count : 0 } ] } ) ;
} ) ;