2020-07-14 22:41:26 +02:00
/*global mock, converse */
2020-04-22 13:11:48 +02:00
2020-05-15 14:33:31 +02:00
const _ = converse . env . _ ;
const $pres = converse . env . $pres ;
const $iq = converse . env . $iq ;
const $msg = converse . env . $msg ;
const Model = converse . env . Model ;
const Strophe = converse . env . Strophe ;
const Promise = converse . env . Promise ;
const sizzle = converse . env . sizzle ;
const u = converse . env . utils ;
2020-04-22 13:11:48 +02:00
describe ( "Groupchats" , function ( ) {
describe ( "The \"rooms\" API" , function ( ) {
it ( "has a method 'close' which closes rooms by JID or all rooms when called with no arguments" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
await mock . openAndEnterChatRoom ( _converse , 'lounge@montague.lit' , 'romeo' ) ;
_converse . connection . IQ _stanzas = [ ] ;
await mock . openAndEnterChatRoom ( _converse , 'leisure@montague.lit' , 'romeo' ) ;
_converse . connection . IQ _stanzas = [ ] ;
await mock . openAndEnterChatRoom ( _converse , 'news@montague.lit' , 'romeo' ) ;
expect ( u . isVisible ( _converse . chatboxviews . get ( 'lounge@montague.lit' ) . el ) ) . toBeTruthy ( ) ;
expect ( u . isVisible ( _converse . chatboxviews . get ( 'leisure@montague.lit' ) . el ) ) . toBeTruthy ( ) ;
expect ( u . isVisible ( _converse . chatboxviews . get ( 'news@montague.lit' ) . el ) ) . toBeTruthy ( ) ;
await _converse . api . roomviews . close ( 'lounge@montague.lit' ) ;
expect ( _converse . chatboxviews . get ( 'lounge@montague.lit' ) ) . toBeUndefined ( ) ;
expect ( u . isVisible ( _converse . chatboxviews . get ( 'leisure@montague.lit' ) . el ) ) . toBeTruthy ( ) ;
expect ( u . isVisible ( _converse . chatboxviews . get ( 'news@montague.lit' ) . el ) ) . toBeTruthy ( ) ;
await _converse . api . roomviews . close ( [ 'leisure@montague.lit' , 'news@montague.lit' ] ) ;
expect ( _converse . chatboxviews . get ( 'lounge@montague.lit' ) ) . toBeUndefined ( ) ;
expect ( _converse . chatboxviews . get ( 'leisure@montague.lit' ) ) . toBeUndefined ( ) ;
expect ( _converse . chatboxviews . get ( 'news@montague.lit' ) ) . toBeUndefined ( ) ;
await mock . openAndEnterChatRoom ( _converse , 'lounge@montague.lit' , 'romeo' ) ;
await mock . openAndEnterChatRoom ( _converse , 'leisure@montague.lit' , 'romeo' ) ;
expect ( u . isVisible ( _converse . chatboxviews . get ( 'lounge@montague.lit' ) . el ) ) . toBeTruthy ( ) ;
expect ( u . isVisible ( _converse . chatboxviews . get ( 'leisure@montague.lit' ) . el ) ) . toBeTruthy ( ) ;
await _converse . api . roomviews . close ( ) ;
expect ( _converse . chatboxviews . get ( 'lounge@montague.lit' ) ) . toBeUndefined ( ) ;
expect ( _converse . chatboxviews . get ( 'leisure@montague.lit' ) ) . toBeUndefined ( ) ;
done ( ) ;
} ) ) ;
it ( "has a method 'get' which returns a wrapped groupchat (if it exists)" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
await mock . waitForRoster ( _converse , 'current' ) ;
await u . waitUntil ( ( ) => _converse . rosterview . el . querySelectorAll ( '.roster-group .group-toggle' ) . length , 300 ) ;
let muc _jid = 'chillout@montague.lit' ;
await mock . openAndEnterChatRoom ( _converse , muc _jid , 'romeo' ) ;
let room = await _converse . api . rooms . get ( muc _jid ) ;
expect ( room instanceof Object ) . toBeTruthy ( ) ;
let chatroomview = _converse . chatboxviews . get ( muc _jid ) ;
expect ( chatroomview . is _chatroom ) . toBeTruthy ( ) ;
expect ( u . isVisible ( chatroomview . el ) ) . toBeTruthy ( ) ;
await chatroomview . close ( ) ;
// Test with mixed case
muc _jid = 'Leisure@montague.lit' ;
await mock . openAndEnterChatRoom ( _converse , muc _jid , 'romeo' ) ;
room = await _converse . api . rooms . get ( muc _jid ) ;
expect ( room instanceof Object ) . toBeTruthy ( ) ;
chatroomview = _converse . chatboxviews . get ( muc _jid . toLowerCase ( ) ) ;
expect ( u . isVisible ( chatroomview . el ) ) . toBeTruthy ( ) ;
muc _jid = 'leisure@montague.lit' ;
room = await _converse . api . rooms . get ( muc _jid ) ;
expect ( room instanceof Object ) . toBeTruthy ( ) ;
chatroomview = _converse . chatboxviews . get ( muc _jid . toLowerCase ( ) ) ;
expect ( u . isVisible ( chatroomview . el ) ) . toBeTruthy ( ) ;
muc _jid = 'leiSure@montague.lit' ;
room = await _converse . api . rooms . get ( muc _jid ) ;
expect ( room instanceof Object ) . toBeTruthy ( ) ;
chatroomview = _converse . chatboxviews . get ( muc _jid . toLowerCase ( ) ) ;
expect ( u . isVisible ( chatroomview . el ) ) . toBeTruthy ( ) ;
chatroomview . close ( ) ;
// Non-existing room
muc _jid = 'chillout2@montague.lit' ;
room = await _converse . api . rooms . get ( muc _jid ) ;
expect ( room ) . toBe ( null ) ;
done ( ) ;
} ) ) ;
it ( "has a method 'open' which opens (optionally configures) and returns a wrapped chat box" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { } ,
async function ( done , _converse ) {
// Mock 'getDiscoInfo', otherwise the room won't be
// displayed as it waits first for the features to be returned
// (when it's a new room being created).
spyOn ( _converse . ChatRoom . prototype , 'getDiscoInfo' ) . and . callFake ( ( ) => Promise . resolve ( ) ) ;
let jid = 'lounge@montague.lit' ;
let chatroomview , IQ _id ;
await mock . openControlBox ( _converse ) ;
await mock . waitForRoster ( _converse , 'current' ) ;
await u . waitUntil ( ( ) => _converse . rosterview . el . querySelectorAll ( '.roster-group .group-toggle' ) . length ) ;
let room = await _converse . api . rooms . open ( jid ) ;
// Test on groupchat that's not yet open
expect ( room instanceof Model ) . toBeTruthy ( ) ;
chatroomview = _converse . chatboxviews . get ( jid ) ;
expect ( chatroomview . is _chatroom ) . toBeTruthy ( ) ;
await u . waitUntil ( ( ) => u . isVisible ( chatroomview . el ) ) ;
// Test again, now that the room exists.
room = await _converse . api . rooms . open ( jid ) ;
expect ( room instanceof Model ) . toBeTruthy ( ) ;
chatroomview = _converse . chatboxviews . get ( jid ) ;
expect ( chatroomview . is _chatroom ) . toBeTruthy ( ) ;
expect ( u . isVisible ( chatroomview . el ) ) . toBeTruthy ( ) ;
await chatroomview . close ( ) ;
// Test with mixed case in JID
jid = 'Leisure@montague.lit' ;
room = await _converse . api . rooms . open ( jid ) ;
expect ( room instanceof Model ) . toBeTruthy ( ) ;
chatroomview = _converse . chatboxviews . get ( jid . toLowerCase ( ) ) ;
await u . waitUntil ( ( ) => u . isVisible ( chatroomview . el ) ) ;
jid = 'leisure@montague.lit' ;
room = await _converse . api . rooms . open ( jid ) ;
expect ( room instanceof Model ) . toBeTruthy ( ) ;
chatroomview = _converse . chatboxviews . get ( jid . toLowerCase ( ) ) ;
await u . waitUntil ( ( ) => u . isVisible ( chatroomview . el ) ) ;
jid = 'leiSure@montague.lit' ;
room = await _converse . api . rooms . open ( jid ) ;
expect ( room instanceof Model ) . toBeTruthy ( ) ;
chatroomview = _converse . chatboxviews . get ( jid . toLowerCase ( ) ) ;
await u . waitUntil ( ( ) => u . isVisible ( chatroomview . el ) ) ;
chatroomview . close ( ) ;
_converse . muc _instant _rooms = false ;
const sendIQ = _converse . connection . sendIQ ;
spyOn ( _converse . connection , 'sendIQ' ) . and . callFake ( function ( iq , callback , errback ) {
IQ _id = sendIQ . bind ( this ) ( iq , callback , errback ) ;
} ) ;
// Test with configuration
room = await _converse . api . rooms . open ( 'room@conference.example.org' , {
'nick' : 'some1' ,
'auto_configure' : true ,
'roomconfig' : {
'getmemberlist' : [ 'moderator' , 'participant' ] ,
'changesubject' : false ,
'membersonly' : true ,
'persistentroom' : true ,
'publicroom' : true ,
'roomdesc' : 'Welcome to this groupchat' ,
'whois' : 'anyone'
}
} ) ;
expect ( room instanceof Model ) . toBeTruthy ( ) ;
chatroomview = _converse . chatboxviews . get ( 'room@conference.example.org' ) ;
2019-12-08 20:42:09 +01:00
2020-04-22 13:11:48 +02:00
// We pretend this is a new room, so no disco info is returned.
const features _stanza = $iq ( {
from : 'room@conference.example.org' ,
'id' : IQ _id ,
'to' : 'romeo@montague.lit/desktop' ,
'type' : 'error'
} ) . c ( 'error' , { 'type' : 'cancel' } )
. c ( 'item-not-found' , { 'xmlns' : "urn:ietf:params:xml:ns:xmpp-stanzas" } ) ;
_converse . connection . _dataRecv ( mock . createRequest ( features _stanza ) ) ;
/ * < p r e s e n c e x m l n s = " j a b b e r : c l i e n t " t o = " r o m e o @ m o n t a g u e . l i t / p d a " f r o m = " r o o m @ c o n f e r e n c e . e x a m p l e . o r g / y o " >
* < x xmlns = "http://jabber.org/protocol/muc#user" >
* < item affiliation = "owner" jid = "romeo@montague.lit/pda" role = "moderator" / >
* < status code = "110" / >
* < status code = "201" / >
* < / x >
* < / p r e s e n c e >
* /
const presence = $pres ( {
from : 'room@conference.example.org/some1' ,
to : 'romeo@montague.lit/pda'
} )
. c ( 'x' , { xmlns : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' , {
affiliation : 'owner' ,
jid : 'romeo@montague.lit/pda' ,
role : 'moderator'
} ) . up ( )
. c ( 'status' , { code : '110' } ) . up ( )
. c ( 'status' , { code : '201' } ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
expect ( _converse . connection . sendIQ ) . toHaveBeenCalled ( ) ;
const IQ _stanzas = _converse . connection . IQ _stanzas ;
const iq = IQ _stanzas . filter ( s => s . querySelector ( ` query[xmlns=" ${ Strophe . NS . MUC _OWNER } "] ` ) ) . pop ( ) ;
expect ( Strophe . serialize ( iq ) ) . toBe (
` <iq id=" ${ iq . getAttribute ( 'id' ) } " to="room@conference.example.org" type="get" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/muc#owner"/></iq> ` ) ;
const node = u . toStanza ( `
< iq xmlns = "jabber:client"
type = "result"
to = "romeo@montague.lit/pda"
from = "room@conference.example.org" id = "${iq.getAttribute('id')}" >
< query xmlns = "http://jabber.org/protocol/muc#owner" >
< x xmlns = "jabber:x:data" type = "form" >
< title > Configuration for room @ conference . example . org < / t i t l e >
< instructions > Complete and submit this form to configure the room . < / i n s t r u c t i o n s >
< field var = "FORM_TYPE" type = "hidden" >
< value > http : //jabber.org/protocol/muc#roomconfig</value>
< / f i e l d >
< field type = "text-single" var = "muc#roomconfig_roomname" label = "Name" >
< value > Room < / v a l u e >
< / f i e l d >
< field type = "text-single" var = "muc#roomconfig_roomdesc" label = "Description" > < value / > < / f i e l d >
< field type = "boolean" var = "muc#roomconfig_persistentroom" label = "Make Room Persistent?" / >
< field type = "boolean" var = "muc#roomconfig_publicroom" label = "Make Room Publicly Searchable?" > < value > 1 < / v a l u e > < / f i e l d >
< field type = "boolean" var = "muc#roomconfig_changesubject" label = "Allow Occupants to Change Subject?" / >
< field type = "list-single" var = "muc#roomconfig_whois" label = "Who May Discover Real JIDs?" > < option label = "Moderators Only" >
< value > moderators < / v a l u e > < / o p t i o n > < o p t i o n l a b e l = " A n y o n e " > < v a l u e > a n y o n e < / v a l u e > < / o p t i o n >
< / f i e l d >
< field label = "Roles and Affiliations that May Retrieve Member List"
type = "list-multi"
var = "muc#roomconfig_getmemberlist" >
< value > moderator < / v a l u e >
< value > participant < / v a l u e >
< value > visitor < / v a l u e >
< / f i e l d >
< field type = "text-private" var = "muc#roomconfig_roomsecret" label = "Password" > < value / > < / f i e l d >
< field type = "boolean" var = "muc#roomconfig_moderatedroom" label = "Make Room Moderated?" / >
< field type = "boolean" var = "muc#roomconfig_membersonly" label = "Make Room Members-Only?" / >
< field type = "text-single" var = "muc#roomconfig_historylength" label = "Maximum Number of History Messages Returned by Room" >
< value > 20 < / v a l u e > < / f i e l d >
< / x >
< / q u e r y >
< / i q > ` ) ;
spyOn ( chatroomview . model , 'sendConfiguration' ) . and . callThrough ( ) ;
_converse . connection . _dataRecv ( mock . createRequest ( node ) ) ;
await u . waitUntil ( ( ) => chatroomview . model . sendConfiguration . calls . count ( ) === 1 ) ;
const sent _stanza = IQ _stanzas . filter ( s => s . getAttribute ( 'type' ) === 'set' ) . pop ( ) ;
expect ( sizzle ( 'field[var="muc#roomconfig_roomname"] value' , sent _stanza ) . pop ( ) . textContent . trim ( ) ) . toBe ( 'Room' ) ;
expect ( sizzle ( 'field[var="muc#roomconfig_roomdesc"] value' , sent _stanza ) . pop ( ) . textContent . trim ( ) ) . toBe ( 'Welcome to this groupchat' ) ;
expect ( sizzle ( 'field[var="muc#roomconfig_persistentroom"] value' , sent _stanza ) . pop ( ) . textContent . trim ( ) ) . toBe ( '1' ) ;
expect ( sizzle ( 'field[var="muc#roomconfig_getmemberlist"] value' , sent _stanza ) . map ( e => e . textContent . trim ( ) ) . join ( ' ' ) ) . toBe ( 'moderator participant' ) ;
expect ( sizzle ( 'field[var="muc#roomconfig_publicroom"] value ' , sent _stanza ) . pop ( ) . textContent . trim ( ) ) . toBe ( '1' ) ;
expect ( sizzle ( 'field[var="muc#roomconfig_changesubject"] value' , sent _stanza ) . pop ( ) . textContent . trim ( ) ) . toBe ( '0' ) ;
expect ( sizzle ( 'field[var="muc#roomconfig_whois"] value ' , sent _stanza ) . pop ( ) . textContent . trim ( ) ) . toBe ( 'anyone' ) ;
expect ( sizzle ( 'field[var="muc#roomconfig_membersonly"] value' , sent _stanza ) . pop ( ) . textContent . trim ( ) ) . toBe ( '1' ) ;
expect ( sizzle ( 'field[var="muc#roomconfig_historylength"] value' , sent _stanza ) . pop ( ) . textContent . trim ( ) ) . toBe ( '20' ) ;
done ( ) ;
} ) ) ;
} ) ;
2019-12-08 20:42:09 +01:00
2020-04-22 13:11:48 +02:00
describe ( "An instant groupchat" , function ( ) {
2016-06-03 10:22:31 +02:00
2020-04-22 13:11:48 +02:00
it ( "will be created when muc_instant_rooms is set to true" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { } ,
async function ( done , _converse ) {
2017-07-12 09:55:43 +02:00
2020-04-22 13:11:48 +02:00
let IQ _stanzas = _converse . connection . IQ _stanzas ;
const muc _jid = 'lounge@montague.lit' ;
2020-09-09 15:29:12 +02:00
const nick = 'nicky' ;
2020-04-22 13:11:48 +02:00
await mock . openChatRoom ( _converse , 'lounge' , 'montague.lit' , 'romeo' ) ;
2016-06-03 10:22:31 +02:00
2020-04-22 13:11:48 +02:00
const disco _selector = ` iq[to=" ${ muc _jid } "] query[xmlns="http://jabber.org/protocol/disco#info"] ` ;
const stanza = await u . waitUntil ( ( ) => IQ _stanzas . filter ( iq => iq . querySelector ( disco _selector ) ) . pop ( ) ) ;
// We pretend this is a new room, so no disco info is returned.
const features _stanza = $iq ( {
'from' : 'lounge@montague.lit' ,
'id' : stanza . getAttribute ( 'id' ) ,
'to' : 'romeo@montague.lit/desktop' ,
'type' : 'error'
} ) . c ( 'error' , { 'type' : 'cancel' } )
. c ( 'item-not-found' , { 'xmlns' : "urn:ietf:params:xml:ns:xmpp-stanzas" } ) ;
_converse . connection . _dataRecv ( mock . createRequest ( features _stanza ) ) ;
const view = _converse . chatboxviews . get ( 'lounge@montague.lit' ) ;
spyOn ( view . model , 'join' ) . and . callThrough ( ) ;
await mock . waitForReservedNick ( _converse , muc _jid , '' ) ;
const input = await u . waitUntil ( ( ) => view . el . querySelector ( 'input[name="nick"]' ) , 1000 ) ;
expect ( view . model . session . get ( 'connection_status' ) ) . toBe ( converse . ROOMSTATUS . NICKNAME _REQUIRED ) ;
2020-09-09 15:29:12 +02:00
input . value = nick ;
2020-04-22 13:11:48 +02:00
view . el . querySelector ( 'input[type=submit]' ) . click ( ) ;
expect ( view . model . join ) . toHaveBeenCalled ( ) ;
2020-09-09 15:29:12 +02:00
2020-04-22 13:11:48 +02:00
_converse . connection . IQ _stanzas = [ ] ;
await mock . getRoomFeatures ( _converse , muc _jid ) ;
await u . waitUntil ( ( ) => view . model . session . get ( 'connection_status' ) === converse . ROOMSTATUS . CONNECTING ) ;
2020-09-09 15:29:12 +02:00
await mock . receiveOwnMUCPresence ( _converse , muc _jid , nick ) ;
2020-04-22 13:11:48 +02:00
// The user has just entered the room (because join was called)
// and receives their own presence from the server.
// See example 24:
// https://xmpp.org/extensions/xep-0045.html#enter-pres
//
/ * < p r e s e n c e x m l n s = " j a b b e r : c l i e n t " t o = " j o r d i e . l a n g e n @ c h a t . e x a m p l e . o r g / c o n v e r s e . j s - 1 1 6 5 9 2 9 9 " f r o m = " m y r o o m @ c o n f e r e n c e . c h a t . e x a m p l e . o r g / j c " >
* < x xmlns = "http://jabber.org/protocol/muc#user" >
* < item jid = "jordie.langen@chat.example.org/converse.js-11659299" affiliation = "owner" role = "moderator" / >
* < status code = "110" / >
* < status code = "201" / >
* < / x >
* < / p r e s e n c e >
* /
const presence = $pres ( {
to : 'romeo@montague.lit/orchard' ,
from : 'lounge@montague.lit/nicky' ,
id : '5025e055-036c-4bc5-a227-706e7e352053'
} ) . c ( 'x' ) . attrs ( { xmlns : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' ) . attrs ( {
affiliation : 'owner' ,
jid : 'romeo@montague.lit/orchard' ,
role : 'moderator'
} ) . up ( )
. c ( 'status' ) . attrs ( { code : '110' } ) . up ( )
. c ( 'status' ) . attrs ( { code : '201' } ) . nodeTree ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . model . session . get ( 'connection_status' ) === converse . ROOMSTATUS . ENTERED ) ;
await mock . returnMemberLists ( _converse , muc _jid ) ;
const num _info _msgs = await u . waitUntil ( ( ) => view . el . querySelectorAll ( '.chat-content .chat-info' ) . length ) ;
expect ( num _info _msgs ) . toBe ( 1 ) ;
const info _texts = Array . from ( view . el . querySelectorAll ( '.chat-content .chat-info' ) ) . map ( e => e . textContent . trim ( ) ) ;
expect ( info _texts [ 0 ] ) . toBe ( 'A new groupchat has been created' ) ;
const csntext = await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent ) ;
expect ( csntext . trim ( ) ) . toEqual ( "nicky has entered the groupchat" ) ;
// An instant room is created by saving the default configuratoin.
//
/ * < i q t o = " m y r o o m @ c o n f e r e n c e . c h a t . e x a m p l e . o r g " t y p e = " s e t " x m l n s = " j a b b e r : c l i e n t " i d = " 5 0 2 5 e 0 5 5 - 0 3 6 c - 4 b c 5 - a 2 2 7 - 7 0 6 e 7 e 3 5 2 0 5 3 : s e n d I Q " >
* < query xmlns = "http://jabber.org/protocol/muc#owner" > < x xmlns = "jabber:x:data" type = "submit" / > < / q u e r y >
* < / i q >
* /
const selector = ` query[xmlns=" ${ Strophe . NS . MUC _OWNER } "] ` ;
IQ _stanzas = _converse . connection . IQ _stanzas ;
const iq = await u . waitUntil ( ( ) => IQ _stanzas . filter ( s => s . querySelector ( selector ) ) . pop ( ) ) ;
expect ( Strophe . serialize ( iq ) ) . toBe (
` <iq id=" ${ iq . getAttribute ( 'id' ) } " to="lounge@montague.lit" type="set" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/muc#owner"><x type="submit" xmlns="jabber:x:data"/> ` +
` </query></iq> ` ) ;
done ( ) ;
} ) ) ;
} ) ;
2017-07-12 09:55:43 +02:00
2020-04-22 13:11:48 +02:00
describe ( "A Groupchat" , function ( ) {
2016-06-03 10:22:31 +02:00
2020-07-14 22:41:26 +02:00
it ( "maintains its state across reloads" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , {
'clear_messages_on_reconnection' : true ,
'loglevel' : 'debug' ,
'enable_smacks' : false
} , async function ( done , _converse ) {
const nick = 'romeo' ;
const sent _IQs = _converse . connection . IQ _stanzas ;
const muc _jid = 'lounge@montague.lit'
await mock . openAndEnterChatRoom ( _converse , muc _jid , nick , [ ] , [ ] ) ;
const view = _converse . chatboxviews . get ( muc _jid ) ;
let iq _get = await u . waitUntil ( ( ) => sent _IQs . filter ( iq => iq . querySelector ( ` iq query[xmlns=" ${ Strophe . NS . MAM } "] ` ) ) . pop ( ) ) ;
const first _msg _id = _converse . connection . getUniqueId ( ) ;
const last _msg _id = _converse . connection . getUniqueId ( ) ;
let message = u . toStanza (
` <message xmlns="jabber:client"
to = "romeo@montague.lit/orchard"
from = "${muc_jid}" >
< result xmlns = "urn:xmpp:mam:2" queryid = "${iq_get.querySelector('query').getAttribute('queryid')}" id = "${first_msg_id}" >
< forwarded xmlns = "urn:xmpp:forward:0" >
< delay xmlns = "urn:xmpp:delay" stamp = "2018-01-09T06:15:23Z" / >
< message from = "${muc_jid}/some1" type = "groupchat" >
< body > 1 st Message < / b o d y >
< / m e s s a g e >
< / f o r w a r d e d >
< / r e s u l t >
< / m e s s a g e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( message ) ) ;
message = u . toStanza (
` <message xmlns="jabber:client"
to = "romeo@montague.lit/orchard"
from = "${muc_jid}" >
< result xmlns = "urn:xmpp:mam:2" queryid = "${iq_get.querySelector('query').getAttribute('queryid')}" id = "${last_msg_id}" >
< forwarded xmlns = "urn:xmpp:forward:0" >
< delay xmlns = "urn:xmpp:delay" stamp = "2018-01-09T06:16:23Z" / >
< message from = "${muc_jid}/some1" type = "groupchat" >
< body > 2 nd Message < / b o d y >
< / m e s s a g e >
< / f o r w a r d e d >
< / r e s u l t >
< / m e s s a g e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( message ) ) ;
const result = u . toStanza (
` <iq type='result' id=' ${ iq _get . getAttribute ( 'id' ) } '>
< fin xmlns = 'urn:xmpp:mam:2' >
< set xmlns = 'http://jabber.org/protocol/rsm' >
< first index = '0' > $ { first _msg _id } < / f i r s t >
< last > $ { last _msg _id } < / l a s t >
< count > 2 < / c o u n t >
< / s e t >
< / f i n >
< / i q > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( result ) ) ;
await u . waitUntil ( ( ) => view . el . querySelectorAll ( '.chat-msg__text' ) . length === 2 ) ;
while ( sent _IQs . length ) { sent _IQs . pop ( ) ; } // Clear so that we don't match the older query
await _converse . api . connection . reconnect ( ) ;
await mock . getRoomFeatures ( _converse , muc _jid , [ ] ) ;
await u . waitUntil ( ( ) => ( view . model . session . get ( 'connection_status' ) === converse . ROOMSTATUS . CONNECTING ) ) ;
// The user has just entered the room (because join was called)
// and receives their own presence from the server.
// See example 24: https://xmpp.org/extensions/xep-0045.html#enter-pres
await mock . receiveOwnMUCPresence ( _converse , muc _jid , nick ) ;
message = u . toStanza ( `
< message xmlns = "jabber:client" type = "groupchat" id = "918172de-d5c5-4da4-b388-446ef4a05bec" to = "${_converse.jid}" xml : lang = "en" from = "${muc_jid}/juliet" >
< body > Wherefore art though ? < / b o d y >
< active xmlns = "http://jabber.org/protocol/chatstates" / >
< origin - id xmlns = "urn:xmpp:sid:0" id = "918172de-d5c5-4da4-b388-446ef4a05bec" / >
< stanza - id xmlns = "urn:xmpp:sid:0" id = "88cc9c93-a8f4-4dd5-b02a-d19855eb6303" by = "${muc_jid}" / >
< delay xmlns = "urn:xmpp:delay" stamp = "2020-07-14T17:46:47Z" from = "juliet@shakespeare.lit" / >
< / m e s s a g e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( message ) ) ;
message = u . toStanza ( `
< message xmlns = "jabber:client" type = "groupchat" id = "awQo6a-mi-Wa6NYh" to = "${_converse.jid}" from = "${muc_jid}/ews000" xml : lang = "en" >
< composing xmlns = "http://jabber.org/protocol/chatstates" / >
< no - store xmlns = "urn:xmpp:hints" / >
< no - permanent - store xmlns = "urn:xmpp:hints" / >
< delay xmlns = "urn:xmpp:delay" stamp = "2020-07-14T17:46:54Z" from = "juliet@shakespeare.lit" / >
< / m e s s a g e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( message ) ) ;
const affs = _converse . muc _fetch _members ;
const all _affiliations = Array . isArray ( affs ) ? affs : ( affs ? [ 'member' , 'admin' , 'owner' ] : [ ] ) ;
await mock . returnMemberLists ( _converse , muc _jid , [ ] , all _affiliations ) ;
iq _get = await u . waitUntil ( ( ) => sent _IQs . filter ( iq => iq . querySelector ( ` iq query[xmlns=" ${ Strophe . NS . MAM } "] ` ) ) . pop ( ) ) ;
expect ( Strophe . serialize ( iq _get ) ) . toBe (
` <iq id=" ${ iq _get . getAttribute ( 'id' ) } " to=" ${ muc _jid } " type="set" xmlns="jabber:client"> ` +
` <query queryid=" ${ iq _get . querySelector ( 'query' ) . getAttribute ( 'queryid' ) } " xmlns=" ${ Strophe . NS . MAM } "> ` +
` <x type="submit" xmlns="jabber:x:data"> ` +
` <field type="hidden" var="FORM_TYPE"><value>urn:xmpp:mam:2</value></field> ` +
` </x> ` +
2020-09-30 12:46:07 +02:00
` <set xmlns="http://jabber.org/protocol/rsm"><before></before><max>50</max></set> ` +
2020-07-14 22:41:26 +02:00
` </query> ` +
` </iq> ` ) ;
done ( ) ;
} ) ) ;
2020-04-22 13:11:48 +02:00
describe ( "upon being entered" , function ( ) {
2018-01-10 13:25:40 +01:00
2020-04-22 13:11:48 +02:00
it ( "will fetch the member list if muc_fetch_members is true" ,
2019-02-12 14:21:45 +01:00
mock . initConverse (
2020-04-22 13:11:48 +02:00
[ 'rosterGroupsFetched' ] , { 'muc_fetch_members' : true } ,
2018-11-09 11:55:34 +01:00
async function ( done , _converse ) {
2017-07-11 10:41:11 +02:00
2020-04-22 13:11:48 +02:00
let sent _IQs = _converse . connection . IQ _stanzas ;
2019-07-03 14:18:46 +02:00
const muc _jid = 'lounge@montague.lit' ;
2020-04-22 13:11:48 +02:00
await mock . openAndEnterChatRoom ( _converse , muc _jid , 'romeo' ) ;
let view = _converse . chatboxviews . get ( muc _jid ) ;
expect ( sent _IQs . filter ( iq => iq . querySelector ( 'query item[affiliation]' ) ) . length ) . toBe ( 3 ) ;
// Check in reverse order that we requested all three lists
const owner _iq = sent _IQs . pop ( ) ;
expect ( Strophe . serialize ( owner _iq ) ) . toBe (
` <iq id=" ${ owner _iq . getAttribute ( 'id' ) } " to=" ${ muc _jid } " type="get" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/muc#admin"><item affiliation="owner"/></query> ` +
` </iq> ` ) ;
2018-11-09 11:55:34 +01:00
2020-04-22 13:11:48 +02:00
const admin _iq = sent _IQs . pop ( ) ;
expect ( Strophe . serialize ( admin _iq ) ) . toBe (
` <iq id=" ${ admin _iq . getAttribute ( 'id' ) } " to=" ${ muc _jid } " type="get" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/muc#admin"><item affiliation="admin"/></query> ` +
` </iq> ` ) ;
2019-06-11 12:16:27 +02:00
2020-04-22 13:11:48 +02:00
const member _iq = sent _IQs . pop ( ) ;
expect ( Strophe . serialize ( member _iq ) ) . toBe (
` <iq id=" ${ member _iq . getAttribute ( 'id' ) } " to=" ${ muc _jid } " type="get" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/muc#admin"><item affiliation="member"/></query> ` +
` </iq> ` ) ;
view . close ( ) ;
2020-03-31 22:43:55 +02:00
2020-04-22 13:11:48 +02:00
_converse . connection . IQ _stanzas = [ ] ;
sent _IQs = _converse . connection . IQ _stanzas ;
_converse . muc _fetch _members = false ;
await mock . openAndEnterChatRoom ( _converse , 'orchard@montague.lit' , 'romeo' ) ;
view = _converse . chatboxviews . get ( 'orchard@montague.lit' ) ;
expect ( sent _IQs . filter ( iq => iq . querySelector ( 'query item[affiliation]' ) ) . length ) . toBe ( 0 ) ;
await view . close ( ) ;
2019-05-21 11:06:21 +02:00
2020-04-22 13:11:48 +02:00
_converse . connection . IQ _stanzas = [ ] ;
sent _IQs = _converse . connection . IQ _stanzas ;
_converse . muc _fetch _members = [ 'admin' ] ;
await mock . openAndEnterChatRoom ( _converse , 'courtyard@montague.lit' , 'romeo' ) ;
view = _converse . chatboxviews . get ( 'courtyard@montague.lit' ) ;
expect ( sent _IQs . filter ( iq => iq . querySelector ( 'query item[affiliation]' ) ) . length ) . toBe ( 1 ) ;
expect ( sent _IQs . filter ( iq => iq . querySelector ( 'query item[affiliation="admin"]' ) ) . length ) . toBe ( 1 ) ;
view . close ( ) ;
2019-07-10 09:47:13 +02:00
2020-04-22 13:11:48 +02:00
_converse . connection . IQ _stanzas = [ ] ;
sent _IQs = _converse . connection . IQ _stanzas ;
_converse . muc _fetch _members = [ 'owner' ] ;
await mock . openAndEnterChatRoom ( _converse , 'garden@montague.lit' , 'romeo' ) ;
view = _converse . chatboxviews . get ( 'garden@montague.lit' ) ;
expect ( sent _IQs . filter ( iq => iq . querySelector ( 'query item[affiliation]' ) ) . length ) . toBe ( 1 ) ;
expect ( sent _IQs . filter ( iq => iq . querySelector ( 'query item[affiliation="owner"]' ) ) . length ) . toBe ( 1 ) ;
view . close ( ) ;
2018-11-09 11:55:34 +01:00
done ( ) ;
2017-02-14 17:55:32 +01:00
} ) ) ;
2020-04-22 13:11:48 +02:00
describe ( "when fetching the member lists" , function ( ) {
2014-01-19 06:02:18 +01:00
2020-04-22 13:11:48 +02:00
it ( "gracefully handles being forbidden from fetching the lists for certain affiliations" ,
2019-07-26 13:32:21 +02:00
mock . initConverse (
2019-10-11 16:38:01 +02:00
[ 'rosterGroupsFetched' ] , { 'muc_fetch_members' : true } ,
2019-07-26 13:32:21 +02:00
async function ( done , _converse ) {
2020-04-22 13:11:48 +02:00
const sent _IQs = _converse . connection . IQ _stanzas ;
2019-08-08 15:12:18 +02:00
const muc _jid = 'lounge@montague.lit' ;
2020-04-22 13:11:48 +02:00
const features = [
'http://jabber.org/protocol/muc' ,
'jabber:iq:register' ,
'muc_hidden' ,
'muc_membersonly' ,
'muc_passwordprotected' ,
Strophe . NS . MAM ,
Strophe . NS . SID
] ;
const nick = 'romeo' ;
await _converse . api . rooms . open ( muc _jid ) ;
await mock . getRoomFeatures ( _converse , muc _jid , features ) ;
await mock . waitForReservedNick ( _converse , muc _jid , nick ) ;
mock . receiveOwnMUCPresence ( _converse , muc _jid , nick ) ;
const view = _converse . chatboxviews . get ( muc _jid ) ;
await u . waitUntil ( ( ) => ( view . model . session . get ( 'connection_status' ) === converse . ROOMSTATUS . ENTERED ) ) ;
2019-07-26 13:32:21 +02:00
2019-08-08 15:12:18 +02:00
// Check in reverse order that we requested all three lists
const owner _iq = sent _IQs . pop ( ) ;
expect ( Strophe . serialize ( owner _iq ) ) . toBe (
` <iq id=" ${ owner _iq . getAttribute ( 'id' ) } " to=" ${ muc _jid } " type="get" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/muc#admin"><item affiliation="owner"/></query> ` +
` </iq> ` ) ;
const admin _iq = sent _IQs . pop ( ) ;
expect ( Strophe . serialize ( admin _iq ) ) . toBe (
` <iq id=" ${ admin _iq . getAttribute ( 'id' ) } " to=" ${ muc _jid } " type="get" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/muc#admin"><item affiliation="admin"/></query> ` +
` </iq> ` ) ;
const member _iq = sent _IQs . pop ( ) ;
expect ( Strophe . serialize ( member _iq ) ) . toBe (
` <iq id=" ${ member _iq . getAttribute ( 'id' ) } " to=" ${ muc _jid } " type="get" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/muc#admin"><item affiliation="member"/></query> ` +
` </iq> ` ) ;
2020-04-07 11:30:43 +02:00
2020-04-22 13:11:48 +02:00
// It might be that the user is not allowed to fetch certain lists.
let err _stanza = u . toStanza (
` <iq xmlns="jabber:client" type="error" to=" ${ _converse . jid } " from=" ${ muc _jid } " id=" ${ admin _iq . getAttribute ( 'id' ) } ">
< error type = "auth" > < forbidden xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas" / > < / e r r o r >
< / i q > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( err _stanza ) ) ;
err _stanza = u . toStanza (
` <iq xmlns="jabber:client" type="error" to=" ${ _converse . jid } " from=" ${ muc _jid } " id=" ${ owner _iq . getAttribute ( 'id' ) } ">
< error type = "auth" > < forbidden xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas" / > < / e r r o r >
< / i q > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( err _stanza ) ) ;
// Now the service sends the member lists to the user
const member _list _stanza = $iq ( {
'from' : muc _jid ,
'id' : member _iq . getAttribute ( 'id' ) ,
'to' : 'romeo@montague.lit/orchard' ,
'type' : 'result'
} ) . c ( 'query' , { 'xmlns' : Strophe . NS . MUC _ADMIN } )
. c ( 'item' , {
'affiliation' : 'member' ,
'jid' : 'hag66@shakespeare.lit' ,
'nick' : 'thirdwitch' ,
'role' : 'participant'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( member _list _stanza ) ) ;
await u . waitUntil ( ( ) => view . model . occupants . length > 1 ) ;
expect ( view . model . occupants . length ) . toBe ( 2 ) ;
// The existing owner occupant should not have their
// affiliation removed due to the owner list
// not being returned (forbidden err).
expect ( view . model . occupants . findWhere ( { 'jid' : _converse . bare _jid } ) . get ( 'affiliation' ) ) . toBe ( 'owner' ) ;
expect ( view . model . occupants . findWhere ( { 'jid' : 'hag66@shakespeare.lit' } ) . get ( 'affiliation' ) ) . toBe ( 'member' ) ;
2020-04-07 11:30:43 +02:00
done ( ) ;
} ) ) ;
2020-04-06 14:27:44 +02:00
} ) ;
2020-04-22 13:11:48 +02:00
} ) ;
2020-04-06 14:27:44 +02:00
2020-04-22 13:11:48 +02:00
describe ( "topic" , function ( ) {
2020-04-06 14:27:44 +02:00
2020-04-22 13:11:48 +02:00
it ( "is shown the header" ,
2019-05-16 08:24:25 +02:00
mock . initConverse (
2020-04-22 13:11:48 +02:00
[ 'rosterGroupsFetched' ] , { } ,
2019-05-16 08:24:25 +02:00
async function ( done , _converse ) {
2020-04-22 13:11:48 +02:00
await mock . openAndEnterChatRoom ( _converse , 'jdev@conference.jabber.org' , 'jc' ) ;
const text = 'Jabber/XMPP Development | RFCs and Extensions: https://xmpp.org/ | Protocol and XSF discussions: xsf@muc.xmpp.org' ;
let stanza = u . toStanza ( `
< message xmlns = "jabber:client" to = "${_converse.jid}" type = "groupchat" from = "jdev@conference.jabber.org/ralphm" >
< subject > $ { text } < / s u b j e c t >
< delay xmlns = "urn:xmpp:delay" stamp = "2014-02-04T09:35:39Z" from = "jdev@conference.jabber.org" / >
< x xmlns = "jabber:x:delay" stamp = "20140204T09:35:39" from = "jdev@conference.jabber.org" / >
< / m e s s a g e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( stanza ) ) ;
const view = _converse . chatboxviews . get ( 'jdev@conference.jabber.org' ) ;
await new Promise ( resolve => view . model . once ( 'change:subject' , resolve ) ) ;
2020-04-24 17:33:32 +02:00
const head _desc = await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-head__desc' ) , 1000 ) ;
2020-04-22 13:11:48 +02:00
expect ( head _desc ? . textContent . trim ( ) ) . toBe ( text ) ;
2019-05-16 08:24:25 +02:00
2020-04-22 13:11:48 +02:00
stanza = u . toStanza (
` <message xmlns="jabber:client" to=" ${ _converse . jid } " type="groupchat" from="jdev@conference.jabber.org/ralphm">
< subject > This is a message subject < / s u b j e c t >
< body > This is a message < / b o d y >
< / m e s s a g e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( stanza ) ) ;
2020-05-15 14:33:31 +02:00
await new Promise ( resolve => view . model . messages . once ( 'rendered' , resolve ) ) ;
2020-04-22 13:11:48 +02:00
expect ( sizzle ( '.chat-msg__subject' , view . el ) . length ) . toBe ( 1 ) ;
expect ( sizzle ( '.chat-msg__subject' , view . el ) . pop ( ) . textContent . trim ( ) ) . toBe ( 'This is a message subject' ) ;
expect ( sizzle ( '.chat-msg__text' ) . length ) . toBe ( 1 ) ;
expect ( sizzle ( '.chat-msg__text' ) . pop ( ) . textContent . trim ( ) ) . toBe ( 'This is a message' ) ;
expect ( view . el . querySelector ( '.chat-head__desc' ) . textContent . trim ( ) ) . toBe ( text ) ;
done ( ) ;
2019-05-16 08:24:25 +02:00
} ) ) ;
2020-04-22 13:11:48 +02:00
it ( "can be toggled by the user" ,
2019-02-12 14:21:45 +01:00
mock . initConverse (
2020-01-21 12:45:34 +01:00
[ 'rosterGroupsFetched' ] , { } ,
2018-10-13 23:25:01 +02:00
async function ( done , _converse ) {
2018-10-13 20:09:01 +02:00
2020-04-22 13:11:48 +02:00
await mock . openAndEnterChatRoom ( _converse , 'jdev@conference.jabber.org' , 'jc' ) ;
const text = 'Jabber/XMPP Development | RFCs and Extensions: https://xmpp.org/ | Protocol and XSF discussions: xsf@muc.xmpp.org' ;
let stanza = u . toStanza ( `
< message xmlns = "jabber:client" to = "${_converse.jid}" type = "groupchat" from = "jdev@conference.jabber.org/ralphm" >
< subject > $ { text } < / s u b j e c t >
< delay xmlns = "urn:xmpp:delay" stamp = "2014-02-04T09:35:39Z" from = "jdev@conference.jabber.org" / >
< x xmlns = "jabber:x:delay" stamp = "20140204T09:35:39" from = "jdev@conference.jabber.org" / >
< / m e s s a g e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( stanza ) ) ;
const view = _converse . chatboxviews . get ( 'jdev@conference.jabber.org' ) ;
await new Promise ( resolve => view . model . once ( 'change:subject' , resolve ) ) ;
2018-10-13 23:25:01 +02:00
2020-04-22 13:11:48 +02:00
const head _desc = await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-head__desc' ) ) ;
expect ( head _desc ? . textContent . trim ( ) ) . toBe ( text ) ;
stanza = u . toStanza (
` <message xmlns="jabber:client" to=" ${ _converse . jid } " type="groupchat" from="jdev@conference.jabber.org/ralphm">
< subject > This is a message subject < / s u b j e c t >
< body > This is a message < / b o d y >
< / m e s s a g e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( stanza ) ) ;
2020-05-15 14:33:31 +02:00
await new Promise ( resolve => view . model . messages . once ( 'rendered' , resolve ) ) ;
2020-04-22 13:11:48 +02:00
expect ( sizzle ( '.chat-msg__subject' , view . el ) . length ) . toBe ( 1 ) ;
expect ( sizzle ( '.chat-msg__subject' , view . el ) . pop ( ) . textContent . trim ( ) ) . toBe ( 'This is a message subject' ) ;
expect ( sizzle ( '.chat-msg__text' ) . length ) . toBe ( 1 ) ;
expect ( sizzle ( '.chat-msg__text' ) . pop ( ) . textContent . trim ( ) ) . toBe ( 'This is a message' ) ;
const topic _el = view . el . querySelector ( '.chat-head__desc' ) ;
expect ( topic _el . textContent . trim ( ) ) . toBe ( text ) ;
expect ( u . isVisible ( topic _el ) ) . toBe ( true ) ;
const toggle = view . el . querySelector ( '.hide-topic' ) ;
expect ( toggle . textContent ) . toBe ( 'Hide topic' ) ;
toggle . click ( ) ;
2020-08-24 11:24:36 +02:00
await u . waitUntil ( ( ) => view . el . querySelector ( '.hide-topic' ) . textContent === 'Show topic' ) ;
2020-04-22 13:11:48 +02:00
done ( ) ;
2018-10-13 20:09:01 +02:00
} ) ) ;
2020-04-22 13:11:48 +02:00
it ( "will always be shown when it's new" ,
2019-02-12 14:21:45 +01:00
mock . initConverse (
2020-04-22 13:11:48 +02:00
[ 'rosterGroupsFetched' ] , { } ,
2018-11-09 11:55:34 +01:00
async function ( done , _converse ) {
2018-10-13 14:40:05 +02:00
2020-04-22 13:11:48 +02:00
await mock . openAndEnterChatRoom ( _converse , 'jdev@conference.jabber.org' , 'jc' ) ;
const text = 'Jabber/XMPP Development | RFCs and Extensions: https://xmpp.org/ | Protocol and XSF discussions: xsf@muc.xmpp.org' ;
let stanza = u . toStanza ( `
< message xmlns = "jabber:client" to = "${_converse.jid}" type = "groupchat" from = "jdev@conference.jabber.org/ralphm" >
< subject > $ { text } < / s u b j e c t >
< / m e s s a g e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( stanza ) ) ;
const view = _converse . chatboxviews . get ( 'jdev@conference.jabber.org' ) ;
await new Promise ( resolve => view . model . once ( 'change:subject' , resolve ) ) ;
2019-08-14 09:40:45 +02:00
2020-04-22 13:11:48 +02:00
const head _desc = await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-head__desc' ) ) ;
expect ( head _desc ? . textContent . trim ( ) ) . toBe ( text ) ;
2018-10-13 14:40:05 +02:00
2020-04-22 13:11:48 +02:00
let topic _el = view . el . querySelector ( '.chat-head__desc' ) ;
expect ( topic _el . textContent . trim ( ) ) . toBe ( text ) ;
expect ( u . isVisible ( topic _el ) ) . toBe ( true ) ;
2020-03-31 22:43:55 +02:00
2020-04-22 13:11:48 +02:00
const toggle = view . el . querySelector ( '.hide-topic' ) ;
expect ( toggle . textContent ) . toBe ( 'Hide topic' ) ;
toggle . click ( ) ;
await u . waitUntil ( ( ) => ! u . isVisible ( topic _el ) ) ;
stanza = u . toStanza ( `
< message xmlns = "jabber:client" to = "${_converse.jid}" type = "groupchat" from = "jdev@conference.jabber.org/ralphm" >
< subject > Another topic < / s u b j e c t >
< / m e s s a g e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( stanza ) ) ;
await u . waitUntil ( ( ) => u . isVisible ( view . el . querySelector ( '.chat-head__desc' ) ) ) ;
topic _el = view . el . querySelector ( '.chat-head__desc' ) ;
expect ( topic _el . textContent . trim ( ) ) . toBe ( 'Another topic' ) ;
2018-11-09 11:55:34 +01:00
done ( ) ;
2018-10-13 14:40:05 +02:00
} ) ) ;
2020-04-22 13:11:48 +02:00
it ( "causes an info message to be shown when received in real-time" ,
2019-02-12 14:21:45 +01:00
mock . initConverse (
2020-04-22 13:11:48 +02:00
[ 'rosterGroupsFetched' ] , { } ,
2018-10-13 23:25:01 +02:00
async function ( done , _converse ) {
2020-04-22 13:11:48 +02:00
spyOn ( _converse . ChatRoom . prototype , 'handleSubjectChange' ) . and . callThrough ( ) ;
await mock . openAndEnterChatRoom ( _converse , 'jdev@conference.jabber.org' , 'romeo' ) ;
const view = _converse . chatboxviews . get ( 'jdev@conference.jabber.org' ) ;
_converse . connection . _dataRecv ( mock . createRequest ( u . toStanza ( `
< message xmlns = "jabber:client" to = "${_converse.jid}" type = "groupchat" from = "jdev@conference.jabber.org/ralphm" >
< subject > This is an older topic < / s u b j e c t >
< delay xmlns = "urn:xmpp:delay" stamp = "2014-02-04T09:35:39Z" from = "jdev@conference.jabber.org" / >
< x xmlns = "jabber:x:delay" stamp = "20140204T09:35:39" from = "jdev@conference.jabber.org" / >
< / m e s s a g e > ` ) ) ) ;
await u . waitUntil ( ( ) => view . model . handleSubjectChange . calls . count ( ) ) ;
expect ( sizzle ( '.chat-info__message' , view . el ) . length ) . toBe ( 0 ) ;
const desc = await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-head__desc' ) ) ;
expect ( desc . textContent . trim ( ) ) . toBe ( 'This is an older topic' ) ;
_converse . connection . _dataRecv ( mock . createRequest ( u . toStanza ( `
< message xmlns = "jabber:client" to = "${_converse.jid}" type = "groupchat" from = "jdev@conference.jabber.org/ralphm" >
< subject > This is a new topic < / s u b j e c t >
< / m e s s a g e > ` ) ) ) ;
await u . waitUntil ( ( ) => view . model . handleSubjectChange . calls . count ( ) === 2 ) ;
2020-05-15 14:33:31 +02:00
await u . waitUntil ( ( ) => sizzle ( '.chat-info__message' , view . el ) . pop ( ) ? . textContent . trim ( ) === 'Topic set by ralphm' ) ;
2020-04-22 13:11:48 +02:00
await u . waitUntil ( ( ) => desc . textContent . trim ( ) === 'This is a new topic' ) ;
// Doesn't show multiple subsequent topic change notifications
_converse . connection . _dataRecv ( mock . createRequest ( u . toStanza ( `
< message xmlns = "jabber:client" to = "${_converse.jid}" type = "groupchat" from = "jdev@conference.jabber.org/ralphm" >
< subject > Yet another topic < / s u b j e c t >
< / m e s s a g e > ` ) ) ) ;
await u . waitUntil ( ( ) => view . model . handleSubjectChange . calls . count ( ) === 3 ) ;
await u . waitUntil ( ( ) => desc . textContent . trim ( ) === 'Yet another topic' ) ;
expect ( sizzle ( '.chat-info__message' , view . el ) . length ) . toBe ( 1 ) ;
// Sow multiple subsequent topic change notification from someone else
_converse . connection . _dataRecv ( mock . createRequest ( u . toStanza ( `
< message xmlns = "jabber:client" to = "${_converse.jid}" type = "groupchat" from = "jdev@conference.jabber.org/some1" >
< subject > Some1 ' s topic < / s u b j e c t >
< / m e s s a g e > ` ) ) ) ;
await u . waitUntil ( ( ) => view . model . handleSubjectChange . calls . count ( ) === 4 ) ;
await u . waitUntil ( ( ) => desc . textContent . trim ( ) === "Some1's topic" ) ;
expect ( sizzle ( '.chat-info__message' , view . el ) . length ) . toBe ( 2 ) ;
2020-05-15 14:33:31 +02:00
const el = sizzle ( '.chat-info__message' , view . el ) . pop ( ) ;
2020-04-22 13:11:48 +02:00
expect ( el . textContent . trim ( ) ) . toBe ( 'Topic set by some1' ) ;
// Removes current topic
const stanza = u . toStanza (
` <message xmlns="jabber:client" to=" ${ _converse . jid } " type="groupchat" from="jdev@conference.jabber.org/some1">
< subject / >
< / m e s s a g e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( stanza ) ) ;
await u . waitUntil ( ( ) => view . model . handleSubjectChange . calls . count ( ) === 5 ) ;
2020-05-15 14:33:31 +02:00
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-head__desc' ) . textContent . replace ( /<!---->/g , '' ) === '' ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( 'converse-chat-message:last-child .chat-info' ) . textContent . trim ( ) === "Topic cleared by some1" ) ;
2020-04-22 13:11:48 +02:00
done ( ) ;
} ) ) ;
} ) ;
2020-03-31 22:43:55 +02:00
2018-10-13 23:25:01 +02:00
2020-04-22 13:11:48 +02:00
it ( "clears cached messages when it gets closed and clear_messages_on_reconnection is true" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { 'clear_messages_on_reconnection' : true } ,
async function ( done , _converse ) {
2020-02-19 16:46:56 +01:00
2020-04-22 13:11:48 +02:00
const muc _jid = 'lounge@montague.lit' ;
await mock . openAndEnterChatRoom ( _converse , muc _jid , 'romeo' ) ;
const view = _converse . chatboxviews . get ( muc _jid ) ;
const message = 'Hello world' ,
nick = mock . chatroom _names [ 0 ] ,
msg = $msg ( {
'from' : 'lounge@montague.lit/' + nick ,
'id' : u . getUniqueId ( ) ,
'to' : 'romeo@montague.lit' ,
'type' : 'groupchat'
} ) . c ( 'body' ) . t ( message ) . tree ( ) ;
2018-10-13 23:25:01 +02:00
2020-04-24 17:33:32 +02:00
await view . model . handleMessageStanza ( msg ) ;
2020-04-22 13:11:48 +02:00
spyOn ( view . model , 'clearMessages' ) . and . callThrough ( ) ;
await view . model . close ( ) ;
await u . waitUntil ( ( ) => view . model . clearMessages . calls . count ( ) ) ;
expect ( view . model . messages . length ) . toBe ( 0 ) ;
2020-05-15 14:33:31 +02:00
expect ( view . el . querySelector ( 'converse-chat-history' ) ) . toBe ( null ) ;
2020-04-22 13:11:48 +02:00
done ( )
} ) ) ;
it ( "is opened when an xmpp: URI is clicked inside another groupchat" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
await mock . waitForRoster ( _converse , 'current' ) ;
await mock . openAndEnterChatRoom ( _converse , 'lounge@montague.lit' , 'romeo' ) ;
const view = _converse . chatboxviews . get ( 'lounge@montague.lit' ) ;
if ( ! view . el . querySelectorAll ( '.chat-area' ) . length ) {
view . renderChatArea ( ) ;
}
expect ( _converse . chatboxes . length ) . toEqual ( 2 ) ;
const message = 'Please go to xmpp:coven@chat.shakespeare.lit?join' ,
nick = mock . chatroom _names [ 0 ] ,
msg = $msg ( {
'from' : 'lounge@montague.lit/' + nick ,
2020-02-07 12:24:02 +01:00
'id' : u . getUniqueId ( ) ,
2019-06-03 07:58:51 +02:00
'to' : 'romeo@montague.lit' ,
2018-10-13 23:25:01 +02:00
'type' : 'groupchat'
2020-04-22 13:11:48 +02:00
} ) . c ( 'body' ) . t ( message ) . tree ( ) ;
2018-10-13 23:25:01 +02:00
2020-04-24 17:33:32 +02:00
await view . model . handleMessageStanza ( msg ) ;
2020-04-22 13:11:48 +02:00
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-msg__text a' ) ) ;
view . el . querySelector ( '.chat-msg__text a' ) . click ( ) ;
await u . waitUntil ( ( ) => _converse . chatboxes . length === 3 )
expect ( _ . includes ( _converse . chatboxes . pluck ( 'id' ) , 'coven@chat.shakespeare.lit' ) ) . toBe ( true ) ;
done ( )
} ) ) ;
it ( "shows a notification if it's not anonymous" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { } ,
async function ( done , _converse ) {
const muc _jid = 'coven@chat.shakespeare.lit' ;
const nick = 'romeo' ;
await _converse . api . rooms . open ( muc _jid ) ;
await mock . getRoomFeatures ( _converse , muc _jid ) ;
await mock . waitForReservedNick ( _converse , muc _jid , nick ) ;
const view = _converse . chatboxviews . get ( muc _jid ) ;
/ * < p r e s e n c e t o = " r o m e o @ m o n t a g u e . l i t / _ c o n v e r s e . j s - 2 9 0 9 2 1 6 0 "
* from = "coven@chat.shakespeare.lit/some1" >
* < x xmlns = "http://jabber.org/protocol/muc#user" >
* < item affiliation = "owner" jid = "romeo@montague.lit/_converse.js-29092160" role = "moderator" / >
* < status code = "110" / >
* < status code = "100" / >
* < / x >
* < / p r e s e n c e > < / b o d y >
* /
const presence = $pres ( {
to : 'romeo@montague.lit/orchard' ,
from : 'coven@chat.shakespeare.lit/some1'
} ) . c ( 'x' , { xmlns : Strophe . NS . MUC _USER } )
. c ( 'item' , {
'affiliation' : 'owner' ,
'jid' : 'romeo@montague.lit/_converse.js-29092160' ,
'role' : 'moderator'
} ) . up ( )
. c ( 'status' , { code : '110' } ) . up ( )
. c ( 'status' , { code : '100' } ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
const num _info _msgs = await u . waitUntil ( ( ) => view . el . querySelectorAll ( '.chat-content .chat-info' ) . length ) ;
expect ( num _info _msgs ) . toBe ( 1 ) ;
expect ( sizzle ( 'div.chat-info' , view . content ) . pop ( ) . textContent . trim ( ) ) . toBe ( "This groupchat is not anonymous" ) ;
const csntext = await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent ) ;
expect ( csntext . trim ( ) ) . toEqual ( "some1 has entered the groupchat" ) ;
done ( ) ;
} ) ) ;
it ( "shows join/leave messages when users enter or exit a groupchat" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { 'muc_fetch_members' : false } ,
async function ( done , _converse ) {
const muc _jid = 'coven@chat.shakespeare.lit' ;
const nick = 'some1' ;
const room _creation _promise = await _converse . api . rooms . open ( muc _jid , { nick } ) ;
await mock . getRoomFeatures ( _converse , muc _jid ) ;
const sent _stanzas = _converse . connection . sent _stanzas ;
await u . waitUntil ( ( ) => sent _stanzas . filter ( iq => sizzle ( 'presence history' , iq ) . length ) . pop ( ) ) ;
const view = _converse . chatboxviews . get ( 'coven@chat.shakespeare.lit' ) ;
await _converse . api . waitUntil ( 'chatRoomViewInitialized' ) ;
/ * W e d o n ' t s h o w j o i n / l e a v e m e s s a g e s f o r e x i s t i n g o c c u p a n t s . W e
* know about them because we receive their presences before we
* receive our own .
* /
let presence = $pres ( {
to : 'romeo@montague.lit/_converse.js-29092160' ,
from : 'coven@chat.shakespeare.lit/oldguy'
} ) . c ( 'x' , { xmlns : Strophe . NS . MUC _USER } )
. c ( 'item' , {
'affiliation' : 'none' ,
'jid' : 'oldguy@montague.lit/_converse.js-290929789' ,
'role' : 'participant'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
/ * < p r e s e n c e t o = " r o m e o @ m o n t a g u e . l i t / _ c o n v e r s e . j s - 2 9 0 9 2 1 6 0 "
* from = "coven@chat.shakespeare.lit/some1" >
* < x xmlns = "http://jabber.org/protocol/muc#user" >
* < item affiliation = "owner" jid = "romeo@montague.lit/_converse.js-29092160" role = "moderator" / >
* < status code = "110" / >
* < / x >
* < / p r e s e n c e > < / b o d y >
* /
presence = $pres ( {
to : 'romeo@montague.lit/_converse.js-29092160' ,
from : 'coven@chat.shakespeare.lit/some1'
} ) . c ( 'x' , { xmlns : Strophe . NS . MUC _USER } )
. c ( 'item' , {
'affiliation' : 'owner' ,
'jid' : 'romeo@montague.lit/_converse.js-29092160' ,
'role' : 'moderator'
} ) . up ( )
. c ( 'status' , { code : '110' } ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
const csntext = await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent ) ;
expect ( csntext . trim ( ) ) . toEqual ( "some1 has entered the groupchat" ) ;
await room _creation _promise ;
await u . waitUntil ( ( ) => ( view . model . session . get ( 'connection_status' ) === converse . ROOMSTATUS . ENTERED ) ) ;
await view . model . messages . fetched ;
presence = $pres ( {
to : 'romeo@montague.lit/_converse.js-29092160' ,
from : 'coven@chat.shakespeare.lit/newguy'
} )
. c ( 'x' , { xmlns : Strophe . NS . MUC _USER } )
. c ( 'item' , {
'affiliation' : 'none' ,
'jid' : 'newguy@montague.lit/_converse.js-290929789' ,
'role' : 'participant'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ===
"some1 and newguy have entered the groupchat" ) ;
const msg = $msg ( {
'from' : 'coven@chat.shakespeare.lit/some1' ,
'id' : u . getUniqueId ( ) ,
'to' : 'romeo@montague.lit' ,
'type' : 'groupchat'
} ) . c ( 'body' ) . t ( 'hello world' ) . tree ( ) ;
_converse . connection . _dataRecv ( mock . createRequest ( msg ) ) ;
2020-05-15 14:33:31 +02:00
await new Promise ( resolve => view . model . messages . once ( 'rendered' , resolve ) ) ;
2020-04-22 13:11:48 +02:00
// Add another entrant, otherwise the above message will be
// collapsed if "newguy" leaves immediately again
presence = $pres ( {
to : 'romeo@montague.lit/_converse.js-29092160' ,
from : 'coven@chat.shakespeare.lit/newgirl'
} )
. c ( 'x' , { xmlns : Strophe . NS . MUC _USER } )
. c ( 'item' , {
'affiliation' : 'none' ,
'jid' : 'newgirl@montague.lit/_converse.js-213098781' ,
'role' : 'participant'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ===
"some1, newguy and newgirl have entered the groupchat" ) ;
// Don't show duplicate join messages
presence = $pres ( {
to : 'romeo@montague.lit/_converse.js-290918392' ,
from : 'coven@chat.shakespeare.lit/newguy'
} ) . c ( 'x' , { xmlns : Strophe . NS . MUC _USER } )
. c ( 'item' , {
'affiliation' : 'none' ,
'jid' : 'newguy@montague.lit/_converse.js-290929789' ,
'role' : 'participant'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
/ * < p r e s e n c e
* from = 'coven@chat.shakespeare.lit/thirdwitch'
* to = 'crone1@shakespeare.lit/desktop'
* type = 'unavailable' >
* < status > Disconnected : Replaced by new connection < / s t a t u s >
* < x xmlns = 'http://jabber.org/protocol/muc#user' >
* < item affiliation = 'member'
* jid = 'hag66@shakespeare.lit/pda'
* role = 'none' / >
* < / x >
* < / p r e s e n c e >
* /
presence = $pres ( {
to : 'romeo@montague.lit/_converse.js-29092160' ,
type : 'unavailable' ,
from : 'coven@chat.shakespeare.lit/newguy'
} )
. c ( 'status' , 'Disconnected: Replaced by new connection' ) . up ( )
. c ( 'x' , { xmlns : Strophe . NS . MUC _USER } )
2018-10-13 23:25:01 +02:00
. c ( 'item' , {
'affiliation' : 'none' ,
2020-04-22 13:11:48 +02:00
'jid' : 'newguy@montague.lit/_converse.js-290929789' ,
'role' : 'none'
2018-10-13 23:25:01 +02:00
} ) ;
2020-04-22 13:11:48 +02:00
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ===
2020-04-23 13:36:16 +02:00
"some1 and newgirl have entered the groupchat\nnewguy has left the groupchat" ) ;
2020-04-22 13:11:48 +02:00
// When the user immediately joins again, we collapse the
// multiple join/leave messages.
presence = $pres ( {
to : 'romeo@montague.lit/_converse.js-29092160' ,
from : 'coven@chat.shakespeare.lit/newguy'
} ) . c ( 'x' , { xmlns : Strophe . NS . MUC _USER } )
. c ( 'item' , {
'affiliation' : 'none' ,
'jid' : 'newguy@montague.lit/_converse.js-290929789' ,
'role' : 'participant'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
2018-10-13 23:25:01 +02:00
2020-04-22 13:11:48 +02:00
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ===
"some1, newgirl and newguy have entered the groupchat" ) ;
presence = $pres ( {
to : 'romeo@montague.lit/_converse.js-29092160' ,
type : 'unavailable' ,
from : 'coven@chat.shakespeare.lit/newguy'
} )
. c ( 'x' , { xmlns : Strophe . NS . MUC _USER } )
2018-10-13 23:25:01 +02:00
. c ( 'item' , {
'affiliation' : 'none' ,
2019-06-03 07:58:51 +02:00
'jid' : 'newguy@montague.lit/_converse.js-290929789' ,
2020-04-22 13:11:48 +02:00
'role' : 'none'
2018-10-13 23:25:01 +02:00
} ) ;
2020-04-22 13:11:48 +02:00
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ===
2020-04-23 13:36:16 +02:00
"some1 and newgirl have entered the groupchat\nnewguy has left the groupchat" ) ;
2017-12-06 22:10:54 +01:00
2020-04-22 13:11:48 +02:00
presence = $pres ( {
to : 'romeo@montague.lit/_converse.js-29092160' ,
from : 'coven@chat.shakespeare.lit/nomorenicks'
} )
. c ( 'x' , { xmlns : Strophe . NS . MUC _USER } )
. c ( 'item' , {
'affiliation' : 'none' ,
'jid' : 'nomorenicks@montague.lit/_converse.js-290929789' ,
'role' : 'participant'
2018-11-20 18:10:55 +01:00
} ) ;
2020-04-22 13:11:48 +02:00
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ===
2020-04-23 13:36:16 +02:00
"some1, newgirl and nomorenicks have entered the groupchat\nnewguy has left the groupchat" ) ;
2020-04-22 13:11:48 +02:00
presence = $pres ( {
to : 'romeo@montague.lit/_converse.js-290918392' ,
type : 'unavailable' ,
from : 'coven@chat.shakespeare.lit/nomorenicks'
} ) . c ( 'x' , { xmlns : Strophe . NS . MUC _USER } )
. c ( 'item' , {
'affiliation' : 'none' ,
'jid' : 'nomorenicks@montague.lit/_converse.js-290929789' ,
'role' : 'none'
2018-11-20 18:10:55 +01:00
} ) ;
2020-04-22 13:11:48 +02:00
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ===
2020-04-23 13:36:16 +02:00
"some1 and newgirl have entered the groupchat\nnewguy and nomorenicks have left the groupchat" ) ;
2018-11-20 18:10:55 +01:00
2020-04-22 13:11:48 +02:00
presence = $pres ( {
to : 'romeo@montague.lit/_converse.js-29092160' ,
from : 'coven@chat.shakespeare.lit/nomorenicks'
} )
. c ( 'x' , { xmlns : Strophe . NS . MUC _USER } )
. c ( 'item' , {
'affiliation' : 'none' ,
'jid' : 'nomorenicks@montague.lit/_converse.js-290929789' ,
'role' : 'participant'
2020-04-13 11:10:25 +02:00
} ) ;
2020-04-22 13:11:48 +02:00
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ===
2020-04-23 13:36:16 +02:00
"some1, newgirl and nomorenicks have entered the groupchat\nnewguy has left the groupchat" ) ;
2020-04-22 13:11:48 +02:00
// Test a member joining and leaving
presence = $pres ( {
to : 'romeo@montague.lit/_converse.js-290918392' ,
from : 'coven@chat.shakespeare.lit/insider'
} ) . c ( 'x' , { xmlns : Strophe . NS . MUC _USER } )
. c ( 'item' , {
'affiliation' : 'member' ,
'jid' : 'insider@montague.lit/_converse.js-290929789' ,
'role' : 'participant'
2018-11-20 18:10:55 +01:00
} ) ;
2020-04-22 13:11:48 +02:00
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
/ * < p r e s e n c e
* from = 'coven@chat.shakespeare.lit/thirdwitch'
* to = 'crone1@shakespeare.lit/desktop'
* type = 'unavailable' >
* < status > Disconnected : Replaced by new connection < / s t a t u s >
* < x xmlns = 'http://jabber.org/protocol/muc#user' >
* < item affiliation = 'member'
* jid = 'hag66@shakespeare.lit/pda'
* role = 'none' / >
* < / x >
* < / p r e s e n c e >
* /
presence = $pres ( {
to : 'romeo@montague.lit/_converse.js-29092160' ,
type : 'unavailable' ,
from : 'coven@chat.shakespeare.lit/insider'
} )
. c ( 'status' , 'Disconnected: Replaced by new connection' ) . up ( )
. c ( 'x' , { xmlns : Strophe . NS . MUC _USER } )
. c ( 'item' , {
'affiliation' : 'member' ,
'jid' : 'insider@montague.lit/_converse.js-290929789' ,
'role' : 'none'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ===
2020-04-23 13:36:16 +02:00
"some1, newgirl and nomorenicks have entered the groupchat\nnewguy and insider have left the groupchat" ) ;
2020-04-22 13:11:48 +02:00
expect ( view . model . occupants . length ) . toBe ( 5 ) ;
expect ( view . model . occupants . findWhere ( { 'jid' : 'insider@montague.lit' } ) . get ( 'show' ) ) . toBe ( 'offline' ) ;
// New girl leaves
presence = $pres ( {
'to' : 'romeo@montague.lit/_converse.js-29092160' ,
'type' : 'unavailable' ,
'from' : 'coven@chat.shakespeare.lit/newgirl'
} )
. c ( 'x' , { xmlns : Strophe . NS . MUC _USER } )
. c ( 'item' , {
'affiliation' : 'none' ,
'jid' : 'newgirl@montague.lit/_converse.js-213098781' ,
'role' : 'none'
2018-11-20 18:10:55 +01:00
} ) ;
2020-04-22 13:11:48 +02:00
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ===
2020-04-23 13:36:16 +02:00
"some1 and nomorenicks have entered the groupchat\nnewguy, insider and newgirl have left the groupchat" ) ;
2020-04-22 13:11:48 +02:00
expect ( view . model . occupants . length ) . toBe ( 4 ) ;
done ( ) ;
} ) ) ;
it ( "combines subsequent join/leave messages when users enter or exit a groupchat" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { } ,
async function ( done , _converse ) {
await mock . openAndEnterChatRoom ( _converse , 'coven@chat.shakespeare.lit' , 'romeo' )
const view = _converse . chatboxviews . get ( 'coven@chat.shakespeare.lit' ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) === "romeo has entered the groupchat" ) ;
let presence = u . toStanza (
` <presence xmlns="jabber:client" to="romeo@montague.lit/orchard" from="coven@chat.shakespeare.lit/fabio">
< c xmlns = "http://jabber.org/protocol/caps" node = "http://conversations.im" ver = "INI3xjRUioclBTP/aACfWi5m9UY=" hash = "sha-1" / >
< x xmlns = "http://jabber.org/protocol/muc#user" >
< item affiliation = "none" jid = "fabio@montefuscolo.com.br/Conversations.ZvLu" role = "participant" / >
< / x >
< / p r e s e n c e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) === "romeo and fabio have entered the groupchat" ) ;
presence = u . toStanza (
` <presence xmlns="jabber:client" to="romeo@montague.lit/orchard" from="coven@chat.shakespeare.lit/Dele Olajide">
< x xmlns = "http://jabber.org/protocol/muc#user" >
< item affiliation = "none" jid = "deleo@traderlynk.4ng.net/converse.js-39320524" role = "participant" / >
< / x >
< / p r e s e n c e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) === "romeo, fabio and Dele Olajide have entered the groupchat" ) ;
presence = u . toStanza (
` <presence xmlns="jabber:client" to="romeo@montague.lit/orchard" from="coven@chat.shakespeare.lit/jcbrand">
< x xmlns = "http://jabber.org/protocol/muc#user" >
< item affiliation = "owner" jid = "jc@opkode.com/converse.js-30645022" role = "moderator" / >
< status code = "110" / >
< / x >
< / p r e s e n c e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) === "romeo, fabio and others have entered the groupchat" ) ;
presence = u . toStanza (
` <presence xmlns="jabber:client" to="romeo@montague.lit/orchard" type="unavailable" from="coven@chat.shakespeare.lit/Dele Olajide">
< x xmlns = "http://jabber.org/protocol/muc#user" >
< item affiliation = "none" jid = "deleo@traderlynk.4ng.net/converse.js-39320524" role = "none" / >
< / x >
< / p r e s e n c e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ===
2020-04-23 13:36:16 +02:00
"romeo, fabio and jcbrand have entered the groupchat\nDele Olajide has left the groupchat" ) ;
2020-04-22 13:11:48 +02:00
presence = u . toStanza (
` <presence xmlns="jabber:client" to="romeo@montague.lit/orchard" from="coven@chat.shakespeare.lit/Dele Olajide">
< x xmlns = "http://jabber.org/protocol/muc#user" >
< item affiliation = "none" jid = "deleo@traderlynk.4ng.net/converse.js-74567907" role = "participant" / >
< / x >
< / p r e s e n c e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ===
"romeo, fabio and others have entered the groupchat" ) ;
presence = u . toStanza (
` <presence xmlns="jabber:client" to="romeo@montague.lit/orchard" from="coven@chat.shakespeare.lit/fuvuv" xml:lang="en">
< c xmlns = "http://jabber.org/protocol/caps" node = "http://jabber.pix-art.de" ver = "5tOurnuFnp2h50hKafeUyeN4Yl8=" hash = "sha-1" / >
< x xmlns = "vcard-temp:x:update" / >
< x xmlns = "http://jabber.org/protocol/muc#user" >
< item affiliation = "none" jid = "fuvuv@blabber.im/Pix-Art Messenger.8zoB" role = "participant" / >
< / x >
< / p r e s e n c e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ===
"romeo, fabio and others have entered the groupchat" ) ;
presence = u . toStanza (
` <presence xmlns="jabber:client" to="romeo@montague.lit/orchard" type="unavailable" from="coven@chat.shakespeare.lit/fuvuv">
< x xmlns = "http://jabber.org/protocol/muc#user" >
< item affiliation = "none" jid = "fuvuv@blabber.im/Pix-Art Messenger.8zoB" role = "none" / >
< / x >
< / p r e s e n c e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ===
2020-04-23 13:36:16 +02:00
"romeo, fabio and others have entered the groupchat\nfuvuv has left the groupchat" ) ;
2020-04-22 13:11:48 +02:00
presence = u . toStanza (
` <presence xmlns="jabber:client" to="romeo@montague.lit/orchard" type="unavailable" from="coven@chat.shakespeare.lit/fabio">
< status > Disconnected : Replaced by new connection < / s t a t u s >
< x xmlns = "http://jabber.org/protocol/muc#user" >
< item affiliation = "none" jid = "fabio@montefuscolo.com.br/Conversations.ZvLu" role = "none" / >
< / x >
< / p r e s e n c e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ===
2020-04-23 13:36:16 +02:00
"romeo, jcbrand and Dele Olajide have entered the groupchat\nfuvuv and fabio have left the groupchat" ) ;
2020-04-22 13:11:48 +02:00
presence = u . toStanza (
` <presence xmlns="jabber:client" to="romeo@montague.lit/orchard" from="coven@chat.shakespeare.lit/fabio">
< c xmlns = "http://jabber.org/protocol/caps" node = "http://conversations.im" ver = "INI3xjRUioclBTP/aACfWi5m9UY=" hash = "sha-1" / >
< status > Ready for a new day < / s t a t u s >
< x xmlns = "http://jabber.org/protocol/muc#user" >
< item affiliation = "none" jid = "fabio@montefuscolo.com.br/Conversations.ZvLu" role = "participant" / >
< / x >
< / p r e s e n c e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ===
2020-04-23 13:36:16 +02:00
"romeo, jcbrand and others have entered the groupchat\nfuvuv has left the groupchat" ) ;
2020-04-22 13:11:48 +02:00
presence = u . toStanza (
` <presence xmlns="jabber:client" to="romeo@montague.lit/orchard" type="unavailable" from="coven@chat.shakespeare.lit/fabio">
< status > Disconnected : closed < / s t a t u s >
< x xmlns = "http://jabber.org/protocol/muc#user" >
< item affiliation = "none" jid = "fabio@montefuscolo.com.br/Conversations.ZvLu" role = "none" / >
< / x >
< / p r e s e n c e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ===
2020-04-23 13:36:16 +02:00
"romeo, jcbrand and Dele Olajide have entered the groupchat\nfuvuv and fabio have left the groupchat" ) ;
2020-04-22 13:11:48 +02:00
presence = u . toStanza (
` <presence xmlns="jabber:client" to="romeo@montague.lit/orchard" type="unavailable" from="coven@chat.shakespeare.lit/Dele Olajide">
< x xmlns = "http://jabber.org/protocol/muc#user" >
< item affiliation = "none" jid = "deleo@traderlynk.4ng.net/converse.js-74567907" role = "none" / >
< / x >
< / p r e s e n c e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ===
2020-04-23 13:36:16 +02:00
"romeo and jcbrand have entered the groupchat\nfuvuv, fabio and Dele Olajide have left the groupchat" ) ;
2020-04-22 13:11:48 +02:00
presence = u . toStanza (
` <presence xmlns="jabber:client" to="romeo@montague.lit/orchard" from="coven@chat.shakespeare.lit/fabio">
< c xmlns = "http://jabber.org/protocol/caps" node = "http://conversations.im" ver = "INI3xjRUioclBTP/aACfWi5m9UY=" hash = "sha-1" / >
< x xmlns = "http://jabber.org/protocol/muc#user" >
< item affiliation = "none" jid = "fabio@montefuscolo.com.br/Conversations.ZvLu" role = "participant" / >
< / x >
< / p r e s e n c e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ===
2020-04-23 13:36:16 +02:00
"romeo, jcbrand and fabio have entered the groupchat\nfuvuv and Dele Olajide have left the groupchat" ) ;
2020-04-22 13:11:48 +02:00
expect ( 1 ) . toBe ( 1 ) ;
done ( ) ;
} ) ) ;
it ( "doesn't show the disconnection messages when muc_show_join_leave is false" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { 'muc_show_join_leave' : false } ,
async function ( done , _converse ) {
spyOn ( _converse . ChatRoom . prototype , 'onOccupantAdded' ) . and . callThrough ( ) ;
spyOn ( _converse . ChatRoom . prototype , 'onOccupantRemoved' ) . and . callThrough ( ) ;
await mock . openAndEnterChatRoom ( _converse , 'coven@chat.shakespeare.lit' , 'some1' ) ;
const view = _converse . chatboxviews . get ( 'coven@chat.shakespeare.lit' ) ;
let presence = $pres ( {
to : 'romeo@montague.lit/orchard' ,
from : 'coven@chat.shakespeare.lit/newguy'
} ) . c ( 'x' , { xmlns : Strophe . NS . MUC _USER } )
. c ( 'item' , {
'affiliation' : 'none' ,
'jid' : 'newguy@montague.lit/_converse.js-290929789' ,
'role' : 'participant'
2018-12-22 18:27:07 +01:00
} ) ;
2020-04-22 13:11:48 +02:00
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . model . onOccupantAdded . calls . count ( ) === 2 ) ;
expect ( view . model . notifications . get ( 'entered' ) ) . toBeFalsy ( ) ;
expect ( view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ) . toBe ( '' ) ;
await mock . sendMessage ( view , 'hello world' ) ;
presence = u . toStanza (
` <presence xmlns="jabber:client" to="romeo@montague.lit/orchard" type="unavailable" from="coven@chat.shakespeare.lit/newguy">
< status > Gotta go ! < / s t a t u s >
< x xmlns = "http://jabber.org/protocol/muc#user" >
< item affiliation = "none" jid = "newguy@montague.lit/_converse.js-290929789" role = "none" / >
< / x >
< / p r e s e n c e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . model . onOccupantRemoved . calls . count ( ) ) ;
expect ( view . model . onOccupantRemoved . calls . count ( ) ) . toBe ( 1 ) ;
expect ( view . model . notifications . get ( 'entered' ) ) . toBeFalsy ( ) ;
await mock . sendMessage ( view , 'hello world' ) ;
expect ( view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ) . toBe ( '' ) ;
done ( ) ;
} ) ) ;
it ( "role-change messages that follow a MUC leave are left out" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { } ,
async function ( done , _converse ) {
// See https://github.com/conversejs/converse.js/issues/1259
await mock . openAndEnterChatRoom ( _converse , 'conversations@conference.siacs.eu' , 'romeo' ) ;
const presence = $pres ( {
to : 'romeo@montague.lit/orchard' ,
from : 'conversations@conference.siacs.eu/Guus'
} ) . c ( 'x' , {
'xmlns' : Strophe . NS . MUC _USER
} ) . c ( 'item' , {
'affiliation' : 'none' ,
'jid' : 'Guus@montague.lit/xxx' ,
'role' : 'visitor'
2020-04-15 09:13:50 +02:00
} ) ;
2020-04-22 13:11:48 +02:00
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
2013-05-11 14:20:07 +02:00
2020-04-22 13:11:48 +02:00
const view = _converse . chatboxviews . get ( 'conversations@conference.siacs.eu' ) ;
const msg = $msg ( {
'from' : 'conversations@conference.siacs.eu/romeo' ,
'id' : u . getUniqueId ( ) ,
'to' : 'romeo@montague.lit' ,
'type' : 'groupchat'
} ) . c ( 'body' ) . t ( 'Some message' ) . tree ( ) ;
2016-11-21 13:55:19 +01:00
2020-04-24 17:33:32 +02:00
await view . model . handleMessageStanza ( msg ) ;
2020-04-22 13:11:48 +02:00
await u . waitUntil ( ( ) => sizzle ( '.chat-msg:last .chat-msg__text' , view . content ) . pop ( ) ) ;
2018-08-07 08:52:28 +02:00
2020-04-22 13:11:48 +02:00
let stanza = u . toStanza (
` <presence xmlns="jabber:client" to="romeo@montague.lit/orchard" type="unavailable" from="conversations@conference.siacs.eu/Guus">
< x xmlns = "http://jabber.org/protocol/muc#user" >
< item affiliation = "none" role = "none" / >
< / x >
< / p r e s e n c e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( stanza ) ) ;
stanza = u . toStanza (
` <presence xmlns="jabber:client" to="romeo@montague.lit/orchard" from="conversations@conference.siacs.eu/Guus">
< c xmlns = "http://jabber.org/protocol/caps" node = "http://conversations.im" ver = "ISg6+9AoK1/cwhbNEDviSvjdPzI=" hash = "sha-1" / >
< x xmlns = "vcard-temp:x:update" >
< photo > bf987c486c51fbc05a6a4a9f20dd19b5efba3758 < / p h o t o >
< / x >
< x xmlns = "http://jabber.org/protocol/muc#user" >
< item affiliation = "none" role = "visitor" / >
< / x >
< / p r e s e n c e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( stanza ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( )
=== "romeo and Guus have entered the groupchat" ) ;
expect ( 1 ) . toBe ( 1 ) ;
done ( ) ;
} ) ) ;
it ( "supports the /me command" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
await mock . waitUntilDiscoConfirmed ( _converse , 'montague.lit' , [ ] , [ 'vcard-temp' ] ) ;
await u . waitUntil ( ( ) => _converse . xmppstatus . vcard . get ( 'fullname' ) ) ;
await mock . waitForRoster ( _converse , 'current' ) ;
await mock . openAndEnterChatRoom ( _converse , 'lounge@montague.lit' , 'romeo' ) ;
const view = _converse . chatboxviews . get ( 'lounge@montague.lit' ) ;
if ( ! view . el . querySelectorAll ( '.chat-area' ) . length ) {
view . renderChatArea ( ) ;
}
let message = '/me is tired' ;
const nick = mock . chatroom _names [ 0 ] ;
let msg = $msg ( {
'from' : 'lounge@montague.lit/' + nick ,
'id' : u . getUniqueId ( ) ,
'to' : 'romeo@montague.lit' ,
'type' : 'groupchat'
} ) . c ( 'body' ) . t ( message ) . tree ( ) ;
2020-04-24 17:33:32 +02:00
await view . model . handleMessageStanza ( msg ) ;
2020-04-22 13:11:48 +02:00
await u . waitUntil ( ( ) => sizzle ( '.chat-msg:last .chat-msg__text' , view . content ) . pop ( ) ) ;
expect ( _ . includes ( view . el . querySelector ( '.chat-msg__author' ) . textContent , '**Dyon van de Wege' ) ) . toBeTruthy ( ) ;
expect ( view . el . querySelector ( '.chat-msg__text' ) . textContent . trim ( ) ) . toBe ( 'is tired' ) ;
message = '/me is as well' ;
msg = $msg ( {
from : 'lounge@montague.lit/Romeo Montague' ,
id : u . getUniqueId ( ) ,
to : 'romeo@montague.lit' ,
type : 'groupchat'
} ) . c ( 'body' ) . t ( message ) . tree ( ) ;
2020-04-24 17:33:32 +02:00
await view . model . handleMessageStanza ( msg ) ;
2020-04-22 13:11:48 +02:00
await u . waitUntil ( ( ) => view . el . querySelectorAll ( '.chat-msg' ) . length === 2 ) ;
expect ( sizzle ( '.chat-msg__author:last' , view . el ) . pop ( ) . textContent . includes ( '**Romeo Montague' ) ) . toBeTruthy ( ) ;
expect ( sizzle ( '.chat-msg__text:last' , view . el ) . pop ( ) . textContent . trim ( ) ) . toBe ( 'is as well' ) ;
done ( ) ;
} ) ) ;
it ( "can be configured if you're its owner" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { } ,
async function ( done , _converse ) {
let sent _IQ , IQ _id ;
const sendIQ = _converse . connection . sendIQ ;
spyOn ( _converse . connection , 'sendIQ' ) . and . callFake ( function ( iq , callback , errback ) {
sent _IQ = iq ;
IQ _id = sendIQ . bind ( this ) ( iq , callback , errback ) ;
} ) ;
2018-08-07 08:52:28 +02:00
2020-04-22 13:11:48 +02:00
await _converse . api . rooms . open ( 'coven@chat.shakespeare.lit' , { 'nick' : 'some1' } ) ;
const view = _converse . chatboxviews . get ( 'coven@chat.shakespeare.lit' ) ;
await u . waitUntil ( ( ) => u . isVisible ( view . el ) ) ;
// We pretend this is a new room, so no disco info is returned.
const features _stanza = $iq ( {
from : 'coven@chat.shakespeare.lit' ,
'id' : IQ _id ,
'to' : 'romeo@montague.lit/desktop' ,
'type' : 'error'
} ) . c ( 'error' , { 'type' : 'cancel' } )
. c ( 'item-not-found' , { 'xmlns' : "urn:ietf:params:xml:ns:xmpp-stanzas" } ) ;
_converse . connection . _dataRecv ( mock . createRequest ( features _stanza ) ) ;
/ * < p r e s e n c e t o = " r o m e o @ m o n t a g u e . l i t / _ c o n v e r s e . j s - 2 9 0 9 2 1 6 0 "
* from = "coven@chat.shakespeare.lit/some1" >
* < x xmlns = "http://jabber.org/protocol/muc#user" >
* < item affiliation = "owner" jid = "romeo@montague.lit/_converse.js-29092160" role = "moderator" / >
* < status code = "110" / >
* < / x >
* < / p r e s e n c e > < / b o d y >
* /
const presence = $pres ( {
to : 'romeo@montague.lit/_converse.js-29092160' ,
from : 'coven@chat.shakespeare.lit/some1'
} ) . c ( 'x' , { xmlns : Strophe . NS . MUC _USER } )
. c ( 'item' , {
'affiliation' : 'owner' ,
'jid' : 'romeo@montague.lit/_converse.js-29092160' ,
'role' : 'moderator'
} ) . up ( )
. c ( 'status' , { code : '110' } ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.configure-chatroom-button' ) !== null ) ;
view . el . querySelector ( '.configure-chatroom-button' ) . click ( ) ;
/ * C h e c k t h a t a n I Q i s s e n t o u t , a s k i n g f o r t h e
* configuration form .
* See : // https://xmpp.org/extensions/xep-0045.html#example-163
*
* < iq from = 'crone1@shakespeare.lit/desktop'
* id = 'config1'
* to = 'coven@chat.shakespeare.lit'
* type = 'get' >
* < query xmlns = 'http://jabber.org/protocol/muc#owner' / >
* < / i q >
* /
2020-09-25 13:17:56 +02:00
expect ( Strophe . serialize ( sent _IQ ) ) . toBe (
2020-04-22 13:11:48 +02:00
` <iq id=" ` + IQ _id + ` " to="coven@chat.shakespeare.lit" type="get" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/muc#owner"/> ` +
` </iq> ` ) ;
/ * S e r v e r r e s p o n d s w i t h t h e c o n f i g u r a t i o n f o r m .
* See : // https://xmpp.org/extensions/xep-0045.html#example-165
* /
const config _stanza = $iq ( { from : 'coven@chat.shakespeare.lit' ,
'id' : IQ _id ,
'to' : 'romeo@montague.lit/desktop' ,
'type' : 'result' } )
. c ( 'query' , { 'xmlns' : 'http://jabber.org/protocol/muc#owner' } )
. c ( 'x' , { 'xmlns' : 'jabber:x:data' , 'type' : 'form' } )
. c ( 'title' ) . t ( 'Configuration for "coven" Room' ) . up ( )
. c ( 'instructions' ) . t ( 'Complete this form to modify the configuration of your room.' ) . up ( )
. c ( 'field' , { 'type' : 'hidden' , 'var' : 'FORM_TYPE' } )
. c ( 'value' ) . t ( 'http://jabber.org/protocol/muc#roomconfig' ) . up ( ) . up ( )
. c ( 'field' , {
'label' : 'Natural-Language Room Name' ,
'type' : 'text-single' ,
'var' : 'muc#roomconfig_roomname' } )
. c ( 'value' ) . t ( 'A Dark Cave' ) . up ( ) . up ( )
. c ( 'field' , {
'label' : 'Short Description of Room' ,
'type' : 'text-single' ,
'var' : 'muc#roomconfig_roomdesc' } )
. c ( 'value' ) . t ( 'The place for all good witches!' ) . up ( ) . up ( )
. c ( 'field' , {
'label' : 'Enable Public Logging?' ,
'type' : 'boolean' ,
'var' : 'muc#roomconfig_enablelogging' } )
. c ( 'value' ) . t ( 0 ) . up ( ) . up ( )
. c ( 'field' , {
'label' : 'Allow Occupants to Change Subject?' ,
'type' : 'boolean' ,
'var' : 'muc#roomconfig_changesubject' } )
. c ( 'value' ) . t ( 0 ) . up ( ) . up ( )
. c ( 'field' , {
'label' : 'Allow Occupants to Invite Others?' ,
'type' : 'boolean' ,
'var' : 'muc#roomconfig_allowinvites' } )
. c ( 'value' ) . t ( 0 ) . up ( ) . up ( )
. c ( 'field' , {
'label' : 'Who Can Send Private Messages?' ,
'type' : 'list-single' ,
'var' : 'muc#roomconfig_allowpm' } )
. c ( 'value' ) . t ( 'anyone' ) . up ( )
. c ( 'option' , { 'label' : 'Anyone' } )
. c ( 'value' ) . t ( 'anyone' ) . up ( ) . up ( )
. c ( 'option' , { 'label' : 'Anyone with Voice' } )
. c ( 'value' ) . t ( 'participants' ) . up ( ) . up ( )
. c ( 'option' , { 'label' : 'Moderators Only' } )
. c ( 'value' ) . t ( 'moderators' ) . up ( ) . up ( )
. c ( 'option' , { 'label' : 'Nobody' } )
. c ( 'value' ) . t ( 'none' ) . up ( ) . up ( ) . up ( )
. c ( 'field' , {
'label' : 'Roles for which Presence is Broadcasted' ,
'type' : 'list-multi' ,
'var' : 'muc#roomconfig_presencebroadcast' } )
. c ( 'value' ) . t ( 'moderator' ) . up ( )
. c ( 'value' ) . t ( 'participant' ) . up ( )
. c ( 'value' ) . t ( 'visitor' ) . up ( )
. c ( 'option' , { 'label' : 'Moderator' } )
. c ( 'value' ) . t ( 'moderator' ) . up ( ) . up ( )
. c ( 'option' , { 'label' : 'Participant' } )
. c ( 'value' ) . t ( 'participant' ) . up ( ) . up ( )
. c ( 'option' , { 'label' : 'Visitor' } )
. c ( 'value' ) . t ( 'visitor' ) . up ( ) . up ( ) . up ( )
. c ( 'field' , {
'label' : 'Roles and Affiliations that May Retrieve Member List' ,
'type' : 'list-multi' ,
'var' : 'muc#roomconfig_getmemberlist' } )
. c ( 'value' ) . t ( 'moderator' ) . up ( )
. c ( 'value' ) . t ( 'participant' ) . up ( )
. c ( 'value' ) . t ( 'visitor' ) . up ( )
. c ( 'option' , { 'label' : 'Moderator' } )
. c ( 'value' ) . t ( 'moderator' ) . up ( ) . up ( )
. c ( 'option' , { 'label' : 'Participant' } )
. c ( 'value' ) . t ( 'participant' ) . up ( ) . up ( )
. c ( 'option' , { 'label' : 'Visitor' } )
. c ( 'value' ) . t ( 'visitor' ) . up ( ) . up ( ) . up ( )
. c ( 'field' , {
'label' : 'Make Room Publicly Searchable?' ,
'type' : 'boolean' ,
'var' : 'muc#roomconfig_publicroom' } )
. c ( 'value' ) . t ( 0 ) . up ( ) . up ( )
. c ( 'field' , {
'label' : 'Make Room Publicly Searchable?' ,
'type' : 'boolean' ,
'var' : 'muc#roomconfig_publicroom' } )
. c ( 'value' ) . t ( 0 ) . up ( ) . up ( )
. c ( 'field' , {
'label' : 'Make Room Persistent?' ,
'type' : 'boolean' ,
'var' : 'muc#roomconfig_persistentroom' } )
. c ( 'value' ) . t ( 0 ) . up ( ) . up ( )
. c ( 'field' , {
'label' : 'Make Room Moderated?' ,
'type' : 'boolean' ,
'var' : 'muc#roomconfig_moderatedroom' } )
. c ( 'value' ) . t ( 0 ) . up ( ) . up ( )
. c ( 'field' , {
'label' : 'Make Room Members Only?' ,
'type' : 'boolean' ,
'var' : 'muc#roomconfig_membersonly' } )
. c ( 'value' ) . t ( 0 ) . up ( ) . up ( )
. c ( 'field' , {
'label' : 'Password Required for Entry?' ,
'type' : 'boolean' ,
'var' : 'muc#roomconfig_passwordprotectedroom' } )
. c ( 'value' ) . t ( 1 ) . up ( ) . up ( )
. c ( 'field' , { 'type' : 'fixed' } )
. c ( 'value' ) . t (
'If a password is required to enter this groupchat, you must specify the password below.'
) . up ( ) . up ( )
. c ( 'field' , {
'label' : 'Password' ,
'type' : 'text-private' ,
'var' : 'muc#roomconfig_roomsecret' } )
. c ( 'value' ) . t ( 'cauldronburn' ) ;
_converse . connection . _dataRecv ( mock . createRequest ( config _stanza ) ) ;
const form = await u . waitUntil ( ( ) => view . el . querySelector ( '.muc-config-form' ) ) ;
expect ( form . querySelectorAll ( 'fieldset' ) . length ) . toBe ( 2 ) ;
const membersonly = view . el . querySelectorAll ( 'input[name="muc#roomconfig_membersonly"]' ) ;
expect ( membersonly . length ) . toBe ( 1 ) ;
expect ( membersonly [ 0 ] . getAttribute ( 'type' ) ) . toBe ( 'checkbox' ) ;
membersonly [ 0 ] . checked = true ;
const moderated = view . el . querySelectorAll ( 'input[name="muc#roomconfig_moderatedroom"]' ) ;
expect ( moderated . length ) . toBe ( 1 ) ;
expect ( moderated [ 0 ] . getAttribute ( 'type' ) ) . toBe ( 'checkbox' ) ;
moderated [ 0 ] . checked = true ;
const password = view . el . querySelectorAll ( 'input[name="muc#roomconfig_roomsecret"]' ) ;
expect ( password . length ) . toBe ( 1 ) ;
expect ( password [ 0 ] . getAttribute ( 'type' ) ) . toBe ( 'password' ) ;
const allowpm = view . el . querySelectorAll ( 'select[name="muc#roomconfig_allowpm"]' ) ;
expect ( allowpm . length ) . toBe ( 1 ) ;
allowpm [ 0 ] . value = 'moderators' ;
const presencebroadcast = view . el . querySelectorAll ( 'select[name="muc#roomconfig_presencebroadcast"]' ) ;
expect ( presencebroadcast . length ) . toBe ( 1 ) ;
presencebroadcast [ 0 ] . value = [ 'moderator' ] ;
view . el . querySelector ( '.chatroom-form input[type="submit"]' ) . click ( ) ;
2020-09-25 13:17:56 +02:00
expect ( sent _IQ . querySelector ( 'field[var="muc#roomconfig_membersonly"] value' ) . textContent . trim ( ) ) . toBe ( '1' ) ;
expect ( sent _IQ . querySelector ( 'field[var="muc#roomconfig_moderatedroom"] value' ) . textContent . trim ( ) ) . toBe ( '1' ) ;
expect ( sent _IQ . querySelector ( 'field[var="muc#roomconfig_allowpm"] value' ) . textContent . trim ( ) ) . toBe ( 'moderators' ) ;
expect ( sent _IQ . querySelector ( 'field[var="muc#roomconfig_presencebroadcast"] value' ) . textContent . trim ( ) ) . toBe ( 'moderator' ) ;
2020-04-22 13:11:48 +02:00
done ( ) ;
} ) ) ;
it ( "shows all members even if they're not currently present in the groupchat" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
const muc _jid = 'lounge@montague.lit'
const members = [ {
'nick' : 'juliet' ,
'jid' : 'juliet@capulet.lit' ,
'affiliation' : 'member'
} ] ;
await mock . openAndEnterChatRoom ( _converse , muc _jid , 'romeo' , [ ] , members ) ;
const view = _converse . chatboxviews . get ( muc _jid ) ;
await u . waitUntil ( ( ) => view . model . occupants . length === 2 ) ;
const occupants = view . el . querySelector ( '.occupant-list' ) ;
for ( let i = 0 ; i < mock . chatroom _names . length ; i ++ ) {
const name = mock . chatroom _names [ i ] ;
const role = mock . chatroom _roles [ name ] . role ;
// See example 21 https://xmpp.org/extensions/xep-0045.html#enter-pres
const presence = $pres ( {
to : 'romeo@montague.lit/pda' ,
from : 'lounge@montague.lit/' + name
} ) . c ( 'x' ) . attrs ( { xmlns : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' ) . attrs ( {
affiliation : mock . chatroom _roles [ name ] . affiliation ,
jid : name . replace ( / /g , '.' ) . toLowerCase ( ) + '@montague.lit' ,
role : role
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
}
2018-04-08 19:44:53 +02:00
2020-04-22 13:11:48 +02:00
await u . waitUntil ( ( ) => occupants . querySelectorAll ( 'li' ) . length > 2 , 500 ) ;
expect ( occupants . querySelectorAll ( 'li' ) . length ) . toBe ( 2 + mock . chatroom _names . length ) ;
expect ( view . model . occupants . length ) . toBe ( 2 + mock . chatroom _names . length ) ;
2013-05-11 14:20:07 +02:00
2020-04-22 13:11:48 +02:00
mock . chatroom _names . forEach ( name => {
const model = view . model . occupants . findWhere ( { 'nick' : name } ) ;
const index = view . model . occupants . indexOf ( model ) ;
expect ( occupants . querySelectorAll ( 'li .occupant-nick' ) [ index ] . textContent . trim ( ) ) . toBe ( name ) ;
} ) ;
2017-07-11 10:41:11 +02:00
2020-04-22 13:11:48 +02:00
// Test users leaving the groupchat
// https://xmpp.org/extensions/xep-0045.html#exit
for ( let i = mock . chatroom _names . length - 1 ; i > - 1 ; i -- ) {
const name = mock . chatroom _names [ i ] ;
// See example 21 https://xmpp.org/extensions/xep-0045.html#enter-pres
const presence = $pres ( {
to : 'romeo@montague.lit/pda' ,
from : 'lounge@montague.lit/' + name ,
type : 'unavailable'
} ) . c ( 'x' ) . attrs ( { xmlns : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' ) . attrs ( {
affiliation : mock . chatroom _roles [ name ] . affiliation ,
jid : name . replace ( / /g , '.' ) . toLowerCase ( ) + '@montague.lit' ,
role : 'none'
} ) . nodeTree ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
expect ( occupants . querySelectorAll ( 'li' ) . length ) . toBe ( 8 ) ;
}
const presence = $pres ( {
to : 'romeo@montague.lit/pda' ,
from : 'lounge@montague.lit/nonmember'
} ) . c ( 'x' ) . attrs ( { xmlns : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' ) . attrs ( {
affiliation : null ,
jid : 'servant@montague.lit' ,
role : 'visitor'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => occupants . querySelectorAll ( 'li' ) . length > 8 , 500 ) ;
expect ( occupants . querySelectorAll ( 'li' ) . length ) . toBe ( 9 ) ;
expect ( view . model . occupants . length ) . toBe ( 9 ) ;
expect ( view . model . occupants . filter ( o => o . isMember ( ) ) . length ) . toBe ( 8 ) ;
view . model . rejoin ( ) ;
// Test that members aren't removed when we reconnect
expect ( view . model . occupants . length ) . toBe ( 8 ) ;
expect ( occupants . querySelectorAll ( 'li' ) . length ) . toBe ( 8 ) ;
done ( ) ;
} ) ) ;
it ( "shows users currently present in the groupchat" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
await mock . openAndEnterChatRoom ( _converse , 'lounge@montague.lit' , 'romeo' ) ;
2020-06-15 12:23:52 +02:00
var view = _converse . chatboxviews . get ( 'lounge@montague.lit' ) ;
const occupants = view . el . querySelector ( '.occupant-list' ) ;
2020-04-22 13:11:48 +02:00
for ( var i = 0 ; i < mock . chatroom _names . length ; i ++ ) {
const name = mock . chatroom _names [ i ] ;
// See example 21 https://xmpp.org/extensions/xep-0045.html#enter-pres
2020-06-15 12:23:52 +02:00
const presence = $pres ( {
to : 'romeo@montague.lit/pda' ,
from : 'lounge@montague.lit/' + name
2020-04-22 13:11:48 +02:00
} ) . c ( 'x' ) . attrs ( { xmlns : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' ) . attrs ( {
affiliation : 'none' ,
jid : name . replace ( / /g , '.' ) . toLowerCase ( ) + '@montague.lit' ,
2020-06-15 12:23:52 +02:00
role : 'participant'
2020-04-22 13:11:48 +02:00
} ) . up ( )
2020-06-15 12:23:52 +02:00
. c ( 'status' ) ;
2020-04-22 13:11:48 +02:00
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
}
2013-05-11 14:20:07 +02:00
2020-04-22 13:11:48 +02:00
await u . waitUntil ( ( ) => occupants . querySelectorAll ( 'li' ) . length > 1 , 500 ) ;
expect ( occupants . querySelectorAll ( 'li' ) . length ) . toBe ( 1 + mock . chatroom _names . length ) ;
2017-07-11 10:41:11 +02:00
2020-04-22 13:11:48 +02:00
mock . chatroom _names . forEach ( name => {
const model = view . model . occupants . findWhere ( { 'nick' : name } ) ;
const index = view . model . occupants . indexOf ( model ) ;
expect ( occupants . querySelectorAll ( 'li .occupant-nick' ) [ index ] . textContent . trim ( ) ) . toBe ( name ) ;
} ) ;
2019-06-11 12:16:27 +02:00
2020-04-22 13:11:48 +02:00
// Test users leaving the groupchat
// https://xmpp.org/extensions/xep-0045.html#exit
for ( i = mock . chatroom _names . length - 1 ; i > - 1 ; i -- ) {
const name = mock . chatroom _names [ i ] ;
// See example 21 https://xmpp.org/extensions/xep-0045.html#enter-pres
2020-06-15 12:23:52 +02:00
const presence = $pres ( {
2020-04-22 13:11:48 +02:00
to : 'romeo@montague.lit/pda' ,
from : 'lounge@montague.lit/' + name ,
type : 'unavailable'
} ) . c ( 'x' ) . attrs ( { xmlns : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' ) . attrs ( {
2020-06-15 12:23:52 +02:00
affiliation : "none" ,
2020-04-22 13:11:48 +02:00
jid : name . replace ( / /g , '.' ) . toLowerCase ( ) + '@montague.lit' ,
role : 'none'
2020-06-15 12:23:52 +02:00
} ) ;
2020-04-22 13:11:48 +02:00
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
expect ( occupants . querySelectorAll ( 'li' ) . length ) . toBe ( i + 1 ) ;
}
done ( ) ;
} ) ) ;
it ( "indicates moderators and visitors by means of a special css class and tooltip" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { 'view_mode' : 'fullscreen' } ,
async function ( done , _converse ) {
await mock . openAndEnterChatRoom ( _converse , 'lounge@montague.lit' , 'romeo' ) ;
const view = _converse . chatboxviews . get ( 'lounge@montague.lit' ) ;
let contact _jid = mock . cur _names [ 2 ] . replace ( / /g , '.' ) . toLowerCase ( ) + '@montague.lit' ;
await u . waitUntil ( ( ) => view . el . querySelectorAll ( '.occupant-list li' ) . length , 500 ) ;
let occupants = view . el . querySelectorAll ( '.occupant-list li' ) ;
expect ( occupants . length ) . toBe ( 1 ) ;
expect ( occupants [ 0 ] . querySelector ( '.occupant-nick' ) . textContent . trim ( ) ) . toBe ( "romeo" ) ;
expect ( occupants [ 0 ] . querySelectorAll ( '.badge' ) . length ) . toBe ( 2 ) ;
expect ( occupants [ 0 ] . querySelectorAll ( '.badge' ) [ 0 ] . textContent . trim ( ) ) . toBe ( 'Owner' ) ;
expect ( sizzle ( '.badge:last' , occupants [ 0 ] ) . pop ( ) . textContent . trim ( ) ) . toBe ( 'Moderator' ) ;
var presence = $pres ( {
to : 'romeo@montague.lit/pda' ,
from : 'lounge@montague.lit/moderatorman'
} ) . c ( 'x' ) . attrs ( { xmlns : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' ) . attrs ( {
affiliation : 'admin' ,
jid : contact _jid ,
role : 'moderator' ,
} ) . up ( )
. c ( 'status' ) . attrs ( { code : '110' } ) . nodeTree ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelectorAll ( '.occupant-list li' ) . length > 1 , 500 ) ;
occupants = view . el . querySelectorAll ( '.occupant-list li' ) ;
expect ( occupants . length ) . toBe ( 2 ) ;
expect ( occupants [ 0 ] . querySelector ( '.occupant-nick' ) . textContent . trim ( ) ) . toBe ( "moderatorman" ) ;
expect ( occupants [ 1 ] . querySelector ( '.occupant-nick' ) . textContent . trim ( ) ) . toBe ( "romeo" ) ;
expect ( occupants [ 0 ] . querySelectorAll ( '.badge' ) . length ) . toBe ( 2 ) ;
expect ( occupants [ 0 ] . querySelectorAll ( '.badge' ) [ 0 ] . textContent . trim ( ) ) . toBe ( 'Admin' ) ;
expect ( occupants [ 0 ] . querySelectorAll ( '.badge' ) [ 1 ] . textContent . trim ( ) ) . toBe ( 'Moderator' ) ;
expect ( occupants [ 0 ] . getAttribute ( 'title' ) ) . toBe (
contact _jid + ' This user is a moderator. Click to mention moderatorman in your message.'
) ;
contact _jid = mock . cur _names [ 3 ] . replace ( / /g , '.' ) . toLowerCase ( ) + '@montague.lit' ;
presence = $pres ( {
to : 'romeo@montague.lit/pda' ,
from : 'lounge@montague.lit/visitorwoman'
} ) . c ( 'x' ) . attrs ( { xmlns : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' ) . attrs ( {
jid : contact _jid ,
role : 'visitor' ,
} ) . up ( )
. c ( 'status' ) . attrs ( { code : '110' } ) . nodeTree ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelectorAll ( '.occupant-list li' ) . length > 2 , 500 ) ;
occupants = view . el . querySelector ( '.occupant-list' ) . querySelectorAll ( 'li' ) ;
expect ( occupants . length ) . toBe ( 3 ) ;
expect ( occupants [ 2 ] . querySelector ( '.occupant-nick' ) . textContent . trim ( ) ) . toBe ( "visitorwoman" ) ;
expect ( occupants [ 2 ] . querySelectorAll ( '.badge' ) . length ) . toBe ( 1 ) ;
expect ( sizzle ( '.badge' , occupants [ 2 ] ) . pop ( ) . textContent . trim ( ) ) . toBe ( 'Visitor' ) ;
expect ( occupants [ 2 ] . getAttribute ( 'title' ) ) . toBe (
contact _jid + ' This user can NOT send messages in this groupchat. Click to mention visitorwoman in your message.'
) ;
done ( ) ;
} ) ) ;
it ( "properly handles notification that a room has been destroyed" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
await mock . openChatRoomViaModal ( _converse , 'problematic@muc.montague.lit' , 'romeo' )
const presence = $pres ( ) . attrs ( {
from : 'problematic@muc.montague.lit' ,
id : 'n13mt3l' ,
to : 'romeo@montague.lit/pda' ,
type : 'error' } )
. c ( 'error' ) . attrs ( { 'type' : 'cancel' } )
. c ( 'gone' ) . attrs ( { 'xmlns' : 'urn:ietf:params:xml:ns:xmpp-stanzas' } )
. t ( 'xmpp:other-room@chat.jabberfr.org?join' ) . up ( )
. c ( 'text' ) . attrs ( { 'xmlns' : 'urn:ietf:params:xml:ns:xmpp-stanzas' } )
. t ( "We didn't like the name" ) . nodeTree ;
const view = _converse . chatboxviews . get ( 'problematic@muc.montague.lit' ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
expect ( view . el . querySelector ( '.chatroom-body .disconnect-msg' ) . textContent . trim ( ) )
. toBe ( 'This groupchat no longer exists' ) ;
expect ( view . el . querySelector ( '.chatroom-body .destroyed-reason' ) . textContent . trim ( ) )
. toBe ( ` "We didn't like the name" ` ) ;
expect ( view . el . querySelector ( '.chatroom-body .moved-label' ) . textContent . trim ( ) )
. toBe ( 'The conversation has moved. Click below to enter.' ) ;
expect ( view . el . querySelector ( '.chatroom-body .moved-link' ) . textContent . trim ( ) )
. toBe ( ` other-room@chat.jabberfr.org ` ) ;
done ( ) ;
} ) ) ;
it ( "will use the user's reserved nickname, if it exists" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { } ,
async function ( done , _converse ) {
const IQ _stanzas = _converse . connection . IQ _stanzas ;
const muc _jid = 'lounge@montague.lit' ;
await mock . openChatRoom ( _converse , 'lounge' , 'montague.lit' , 'romeo' ) ;
let stanza = await u . waitUntil ( ( ) => IQ _stanzas . filter (
iq => iq . querySelector (
` iq[to=" ${ muc _jid } "] query[xmlns="http://jabber.org/protocol/disco#info"] `
) ) . pop ( )
) ;
// We pretend this is a new room, so no disco info is returned.
const features _stanza = $iq ( {
from : 'lounge@montague.lit' ,
'id' : stanza . getAttribute ( 'id' ) ,
'to' : 'romeo@montague.lit/desktop' ,
'type' : 'error'
} ) . c ( 'error' , { 'type' : 'cancel' } )
. c ( 'item-not-found' , { 'xmlns' : "urn:ietf:params:xml:ns:xmpp-stanzas" } ) ;
_converse . connection . _dataRecv ( mock . createRequest ( features _stanza ) ) ;
/ * < i q f r o m = ' h a g 6 6 @ s h a k e s p e a r e . l i t / p d a '
* id = 'getnick1'
* to = 'coven@chat.shakespeare.lit'
* type = 'get' >
* < query xmlns = 'http://jabber.org/protocol/disco#info'
* node = 'x-roomuser-item' / >
* < / i q >
* /
const iq = await u . waitUntil ( ( ) => _ . filter (
IQ _stanzas ,
s => sizzle ( ` iq[to=" ${ muc _jid } "] query[node="x-roomuser-item"] ` , s ) . length
) . pop ( )
) ;
expect ( Strophe . serialize ( iq ) ) . toBe (
` <iq from="romeo@montague.lit/orchard" id=" ${ iq . getAttribute ( 'id' ) } " to="lounge@montague.lit" ` +
` type="get" xmlns="jabber:client"> ` +
` <query node="x-roomuser-item" xmlns="http://jabber.org/protocol/disco#info"/></iq> ` ) ;
/ * < i q f r o m = ' c o v e n @ c h a t . s h a k e s p e a r e . l i t '
* id = 'getnick1'
* to = 'hag66@shakespeare.lit/pda'
* type = 'result' >
* < query xmlns = 'http://jabber.org/protocol/disco#info'
* node = 'x-roomuser-item' >
* < identity
* category = 'conference'
* name = 'thirdwitch'
* type = 'text' / >
* < / q u e r y >
* < / i q >
* /
const view = _converse . chatboxviews . get ( 'lounge@montague.lit' ) ;
stanza = $iq ( {
'type' : 'result' ,
'id' : iq . getAttribute ( 'id' ) ,
'from' : view . model . get ( 'jid' ) ,
'to' : _converse . connection . jid
} ) . c ( 'query' , { 'xmlns' : 'http://jabber.org/protocol/disco#info' , 'node' : 'x-roomuser-item' } )
. c ( 'identity' , { 'category' : 'conference' , 'name' : 'thirdwitch' , 'type' : 'text' } ) ;
_converse . connection . _dataRecv ( mock . createRequest ( stanza ) ) ;
// The user has just entered the groupchat (because join was called)
// and receives their own presence from the server.
// See example 24:
// https://xmpp.org/extensions/xep-0045.html#enter-pres
const presence = $pres ( {
to : 'romeo@montague.lit/orchard' ,
from : 'lounge@montague.lit/thirdwitch' ,
id : 'DC352437-C019-40EC-B590-AF29E879AF97'
} ) . c ( 'x' ) . attrs ( { xmlns : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' ) . attrs ( {
affiliation : 'member' ,
jid : 'romeo@montague.lit/orchard' ,
role : 'participant'
} ) . up ( )
. c ( 'status' ) . attrs ( { code : '110' } ) . up ( )
. c ( 'status' ) . attrs ( { code : '210' } ) . nodeTree ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelectorAll ( '.chat-content .chat-info' ) . length ) ;
const info _text = sizzle ( '.chat-content .chat-info:first' , view . el ) . pop ( ) . textContent . trim ( ) ;
expect ( info _text ) . toBe ( 'Your nickname has been automatically set to thirdwitch' ) ;
done ( ) ;
} ) ) ;
it ( "allows the user to invite their roster contacts to enter the groupchat" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { 'view_mode' : 'fullscreen' } ,
async function ( done , _converse ) {
// We need roster contacts, so that we have someone to invite
await mock . waitForRoster ( _converse , 'current' ) ;
const features = [
'http://jabber.org/protocol/muc' ,
'jabber:iq:register' ,
'muc_passwordprotected' ,
'muc_hidden' ,
'muc_temporary' ,
'muc_membersonly' ,
'muc_unmoderated' ,
'muc_anonymous'
]
await mock . openAndEnterChatRoom ( _converse , 'lounge@montague.lit' , 'romeo' , features ) ;
const view = _converse . chatboxviews . get ( 'lounge@montague.lit' ) ;
expect ( view . model . getOwnAffiliation ( ) ) . toBe ( 'owner' ) ;
expect ( view . model . features . get ( 'open' ) ) . toBe ( false ) ;
expect ( view . el . querySelector ( '.open-invite-modal' ) ) . not . toBe ( null ) ;
// Members can't invite if the room isn't open
view . model . getOwnOccupant ( ) . set ( 'affiliation' , 'member' ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.open-invite-modal' ) === null ) ;
view . model . features . set ( 'open' , 'true' ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.open-invite-modal' ) ) ;
view . el . querySelector ( '.open-invite-modal' ) . click ( ) ;
const modal = view . muc _invite _modal ;
await u . waitUntil ( ( ) => u . isVisible ( modal . el ) , 1000 )
expect ( modal . el . querySelectorAll ( '#invitee_jids' ) . length ) . toBe ( 1 ) ;
expect ( modal . el . querySelectorAll ( 'textarea' ) . length ) . toBe ( 1 ) ;
spyOn ( view . model , 'directInvite' ) . and . callThrough ( ) ;
const input = modal . el . querySelector ( '#invitee_jids' ) ;
input . value = "Balt" ;
modal . el . querySelector ( 'button[type="submit"]' ) . click ( ) ;
await u . waitUntil ( ( ) => modal . el . querySelector ( '.error' ) ) ;
const error = modal . el . querySelector ( '.error' ) ;
expect ( error . textContent ) . toBe ( 'Please enter a valid XMPP address' ) ;
let evt = new Event ( 'input' ) ;
input . dispatchEvent ( evt ) ;
let sent _stanza ;
spyOn ( _converse . connection , 'send' ) . and . callFake ( stanza => ( sent _stanza = stanza ) ) ;
const hint = await u . waitUntil ( ( ) => modal . el . querySelector ( '.suggestion-box__results li' ) ) ;
expect ( input . value ) . toBe ( 'Balt' ) ;
expect ( hint . textContent . trim ( ) ) . toBe ( 'Balthasar' ) ;
evt = new Event ( 'mousedown' , { 'bubbles' : true } ) ;
evt . button = 0 ;
hint . dispatchEvent ( evt ) ;
const textarea = modal . el . querySelector ( 'textarea' ) ;
textarea . value = "Please join!" ;
modal . el . querySelector ( 'button[type="submit"]' ) . click ( ) ;
expect ( view . model . directInvite ) . toHaveBeenCalled ( ) ;
expect ( sent _stanza . toLocaleString ( ) ) . toBe (
` <message from="romeo@montague.lit/orchard" ` +
` id=" ${ sent _stanza . nodeTree . getAttribute ( "id" ) } " ` +
` to="balthasar@montague.lit" ` +
` xmlns="jabber:client"> ` +
` <x jid="lounge@montague.lit" reason="Please join!" xmlns="jabber:x:conference"/> ` +
` </message> `
) ;
done ( ) ;
} ) ) ;
it ( "can be joined automatically, based upon a received invite" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
await mock . waitForRoster ( _converse , 'current' ) ; // We need roster contacts, who can invite us
const name = mock . cur _names [ 0 ] ;
const from _jid = name . replace ( / /g , '.' ) . toLowerCase ( ) + '@montague.lit' ;
await u . waitUntil ( ( ) => _converse . roster . get ( from _jid ) . vcard . get ( 'fullname' ) ) ;
spyOn ( window , 'confirm' ) . and . callFake ( ( ) => true ) ;
await mock . openAndEnterChatRoom ( _converse , 'lounge@montague.lit' , 'romeo' ) ;
const view = _converse . chatboxviews . get ( 'lounge@montague.lit' ) ;
await view . close ( ) ; // Hack, otherwise we have to mock stanzas.
const muc _jid = 'lounge@montague.lit' ;
const reason = "Please join this groupchat" ;
expect ( _converse . chatboxes . models . length ) . toBe ( 1 ) ;
expect ( _converse . chatboxes . models [ 0 ] . id ) . toBe ( "controlbox" ) ;
const stanza = u . toStanza ( `
< message xmlns = "jabber:client" to = "${_converse.bare_jid}" from = "${from_jid}" id = "9bceb415-f34b-4fa4-80d5-c0d076a24231" >
< x xmlns = "jabber:x:conference" jid = "${muc_jid}" reason = "${reason}" / >
< / m e s s a g e > ` ) ;
await _converse . onDirectMUCInvitation ( stanza ) ;
expect ( window . confirm ) . toHaveBeenCalledWith (
name + ' has invited you to join a groupchat: ' + muc _jid +
', and left the following reason: "' + reason + '"' ) ;
expect ( _converse . chatboxes . models . length ) . toBe ( 2 ) ;
expect ( _converse . chatboxes . models [ 0 ] . id ) . toBe ( 'controlbox' ) ;
expect ( _converse . chatboxes . models [ 1 ] . id ) . toBe ( muc _jid ) ;
done ( ) ;
} ) ) ;
it ( "shows received groupchat messages" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
const text = 'This is a received message' ;
await mock . openAndEnterChatRoom ( _converse , 'lounge@montague.lit' , 'romeo' ) ;
spyOn ( _converse . api , "trigger" ) . and . callThrough ( ) ;
const view = _converse . chatboxviews . get ( 'lounge@montague.lit' ) ;
if ( ! view . el . querySelectorAll ( '.chat-area' ) . length ) {
view . renderChatArea ( ) ;
}
var nick = mock . chatroom _names [ 0 ] ;
view . model . occupants . create ( {
'nick' : nick ,
'muc_jid' : ` ${ view . model . get ( 'jid' ) } / ${ nick } `
} ) ;
2019-06-11 12:16:27 +02:00
2020-04-22 13:11:48 +02:00
const message = $msg ( {
from : 'lounge@montague.lit/' + nick ,
id : '1' ,
to : 'romeo@montague.lit' ,
type : 'groupchat'
} ) . c ( 'body' ) . t ( text ) ;
2020-04-24 17:33:32 +02:00
await view . model . handleMessageStanza ( message . nodeTree ) ;
2020-04-22 13:11:48 +02:00
await u . waitUntil ( ( ) => view . el . querySelectorAll ( '.chat-msg' ) . length ) ;
expect ( view . content . querySelectorAll ( '.chat-msg' ) . length ) . toBe ( 1 ) ;
expect ( view . content . querySelector ( '.chat-msg__text' ) . textContent . trim ( ) ) . toBe ( text ) ;
expect ( _converse . api . trigger ) . toHaveBeenCalledWith ( 'message' , jasmine . any ( Object ) ) ;
done ( ) ;
} ) ) ;
it ( "shows sent groupchat messages" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
await mock . openAndEnterChatRoom ( _converse , 'lounge@montague.lit' , 'romeo' ) ;
spyOn ( _converse . api , "trigger" ) . and . callThrough ( ) ;
const view = _converse . chatboxviews . get ( 'lounge@montague.lit' ) ;
if ( ! view . el . querySelectorAll ( '.chat-area' ) . length ) {
view . renderChatArea ( ) ;
}
const text = 'This is a sent message' ;
const textarea = view . el . querySelector ( '.chat-textarea' ) ;
textarea . value = text ;
view . onKeyDown ( {
target : textarea ,
preventDefault : function preventDefault ( ) { } ,
keyCode : 13
} ) ;
2020-05-15 14:33:31 +02:00
await new Promise ( resolve => view . model . messages . once ( 'rendered' , resolve ) ) ;
2020-04-22 13:11:48 +02:00
expect ( _converse . api . trigger ) . toHaveBeenCalledWith ( 'messageSend' , jasmine . any ( _converse . Message ) ) ;
expect ( view . content . querySelectorAll ( '.chat-msg' ) . length ) . toBe ( 1 ) ;
// Let's check that if we receive the same message again, it's
// not shown.
const stanza = u . toStanza ( `
< message xmlns = "jabber:client"
from = "lounge@montague.lit/romeo"
to = "${_converse.connection.jid}"
type = "groupchat" >
< body > $ { text } < / b o d y >
< stanza - id xmlns = "urn:xmpp:sid:0"
id = "5f3dbc5e-e1d3-4077-a492-693f3769c7ad"
by = "lounge@montague.lit" / >
< origin - id xmlns = "urn:xmpp:sid:0" id = "${view.model.messages.at(0).get('origin_id')}" / >
< / m e s s a g e > ` ) ;
2020-04-24 17:33:32 +02:00
await view . model . handleMessageStanza ( stanza ) ;
2020-04-22 13:11:48 +02:00
expect ( view . content . querySelectorAll ( '.chat-msg' ) . length ) . toBe ( 1 ) ;
expect ( sizzle ( '.chat-msg__text:last' ) . pop ( ) . textContent . trim ( ) ) . toBe ( text ) ;
expect ( view . model . messages . length ) . toBe ( 1 ) ;
// We don't emit an event if it's our own message
expect ( _converse . api . trigger . calls . count ( ) , 1 ) ;
done ( ) ;
} ) ) ;
it ( "will cause the chat area to be scrolled down only if it was at the bottom already" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
const message = 'This message is received while the chat area is scrolled up' ;
await mock . openAndEnterChatRoom ( _converse , 'lounge@montague.lit' , 'romeo' ) ;
const view = _converse . chatboxviews . get ( 'lounge@montague.lit' ) ;
spyOn ( view , 'scrollDown' ) . and . callThrough ( ) ;
// Create enough messages so that there's a scrollbar.
const promises = [ ] ;
for ( let i = 0 ; i < 20 ; i ++ ) {
promises . push (
2020-04-24 17:33:32 +02:00
view . model . handleMessageStanza (
2020-04-22 13:11:48 +02:00
$msg ( {
from : 'lounge@montague.lit/someone' ,
to : 'romeo@montague.lit.com' ,
type : 'groupchat' ,
id : u . getUniqueId ( ) ,
} ) . c ( 'body' ) . t ( 'Message: ' + i ) . tree ( ) )
) ;
}
await Promise . all ( promises ) ;
// Give enough time for `markScrolled` to have been called
setTimeout ( async ( ) => {
view . content . scrollTop = 0 ;
2020-04-24 17:33:32 +02:00
await view . model . handleMessageStanza (
2020-04-22 13:11:48 +02:00
$msg ( {
from : 'lounge@montague.lit/someone' ,
to : 'romeo@montague.lit.com' ,
type : 'groupchat' ,
2018-10-22 09:59:53 +02:00
id : u . getUniqueId ( ) ,
2020-04-22 13:11:48 +02:00
} ) . c ( 'body' ) . t ( message ) . tree ( ) ) ;
2020-05-15 14:33:31 +02:00
await new Promise ( resolve => view . model . messages . once ( 'rendered' , resolve ) ) ;
2020-04-22 13:11:48 +02:00
// Now check that the message appears inside the chatbox in the DOM
const msg _txt = sizzle ( '.chat-msg:last .chat-msg__text' , view . content ) . pop ( ) . textContent ;
expect ( msg _txt ) . toEqual ( message ) ;
expect ( view . content . scrollTop ) . toBe ( 0 ) ;
2018-10-22 09:59:53 +02:00
done ( ) ;
2020-04-22 13:11:48 +02:00
} , 500 ) ;
} ) ) ;
it ( "reconnects when no-acceptable error is returned when sending a message" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
const muc _jid = 'coven@chat.shakespeare.lit' ;
await mock . openAndEnterChatRoom ( _converse , muc _jid , 'romeo' ) ;
const view = _converse . chatboxviews . get ( muc _jid ) ;
expect ( view . model . session . get ( 'connection_status' ) ) . toBe ( converse . ROOMSTATUS . ENTERED ) ;
await mock . sendMessage ( view , 'hello world' ) ;
const stanza = u . toStanza ( `
< message xmlns = 'jabber:client'
from = '${muc_jid}'
type = 'error'
to = '${_converse.bare_jid}' >
< error type = 'cancel' >
< not - acceptable xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' / >
< / e r r o r >
< / m e s s a g e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( stanza ) ) ;
let sent _stanzas = _converse . connection . sent _stanzas ;
const iq = await u . waitUntil ( ( ) => sent _stanzas . filter ( s => sizzle ( ` [xmlns=" ${ Strophe . NS . PING } "] ` , s ) . length ) . pop ( ) ) ;
expect ( Strophe . serialize ( iq ) ) . toBe (
` <iq id=" ${ iq . getAttribute ( 'id' ) } " to="coven@chat.shakespeare.lit/romeo" type="get" xmlns="jabber:client"> ` +
` <ping xmlns="urn:xmpp:ping"/> ` +
` </iq> ` ) ;
const result = u . toStanza ( `
< iq from = '${muc_jid}'
id = '${iq.getAttribute(' id ')}'
to = '${_converse.bare_jid}'
type = 'error' >
< error type = 'cancel' >
< not - acceptable xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' / >
< / e r r o r >
< / i q > ` ) ;
sent _stanzas = _converse . connection . sent _stanzas ;
const index = sent _stanzas . length - 1 ;
_converse . connection . IQ _stanzas = [ ] ;
_converse . connection . _dataRecv ( mock . createRequest ( result ) ) ;
await mock . getRoomFeatures ( _converse , muc _jid ) ;
const pres = await u . waitUntil (
( ) => sent _stanzas . slice ( index ) . filter ( s => s . nodeName === 'presence' ) . pop ( ) ) ;
expect ( Strophe . serialize ( pres ) ) . toBe (
` <presence from=" ${ _converse . jid } " to="coven@chat.shakespeare.lit/romeo" xmlns="jabber:client"> ` +
` <x xmlns="http://jabber.org/protocol/muc"><history maxstanzas="0"/></x> ` +
` </presence> ` ) ;
done ( ) ;
} ) ) ;
it ( "informs users if the room configuration has changed" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
const muc _jid = 'coven@chat.shakespeare.lit' ;
await mock . openAndEnterChatRoom ( _converse , 'coven@chat.shakespeare.lit' , 'romeo' ) ;
const view = _converse . chatboxviews . get ( muc _jid ) ;
expect ( view . model . session . get ( 'connection_status' ) ) . toBe ( converse . ROOMSTATUS . ENTERED ) ;
const stanza = u . toStanza ( `
< message from = '${muc_jid}'
id = '80349046-F26A-44F3-A7A6-54825064DD9E'
to = '${_converse.jid}'
type = 'groupchat' >
< x xmlns = 'http://jabber.org/protocol/muc#user' >
< status code = '170' / >
< / x >
< / m e s s a g e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( stanza ) ) ;
await u . waitUntil ( ( ) => view . el . querySelectorAll ( '.chat-content .chat-info' ) . length ) ;
const info _messages = view . el . querySelectorAll ( '.chat-content .chat-info' ) ;
expect ( info _messages [ 0 ] . textContent . trim ( ) ) . toBe ( 'Groupchat logging is now enabled' ) ;
done ( ) ;
} ) ) ;
it ( "informs users if their nicknames have been changed." ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
/ * T h e s e r v i c e t h e n s e n d s t w o p r e s e n c e s t a n z a s t o t h e f u l l J I D
* of each occupant ( including the occupant who is changing his
* or her room nickname ) , one of type "unavailable" for the old
* nickname and one indicating availability for the new
* nickname .
*
* See : https : //xmpp.org/extensions/xep-0045.html#changenick
*
* < presence
* from = 'coven@montague.lit/thirdwitch'
* id = 'DC352437-C019-40EC-B590-AF29E879AF98'
* to = 'hag66@shakespeare.lit/pda'
* type = 'unavailable' >
* < x xmlns = 'http://jabber.org/protocol/muc#user' >
* < item affiliation = 'member'
* jid = 'hag66@shakespeare.lit/pda'
* nick = 'oldhag'
* role = 'participant' / >
* < status code = '303' / >
* < status code = '110' / >
* < / x >
* < / p r e s e n c e >
*
* < presence
* from = 'coven@montague.lit/oldhag'
* id = '5B4F27A4-25ED-43F7-A699-382C6B4AFC67'
* to = 'hag66@shakespeare.lit/pda' >
* < x xmlns = 'http://jabber.org/protocol/muc#user' >
* < item affiliation = 'member'
* jid = 'hag66@shakespeare.lit/pda'
* role = 'participant' / >
* < status code = '110' / >
* < / x >
* < / p r e s e n c e >
* /
const _ _ = _converse . _ _ ;
await mock . openAndEnterChatRoom ( _converse , 'lounge@montague.lit' , 'oldnick' ) ;
const view = _converse . chatboxviews . get ( 'lounge@montague.lit' ) ;
expect ( view . model . session . get ( 'connection_status' ) ) . toBe ( converse . ROOMSTATUS . ENTERED ) ;
await u . waitUntil ( ( ) => view . el . querySelectorAll ( 'li .occupant-nick' ) . length , 500 ) ;
let occupants = view . el . querySelector ( '.occupant-list' ) ;
expect ( occupants . childElementCount ) . toBe ( 1 ) ;
expect ( occupants . firstElementChild . querySelector ( '.occupant-nick' ) . textContent . trim ( ) ) . toBe ( "oldnick" ) ;
const csntext = await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent ) ;
expect ( csntext . trim ( ) ) . toEqual ( "oldnick has entered the groupchat" ) ;
let presence = $pres ( ) . attrs ( {
from : 'lounge@montague.lit/oldnick' ,
id : 'DC352437-C019-40EC-B590-AF29E879AF98' ,
to : 'romeo@montague.lit/pda' ,
type : 'unavailable'
} )
. c ( 'x' ) . attrs ( { xmlns : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' ) . attrs ( {
affiliation : 'owner' ,
jid : 'romeo@montague.lit/pda' ,
nick : 'newnick' ,
role : 'moderator'
} ) . up ( )
. c ( 'status' ) . attrs ( { code : '303' } ) . up ( )
. c ( 'status' ) . attrs ( { code : '110' } ) . nodeTree ;
2017-07-11 10:41:11 +02:00
2020-04-22 13:11:48 +02:00
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelectorAll ( '.chat-info' ) . length ) ;
2016-08-11 14:00:52 +02:00
2020-04-22 13:11:48 +02:00
expect ( sizzle ( 'div.chat-info:last' ) . pop ( ) . textContent . trim ( ) ) . toBe (
_ _ ( _converse . muc . new _nickname _messages [ "303" ] , "newnick" )
) ;
expect ( view . model . session . get ( 'connection_status' ) ) . toBe ( converse . ROOMSTATUS . ENTERED ) ;
2019-06-11 12:16:27 +02:00
2020-04-22 13:11:48 +02:00
occupants = view . el . querySelector ( '.occupant-list' ) ;
expect ( occupants . childElementCount ) . toBe ( 1 ) ;
2017-07-11 10:41:11 +02:00
2020-04-22 13:11:48 +02:00
presence = $pres ( ) . attrs ( {
from : 'lounge@montague.lit/newnick' ,
id : '5B4F27A4-25ED-43F7-A699-382C6B4AFC67' ,
to : 'romeo@montague.lit/pda'
} )
. c ( 'x' ) . attrs ( { xmlns : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' ) . attrs ( {
affiliation : 'owner' ,
jid : 'romeo@montague.lit/pda' ,
role : 'moderator'
} ) . up ( )
. c ( 'status' ) . attrs ( { code : '110' } ) . nodeTree ;
2018-10-22 09:59:53 +02:00
2020-04-22 13:11:48 +02:00
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
expect ( view . model . session . get ( 'connection_status' ) ) . toBe ( converse . ROOMSTATUS . ENTERED ) ;
expect ( view . content . querySelectorAll ( 'div.chat-info' ) . length ) . toBe ( 1 ) ;
expect ( sizzle ( 'div.chat-info' , view . content ) [ 0 ] . textContent . trim ( ) ) . toBe (
_ _ ( _converse . muc . new _nickname _messages [ "303" ] , "newnick" )
) ;
occupants = view . el . querySelector ( '.occupant-list' ) ;
expect ( occupants . childElementCount ) . toBe ( 1 ) ;
expect ( sizzle ( '.occupant-nick:first' , occupants ) . pop ( ) . textContent . trim ( ) ) . toBe ( "newnick" ) ;
done ( ) ;
} ) ) ;
it ( "queries for the groupchat information before attempting to join the user" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { } ,
async function ( done , _converse ) {
const IQ _stanzas = _converse . connection . IQ _stanzas ;
const muc _jid = 'coven@chat.shakespeare.lit' ;
await _converse . api . rooms . open ( muc _jid , { 'nick' : 'some1' } ) ;
const stanza = await u . waitUntil ( ( ) => _ . filter (
IQ _stanzas ,
iq => iq . querySelector (
` iq[to=" ${ muc _jid } "] query[xmlns="http://jabber.org/protocol/disco#info"] `
) ) . pop ( ) ) ;
// Check that the groupchat queried for the feautures.
expect ( Strophe . serialize ( stanza ) ) . toBe (
` <iq from="romeo@montague.lit/orchard" id=" ${ stanza . getAttribute ( "id" ) } " to=" ${ muc _jid } " type="get" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/disco#info"/> ` +
` </iq> ` ) ;
/ * < i q f r o m = ' c o v e n @ c h a t . s h a k e s p e a r e . l i t '
* id = 'ik3vs715'
* to = 'hag66@shakespeare.lit/pda'
* type = 'result' >
* < query xmlns = 'http://jabber.org/protocol/disco#info' >
* < identity
* category = 'conference'
* name = 'A Dark Cave'
* type = 'text' / >
* < feature var = 'http://jabber.org/protocol/muc' / >
* < feature var = 'muc_passwordprotected' / >
* < feature var = 'muc_hidden' / >
* < feature var = 'muc_temporary' / >
* < feature var = 'muc_open' / >
* < feature var = 'muc_unmoderated' / >
* < feature var = 'muc_nonanonymous' / >
* < / q u e r y >
* < / i q >
* /
const features _stanza = $iq ( {
'from' : muc _jid ,
'id' : stanza . getAttribute ( 'id' ) ,
'to' : 'romeo@montague.lit/desktop' ,
'type' : 'result'
} )
. c ( 'query' , { 'xmlns' : 'http://jabber.org/protocol/disco#info' } )
. c ( 'identity' , {
'category' : 'conference' ,
'name' : 'A Dark Cave' ,
'type' : 'text'
} ) . up ( )
. c ( 'feature' , { 'var' : 'http://jabber.org/protocol/muc' } ) . up ( )
. c ( 'feature' , { 'var' : 'muc_passwordprotected' } ) . up ( )
. c ( 'feature' , { 'var' : 'muc_hidden' } ) . up ( )
. c ( 'feature' , { 'var' : 'muc_temporary' } ) . up ( )
. c ( 'feature' , { 'var' : 'muc_open' } ) . up ( )
. c ( 'feature' , { 'var' : 'muc_unmoderated' } ) . up ( )
. c ( 'feature' , { 'var' : 'muc_nonanonymous' } ) ;
_converse . connection . _dataRecv ( mock . createRequest ( features _stanza ) ) ;
let view = _converse . chatboxviews . get ( 'coven@chat.shakespeare.lit' ) ;
await u . waitUntil ( ( ) => ( view . model . session . get ( 'connection_status' ) === converse . ROOMSTATUS . CONNECTING ) ) ;
view = _converse . chatboxviews . get ( 'coven@chat.shakespeare.lit' ) ;
expect ( view . model . features . get ( 'fetched' ) ) . toBeTruthy ( ) ;
expect ( view . model . features . get ( 'passwordprotected' ) ) . toBe ( true ) ;
expect ( view . model . features . get ( 'hidden' ) ) . toBe ( true ) ;
expect ( view . model . features . get ( 'temporary' ) ) . toBe ( true ) ;
expect ( view . model . features . get ( 'open' ) ) . toBe ( true ) ;
expect ( view . model . features . get ( 'unmoderated' ) ) . toBe ( true ) ;
expect ( view . model . features . get ( 'nonanonymous' ) ) . toBe ( true ) ;
done ( ) ;
} ) ) ;
it ( "updates the shown features when the groupchat configuration has changed" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { 'view_mode' : 'fullscreen' } ,
async function ( done , _converse ) {
let features = [
'http://jabber.org/protocol/muc' ,
'jabber:iq:register' ,
'muc_passwordprotected' ,
'muc_publicroom' ,
'muc_temporary' ,
'muc_open' ,
'muc_unmoderated' ,
'muc_nonanonymous'
] ;
await mock . openAndEnterChatRoom ( _converse , 'room@conference.example.org' , 'romeo' , features ) ;
const jid = 'room@conference.example.org' ;
const view = _converse . chatboxviews . get ( jid ) ;
const info _el = view . el . querySelector ( ".show-room-details-modal" ) ;
info _el . click ( ) ;
const modal = view . model . room _details _modal ;
await u . waitUntil ( ( ) => u . isVisible ( modal . el ) , 1000 ) ;
let features _list = modal . el . querySelector ( '.features-list' ) ;
let features _shown = features _list . textContent . split ( '\n' ) . map ( s => s . trim ( ) ) . filter ( s => s ) ;
expect ( features _shown . join ( ' ' ) ) . toBe (
'Password protected - This groupchat requires a password before entry ' +
'Open - Anyone can join this groupchat ' +
'Temporary - This groupchat will disappear once the last person leaves ' +
'Not anonymous - All other groupchat participants can see your XMPP address ' +
'Not moderated - Participants entering this groupchat can write right away' ) ;
expect ( view . model . features . get ( 'hidden' ) ) . toBe ( false ) ;
expect ( view . model . features . get ( 'mam_enabled' ) ) . toBe ( false ) ;
expect ( view . model . features . get ( 'membersonly' ) ) . toBe ( false ) ;
expect ( view . model . features . get ( 'moderated' ) ) . toBe ( false ) ;
expect ( view . model . features . get ( 'nonanonymous' ) ) . toBe ( true ) ;
expect ( view . model . features . get ( 'open' ) ) . toBe ( true ) ;
expect ( view . model . features . get ( 'passwordprotected' ) ) . toBe ( true ) ;
expect ( view . model . features . get ( 'persistent' ) ) . toBe ( false ) ;
expect ( view . model . features . get ( 'publicroom' ) ) . toBe ( true ) ;
expect ( view . model . features . get ( 'semianonymous' ) ) . toBe ( false ) ;
expect ( view . model . features . get ( 'temporary' ) ) . toBe ( true ) ;
expect ( view . model . features . get ( 'unmoderated' ) ) . toBe ( true ) ;
expect ( view . model . features . get ( 'unsecured' ) ) . toBe ( false ) ;
expect ( view . el . querySelector ( '.chatbox-title__text' ) . textContent . trim ( ) ) . toBe ( 'Room' ) ;
view . el . querySelector ( '.configure-chatroom-button' ) . click ( ) ;
const IQs = _converse . connection . IQ _stanzas ;
let iq = await u . waitUntil ( ( ) => _ . filter (
IQs ,
iq => iq . querySelector (
` iq[to=" ${ jid } "] query[xmlns=" ${ Strophe . NS . MUC _OWNER } "] `
) ) . pop ( ) ) ;
const response _el = u . toStanza (
` <iq xmlns="jabber:client"
type = "result"
to = "romeo@montague.lit/pda"
from = "room@conference.example.org" id = "${iq.getAttribute('id')}" >
< query xmlns = "http://jabber.org/protocol/muc#owner" >
< x xmlns = "jabber:x:data" type = "form" >
< title > Configuration for room @ conference . example . org < / t i t l e >
< instructions > Complete and submit this form to configure the room . < / i n s t r u c t i o n s >
< field var = "FORM_TYPE" type = "hidden" >
< value > http : //jabber.org/protocol/muc#roomconfig</value>
< / f i e l d >
< field type = "fixed" >
< value > Room information < / v a l u e >
< / f i e l d >
< field var = "muc#roomconfig_roomname" type = "text-single" label = "Title" >
< value > Room < / v a l u e >
< / f i e l d >
< field var = "muc#roomconfig_roomdesc" type = "text-single" label = "Description" >
< desc > A brief description of the room < / d e s c >
< value > This room is used in tests < / v a l u e >
< / f i e l d >
< field var = "muc#roomconfig_lang" type = "text-single" label = "Language tag for room (e.g. 'en', 'de', 'fr' etc.)" >
< desc > Indicate the primary language spoken in this room < / d e s c >
< value > en < / v a l u e >
< / f i e l d >
< field var = "muc#roomconfig_persistentroom" type = "boolean" label = "Persistent (room should remain even when it is empty)" >
< desc > Rooms are automatically deleted when they are empty , unless this option is enabled < / d e s c >
< value > 1 < / v a l u e >
< / f i e l d >
< field var = "muc#roomconfig_publicroom" type = "boolean" label = "Include room information in public lists" >
< desc > Enable this to allow people to find the room < / d e s c >
< value > 1 < / v a l u e >
< / f i e l d >
< field type = "fixed" > < value > Access to the room < / v a l u e > < / f i e l d >
< field var = "muc#roomconfig_roomsecret" type = "text-private" label = "Password" > < value / > < / f i e l d >
< field var = "muc#roomconfig_membersonly" type = "boolean" label = "Only allow members to join" >
< desc > Enable this to only allow access for room owners , admins and members < / d e s c >
< / f i e l d >
< field var = "{http://prosody.im/protocol/muc}roomconfig_allowmemberinvites" type = "boolean" label = "Allow members to invite new members" / >
< field type = "fixed" > < value > Permissions in the room < / v a l u e >
< / f i e l d >
< field var = "muc#roomconfig_changesubject" type = "boolean" label = "Allow anyone to set the room's subject" >
< desc > Choose whether anyone , or only moderators , may set the room ' s subject < / d e s c >
< / f i e l d >
< field var = "muc#roomconfig_moderatedroom" type = "boolean" label = "Moderated (require permission to speak)" >
< desc > In moderated rooms occupants must be given permission to speak by a room moderator < / d e s c >
< / f i e l d >
< field var = "muc#roomconfig_whois" type = "list-single" label = "Addresses (JIDs) of room occupants may be viewed by:" >
< option label = "Moderators only" > < value > moderators < / v a l u e > < / o p t i o n >
< option label = "Anyone" > < value > anyone < / v a l u e > < / o p t i o n >
< value > anyone < / v a l u e >
< / f i e l d >
< field type = "fixed" > < value > Other options < / v a l u e > < / f i e l d >
< field var = "muc#roomconfig_historylength" type = "text-single" label = "Maximum number of history messages returned by room" >
< desc > Specify the maximum number of previous messages that should be sent to users when they join the room < / d e s c >
< value > 50 < / v a l u e >
< / f i e l d >
< field var = "muc#roomconfig_defaulthistorymessages" type = "text-single" label = "Default number of history messages returned by room" >
< desc > Specify the number of previous messages sent to new users when they join the room < / d e s c >
< value > 20 < / v a l u e >
< / f i e l d >
< / x >
< / q u e r y >
< / i q > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( response _el ) ) ;
const el = await u . waitUntil ( ( ) => document . querySelector ( '.chatroom-form legend' ) ) ;
expect ( el . textContent . trim ( ) ) . toBe ( "Configuration for room@conference.example.org" ) ;
sizzle ( '[name="muc#roomconfig_membersonly"]' , view . el ) . pop ( ) . click ( ) ;
sizzle ( '[name="muc#roomconfig_roomname"]' , view . el ) . pop ( ) . value = "New room name"
view . el . querySelector ( '.chatroom-form input[type="submit"]' ) . click ( ) ;
iq = await u . waitUntil ( ( ) => _ . filter ( IQs , iq => u . matchesSelector ( iq , ` iq[to=" ${ jid } "][type="set"] ` ) ) . pop ( ) ) ;
const result = $iq ( {
"xmlns" : "jabber:client" ,
"type" : "result" ,
"to" : "romeo@montague.lit/orchard" ,
"from" : "lounge@muc.montague.lit" ,
"id" : iq . getAttribute ( 'id' )
} ) ;
2013-05-11 14:20:07 +02:00
2020-04-22 13:11:48 +02:00
IQs . length = 0 ; // Empty the array
_converse . connection . _dataRecv ( mock . createRequest ( result ) ) ;
iq = await u . waitUntil ( ( ) => _ . filter (
IQs ,
iq => iq . querySelector (
` iq[to=" ${ jid } "] query[xmlns="http://jabber.org/protocol/disco#info"] `
) ) . pop ( ) ) ;
const features _stanza = $iq ( {
'from' : jid ,
'id' : iq . getAttribute ( 'id' ) ,
'to' : 'romeo@montague.lit/desktop' ,
'type' : 'result'
} ) . c ( 'query' , { 'xmlns' : 'http://jabber.org/protocol/disco#info' } )
. c ( 'identity' , {
'category' : 'conference' ,
'name' : 'New room name' ,
'type' : 'text'
} ) . up ( ) ;
features = [
'http://jabber.org/protocol/muc' ,
'jabber:iq:register' ,
'muc_passwordprotected' ,
'muc_hidden' ,
'muc_temporary' ,
'muc_membersonly' ,
'muc_unmoderated' ,
'muc_nonanonymous'
] ;
features . forEach ( f => features _stanza . c ( 'feature' , { 'var' : f } ) . up ( ) ) ;
features _stanza . c ( 'x' , { 'xmlns' : 'jabber:x:data' , 'type' : 'result' } )
. c ( 'field' , { 'var' : 'FORM_TYPE' , 'type' : 'hidden' } )
. c ( 'value' ) . t ( 'http://jabber.org/protocol/muc#roominfo' ) . up ( ) . up ( )
. c ( 'field' , { 'type' : 'text-single' , 'var' : 'muc#roominfo_description' , 'label' : 'Description' } )
. c ( 'value' ) . t ( 'This is the description' ) . up ( ) . up ( )
. c ( 'field' , { 'type' : 'text-single' , 'var' : 'muc#roominfo_occupants' , 'label' : 'Number of occupants' } )
. c ( 'value' ) . t ( 0 ) ;
_converse . connection . _dataRecv ( mock . createRequest ( features _stanza ) ) ;
await u . waitUntil ( ( ) => new Promise ( success => view . model . features . on ( 'change' , success ) ) ) ;
features _list = modal . el . querySelector ( '.features-list' ) ;
features _shown = features _list . textContent . split ( '\n' ) . map ( s => s . trim ( ) ) . filter ( s => s ) ;
expect ( features _shown . join ( ' ' ) ) . toBe (
'Password protected - This groupchat requires a password before entry ' +
'Hidden - This groupchat is not publicly searchable ' +
'Members only - This groupchat is restricted to members only ' +
'Temporary - This groupchat will disappear once the last person leaves ' +
'Not anonymous - All other groupchat participants can see your XMPP address ' +
'Not moderated - Participants entering this groupchat can write right away' ) ;
expect ( view . model . features . get ( 'hidden' ) ) . toBe ( true ) ;
expect ( view . model . features . get ( 'mam_enabled' ) ) . toBe ( false ) ;
expect ( view . model . features . get ( 'membersonly' ) ) . toBe ( true ) ;
expect ( view . model . features . get ( 'moderated' ) ) . toBe ( false ) ;
expect ( view . model . features . get ( 'nonanonymous' ) ) . toBe ( true ) ;
expect ( view . model . features . get ( 'open' ) ) . toBe ( false ) ;
expect ( view . model . features . get ( 'passwordprotected' ) ) . toBe ( true ) ;
expect ( view . model . features . get ( 'persistent' ) ) . toBe ( false ) ;
expect ( view . model . features . get ( 'publicroom' ) ) . toBe ( false ) ;
expect ( view . model . features . get ( 'semianonymous' ) ) . toBe ( false ) ;
expect ( view . model . features . get ( 'temporary' ) ) . toBe ( true ) ;
expect ( view . model . features . get ( 'unmoderated' ) ) . toBe ( true ) ;
expect ( view . model . features . get ( 'unsecured' ) ) . toBe ( false ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chatbox-title__text' ) ? . textContent . trim ( ) === 'New room name' ) ;
done ( ) ;
} ) ) ;
it ( "indicates when a room is no longer anonymous" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
let IQ _id ;
const sendIQ = _converse . connection . sendIQ ;
await mock . openAndEnterChatRoom ( _converse , 'coven@chat.shakespeare.lit' , 'some1' ) ;
spyOn ( _converse . connection , 'sendIQ' ) . and . callFake ( function ( iq , callback , errback ) {
IQ _id = sendIQ . bind ( this ) ( iq , callback , errback ) ;
} ) ;
2017-07-11 10:41:11 +02:00
2020-04-22 13:11:48 +02:00
// We pretend this is a new room, so no disco info is returned.
const features _stanza = $iq ( {
from : 'coven@chat.shakespeare.lit' ,
'id' : IQ _id ,
'to' : 'romeo@montague.lit/desktop' ,
'type' : 'error'
} ) . c ( 'error' , { 'type' : 'cancel' } )
. c ( 'item-not-found' , { 'xmlns' : "urn:ietf:params:xml:ns:xmpp-stanzas" } ) ;
_converse . connection . _dataRecv ( mock . createRequest ( features _stanza ) ) ;
const view = _converse . chatboxviews . get ( 'coven@chat.shakespeare.lit' ) ;
/ * < m e s s a g e x m l n s = " j a b b e r : c l i e n t "
* type = "groupchat"
* to = "romeo@montague.lit/_converse.js-27854181"
* from = "coven@chat.shakespeare.lit" >
* < x xmlns = "http://jabber.org/protocol/muc#user" >
* < status code = "104" / >
* < status code = "172" / >
* < / x >
* < / m e s s a g e >
* /
const message = $msg ( {
type : 'groupchat' ,
to : 'romeo@montague.lit/_converse.js-27854181' ,
from : 'coven@chat.shakespeare.lit'
} ) . c ( 'x' , { xmlns : Strophe . NS . MUC _USER } )
. c ( 'status' , { code : '104' } ) . up ( )
. c ( 'status' , { code : '172' } ) ;
_converse . connection . _dataRecv ( mock . createRequest ( message ) ) ;
await u . waitUntil ( ( ) => view . el . querySelectorAll ( '.chat-content .chat-info' ) . length ) ;
const chat _body = view . el . querySelector ( '.chatroom-body' ) ;
expect ( sizzle ( '.message:last' , chat _body ) . pop ( ) . textContent . trim ( ) )
. toBe ( 'This groupchat is now no longer anonymous' ) ;
done ( ) ;
} ) ) ;
it ( "informs users if they have been kicked out of the groupchat" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
/ * < p r e s e n c e
* from = 'harfleur@chat.shakespeare.lit/pistol'
* to = 'pistol@shakespeare.lit/harfleur'
* type = 'unavailable' >
* < x xmlns = 'http://jabber.org/protocol/muc#user' >
* < item affiliation = 'none' role = 'none' >
* < actor nick = 'Fluellen' / >
* < reason > Avaunt , you cullion ! < / r e a s o n >
* < / i t e m >
* < status code = '110' / >
* < status code = '307' / >
* < / x >
* < / p r e s e n c e >
* /
await mock . openAndEnterChatRoom ( _converse , 'lounge@montague.lit' , 'romeo' ) ;
var presence = $pres ( ) . attrs ( {
from : 'lounge@montague.lit/romeo' ,
to : 'romeo@montague.lit/pda' ,
type : 'unavailable'
} )
. c ( 'x' ) . attrs ( { xmlns : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' ) . attrs ( {
affiliation : 'none' ,
jid : 'romeo@montague.lit/pda' ,
role : 'none'
} )
. c ( 'actor' ) . attrs ( { nick : 'Fluellen' } ) . up ( )
. c ( 'reason' ) . t ( 'Avaunt, you cullion!' ) . up ( )
. up ( )
. c ( 'status' ) . attrs ( { code : '110' } ) . up ( )
. c ( 'status' ) . attrs ( { code : '307' } ) . nodeTree ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
const view = _converse . chatboxviews . get ( 'lounge@montague.lit' ) ;
expect ( u . isVisible ( view . el . querySelector ( '.chat-area' ) ) ) . toBeFalsy ( ) ;
expect ( u . isVisible ( view . el . querySelector ( '.occupants' ) ) ) . toBeFalsy ( ) ;
const chat _body = view . el . querySelector ( '.chatroom-body' ) ;
expect ( chat _body . querySelectorAll ( '.disconnect-msg' ) . length ) . toBe ( 3 ) ;
expect ( chat _body . querySelector ( '.disconnect-msg:first-child' ) . textContent . trim ( ) ) . toBe (
'You have been kicked from this groupchat' ) ;
expect ( chat _body . querySelector ( '.disconnect-msg:nth-child(2)' ) . textContent . trim ( ) ) . toBe (
'This action was done by Fluellen.' ) ;
expect ( chat _body . querySelector ( '.disconnect-msg:nth-child(3)' ) . textContent . trim ( ) ) . toBe (
'The reason given is: "Avaunt, you cullion!".' ) ;
done ( ) ;
} ) ) ;
2020-07-22 10:11:05 +02:00
it ( "informs users if they have exited the groupchat due to a technical reason" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
/ * < p r e s e n c e
* from = 'harfleur@chat.shakespeare.lit/pistol'
* to = 'pistol@shakespeare.lit/harfleur'
* type = 'unavailable' >
* < x xmlns = 'http://jabber.org/protocol/muc#user' >
* < item affiliation = 'none' role = 'none' >
* < actor nick = 'Fluellen' / >
* < reason > Avaunt , you cullion ! < / r e a s o n >
* < / i t e m >
* < status code = '110' / >
* < status code = '307' / >
* < / x >
* < / p r e s e n c e >
* /
await mock . openAndEnterChatRoom ( _converse , 'lounge@montague.lit' , 'romeo' ) ;
var presence = $pres ( ) . attrs ( {
from : 'lounge@montague.lit/romeo' ,
to : 'romeo@montague.lit/pda' ,
type : 'unavailable'
} )
. c ( 'x' ) . attrs ( { xmlns : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' ) . attrs ( {
affiliation : 'none' ,
jid : 'romeo@montague.lit/pda' ,
role : 'none'
} )
. c ( 'reason' ) . t ( 'Flux capacitor overload!' ) . up ( )
. up ( )
. c ( 'status' ) . attrs ( { code : '110' } ) . up ( )
. c ( 'status' ) . attrs ( { code : '333' } ) . up ( )
. c ( 'status' ) . attrs ( { code : '307' } ) . nodeTree ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
const view = _converse . chatboxviews . get ( 'lounge@montague.lit' ) ;
expect ( u . isVisible ( view . el . querySelector ( '.chat-area' ) ) ) . toBeFalsy ( ) ;
expect ( u . isVisible ( view . el . querySelector ( '.occupants' ) ) ) . toBeFalsy ( ) ;
const chat _body = view . el . querySelector ( '.chatroom-body' ) ;
expect ( chat _body . querySelectorAll ( '.disconnect-msg' ) . length ) . toBe ( 2 ) ;
expect ( chat _body . querySelector ( '.disconnect-msg:first-child' ) . textContent . trim ( ) ) . toBe (
'You have exited this groupchat due to a technical problem' ) ;
expect ( chat _body . querySelector ( '.disconnect-msg:nth-child(2)' ) . textContent . trim ( ) ) . toBe (
'The reason given is: "Flux capacitor overload!".' ) ;
done ( ) ;
} ) ) ;
2020-04-22 13:11:48 +02:00
it ( "can be saved to, and retrieved from, browserStorage" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { } ,
async function ( done , _converse ) {
await mock . openChatRoom ( _converse , 'lounge' , 'montague.lit' , 'romeo' ) ;
// We instantiate a new ChatBoxes collection, which by default
// will be empty.
await mock . openControlBox ( _converse ) ;
const newchatboxes = new _converse . ChatBoxes ( ) ;
expect ( newchatboxes . length ) . toEqual ( 0 ) ;
// The chatboxes will then be fetched from browserStorage inside the
// onConnected method
newchatboxes . onConnected ( ) ;
await new Promise ( resolve => _converse . api . listen . once ( 'chatBoxesFetched' , resolve ) ) ;
expect ( newchatboxes . length ) . toEqual ( 2 ) ;
// Check that the chatrooms retrieved from browserStorage
// have the same attributes values as the original ones.
const attrs = [ 'id' , 'box_id' , 'visible' ] ;
let new _attrs , old _attrs ;
for ( var i = 0 ; i < attrs . length ; i ++ ) {
new _attrs = _ . map ( _ . map ( newchatboxes . models , 'attributes' ) , attrs [ i ] ) ;
old _attrs = _ . map ( _ . map ( _converse . chatboxes . models , 'attributes' ) , attrs [ i ] ) ;
// FIXME: should have have to sort here? Order must
// probably be the same...
// This should be fixed once the controlbox always opens
// only on the right.
expect ( _ . isEqual ( new _attrs . sort ( ) , old _attrs . sort ( ) ) ) . toEqual ( true ) ;
}
_converse . rosterview . render ( ) ;
done ( ) ;
} ) ) ;
it ( "can be closed again by clicking a DOM element with class 'close-chatbox-button'" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { } ,
async function ( done , _converse ) {
await mock . openChatRoom ( _converse , 'lounge' , 'montague.lit' , 'romeo' ) ;
const view = _converse . chatboxviews . get ( 'lounge@montague.lit' ) ;
spyOn ( view , 'close' ) . and . callThrough ( ) ;
spyOn ( _converse . api , "trigger" ) . and . callThrough ( ) ;
spyOn ( view . model , 'leave' ) ;
view . delegateEvents ( ) ; // We need to rebind all events otherwise our spy won't be called
spyOn ( _converse . api , 'confirm' ) . and . callFake ( ( ) => Promise . resolve ( true ) ) ;
const button = await u . waitUntil ( ( ) => view . el . querySelector ( '.close-chatbox-button' ) ) ;
button . click ( ) ;
await u . waitUntil ( ( ) => view . close . calls . count ( ) ) ;
expect ( view . model . leave ) . toHaveBeenCalled ( ) ;
await u . waitUntil ( ( ) => _converse . api . trigger . calls . count ( ) ) ;
expect ( _converse . api . trigger ) . toHaveBeenCalledWith ( 'chatBoxClosed' , jasmine . any ( Object ) ) ;
done ( ) ;
} ) ) ;
it ( "informs users of role and affiliation changes" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
const muc _jid = 'lounge@montague.lit' ;
await mock . openAndEnterChatRoom ( _converse , muc _jid , 'romeo' ) ;
const view = _converse . api . chatviews . get ( muc _jid ) ;
let presence = $pres ( {
'from' : 'lounge@montague.lit/annoyingGuy' ,
'id' : '27C55F89-1C6A-459A-9EB5-77690145D624' ,
'to' : 'romeo@montague.lit/desktop'
} )
. c ( 'x' , { 'xmlns' : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' , {
'jid' : 'annoyingguy@montague.lit' ,
'affiliation' : 'member' ,
'role' : 'participant'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
const csntext = await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent ) ;
expect ( csntext . trim ( ) ) . toEqual ( "romeo and annoyingGuy have entered the groupchat" ) ;
2013-05-11 14:20:07 +02:00
2020-04-22 13:11:48 +02:00
presence = $pres ( {
'from' : 'lounge@montague.lit/annoyingGuy' ,
'to' : 'romeo@montague.lit/desktop'
} )
. c ( 'x' , { 'xmlns' : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' , {
'jid' : 'annoyingguy@montague.lit' ,
'affiliation' : 'member' ,
'role' : 'visitor'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
2020-04-23 13:36:16 +02:00
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ===
"romeo has entered the groupchat\nannoyingGuy has been muted" ) ;
2017-07-11 10:41:11 +02:00
2020-04-22 13:11:48 +02:00
presence = $pres ( {
'from' : 'lounge@montague.lit/annoyingGuy' ,
'to' : 'romeo@montague.lit/desktop'
} )
. c ( 'x' , { 'xmlns' : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' , {
'jid' : 'annoyingguy@montague.lit' ,
'affiliation' : 'member' ,
'role' : 'participant'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
2020-04-23 13:36:16 +02:00
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ===
"romeo has entered the groupchat\nannoyingGuy has been given a voice" ) ;
2013-05-11 14:20:07 +02:00
2020-04-22 13:11:48 +02:00
// Check that we don't see an info message concerning the role,
// if the affiliation has changed.
presence = $pres ( {
'from' : 'lounge@montague.lit/annoyingGuy' ,
'to' : 'romeo@montague.lit/desktop'
} )
. c ( 'x' , { 'xmlns' : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' , {
'jid' : 'annoyingguy@montague.lit' ,
'affiliation' : 'none' ,
'role' : 'visitor'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) =>
Array . from ( view . el . querySelectorAll ( '.chat-info__message' ) ) . pop ( ) ? . textContent . trim ( ) ===
"annoyingGuy is no longer a member of this groupchat"
) ;
done ( ) ;
} ) ) ;
it ( "notifies users of role and affiliation changes for members not currently in the groupchat" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
const muc _jid = 'lounge@montague.lit' ;
await mock . openAndEnterChatRoom ( _converse , muc _jid , 'romeo' ) ;
const view = _converse . api . chatviews . get ( muc _jid ) ;
let message = $msg ( {
from : 'lounge@montague.lit' ,
id : '2CF9013B-E8A8-42A1-9633-85AD7CA12F40' ,
to : 'romeo@montague.lit'
} )
. c ( 'x' , { 'xmlns' : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' , {
'jid' : 'absentguy@montague.lit' ,
'affiliation' : 'member' ,
'role' : 'none'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( message ) ) ;
await u . waitUntil ( ( ) => view . model . occupants . length > 1 ) ;
expect ( view . model . occupants . length ) . toBe ( 2 ) ;
expect ( view . model . occupants . findWhere ( { 'jid' : 'absentguy@montague.lit' } ) . get ( 'affiliation' ) ) . toBe ( 'member' ) ;
message = $msg ( {
from : 'lounge@montague.lit' ,
id : '2CF9013B-E8A8-42A1-9633-85AD7CA12F41' ,
to : 'romeo@montague.lit'
} )
. c ( 'x' , { 'xmlns' : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' , {
'jid' : 'absentguy@montague.lit' ,
'affiliation' : 'none' ,
'role' : 'none'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( message ) ) ;
expect ( view . model . occupants . length ) . toBe ( 2 ) ;
expect ( view . model . occupants . findWhere ( { 'jid' : 'absentguy@montague.lit' } ) . get ( 'affiliation' ) ) . toBe ( 'none' ) ;
2017-07-11 10:41:11 +02:00
2020-04-22 13:11:48 +02:00
done ( ) ;
} ) ) ;
} ) ;
2013-05-11 14:20:07 +02:00
2017-07-11 10:41:11 +02:00
2020-04-22 13:11:48 +02:00
describe ( "Each chat groupchat can take special commands" , function ( ) {
it ( "takes /help to show the available commands" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
spyOn ( window , 'confirm' ) . and . callFake ( ( ) => true ) ;
await mock . openAndEnterChatRoom ( _converse , 'lounge@montague.lit' , 'romeo' ) ;
const view = _converse . chatboxviews . get ( 'lounge@montague.lit' ) ;
let textarea = view . el . querySelector ( '.chat-textarea' ) ;
const enter = { 'target' : textarea , 'preventDefault' : function preventDefault ( ) { } , 'keyCode' : 13 } ;
textarea . value = '/help' ;
view . onKeyDown ( enter ) ;
2020-05-15 14:33:31 +02:00
await u . waitUntil ( ( ) => sizzle ( 'converse-chat-help .chat-info' , view . el ) . length ) ;
const chat _help _el = view . el . querySelector ( 'converse-chat-help' ) ;
let info _messages = sizzle ( '.chat-info' , chat _help _el ) ;
expect ( info _messages . length ) . toBe ( 19 ) ;
2020-04-22 13:11:48 +02:00
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/voice: Allow muted user to post messages' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/topic: Set groupchat subject (alias for /subject)' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/subject: Set groupchat subject' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/revoke: Revoke the user\'s current affiliation' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/register: Register your nickname' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/owner: Grant ownership of this groupchat' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/op: Grant moderator role to user' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/nick: Change your nickname' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/mute: Remove user\'s ability to post messages' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/modtools: Opens up the moderator tools GUI' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/member: Grant membership to a user' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/me: Write in 3rd person' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/kick: Kick user from groupchat' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/help: Show this menu' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/destroy: Remove this groupchat' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/deop: Change user role to participant' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/clear: Clear the chat area' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/ban: Ban user by changing their affiliation to outcast' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/admin: Change user\'s affiliation to admin' ) ;
const occupant = view . model . occupants . findWhere ( { 'jid' : _converse . bare _jid } ) ;
occupant . set ( 'affiliation' , 'admin' ) ;
2020-05-15 14:33:31 +02:00
view . el . querySelector ( '.close-chat-help' ) . click ( ) ;
await u . waitUntil ( ( ) => chat _help _el . hidden ) ;
2020-04-22 13:11:48 +02:00
textarea . value = '/help' ;
view . onKeyDown ( enter ) ;
2020-05-15 14:33:31 +02:00
await u . waitUntil ( ( ) => ! chat _help _el . hidden ) ;
info _messages = sizzle ( '.chat-info' , chat _help _el ) ;
expect ( info _messages . length ) . toBe ( 18 ) ;
2020-04-22 13:11:48 +02:00
let commands = info _messages . map ( m => m . textContent . replace ( /:.*$/ , '' ) ) ;
expect ( commands ) . toEqual ( [
"/admin" , "/ban" , "/clear" , "/deop" , "/destroy" ,
"/help" , "/kick" , "/me" , "/member" , "/modtools" , "/mute" , "/nick" ,
"/op" , "/register" , "/revoke" , "/subject" , "/topic" , "/voice"
] ) ;
occupant . set ( 'affiliation' , 'member' ) ;
2020-05-15 14:33:31 +02:00
view . el . querySelector ( '.close-chat-help' ) . click ( ) ;
await u . waitUntil ( ( ) => chat _help _el . hidden ) ;
2020-04-22 13:11:48 +02:00
textarea . value = '/help' ;
view . onKeyDown ( enter ) ;
2020-05-15 14:33:31 +02:00
await u . waitUntil ( ( ) => ! chat _help _el . hidden ) ;
info _messages = sizzle ( '.chat-info' , chat _help _el ) ;
2020-04-22 13:11:48 +02:00
expect ( info _messages . length ) . toBe ( 9 ) ;
commands = info _messages . map ( m => m . textContent . replace ( /:.*$/ , '' ) ) ;
expect ( commands ) . toEqual ( [ "/clear" , "/help" , "/kick" , "/me" , "/modtools" , "/mute" , "/nick" , "/register" , "/voice" ] ) ;
2020-05-15 14:33:31 +02:00
view . el . querySelector ( '.close-chat-help' ) . click ( ) ;
await u . waitUntil ( ( ) => chat _help _el . hidden ) ;
expect ( view . model . get ( 'show_help_messages' ) ) . toBe ( false ) ;
2020-04-22 13:11:48 +02:00
occupant . set ( 'role' , 'participant' ) ;
2020-05-15 14:33:31 +02:00
// Role changes causes rerender, so we need to get the new textarea
2020-04-22 13:11:48 +02:00
textarea = view . el . querySelector ( '.chat-textarea' ) ;
textarea . value = '/help' ;
view . onKeyDown ( enter ) ;
2020-05-15 14:33:31 +02:00
await u . waitUntil ( ( ) => view . model . get ( 'show_help_messages' ) ) ;
await u . waitUntil ( ( ) => ! chat _help _el . hidden ) ;
info _messages = sizzle ( '.chat-info' , chat _help _el ) ;
2020-04-22 13:11:48 +02:00
expect ( info _messages . length ) . toBe ( 5 ) ;
commands = info _messages . map ( m => m . textContent . replace ( /:.*$/ , '' ) ) ;
expect ( commands ) . toEqual ( [ "/clear" , "/help" , "/me" , "/nick" , "/register" ] ) ;
// Test that /topic is available if all users may change the subject
// Note: we're making a shortcut here, this value should never be set manually
view . model . config . set ( 'changesubject' , true ) ;
2020-05-15 14:33:31 +02:00
view . el . querySelector ( '.close-chat-help' ) . click ( ) ;
await u . waitUntil ( ( ) => chat _help _el . hidden ) ;
2020-04-22 13:11:48 +02:00
textarea . value = '/help' ;
view . onKeyDown ( enter ) ;
2020-05-15 14:33:31 +02:00
await u . waitUntil ( ( ) => ! chat _help _el . hidden , 1000 ) ;
info _messages = sizzle ( '.chat-info' , chat _help _el ) ;
2020-04-22 13:11:48 +02:00
expect ( info _messages . length ) . toBe ( 7 ) ;
commands = info _messages . map ( m => m . textContent . replace ( /:.*$/ , '' ) ) ;
expect ( commands ) . toEqual ( [ "/clear" , "/help" , "/me" , "/nick" , "/register" , "/subject" , "/topic" ] ) ;
done ( ) ;
} ) ) ;
it ( "takes /help to show the available commands and commands can be disabled by config" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { muc _disable _slash _commands : [ 'mute' , 'voice' ] } ,
async function ( done , _converse ) {
await mock . openAndEnterChatRoom ( _converse , 'lounge@montague.lit' , 'romeo' ) ;
const view = _converse . chatboxviews . get ( 'lounge@montague.lit' ) ;
var textarea = view . el . querySelector ( '.chat-textarea' ) ;
const enter = { 'target' : textarea , 'preventDefault' : function ( ) { } , 'keyCode' : 13 } ;
spyOn ( window , 'confirm' ) . and . callFake ( ( ) => true ) ;
textarea . value = '/clear' ;
view . onKeyDown ( enter ) ;
textarea . value = '/help' ;
view . onKeyDown ( enter ) ;
2020-05-15 14:33:31 +02:00
await u . waitUntil ( ( ) => sizzle ( '.chat-info:not(.chat-event)' , view . el ) . length ) ;
2020-04-22 13:11:48 +02:00
const info _messages = sizzle ( '.chat-info:not(.chat-event)' , view . el ) ;
2020-05-15 14:33:31 +02:00
expect ( info _messages . length ) . toBe ( 17 ) ;
2020-04-22 13:11:48 +02:00
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/topic: Set groupchat subject (alias for /subject)' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/subject: Set groupchat subject' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/revoke: Revoke the user\'s current affiliation' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/register: Register your nickname' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/owner: Grant ownership of this groupchat' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/op: Grant moderator role to user' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/nick: Change your nickname' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/modtools: Opens up the moderator tools GUI' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/member: Grant membership to a user' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/me: Write in 3rd person' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/kick: Kick user from groupchat' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/help: Show this menu' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/destroy: Remove this groupchat' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/deop: Change user role to participant' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/clear: Clear the chat area' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/ban: Ban user by changing their affiliation to outcast' ) ;
expect ( info _messages . pop ( ) . textContent . trim ( ) ) . toBe ( '/admin: Change user\'s affiliation to admin' ) ;
done ( ) ;
} ) ) ;
it ( "takes /member to make an occupant a member" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
let iq _stanza ;
await mock . openAndEnterChatRoom ( _converse , 'lounge@muc.montague.lit' , 'romeo' ) ;
const view = _converse . chatboxviews . get ( 'lounge@muc.montague.lit' ) ;
/ * W e d o n ' t s h o w j o i n / l e a v e m e s s a g e s f o r e x i s t i n g o c c u p a n t s . W e
* know about them because we receive their presences before we
* receive our own .
* /
const presence = $pres ( {
to : 'romeo@montague.lit/orchard' ,
from : 'lounge@muc.montague.lit/marc'
} ) . c ( 'x' , { xmlns : Strophe . NS . MUC _USER } )
. c ( 'item' , {
'affiliation' : 'none' ,
'jid' : 'marc@montague.lit/_converse.js-290929789' ,
'role' : 'participant'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
expect ( view . model . occupants . length ) . toBe ( 2 ) ;
2016-12-05 11:50:01 +01:00
2020-04-22 13:11:48 +02:00
const textarea = view . el . querySelector ( '.chat-textarea' ) ;
let sent _stanza ;
spyOn ( _converse . connection , 'send' ) . and . callFake ( ( stanza ) => {
sent _stanza = stanza ;
} ) ;
2016-12-05 11:50:01 +01:00
2020-04-22 13:11:48 +02:00
// First check that an error message appears when a
// non-existent nick is used.
textarea . value = '/member chris Welcome to the club!' ;
view . onKeyDown ( {
target : textarea ,
preventDefault : function preventDefault ( ) { } ,
keyCode : 13
} ) ;
expect ( _converse . connection . send ) . not . toHaveBeenCalled ( ) ;
await u . waitUntil ( ( ) => view . el . querySelectorAll ( '.chat-error' ) . length ) ;
expect ( view . el . querySelector ( '.chat-error' ) . textContent . trim ( ) )
. toBe ( 'Error: couldn\'t find a groupchat participant based on your arguments' ) ;
// Now test with an existing nick
textarea . value = '/member marc Welcome to the club!' ;
view . onKeyDown ( {
target : textarea ,
preventDefault : function preventDefault ( ) { } ,
keyCode : 13
} ) ;
expect ( _converse . connection . send ) . toHaveBeenCalled ( ) ;
expect ( Strophe . serialize ( sent _stanza ) ) . toBe (
` <iq id=" ${ sent _stanza . getAttribute ( 'id' ) } " to="lounge@muc.montague.lit" type="set" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/muc#admin"> ` +
` <item affiliation="member" jid="marc@montague.lit"> ` +
` <reason>Welcome to the club!</reason> ` +
` </item> ` +
` </query> ` +
` </iq> ` ) ;
let result = $iq ( {
"xmlns" : "jabber:client" ,
"type" : "result" ,
"to" : "romeo@montague.lit/orchard" ,
"from" : "lounge@muc.montague.lit" ,
"id" : sent _stanza . getAttribute ( 'id' )
} ) ;
_converse . connection . IQ _stanzas = [ ] ;
_converse . connection . _dataRecv ( mock . createRequest ( result ) ) ;
iq _stanza = await u . waitUntil ( ( ) => _ . filter (
_converse . connection . IQ _stanzas ,
iq => iq . querySelector ( 'iq[to="lounge@muc.montague.lit"][type="get"] item[affiliation="member"]' ) ) . pop ( )
) ;
expect ( Strophe . serialize ( iq _stanza ) ) . toBe (
` <iq id=" ${ iq _stanza . getAttribute ( 'id' ) } " to="lounge@muc.montague.lit" type="get" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/muc#admin"> ` +
` <item affiliation="member"/> ` +
` </query> ` +
` </iq> ` )
expect ( view . model . occupants . length ) . toBe ( 2 ) ;
result = $iq ( {
"xmlns" : "jabber:client" ,
"type" : "result" ,
"to" : "romeo@montague.lit/orchard" ,
"from" : "lounge@muc.montague.lit" ,
"id" : iq _stanza . getAttribute ( "id" )
} ) . c ( "query" , { "xmlns" : "http://jabber.org/protocol/muc#admin" } )
. c ( "item" , { "jid" : "marc" , "affiliation" : "member" } ) ;
_converse . connection . _dataRecv ( mock . createRequest ( result ) ) ;
expect ( view . model . occupants . length ) . toBe ( 2 ) ;
iq _stanza = await u . waitUntil ( ( ) => _ . filter (
_converse . connection . IQ _stanzas ,
iq => iq . querySelector ( 'iq[to="lounge@muc.montague.lit"][type="get"] item[affiliation="owner"]' ) ) . pop ( )
) ;
expect ( Strophe . serialize ( iq _stanza ) ) . toBe (
` <iq id=" ${ iq _stanza . getAttribute ( 'id' ) } " to="lounge@muc.montague.lit" type="get" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/muc#admin"> ` +
` <item affiliation="owner"/> ` +
` </query> ` +
` </iq> ` )
expect ( view . model . occupants . length ) . toBe ( 2 ) ;
result = $iq ( {
"xmlns" : "jabber:client" ,
"type" : "result" ,
"to" : "romeo@montague.lit/orchard" ,
"from" : "lounge@muc.montague.lit" ,
"id" : iq _stanza . getAttribute ( "id" )
} ) . c ( "query" , { "xmlns" : "http://jabber.org/protocol/muc#admin" } )
. c ( "item" , { "jid" : "romeo@montague.lit" , "affiliation" : "owner" } ) ;
_converse . connection . _dataRecv ( mock . createRequest ( result ) ) ;
expect ( view . model . occupants . length ) . toBe ( 2 ) ;
iq _stanza = await u . waitUntil ( ( ) => _ . filter (
_converse . connection . IQ _stanzas ,
iq => iq . querySelector ( 'iq[to="lounge@muc.montague.lit"][type="get"] item[affiliation="admin"]' ) ) . pop ( )
) ;
expect ( Strophe . serialize ( iq _stanza ) ) . toBe (
` <iq id=" ${ iq _stanza . getAttribute ( 'id' ) } " to="lounge@muc.montague.lit" type="get" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/muc#admin"> ` +
` <item affiliation="admin"/> ` +
` </query> ` +
` </iq> ` )
expect ( view . model . occupants . length ) . toBe ( 2 ) ;
result = $iq ( {
"xmlns" : "jabber:client" ,
"type" : "result" ,
"to" : "romeo@montague.lit/orchard" ,
"from" : "lounge@muc.montague.lit" ,
"id" : iq _stanza . getAttribute ( "id" )
} ) . c ( "query" , { "xmlns" : "http://jabber.org/protocol/muc#admin" } )
_converse . connection . _dataRecv ( mock . createRequest ( result ) ) ;
await u . waitUntil ( ( ) => view . el . querySelectorAll ( '.occupant' ) . length , 500 ) ;
await u . waitUntil ( ( ) => view . el . querySelectorAll ( '.badge' ) . length > 1 ) ;
expect ( view . model . occupants . length ) . toBe ( 2 ) ;
expect ( view . el . querySelectorAll ( '.occupant' ) . length ) . toBe ( 2 ) ;
done ( ) ;
} ) ) ;
it ( "takes /topic to set the groupchat topic" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
await mock . openAndEnterChatRoom ( _converse , 'lounge@montague.lit' , 'romeo' ) ;
const view = _converse . chatboxviews . get ( 'lounge@montague.lit' ) ;
spyOn ( view , 'clearMessages' ) ;
let sent _stanza ;
spyOn ( _converse . connection , 'send' ) . and . callFake ( function ( stanza ) {
sent _stanza = stanza ;
} ) ;
// Check the alias /topic
const textarea = view . el . querySelector ( '.chat-textarea' ) ;
textarea . value = '/topic This is the groupchat subject' ;
view . onKeyDown ( {
target : textarea ,
preventDefault : function preventDefault ( ) { } ,
keyCode : 13
} ) ;
expect ( _converse . connection . send ) . toHaveBeenCalled ( ) ;
expect ( sent _stanza . textContent . trim ( ) ) . toBe ( 'This is the groupchat subject' ) ;
// Check /subject
textarea . value = '/subject This is a new subject' ;
view . onKeyDown ( {
target : textarea ,
preventDefault : function preventDefault ( ) { } ,
keyCode : 13
} ) ;
2017-07-11 10:41:11 +02:00
2020-04-22 13:11:48 +02:00
expect ( sent _stanza . textContent . trim ( ) ) . toBe ( 'This is a new subject' ) ;
expect ( Strophe . serialize ( sent _stanza ) . toLocaleString ( ) ) . toBe (
'<message from="romeo@montague.lit/orchard" to="lounge@montague.lit" type="groupchat" xmlns="jabber:client">' +
'<subject xmlns="jabber:client">This is a new subject</subject>' +
'</message>' ) ;
// Check case insensitivity
textarea . value = '/Subject This is yet another subject' ;
view . onKeyDown ( {
target : textarea ,
preventDefault : function preventDefault ( ) { } ,
keyCode : 13
} ) ;
expect ( sent _stanza . textContent . trim ( ) ) . toBe ( 'This is yet another subject' ) ;
expect ( Strophe . serialize ( sent _stanza ) . toLocaleString ( ) ) . toBe (
'<message from="romeo@montague.lit/orchard" to="lounge@montague.lit" type="groupchat" xmlns="jabber:client">' +
'<subject xmlns="jabber:client">This is yet another subject</subject>' +
'</message>' ) ;
// Check unsetting the topic
textarea . value = '/topic' ;
view . onKeyDown ( {
target : textarea ,
preventDefault : function preventDefault ( ) { } ,
keyCode : 13
} ) ;
expect ( Strophe . serialize ( sent _stanza ) . toLocaleString ( ) ) . toBe (
'<message from="romeo@montague.lit/orchard" to="lounge@montague.lit" type="groupchat" xmlns="jabber:client">' +
'<subject xmlns="jabber:client"></subject>' +
'</message>' ) ;
done ( ) ;
} ) ) ;
it ( "takes /clear to clear messages" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
await mock . openAndEnterChatRoom ( _converse , 'lounge@montague.lit' , 'romeo' ) ;
const view = _converse . chatboxviews . get ( 'lounge@montague.lit' ) ;
spyOn ( view , 'clearMessages' ) ;
const textarea = view . el . querySelector ( '.chat-textarea' )
textarea . value = '/clear' ;
view . onKeyDown ( {
target : textarea ,
preventDefault : function preventDefault ( ) { } ,
keyCode : 13
} ) ;
expect ( view . clearMessages ) . toHaveBeenCalled ( ) ;
done ( ) ;
} ) ) ;
it ( "takes /owner to make a user an owner" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
let sent _IQ , IQ _id ;
const sendIQ = _converse . connection . sendIQ ;
spyOn ( _converse . connection , 'sendIQ' ) . and . callFake ( function ( iq , callback , errback ) {
sent _IQ = iq ;
IQ _id = sendIQ . bind ( this ) ( iq , callback , errback ) ;
} ) ;
2016-12-05 11:50:01 +01:00
2020-04-22 13:11:48 +02:00
await mock . openAndEnterChatRoom ( _converse , 'lounge@montague.lit' , 'romeo' ) ;
const view = _converse . chatboxviews . get ( 'lounge@montague.lit' ) ;
spyOn ( view . model , 'setAffiliation' ) . and . callThrough ( ) ;
spyOn ( view , 'validateRoleOrAffiliationChangeArgs' ) . and . callThrough ( ) ;
2019-08-08 15:12:18 +02:00
2020-04-22 13:11:48 +02:00
let presence = $pres ( {
'from' : 'lounge@montague.lit/annoyingGuy' ,
'id' : '27C55F89-1C6A-459A-9EB5-77690145D624' ,
'to' : 'romeo@montague.lit/desktop'
} )
. c ( 'x' , { 'xmlns' : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' , {
'jid' : 'annoyingguy@montague.lit' ,
'affiliation' : 'member' ,
'role' : 'participant'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
var textarea = view . el . querySelector ( '.chat-textarea' )
textarea . value = '/owner' ;
view . onKeyDown ( {
target : textarea ,
preventDefault : function preventDefault ( ) { } ,
keyCode : 13
} ) ;
expect ( view . validateRoleOrAffiliationChangeArgs ) . toHaveBeenCalled ( ) ;
const err _msg = await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-error' ) ) ;
expect ( err _msg . textContent . trim ( ) ) . toBe (
"Error: the \"owner\" command takes two arguments, the user's nickname and optionally a reason." ) ;
expect ( view . model . setAffiliation ) . not . toHaveBeenCalled ( ) ;
// XXX: Calling onFormSubmitted directly, trying
// again via triggering Event doesn't work for some weird
// reason.
textarea . value = '/owner nobody You\'re responsible' ;
view . onFormSubmitted ( new Event ( 'submit' ) ) ;
await u . waitUntil ( ( ) => view . el . querySelectorAll ( '.chat-error' ) . length === 2 ) ;
expect ( Array . from ( view . el . querySelectorAll ( '.chat-error' ) ) . pop ( ) . textContent . trim ( ) ) . toBe (
"Error: couldn't find a groupchat participant based on your arguments" ) ;
expect ( view . model . setAffiliation ) . not . toHaveBeenCalled ( ) ;
// Call now with the correct of arguments.
// XXX: Calling onFormSubmitted directly, trying
// again via triggering Event doesn't work for some weird
// reason.
textarea . value = '/owner annoyingGuy You\'re responsible' ;
view . onFormSubmitted ( new Event ( 'submit' ) ) ;
expect ( view . validateRoleOrAffiliationChangeArgs . calls . count ( ) ) . toBe ( 3 ) ;
expect ( view . model . setAffiliation ) . toHaveBeenCalled ( ) ;
// Check that the member list now gets updated
2020-09-25 13:17:56 +02:00
expect ( Strophe . serialize ( sent _IQ ) ) . toBe (
2020-04-22 13:11:48 +02:00
` <iq id=" ${ IQ _id } " to="lounge@montague.lit" type="set" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/muc#admin"> ` +
` <item affiliation="owner" jid="annoyingguy@montague.lit"> ` +
` <reason>You're responsible</reason> ` +
` </item> ` +
` </query> ` +
` </iq> ` ) ;
presence = $pres ( {
'from' : 'lounge@montague.lit/annoyingGuy' ,
'id' : '27C55F89-1C6A-459A-9EB5-77690145D628' ,
'to' : 'romeo@montague.lit/desktop'
} )
. c ( 'x' , { 'xmlns' : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' , {
'jid' : 'annoyingguy@montague.lit' ,
'affiliation' : 'owner' ,
'role' : 'participant'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) =>
Array . from ( view . el . querySelectorAll ( '.chat-info__message' ) ) . pop ( ) ? . textContent . trim ( ) ===
"annoyingGuy is now an owner of this groupchat"
) ;
done ( ) ;
} ) ) ;
it ( "takes /ban to ban a user" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
let sent _IQ , IQ _id ;
const sendIQ = _converse . connection . sendIQ ;
spyOn ( _converse . connection , 'sendIQ' ) . and . callFake ( function ( iq , callback , errback ) {
sent _IQ = iq ;
IQ _id = sendIQ . bind ( this ) ( iq , callback , errback ) ;
} ) ;
2018-11-20 18:10:55 +01:00
2020-04-22 13:11:48 +02:00
await mock . openAndEnterChatRoom ( _converse , 'lounge@montague.lit' , 'romeo' ) ;
const view = _converse . chatboxviews . get ( 'lounge@montague.lit' ) ;
spyOn ( view . model , 'setAffiliation' ) . and . callThrough ( ) ;
spyOn ( view , 'validateRoleOrAffiliationChangeArgs' ) . and . callThrough ( ) ;
2018-11-20 18:10:55 +01:00
2020-04-22 13:11:48 +02:00
let presence = $pres ( {
'from' : 'lounge@montague.lit/annoyingGuy' ,
'id' : '27C55F89-1C6A-459A-9EB5-77690145D624' ,
'to' : 'romeo@montague.lit/desktop'
} )
. c ( 'x' , { 'xmlns' : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' , {
'jid' : 'annoyingguy@montague.lit' ,
'affiliation' : 'member' ,
'role' : 'participant'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
const textarea = view . el . querySelector ( '.chat-textarea' )
textarea . value = '/ban' ;
view . onKeyDown ( {
target : textarea ,
preventDefault : function preventDefault ( ) { } ,
keyCode : 13
} ) ;
expect ( view . validateRoleOrAffiliationChangeArgs ) . toHaveBeenCalled ( ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.message:last-child' ) ? . textContent ? . trim ( ) ===
"Error: the \"ban\" command takes two arguments, the user's nickname and optionally a reason." ) ;
expect ( view . model . setAffiliation ) . not . toHaveBeenCalled ( ) ;
// Call now with the correct amount of arguments.
// XXX: Calling onFormSubmitted directly, trying
// again via triggering Event doesn't work for some weird
// reason.
textarea . value = '/ban annoyingGuy You\'re annoying' ;
view . onFormSubmitted ( new Event ( 'submit' ) ) ;
expect ( view . validateRoleOrAffiliationChangeArgs . calls . count ( ) ) . toBe ( 2 ) ;
expect ( view . model . setAffiliation ) . toHaveBeenCalled ( ) ;
// Check that the member list now gets updated
2020-09-25 13:17:56 +02:00
expect ( Strophe . serialize ( sent _IQ ) ) . toBe (
2020-04-22 13:11:48 +02:00
` <iq id=" ${ IQ _id } " to="lounge@montague.lit" type="set" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/muc#admin"> ` +
` <item affiliation="outcast" jid="annoyingguy@montague.lit"> ` +
` <reason>You're annoying</reason> ` +
` </item> ` +
` </query> ` +
` </iq> ` ) ;
presence = $pres ( {
'from' : 'lounge@montague.lit/annoyingGuy' ,
'id' : '27C55F89-1C6A-459A-9EB5-77690145D628' ,
'to' : 'romeo@montague.lit/desktop'
} ) . c ( 'x' , { 'xmlns' : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' , {
'jid' : 'annoyingguy@montague.lit' ,
'affiliation' : 'outcast' ,
'role' : 'participant'
} ) . c ( 'actor' , { 'nick' : 'romeo' } ) . up ( )
. c ( 'reason' ) . t ( "You're annoying" ) . up ( ) . up ( )
. c ( 'status' , { 'code' : '301' } ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelectorAll ( '.chat-info' ) . length === 2 ) ;
expect ( view . el . querySelectorAll ( '.chat-info__message' ) [ 1 ] . textContent . trim ( ) ) . toBe ( "annoyingGuy has been banned by romeo" ) ;
expect ( view . el . querySelector ( '.chat-info:last-child q' ) . textContent . trim ( ) ) . toBe ( "You're annoying" ) ;
presence = $pres ( {
'from' : 'lounge@montague.lit/joe2' ,
'id' : '27C55F89-1C6A-459A-9EB5-77690145D624' ,
'to' : 'romeo@montague.lit/desktop'
} )
. c ( 'x' , { 'xmlns' : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' , {
'jid' : 'joe2@montague.lit' ,
'affiliation' : 'member' ,
'role' : 'participant'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
textarea . value = '/ban joe22' ;
view . onFormSubmitted ( new Event ( 'submit' ) ) ;
2020-05-15 14:33:31 +02:00
await u . waitUntil ( ( ) => view . el . querySelector ( 'converse-chat-message:last-child' ) ? . textContent ? . trim ( ) ===
2020-04-22 13:11:48 +02:00
"Error: couldn't find a groupchat participant based on your arguments" ) ;
done ( ) ;
} ) ) ;
it ( "takes a /kick command to kick a user" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
let sent _IQ , IQ _id ;
const sendIQ = _converse . connection . sendIQ ;
spyOn ( _converse . connection , 'sendIQ' ) . and . callFake ( function ( iq , callback , errback ) {
sent _IQ = iq ;
IQ _id = sendIQ . bind ( this ) ( iq , callback , errback ) ;
} ) ;
2019-08-08 15:12:18 +02:00
2020-04-22 13:11:48 +02:00
const muc _jid = 'lounge@montague.lit' ;
await mock . openAndEnterChatRoom ( _converse , muc _jid , 'romeo' ) ;
const view = _converse . api . chatviews . get ( muc _jid ) ;
spyOn ( view . model , 'setRole' ) . and . callThrough ( ) ;
spyOn ( view , 'validateRoleOrAffiliationChangeArgs' ) . and . callThrough ( ) ;
2019-08-08 15:12:18 +02:00
2020-04-22 13:11:48 +02:00
let presence = $pres ( {
'from' : 'lounge@montague.lit/annoying guy' ,
'id' : '27C55F89-1C6A-459A-9EB5-77690145D624' ,
'to' : 'romeo@montague.lit/desktop'
} )
. c ( 'x' , { 'xmlns' : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' , {
'jid' : 'annoyingguy@montague.lit' ,
'affiliation' : 'none' ,
'role' : 'participant'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
const textarea = view . el . querySelector ( '.chat-textarea' )
textarea . value = '/kick' ;
view . onKeyDown ( {
target : textarea ,
preventDefault : function preventDefault ( ) { } ,
keyCode : 13
} ) ;
expect ( view . validateRoleOrAffiliationChangeArgs ) . toHaveBeenCalled ( ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.message:last-child' ) ? . textContent ? . trim ( ) ===
"Error: the \"kick\" command takes two arguments, the user's nickname and optionally a reason." ) ;
expect ( view . model . setRole ) . not . toHaveBeenCalled ( ) ;
// Call now with the correct amount of arguments.
// XXX: Calling onFormSubmitted directly, trying
// again via triggering Event doesn't work for some weird
// reason.
textarea . value = '/kick @annoying guy You\'re annoying' ;
view . onFormSubmitted ( new Event ( 'submit' ) ) ;
expect ( view . validateRoleOrAffiliationChangeArgs . calls . count ( ) ) . toBe ( 2 ) ;
expect ( view . model . setRole ) . toHaveBeenCalled ( ) ;
2020-09-25 13:17:56 +02:00
expect ( Strophe . serialize ( sent _IQ ) ) . toBe (
2020-04-22 13:11:48 +02:00
` <iq id=" ${ IQ _id } " to="lounge@montague.lit" type="set" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/muc#admin"> ` +
` <item nick="annoying guy" role="none"> ` +
` <reason>You're annoying</reason> ` +
` </item> ` +
` </query> ` +
` </iq> ` ) ;
/ * < p r e s e n c e
* from = 'harfleur@chat.shakespeare.lit/pistol'
* to = 'gower@shakespeare.lit/cell'
* type = 'unavailable' >
* < x xmlns = 'http://jabber.org/protocol/muc#user' >
* < item affiliation = 'none' role = 'none' / >
* < status code = '307' / >
* < / x >
* < / p r e s e n c e >
* /
presence = $pres ( {
'from' : 'lounge@montague.lit/annoying guy' ,
'to' : 'romeo@montague.lit/desktop' ,
'type' : 'unavailable'
} )
. c ( 'x' , { 'xmlns' : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' , {
'affiliation' : 'none' ,
'role' : 'none'
} ) . c ( 'actor' , { 'nick' : 'romeo' } ) . up ( )
. c ( 'reason' ) . t ( "You're annoying" ) . up ( ) . up ( )
. c ( 'status' , { 'code' : '307' } ) ;
2020-05-15 14:33:31 +02:00
2020-04-22 13:11:48 +02:00
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelectorAll ( '.chat-info' ) . length === 2 ) ;
expect ( view . el . querySelectorAll ( '.chat-info__message' ) [ 1 ] . textContent . trim ( ) ) . toBe ( "annoying guy has been kicked out by romeo" ) ;
expect ( view . el . querySelector ( '.chat-info:last-child q' ) . textContent . trim ( ) ) . toBe ( "You're annoying" ) ;
done ( ) ;
} ) ) ;
it ( "takes /op and /deop to make a user a moderator or not" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
const muc _jid = 'lounge@montague.lit' ;
await mock . openAndEnterChatRoom ( _converse , muc _jid , 'romeo' ) ;
const view = _converse . api . chatviews . get ( muc _jid ) ;
let sent _IQ , IQ _id ;
const sendIQ = _converse . connection . sendIQ ;
spyOn ( _converse . connection , 'sendIQ' ) . and . callFake ( function ( iq , callback , errback ) {
sent _IQ = iq ;
IQ _id = sendIQ . bind ( this ) ( iq , callback , errback ) ;
} ) ;
spyOn ( view . model , 'setRole' ) . and . callThrough ( ) ;
spyOn ( view , 'validateRoleOrAffiliationChangeArgs' ) . and . callThrough ( ) ;
// New user enters the groupchat
/ * < p r e s e n c e
* from = 'coven@chat.shakespeare.lit/thirdwitch'
* id = '27C55F89-1C6A-459A-9EB5-77690145D624'
* to = 'crone1@shakespeare.lit/desktop' >
* < x xmlns = 'http://jabber.org/protocol/muc#user' >
* < item affiliation = 'member' role = 'moderator' / >
* < / x >
* < / p r e s e n c e >
* /
let presence = $pres ( {
'from' : 'lounge@montague.lit/trustworthyguy' ,
'id' : '27C55F89-1C6A-459A-9EB5-77690145D624' ,
'to' : 'romeo@montague.lit/desktop'
} )
. c ( 'x' , { 'xmlns' : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' , {
'jid' : 'trustworthyguy@montague.lit' ,
'affiliation' : 'member' ,
'role' : 'participant'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
const csntext = await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent ) ;
expect ( csntext . trim ( ) ) . toEqual ( "romeo and trustworthyguy have entered the groupchat" ) ;
const textarea = view . el . querySelector ( '.chat-textarea' )
textarea . value = '/op' ;
view . onKeyDown ( {
target : textarea ,
preventDefault : function preventDefault ( ) { } ,
keyCode : 13
} ) ;
2018-11-20 18:10:55 +01:00
2020-04-22 13:11:48 +02:00
expect ( view . validateRoleOrAffiliationChangeArgs ) . toHaveBeenCalled ( ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.message:last-child' ) ? . textContent ? . trim ( ) ===
"Error: the \"op\" command takes two arguments, the user's nickname and optionally a reason." ) ;
expect ( view . model . setRole ) . not . toHaveBeenCalled ( ) ;
// Call now with the correct amount of arguments.
// XXX: Calling onFormSubmitted directly, trying
// again via triggering Event doesn't work for some weird
// reason.
textarea . value = '/op trustworthyguy You\'re trustworthy' ;
view . onFormSubmitted ( new Event ( 'submit' ) ) ;
expect ( view . validateRoleOrAffiliationChangeArgs . calls . count ( ) ) . toBe ( 2 ) ;
expect ( view . model . setRole ) . toHaveBeenCalled ( ) ;
2020-09-25 13:17:56 +02:00
expect ( Strophe . serialize ( sent _IQ ) ) . toBe (
2020-04-22 13:11:48 +02:00
` <iq id=" ${ IQ _id } " to="lounge@montague.lit" type="set" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/muc#admin"> ` +
` <item nick="trustworthyguy" role="moderator"> ` +
` <reason>You're trustworthy</reason> ` +
` </item> ` +
` </query> ` +
` </iq> ` ) ;
/ * < p r e s e n c e
* from = 'coven@chat.shakespeare.lit/thirdwitch'
* to = 'crone1@shakespeare.lit/desktop' >
* < x xmlns = 'http://jabber.org/protocol/muc#user' >
* < item affiliation = 'member'
* jid = 'hag66@shakespeare.lit/pda'
* role = 'moderator' / >
* < / x >
* < / p r e s e n c e >
* /
presence = $pres ( {
'from' : 'lounge@montague.lit/trustworthyguy' ,
'to' : 'romeo@montague.lit/desktop'
} )
. c ( 'x' , { 'xmlns' : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' , {
'jid' : 'trustworthyguy@montague.lit' ,
'affiliation' : 'member' ,
'role' : 'moderator'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
2020-04-23 13:36:16 +02:00
// Check now that things get restored when the user is given a voice
await u . waitUntil (
( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . split ( '\n' , 2 ) . pop ( ) ? . trim ( ) ===
"trustworthyguy is now a moderator" ) ;
2020-04-22 13:11:48 +02:00
// Call now with the correct amount of arguments.
// XXX: Calling onFormSubmitted directly, trying
// again via triggering Event doesn't work for some weird
// reason.
textarea . value = '/deop trustworthyguy Perhaps not' ;
view . onFormSubmitted ( new Event ( 'submit' ) ) ;
expect ( view . validateRoleOrAffiliationChangeArgs . calls . count ( ) ) . toBe ( 3 ) ;
expect ( view . model . setRole ) . toHaveBeenCalled ( ) ;
2020-09-25 13:17:56 +02:00
expect ( Strophe . serialize ( sent _IQ ) ) . toBe (
2020-04-22 13:11:48 +02:00
` <iq id=" ${ IQ _id } " to="lounge@montague.lit" type="set" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/muc#admin"> ` +
` <item nick="trustworthyguy" role="participant"> ` +
` <reason>Perhaps not</reason> ` +
` </item> ` +
` </query> ` +
` </iq> ` ) ;
/ * < p r e s e n c e
* from = 'coven@chat.shakespeare.lit/thirdwitch'
* to = 'crone1@shakespeare.lit/desktop' >
* < x xmlns = 'http://jabber.org/protocol/muc#user' >
* < item affiliation = 'member'
* jid = 'hag66@shakespeare.lit/pda'
* role = 'participant' / >
* < / x >
* < / p r e s e n c e >
* /
presence = $pres ( {
'from' : 'lounge@montague.lit/trustworthyguy' ,
'to' : 'romeo@montague.lit/desktop'
} ) . c ( 'x' , { 'xmlns' : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' , {
'jid' : 'trustworthyguy@montague.lit' ,
'affiliation' : 'member' ,
'role' : 'participant'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
2020-04-23 13:36:16 +02:00
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . includes ( "trustworthyguy is no longer a moderator" ) ) ;
2020-04-22 13:11:48 +02:00
done ( ) ;
} ) ) ;
it ( "takes /mute and /voice to mute and unmute a user" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
const muc _jid = 'lounge@montague.lit' ;
await mock . openAndEnterChatRoom ( _converse , muc _jid , 'romeo' ) ;
const view = _converse . api . chatviews . get ( muc _jid ) ;
var sent _IQ , IQ _id ;
var sendIQ = _converse . connection . sendIQ ;
spyOn ( _converse . connection , 'sendIQ' ) . and . callFake ( function ( iq , callback , errback ) {
sent _IQ = iq ;
IQ _id = sendIQ . bind ( this ) ( iq , callback , errback ) ;
} ) ;
spyOn ( view . model , 'setRole' ) . and . callThrough ( ) ;
spyOn ( view , 'validateRoleOrAffiliationChangeArgs' ) . and . callThrough ( ) ;
// New user enters the groupchat
/ * < p r e s e n c e
* from = 'coven@chat.shakespeare.lit/thirdwitch'
* id = '27C55F89-1C6A-459A-9EB5-77690145D624'
* to = 'crone1@shakespeare.lit/desktop' >
* < x xmlns = 'http://jabber.org/protocol/muc#user' >
* < item affiliation = 'member' role = 'participant' / >
* < / x >
* < / p r e s e n c e >
* /
let presence = $pres ( {
'from' : 'lounge@montague.lit/annoyingGuy' ,
'id' : '27C55F89-1C6A-459A-9EB5-77690145D624' ,
'to' : 'romeo@montague.lit/desktop'
} )
. c ( 'x' , { 'xmlns' : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' , {
'jid' : 'annoyingguy@montague.lit' ,
'affiliation' : 'member' ,
'role' : 'participant'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
const csntext = await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent ) ;
expect ( csntext . trim ( ) ) . toEqual ( "romeo and annoyingGuy have entered the groupchat" ) ;
const textarea = view . el . querySelector ( '.chat-textarea' )
textarea . value = '/mute' ;
view . onKeyDown ( {
target : textarea ,
preventDefault : function preventDefault ( ) { } ,
keyCode : 13
} ) ;
2019-07-26 13:32:21 +02:00
2020-04-22 13:11:48 +02:00
expect ( view . validateRoleOrAffiliationChangeArgs ) . toHaveBeenCalled ( ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.message:last-child' ) ? . textContent ? . trim ( ) ===
"Error: the \"mute\" command takes two arguments, the user's nickname and optionally a reason." ) ;
expect ( view . model . setRole ) . not . toHaveBeenCalled ( ) ;
// Call now with the correct amount of arguments.
// XXX: Calling onFormSubmitted directly, trying
// again via triggering Event doesn't work for some weird
// reason.
textarea . value = '/mute annoyingGuy You\'re annoying' ;
view . onFormSubmitted ( new Event ( 'submit' ) ) ;
expect ( view . validateRoleOrAffiliationChangeArgs . calls . count ( ) ) . toBe ( 2 ) ;
expect ( view . model . setRole ) . toHaveBeenCalled ( ) ;
2020-09-25 13:17:56 +02:00
expect ( Strophe . serialize ( sent _IQ ) ) . toBe (
2020-04-22 13:11:48 +02:00
` <iq id=" ${ IQ _id } " to="lounge@montague.lit" type="set" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/muc#admin"> ` +
` <item nick="annoyingGuy" role="visitor"> ` +
` <reason>You're annoying</reason> ` +
` </item> ` +
` </query> ` +
` </iq> ` ) ;
/ * < p r e s e n c e
* from = 'coven@chat.shakespeare.lit/thirdwitch'
* to = 'crone1@shakespeare.lit/desktop' >
* < x xmlns = 'http://jabber.org/protocol/muc#user' >
* < item affiliation = 'member'
* jid = 'hag66@shakespeare.lit/pda'
* role = 'visitor' / >
* < / x >
* < / p r e s e n c e >
* /
presence = $pres ( {
'from' : 'lounge@montague.lit/annoyingGuy' ,
'to' : 'romeo@montague.lit/desktop'
} )
. c ( 'x' , { 'xmlns' : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' , {
'jid' : 'annoyingguy@montague.lit' ,
'affiliation' : 'member' ,
'role' : 'visitor'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
2020-04-23 13:36:16 +02:00
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . includes ( "annoyingGuy has been muted" ) ) ;
2020-04-22 13:11:48 +02:00
// Call now with the correct of arguments.
// XXX: Calling onFormSubmitted directly, trying
// again via triggering Event doesn't work for some weird
// reason.
textarea . value = '/voice annoyingGuy Now you can talk again' ;
view . onFormSubmitted ( new Event ( 'submit' ) ) ;
expect ( view . validateRoleOrAffiliationChangeArgs . calls . count ( ) ) . toBe ( 3 ) ;
expect ( view . model . setRole ) . toHaveBeenCalled ( ) ;
2020-09-25 13:17:56 +02:00
expect ( Strophe . serialize ( sent _IQ ) ) . toBe (
2020-04-22 13:11:48 +02:00
` <iq id=" ${ IQ _id } " to="lounge@montague.lit" type="set" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/muc#admin"> ` +
` <item nick="annoyingGuy" role="participant"> ` +
` <reason>Now you can talk again</reason> ` +
` </item> ` +
` </query> ` +
` </iq> ` ) ;
/ * < p r e s e n c e
* from = 'coven@chat.shakespeare.lit/thirdwitch'
* to = 'crone1@shakespeare.lit/desktop' >
* < x xmlns = 'http://jabber.org/protocol/muc#user' >
* < item affiliation = 'member'
* jid = 'hag66@shakespeare.lit/pda'
* role = 'visitor' / >
* < / x >
* < / p r e s e n c e >
* /
presence = $pres ( {
'from' : 'lounge@montague.lit/annoyingGuy' ,
'to' : 'romeo@montague.lit/desktop'
} )
. c ( 'x' , { 'xmlns' : 'http://jabber.org/protocol/muc#user' } )
. c ( 'item' , {
'jid' : 'annoyingguy@montague.lit' ,
'affiliation' : 'member' ,
'role' : 'participant'
2019-07-26 13:32:21 +02:00
} ) ;
2020-04-22 13:11:48 +02:00
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
2020-04-23 13:36:16 +02:00
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . includes ( "annoyingGuy has been given a voice" ) ) ;
2020-04-22 13:11:48 +02:00
done ( ) ;
} ) ) ;
it ( "takes /destroy to destroy a muc" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
const muc _jid = 'lounge@montague.lit' ;
const new _muc _jid = 'foyer@montague.lit' ;
await mock . openAndEnterChatRoom ( _converse , muc _jid , 'romeo' ) ;
let view = _converse . api . chatviews . get ( muc _jid ) ;
spyOn ( _converse . api , 'confirm' ) . and . callThrough ( ) ;
let textarea = view . el . querySelector ( '.chat-textarea' ) ;
textarea . value = '/destroy' ;
view . onFormSubmitted ( new Event ( 'submit' ) ) ;
let modal = await u . waitUntil ( ( ) => document . querySelector ( '.modal-dialog' ) ) ;
await u . waitUntil ( ( ) => u . isVisible ( modal ) ) ;
let challenge _el = modal . querySelector ( '[name="challenge"]' ) ;
challenge _el . value = muc _jid + 'e' ;
const reason _el = modal . querySelector ( '[name="reason"]' ) ;
reason _el . value = 'Moved to a new location' ;
const newjid _el = modal . querySelector ( '[name="newjid"]' ) ;
newjid _el . value = new _muc _jid ;
let submit = modal . querySelector ( '[type="submit"]' ) ;
submit . click ( ) ;
expect ( u . isVisible ( modal ) ) . toBeTruthy ( ) ;
expect ( u . hasClass ( 'error' , challenge _el ) ) . toBeTruthy ( ) ;
challenge _el . value = muc _jid ;
submit . click ( ) ;
let sent _IQs = _converse . connection . IQ _stanzas ;
let sent _IQ = await u . waitUntil ( ( ) => sent _IQs . filter ( iq => iq . querySelector ( 'destroy' ) ) . pop ( ) ) ;
expect ( Strophe . serialize ( sent _IQ ) ) . toBe (
` <iq id=" ${ sent _IQ . getAttribute ( 'id' ) } " to=" ${ muc _jid } " type="set" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/muc#owner"> ` +
` <destroy jid=" ${ new _muc _jid } "> ` +
` <reason> ` +
` Moved to a new location ` +
` </reason> ` +
` </destroy> ` +
` </query> ` +
` </iq> ` ) ;
let result _stanza = $iq ( {
'type' : 'result' ,
'id' : sent _IQ . getAttribute ( 'id' ) ,
'from' : view . model . get ( 'jid' ) ,
'to' : _converse . connection . jid
} ) ;
spyOn ( _converse . api , "trigger" ) . and . callThrough ( ) ;
expect ( _converse . chatboxes . length ) . toBe ( 2 ) ;
_converse . connection . _dataRecv ( mock . createRequest ( result _stanza ) ) ;
await u . waitUntil ( ( ) => ( view . model . session . get ( 'connection_status' ) === converse . ROOMSTATUS . DISCONNECTED ) ) ;
await u . waitUntil ( ( ) => _converse . chatboxes . length === 1 ) ;
expect ( _converse . api . trigger ) . toHaveBeenCalledWith ( 'chatBoxClosed' , jasmine . any ( Object ) ) ;
// Try again without reason or new JID
_converse . connection . IQ _stanzas = [ ] ;
sent _IQs = _converse . connection . IQ _stanzas ;
await mock . openAndEnterChatRoom ( _converse , new _muc _jid , 'romeo' ) ;
view = _converse . api . chatviews . get ( new _muc _jid ) ;
textarea = view . el . querySelector ( '.chat-textarea' ) ;
textarea . value = '/destroy' ;
view . onFormSubmitted ( new Event ( 'submit' ) ) ;
modal = await u . waitUntil ( ( ) => document . querySelector ( '.modal-dialog' ) ) ;
await u . waitUntil ( ( ) => u . isVisible ( modal ) ) ;
challenge _el = modal . querySelector ( '[name="challenge"]' ) ;
challenge _el . value = new _muc _jid ;
submit = modal . querySelector ( '[type="submit"]' ) ;
submit . click ( ) ;
sent _IQ = await u . waitUntil ( ( ) => sent _IQs . filter ( iq => iq . querySelector ( 'destroy' ) ) . pop ( ) ) ;
expect ( Strophe . serialize ( sent _IQ ) ) . toBe (
` <iq id=" ${ sent _IQ . getAttribute ( 'id' ) } " to=" ${ new _muc _jid } " type="set" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/muc#owner"> ` +
` <destroy/> ` +
` </query> ` +
` </iq> ` ) ;
result _stanza = $iq ( {
'type' : 'result' ,
'id' : sent _IQ . getAttribute ( 'id' ) ,
'from' : view . model . get ( 'jid' ) ,
'to' : _converse . connection . jid
} ) ;
expect ( _converse . chatboxes . length ) . toBe ( 2 ) ;
_converse . connection . _dataRecv ( mock . createRequest ( result _stanza ) ) ;
await u . waitUntil ( ( ) => ( view . model . session . get ( 'connection_status' ) === converse . ROOMSTATUS . DISCONNECTED ) ) ;
await u . waitUntil ( ( ) => _converse . chatboxes . length === 1 ) ;
done ( ) ;
} ) ) ;
} ) ;
2019-07-26 13:32:21 +02:00
2020-04-22 13:11:48 +02:00
describe ( "When attempting to enter a groupchat" , function ( ) {
2019-07-26 13:32:21 +02:00
2020-04-22 13:11:48 +02:00
it ( "will use the nickname set in the global settings if the user doesn't have a VCard nickname" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { 'nickname' : 'Benedict-Cucumberpatch' } ,
async function ( done , _converse ) {
2016-12-08 13:44:18 +01:00
2020-04-22 13:11:48 +02:00
await mock . openChatRoomViaModal ( _converse , 'roomy@muc.montague.lit' ) ;
const view = _converse . chatboxviews . get ( 'roomy@muc.montague.lit' ) ;
expect ( view . model . get ( 'nick' ) ) . toBe ( 'Benedict-Cucumberpatch' ) ;
done ( ) ;
} ) ) ;
2016-12-08 13:44:18 +01:00
2020-04-22 13:11:48 +02:00
it ( "will show an error message if the groupchat requires a password" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { } ,
async function ( done , _converse ) {
2017-07-11 10:41:11 +02:00
2020-04-22 13:11:48 +02:00
const muc _jid = 'protected' ;
await mock . openChatRoomViaModal ( _converse , muc _jid , 'romeo' ) ;
const view = _converse . chatboxviews . get ( muc _jid ) ;
spyOn ( view , 'renderPasswordForm' ) . and . callThrough ( ) ;
2017-06-05 14:50:29 +02:00
2020-04-22 13:11:48 +02:00
const presence = $pres ( ) . attrs ( {
'from' : ` ${ muc _jid } /romeo ` ,
'id' : u . getUniqueId ( ) ,
'to' : 'romeo@montague.lit/pda' ,
'type' : 'error'
} ) . c ( 'x' ) . attrs ( { xmlns : 'http://jabber.org/protocol/muc' } ) . up ( )
. c ( 'error' ) . attrs ( { by : 'lounge@montague.lit' , type : 'auth' } )
. c ( 'not-authorized' ) . attrs ( { xmlns : 'urn:ietf:params:xml:ns:xmpp-stanzas' } ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
const chat _body = view . el . querySelector ( '.chatroom-body' ) ;
expect ( view . renderPasswordForm ) . toHaveBeenCalled ( ) ;
expect ( chat _body . querySelectorAll ( 'form.chatroom-form' ) . length ) . toBe ( 1 ) ;
expect ( chat _body . querySelector ( '.chatroom-form label' ) . textContent . trim ( ) )
. toBe ( 'This groupchat requires a password' ) ;
// Let's submit the form
spyOn ( view . model , 'join' ) ;
const input _el = view . el . querySelector ( '[name="password"]' ) ;
input _el . value = 'secret' ;
view . el . querySelector ( 'input[type=submit]' ) . click ( ) ;
expect ( view . model . join ) . toHaveBeenCalledWith ( 'romeo' , 'secret' ) ;
done ( ) ;
} ) ) ;
it ( "will show an error message if the groupchat is members-only and the user not included" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
const muc _jid = 'members-only@muc.montague.lit'
await mock . openChatRoomViaModal ( _converse , muc _jid , 'romeo' ) ;
const view = _converse . chatboxviews . get ( muc _jid ) ;
const iq = await u . waitUntil ( ( ) => _ . filter (
_converse . connection . IQ _stanzas ,
iq => iq . querySelector (
` iq[to=" ${ muc _jid } "] query[xmlns="http://jabber.org/protocol/disco#info"] `
) ) . pop ( ) ) ;
// State that the chat is members-only via the features IQ
const features _stanza = $iq ( {
'from' : muc _jid ,
'id' : iq . getAttribute ( 'id' ) ,
'to' : 'romeo@montague.lit/desktop' ,
'type' : 'result'
} )
. c ( 'query' , { 'xmlns' : 'http://jabber.org/protocol/disco#info' } )
. c ( 'identity' , {
'category' : 'conference' ,
'name' : 'A Dark Cave' ,
'type' : 'text'
} ) . up ( )
. c ( 'feature' , { 'var' : 'http://jabber.org/protocol/muc' } ) . up ( )
. c ( 'feature' , { 'var' : 'muc_hidden' } ) . up ( )
. c ( 'feature' , { 'var' : 'muc_temporary' } ) . up ( )
. c ( 'feature' , { 'var' : 'muc_membersonly' } ) . up ( ) ;
_converse . connection . _dataRecv ( mock . createRequest ( features _stanza ) ) ;
await u . waitUntil ( ( ) => view . model . session . get ( 'connection_status' ) === converse . ROOMSTATUS . CONNECTING ) ;
const presence = $pres ( ) . attrs ( {
from : ` ${ muc _jid } /romeo ` ,
id : u . getUniqueId ( ) ,
to : 'romeo@montague.lit/pda' ,
type : 'error'
} ) . c ( 'x' ) . attrs ( { xmlns : 'http://jabber.org/protocol/muc' } ) . up ( )
. c ( 'error' ) . attrs ( { by : 'lounge@montague.lit' , type : 'auth' } )
. c ( 'registration-required' ) . attrs ( { xmlns : 'urn:ietf:params:xml:ns:xmpp-stanzas' } ) . nodeTree ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
expect ( view . el . querySelector ( '.chatroom-body .disconnect-container .disconnect-msg:last-child' ) . textContent . trim ( ) )
. toBe ( 'You are not on the member list of this groupchat.' ) ;
done ( ) ;
} ) ) ;
it ( "will show an error message if the user has been banned" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
const muc _jid = 'off-limits@muc.montague.lit'
await mock . openChatRoomViaModal ( _converse , muc _jid , 'romeo' ) ;
const iq = await u . waitUntil ( ( ) => _ . filter (
_converse . connection . IQ _stanzas ,
iq => iq . querySelector (
` iq[to=" ${ muc _jid } "] query[xmlns="http://jabber.org/protocol/disco#info"] `
) ) . pop ( ) ) ;
const features _stanza = $iq ( {
'from' : muc _jid ,
'id' : iq . getAttribute ( 'id' ) ,
'to' : 'romeo@montague.lit/desktop' ,
'type' : 'result'
} )
. c ( 'query' , { 'xmlns' : 'http://jabber.org/protocol/disco#info' } )
. c ( 'identity' , { 'category' : 'conference' , 'name' : 'A Dark Cave' , 'type' : 'text' } ) . up ( )
. c ( 'feature' , { 'var' : 'http://jabber.org/protocol/muc' } ) . up ( )
. c ( 'feature' , { 'var' : 'muc_hidden' } ) . up ( )
. c ( 'feature' , { 'var' : 'muc_temporary' } ) . up ( )
_converse . connection . _dataRecv ( mock . createRequest ( features _stanza ) ) ;
const view = _converse . chatboxviews . get ( muc _jid ) ;
await u . waitUntil ( ( ) => view . model . session . get ( 'connection_status' ) === converse . ROOMSTATUS . CONNECTING ) ;
const presence = $pres ( ) . attrs ( {
from : ` ${ muc _jid } /romeo ` ,
id : u . getUniqueId ( ) ,
to : 'romeo@montague.lit/pda' ,
type : 'error'
} ) . c ( 'x' ) . attrs ( { xmlns : 'http://jabber.org/protocol/muc' } ) . up ( )
. c ( 'error' ) . attrs ( { by : 'lounge@montague.lit' , type : 'auth' } )
. c ( 'forbidden' ) . attrs ( { xmlns : 'urn:ietf:params:xml:ns:xmpp-stanzas' } ) . nodeTree ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
expect ( view . el . querySelector ( '.chatroom-body .disconnect-container .disconnect-msg:last-child' ) . textContent . trim ( ) )
. toBe ( 'You have been banned from this groupchat.' ) ;
done ( ) ;
} ) ) ;
it ( "will render a nickname form if a nickname conflict happens and muc_nickname_from_jid=false" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
const muc _jid = 'conflicted@muc.montague.lit' ;
await mock . openChatRoomViaModal ( _converse , muc _jid , 'romeo' ) ;
var presence = $pres ( ) . attrs ( {
from : ` ${ muc _jid } /romeo ` ,
id : u . getUniqueId ( ) ,
to : 'romeo@montague.lit/pda' ,
type : 'error'
} ) . c ( 'x' ) . attrs ( { xmlns : 'http://jabber.org/protocol/muc' } ) . up ( )
. c ( 'error' ) . attrs ( { by : 'lounge@montague.lit' , type : 'cancel' } )
. c ( 'conflict' ) . attrs ( { xmlns : 'urn:ietf:params:xml:ns:xmpp-stanzas' } ) . nodeTree ;
const view = _converse . chatboxviews . get ( muc _jid ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
expect ( sizzle ( '.chatroom-body form.chatroom-form label:first' , view . el ) . pop ( ) . textContent . trim ( ) )
. toBe ( 'Please choose your nickname' ) ;
const input = sizzle ( '.chatroom-body form.chatroom-form input:first' , view . el ) . pop ( ) ;
2020-09-26 13:13:50 +02:00
expect ( input . value ) . toBe ( 'romeo' ) ;
2020-04-22 13:11:48 +02:00
input . value = 'nicky' ;
view . el . querySelector ( 'input[type=submit]' ) . click ( ) ;
done ( ) ;
} ) ) ;
it ( "will automatically choose a new nickname if a nickname conflict happens and muc_nickname_from_jid=true" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { } ,
async function ( done , _converse ) {
const muc _jid = 'conflicting@muc.montague.lit'
await mock . openChatRoomViaModal ( _converse , muc _jid , 'romeo' ) ;
/ * < p r e s e n c e
* from = 'coven@chat.shakespeare.lit/thirdwitch'
* id = 'n13mt3l'
* to = 'hag66@shakespeare.lit/pda'
* type = 'error' >
* < x xmlns = 'http://jabber.org/protocol/muc' / >
* < error by = 'coven@chat.shakespeare.lit' type = 'cancel' >
* < conflict xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' / >
* < / e r r o r >
* < / p r e s e n c e >
* /
_converse . muc _nickname _from _jid = true ;
const attrs = {
'from' : ` ${ muc _jid } /romeo ` ,
'id' : u . getUniqueId ( ) ,
'to' : 'romeo@montague.lit/pda' ,
'type' : 'error'
} ;
let presence = $pres ( ) . attrs ( attrs )
. c ( 'x' ) . attrs ( { 'xmlns' : 'http://jabber.org/protocol/muc' } ) . up ( )
. c ( 'error' ) . attrs ( { 'by' : muc _jid , 'type' : 'cancel' } )
. c ( 'conflict' ) . attrs ( { 'xmlns' : 'urn:ietf:params:xml:ns:xmpp-stanzas' } ) . nodeTree ;
const view = _converse . chatboxviews . get ( muc _jid ) ;
spyOn ( view . model , 'join' ) . and . callThrough ( ) ;
// Simulate repeatedly that there's already someone in the groupchat
// with that nickname
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
expect ( view . model . join ) . toHaveBeenCalledWith ( 'romeo-2' ) ;
attrs . from = ` ${ muc _jid } /romeo-2 ` ;
attrs . id = u . getUniqueId ( ) ;
presence = $pres ( ) . attrs ( attrs )
. c ( 'x' ) . attrs ( { 'xmlns' : 'http://jabber.org/protocol/muc' } ) . up ( )
. c ( 'error' ) . attrs ( { 'by' : muc _jid , type : 'cancel' } )
. c ( 'conflict' ) . attrs ( { xmlns : 'urn:ietf:params:xml:ns:xmpp-stanzas' } ) . nodeTree ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
expect ( view . model . join ) . toHaveBeenCalledWith ( 'romeo-3' ) ;
attrs . from = ` ${ muc _jid } /romeo-3 ` ;
attrs . id = new Date ( ) . getTime ( ) ;
presence = $pres ( ) . attrs ( attrs )
. c ( 'x' ) . attrs ( { 'xmlns' : 'http://jabber.org/protocol/muc' } ) . up ( )
. c ( 'error' ) . attrs ( { 'by' : muc _jid , 'type' : 'cancel' } )
. c ( 'conflict' ) . attrs ( { 'xmlns' : 'urn:ietf:params:xml:ns:xmpp-stanzas' } ) . nodeTree ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
expect ( view . model . join ) . toHaveBeenCalledWith ( 'romeo-4' ) ;
done ( ) ;
} ) ) ;
it ( "will show an error message if the user is not allowed to have created the groupchat" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
const muc _jid = 'impermissable@muc.montague.lit'
await mock . openChatRoomViaModal ( _converse , muc _jid , 'romeo' )
// We pretend this is a new room, so no disco info is returned.
const iq = await u . waitUntil ( ( ) => _ . filter (
_converse . connection . IQ _stanzas ,
iq => iq . querySelector (
` iq[to=" ${ muc _jid } "] query[xmlns="http://jabber.org/protocol/disco#info"] `
) ) . pop ( ) ) ;
const features _stanza = $iq ( {
'from' : 'room@conference.example.org' ,
'id' : iq . getAttribute ( 'id' ) ,
'to' : 'romeo@montague.lit/desktop' ,
'type' : 'error'
} ) . c ( 'error' , { 'type' : 'cancel' } )
. c ( 'item-not-found' , { 'xmlns' : "urn:ietf:params:xml:ns:xmpp-stanzas" } ) ;
_converse . connection . _dataRecv ( mock . createRequest ( features _stanza ) ) ;
2017-06-05 21:42:25 +02:00
2020-04-22 13:11:48 +02:00
const view = _converse . chatboxviews . get ( muc _jid ) ;
await u . waitUntil ( ( ) => ( view . model . session . get ( 'connection_status' ) === converse . ROOMSTATUS . CONNECTING ) ) ;
2017-07-11 10:41:11 +02:00
2020-04-22 13:11:48 +02:00
const presence = $pres ( ) . attrs ( {
from : ` ${ muc _jid } /romeo ` ,
id : u . getUniqueId ( ) ,
to : 'romeo@montague.lit/pda' ,
type : 'error'
} ) . c ( 'x' ) . attrs ( { xmlns : 'http://jabber.org/protocol/muc' } ) . up ( )
. c ( 'error' ) . attrs ( { by : 'lounge@montague.lit' , type : 'cancel' } )
. c ( 'not-allowed' ) . attrs ( { xmlns : 'urn:ietf:params:xml:ns:xmpp-stanzas' } ) . nodeTree ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
expect ( view . el . querySelector ( '.chatroom-body .disconnect-container .disconnect-msg:last-child' ) . textContent . trim ( ) )
. toBe ( 'You are not allowed to create new groupchats.' ) ;
done ( ) ;
} ) ) ;
it ( "will show an error message if the user's nickname doesn't conform to groupchat policy" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
const muc _jid = 'conformist@muc.montague.lit'
await mock . openChatRoomViaModal ( _converse , muc _jid , 'romeo' ) ;
const iq = await u . waitUntil ( ( ) => _ . filter (
_converse . connection . IQ _stanzas ,
iq => iq . querySelector (
` iq[to=" ${ muc _jid } "] query[xmlns="http://jabber.org/protocol/disco#info"] `
) ) . pop ( ) ) ;
const features _stanza = $iq ( {
'from' : muc _jid ,
'id' : iq . getAttribute ( 'id' ) ,
'to' : 'romeo@montague.lit/desktop' ,
'type' : 'result'
} ) . c ( 'query' , { 'xmlns' : 'http://jabber.org/protocol/disco#info' } )
. c ( 'identity' , { 'category' : 'conference' , 'name' : 'A Dark Cave' , 'type' : 'text' } ) . up ( )
. c ( 'feature' , { 'var' : 'http://jabber.org/protocol/muc' } ) . up ( )
_converse . connection . _dataRecv ( mock . createRequest ( features _stanza ) ) ;
2019-02-26 10:34:41 +01:00
2020-04-22 13:11:48 +02:00
const view = _converse . chatboxviews . get ( muc _jid ) ;
await u . waitUntil ( ( ) => ( view . model . session . get ( 'connection_status' ) === converse . ROOMSTATUS . CONNECTING ) ) ;
2019-03-26 13:29:33 +01:00
2020-04-22 13:11:48 +02:00
const presence = $pres ( ) . attrs ( {
from : ` ${ muc _jid } /romeo ` ,
id : u . getUniqueId ( ) ,
to : 'romeo@montague.lit/pda' ,
type : 'error'
} ) . c ( 'x' ) . attrs ( { xmlns : 'http://jabber.org/protocol/muc' } ) . up ( )
. c ( 'error' ) . attrs ( { by : 'lounge@montague.lit' , type : 'cancel' } )
. c ( 'not-acceptable' ) . attrs ( { xmlns : 'urn:ietf:params:xml:ns:xmpp-stanzas' } ) . nodeTree ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
expect ( view . el . querySelector ( '.chatroom-body .disconnect-container .disconnect-msg:last-child' ) . textContent . trim ( ) )
. toBe ( "Your nickname doesn't conform to this groupchat's policies." ) ;
done ( ) ;
} ) ) ;
it ( "will show an error message if the groupchat doesn't yet exist" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
const muc _jid = 'nonexistent@muc.montague.lit'
await mock . openChatRoomViaModal ( _converse , muc _jid , 'romeo' ) ;
const iq = await u . waitUntil ( ( ) => _ . filter (
_converse . connection . IQ _stanzas ,
iq => iq . querySelector (
` iq[to=" ${ muc _jid } "] query[xmlns="http://jabber.org/protocol/disco#info"] `
) ) . pop ( ) ) ;
const features _stanza = $iq ( {
'from' : muc _jid ,
'id' : iq . getAttribute ( 'id' ) ,
'to' : 'romeo@montague.lit/desktop' ,
'type' : 'result'
} ) . c ( 'query' , { 'xmlns' : 'http://jabber.org/protocol/disco#info' } )
. c ( 'identity' , { 'category' : 'conference' , 'name' : 'A Dark Cave' , 'type' : 'text' } ) . up ( )
. c ( 'feature' , { 'var' : 'http://jabber.org/protocol/muc' } ) . up ( )
_converse . connection . _dataRecv ( mock . createRequest ( features _stanza ) ) ;
2019-03-26 13:29:33 +01:00
2020-04-22 13:11:48 +02:00
const view = _converse . chatboxviews . get ( muc _jid ) ;
await u . waitUntil ( ( ) => ( view . model . session . get ( 'connection_status' ) === converse . ROOMSTATUS . CONNECTING ) ) ;
2019-03-26 11:31:11 +01:00
2020-04-22 13:11:48 +02:00
const presence = $pres ( ) . attrs ( {
from : ` ${ muc _jid } /romeo ` ,
id : u . getUniqueId ( ) ,
to : 'romeo@montague.lit/pda' ,
type : 'error'
} ) . c ( 'x' ) . attrs ( { xmlns : 'http://jabber.org/protocol/muc' } ) . up ( )
. c ( 'error' ) . attrs ( { by : 'lounge@montague.lit' , type : 'cancel' } )
. c ( 'item-not-found' ) . attrs ( { xmlns : 'urn:ietf:params:xml:ns:xmpp-stanzas' } ) . nodeTree ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
expect ( view . el . querySelector ( '.chatroom-body .disconnect-container .disconnect-msg:last-child' ) . textContent . trim ( ) )
. toBe ( "This groupchat does not (yet) exist." ) ;
done ( ) ;
} ) ) ;
it ( "will show an error message if the groupchat has reached its maximum number of participants" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
const muc _jid = 'maxed-out@muc.montague.lit'
await mock . openChatRoomViaModal ( _converse , muc _jid , 'romeo' )
const iq = await u . waitUntil ( ( ) => _ . filter (
_converse . connection . IQ _stanzas ,
iq => iq . querySelector (
` iq[to=" ${ muc _jid } "] query[xmlns="http://jabber.org/protocol/disco#info"] `
) ) . pop ( ) ) ;
const features _stanza = $iq ( {
'from' : muc _jid ,
'id' : iq . getAttribute ( 'id' ) ,
'to' : 'romeo@montague.lit/desktop' ,
'type' : 'result'
} ) . c ( 'query' , { 'xmlns' : 'http://jabber.org/protocol/disco#info' } )
. c ( 'identity' , { 'category' : 'conference' , 'name' : 'A Dark Cave' , 'type' : 'text' } ) . up ( )
. c ( 'feature' , { 'var' : 'http://jabber.org/protocol/muc' } ) . up ( )
_converse . connection . _dataRecv ( mock . createRequest ( features _stanza ) ) ;
2019-03-26 11:31:11 +01:00
2020-04-22 13:11:48 +02:00
const view = _converse . chatboxviews . get ( muc _jid ) ;
await u . waitUntil ( ( ) => ( view . model . session . get ( 'connection_status' ) === converse . ROOMSTATUS . CONNECTING ) ) ;
2019-03-26 11:31:11 +01:00
2020-04-22 13:11:48 +02:00
const presence = $pres ( ) . attrs ( {
from : ` ${ muc _jid } /romeo ` ,
id : u . getUniqueId ( ) ,
to : 'romeo@montague.lit/pda' ,
type : 'error'
} ) . c ( 'x' ) . attrs ( { xmlns : 'http://jabber.org/protocol/muc' } ) . up ( )
. c ( 'error' ) . attrs ( { by : 'lounge@montague.lit' , type : 'cancel' } )
. c ( 'service-unavailable' ) . attrs ( { xmlns : 'urn:ietf:params:xml:ns:xmpp-stanzas' } ) . nodeTree ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
expect ( view . el . querySelector ( '.chatroom-body .disconnect-container .disconnect-msg:last-child' ) . textContent . trim ( ) )
. toBe ( "This groupchat has reached its maximum number of participants." ) ;
done ( ) ;
} ) ) ;
} ) ;
2019-03-26 11:31:11 +01:00
2020-04-22 13:11:48 +02:00
describe ( "Someone being invited to a groupchat" , function ( ) {
2019-02-26 10:34:41 +01:00
2020-04-22 13:11:48 +02:00
it ( "will first be added to the member list if the groupchat is members only" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { } ,
async function ( done , _converse ) {
2019-02-26 10:34:41 +01:00
2020-04-22 13:11:48 +02:00
await mock . waitForRoster ( _converse , 'current' , 0 ) ;
spyOn ( _converse . ChatRoomOccupants . prototype , 'fetchMembers' ) . and . callThrough ( ) ;
const sent _IQs = _converse . connection . IQ _stanzas ;
const muc _jid = 'coven@chat.shakespeare.lit' ;
2019-02-26 10:34:41 +01:00
2020-04-22 13:11:48 +02:00
const room _creation _promise = _converse . api . rooms . open ( muc _jid , { 'nick' : 'romeo' } ) ;
2019-02-26 11:05:15 +01:00
2020-04-22 13:11:48 +02:00
// Check that the groupchat queried for the features.
let stanza = await u . waitUntil ( ( ) => sent _IQs . filter ( iq => iq . querySelector ( ` iq[to=" ${ muc _jid } "] query[xmlns="http://jabber.org/protocol/disco#info"] ` ) ) . pop ( ) ) ;
expect ( Strophe . serialize ( stanza ) ) . toBe (
` <iq from="romeo@montague.lit/orchard" id=" ${ stanza . getAttribute ( "id" ) } " to=" ${ muc _jid } " type="get" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/disco#info"/> ` +
` </iq> ` ) ;
2017-06-05 21:42:25 +02:00
2020-04-22 13:11:48 +02:00
// State that the chat is members-only via the features IQ
const view = _converse . chatboxviews . get ( muc _jid ) ;
const features _stanza = $iq ( {
from : 'coven@chat.shakespeare.lit' ,
'id' : stanza . getAttribute ( 'id' ) ,
'to' : 'romeo@montague.lit/desktop' ,
'type' : 'result'
} )
. c ( 'query' , { 'xmlns' : 'http://jabber.org/protocol/disco#info' } )
. c ( 'identity' , {
'category' : 'conference' ,
'name' : 'A Dark Cave' ,
'type' : 'text'
} ) . up ( )
. c ( 'feature' , { 'var' : 'http://jabber.org/protocol/muc' } ) . up ( )
. c ( 'feature' , { 'var' : 'muc_hidden' } ) . up ( )
. c ( 'feature' , { 'var' : 'muc_temporary' } ) . up ( )
. c ( 'feature' , { 'var' : 'muc_membersonly' } ) . up ( ) ;
_converse . connection . _dataRecv ( mock . createRequest ( features _stanza ) ) ;
await u . waitUntil ( ( ) => ( view . model . session . get ( 'connection_status' ) === converse . ROOMSTATUS . CONNECTING ) ) ;
expect ( view . model . features . get ( 'membersonly' ) ) . toBeTruthy ( ) ;
await room _creation _promise ;
await mock . createContacts ( _converse , 'current' ) ;
let sent _stanza , sent _id ;
spyOn ( _converse . connection , 'send' ) . and . callFake ( function ( stanza ) {
if ( stanza . nodeTree && stanza . nodeTree . nodeName === 'message' ) {
sent _id = stanza . nodeTree . getAttribute ( 'id' ) ;
sent _stanza = stanza ;
}
} ) ;
const invitee _jid = mock . cur _names [ 0 ] . replace ( / /g , '.' ) . toLowerCase ( ) + '@montague.lit' ;
const reason = "Please join this groupchat" ;
view . model . directInvite ( invitee _jid , reason ) ;
// Check in reverse order that we requested all three lists
const owner _iq = sent _IQs . pop ( ) ;
expect ( Strophe . serialize ( owner _iq ) ) . toBe (
` <iq id=" ${ owner _iq . getAttribute ( 'id' ) } " to="coven@chat.shakespeare.lit" type="get" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/muc#admin"><item affiliation="owner"/></query> ` +
` </iq> ` ) ;
const admin _iq = sent _IQs . pop ( ) ;
expect ( Strophe . serialize ( admin _iq ) ) . toBe (
` <iq id=" ${ admin _iq . getAttribute ( 'id' ) } " to="coven@chat.shakespeare.lit" type="get" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/muc#admin"><item affiliation="admin"/></query> ` +
` </iq> ` ) ;
const member _iq = sent _IQs . pop ( ) ;
expect ( Strophe . serialize ( member _iq ) ) . toBe (
` <iq id=" ${ member _iq . getAttribute ( 'id' ) } " to="coven@chat.shakespeare.lit" type="get" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/muc#admin"><item affiliation="member"/></query> ` +
` </iq> ` ) ;
// Now the service sends the member lists to the user
const member _list _stanza = $iq ( {
'from' : 'coven@chat.shakespeare.lit' ,
'id' : member _iq . getAttribute ( 'id' ) ,
'to' : 'romeo@montague.lit/orchard' ,
'type' : 'result'
} ) . c ( 'query' , { 'xmlns' : Strophe . NS . MUC _ADMIN } )
. c ( 'item' , {
'affiliation' : 'member' ,
'jid' : 'hag66@shakespeare.lit' ,
'nick' : 'thirdwitch' ,
'role' : 'participant'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( member _list _stanza ) ) ;
2018-03-11 13:56:53 +01:00
2020-04-22 13:11:48 +02:00
const admin _list _stanza = $iq ( {
'from' : 'coven@chat.shakespeare.lit' ,
'id' : admin _iq . getAttribute ( 'id' ) ,
'to' : 'romeo@montague.lit/orchard' ,
'type' : 'result'
} ) . c ( 'query' , { 'xmlns' : Strophe . NS . MUC _ADMIN } )
. c ( 'item' , {
'affiliation' : 'admin' ,
'jid' : 'wiccarocks@shakespeare.lit' ,
'nick' : 'secondwitch'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( admin _list _stanza ) ) ;
2019-02-26 10:34:41 +01:00
2020-04-22 13:11:48 +02:00
const owner _list _stanza = $iq ( {
'from' : 'coven@chat.shakespeare.lit' ,
'id' : owner _iq . getAttribute ( 'id' ) ,
'to' : 'romeo@montague.lit/orchard' ,
'type' : 'result'
} ) . c ( 'query' , { 'xmlns' : Strophe . NS . MUC _ADMIN } )
. c ( 'item' , {
'affiliation' : 'owner' ,
'jid' : 'crone1@shakespeare.lit' ,
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( owner _list _stanza ) ) ;
// Converse puts the user on the member list
stanza = await u . waitUntil ( ( ) => sent _IQs . filter ( iq => iq . querySelector ( ` iq[to=" ${ muc _jid } "] query[xmlns="http://jabber.org/protocol/muc#admin"] ` ) ) . pop ( ) ) ;
expect ( stanza . outerHTML ,
` <iq id=" ${ stanza . getAttribute ( 'id' ) } " to="coven@chat.shakespeare.lit" type="set" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/muc#admin"> ` +
` <item affiliation="member" jid=" ${ invitee _jid } "> ` +
` <reason>Please join this groupchat</reason> ` +
` </item> ` +
` </query> ` +
` </iq> ` ) ;
const result = $iq ( {
'from' : 'coven@chat.shakespeare.lit' ,
'id' : stanza . getAttribute ( 'id' ) ,
'to' : 'romeo@montague.lit/orchard' ,
'type' : 'result'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( result ) ) ;
await u . waitUntil ( ( ) => view . model . occupants . fetchMembers . calls . count ( ) ) ;
// Finally check that the user gets invited.
expect ( sent _stanza . toLocaleString ( ) ) . toBe ( // Strophe adds the xmlns attr (although not in spec)
` <message from="romeo@montague.lit/orchard" id=" ${ sent _id } " to=" ${ invitee _jid } " xmlns="jabber:client"> ` +
` <x jid="coven@chat.shakespeare.lit" reason="Please join this groupchat" xmlns="jabber:x:conference"/> ` +
` </message> `
) ;
done ( ) ;
} ) ) ;
} ) ;
2019-02-26 10:34:41 +01:00
2020-04-22 13:11:48 +02:00
describe ( "The affiliations delta" , function ( ) {
it ( "can be computed in various ways" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { } ,
async function ( done , _converse ) {
await mock . openChatRoom ( _converse , 'coven' , 'chat.shakespeare.lit' , 'romeo' ) ;
var exclude _existing = false ;
var remove _absentees = false ;
var new _list = [ ] ;
var old _list = [ ] ;
const muc _utils = converse . env . muc _utils ;
var delta = muc _utils . computeAffiliationsDelta ( exclude _existing , remove _absentees , new _list , old _list ) ;
expect ( delta . length ) . toBe ( 0 ) ;
new _list = [ { 'jid' : 'wiccarocks@shakespeare.lit' , 'affiliation' : 'member' } ] ;
old _list = [ { 'jid' : 'wiccarocks@shakespeare.lit' , 'affiliation' : 'member' } ] ;
delta = muc _utils . computeAffiliationsDelta ( exclude _existing , remove _absentees , new _list , old _list ) ;
expect ( delta . length ) . toBe ( 0 ) ;
// When remove_absentees is false, then affiliations in the old
// list which are not in the new one won't be removed.
old _list = [ { 'jid' : 'oldhag666@shakespeare.lit' , 'affiliation' : 'owner' } ,
{ 'jid' : 'wiccarocks@shakespeare.lit' , 'affiliation' : 'member' } ] ;
delta = muc _utils . computeAffiliationsDelta ( exclude _existing , remove _absentees , new _list , old _list ) ;
expect ( delta . length ) . toBe ( 0 ) ;
// With exclude_existing set to false, any changed affiliations
// will be included in the delta (i.e. existing affiliations are included in the comparison).
old _list = [ { 'jid' : 'wiccarocks@shakespeare.lit' , 'affiliation' : 'owner' } ] ;
delta = muc _utils . computeAffiliationsDelta ( exclude _existing , remove _absentees , new _list , old _list ) ;
expect ( delta . length ) . toBe ( 1 ) ;
expect ( delta [ 0 ] . jid ) . toBe ( 'wiccarocks@shakespeare.lit' ) ;
expect ( delta [ 0 ] . affiliation ) . toBe ( 'member' ) ;
// To also remove affiliations from the old list which are not
// in the new list, we set remove_absentees to true
remove _absentees = true ;
old _list = [ { 'jid' : 'oldhag666@shakespeare.lit' , 'affiliation' : 'owner' } ,
{ 'jid' : 'wiccarocks@shakespeare.lit' , 'affiliation' : 'member' } ] ;
delta = muc _utils . computeAffiliationsDelta ( exclude _existing , remove _absentees , new _list , old _list ) ;
expect ( delta . length ) . toBe ( 1 ) ;
expect ( delta [ 0 ] . jid ) . toBe ( 'oldhag666@shakespeare.lit' ) ;
expect ( delta [ 0 ] . affiliation ) . toBe ( 'none' ) ;
delta = muc _utils . computeAffiliationsDelta ( exclude _existing , remove _absentees , [ ] , old _list ) ;
expect ( delta . length ) . toBe ( 2 ) ;
expect ( delta [ 0 ] . jid ) . toBe ( 'oldhag666@shakespeare.lit' ) ;
expect ( delta [ 0 ] . affiliation ) . toBe ( 'none' ) ;
expect ( delta [ 1 ] . jid ) . toBe ( 'wiccarocks@shakespeare.lit' ) ;
expect ( delta [ 1 ] . affiliation ) . toBe ( 'none' ) ;
// To only add a user if they don't already have an
// affiliation, we set 'exclude_existing' to true
exclude _existing = true ;
old _list = [ { 'jid' : 'wiccarocks@shakespeare.lit' , 'affiliation' : 'owner' } ] ;
delta = muc _utils . computeAffiliationsDelta ( exclude _existing , remove _absentees , new _list , old _list ) ;
expect ( delta . length ) . toBe ( 0 ) ;
old _list = [ { 'jid' : 'wiccarocks@shakespeare.lit' , 'affiliation' : 'admin' } ] ;
delta = muc _utils . computeAffiliationsDelta ( exclude _existing , remove _absentees , new _list , old _list ) ;
expect ( delta . length ) . toBe ( 0 ) ;
done ( ) ;
} ) ) ;
} ) ;
2019-02-26 10:34:41 +01:00
2020-04-22 13:11:48 +02:00
describe ( "The \"Groupchats\" Add modal" , function ( ) {
it ( "can be opened from a link in the \"Groupchats\" section of the controlbox" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { } ,
async function ( done , _converse ) {
await mock . openControlBox ( _converse ) ;
await mock . waitForRoster ( _converse , 'current' , 0 ) ;
const roomspanel = _converse . chatboxviews . get ( 'controlbox' ) . roomspanel ;
roomspanel . el . querySelector ( '.show-add-muc-modal' ) . click ( ) ;
mock . closeControlBox ( _converse ) ;
const modal = roomspanel . add _room _modal ;
await u . waitUntil ( ( ) => u . isVisible ( modal . el ) , 1000 )
let label _name = modal . el . querySelector ( 'label[for="chatroom"]' ) ;
expect ( label _name . textContent . trim ( ) ) . toBe ( 'Groupchat address:' ) ;
let name _input = modal . el . querySelector ( 'input[name="chatroom"]' ) ;
expect ( name _input . placeholder ) . toBe ( 'name@conference.example.org' ) ;
const label _nick = modal . el . querySelector ( 'label[for="nickname"]' ) ;
expect ( label _nick . textContent . trim ( ) ) . toBe ( 'Nickname:' ) ;
const nick _input = modal . el . querySelector ( 'input[name="nickname"]' ) ;
expect ( nick _input . value ) . toBe ( '' ) ;
nick _input . value = 'romeo' ;
expect ( modal . el . querySelector ( '.modal-title' ) . textContent . trim ( ) ) . toBe ( 'Enter a new Groupchat' ) ;
spyOn ( _converse . ChatRoom . prototype , 'getDiscoInfo' ) . and . callFake ( ( ) => Promise . resolve ( ) ) ;
roomspanel . delegateEvents ( ) ; // We need to rebind all events otherwise our spy won't be called
modal . el . querySelector ( 'input[name="chatroom"]' ) . value = 'lounce@muc.montague.lit' ;
modal . el . querySelector ( 'form input[type="submit"]' ) . click ( ) ;
await u . waitUntil ( ( ) => _converse . chatboxes . length ) ;
await u . waitUntil ( ( ) => sizzle ( '.chatroom' , _converse . el ) . filter ( u . isVisible ) . length === 1 ) ;
roomspanel . model . set ( 'muc_domain' , 'muc.example.org' ) ;
roomspanel . el . querySelector ( '.show-add-muc-modal' ) . click ( ) ;
label _name = modal . el . querySelector ( 'label[for="chatroom"]' ) ;
expect ( label _name . textContent . trim ( ) ) . toBe ( 'Groupchat address:' ) ;
name _input = modal . el . querySelector ( 'input[name="chatroom"]' ) ;
expect ( name _input . placeholder ) . toBe ( 'name@muc.example.org' ) ;
done ( ) ;
} ) ) ;
it ( "doesn't show the nickname field if locked_muc_nickname is true" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { 'locked_muc_nickname' : true , 'muc_nickname_from_jid' : true } ,
async function ( done , _converse ) {
await mock . openControlBox ( _converse ) ;
await mock . waitForRoster ( _converse , 'current' , 0 ) ;
const roomspanel = _converse . chatboxviews . get ( 'controlbox' ) . roomspanel ;
roomspanel . el . querySelector ( '.show-add-muc-modal' ) . click ( ) ;
mock . closeControlBox ( _converse ) ;
const modal = roomspanel . add _room _modal ;
await u . waitUntil ( ( ) => u . isVisible ( modal . el ) , 1000 )
const name _input = modal . el . querySelector ( 'input[name="chatroom"]' ) ;
name _input . value = 'lounge@montague.lit' ;
expect ( modal . el . querySelector ( 'label[for="nickname"]' ) ) . toBe ( null ) ;
expect ( modal . el . querySelector ( 'input[name="nickname"]' ) ) . toBe ( null ) ;
modal . el . querySelector ( 'form input[type="submit"]' ) . click ( ) ;
await u . waitUntil ( ( ) => _converse . chatboxes . length > 1 ) ;
const chatroom = _converse . chatboxes . get ( 'lounge@montague.lit' ) ;
expect ( chatroom . get ( 'nick' ) ) . toBe ( 'romeo' ) ;
done ( ) ;
} ) ) ;
it ( "uses the JID node if muc_nickname_from_jid is set to true" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { 'muc_nickname_from_jid' : true } ,
async function ( done , _converse ) {
await mock . openControlBox ( _converse ) ;
await mock . waitForRoster ( _converse , 'current' , 0 ) ;
const roomspanel = _converse . chatboxviews . get ( 'controlbox' ) . roomspanel ;
roomspanel . el . querySelector ( '.show-add-muc-modal' ) . click ( ) ;
mock . closeControlBox ( _converse ) ;
const modal = roomspanel . add _room _modal ;
await u . waitUntil ( ( ) => u . isVisible ( modal . el ) , 1000 )
const label _nick = modal . el . querySelector ( 'label[for="nickname"]' ) ;
expect ( label _nick . textContent . trim ( ) ) . toBe ( 'Nickname:' ) ;
const nick _input = modal . el . querySelector ( 'input[name="nickname"]' ) ;
expect ( nick _input . value ) . toBe ( 'romeo' ) ;
done ( ) ;
} ) ) ;
it ( "uses the nickname passed in to converse.initialize" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { 'nickname' : 'st.nick' } ,
async function ( done , _converse ) {
await mock . openControlBox ( _converse ) ;
await mock . waitForRoster ( _converse , 'current' , 0 ) ;
const roomspanel = _converse . chatboxviews . get ( 'controlbox' ) . roomspanel ;
roomspanel . el . querySelector ( '.show-add-muc-modal' ) . click ( ) ;
mock . closeControlBox ( _converse ) ;
const modal = roomspanel . add _room _modal ;
await u . waitUntil ( ( ) => u . isVisible ( modal . el ) , 1000 )
const label _nick = modal . el . querySelector ( 'label[for="nickname"]' ) ;
expect ( label _nick . textContent . trim ( ) ) . toBe ( 'Nickname:' ) ;
const nick _input = modal . el . querySelector ( 'input[name="nickname"]' ) ;
expect ( nick _input . value ) . toBe ( 'st.nick' ) ;
done ( ) ;
} ) ) ;
it ( "doesn't require the domain when muc_domain is set" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { 'muc_domain' : 'muc.example.org' } ,
async function ( done , _converse ) {
await mock . openControlBox ( _converse ) ;
const roomspanel = _converse . chatboxviews . get ( 'controlbox' ) . roomspanel ;
roomspanel . el . querySelector ( '.show-add-muc-modal' ) . click ( ) ;
const modal = roomspanel . add _room _modal ;
await u . waitUntil ( ( ) => u . isVisible ( modal . el ) , 1000 )
expect ( modal . el . querySelector ( '.modal-title' ) . textContent . trim ( ) ) . toBe ( 'Enter a new Groupchat' ) ;
spyOn ( _converse . ChatRoom . prototype , 'getDiscoInfo' ) . and . callFake ( ( ) => Promise . resolve ( ) ) ;
roomspanel . delegateEvents ( ) ; // We need to rebind all events otherwise our spy won't be called
const label _name = modal . el . querySelector ( 'label[for="chatroom"]' ) ;
expect ( label _name . textContent . trim ( ) ) . toBe ( 'Groupchat name:' ) ;
let name _input = modal . el . querySelector ( 'input[name="chatroom"]' ) ;
expect ( name _input . placeholder ) . toBe ( 'name@muc.example.org' ) ;
name _input . value = 'lounge' ;
let nick _input = modal . el . querySelector ( 'input[name="nickname"]' ) ;
nick _input . value = 'max' ;
modal . el . querySelector ( 'form input[type="submit"]' ) . click ( ) ;
await u . waitUntil ( ( ) => _converse . chatboxes . length ) ;
await u . waitUntil ( ( ) => sizzle ( '.chatroom' , _converse . el ) . filter ( u . isVisible ) . length === 1 ) ;
expect ( _ . includes ( _converse . chatboxes . models . map ( m => m . get ( 'id' ) ) , 'lounge@muc.example.org' ) ) . toBe ( true ) ;
// However, you can still open MUCs with different domains
roomspanel . el . querySelector ( '.show-add-muc-modal' ) . click ( ) ;
await u . waitUntil ( ( ) => u . isVisible ( modal . el ) , 1000 ) ;
name _input = modal . el . querySelector ( 'input[name="chatroom"]' ) ;
name _input . value = 'lounge@conference.example.org' ;
nick _input = modal . el . querySelector ( 'input[name="nickname"]' ) ;
nick _input . value = 'max' ;
modal . el . querySelector ( 'form input[type="submit"]' ) . click ( ) ;
await u . waitUntil ( ( ) => _converse . chatboxes . models . filter ( c => c . get ( 'type' ) === 'chatroom' ) . length === 2 ) ;
await u . waitUntil ( ( ) => sizzle ( '.chatroom' , _converse . el ) . filter ( u . isVisible ) . length === 2 ) ;
expect ( _ . includes ( _converse . chatboxes . models . map ( m => m . get ( 'id' ) ) , 'lounge@conference.example.org' ) ) . toBe ( true ) ;
done ( ) ;
} ) ) ;
it ( "only uses the muc_domain is locked_muc_domain is true" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { 'muc_domain' : 'muc.example.org' , 'locked_muc_domain' : true } ,
async function ( done , _converse ) {
await mock . openControlBox ( _converse ) ;
const roomspanel = _converse . chatboxviews . get ( 'controlbox' ) . roomspanel ;
roomspanel . el . querySelector ( '.show-add-muc-modal' ) . click ( ) ;
const modal = roomspanel . add _room _modal ;
await u . waitUntil ( ( ) => u . isVisible ( modal . el ) , 1000 )
expect ( modal . el . querySelector ( '.modal-title' ) . textContent . trim ( ) ) . toBe ( 'Enter a new Groupchat' ) ;
spyOn ( _converse . ChatRoom . prototype , 'getDiscoInfo' ) . and . callFake ( ( ) => Promise . resolve ( ) ) ;
roomspanel . delegateEvents ( ) ; // We need to rebind all events otherwise our spy won't be called
const label _name = modal . el . querySelector ( 'label[for="chatroom"]' ) ;
expect ( label _name . textContent . trim ( ) ) . toBe ( 'Groupchat name:' ) ;
let name _input = modal . el . querySelector ( 'input[name="chatroom"]' ) ;
expect ( name _input . placeholder ) . toBe ( '' ) ;
name _input . value = 'lounge' ;
let nick _input = modal . el . querySelector ( 'input[name="nickname"]' ) ;
nick _input . value = 'max' ;
modal . el . querySelector ( 'form input[type="submit"]' ) . click ( ) ;
await u . waitUntil ( ( ) => _converse . chatboxes . length ) ;
await u . waitUntil ( ( ) => sizzle ( '.chatroom' , _converse . el ) . filter ( u . isVisible ) . length === 1 ) ;
expect ( _ . includes ( _converse . chatboxes . models . map ( m => m . get ( 'id' ) ) , 'lounge@muc.example.org' ) ) . toBe ( true ) ;
// However, you can still open MUCs with different domains
roomspanel . el . querySelector ( '.show-add-muc-modal' ) . click ( ) ;
await u . waitUntil ( ( ) => u . isVisible ( modal . el ) , 1000 ) ;
name _input = modal . el . querySelector ( 'input[name="chatroom"]' ) ;
name _input . value = 'lounge@conference' ;
nick _input = modal . el . querySelector ( 'input[name="nickname"]' ) ;
nick _input . value = 'max' ;
modal . el . querySelector ( 'form input[type="submit"]' ) . click ( ) ;
await u . waitUntil ( ( ) => _converse . chatboxes . models . filter ( c => c . get ( 'type' ) === 'chatroom' ) . length === 2 ) ;
await u . waitUntil ( ( ) => sizzle ( '.chatroom' , _converse . el ) . filter ( u . isVisible ) . length === 2 ) ;
expect ( _ . includes ( _converse . chatboxes . models . map ( m => m . get ( 'id' ) ) , 'lounge\\40conference@muc.example.org' ) ) . toBe ( true ) ;
done ( ) ;
} ) ) ;
} ) ;
2019-02-26 10:34:41 +01:00
2020-04-22 13:11:48 +02:00
describe ( "The \"Groupchats\" List modal" , function ( ) {
it ( "can be opened from a link in the \"Groupchats\" section of the controlbox" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { } ,
async function ( done , _converse ) {
await mock . openControlBox ( _converse ) ;
const roomspanel = _converse . chatboxviews . get ( 'controlbox' ) . roomspanel ;
roomspanel . el . querySelector ( '.show-list-muc-modal' ) . click ( ) ;
mock . closeControlBox ( _converse ) ;
2020-07-28 12:40:02 +02:00
const modal = roomspanel . muc _list _modal ;
2020-04-22 13:11:48 +02:00
await u . waitUntil ( ( ) => u . isVisible ( modal . el ) , 1000 ) ;
spyOn ( _converse . ChatRoom . prototype , 'getDiscoInfo' ) . and . callFake ( ( ) => Promise . resolve ( ) ) ;
roomspanel . delegateEvents ( ) ; // We need to rebind all events otherwise our spy won't be called
// See: https://xmpp.org/extensions/xep-0045.html#disco-rooms
expect ( modal . el . querySelectorAll ( '.available-chatrooms li' ) . length ) . toBe ( 0 ) ;
const server _input = modal . el . querySelector ( 'input[name="server"]' ) ;
expect ( server _input . placeholder ) . toBe ( 'conference.example.org' ) ;
server _input . value = 'chat.shakespeare.lit' ;
modal . el . querySelector ( 'input[type="submit"]' ) . click ( ) ;
await u . waitUntil ( ( ) => _converse . chatboxes . length ) ;
const IQ _stanzas = _converse . connection . IQ _stanzas ;
const sent _stanza = await u . waitUntil (
( ) => IQ _stanzas . filter ( s => sizzle ( ` query[xmlns=" ${ Strophe . NS . DISCO _ITEMS } "] ` , s ) . length ) . pop ( )
) ;
const id = sent _stanza . getAttribute ( 'id' ) ;
expect ( Strophe . serialize ( sent _stanza ) ) . toBe (
` <iq from="romeo@montague.lit/orchard" id=" ${ id } " ` +
` to="chat.shakespeare.lit" ` +
` type="get" ` +
` xmlns="jabber:client"> ` +
2019-02-26 10:34:41 +01:00
` <query xmlns="http://jabber.org/protocol/disco#items"/> ` +
2020-04-22 13:11:48 +02:00
` </iq> `
) ;
const iq = $iq ( {
'from' : 'muc.montague.lit' ,
'to' : 'romeo@montague.lit/pda' ,
'id' : id ,
'type' : 'result'
} ) . c ( 'query' )
. c ( 'item' , { jid : 'heath@chat.shakespeare.lit' , name : 'A Lonely Heath' } ) . up ( )
. c ( 'item' , { jid : 'coven@chat.shakespeare.lit' , name : 'A Dark Cave' } ) . up ( )
. c ( 'item' , { jid : 'forres@chat.shakespeare.lit' , name : 'The Palace' } ) . up ( )
. c ( 'item' , { jid : 'inverness@chat.shakespeare.lit' , name : 'Macbeth's Castle' } ) . up ( )
. c ( 'item' , { jid : 'orchard@chat.shakespeare.lit' , name : 'Capulet\'s Orchard' } ) . up ( )
. c ( 'item' , { jid : 'friar@chat.shakespeare.lit' , name : 'Friar Laurence\'s cell' } ) . up ( )
. c ( 'item' , { jid : 'hall@chat.shakespeare.lit' , name : 'Hall in Capulet\'s house' } ) . up ( )
. c ( 'item' , { jid : 'chamber@chat.shakespeare.lit' , name : 'Juliet\'s chamber' } ) . up ( )
. c ( 'item' , { jid : 'public@chat.shakespeare.lit' , name : 'A public place' } ) . up ( )
. c ( 'item' , { jid : 'street@chat.shakespeare.lit' , name : 'A street' } ) . nodeTree ;
_converse . connection . _dataRecv ( mock . createRequest ( iq ) ) ;
await u . waitUntil ( ( ) => modal . el . querySelectorAll ( '.available-chatrooms li' ) . length === 11 ) ;
const rooms = modal . el . querySelectorAll ( '.available-chatrooms li' ) ;
2020-04-28 10:27:29 +02:00
expect ( rooms [ 0 ] . textContent . trim ( ) ) . toBe ( "Groupchats found" ) ;
2020-04-22 13:11:48 +02:00
expect ( rooms [ 1 ] . textContent . trim ( ) ) . toBe ( "A Lonely Heath" ) ;
expect ( rooms [ 2 ] . textContent . trim ( ) ) . toBe ( "A Dark Cave" ) ;
expect ( rooms [ 3 ] . textContent . trim ( ) ) . toBe ( "The Palace" ) ;
expect ( rooms [ 4 ] . textContent . trim ( ) ) . toBe ( "Macbeth's Castle" ) ;
expect ( rooms [ 5 ] . textContent . trim ( ) ) . toBe ( 'Capulet\'s Orchard' ) ;
expect ( rooms [ 6 ] . textContent . trim ( ) ) . toBe ( 'Friar Laurence\'s cell' ) ;
expect ( rooms [ 7 ] . textContent . trim ( ) ) . toBe ( 'Hall in Capulet\'s house' ) ;
expect ( rooms [ 8 ] . textContent . trim ( ) ) . toBe ( 'Juliet\'s chamber' ) ;
expect ( rooms [ 9 ] . textContent . trim ( ) ) . toBe ( 'A public place' ) ;
expect ( rooms [ 10 ] . textContent . trim ( ) ) . toBe ( 'A street' ) ;
rooms [ 4 ] . querySelector ( '.open-room' ) . click ( ) ;
await u . waitUntil ( ( ) => _converse . chatboxes . length > 1 ) ;
expect ( sizzle ( '.chatroom' , _converse . el ) . filter ( u . isVisible ) . length ) . toBe ( 1 ) ; // There should now be an open chatroom
var view = _converse . chatboxviews . get ( 'inverness@chat.shakespeare.lit' ) ;
expect ( view . el . querySelector ( '.chatbox-title__text' ) . textContent . trim ( ) ) . toBe ( "Macbeth's Castle" ) ;
done ( ) ;
} ) ) ;
it ( "is pre-filled with the muc_domain" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] ,
{ 'muc_domain' : 'muc.example.org' } ,
async function ( done , _converse ) {
await mock . openControlBox ( _converse ) ;
const roomspanel = _converse . chatboxviews . get ( 'controlbox' ) . roomspanel ;
roomspanel . el . querySelector ( '.show-list-muc-modal' ) . click ( ) ;
mock . closeControlBox ( _converse ) ;
2020-07-28 12:40:02 +02:00
const modal = roomspanel . muc _list _modal ;
2020-04-22 13:11:48 +02:00
await u . waitUntil ( ( ) => u . isVisible ( modal . el ) , 1000 ) ;
const server _input = modal . el . querySelector ( 'input[name="server"]' ) ;
expect ( server _input . value ) . toBe ( 'muc.example.org' ) ;
done ( ) ;
} ) ) ;
it ( "doesn't let you set the MUC domain if it's locked" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] ,
{ 'muc_domain' : 'chat.shakespeare.lit' , 'locked_muc_domain' : true } ,
async function ( done , _converse ) {
await mock . openControlBox ( _converse ) ;
const roomspanel = _converse . chatboxviews . get ( 'controlbox' ) . roomspanel ;
roomspanel . el . querySelector ( '.show-list-muc-modal' ) . click ( ) ;
mock . closeControlBox ( _converse ) ;
2020-07-28 12:40:02 +02:00
const modal = roomspanel . muc _list _modal ;
2020-04-22 13:11:48 +02:00
await u . waitUntil ( ( ) => u . isVisible ( modal . el ) , 1000 ) ;
spyOn ( _converse . ChatRoom . prototype , 'getDiscoInfo' ) . and . callFake ( ( ) => Promise . resolve ( ) ) ;
roomspanel . delegateEvents ( ) ; // We need to rebind all events otherwise our spy won't be called
expect ( modal . el . querySelector ( 'input[name="server"]' ) ) . toBe ( null ) ;
expect ( modal . el . querySelector ( 'input[type="submit"]' ) ) . toBe ( null ) ;
await u . waitUntil ( ( ) => _converse . chatboxes . length ) ;
const sent _stanza = await u . waitUntil ( ( ) =>
_converse . connection . sent _stanzas . filter (
s => sizzle ( ` query[xmlns="http://jabber.org/protocol/disco#items"] ` , s ) . length ) . pop ( )
) ;
expect ( Strophe . serialize ( sent _stanza ) ) . toBe (
` <iq from="romeo@montague.lit/orchard" id=" ${ sent _stanza . getAttribute ( 'id' ) } " ` +
` to="chat.shakespeare.lit" type="get" xmlns="jabber:client"> ` +
` <query xmlns="http://jabber.org/protocol/disco#items"/> ` +
` </iq> `
) ;
const iq = $iq ( {
from : 'muc.montague.lit' ,
to : 'romeo@montague.lit/pda' ,
id : sent _stanza . getAttribute ( 'id' ) ,
type : 'result'
} ) . c ( 'query' )
. c ( 'item' , { jid : 'heath@chat.shakespeare.lit' , name : 'A Lonely Heath' } ) . up ( )
. c ( 'item' , { jid : 'coven@chat.shakespeare.lit' , name : 'A Dark Cave' } ) . up ( )
. c ( 'item' , { jid : 'forres@chat.shakespeare.lit' , name : 'The Palace' } ) . up ( )
_converse . connection . _dataRecv ( mock . createRequest ( iq ) ) ;
await u . waitUntil ( ( ) => modal . el . querySelectorAll ( '.available-chatrooms li' ) . length === 4 ) ;
const rooms = modal . el . querySelectorAll ( '.available-chatrooms li' ) ;
2020-04-28 10:27:29 +02:00
expect ( rooms [ 0 ] . textContent . trim ( ) ) . toBe ( "Groupchats found" ) ;
2020-04-22 13:11:48 +02:00
expect ( rooms [ 1 ] . textContent . trim ( ) ) . toBe ( "A Lonely Heath" ) ;
expect ( rooms [ 2 ] . textContent . trim ( ) ) . toBe ( "A Dark Cave" ) ;
expect ( rooms [ 3 ] . textContent . trim ( ) ) . toBe ( "The Palace" ) ;
done ( ) ;
} ) ) ;
} ) ;
2017-06-05 21:42:25 +02:00
2020-04-22 13:11:48 +02:00
describe ( "The \"Groupchats\" section" , function ( ) {
2018-03-11 13:56:53 +01:00
2020-04-22 13:11:48 +02:00
it ( "shows the number of unread mentions received" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { 'allow_bookmarks' : false } ,
async function ( done , _converse ) {
2017-07-11 10:41:11 +02:00
2020-04-22 13:11:48 +02:00
await mock . openControlBox ( _converse ) ;
const roomspanel = _converse . chatboxviews . get ( 'controlbox' ) . roomspanel ;
expect ( roomspanel . el . querySelectorAll ( '.available-room' ) . length ) . toBe ( 0 ) ;
2017-06-05 14:50:29 +02:00
2020-04-22 13:11:48 +02:00
const muc _jid = 'kitchen@conference.shakespeare.lit' ;
const message = 'fires: Your attention is required' ;
await mock . openAndEnterChatRoom ( _converse , muc _jid , 'fires' ) ;
const view = _converse . api . chatviews . get ( muc _jid ) ;
await u . waitUntil ( ( ) => roomspanel . el . querySelectorAll ( '.available-room' ) . length ) ;
expect ( roomspanel . el . querySelectorAll ( '.available-room' ) . length ) . toBe ( 1 ) ;
expect ( roomspanel . el . querySelectorAll ( '.msgs-indicator' ) . length ) . toBe ( 0 ) ;
2017-06-05 14:50:29 +02:00
2020-04-22 13:11:48 +02:00
view . model . set ( { 'minimized' : true } ) ;
2018-04-30 15:24:37 +02:00
2020-04-22 13:11:48 +02:00
const nick = mock . chatroom _names [ 0 ] ;
2020-04-24 17:33:32 +02:00
await view . model . handleMessageStanza ( $msg ( {
2020-04-22 13:11:48 +02:00
from : muc _jid + '/' + nick ,
id : u . getUniqueId ( ) ,
to : 'romeo@montague.lit' ,
type : 'groupchat'
2018-11-20 18:10:55 +01:00
} ) . c ( 'body' ) . t ( message ) . tree ( ) ) ;
2020-04-22 13:11:48 +02:00
await u . waitUntil ( ( ) => view . model . messages . length ) ;
expect ( roomspanel . el . querySelectorAll ( '.available-room' ) . length ) . toBe ( 1 ) ;
expect ( roomspanel . el . querySelectorAll ( '.msgs-indicator' ) . length ) . toBe ( 1 ) ;
expect ( roomspanel . el . querySelector ( '.msgs-indicator' ) . textContent . trim ( ) ) . toBe ( '1' ) ;
2020-04-24 17:33:32 +02:00
await view . model . handleMessageStanza ( $msg ( {
2020-04-22 13:11:48 +02:00
'from' : muc _jid + '/' + nick ,
'id' : u . getUniqueId ( ) ,
'to' : 'romeo@montague.lit' ,
'type' : 'groupchat'
} ) . c ( 'body' ) . t ( message ) . tree ( ) ) ;
await u . waitUntil ( ( ) => view . model . messages . length > 1 ) ;
expect ( roomspanel . el . querySelectorAll ( '.available-room' ) . length ) . toBe ( 1 ) ;
expect ( roomspanel . el . querySelectorAll ( '.msgs-indicator' ) . length ) . toBe ( 1 ) ;
expect ( roomspanel . el . querySelector ( '.msgs-indicator' ) . textContent . trim ( ) ) . toBe ( '2' ) ;
view . model . set ( { 'minimized' : false } ) ;
expect ( roomspanel . el . querySelectorAll ( '.available-room' ) . length ) . toBe ( 1 ) ;
expect ( roomspanel . el . querySelectorAll ( '.msgs-indicator' ) . length ) . toBe ( 0 ) ;
done ( ) ;
} ) ) ;
} ) ;
2019-07-10 09:47:13 +02:00
2020-04-22 13:11:48 +02:00
describe ( "A XEP-0085 Chat Status Notification" , function ( ) {
it ( "is is not sent out to a MUC if the user is a visitor in a moderated room" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { } ,
async function ( done , _converse ) {
spyOn ( _converse . ChatRoom . prototype , 'sendChatState' ) . and . callThrough ( ) ;
const muc _jid = 'lounge@montague.lit' ;
const features = [
'http://jabber.org/protocol/muc' ,
'jabber:iq:register' ,
'muc_passwordprotected' ,
'muc_hidden' ,
'muc_temporary' ,
'muc_membersonly' ,
'muc_moderated' ,
'muc_anonymous'
]
await mock . openAndEnterChatRoom ( _converse , muc _jid , 'romeo' , features ) ;
const view = _converse . api . chatviews . get ( muc _jid ) ;
view . model . setChatState ( _converse . ACTIVE ) ;
expect ( view . model . sendChatState ) . toHaveBeenCalled ( ) ;
const last _stanza = _converse . connection . sent _stanzas . pop ( ) ;
expect ( Strophe . serialize ( last _stanza ) ) . toBe (
` <message to="lounge@montague.lit" type="groupchat" xmlns="jabber:client"> ` +
` <active xmlns="http://jabber.org/protocol/chatstates"/> ` +
` <no-store xmlns="urn:xmpp:hints"/> ` +
` <no-permanent-store xmlns="urn:xmpp:hints"/> ` +
` </message> ` ) ;
// Romeo loses his voice
const presence = $pres ( {
to : 'romeo@montague.lit/orchard' ,
from : ` ${ muc _jid } /romeo `
} ) . c ( 'x' , { xmlns : Strophe . NS . MUC _USER } )
. c ( 'item' , { 'affiliation' : 'none' , 'role' : 'visitor' } ) . up ( )
. c ( 'status' , { code : '110' } ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
const occupant = view . model . occupants . findWhere ( { 'jid' : _converse . bare _jid } ) ;
expect ( occupant . get ( 'role' ) ) . toBe ( 'visitor' ) ;
spyOn ( _converse . connection , 'send' ) ;
view . model . setChatState ( _converse . INACTIVE ) ;
expect ( view . model . sendChatState . calls . count ( ) ) . toBe ( 2 ) ;
expect ( _converse . connection . send ) . not . toHaveBeenCalled ( ) ;
done ( ) ;
} ) ) ;
describe ( "A composing notification" , function ( ) {
it ( "will be shown if received" ,
2019-07-10 09:47:13 +02:00
mock . initConverse (
2020-04-22 13:11:48 +02:00
[ 'rosterGroupsFetched' ] , { } ,
2019-07-10 09:47:13 +02:00
async function ( done , _converse ) {
2020-04-22 13:11:48 +02:00
const muc _jid = 'coven@chat.shakespeare.lit' ;
const members = [
{ 'affiliation' : 'member' , 'nick' : 'majortom' , 'jid' : 'majortom@example.org' } ,
{ 'affiliation' : 'admin' , 'nick' : 'groundcontrol' , 'jid' : 'groundcontrol@example.org' }
] ;
await mock . openAndEnterChatRoom ( _converse , muc _jid , 'some1' , [ ] , members ) ;
2019-07-10 09:47:13 +02:00
const view = _converse . api . chatviews . get ( muc _jid ) ;
2019-02-25 18:57:09 +01:00
2020-04-22 13:11:48 +02:00
let csntext = await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent ) ;
expect ( csntext . trim ( ) ) . toEqual ( "some1 has entered the groupchat" ) ;
2019-02-25 18:57:09 +01:00
2020-04-22 13:11:48 +02:00
let presence = $pres ( {
to : 'romeo@montague.lit/_converse.js-29092160' ,
from : 'coven@chat.shakespeare.lit/newguy'
} )
. c ( 'x' , { xmlns : Strophe . NS . MUC _USER } )
. c ( 'item' , {
'affiliation' : 'none' ,
'jid' : 'newguy@montague.lit/_converse.js-290929789' ,
'role' : 'participant'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ===
"some1 and newguy have entered the groupchat" ) ;
2019-02-25 18:57:09 +01:00
2020-04-22 13:11:48 +02:00
presence = $pres ( {
to : 'romeo@montague.lit/_converse.js-29092160' ,
from : 'coven@chat.shakespeare.lit/nomorenicks'
} )
. c ( 'x' , { xmlns : Strophe . NS . MUC _USER } )
. c ( 'item' , {
'affiliation' : 'none' ,
'jid' : 'nomorenicks@montague.lit/_converse.js-290929789' ,
'role' : 'participant'
2020-03-18 19:32:03 +01:00
} ) ;
2020-04-22 13:11:48 +02:00
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ===
2020-06-04 15:29:31 +02:00
"some1, newguy and nomorenicks have entered the groupchat" , 1000 ) ;
2020-03-18 19:32:03 +01:00
2020-04-22 13:11:48 +02:00
// Manually clear so that we can more easily test
view . model . notifications . set ( 'entered' , [ ] ) ;
2020-06-04 15:29:31 +02:00
await u . waitUntil ( ( ) => ! view . el . querySelector ( '.chat-content__notifications' ) . textContent , 1000 ) ;
2018-04-06 13:56:14 +02:00
2020-04-22 13:11:48 +02:00
// See XEP-0085 https://xmpp.org/extensions/xep-0085.html#definitions
2019-02-25 18:57:09 +01:00
2020-05-15 14:33:31 +02:00
const remove _notifications _timeouts = [ ] ;
const setTimeout = window . setTimeout ;
spyOn ( window , 'setTimeout' ) . and . callFake ( ( f , w ) => {
2020-04-22 13:11:48 +02:00
if ( f . toString ( ) === "() => this.removeNotification(actor, state)" ) {
2020-05-15 14:33:31 +02:00
remove _notifications _timeouts . push ( f )
2020-04-22 13:11:48 +02:00
}
2020-05-15 14:33:31 +02:00
setTimeout ( f , w ) ;
2020-04-22 13:11:48 +02:00
} ) ;
2019-02-25 18:57:09 +01:00
2020-04-22 13:11:48 +02:00
// <composing> state
let msg = $msg ( {
from : muc _jid + '/newguy' ,
id : u . getUniqueId ( ) ,
to : 'romeo@montague.lit' ,
type : 'groupchat'
} ) . c ( 'body' ) . c ( 'composing' , { 'xmlns' : Strophe . NS . CHATSTATES } ) . tree ( ) ;
_converse . connection . _dataRecv ( mock . createRequest ( msg ) ) ;
2019-02-25 18:57:09 +01:00
2020-06-04 15:29:31 +02:00
csntext = await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent , 1000 ) ;
2020-04-22 13:11:48 +02:00
expect ( csntext . trim ( ) ) . toEqual ( 'newguy is typing' ) ;
2020-05-15 14:33:31 +02:00
expect ( remove _notifications _timeouts . length ) . toBe ( 1 ) ;
2020-04-22 13:11:48 +02:00
expect ( view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ) . toEqual ( 'newguy is typing' ) ;
2019-02-25 18:57:09 +01:00
2020-04-22 13:11:48 +02:00
// <composing> state for a different occupant
msg = $msg ( {
from : muc _jid + '/nomorenicks' ,
2020-02-07 12:24:02 +01:00
id : u . getUniqueId ( ) ,
2019-06-03 07:58:51 +02:00
to : 'romeo@montague.lit' ,
2019-02-25 18:57:09 +01:00
type : 'groupchat'
2020-04-22 13:11:48 +02:00
} ) . c ( 'body' ) . c ( 'composing' , { 'xmlns' : Strophe . NS . CHATSTATES } ) . tree ( ) ;
2020-04-24 17:33:32 +02:00
await view . model . handleMessageStanza ( msg ) ;
2020-06-04 15:29:31 +02:00
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) === 'newguy and nomorenicks are typing' , 1000 ) ;
2019-02-25 18:57:09 +01:00
2020-04-22 13:11:48 +02:00
// <composing> state for a different occupant
msg = $msg ( {
from : muc _jid + '/majortom' ,
id : u . getUniqueId ( ) ,
to : 'romeo@montague.lit' ,
type : 'groupchat'
} ) . c ( 'body' ) . c ( 'composing' , { 'xmlns' : Strophe . NS . CHATSTATES } ) . tree ( ) ;
2020-04-24 17:33:32 +02:00
await view . model . handleMessageStanza ( msg ) ;
2020-06-04 15:29:31 +02:00
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) === 'newguy, nomorenicks and majortom are typing' , 1000 ) ;
2020-02-19 16:46:56 +01:00
2020-04-22 13:11:48 +02:00
// <composing> state for a different occupant
msg = $msg ( {
from : muc _jid + '/groundcontrol' ,
id : u . getUniqueId ( ) ,
to : 'romeo@montague.lit' ,
type : 'groupchat'
} ) . c ( 'body' ) . c ( 'composing' , { 'xmlns' : Strophe . NS . CHATSTATES } ) . tree ( ) ;
2020-04-24 17:33:32 +02:00
await view . model . handleMessageStanza ( msg ) ;
2020-06-04 15:29:31 +02:00
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) === 'newguy, nomorenicks and others are typing' , 1000 ) ;
2019-02-25 18:57:09 +01:00
2020-04-22 13:11:48 +02:00
msg = $msg ( {
from : ` ${ muc _jid } /some1 ` ,
id : u . getUniqueId ( ) ,
to : 'romeo@montague.lit' ,
type : 'groupchat'
} ) . c ( 'body' ) . t ( 'hello world' ) . tree ( ) ;
2020-04-24 17:33:32 +02:00
await view . model . handleMessageStanza ( msg ) ;
2019-02-25 18:57:09 +01:00
2020-06-04 15:29:31 +02:00
await u . waitUntil ( ( ) => view . el . querySelectorAll ( '.chat-msg' ) . length === 1 ) ;
2020-04-22 13:11:48 +02:00
expect ( view . el . querySelector ( '.chat-msg .chat-msg__text' ) . textContent . trim ( ) ) . toBe ( 'hello world' ) ;
2019-02-25 18:57:09 +01:00
2020-04-22 13:11:48 +02:00
// Test that the composing notifications get removed via timeout.
2020-06-04 15:29:31 +02:00
if ( remove _notifications _timeouts . length ) {
remove _notifications _timeouts [ 0 ] ( ) ;
}
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) === 'nomorenicks, majortom and groundcontrol are typing' , 1000 ) ;
2020-04-22 13:11:48 +02:00
done ( ) ;
} ) ) ;
2017-06-05 14:50:29 +02:00
} ) ;
2019-04-10 22:03:00 +02:00
2020-04-22 13:11:48 +02:00
describe ( "A paused notification" , function ( ) {
2019-04-10 22:03:00 +02:00
2020-04-22 13:11:48 +02:00
it ( "will be shown if received" ,
2019-04-10 22:03:00 +02:00
mock . initConverse (
2019-10-11 16:38:01 +02:00
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { } ,
2019-04-10 22:03:00 +02:00
async function ( done , _converse ) {
2020-04-22 13:11:48 +02:00
const muc _jid = 'coven@chat.shakespeare.lit' ;
await mock . openAndEnterChatRoom ( _converse , muc _jid , 'some1' ) ;
const view = _converse . chatboxviews . get ( 'coven@chat.shakespeare.lit' ) ;
2019-04-10 22:03:00 +02:00
2020-04-22 13:11:48 +02:00
/ * < p r e s e n c e t o = " r o m e o @ m o n t a g u e . l i t / _ c o n v e r s e . j s - 2 9 0 9 2 1 6 0 "
* from = "coven@chat.shakespeare.lit/some1" >
* < x xmlns = "http://jabber.org/protocol/muc#user" >
* < item affiliation = "owner" jid = "romeo@montague.lit/_converse.js-29092160" role = "moderator" / >
* < status code = "110" / >
* < / x >
* < / p r e s e n c e > < / b o d y >
* /
let presence = $pres ( {
to : 'romeo@montague.lit/_converse.js-29092160' ,
from : 'coven@chat.shakespeare.lit/some1'
} ) . c ( 'x' , { xmlns : Strophe . NS . MUC _USER } )
. c ( 'item' , {
'affiliation' : 'owner' ,
'jid' : 'romeo@montague.lit/_converse.js-29092160' ,
'role' : 'moderator'
} ) . up ( )
. c ( 'status' , { code : '110' } ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
const csntext = await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent ) ;
expect ( csntext . trim ( ) ) . toEqual ( "some1 has entered the groupchat" ) ;
2019-04-10 22:03:00 +02:00
2020-04-22 13:11:48 +02:00
presence = $pres ( {
to : 'romeo@montague.lit/_converse.js-29092160' ,
from : 'coven@chat.shakespeare.lit/newguy'
} )
. c ( 'x' , { xmlns : Strophe . NS . MUC _USER } )
. c ( 'item' , {
'affiliation' : 'none' ,
'jid' : 'newguy@montague.lit/_converse.js-290929789' ,
'role' : 'participant'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ===
"some1 and newguy have entered the groupchat" ) ;
2019-09-24 15:33:41 +02:00
2020-04-22 13:11:48 +02:00
presence = $pres ( {
to : 'romeo@montague.lit/_converse.js-29092160' ,
from : 'coven@chat.shakespeare.lit/nomorenicks'
} )
. c ( 'x' , { xmlns : Strophe . NS . MUC _USER } )
. c ( 'item' , {
'affiliation' : 'none' ,
'jid' : 'nomorenicks@montague.lit/_converse.js-290929789' ,
'role' : 'participant'
} ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ===
"some1, newguy and nomorenicks have entered the groupchat" ) ;
2020-01-17 14:43:33 +01:00
2020-04-22 13:11:48 +02:00
// Manually clear so that we can more easily test
view . model . notifications . set ( 'entered' , [ ] ) ;
await u . waitUntil ( ( ) => ! view . el . querySelector ( '.chat-content__notifications' ) . textContent ) ;
2019-04-10 23:56:06 +02:00
2020-04-22 13:11:48 +02:00
// See XEP-0085 https://xmpp.org/extensions/xep-0085.html#definitions
2019-04-10 23:56:06 +02:00
2020-04-22 13:11:48 +02:00
// <composing> state
var msg = $msg ( {
from : muc _jid + '/newguy' ,
id : u . getUniqueId ( ) ,
to : 'romeo@montague.lit' ,
type : 'groupchat'
} ) . c ( 'body' ) . c ( 'composing' , { 'xmlns' : Strophe . NS . CHATSTATES } ) . tree ( ) ;
2020-04-24 17:33:32 +02:00
await view . model . handleMessageStanza ( msg ) ;
2020-04-22 13:11:48 +02:00
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent ) ;
expect ( view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) ) . toBe ( 'newguy is typing' ) ;
2019-04-11 23:10:57 +02:00
2020-04-22 13:11:48 +02:00
// <composing> state for a different occupant
msg = $msg ( {
from : muc _jid + '/nomorenicks' ,
id : u . getUniqueId ( ) ,
to : 'romeo@montague.lit' ,
type : 'groupchat'
} ) . c ( 'body' ) . c ( 'composing' , { 'xmlns' : Strophe . NS . CHATSTATES } ) . tree ( ) ;
2020-04-24 17:33:32 +02:00
await view . model . handleMessageStanza ( msg ) ;
2019-04-10 23:56:06 +02:00
2020-04-22 13:11:48 +02:00
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) == 'newguy and nomorenicks are typing' ) ;
// <paused> state from occupant who typed first
msg = $msg ( {
from : muc _jid + '/newguy' ,
id : u . getUniqueId ( ) ,
to : 'romeo@montague.lit' ,
type : 'groupchat'
} ) . c ( 'body' ) . c ( 'paused' , { 'xmlns' : Strophe . NS . CHATSTATES } ) . tree ( ) ;
2020-04-24 17:33:32 +02:00
await view . model . handleMessageStanza ( msg ) ;
2020-04-23 13:36:16 +02:00
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) == 'nomorenicks is typing\nnewguy has stopped typing' ) ;
2019-04-10 23:56:06 +02:00
done ( ) ;
} ) ) ;
2019-04-10 22:03:00 +02:00
} ) ;
2016-11-03 11:01:09 +01:00
} ) ;
2020-04-22 13:11:48 +02:00
describe ( "A muted user" , function ( ) {
it ( "will receive a user-friendly error message when trying to send a message" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { } ,
async function ( done , _converse ) {
const muc _jid = 'trollbox@montague.lit' ;
await mock . openAndEnterChatRoom ( _converse , muc _jid , 'troll' ) ;
const view = _converse . api . chatviews . get ( muc _jid ) ;
const textarea = view . el . querySelector ( '.chat-textarea' ) ;
textarea . value = 'Hello world' ;
view . onFormSubmitted ( new Event ( 'submit' ) ) ;
2020-05-15 14:33:31 +02:00
await new Promise ( resolve => view . model . messages . once ( 'rendered' , resolve ) ) ;
2020-04-22 13:11:48 +02:00
let stanza = u . toStanza ( `
2020-05-15 14:33:31 +02:00
< message id = "${view.model.messages.at(0).get('msgid')}"
xmlns = "jabber:client"
type = "error"
to = "troll@montague.lit/resource"
from = "trollbox@montague.lit" >
2020-04-22 13:11:48 +02:00
< error type = "auth" > < forbidden xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas" / > < / e r r o r >
< / m e s s a g e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( stanza ) ) ;
2020-05-15 14:33:31 +02:00
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-msg__error' ) ? . textContent . trim ( ) , 1000 ) ;
expect ( view . el . querySelector ( '.chat-msg__error' ) . textContent . trim ( ) ) . toBe (
2020-04-22 13:11:48 +02:00
"Your message was not delivered because you weren't allowed to send it." ) ;
textarea . value = 'Hello again' ;
view . onFormSubmitted ( new Event ( 'submit' ) ) ;
2020-05-15 14:33:31 +02:00
await u . waitUntil ( ( ) => view . el . querySelectorAll ( '.chat-msg__text' ) . length === 2 ) ;
2020-04-22 13:11:48 +02:00
stanza = u . toStanza ( `
2020-05-15 14:33:31 +02:00
< message id = "${view.model.messages.at(1).get('msgid')}"
xmlns = "jabber:client"
type = "error"
to = "troll@montague.lit/resource"
from = "trollbox@montague.lit" >
2020-04-22 13:11:48 +02:00
< error type = "auth" >
< forbidden xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas" / >
< text xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas" > Thou shalt not ! < / t e x t >
< / e r r o r >
< / m e s s a g e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( stanza ) ) ;
2020-05-15 14:33:31 +02:00
await u . waitUntil ( ( ) => view . el . querySelectorAll ( '.chat-msg__error' ) . length === 2 ) ;
const sel = 'converse-message-history converse-chat-message:last-child .chat-msg__error' ;
await u . waitUntil ( ( ) => view . el . querySelector ( sel ) ? . textContent . trim ( ) ) ;
expect ( view . el . querySelector ( sel ) . textContent . trim ( ) ) . toBe ( 'Thou shalt not!' )
2020-04-22 13:11:48 +02:00
done ( ) ;
} ) ) ;
it ( "will see an explanatory message instead of a textarea" ,
mock . initConverse (
[ 'rosterGroupsFetched' , 'chatBoxesFetched' ] , { } ,
async function ( done , _converse ) {
const features = [
'http://jabber.org/protocol/muc' ,
'jabber:iq:register' ,
Strophe . NS . SID ,
'muc_moderated' ,
]
const muc _jid = 'trollbox@montague.lit' ;
await mock . openAndEnterChatRoom ( _converse , muc _jid , 'troll' , features ) ;
const view = _converse . api . chatviews . get ( muc _jid ) ;
expect ( _ . isNull ( view . el . querySelector ( '.chat-textarea' ) ) ) . toBe ( false ) ;
let stanza = u . toStanza ( `
< presence
from = 'trollbox@montague.lit/troll'
to = 'romeo@montague.lit/orchard' >
< x xmlns = 'http://jabber.org/protocol/muc#user' >
< item affiliation = 'none'
nick = 'troll'
role = 'visitor' / >
< status code = '110' / >
< / x >
< / p r e s e n c e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( stanza ) ) ;
expect ( view . el . querySelector ( '.chat-textarea' ) ) . toBe ( null ) ;
let bottom _panel = view . el . querySelector ( '.muc-bottom-panel' ) ;
expect ( bottom _panel . textContent . trim ( ) ) . toBe ( "You're not allowed to send messages in this room" ) ;
// This only applies to moderated rooms, so let's check that
// the textarea becomes visible when the room's
// configuration changes to be non-moderated
view . model . features . set ( 'moderated' , false ) ;
expect ( view . el . querySelector ( '.muc-bottom-panel' ) ) . toBe ( null ) ;
let textarea = view . el . querySelector ( '.chat-textarea' ) ;
expect ( textarea === null ) . toBe ( false ) ;
view . model . features . set ( 'moderated' , true ) ;
expect ( view . el . querySelector ( '.chat-textarea' ) ) . toBe ( null ) ;
bottom _panel = view . el . querySelector ( '.muc-bottom-panel' ) ;
expect ( bottom _panel . textContent . trim ( ) ) . toBe ( "You're not allowed to send messages in this room" ) ;
// Check now that things get restored when the user is given a voice
await u . waitUntil ( ( ) =>
Array . from ( view . el . querySelectorAll ( '.chat-info__message' ) ) . pop ( ) ? . textContent . trim ( ) ===
"troll is no longer an owner of this groupchat"
) ;
stanza = u . toStanza ( `
< presence
from = 'trollbox@montague.lit/troll'
to = 'romeo@montague.lit/orchard' >
< x xmlns = 'http://jabber.org/protocol/muc#user' >
< item affiliation = 'none'
nick = 'troll'
role = 'participant' / >
< status code = '110' / >
< / x >
< / p r e s e n c e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( stanza ) ) ;
bottom _panel = view . el . querySelector ( '.muc-bottom-panel' ) ;
expect ( bottom _panel ) . toBe ( null ) ;
textarea = view . el . querySelector ( '.chat-textarea' ) ;
expect ( textarea === null ) . toBe ( false ) ;
// Check now that things get restored when the user is given a voice
2020-04-23 13:36:16 +02:00
await u . waitUntil ( ( ) => view . el . querySelector ( '.chat-content__notifications' ) . textContent . trim ( ) === "troll has been given a voice" ) ;
2020-04-22 13:11:48 +02:00
done ( ) ;
} ) ) ;
} ) ;
2020-05-07 10:54:03 +02:00
describe ( "when muc_send_probes is true" , function ( ) {
it ( "sends presence probes when muc_send_probes is true" ,
mock . initConverse (
[ 'rosterGroupsFetched' ] , { 'muc_send_probes' : true } ,
async function ( done , _converse ) {
const muc _jid = 'lounge@montague.lit' ;
await mock . openAndEnterChatRoom ( _converse , muc _jid , 'romeo' ) ;
2020-06-15 12:23:52 +02:00
let stanza = u . toStanza ( `
2020-05-07 10:54:03 +02:00
< message xmlns = "jabber:client" to = "${_converse.jid}" type = "groupchat" from = "${muc_jid}/ralphm" >
< body > This message will trigger a presence probe < / b o d y >
< / m e s s a g e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( stanza ) ) ;
const view = _converse . chatboxviews . get ( muc _jid ) ;
await u . waitUntil ( ( ) => view . model . messages . length ) ;
2020-06-15 12:23:52 +02:00
let occupant = view . model . messages . at ( 0 ) ? . occupant ;
2020-05-07 10:54:03 +02:00
expect ( occupant ) . toBeDefined ( ) ;
expect ( occupant . get ( 'nick' ) ) . toBe ( 'ralphm' ) ;
expect ( occupant . get ( 'affiliation' ) ) . toBeUndefined ( ) ;
expect ( occupant . get ( 'role' ) ) . toBeUndefined ( ) ;
const sent _stanzas = _converse . connection . sent _stanzas ;
2020-06-15 12:23:52 +02:00
let probe = await u . waitUntil ( ( ) => sent _stanzas . filter ( s => s . matches ( 'presence[type="probe"]' ) ) . pop ( ) ) ;
2020-05-07 10:54:03 +02:00
expect ( Strophe . serialize ( probe ) ) . toBe (
` <presence to=" ${ muc _jid } /ralphm" type="probe" xmlns="jabber:client"> ` +
` <priority>0</priority> ` +
2020-10-14 13:09:33 +02:00
` <c hash="sha-1" node="https://conversejs.org" ver="PxXfr6uz8ClMWIga0OB/MhKNH/M=" xmlns="http://jabber.org/protocol/caps"/> ` +
2020-05-07 10:54:03 +02:00
` </presence> ` ) ;
2020-06-15 12:23:52 +02:00
let presence = u . toStanza (
2020-05-07 10:54:03 +02:00
` <presence xmlns="jabber:client" to=" ${ converse . jid } " from=" ${ muc _jid } /ralphm">
< x xmlns = "http://jabber.org/protocol/muc#user" >
< item affiliation = "member" jid = "ralph@example.org/Conversations.ZvLu" role = "participant" / >
< / x >
< / p r e s e n c e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
2020-06-15 12:23:52 +02:00
expect ( occupant . get ( 'affiliation' ) ) . toBe ( 'member' ) ;
expect ( occupant . get ( 'role' ) ) . toBe ( 'participant' ) ;
// Check that unavailable but affiliated occupants don't get destroyed
stanza = u . toStanza ( `
< message xmlns = "jabber:client" to = "${_converse.jid}" type = "groupchat" from = "${muc_jid}/gonePhising" >
< body > This message from an unavailable user will trigger a presence probe < / b o d y >
< / m e s s a g e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( stanza ) ) ;
await u . waitUntil ( ( ) => view . model . messages . length === 2 ) ;
occupant = view . model . messages . at ( 1 ) ? . occupant ;
expect ( occupant ) . toBeDefined ( ) ;
expect ( occupant . get ( 'nick' ) ) . toBe ( 'gonePhising' ) ;
expect ( occupant . get ( 'affiliation' ) ) . toBeUndefined ( ) ;
expect ( occupant . get ( 'role' ) ) . toBeUndefined ( ) ;
probe = await u . waitUntil ( ( ) => sent _stanzas . filter ( s => s . matches ( ` presence[to=" ${ muc _jid } /gonePhising"] ` ) ) . pop ( ) ) ;
expect ( Strophe . serialize ( probe ) ) . toBe (
` <presence to=" ${ muc _jid } /gonePhising" type="probe" xmlns="jabber:client"> ` +
` <priority>0</priority> ` +
2020-10-14 13:09:33 +02:00
` <c hash="sha-1" node="https://conversejs.org" ver="PxXfr6uz8ClMWIga0OB/MhKNH/M=" xmlns="http://jabber.org/protocol/caps"/> ` +
2020-06-15 12:23:52 +02:00
` </presence> ` ) ;
presence = u . toStanza (
` <presence xmlns="jabber:client" type="unavailable" to=" ${ converse . jid } " from=" ${ muc _jid } /gonePhising">
< x xmlns = "http://jabber.org/protocol/muc#user" >
< item affiliation = "member" jid = "gonePhishing@example.org/d34dBEEF" role = "participant" / >
< / x >
< / p r e s e n c e > ` ) ;
_converse . connection . _dataRecv ( mock . createRequest ( presence ) ) ;
expect ( view . model . occupants . length ) . toBe ( 3 ) ;
2020-05-07 10:54:03 +02:00
expect ( occupant . get ( 'affiliation' ) ) . toBe ( 'member' ) ;
expect ( occupant . get ( 'role' ) ) . toBe ( 'participant' ) ;
done ( ) ;
} ) ) ;
} ) ;
2020-04-22 12:10:39 +02:00
} ) ;