123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991 |
- (function (root, factory) {
- define([
- "jasmine",
- "mock",
- "test-utils"
- ], factory);
- } (this, function (jasmine, mock, test_utils) {
- "use strict";
- const { Promise, Strophe, $msg, $pres, sizzle } = converse.env;
- const u = converse.env.utils;
- describe("A Groupchat Message", function () {
- it("is rejected if it's an unencapsulated forwarded message",
- mock.initConverse(
- ['rosterGroupsFetched', 'chatBoxesFetched'], {},
- async function (done, _converse) {
- const muc_jid = 'lounge@montague.lit';
- await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
- const impersonated_jid = `${muc_jid}/alice`;
- const received_stanza = u.toStanza(`
- <message to='${_converse.jid}' from='${muc_jid}/mallory' type='groupchat' id='${_converse.connection.getUniqueId()}'>
- <forwarded xmlns='urn:xmpp:forward:0'>
- <delay xmlns='urn:xmpp:delay' stamp='2019-07-10T23:08:25Z'/>
- <message from='${impersonated_jid}'
- id='0202197'
- to='${_converse.bare_jid}'
- type='groupchat'
- xmlns='jabber:client'>
- <body>Yet I should kill thee with much cherishing.</body>
- </message>
- </forwarded>
- </message>
- `);
- const view = _converse.api.chatviews.get(muc_jid);
- await view.model.onMessage(received_stanza);
- spyOn(_converse, 'log');
- _converse.connection._dataRecv(test_utils.createRequest(received_stanza));
- expect(_converse.log).toHaveBeenCalledWith(
- 'onMessage: Ignoring unencapsulated forwarded groupchat message',
- Strophe.LogLevel.WARN
- );
- expect(view.el.querySelectorAll('.chat-msg').length).toBe(0);
- expect(view.model.messages.length).toBe(0);
- done();
- }));
- it("is specially marked when you are mentioned in it",
- mock.initConverse(
- ['rosterGroupsFetched', 'chatBoxesFetched'], {},
- async function (done, _converse) {
- const muc_jid = 'lounge@montague.lit';
- await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
- const view = _converse.api.chatviews.get(muc_jid);
- if (!view.el.querySelectorAll('.chat-area').length) { view.renderChatArea(); }
- const message = 'romeo: Your attention is required';
- const nick = mock.chatroom_names[0],
- msg = $msg({
- from: 'lounge@montague.lit/'+nick,
- id: (new Date()).getTime(),
- to: 'romeo@montague.lit',
- type: 'groupchat'
- }).c('body').t(message).tree();
- await view.model.onMessage(msg);
- expect(u.hasClass('mentioned', view.el.querySelector('.chat-msg'))).toBeTruthy();
- done();
- }));
- it("can not be expected to have a unique id attribute",
- mock.initConverse(
- ['rosterGroupsFetched', 'chatBoxesFetched'], {},
- async function (done, _converse) {
- const muc_jid = 'lounge@montague.lit';
- await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
- const view = _converse.api.chatviews.get(muc_jid);
- if (!view.el.querySelectorAll('.chat-area').length) { view.renderChatArea(); }
- const id = u.getUniqueId();
- let msg = $msg({
- from: 'lounge@montague.lit/some1',
- id: id,
- to: 'romeo@montague.lit',
- type: 'groupchat'
- }).c('body').t('First message').tree();
- await view.model.onMessage(msg);
- await u.waitUntil(() => view.el.querySelectorAll('.chat-msg').length === 1);
- msg = $msg({
- from: 'lounge@montague.lit/some2',
- id: id,
- to: 'romeo@montague.lit',
- type: 'groupchat'
- }).c('body').t('Another message').tree();
- await view.model.onMessage(msg);
- await u.waitUntil(() => view.el.querySelectorAll('.chat-msg').length === 2);
- expect(view.model.messages.length).toBe(2);
- done();
- }));
- it("is ignored if it has the same stanza-id of an already received on",
- mock.initConverse(
- ['rosterGroupsFetched'], {},
- async function (done, _converse) {
- const muc_jid = 'room@muc.example.com';
- await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
- const view = _converse.api.chatviews.get(muc_jid);
- spyOn(view.model, 'findDuplicateFromStanzaID').and.callThrough();
- let stanza = u.toStanza(`
- <message xmlns="jabber:client"
- from="room@muc.example.com/some1"
- to="${_converse.connection.jid}"
- type="groupchat">
- <body>Typical body text</body>
- <stanza-id xmlns="urn:xmpp:sid:0"
- id="5f3dbc5e-e1d3-4077-a492-693f3769c7ad"
- by="room@muc.example.com"/>
- </message>`);
- _converse.connection._dataRecv(test_utils.createRequest(stanza));
- await u.waitUntil(() => view.model.messages.length === 1);
- await u.waitUntil(() => view.model.findDuplicateFromStanzaID.calls.count() === 1);
- let result = await view.model.findDuplicateFromStanzaID.calls.all()[0].returnValue;
- expect(result).toBe(undefined);
- stanza = u.toStanza(`
- <message xmlns="jabber:client"
- from="room@muc.example.com/some1"
- to="${_converse.connection.jid}"
- type="groupchat">
- <body>Typical body text</body>
- <stanza-id xmlns="urn:xmpp:sid:0"
- id="5f3dbc5e-e1d3-4077-a492-693f3769c7ad"
- by="room@muc.example.com"/>
- </message>`);
- spyOn(view.model, 'updateMessage');
- _converse.connection._dataRecv(test_utils.createRequest(stanza));
- await u.waitUntil(() => view.model.findDuplicateFromStanzaID.calls.count() === 2);
- result = await view.model.findDuplicateFromStanzaID.calls.all()[1].returnValue;
- expect(result instanceof _converse.Message).toBe(true);
- expect(view.model.messages.length).toBe(1);
- await u.waitUntil(() => view.model.updateMessage.calls.count());
- done();
- }));
- it("will be discarded if it's a malicious message meant to look like a carbon copy",
- mock.initConverse(
- ['rosterGroupsFetched'], {},
- async function (done, _converse) {
- await test_utils.waitForRoster(_converse, 'current');
- await test_utils.openControlBox(_converse);
- const muc_jid = 'xsf@muc.xmpp.org';
- const sender_jid = `${muc_jid}/romeo`;
- const impersonated_jid = `${muc_jid}/i_am_groot`
- await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
- const stanza = $pres({
- to: 'romeo@montague.lit/_converse.js-29092160',
- from: sender_jid
- })
- .c('x', {xmlns: Strophe.NS.MUC_USER})
- .c('item', {
- 'affiliation': 'none',
- 'jid': 'newguy@montague.lit/_converse.js-290929789',
- 'role': 'participant'
- }).tree();
- _converse.connection._dataRecv(test_utils.createRequest(stanza));
- /*
- * <message to="romeo@montague.im/poezio" id="718d40df-3948-4798-a99b-35cc9f03cc4f-641" type="groupchat" from="xsf@muc.xmpp.org/romeo">
- * <received xmlns="urn:xmpp:carbons:2">
- * <forwarded xmlns="urn:xmpp:forward:0">
- * <message xmlns="jabber:client" to="xsf@muc.xmpp.org" type="groupchat" from="xsf@muc.xmpp.org/i_am_groot">
- * <body>I am groot.</body>
- * </message>
- * </forwarded>
- * </received>
- * </message>
- */
- const msg = $msg({
- 'from': sender_jid,
- 'id': _converse.connection.getUniqueId(),
- 'to': _converse.connection.jid,
- 'type': 'groupchat',
- 'xmlns': 'jabber:client'
- }).c('received', {'xmlns': 'urn:xmpp:carbons:2'})
- .c('forwarded', {'xmlns': 'urn:xmpp:forward:0'})
- .c('message', {
- 'xmlns': 'jabber:client',
- 'from': impersonated_jid,
- 'to': muc_jid,
- 'type': 'groupchat'
- }).c('body').t('I am groot').tree();
- const view = _converse.api.chatviews.get(muc_jid);
- spyOn(_converse, 'log');
- await view.model.onMessage(msg);
- expect(_converse.log).toHaveBeenCalledWith(
- 'onMessage: Ignoring XEP-0280 "groupchat" message carbon, '+
- 'according to the XEP groupchat messages SHOULD NOT be carbon copied',
- Strophe.LogLevel.WARN
- );
- expect(view.el.querySelectorAll('.chat-msg').length).toBe(0);
- expect(view.model.messages.length).toBe(0);
- done();
- }));
- it("keeps track of the sender's role and affiliation",
- mock.initConverse(
- ['rosterGroupsFetched'], {},
- async function (done, _converse) {
- const muc_jid = 'lounge@montague.lit';
- await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
- const view = _converse.api.chatviews.get(muc_jid);
- let msg = $msg({
- from: 'lounge@montague.lit/romeo',
- id: (new Date()).getTime(),
- to: 'romeo@montague.lit',
- type: 'groupchat'
- }).c('body').t('I wrote this message!').tree();
- await view.model.onMessage(msg);
- expect(view.model.messages.last().occupant.get('affiliation')).toBe('owner');
- expect(view.model.messages.last().occupant.get('role')).toBe('moderator');
- expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
- expect(sizzle('.chat-msg', view.el).pop().classList.value.trim()).toBe('message chat-msg groupchat moderator owner');
- let presence = $pres({
- to:'romeo@montague.lit/orchard',
- from:'lounge@montague.lit/romeo',
- id: u.getUniqueId()
- }).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(test_utils.createRequest(presence));
- msg = $msg({
- from: 'lounge@montague.lit/romeo',
- id: (new Date()).getTime(),
- to: 'romeo@montague.lit',
- type: 'groupchat'
- }).c('body').t('Another message!').tree();
- await view.model.onMessage(msg);
- expect(view.model.messages.last().occupant.get('affiliation')).toBe('member');
- expect(view.model.messages.last().occupant.get('role')).toBe('participant');
- expect(view.el.querySelectorAll('.chat-msg').length).toBe(2);
- expect(sizzle('.chat-msg', view.el).pop().classList.value.trim()).toBe('message chat-msg groupchat participant member');
- presence = $pres({
- to:'romeo@montague.lit/orchard',
- from:'lounge@montague.lit/romeo',
- id: u.getUniqueId()
- }).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:'210'}).nodeTree;
- _converse.connection._dataRecv(test_utils.createRequest(presence));
- view.model.sendMessage('hello world');
- await u.waitUntil(() => view.el.querySelectorAll('.chat-msg').length === 3);
- expect(view.model.messages.last().occupant.get('affiliation')).toBe('owner');
- expect(view.model.messages.last().occupant.get('role')).toBe('moderator');
- expect(view.el.querySelectorAll('.chat-msg').length).toBe(3);
- expect(sizzle('.chat-msg', view.el).pop().classList.value.trim()).toBe('message chat-msg groupchat moderator owner');
- const add_events = view.model.occupants._events.add.length;
- msg = $msg({
- from: 'lounge@montague.lit/some1',
- id: (new Date()).getTime(),
- to: 'romeo@montague.lit',
- type: 'groupchat'
- }).c('body').t('Message from someone not in the MUC right now').tree();
- await view.model.onMessage(msg);
- expect(view.model.messages.last().occupant).toBeUndefined();
- // Check that there's a new "add" event handler, for when the occupant appears.
- expect(view.model.occupants._events.add.length).toBe(add_events+1);
- // Check that the occupant gets added/removed to the message as it
- // gets removed or added.
- presence = $pres({
- to:'romeo@montague.lit/orchard',
- from:'lounge@montague.lit/some1',
- id: u.getUniqueId()
- }).c('x').attrs({xmlns:'http://jabber.org/protocol/muc#user'})
- .c('item').attrs({jid: 'some1@montague.lit/orchard'});
- _converse.connection._dataRecv(test_utils.createRequest(presence));
- await u.waitUntil(() => view.model.messages.last().occupant);
- expect(view.model.messages.last().get('message')).toBe('Message from someone not in the MUC right now');
- expect(view.model.messages.last().occupant.get('nick')).toBe('some1');
- // Check that the "add" event handler was removed.
- expect(view.model.occupants._events.add.length).toBe(add_events);
- presence = $pres({
- to:'romeo@montague.lit/orchard',
- type: 'unavailable',
- from:'lounge@montague.lit/some1',
- id: u.getUniqueId()
- }).c('x').attrs({xmlns:'http://jabber.org/protocol/muc#user'})
- .c('item').attrs({jid: 'some1@montague.lit/orchard'});
- _converse.connection._dataRecv(test_utils.createRequest(presence));
- await u.waitUntil(() => !view.model.messages.last().occupant);
- expect(view.model.messages.last().get('message')).toBe('Message from someone not in the MUC right now');
- expect(view.model.messages.last().occupant).toBeUndefined();
- // Check that there's a new "add" event handler, for when the occupant appears.
- expect(view.model.occupants._events.add.length).toBe(add_events+1);
- presence = $pres({
- to:'romeo@montague.lit/orchard',
- from:'lounge@montague.lit/some1',
- id: u.getUniqueId()
- }).c('x').attrs({xmlns:'http://jabber.org/protocol/muc#user'})
- .c('item').attrs({jid: 'some1@montague.lit/orchard'});
- _converse.connection._dataRecv(test_utils.createRequest(presence));
- await u.waitUntil(() => view.model.messages.last().occupant);
- expect(view.model.messages.last().get('message')).toBe('Message from someone not in the MUC right now');
- expect(view.model.messages.last().occupant.get('nick')).toBe('some1');
- // Check that the "add" event handler was removed.
- expect(view.model.occupants._events.add.length).toBe(add_events);
- done();
- }));
- it("keeps track whether you are the sender or not",
- mock.initConverse(
- ['rosterGroupsFetched'], {},
- async function (done, _converse) {
- const muc_jid = 'lounge@montague.lit';
- await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
- const view = _converse.api.chatviews.get(muc_jid);
- const msg = $msg({
- from: 'lounge@montague.lit/romeo',
- id: (new Date()).getTime(),
- to: 'romeo@montague.lit',
- type: 'groupchat'
- }).c('body').t('I wrote this message!').tree();
- await view.model.onMessage(msg);
- expect(view.model.messages.last().get('sender')).toBe('me');
- done();
- }));
- it("can be replaced with a correction",
- mock.initConverse(
- ['rosterGroupsFetched'], {},
- async function (done, _converse) {
- const muc_jid = 'lounge@montague.lit';
- await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
- const view = _converse.api.chatviews.get(muc_jid);
- const stanza = $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'
- }).tree();
- _converse.connection._dataRecv(test_utils.createRequest(stanza));
- const msg_id = u.getUniqueId();
- await view.model.onMessage($msg({
- 'from': 'lounge@montague.lit/newguy',
- 'to': _converse.connection.jid,
- 'type': 'groupchat',
- 'id': msg_id,
- }).c('body').t('But soft, what light through yonder airlock breaks?').tree());
- expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
- expect(view.el.querySelector('.chat-msg__text').textContent)
- .toBe('But soft, what light through yonder airlock breaks?');
- await view.model.onMessage($msg({
- 'from': 'lounge@montague.lit/newguy',
- 'to': _converse.connection.jid,
- 'type': 'groupchat',
- 'id': u.getUniqueId(),
- }).c('body').t('But soft, what light through yonder chimney breaks?').up()
- .c('replace', {'id': msg_id, 'xmlns': 'urn:xmpp:message-correct:0'}).tree());
- await u.waitUntil(() => view.el.querySelector('.chat-msg__text').textContent ===
- 'But soft, what light through yonder chimney breaks?', 500);
- expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
- expect(view.el.querySelectorAll('.chat-msg__content .fa-edit').length).toBe(1);
- await view.model.onMessage($msg({
- 'from': 'lounge@montague.lit/newguy',
- 'to': _converse.connection.jid,
- 'type': 'groupchat',
- 'id': u.getUniqueId(),
- }).c('body').t('But soft, what light through yonder window breaks?').up()
- .c('replace', {'id': msg_id, 'xmlns': 'urn:xmpp:message-correct:0'}).tree());
- await u.waitUntil(() => view.el.querySelector('.chat-msg__text').textContent ===
- 'But soft, what light through yonder window breaks?', 500);
- expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
- expect(view.el.querySelectorAll('.chat-msg__content .fa-edit').length).toBe(1);
- view.el.querySelector('.chat-msg__content .fa-edit').click();
- const modal = view.model.messages.at(0).message_versions_modal;
- await u.waitUntil(() => u.isVisible(modal.el), 1000);
- const older_msgs = modal.el.querySelectorAll('.older-msg');
- expect(older_msgs.length).toBe(2);
- expect(older_msgs[0].childNodes[1].textContent).toBe(': But soft, what light through yonder airlock breaks?');
- expect(older_msgs[0].childNodes[0].nodeName).toBe('TIME');
- expect(older_msgs[1].childNodes[0].nodeName).toBe('TIME');
- expect(older_msgs[1].childNodes[1].textContent).toBe(': But soft, what light through yonder chimney breaks?');
- done();
- }));
- it("can be sent as a correction by using the up arrow",
- mock.initConverse(
- ['rosterGroupsFetched'], {},
- async function (done, _converse) {
- const muc_jid = 'lounge@montague.lit';
- await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
- const view = _converse.api.chatviews.get(muc_jid);
- const textarea = view.el.querySelector('textarea.chat-textarea');
- expect(textarea.value).toBe('');
- view.onKeyDown({
- target: textarea,
- keyCode: 38 // Up arrow
- });
- expect(textarea.value).toBe('');
- textarea.value = 'But soft, what light through yonder airlock breaks?';
- view.onKeyDown({
- target: textarea,
- preventDefault: function preventDefault () {},
- keyCode: 13 // Enter
- });
- await u.waitUntil(() => view.el.querySelectorAll('.chat-msg').length === 1);
- expect(view.el.querySelector('.chat-msg__text').textContent)
- .toBe('But soft, what light through yonder airlock breaks?');
- const first_msg = view.model.messages.findWhere({'message': 'But soft, what light through yonder airlock breaks?'});
- expect(textarea.value).toBe('');
- view.onKeyDown({
- target: textarea,
- keyCode: 38 // Up arrow
- });
- await new Promise(resolve => view.model.messages.once('rendered', resolve));
- expect(textarea.value).toBe('But soft, what light through yonder airlock breaks?');
- expect(view.model.messages.at(0).get('correcting')).toBe(true);
- expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
- expect(u.hasClass('correcting', view.el.querySelector('.chat-msg'))).toBe(true);
- spyOn(_converse.connection, 'send');
- textarea.value = 'But soft, what light through yonder window breaks?';
- view.onKeyDown({
- target: textarea,
- preventDefault: function preventDefault () {},
- keyCode: 13 // Enter
- });
- expect(_converse.connection.send).toHaveBeenCalled();
- await new Promise(resolve => view.model.messages.once('rendered', resolve));
- const msg = _converse.connection.send.calls.all()[0].args[0];
- expect(msg.toLocaleString())
- .toBe(`<message from="romeo@montague.lit/orchard" id="${msg.nodeTree.getAttribute("id")}" `+
- `to="lounge@montague.lit" type="groupchat" `+
- `xmlns="jabber:client">`+
- `<body>But soft, what light through yonder window breaks?</body>`+
- `<active xmlns="http://jabber.org/protocol/chatstates"/>`+
- `<replace id="${first_msg.get("msgid")}" xmlns="urn:xmpp:message-correct:0"/>`+
- `<origin-id id="${msg.nodeTree.querySelector('origin-id').getAttribute("id")}" xmlns="urn:xmpp:sid:0"/>`+
- `</message>`);
- expect(view.model.messages.models.length).toBe(1);
- const corrected_message = view.model.messages.at(0);
- expect(corrected_message.get('msgid')).toBe(first_msg.get('msgid'));
- expect(corrected_message.get('correcting')).toBe(false);
- const older_versions = corrected_message.get('older_versions');
- const keys = Object.keys(older_versions);
- expect(keys.length).toBe(1);
- expect(older_versions[keys[0]]).toBe('But soft, what light through yonder airlock breaks?');
- expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
- expect(u.hasClass('correcting', view.el.querySelector('.chat-msg'))).toBe(false);
- // Check that messages from other users are skipped
- await view.model.onMessage($msg({
- 'from': muc_jid+'/someone-else',
- 'id': (new Date()).getTime(),
- 'to': 'romeo@montague.lit',
- 'type': 'groupchat'
- }).c('body').t('Hello world').tree());
- expect(view.el.querySelectorAll('.chat-msg').length).toBe(2);
- // Test that pressing the down arrow cancels message correction
- expect(textarea.value).toBe('');
- view.onKeyDown({
- target: textarea,
- keyCode: 38 // Up arrow
- });
- expect(textarea.value).toBe('But soft, what light through yonder window breaks?');
- expect(view.model.messages.at(0).get('correcting')).toBe(true);
- expect(view.el.querySelectorAll('.chat-msg').length).toBe(2);
- await u.waitUntil(() => u.hasClass('correcting', view.el.querySelector('.chat-msg')), 500);
- expect(textarea.value).toBe('But soft, what light through yonder window breaks?');
- view.onKeyDown({
- target: textarea,
- keyCode: 40 // Down arrow
- });
- expect(textarea.value).toBe('');
- expect(view.model.messages.at(0).get('correcting')).toBe(false);
- expect(view.el.querySelectorAll('.chat-msg').length).toBe(2);
- await u.waitUntil(() => !u.hasClass('correcting', view.el.querySelector('.chat-msg')), 500);
- done();
- }));
- it("will be shown as received upon MUC reflection",
- mock.initConverse(
- ['rosterGroupsFetched'], {},
- async function (done, _converse) {
- await test_utils.waitForRoster(_converse, 'current');
- const muc_jid = 'lounge@montague.lit';
- await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
- const view = _converse.api.chatviews.get(muc_jid);
- const textarea = view.el.querySelector('textarea.chat-textarea');
- textarea.value = 'But soft, what light through yonder airlock breaks?';
- view.onKeyDown({
- target: textarea,
- preventDefault: function preventDefault () {},
- keyCode: 13 // Enter
- });
- await new Promise(resolve => view.once('messageInserted', resolve));
- expect(view.el.querySelectorAll('.chat-msg__body.chat-msg__body--received').length).toBe(0);
- const msg_obj = view.model.messages.at(0);
- const stanza = u.toStanza(`
- <message xmlns="jabber:client"
- from="${msg_obj.get('from')}"
- to="${_converse.connection.jid}"
- type="groupchat">
- <msg_body>${msg_obj.get('message')}</msg_body>
- <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="${msg_obj.get('origin_id')}"/>
- </message>`);
- await view.model.onMessage(stanza);
- await u.waitUntil(() => view.el.querySelectorAll('.chat-msg__body.chat-msg__body--received').length, 500);
- expect(view.el.querySelectorAll('.chat-msg__receipt').length).toBe(0);
- expect(view.el.querySelectorAll('.chat-msg__body.chat-msg__body--received').length).toBe(1);
- expect(view.model.messages.length).toBe(1);
- const message = view.model.messages.at(0);
- expect(message.get('stanza_id lounge@montague.lit')).toBe('5f3dbc5e-e1d3-4077-a492-693f3769c7ad');
- expect(message.get('origin_id')).toBe(msg_obj.get('origin_id'));
- done();
- }));
- it("gets updated with its stanza-id upon MUC reflection",
- mock.initConverse(
- ['rosterGroupsFetched'], {},
- async function (done, _converse) {
- const muc_jid = 'room@muc.example.com';
- await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
- const view = _converse.api.chatviews.get(muc_jid);
- view.model.sendMessage('hello world');
- await u.waitUntil(() => view.model.messages.length === 1);
- const msg = view.model.messages.at(0);
- expect(msg.get('stanza_id')).toBeUndefined();
- expect(msg.get('origin_id')).toBe(msg.get('origin_id'));
- const stanza = u.toStanza(`
- <message xmlns="jabber:client"
- from="room@muc.example.com/romeo"
- to="${_converse.connection.jid}"
- type="groupchat">
- <body>Hello world</body>
- <stanza-id xmlns="urn:xmpp:sid:0"
- id="5f3dbc5e-e1d3-4077-a492-693f3769c7ad"
- by="room@muc.example.com"/>
- <origin-id xmlns="urn:xmpp:sid:0" id="${msg.get('origin_id')}"/>
- </message>`);
- spyOn(view.model, 'updateMessage').and.callThrough();
- _converse.connection._dataRecv(test_utils.createRequest(stanza));
- await u.waitUntil(() => view.model.updateMessage.calls.count() === 1);
- expect(view.model.messages.length).toBe(1);
- expect(view.model.messages.at(0).get('stanza_id room@muc.example.com')).toBe("5f3dbc5e-e1d3-4077-a492-693f3769c7ad");
- expect(view.model.messages.at(0).get('origin_id')).toBe(msg.get('origin_id'));
- done();
- }));
- it("can cause a delivery receipt to be returned",
- mock.initConverse(
- ['rosterGroupsFetched'], {},
- async function (done, _converse) {
- await test_utils.waitForRoster(_converse, 'current');
- const muc_jid = 'lounge@montague.lit';
- await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
- const view = _converse.api.chatviews.get(muc_jid);
- const textarea = view.el.querySelector('textarea.chat-textarea');
- textarea.value = 'But soft, what light through yonder airlock breaks?';
- view.onKeyDown({
- target: textarea,
- preventDefault: function preventDefault () {},
- keyCode: 13 // Enter
- });
- await new Promise(resolve => view.once('messageInserted', resolve));
- expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
- const msg_obj = view.model.messages.at(0);
- const stanza = u.toStanza(`
- <message xml:lang="en" to="romeo@montague.lit/orchard"
- from="lounge@montague.lit/some1" type="groupchat" xmlns="jabber:client">
- <received xmlns="urn:xmpp:receipts" id="${msg_obj.get('msgid')}"/>
- <origin-id xmlns="urn:xmpp:sid:0" id="CE08D448-5ED8-4B6A-BB5B-07ED9DFE4FF0"/>
- </message>`);
- spyOn(_converse.api, "trigger").and.callThrough();
- spyOn(view.model, "isReceipt").and.callThrough();
- _converse.connection._dataRecv(test_utils.createRequest(stanza));
- await u.waitUntil(() => view.model.isReceipt.calls.count() === 1);
- expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
- expect(view.el.querySelectorAll('.chat-msg__receipt').length).toBe(0);
- expect(_converse.api.trigger).toHaveBeenCalledWith('message', jasmine.any(Object));
- done();
- }));
- it("can cause a chat marker to be returned",
- mock.initConverse(
- ['rosterGroupsFetched'], {},
- async function (done, _converse) {
- await test_utils.waitForRoster(_converse, 'current');
- const muc_jid = 'lounge@montague.lit';
- await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
- const view = _converse.api.chatviews.get(muc_jid);
- const textarea = view.el.querySelector('textarea.chat-textarea');
- textarea.value = 'But soft, what light through yonder airlock breaks?';
- view.onKeyDown({
- target: textarea,
- preventDefault: function preventDefault () {},
- keyCode: 13 // Enter
- });
- await new Promise(resolve => view.once('messageInserted', resolve));
- expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
- expect(view.el.querySelector('.chat-msg .chat-msg__body').textContent.trim())
- .toBe("But soft, what light through yonder airlock breaks?");
- const msg_obj = view.model.messages.at(0);
- let stanza = u.toStanza(`
- <message xml:lang="en" to="romeo@montague.lit/orchard"
- from="lounge@montague.lit/some1" type="groupchat" xmlns="jabber:client">
- <received xmlns="urn:xmpp:chat-markers:0" id="${msg_obj.get('msgid')}"/>
- </message>`);
- spyOn(view.model, "isChatMarker").and.callThrough();
- _converse.connection._dataRecv(test_utils.createRequest(stanza));
- await u.waitUntil(() => view.model.isChatMarker.calls.count() === 1);
- expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
- expect(view.el.querySelectorAll('.chat-msg__receipt').length).toBe(0);
- stanza = u.toStanza(`
- <message xml:lang="en" to="romeo@montague.lit/orchard"
- from="lounge@montague.lit/some1" type="groupchat" xmlns="jabber:client">
- <displayed xmlns="urn:xmpp:chat-markers:0" id="${msg_obj.get('msgid')}"/>
- </message>`);
- _converse.connection._dataRecv(test_utils.createRequest(stanza));
- await u.waitUntil(() => view.model.isChatMarker.calls.count() === 2);
- expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
- expect(view.el.querySelectorAll('.chat-msg__receipt').length).toBe(0);
- stanza = u.toStanza(`
- <message xml:lang="en" to="romeo@montague.lit/orchard"
- from="lounge@montague.lit/some1" type="groupchat" xmlns="jabber:client">
- <acknowledged xmlns="urn:xmpp:chat-markers:0" id="${msg_obj.get('msgid')}"/>
- </message>`);
- _converse.connection._dataRecv(test_utils.createRequest(stanza));
- await u.waitUntil(() => view.model.isChatMarker.calls.count() === 3);
- expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
- expect(view.el.querySelectorAll('.chat-msg__receipt').length).toBe(0);
- stanza = u.toStanza(`
- <message xml:lang="en" to="romeo@montague.lit/orchard"
- from="lounge@montague.lit/some1" type="groupchat" xmlns="jabber:client">
- <body>'tis I!</body>
- <markable xmlns="urn:xmpp:chat-markers:0"/>
- </message>`);
- _converse.connection._dataRecv(test_utils.createRequest(stanza));
- await u.waitUntil(() => view.model.isChatMarker.calls.count() === 4);
- expect(view.el.querySelectorAll('.chat-msg').length).toBe(2);
- expect(view.el.querySelectorAll('.chat-msg__receipt').length).toBe(0);
- done();
- }));
- describe("when received", function () {
- it("highlights all users mentioned via XEP-0372 references",
- mock.initConverse(
- ['rosterGroupsFetched'], {},
- async function (done, _converse) {
- const muc_jid = 'lounge@montague.lit';
- await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'tom');
- const view = _converse.api.chatviews.get(muc_jid);
- ['z3r0', 'mr.robot', 'gibson', 'sw0rdf1sh'].forEach((nick) => {
- _converse.connection._dataRecv(test_utils.createRequest(
- $pres({
- 'to': 'tom@montague.lit/resource',
- 'from': `lounge@montague.lit/${nick}`
- })
- .c('x', {xmlns: Strophe.NS.MUC_USER})
- .c('item', {
- 'affiliation': 'none',
- 'jid': `${nick}@montague.lit/resource`,
- 'role': 'participant'
- }))
- );
- });
- const msg = $msg({
- from: 'lounge@montague.lit/gibson',
- id: (new Date()).getTime(),
- to: 'romeo@montague.lit',
- type: 'groupchat'
- }).c('body').t('hello z3r0 tom mr.robot, how are you?').up()
- .c('reference', {'xmlns':'urn:xmpp:reference:0', 'begin':'6', 'end':'10', 'type':'mention', 'uri':'xmpp:z3r0@montague.lit'}).up()
- .c('reference', {'xmlns':'urn:xmpp:reference:0', 'begin':'11', 'end':'14', 'type':'mention', 'uri':'xmpp:romeo@montague.lit'}).up()
- .c('reference', {'xmlns':'urn:xmpp:reference:0', 'begin':'15', 'end':'23', 'type':'mention', 'uri':'xmpp:mr.robot@montague.lit'}).nodeTree;
- await view.model.onMessage(msg);
- const messages = view.el.querySelectorAll('.chat-msg__text');
- expect(messages.length).toBe(1);
- expect(messages[0].classList.length).toEqual(1);
- expect(messages[0].innerHTML).toBe(
- 'hello <span class="mention">z3r0</span> '+
- '<span class="mention mention--self badge badge-info">tom</span> '+
- '<span class="mention">mr.robot</span>, how are you?');
- done();
- }));
- });
- describe("in which someone is mentioned", function () {
- it("gets parsed for mentions which get turned into references",
- mock.initConverse(
- ['rosterGroupsFetched'], {},
- async function (done, _converse) {
- const muc_jid = 'lounge@montague.lit';
- await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'tom');
- const view = _converse.api.chatviews.get(muc_jid);
- ['z3r0', 'mr.robot', 'gibson', 'sw0rdf1sh', 'Link Mauve'].forEach((nick) => {
- _converse.connection._dataRecv(test_utils.createRequest(
- $pres({
- 'to': 'tom@montague.lit/resource',
- 'from': `lounge@montague.lit/${nick}`
- })
- .c('x', {xmlns: Strophe.NS.MUC_USER})
- .c('item', {
- 'affiliation': 'none',
- 'jid': `${nick.replace(/\s/g, '-')}@montague.lit/resource`,
- 'role': 'participant'
- })));
- });
- // Run a few unit tests for the parseTextForReferences method
- let [text, references] = view.model.parseTextForReferences('hello z3r0')
- expect(references.length).toBe(0);
- expect(text).toBe('hello z3r0');
- [text, references] = view.model.parseTextForReferences('hello @z3r0')
- expect(references.length).toBe(1);
- expect(text).toBe('hello z3r0');
- expect(JSON.stringify(references))
- .toBe('[{"begin":6,"end":10,"value":"z3r0","type":"mention","uri":"xmpp:z3r0@montague.lit"}]');
- [text, references] = view.model.parseTextForReferences('hello @some1 @z3r0 @gibson @mr.robot, how are you?')
- expect(text).toBe('hello @some1 z3r0 gibson mr.robot, how are you?');
- expect(JSON.stringify(references))
- .toBe('[{"begin":13,"end":17,"value":"z3r0","type":"mention","uri":"xmpp:z3r0@montague.lit"},'+
- '{"begin":18,"end":24,"value":"gibson","type":"mention","uri":"xmpp:gibson@montague.lit"},'+
- '{"begin":25,"end":33,"value":"mr.robot","type":"mention","uri":"xmpp:mr.robot@montague.lit"}]');
- [text, references] = view.model.parseTextForReferences('yo @gib')
- expect(text).toBe('yo @gib');
- expect(references.length).toBe(0);
- [text, references] = view.model.parseTextForReferences('yo @gibsonian')
- expect(text).toBe('yo @gibsonian');
- expect(references.length).toBe(0);
- [text, references] = view.model.parseTextForReferences('@gibson')
- expect(text).toBe('gibson');
- expect(references.length).toBe(1);
- expect(JSON.stringify(references))
- .toBe('[{"begin":0,"end":6,"value":"gibson","type":"mention","uri":"xmpp:gibson@montague.lit"}]');
- [text, references] = view.model.parseTextForReferences('hi @Link Mauve how are you?')
- expect(text).toBe('hi Link Mauve how are you?');
- expect(references.length).toBe(1);
- expect(JSON.stringify(references))
- .toBe('[{"begin":3,"end":13,"value":"Link Mauve","type":"mention","uri":"xmpp:Link-Mauve@montague.lit"}]');
- [text, references] = view.model.parseTextForReferences('https://example.org/@gibson')
- expect(text).toBe('https://example.org/@gibson');
- expect(references.length).toBe(0);
- expect(JSON.stringify(references))
- .toBe('[]');
- [text, references] = view.model.parseTextForReferences('mail@gibson.com')
- expect(text).toBe('mail@gibson.com');
- expect(references.length).toBe(0);
- expect(JSON.stringify(references))
- .toBe('[]');
- [text, references] = view.model.parseTextForReferences(
- 'https://linkmauve.fr@Link Mauve/ https://linkmauve.fr/@github/is_back gibson@gibson.com gibson@Link Mauve.fr')
- expect(text).toBe(
- 'https://linkmauve.fr@Link Mauve/ https://linkmauve.fr/@github/is_back gibson@gibson.com gibson@Link Mauve.fr');
- expect(references.length).toBe(0);
- expect(JSON.stringify(references))
- .toBe('[]');
- done();
- }));
- it("parses for mentions as indicated with an @ preceded by a space or at the start of the text",
- mock.initConverse(
- ['rosterGroupsFetched'], {},
- async function (done, _converse) {
- const muc_jid = 'lounge@montague.lit';
- await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'tom');
- const view = _converse.api.chatviews.get(muc_jid);
- ['NotAnAdress', 'darnuria'].forEach((nick) => {
- _converse.connection._dataRecv(test_utils.createRequest(
- $pres({
- 'to': 'tom@montague.lit/resource',
- 'from': `lounge@montague.lit/${nick}`
- })
- .c('x', {xmlns: Strophe.NS.MUC_USER})
- .c('item', {
- 'affiliation': 'none',
- 'jid': `${nick.replace(/\s/g, '-')}@montague.lit/resource`,
- 'role': 'participant'
- })));
- });
- // Test that we don't match @nick in email adresses.
- let [text, references] = view.model.parseTextForReferences('contact contact@NotAnAdress.eu');
- expect(references.length).toBe(0);
- expect(text).toBe('contact contact@NotAnAdress.eu');
- // Test that we don't match @nick in url
- [text, references] = view.model.parseTextForReferences('nice website https://darnuria.eu/@darnuria');
- expect(references.length).toBe(0);
- expect(text).toBe('nice website https://darnuria.eu/@darnuria');
- done();
- }));
- it("can get corrected and given new references",
- mock.initConverse(
- ['rosterGroupsFetched'], {},
- async function (done, _converse) {
- const muc_jid = 'lounge@montague.lit';
- await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'tom');
- const view = _converse.api.chatviews.get(muc_jid);
- ['z3r0', 'mr.robot', 'gibson', 'sw0rdf1sh'].forEach((nick) => {
- _converse.connection._dataRecv(test_utils.createRequest(
- $pres({
- 'to': 'tom@montague.lit/resource',
- 'from': `lounge@montague.lit/${nick}`
- })
- .c('x', {xmlns: Strophe.NS.MUC_USER})
- .c('item', {
- 'affiliation': 'none',
- 'jid': `${nick}@montague.lit/resource`,
- 'role': 'participant'
- })));
- });
- const textarea = view.el.querySelector('textarea.chat-textarea');
- textarea.value = 'hello @z3r0 @gibson @mr.robot, how are you?'
- const enter_event = {
- 'target': textarea,
- 'preventDefault': function preventDefault () {},
- 'stopPropagation': function stopPropagation () {},
- 'keyCode': 13 // Enter
- }
- spyOn(_converse.connection, 'send');
- view.onKeyDown(enter_event);
- await new Promise(resolve => view.once('messageInserted', resolve));
- const msg = _converse.connection.send.calls.all()[0].args[0];
- expect(msg.toLocaleString())
- .toBe(`<message from="romeo@montague.lit/orchard" id="${msg.nodeTree.getAttribute("id")}" `+
- `to="lounge@montague.lit" type="groupchat" `+
- `xmlns="jabber:client">`+
- `<body>hello z3r0 gibson mr.robot, how are you?</body>`+
- `<active xmlns="http://jabber.org/protocol/chatstates"/>`+
- `<reference begin="6" end="10" type="mention" uri="xmpp:z3r0@montague.lit" xmlns="urn:xmpp:reference:0"/>`+
- `<reference begin="11" end="17" type="mention" uri="xmpp:gibson@montague.lit" xmlns="urn:xmpp:reference:0"/>`+
- `<reference begin="18" end="26" type="mention" uri="xmpp:mr.robot@montague.lit" xmlns="urn:xmpp:reference:0"/>`+
- `<origin-id id="${msg.nodeTree.querySelector('origin-id').getAttribute("id")}" xmlns="urn:xmpp:sid:0"/>`+
- `</message>`);
- const action = view.el.querySelector('.chat-msg .chat-msg__action');
- action.style.opacity = 1;
- action.click();
- expect(textarea.value).toBe('hello @z3r0 @gibson @mr.robot, how are you?');
- expect(view.model.messages.at(0).get('correcting')).toBe(true);
- expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
- await u.waitUntil(() => u.hasClass('correcting', view.el.querySelector('.chat-msg')), 500);
- textarea.value = 'hello @z3r0 @gibson @sw0rdf1sh, how are you?';
- view.onKeyDown(enter_event);
- await u.waitUntil(() => view.el.querySelector('.chat-msg__text').textContent ===
- 'hello z3r0 gibson sw0rdf1sh, how are you?', 500);
- const correction = _converse.connection.send.calls.all()[2].args[0];
- expect(correction.toLocaleString())
- .toBe(`<message from="romeo@montague.lit/orchard" id="${correction.nodeTree.getAttribute("id")}" `+
- `to="lounge@montague.lit" type="groupchat" `+
- `xmlns="jabber:client">`+
- `<body>hello z3r0 gibson sw0rdf1sh, how are you?</body>`+
- `<active xmlns="http://jabber.org/protocol/chatstates"/>`+
- `<reference begin="6" end="10" type="mention" uri="xmpp:z3r0@montague.lit" xmlns="urn:xmpp:reference:0"/>`+
- `<reference begin="11" end="17" type="mention" uri="xmpp:gibson@montague.lit" xmlns="urn:xmpp:reference:0"/>`+
- `<reference begin="18" end="27" type="mention" uri="xmpp:sw0rdf1sh@montague.lit" xmlns="urn:xmpp:reference:0"/>`+
- `<replace id="${msg.nodeTree.getAttribute("id")}" xmlns="urn:xmpp:message-correct:0"/>`+
- `<origin-id id="${correction.nodeTree.querySelector('origin-id').getAttribute("id")}" xmlns="urn:xmpp:sid:0"/>`+
- `</message>`);
- done();
- }));
- it("includes XEP-0372 references to that person",
- mock.initConverse(
- ['rosterGroupsFetched'], {},
- async function (done, _converse) {
- const muc_jid = 'lounge@montague.lit';
- await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
- const view = _converse.api.chatviews.get(muc_jid);
- ['z3r0', 'mr.robot', 'gibson', 'sw0rdf1sh'].forEach((nick) => {
- _converse.connection._dataRecv(test_utils.createRequest(
- $pres({
- 'to': 'tom@montague.lit/resource',
- 'from': `lounge@montague.lit/${nick}`
- })
- .c('x', {xmlns: Strophe.NS.MUC_USER})
- .c('item', {
- 'affiliation': 'none',
- 'jid': `${nick}@montague.lit/resource`,
- 'role': 'participant'
- })));
- });
- spyOn(_converse.connection, 'send');
- const textarea = view.el.querySelector('textarea.chat-textarea');
- textarea.value = 'hello @z3r0 @gibson @mr.robot, how are you?'
- const enter_event = {
- 'target': textarea,
- 'preventDefault': function preventDefault () {},
- 'stopPropagation': function stopPropagation () {},
- 'keyCode': 13 // Enter
- }
- view.onKeyDown(enter_event);
- const msg = _converse.connection.send.calls.all()[0].args[0];
- expect(msg.toLocaleString())
- .toBe(`<message from="romeo@montague.lit/orchard" id="${msg.nodeTree.getAttribute("id")}" `+
- `to="lounge@montague.lit" type="groupchat" `+
- `xmlns="jabber:client">`+
- `<body>hello z3r0 gibson mr.robot, how are you?</body>`+
- `<active xmlns="http://jabber.org/protocol/chatstates"/>`+
- `<reference begin="6" end="10" type="mention" uri="xmpp:z3r0@montague.lit" xmlns="urn:xmpp:reference:0"/>`+
- `<reference begin="11" end="17" type="mention" uri="xmpp:gibson@montague.lit" xmlns="urn:xmpp:reference:0"/>`+
- `<reference begin="18" end="26" type="mention" uri="xmpp:mr.robot@montague.lit" xmlns="urn:xmpp:reference:0"/>`+
- `<origin-id id="${msg.nodeTree.querySelector('origin-id').getAttribute("id")}" xmlns="urn:xmpp:sid:0"/>`+
- `</message>`);
- done();
- }));
- });
- });
- }));
|