/*global converse */ (function (root, factory) { define([ "jquery", "underscore", "mock", "test_utils", "utils" ], function ($, _, mock, test_utils, utils) { return factory($, _, mock, test_utils, utils); } ); } (this, function ($, _, mock, test_utils, utils) { var $pres = converse_api.env.$pres; var $msg = converse_api.env.$msg; var Strophe = converse_api.env.Strophe; return describe("ChatRooms", function (mock, test_utils) { beforeEach(function () { runs(function () { test_utils.closeAllChatBoxes(); test_utils.clearBrowserStorage(); }); }); describe("The \"rooms\" API", function () { beforeEach(function () { test_utils.closeAllChatBoxes(); test_utils.clearBrowserStorage(); converse.rosterview.model.reset(); test_utils.createContacts('current'); }); it("has a method 'close' which closes rooms by JID or all rooms when called with no arguments", function () { runs(function () { test_utils.openChatRoom('lounge', 'localhost', 'dummy'); test_utils.openChatRoom('leisure', 'localhost', 'dummy'); test_utils.openChatRoom('news', 'localhost', 'dummy'); expect(converse.chatboxviews.get('lounge@localhost').$el.is(':visible')).toBeTruthy(); expect(converse.chatboxviews.get('leisure@localhost').$el.is(':visible')).toBeTruthy(); expect(converse.chatboxviews.get('news@localhost').$el.is(':visible')).toBeTruthy(); }); waits('100'); runs(function () { converse_api.rooms.close('lounge@localhost'); expect(converse.chatboxviews.get('lounge@localhost')).toBeUndefined(); expect(converse.chatboxviews.get('leisure@localhost').$el.is(':visible')).toBeTruthy(); expect(converse.chatboxviews.get('news@localhost').$el.is(':visible')).toBeTruthy(); converse_api.rooms.close(['leisure@localhost', 'news@localhost']); expect(converse.chatboxviews.get('lounge@localhost')).toBeUndefined(); expect(converse.chatboxviews.get('leisure@localhost')).toBeUndefined(); expect(converse.chatboxviews.get('news@localhost')).toBeUndefined(); test_utils.openChatRoom('lounge', 'localhost', 'dummy'); test_utils.openChatRoom('leisure', 'localhost', 'dummy'); expect(converse.chatboxviews.get('lounge@localhost').$el.is(':visible')).toBeTruthy(); expect(converse.chatboxviews.get('leisure@localhost').$el.is(':visible')).toBeTruthy(); }); waits('100'); runs(function () { converse_api.rooms.close(); expect(converse.chatboxviews.get('lounge@localhost')).toBeUndefined(); expect(converse.chatboxviews.get('leisure@localhost')).toBeUndefined(); }); }); it("has a method 'get' which returns a wrapped chat room (if it exists)", function () { waits('300'); // ChatBox.show() is debounced for 250ms runs(function () { test_utils.openChatRoom('lounge', 'localhost', 'dummy'); var jid = 'lounge@localhost'; var room = converse_api.rooms.get(jid); expect(room instanceof Object).toBeTruthy(); expect(room.is_chatroom).toBeTruthy(); var chatroomview = converse.chatboxviews.get(jid); expect(chatroomview.$el.is(':visible')).toBeTruthy(); chatroomview.close(); }); waits('300'); // ChatBox.show() is debounced for 250ms runs(function () { // Test with mixed case test_utils.openChatRoom('Leisure', 'localhost', 'dummy'); var jid = 'Leisure@localhost'; var room = converse_api.rooms.get(jid); expect(room instanceof Object).toBeTruthy(); var chatroomview = converse.chatboxviews.get(jid.toLowerCase()); expect(chatroomview.$el.is(':visible')).toBeTruthy(); }); waits('300'); // ChatBox.show() is debounced for 250ms runs(function () { var jid = 'leisure@localhost'; var room = converse_api.rooms.get(jid); expect(room instanceof Object).toBeTruthy(); var chatroomview = converse.chatboxviews.get(jid.toLowerCase()); expect(chatroomview.$el.is(':visible')).toBeTruthy(); jid = 'leiSure@localhost'; room = converse_api.rooms.get(jid); expect(room instanceof Object).toBeTruthy(); chatroomview = converse.chatboxviews.get(jid.toLowerCase()); expect(chatroomview.$el.is(':visible')).toBeTruthy(); chatroomview.close(); // Non-existing room jid = 'lounge2@localhost'; room = converse_api.rooms.get(jid); expect(typeof room === 'undefined').toBeTruthy(); }); }); it("has a method 'open' which opens and returns a wrapped chat box", function () { var chatroomview; var jid = 'lounge@localhost'; var room = converse_api.rooms.open(jid); runs(function () { // Test on chat room that doesn't exist. expect(room instanceof Object).toBeTruthy(); expect(room.is_chatroom).toBeTruthy(); chatroomview = converse.chatboxviews.get(jid); expect(chatroomview.$el.is(':visible')).toBeTruthy(); }); waits('300'); // ChatBox.show() is debounced for 250ms runs(function () { // Test again, now that the room exists. room = converse_api.rooms.open(jid); expect(room instanceof Object).toBeTruthy(); expect(room.is_chatroom).toBeTruthy(); chatroomview = converse.chatboxviews.get(jid); expect(chatroomview.$el.is(':visible')).toBeTruthy(); }); waits('300'); // ChatBox.show() is debounced for 250ms runs(function () { // Test with mixed case in JID jid = 'Leisure@localhost'; room = converse_api.rooms.open(jid); expect(room instanceof Object).toBeTruthy(); chatroomview = converse.chatboxviews.get(jid.toLowerCase()); expect(chatroomview.$el.is(':visible')).toBeTruthy(); jid = 'leisure@localhost'; room = converse_api.rooms.open(jid); expect(room instanceof Object).toBeTruthy(); chatroomview = converse.chatboxviews.get(jid.toLowerCase()); expect(chatroomview.$el.is(':visible')).toBeTruthy(); jid = 'leiSure@localhost'; room = converse_api.rooms.open(jid); expect(room instanceof Object).toBeTruthy(); chatroomview = converse.chatboxviews.get(jid.toLowerCase()); expect(chatroomview.$el.is(':visible')).toBeTruthy(); chatroomview.close(); }); }); }); describe("A Chat Room", function () { beforeEach(function () { runs(function () { test_utils.closeAllChatBoxes(); test_utils.clearBrowserStorage(); }); }); it("can have spaces and special characters in its name", function () { test_utils.openChatRoom('lounge & leisure', 'localhost', 'dummy'); var view = converse.chatboxviews.get( Strophe.escapeNode('lounge & leisure')+'@localhost'); expect(view instanceof converse.ChatRoomView).toBe(true); }); it("shows users currently present in the room", function () { test_utils.openChatRoom('lounge', 'localhost', 'dummy'); var name; var view = this.chatboxviews.get('lounge@localhost'), $occupants = view.$('.occupant-list'); spyOn(view, 'onChatRoomPresence').andCallThrough(); var presence, role; for (var i=0; i-1; i--) { name = mock.chatroom_names[i]; role = mock.chatroom_roles[name].role; // See example 21 http://xmpp.org/extensions/xep-0045.html#enter-pres presence = $pres({ to:'dummy@localhost/pda', from:'lounge@localhost/'+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() + '@localhost', role: 'none' }).nodeTree; this.connection._dataRecv(test_utils.createRequest(presence)); expect(view.onChatRoomPresence).toHaveBeenCalled(); expect($occupants.find('li.online').length).toBe(i); } }.bind(converse)); it("indicates moderators by means of a special css class and tooltip", function () { test_utils.openChatRoom('lounge', 'localhost', 'dummy'); var view = this.chatboxviews.get('lounge@localhost'); var presence = $pres({ to:'dummy@localhost/pda', from:'lounge@localhost/moderatorman' }).c('x').attrs({xmlns:'http://jabber.org/protocol/muc#user'}) .c('item').attrs({ affiliation: 'admin', jid: name.replace(/ /g,'.').toLowerCase() + '@localhost', role: 'moderator', }).up() .c('status').attrs({code:'110'}).nodeTree; this.connection._dataRecv(test_utils.createRequest(presence)); var occupant = view.$el.find('.occupant-list').find('li'); expect(occupant.length).toBe(1); expect($(occupant).text()).toBe("moderatorman"); expect($(occupant).attr('class').indexOf('moderator')).not.toBe(-1); expect($(occupant).attr('title')).toBe('This user is a moderator'); }.bind(converse)); it("allows the user to invite their roster contacts to enter the chat room", function () { test_utils.openChatRoom('lounge', 'localhost', 'dummy'); spyOn(converse, 'emit'); spyOn(window, 'prompt').andCallFake(function () { return null; }); var $input; var view = this.chatboxviews.get('lounge@localhost'); view.$el.find('.chat-area').remove(); view.renderChatArea(); // Will init the widget test_utils.createContacts('current'); // We need roster contacts, so that we have someone to invite $input = view.$el.find('input.invited-contact.tt-input'); var $hint = view.$el.find('input.invited-contact.tt-hint'); runs (function () { expect($input.length).toBe(1); expect($input.attr('placeholder')).toBe('Invite'); $input.val("Felix"); $input.trigger('input'); }); waits(350); // Needed, due to debounce runs (function () { expect($input.val()).toBe('Felix'); expect($hint.val()).toBe('Felix Amsel'); var $sugg = view.$el.find('[data-jid="felix.amsel@localhost"]'); expect($sugg.length).toBe(1); $sugg.trigger('click'); expect(window.prompt).toHaveBeenCalled(); }); }.bind(converse)); it("can be joined automatically, based upon a received invite", function () { test_utils.openChatRoom('lounge', 'localhost', 'dummy'); spyOn(window, 'confirm').andCallFake(function () { return true; }); test_utils.createContacts('current'); // We need roster contacts, who can invite us var view = this.chatboxviews.get('lounge@localhost'); view.close(); var name = mock.cur_names[0]; var from_jid = name.replace(/ /g,'.').toLowerCase() + '@localhost'; var room_jid = 'lounge@localhost'; var reason = "Please join this chat room"; var message = $( "" + ""+ "" )[0]; expect(converse.chatboxes.models.length).toBe(1); expect(converse.chatboxes.models[0].id).toBe("controlbox"); converse.onDirectMUCInvitation(message); expect(window.confirm).toHaveBeenCalledWith( name + ' has invited you to join a chat room: '+ room_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(room_jid); }.bind(converse)); it("shows received groupchat messages", function () { test_utils.openChatRoom('lounge', 'localhost', 'dummy'); spyOn(converse, 'emit'); var view = this.chatboxviews.get('lounge@localhost'); if (!view.$el.find('.chat-area').length) { view.renderChatArea(); } var nick = mock.chatroom_names[0]; var text = 'This is a received message'; var message = $msg({ from: 'lounge@localhost/'+nick, id: '1', to: 'dummy@localhost', type: 'groupchat' }).c('body').t(text); view.onChatRoomMessage(message.nodeTree); var $chat_content = view.$el.find('.chat-content'); expect($chat_content.find('.chat-message').length).toBe(1); expect($chat_content.find('.chat-msg-content').text()).toBe(text); expect(converse.emit).toHaveBeenCalledWith('message', message.nodeTree); }.bind(converse)); it("shows sent groupchat messages", function () { test_utils.openChatRoom('lounge', 'localhost', 'dummy'); spyOn(converse, 'emit'); var view = converse.chatboxviews.get('lounge@localhost'); if (!view.$el.find('.chat-area').length) { view.renderChatArea(); } var text = 'This is a sent message'; view.$el.find('.chat-textarea').text(text); view.$el.find('textarea.chat-textarea').trigger($.Event('keypress', {keyCode: 13})); expect(converse.emit).toHaveBeenCalledWith('messageSend', text); var message = $msg({ from: 'lounge@localhost/dummy', to: 'dummy@localhost.com', type: 'groupchat', id: view.model.messages.at(0).get('msgid') }).c('body').t(text); view.onChatRoomMessage(message.nodeTree); var $chat_content = view.$el.find('.chat-content'); expect($chat_content.find('.chat-message').length).toBe(1); expect($chat_content.find('.chat-msg-content').last().text()).toBe(text); // We don't emit an event if it's our own message expect(converse.emit.callCount, 1); }); it("will cause the chat area to be scrolled down only if it was at the bottom already", function () { var message = 'This message is received while the chat area is scrolled up'; test_utils.openChatRoom('lounge', 'localhost', 'dummy'); var view = converse.chatboxviews.get('lounge@localhost'); spyOn(view, 'scrollDown').andCallThrough(); runs(function () { /* Create enough messages so that there's a * scrollbar. */ for (var i=0; i<20; i++) { converse.chatboxes.onMessage( $msg({ from: 'lounge@localhost/someone', to: 'dummy@localhost.com', type: 'groupchat', id: (new Date()).getTime(), }).c('body').t('Message: '+i).tree()); } }); waits(50); runs(function () { view.$content.scrollTop(0); }); waits(250); runs(function () { expect(view.model.get('scrolled')).toBeTruthy(); converse.chatboxes.onMessage( $msg({ from: 'lounge@localhost/someone', to: 'dummy@localhost.com', type: 'groupchat', id: (new Date()).getTime(), }).c('body').t(message).tree()); }); waits(150); runs(function () { // Now check that the message appears inside the chatbox in the DOM var $chat_content = view.$el.find('.chat-content'); var msg_txt = $chat_content.find('.chat-message:last').find('.chat-msg-content').text(); expect(msg_txt).toEqual(message); expect(view.$content.scrollTop()).toBe(0); }); }); it("shows received chatroom subject messages", function () { var text = 'Jabber/XMPP Development | RFCs and Extensions: http://xmpp.org/ | Protocol and XSF discussions: xsf@muc.xmpp.org'; var stanza = Strophe.xmlHtmlNode( ''+ ' '+text+''+ ' '+ ' '+ '').firstChild; test_utils.openChatRoom('jdev', 'conference.jabber.org', 'jc'); converse.connection._dataRecv(test_utils.createRequest(stanza)); var view = converse.chatboxviews.get('jdev@conference.jabber.org'); var $chat_content = view.$el.find('.chat-content'); expect($chat_content.find('.chat-info').length).toBe(1); expect($chat_content.find('.chat-info').text()).toBe('Topic set by ralphm to: '+text); }); it("informs users if their nicknames has been changed.", function () { /* The service then sends two presence stanzas to the full JID * 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: http://xmpp.org/extensions/xep-0045.html#changenick * * * * * * * * * * * * * * * */ var __ = utils.__.bind(converse); test_utils.openChatRoom('lounge', 'localhost', 'oldnick'); var view = this.chatboxviews.get('lounge@localhost'); var $chat_content = view.$el.find('.chat-content'); spyOn(view, 'onChatRoomPresence').andCallThrough(); // The user has just entered the room and receives their own // presence from the server. // See example 24: // http://xmpp.org/extensions/xep-0045.html#enter-pres var presence = $pres({ to:'dummy@localhost/pda', from:'lounge@localhost/oldnick', id:'DC352437-C019-40EC-B590-AF29E879AF97' }).c('x').attrs({xmlns:'http://jabber.org/protocol/muc#user'}) .c('item').attrs({ affiliation: 'member', jid: 'dummy@localhost/pda', role: 'occupant' }).up() .c('status').attrs({code:'110'}).up() .c('status').attrs({code:'210'}).nodeTree; this.connection._dataRecv(test_utils.createRequest(presence)); expect(view.onChatRoomPresence).toHaveBeenCalled(); var $occupants = view.$('.occupant-list'); expect($occupants.children().length).toBe(1); expect($occupants.children().first(0).text()).toBe("oldnick"); expect($occupants.children().first().hasClass('online')).toBe(true); expect($chat_content.find('div.chat-info').length).toBe(1); expect($chat_content.find('div.chat-info').html()).toBe(__(view.newNicknameMessages["210"], "oldnick")); presence = $pres().attrs({ from:'lounge@localhost/oldnick', id:'DC352437-C019-40EC-B590-AF29E879AF98', to:'dummy@localhost/pda', type:'unavailable' }) .c('x').attrs({xmlns:'http://jabber.org/protocol/muc#user'}) .c('item').attrs({ affiliation: 'member', jid: 'dummy@localhost/pda', nick: 'newnick', role: 'occupant' }).up() .c('status').attrs({code:'303'}).up() .c('status').attrs({code:'110'}).nodeTree; this.connection._dataRecv(test_utils.createRequest(presence)); expect(view.onChatRoomPresence).toHaveBeenCalled(); expect($chat_content.find('div.chat-info').length).toBe(2); expect($chat_content.find('div.chat-info').last().html()).toBe(__(view.newNicknameMessages["303"], "newnick")); // The occupant is still listed (because they have affiliation // of "member"), but they don't have the "online" class // anymore. $occupants = view.$('.occupant-list'); expect($occupants.children().length).toBe(1); expect($occupants.children().first().hasClass('online')).toBe(false); presence = $pres().attrs({ from:'lounge@localhost/newnick', id:'5B4F27A4-25ED-43F7-A699-382C6B4AFC67', to:'dummy@localhost/pda' }) .c('x').attrs({xmlns:'http://jabber.org/protocol/muc#user'}) .c('item').attrs({ affiliation: 'member', jid: 'dummy@localhost/pda', role: 'occupant' }).up() .c('status').attrs({code:'110'}).nodeTree; this.connection._dataRecv(test_utils.createRequest(presence)); expect(view.onChatRoomPresence).toHaveBeenCalled(); expect($chat_content.find('div.chat-info').length).toBe(2); expect($chat_content.find('div.chat-info').last().html()).toBe(__(view.newNicknameMessages["303"], "newnick")); $occupants = view.$('.occupant-list'); expect($occupants.children().length).toBe(1); expect($occupants.children().first(0).text()).toBe("newnick"); }.bind(converse)); it("informs users if they have been kicked out of the chat room", function () { /* * * * * Avaunt, you cullion! * * * * */ test_utils.openChatRoom('lounge', 'localhost', 'dummy'); var presence = $pres().attrs({ from:'lounge@localhost/dummy', to:'dummy@localhost/pda', type:'unavailable' }) .c('x').attrs({xmlns:'http://jabber.org/protocol/muc#user'}) .c('item').attrs({ affiliation: 'none', jid: 'dummy@localhost/pda', role: 'none' }) .c('actor').attrs({nick: 'Fluellen'}).up() .c('reason').t('Avaunt, you cullion!').up() .up() .c('status').attrs({code:'307'}).nodeTree; var view = this.chatboxviews.get('lounge@localhost'); view.onChatRoomPresence(presence, {nick: 'dummy', name: 'lounge@localhost'}); expect(view.$('.chat-area').is(':visible')).toBeFalsy(); expect(view.$('.occupants').is(':visible')).toBeFalsy(); var $chat_body = view.$('.chatroom-body'); expect($chat_body.html().trim().indexOf('

You have been kicked from this room

The reason given is: "Avaunt, you cullion!"

')).not.toBe(-1); }.bind(converse)); it("can be saved to, and retrieved from, browserStorage", function () { test_utils.openChatRoom('lounge', 'localhost', 'dummy'); // We instantiate a new ChatBoxes collection, which by default // will be empty. test_utils.openControlBox(); var newchatboxes = new this.ChatBoxes(); expect(newchatboxes.length).toEqual(0); // The chatboxes will then be fetched from browserStorage inside the // onConnected method newchatboxes.onConnected(); expect(newchatboxes.length).toEqual(2); // Check that the chatrooms retrieved from browserStorage // have the same attributes values as the original ones. var attrs = ['id', 'box_id', 'visible']; var new_attrs, old_attrs; for (var i=0; i