2024-05-03 12:40:35 +03:00
/* @odoo-module */
import { startServer } from "@bus/../tests/helpers/mock_python_environment" ;
import { Command } from "@mail/../tests/helpers/command" ;
import { start } from "@mail/../tests/helpers/test_utils" ;
import { config as transitionConfig } from "@web/core/transition" ;
import { makeDeferred , nextTick , patchWithCleanup } from "@web/../tests/helpers/utils" ;
import {
click ,
contains ,
createFile ,
dragenterFiles ,
focus ,
insertText ,
scroll ,
triggerEvents ,
} from "@web/../tests/utils" ;
QUnit . module ( "thread" ) ;
QUnit . test ( "dragover files on thread with composer" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
channel _type : "channel" ,
group _public _id : false ,
name : "General" ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
const files = [
await createFile ( {
content : "hello, world" ,
contentType : "text/plain" ,
name : "text3.txt" ,
} ) ,
] ;
await dragenterFiles ( ".o-mail-Thread" , files ) ;
await contains ( ".o-mail-Dropzone" ) ;
} ) ;
QUnit . test ( "load more messages from channel (auto-load on scroll)" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
channel _type : "channel" ,
group _public _id : false ,
name : "General" ,
} ) ;
for ( let i = 0 ; i <= 60 ; i ++ ) {
pyEnv [ "mail.message" ] . create ( {
body : i . toString ( ) ,
model : "discuss.channel" ,
res _id : channelId ,
} ) ;
}
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await contains ( "button" , { text : "Load More" , before : [ ".o-mail-Message" , { count : 30 } ] } ) ;
await contains ( ".o-mail-Thread" , { scroll : "bottom" } ) ;
await scroll ( ".o-mail-Thread" , 0 ) ;
await contains ( ".o-mail-Message" , { count : 60 } ) ;
await contains ( ".o-mail-Message" , { text : "30" , after : [ ".o-mail-Message" , { text : "29" } ] } ) ;
} ) ;
QUnit . test ( "show message subject when subject is not the same as the thread name" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
channel _type : "channel" ,
group _public _id : false ,
name : "General" ,
} ) ;
pyEnv [ "mail.message" ] . create ( {
body : "not empty" ,
model : "discuss.channel" ,
res _id : channelId ,
subject : "Salutations, voyageur" ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await contains ( ".o-mail-Message" , { text : "Subject: Salutations, voyageurnot empty" } ) ;
} ) ;
QUnit . test ( "do not show message subject when subject is the same as the thread name" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
channel _type : "channel" ,
group _public _id : false ,
name : "Salutations, voyageur" ,
} ) ;
pyEnv [ "mail.message" ] . create ( {
body : "not empty" ,
model : "discuss.channel" ,
res _id : channelId ,
subject : "Salutations, voyageur" ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await contains ( ".o-mail-Message" , { text : "not empty" } ) ;
await contains ( ".o-mail-Message" , {
count : 0 ,
text : "Subject: Salutations, voyageurnot empty" ,
} ) ;
} ) ;
QUnit . test ( "auto-scroll to bottom of thread on load" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "general" } ) ;
for ( let i = 1 ; i <= 25 ; i ++ ) {
pyEnv [ "mail.message" ] . create ( {
body : "not empty" ,
model : "discuss.channel" ,
res _id : channelId ,
} ) ;
}
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await contains ( ".o-mail-Message" , { count : 25 } ) ;
await contains ( ".o-mail-Thread" , { scroll : "bottom" } ) ;
} ) ;
QUnit . test ( "display day separator before first message of the day" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "" } ) ;
pyEnv [ "mail.message" ] . create ( [
{
body : "not empty" ,
model : "discuss.channel" ,
res _id : channelId ,
} ,
{
body : "not empty" ,
model : "discuss.channel" ,
res _id : channelId ,
} ,
] ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await contains ( ".o-mail-DateSection" ) ;
} ) ;
QUnit . test ( "do not display day separator if all messages of the day are empty" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "" } ) ;
pyEnv [ "mail.message" ] . create ( {
body : "" ,
model : "discuss.channel" ,
res _id : channelId ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await contains ( ".o-mail-Thread" , { text : "There are no messages in this conversation." } ) ;
await contains ( ".o-mail-DateSection" , { count : 0 } ) ;
} ) ;
QUnit . test ( "scroll position is kept when navigating from one channel to another" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId _1 = pyEnv [ "discuss.channel" ] . create ( { name : "channel-1" } ) ;
const channelId _2 = pyEnv [ "discuss.channel" ] . create ( { name : "channel-2" } ) ;
// Fill both channels with random messages in order for the scrollbar to
// appear.
pyEnv [ "mail.message" ] . create (
Array ( 50 )
. fill ( 0 )
. map ( ( _ , index ) => ( {
body : "Non Empty Body " . repeat ( 25 ) ,
message _type : "comment" ,
model : "discuss.channel" ,
res _id : index < 20 ? channelId _1 : channelId _2 ,
} ) )
) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId _1 ) ;
await contains ( ".o-mail-Message" , { count : 20 } ) ;
const scrollValue1 = $ ( ".o-mail-Thread" ) [ 0 ] . scrollHeight / 2 ;
await contains ( ".o-mail-Thread" , { scroll : "bottom" } ) ;
await scroll ( ".o-mail-Thread" , scrollValue1 ) ;
await click ( ".o-mail-DiscussSidebarChannel" , { text : "channel-2" } ) ;
await contains ( ".o-mail-Message" , { count : 30 } ) ;
const scrollValue2 = $ ( ".o-mail-Thread" ) [ 0 ] . scrollHeight / 3 ;
await contains ( ".o-mail-Thread" , { scroll : "bottom" } ) ;
await scroll ( ".o-mail-Thread" , scrollValue2 ) ;
await click ( ".o-mail-DiscussSidebarChannel" , { text : "channel-1" } ) ;
await contains ( ".o-mail-Message" , { count : 20 } ) ;
await contains ( ".o-mail-Thread" , { scroll : scrollValue1 } ) ;
await click ( ".o-mail-DiscussSidebarChannel" , { text : "channel-2" } ) ;
await contains ( ".o-mail-Message" , { count : 30 } ) ;
await contains ( ".o-mail-Thread" , { scroll : scrollValue2 } ) ;
} ) ;
QUnit . test ( "thread is still scrolling after scrolling up then to bottom" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "channel-1" } ) ;
pyEnv [ "mail.message" ] . create (
Array ( 20 )
. fill ( 0 )
. map ( ( ) => ( {
body : "Non Empty Body " . repeat ( 25 ) ,
message _type : "comment" ,
model : "discuss.channel" ,
res _id : channelId ,
} ) )
) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await contains ( ".o-mail-Message" , { count : 20 } ) ;
await contains ( ".o-mail-Thread" , { scroll : "bottom" } ) ;
await scroll ( ".o-mail-Thread" , $ ( ".o-mail-Thread" ) [ 0 ] . scrollHeight / 2 ) ;
await scroll ( ".o-mail-Thread" , "bottom" ) ;
await insertText ( ".o-mail-Composer-input" , "123" ) ;
await click ( ".o-mail-Composer-send:enabled" ) ;
await contains ( ".o-mail-Message" , { count : 21 } ) ;
await contains ( ".o-mail-Thread" , { scroll : "bottom" } ) ;
} ) ;
QUnit . test ( "Can mention other channels in a group-restricted channel" , async ( ) => {
const pyEnv = await startServer ( ) ;
const groupId = pyEnv [ "res.groups" ] . create ( {
name : "Mario Group" ,
} ) ;
const [ channelId1 ] = pyEnv [ "discuss.channel" ] . create ( [
{
channel _type : "channel" ,
group _public _id : groupId ,
name : "Marios" ,
} ,
{
channel _type : "channel" ,
group _public _id : false ,
name : "Link and Zelda" ,
} ,
] ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId1 ) ;
await insertText ( ".o-mail-Composer-input" , "#" ) ;
await contains ( ".o-mail-Composer-suggestion" , { text : "#Marios" } ) ;
await contains ( ".o-mail-Composer-suggestion" , { text : "#Link and Zelda" } ) ;
await click ( ".o-mail-Composer-suggestion" , { text : "#Link and Zelda" } ) ;
await contains ( ".o-mail-Composer-input" , { value : "#Link and Zelda " } ) ;
await click ( ".o-mail-Composer-send:enabled" ) ;
await contains ( ".o-mail-Message-body .o_channel_redirect" , { text : "#Link and Zelda" } ) ;
} ) ;
QUnit . test ( "mention a channel with space in the name" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "General good boy" } ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await insertText ( ".o-mail-Composer-input" , "#" ) ;
await click ( ".o-mail-Composer-suggestion" ) ;
await contains ( ".o-mail-Composer-input" , { value : "#General good boy " } ) ;
await click ( ".o-mail-Composer-send:enabled" ) ;
await contains ( ".o-mail-Message-body .o_channel_redirect" , { text : "#General good boy" } ) ;
} ) ;
QUnit . test ( 'mention a channel with "&" in the name' , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "General & good" } ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await insertText ( ".o-mail-Composer-input" , "#" ) ;
await click ( ".o-mail-Composer-suggestion" ) ;
await contains ( ".o-mail-Composer-input" , { value : "#General & good " } ) ;
await click ( ".o-mail-Composer-send:enabled" ) ;
await contains ( ".o-mail-Message-body .o_channel_redirect" , { text : "#General & good" } ) ;
} ) ;
QUnit . test (
"mark channel as fetched when a new message is loaded and as seen when focusing composer [REQUIRE FOCUS]" ,
async ( assert ) => {
const pyEnv = await startServer ( ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( {
email : "fred@example.com" ,
name : "Fred" ,
} ) ;
const userId = pyEnv [ "res.users" ] . create ( { partner _id : partnerId } ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
name : "test" ,
channel _member _ids : [
Command . create ( { partner _id : pyEnv . currentPartnerId } ) ,
Command . create ( { partner _id : partnerId } ) ,
] ,
channel _type : "chat" ,
} ) ;
const { env } = await start ( {
mockRPC ( route , args ) {
if ( args . method === "channel_fetched" ) {
assert . strictEqual ( args . args [ 0 ] [ 0 ] , channelId ) ;
assert . strictEqual ( args . model , "discuss.channel" ) ;
assert . step ( "rpc:channel_fetch" ) ;
} else if ( route === "/discuss/channel/set_last_seen_message" ) {
assert . strictEqual ( args . channel _id , channelId ) ;
assert . step ( "rpc:set_last_seen_message" ) ;
}
} ,
} ) ;
await contains ( ".o_menu_systray i[aria-label='Messages']" ) ;
pyEnv . withUser ( userId , ( ) =>
env . services . rpc ( "/mail/message/post" , {
post _data : { body : "Hello!" , message _type : "comment" } ,
thread _id : channelId ,
thread _model : "discuss.channel" ,
} )
) ;
await contains ( ".o-mail-Message" ) ;
assert . verifySteps ( [ "rpc:channel_fetch" ] ) ;
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" } ) ;
assert . verifySteps ( [ "rpc:set_last_seen_message" ] ) ;
}
) ;
QUnit . test (
"mark channel as fetched and seen when a new message is loaded if composer is focused [REQUIRE FOCUS]" ,
async ( assert ) => {
const pyEnv = await startServer ( ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( { } ) ;
const userId = pyEnv [ "res.users" ] . create ( { partner _id : partnerId } ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
name : "test" ,
channel _member _ids : [
Command . create ( { partner _id : pyEnv . currentPartnerId } ) ,
Command . create ( { partner _id : partnerId } ) ,
] ,
} ) ;
const { env , openDiscuss } = await start ( {
async mockRPC ( route , args ) {
if ( args . method === "channel_fetched" && args . args [ 0 ] === channelId ) {
throw new Error (
"'channel_fetched' RPC must not be called for created channel as message is directly seen"
) ;
} else if ( route === "/discuss/channel/set_last_seen_message" ) {
assert . strictEqual ( args . channel _id , channelId ) ;
assert . step ( "rpc:set_last_seen_message" ) ;
}
} ,
} ) ;
openDiscuss ( channelId ) ;
await focus ( ".o-mail-Composer-input" ) ;
// simulate receiving a message
await pyEnv . withUser ( userId , ( ) =>
env . services . rpc ( "/mail/message/post" , {
post _data : { body : "<p>Some new message</p>" , message _type : "comment" } ,
thread _id : channelId ,
thread _model : "discuss.channel" ,
} )
) ;
await contains ( ".o-mail-Message" ) ;
assert . verifySteps ( [ "rpc:set_last_seen_message" ] ) ;
}
) ;
QUnit . test (
"should scroll to bottom on receiving new message if the list is initially scrolled to bottom (asc order)" ,
async ( ) => {
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 ( {
channel _member _ids : [
Command . create ( { partner _id : pyEnv . currentPartnerId } ) ,
Command . create ( { partner _id : partnerId } ) ,
] ,
} ) ;
for ( let i = 0 ; i <= 10 ; i ++ ) {
pyEnv [ "mail.message" ] . create ( {
body : "not empty" ,
model : "discuss.channel" ,
res _id : channelId ,
} ) ;
}
const { env } = await start ( ) ;
await click ( ".o_menu_systray i[aria-label='Messages']" ) ;
await click ( ".o-mail-NotificationItem" ) ;
await contains ( ".o-mail-Message" , { count : 11 } ) ;
await contains ( ".o-mail-Thread" , { scroll : "bottom" } ) ;
// simulate receiving a message
pyEnv . withUser ( userId , ( ) =>
env . services . rpc ( "/mail/message/post" , {
post _data : { body : "hello" , message _type : "comment" } ,
thread _id : channelId ,
thread _model : "discuss.channel" ,
} )
) ;
await contains ( ".o-mail-Message" , { count : 12 } ) ;
await contains ( ".o-mail-Thread" , { scroll : "bottom" } ) ;
}
) ;
QUnit . test (
"should not scroll on receiving new message if the list is initially scrolled anywhere else than bottom (asc order)" ,
async ( ) => {
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 ( {
channel _member _ids : [
Command . create ( { partner _id : pyEnv . currentPartnerId } ) ,
Command . create ( { partner _id : partnerId } ) ,
] ,
} ) ;
for ( let i = 0 ; i <= 10 ; i ++ ) {
pyEnv [ "mail.message" ] . create ( {
body : "not empty" ,
model : "discuss.channel" ,
res _id : channelId ,
} ) ;
}
const { env } = await start ( ) ;
await click ( ".o_menu_systray i[aria-label='Messages']" ) ;
await click ( ".o-mail-NotificationItem" ) ;
await contains ( ".o-mail-Message" , { count : 11 } ) ;
await contains ( ".o-mail-Thread" , { scroll : "bottom" } ) ;
await scroll ( ".o-mail-Thread" , 0 ) ;
// simulate receiving a message
pyEnv . withUser ( userId , ( ) =>
env . services . rpc ( "/mail/message/post" , {
post _data : { body : "hello" , message _type : "comment" } ,
thread _id : channelId ,
thread _model : "discuss.channel" ,
} )
) ;
await contains ( ".o-mail-Message" , { count : 12 } ) ;
await contains ( ".o-mail-ChatWindow .o-mail-Thread" , { scroll : 0 } ) ;
}
) ;
QUnit . test ( "show empty placeholder when thread contains no message" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "general" } ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await contains ( ".o-mail-Thread" , { text : "There are no messages in this conversation." } ) ;
await contains ( ".o-mail-Message" , { count : 0 } ) ;
} ) ;
QUnit . test ( "show empty placeholder when thread contains only empty messages" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "General" } ) ;
pyEnv [ "mail.message" ] . create ( { model : "discuss.channel" , res _id : channelId } ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await contains ( ".o-mail-Thread" , { text : "There are no messages in this conversation." } ) ;
await contains ( ".o-mail-Message" , { count : 0 } ) ;
} ) ;
QUnit . test (
"message list with a full page of empty messages should load more messages until there are some non-empty" ,
async ( ) => {
// Technical assumptions :
// - /discuss/channel/messages fetching exactly 30 messages,
// - empty messages not being displayed
// - auto-load more being triggered on scroll, not automatically when the 30 first messages are empty
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "General" } ) ;
for ( let i = 0 ; i < 50 ; i ++ ) {
pyEnv [ "mail.message" ] . create ( {
body : "not empty" ,
model : "discuss.channel" ,
res _id : channelId ,
} ) ;
}
for ( let i = 0 ; i < 50 ; i ++ ) {
pyEnv [ "mail.message" ] . create ( { model : "discuss.channel" , res _id : channelId } ) ;
}
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
// initial load: +30 empty ; (auto) load more: +20 empty +10 non-empty
await contains ( ".o-mail-Message" , { count : 10 } ) ;
await contains ( "button" , { text : "Load More" } ) ; // still 40 non-empty
}
) ;
QUnit . test ( "no new messages separator on posting message (some message history)" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
channel _member _ids : [
Command . create ( { message _unread _counter : 0 , partner _id : pyEnv . currentPartnerId } ) ,
] ,
channel _type : "channel" ,
name : "General" ,
} ) ;
const messageId = pyEnv [ "mail.message" ] . create ( {
body : "first message" ,
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 : messageId } ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await contains ( ".o-mail-Message" ) ;
await contains ( ".o-mail-Thread-newMessage hr + span" , { count : 0 , text : "New messages" } ) ;
await insertText ( ".o-mail-Composer-input" , "hey!" ) ;
await click ( ".o-mail-Composer-send:enabled" ) ;
await contains ( ".o-mail-Message" , { count : 2 } ) ;
await contains ( ".o-mail-Thread-newMessage hr + span" , { count : 0 , text : "New messages" } ) ;
} ) ;
QUnit . test ( "new messages separator on receiving new message [REQUIRE FOCUS]" , async ( ) => {
patchWithCleanup ( transitionConfig , { disabled : true } ) ;
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 ( {
channel _member _ids : [
Command . create ( { message _unread _counter : 0 , partner _id : pyEnv . currentPartnerId } ) ,
Command . create ( { partner _id : partnerId } ) ,
] ,
channel _type : "channel" ,
name : "General" ,
} ) ;
const messageId = pyEnv [ "mail.message" ] . create ( {
body : "blah" ,
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 : messageId } ) ;
const { env , openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await contains ( ".o-mail-Message" ) ;
await contains ( ".o-mail-Thread-newMessage hr + span" , { count : 0 , text : "New messages" } ) ;
$ ( ".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 : 2 } ) ;
await contains ( ".o-mail-Thread-newMessage hr + span" , { text : "New messages" } ) ;
await contains ( ".o-mail-Thread-newMessage ~ .o-mail-Message" , { text : "hu" } ) ;
await focus ( ".o-mail-Composer-input" ) ;
await nextTick ( ) ;
await contains ( ".o-mail-Thread-newMessage hr + span" , { count : 0 , text : "New messages" } ) ;
} ) ;
QUnit . test ( "no new messages separator on posting message (no message history)" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
channel _member _ids : [
Command . create ( { message _unread _counter : 0 , partner _id : pyEnv . currentPartnerId } ) ,
] ,
channel _type : "channel" ,
name : "General" ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await contains ( ".o-mail-Composer-input" ) ;
await contains ( ".o-mail-Message" , { count : 0 } ) ;
await contains ( ".o-mail-Thread-newMessage hr + span" , { count : 0 , text : "New messages" } ) ;
await insertText ( ".o-mail-Composer-input" , "hey!" ) ;
await click ( ".o-mail-Composer-send:enabled" ) ;
await contains ( ".o-mail-Message" ) ;
await contains ( ".o-mail-Thread-newMessage hr + span" , { count : 0 , text : "New messages" } ) ;
} ) ;
QUnit . test ( "Mention a partner with special character (e.g. apostrophe ')" , async ( ) => {
const pyEnv = await startServer ( ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( {
email : "usatyi@example.com" ,
name : "Pynya's spokesman" ,
} ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
name : "test" ,
channel _member _ids : [
Command . create ( { partner _id : pyEnv . currentPartnerId } ) ,
Command . create ( { partner _id : partnerId } ) ,
] ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await insertText ( ".o-mail-Composer-input" , "@" ) ;
await insertText ( ".o-mail-Composer-input" , "Pyn" ) ;
await click ( ".o-mail-Composer-suggestion" ) ;
await contains ( ".o-mail-Composer-input" , { value : "@Pynya's spokesman " } ) ;
await click ( ".o-mail-Composer-send:enabled" ) ;
await contains (
` .o-mail-Message-body .o_mail_redirect[data-oe-id=" ${ partnerId } "][data-oe-model="res.partner"] ` ,
{ text : "@Pynya's spokesman" }
) ;
} ) ;
QUnit . test ( "mention 2 different partners that have the same name" , async ( ) => {
const pyEnv = await startServer ( ) ;
const [ partnerId _1 , partnerId _2 ] = pyEnv [ "res.partner" ] . create ( [
{
email : "partner1@example.com" ,
name : "TestPartner" ,
} ,
{
email : "partner2@example.com" ,
name : "TestPartner" ,
} ,
] ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
name : "test" ,
channel _member _ids : [
Command . create ( { partner _id : pyEnv . currentPartnerId } ) ,
Command . create ( { partner _id : partnerId _1 } ) ,
Command . create ( { partner _id : partnerId _2 } ) ,
] ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await insertText ( ".o-mail-Composer-input" , "@Te" ) ;
await click ( ":nth-child(1 of .o-mail-Composer-suggestion" ) ;
await contains ( ".o-mail-Composer-input" , { value : "@TestPartner " } ) ;
await insertText ( ".o-mail-Composer-input" , "@Te" ) ;
await click ( ":nth-child(2 of .o-mail-Composer-suggestion" ) ;
await contains ( ".o-mail-Composer-input" , { value : "@TestPartner @TestPartner " } ) ;
await click ( ".o-mail-Composer-send:enabled" ) ;
await contains (
` .o-mail-Message-body .o_mail_redirect[data-oe-id=" ${ partnerId _1 } "][data-oe-model="res.partner"] ` ,
{ text : "@TestPartner" }
) ;
await contains (
` .o-mail-Message-body .o_mail_redirect[data-oe-id=" ${ partnerId _2 } "][data-oe-model="res.partner"] ` ,
{ text : "@TestPartner" }
) ;
} ) ;
QUnit . test ( "mention a channel on a second line when the first line contains #" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "General good" } ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await insertText ( ".o-mail-Composer-input" , "#blabla\n#" ) ;
await click ( ".o-mail-Composer-suggestion" ) ;
await contains ( ".o-mail-Composer-input" , { value : "#blabla\n#General good " } ) ;
await click ( ".o-mail-Composer-send:enabled" ) ;
await contains ( ".o-mail-Message-body .o_channel_redirect" , { text : "#General good" } ) ;
} ) ;
QUnit . test (
"mention a channel when replacing the space after the mention by another char" ,
async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "General good" } ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await insertText ( ".o-mail-Composer-input" , "#" ) ;
await click ( ".o-mail-Composer-suggestion" ) ;
await contains ( ".o-mail-Composer-input" , { value : "#General good " } ) ;
const text = $ ( ".o-mail-Composer-input" ) . val ( ) ;
$ ( ".o-mail-Composer-input" ) . val ( text . slice ( 0 , - 1 ) ) ;
await insertText ( ".o-mail-Composer-input" , ", test" ) ;
await click ( ".o-mail-Composer-send:enabled" ) ;
await contains ( ".o-mail-Message-body .o_channel_redirect" , { text : "#General good" } ) ;
}
) ;
QUnit . test ( "mention 2 different channels that have the same name" , async ( ) => {
const pyEnv = await startServer ( ) ;
const [ channelId _1 , channelId _2 ] = pyEnv [ "discuss.channel" ] . create ( [
{
channel _type : "channel" ,
group _public _id : false ,
name : "my channel" ,
} ,
{
channel _type : "channel" ,
name : "my channel" ,
} ,
] ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId _1 ) ;
await insertText ( ".o-mail-Composer-input" , "#m" ) ;
await click ( ":nth-child(1 of .o-mail-Composer-suggestion)" ) ;
await contains ( ".o-mail-Composer-input" , { value : "#my channel " } ) ;
await insertText ( ".o-mail-Composer-input" , "#m" ) ;
await click ( ":nth-child(2 of .o-mail-Composer-suggestion" ) ;
await contains ( ".o-mail-Composer-input" , { value : "#my channel #my channel " } ) ;
await click ( ".o-mail-Composer-send:enabled" ) ;
await contains (
` .o-mail-Message-body .o_channel_redirect[data-oe-id=" ${ channelId _1 } "][data-oe-model="discuss.channel"] ` ,
{ text : "#my channel" }
) ;
await contains (
` .o-mail-Message-body .o_channel_redirect[data-oe-id=" ${ channelId _2 } "][data-oe-model="discuss.channel"] ` ,
{ text : "#my channel" }
) ;
} ) ;
QUnit . test (
"Post a message containing an email address followed by a mention on another line" ,
async ( ) => {
const pyEnv = await startServer ( ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( {
email : "testpartner@odoo.com" ,
name : "TestPartner" ,
} ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
name : "test" ,
channel _member _ids : [
Command . create ( { partner _id : pyEnv . currentPartnerId } ) ,
Command . create ( { partner _id : partnerId } ) ,
] ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await insertText ( ".o-mail-Composer-input" , "email@odoo.com\n@Te" ) ;
await click ( ".o-mail-Composer-suggestion" ) ;
await contains ( ".o-mail-Composer-input" , { value : "email@odoo.com\n@TestPartner " } ) ;
await click ( ".o-mail-Composer-send:enabled" ) ;
await contains (
` .o-mail-Message-body .o_mail_redirect[data-oe-id=" ${ partnerId } "][data-oe-model="res.partner"] ` ,
{ text : "@TestPartner" }
) ;
}
) ;
QUnit . test ( "basic rendering of canceled notification" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "test" } ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( { name : "Someone" } ) ;
const messageId = pyEnv [ "mail.message" ] . create ( {
body : "not empty" ,
message _type : "email" ,
model : "discuss.channel" ,
res _id : channelId ,
} ) ;
pyEnv [ "mail.notification" ] . create ( {
failure _type : "SMTP" ,
mail _message _id : messageId ,
notification _status : "canceled" ,
notification _type : "email" ,
res _partner _id : partnerId ,
} ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await contains ( ".o-mail-Message-notification .fa-envelope-o" ) ;
await click ( ".o-mail-Message-notification" ) ;
await contains ( ".o-mail-MessageNotificationPopover" ) ;
await contains ( ".o-mail-MessageNotificationPopover .fa-trash-o" ) ;
await contains ( ".o-mail-MessageNotificationPopover" , { text : "Someone" } ) ;
} ) ;
QUnit . test (
"first unseen message should be directly preceded by the new message separator if there is a transient message just before it while composer is not focused [REQUIRE FOCUS]" ,
async ( ) => {
// The goal of removing the focus is to ensure the thread is not marked as seen automatically.
// Indeed that would trigger set_last_seen_message no matter what, which is already covered by other tests.
// The goal of this test is to cover the conditions specific to transient messages,
// and the conditions from focus would otherwise shadow them.
const pyEnv = await startServer ( ) ;
// Needed partner & user to allow simulation of message reception
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 ( {
channel _type : "channel" ,
name : "General" ,
channel _member _ids : [
Command . create ( { partner _id : partnerId } ) ,
Command . create ( { partner _id : pyEnv . currentPartnerId } ) ,
] ,
} ) ;
pyEnv [ "mail.message" ] . create ( [
{
body : "not empty" ,
model : "discuss.channel" ,
res _id : channelId ,
} ,
] ) ;
const { openDiscuss , env } = await start ( ) ;
openDiscuss ( channelId ) ;
// send a command that leads to receiving a transient message
await insertText ( ".o-mail-Composer-input" , "/who" ) ;
await click ( ".o-mail-Composer-send:enabled" ) ;
await contains ( ".o-mail-Message" , { count : 2 } ) ;
// 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 : "test" , message _type : "comment" } ,
thread _id : channelId ,
thread _model : "discuss.channel" ,
} )
) ;
await contains ( ".o-mail-Message" , { count : 3 } ) ;
await contains ( ".o-mail-Thread-newMessage hr + span" , { text : "New messages" } ) ;
await contains ( ".o-mail-Message[aria-label='Note'] + .o-mail-Thread-newMessage" ) ;
}
) ;
QUnit . test (
"composer should be focused automatically after clicking on the send button" ,
async ( ) => {
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" ) ;
await contains ( ".o-mail-Composer-input:focus" ) ;
}
) ;
QUnit . test ( "chat window header should not have unread counter for non-channel thread" , async ( ) => {
const pyEnv = await startServer ( ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( { name : "test" } ) ;
const messageId = pyEnv [ "mail.message" ] . create ( {
author _id : partnerId ,
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 ,
} ) ;
await start ( ) ;
await click ( ".o_menu_systray i[aria-label='Messages']" ) ;
await click ( ".o-mail-NotificationItem" ) ;
await contains ( ".o-mail-ChatWindow-counter" , { count : 0 , text : "1" } ) ;
} ) ;
QUnit . test (
"[technical] opening a non-channel chat window should not call channel_fold" ,
async ( assert ) => {
// channel_fold should not be called when opening non-channels in chat
// window, because there is no server sync of fold state for them.
const pyEnv = await startServer ( ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( { name : "test" } ) ;
const messageId = pyEnv [ "mail.message" ] . create ( {
author _id : partnerId ,
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 ,
} ) ;
await start ( {
async mockRPC ( route , args ) {
if ( route . includes ( "channel_fold" ) ) {
const message =
"should not call channel_fold when opening a non-channel chat window" ;
assert . step ( message ) ;
console . error ( message ) ;
throw Error ( message ) ;
}
} ,
} ) ;
await click ( ".o_menu_systray i[aria-label='Messages']" ) ;
await contains ( ".o-mail-NotificationItem" ) ;
await contains ( ".o-mail-ChatWindow" , { count : 0 } ) ;
await click ( ".o-mail-NotificationItem" ) ;
await contains ( ".o-mail-ChatWindow" ) ;
}
) ;
QUnit . test ( "Thread messages are only loaded once" , async ( assert ) => {
const pyEnv = await startServer ( ) ;
const channelIds = pyEnv [ "discuss.channel" ] . create ( [ { name : "General" } , { name : "Sales" } ] ) ;
const { openDiscuss } = await start ( {
mockRPC ( route , args , originalRPC ) {
if ( route === "/discuss/channel/messages" ) {
assert . step ( ` load messages - ${ args [ "channel_id" ] } ` ) ;
}
return originalRPC ( route , args ) ;
} ,
} ) ;
pyEnv [ "mail.message" ] . create ( [
{
model : "discuss.channel" ,
res _id : channelIds [ 0 ] ,
body : "Message on channel1" ,
} ,
{
model : "discuss.channel" ,
res _id : channelIds [ 1 ] ,
body : "Message on channel2" ,
} ,
] ) ;
openDiscuss ( ) ;
await click ( ":nth-child(1 of .o-mail-DiscussSidebarChannel" ) ;
await contains ( ".o-mail-Message-content" , { text : "Message on channel1" } ) ;
await click ( ":nth-child(2 of .o-mail-DiscussSidebarChannel)" ) ;
await contains ( ".o-mail-Message-content" , { text : "Message on channel2" } ) ;
await click ( ":nth-child(1 of .o-mail-DiscussSidebarChannel)" ) ;
await contains ( ".o-mail-Message-content" , { text : "Message on channel1" } ) ;
assert . verifySteps ( [ ` load messages - ${ channelIds [ 0 ] } ` , ` load messages - ${ channelIds [ 1 ] } ` ] ) ;
} ) ;
QUnit . test (
"Opening thread with needaction messages should mark all messages of thread as read" ,
async ( assert ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "General" } ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( { name : "Demo" } ) ;
const { env , openDiscuss } = await start ( {
mockRPC ( route , args ) {
if ( args . model === "mail.message" && args . method === "mark_all_as_read" ) {
assert . step ( "mark-all-messages-as-read" ) ;
assert . deepEqual ( args . args [ 0 ] , [
[ "model" , "=" , "discuss.channel" ] ,
[ "res_id" , "=" , channelId ] ,
] ) ;
}
} ,
} ) ;
openDiscuss ( channelId ) ;
await contains ( ".o-mail-Composer-input" ) ;
await triggerEvents ( ".o-mail-Composer-input" , [ "blur" , "focusout" ] ) ;
await click ( "button" , { text : "Inbox" } ) ;
await contains ( "h4" , { text : "Congratulations, your inbox is empty" } ) ;
const messageId = pyEnv [ "mail.message" ] . create ( {
author _id : partnerId ,
body : "@Mitchel Admin" ,
needaction : true ,
model : "discuss.channel" ,
res _id : channelId ,
needaction _partner _ids : [ pyEnv . currentPartnerId ] ,
} ) ;
pyEnv [ "mail.notification" ] . create ( {
mail _message _id : messageId ,
notification _status : "sent" ,
notification _type : "inbox" ,
res _partner _id : pyEnv . currentPartnerId ,
} ) ;
// simulate receiving a new needaction message
const [ formattedMessage ] = await env . services . orm . call ( "mail.message" , "message_format" , [
[ messageId ] ,
] ) ;
pyEnv [ "bus.bus" ] . _sendone ( pyEnv . currentPartner , "mail.message/inbox" , formattedMessage ) ;
await contains ( "button" , { text : "Inbox" , contains : [ ".badge" , { text : "1" } ] } ) ;
await click ( "button" , { text : "General" } ) ;
await contains ( ".o-discuss-badge" , { count : 0 } ) ;
await contains ( "button" , { text : "Inbox" , contains : [ ".badge" , { count : 0 } ] } ) ;
assert . verifySteps ( [ "mark-all-messages-as-read" ] ) ;
}
) ;
QUnit . test (
"[technical] Opening thread without needaction messages should not mark all messages of thread as read" ,
async ( assert ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "General" } ) ;
const { env , openDiscuss } = await start ( {
mockRPC ( route , args ) {
if ( args . model === "mail.message" && args . method === "mark_all_as_read" ) {
assert . step ( "mark-all-messages-as-read" ) ;
}
} ,
} ) ;
openDiscuss ( channelId ) ;
await click ( "button" , { text : "Inbox" } ) ;
await env . services . rpc ( "/mail/message/post" , {
post _data : {
body : "Hello world!" ,
attachment _ids : [ ] ,
} ,
thread _id : channelId ,
thread _model : "discuss.channel" ,
} ) ;
await click ( "button" , { text : "General" } ) ;
await nextTick ( ) ;
assert . verifySteps ( [ ] ) ;
}
) ;
QUnit . test ( "can be marked as read while loading" , async function ( ) {
const pyEnv = await startServer ( ) ;
const partnerId = pyEnv [ "res.partner" ] . create ( { name : "Demo" } ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( {
channel _member _ids : [
Command . create ( { message _unread _counter : 1 , partner _id : pyEnv . currentPartnerId } ) ,
Command . create ( { partner _id : partnerId } ) ,
] ,
channel _type : "chat" ,
} ) ;
pyEnv [ "mail.message" ] . create ( {
author _id : partnerId ,
body : "<p>Test</p>" ,
model : "discuss.channel" ,
res _id : channelId ,
} ) ;
const loadDeferred = makeDeferred ( ) ;
const { openDiscuss } = await start ( {
async mockRPC ( route ) {
if ( route === "/discuss/channel/messages" ) {
await loadDeferred ;
}
} ,
} ) ;
openDiscuss ( undefined ) ;
await contains ( ".o-discuss-badge" , { text : "1" } ) ;
await click ( ".o-mail-DiscussSidebarChannel" , { text : "Demo" } ) ;
loadDeferred . resolve ( ) ;
await contains ( ".o-discuss-badge" , { count : 0 } ) ;
} ) ;
QUnit . test ( "New message separator not appearing after showing composer on thread" , async ( ) => {
const pyEnv = await startServer ( ) ;
pyEnv [ "mail.message" ] . create ( [
{
model : "res.partner" ,
res _id : pyEnv . currentPartnerId ,
body : "Message on partner" ,
} ,
{
model : "res.partner" ,
res _id : pyEnv . currentPartnerId ,
body : "Message on partner" ,
} ,
] ) ;
const { openFormView } = await start ( ) ;
openFormView ( "res.partner" , pyEnv . currentPartnerId ) ;
await contains ( "button" , { text : "Log note" } ) ;
await contains ( ".o-mail-Thread-newMessage" , { count : 0 } ) ;
await click ( "button" , { text : "Log note" } ) ;
await contains ( ".o-mail-Thread-newMessage" , { count : 0 } ) ;
} ) ;
QUnit . test ( "Transient messages are added at the end of the thread" , async ( ) => {
const pyEnv = await startServer ( ) ;
const channelId = pyEnv [ "discuss.channel" ] . create ( { name : "General" } ) ;
const { openDiscuss } = await start ( ) ;
openDiscuss ( channelId ) ;
await insertText ( ".o-mail-Composer-input" , "Dummy Message" ) ;
await click ( ".o-mail-Composer-send:enabled" ) ;
await contains ( ".o-mail-Message" ) ;
await insertText ( ".o-mail-Composer-input" , "/help" ) ;
await click ( ".o-mail-Composer-send:enabled" ) ;
await contains ( ".o-mail-Message" , { count : 2 } ) ;
await contains ( ":nth-child(1 of .o-mail-Message)" , { text : "Mitchell Admin" } ) ;
2024-05-08 11:31:09 +03:00
await contains ( ":nth-child(2 of .o-mail-Message)" , { text : "TalismanBot" } ) ;
2024-05-03 12:40:35 +03:00
} ) ;