diff --git a/karma.conf.js b/karma.conf.js
index f8659fa4c..40701141f 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -71,6 +71,7 @@ module.exports = function(config) {
{ pattern: "src/plugins/muc-views/tests/hats.js", type: 'module' },
{ pattern: "src/plugins/muc-views/tests/http-file-upload.js", type: 'module' },
{ pattern: "src/plugins/muc-views/tests/info-messages.js", type: 'module' },
+ { pattern: "src/plugins/muc-views/tests/mam.js", type: 'module' },
{ pattern: "src/plugins/muc-views/tests/markers.js", type: 'module' },
{ pattern: "src/plugins/muc-views/tests/me-messages.js", type: 'module' },
{ pattern: "src/plugins/muc-views/tests/mentions.js", type: 'module' },
diff --git a/package-lock.json b/package-lock.json
index 27a9a25f5..2e66e213c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -23255,6 +23255,7 @@
"version": "7.4.5",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz",
"integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g==",
+ "devOptional": true,
"engines": {
"node": ">=8.3.0"
},
@@ -41306,6 +41307,7 @@
"version": "7.4.5",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz",
"integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g==",
+ "devOptional": true,
"requires": {}
},
"xmlcreate": {
diff --git a/src/plugins/muc-views/tests/mam.js b/src/plugins/muc-views/tests/mam.js
new file mode 100644
index 000000000..d4961f83a
--- /dev/null
+++ b/src/plugins/muc-views/tests/mam.js
@@ -0,0 +1,109 @@
+/*global mock, converse */
+
+const { Strophe, $msg, $pres } = converse.env;
+const u = converse.env.utils;
+
+describe("A MAM archived groupchat message", function () {
+
+ it("is ignored if it has the same archive-id of an already received one",
+ mock.initConverse([], {}, async function (_converse) {
+
+ const muc_jid = 'room@muc.example.com';
+ await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
+ const view = _converse.chatboxviews.get(muc_jid);
+ spyOn(view.model, 'getDuplicateMessage').and.callThrough();
+ let stanza = u.toStanza(`
+
+ Typical body text
+
+ `);
+ _converse.connection._dataRecv(mock.createRequest(stanza));
+ await u.waitUntil(() => view.model.messages.length === 1);
+ await u.waitUntil(() => view.model.getDuplicateMessage.calls.count() === 1);
+ let result = await view.model.getDuplicateMessage.calls.all()[0].returnValue;
+ expect(result).toBe(undefined);
+
+ stanza = u.toStanza(`
+
+
+
+
+
+ Typical body text
+
+
+
+ `);
+
+ spyOn(view.model, 'updateMessage');
+ _converse.handleMAMResult(view.model, { 'messages': [stanza] });
+ await u.waitUntil(() => view.model.getDuplicateMessage.calls.count() === 2);
+ result = await view.model.getDuplicateMessage.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());
+ }));
+
+ it("will be discarded if it's a malicious message meant to look like a carbon copy",
+ mock.initConverse([], {}, async function (_converse) {
+
+ await mock.waitForRoster(_converse, 'current');
+ await mock.openControlBox(_converse);
+ const muc_jid = 'xsf@muc.xmpp.org';
+ const sender_jid = `${muc_jid}/romeo`;
+ const impersonated_jid = `${muc_jid}/i_am_groot`
+ await mock.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': 'owner',
+ 'jid': 'newguy@montague.lit/_converse.js-290929789',
+ 'role': 'participant'
+ }).tree();
+ _converse.connection._dataRecv(mock.createRequest(stanza));
+ /*
+ *
+ *
+ *
+ *
+ * I am groot.
+ *
+ *
+ *
+ *
+ */
+ 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.chatboxviews.get(muc_jid);
+ spyOn(converse.env.log, 'error');
+ await _converse.handleMAMResult(view.model, { 'messages': [msg] });
+ await u.waitUntil(() => converse.env.log.error.calls.count());
+ expect(converse.env.log.error).toHaveBeenCalledWith(
+ 'Invalid Stanza: MUC messages SHOULD NOT be XEP-0280 carbon copied'
+ );
+ expect(view.querySelectorAll('.chat-msg').length).toBe(0);
+ expect(view.model.messages.length).toBe(0);
+ }));
+});
diff --git a/src/plugins/muc-views/tests/muc-messages.js b/src/plugins/muc-views/tests/muc-messages.js
index 2a92f2d55..ee20dc63f 100644
--- a/src/plugins/muc-views/tests/muc-messages.js
+++ b/src/plugins/muc-views/tests/muc-messages.js
@@ -132,52 +132,6 @@ describe("A Groupchat Message", function () {
expect(view.model.messages.length).toBe(2);
}));
- it("is ignored if it has the same archive-id of an already received one",
- mock.initConverse([], {}, async function (_converse) {
-
- const muc_jid = 'room@muc.example.com';
- await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
- const view = _converse.chatboxviews.get(muc_jid);
- spyOn(view.model, 'getDuplicateMessage').and.callThrough();
- let stanza = u.toStanza(`
-
- Typical body text
-
- `);
- _converse.connection._dataRecv(mock.createRequest(stanza));
- await u.waitUntil(() => view.model.messages.length === 1);
- await u.waitUntil(() => view.model.getDuplicateMessage.calls.count() === 1);
- let result = await view.model.getDuplicateMessage.calls.all()[0].returnValue;
- expect(result).toBe(undefined);
-
- stanza = u.toStanza(`
-
-
-
-
-
- Typical body text
-
-
-
- `);
-
- spyOn(view.model, 'updateMessage');
- _converse.handleMAMResult(view.model, { 'messages': [stanza] });
- await u.waitUntil(() => view.model.getDuplicateMessage.calls.count() === 2);
- result = await view.model.getDuplicateMessage.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());
- }));
-
it("is ignored if it has the same stanza-id of an already received one",
mock.initConverse([], {}, async function (_converse) {
@@ -223,62 +177,6 @@ describe("A Groupchat Message", function () {
await u.waitUntil(() => view.model.updateMessage.calls.count());
}));
- it("will be discarded if it's a malicious message meant to look like a carbon copy",
- mock.initConverse([], {}, async function (_converse) {
-
- await mock.waitForRoster(_converse, 'current');
- await mock.openControlBox(_converse);
- const muc_jid = 'xsf@muc.xmpp.org';
- const sender_jid = `${muc_jid}/romeo`;
- const impersonated_jid = `${muc_jid}/i_am_groot`
- await mock.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': 'owner',
- 'jid': 'newguy@montague.lit/_converse.js-290929789',
- 'role': 'participant'
- }).tree();
- _converse.connection._dataRecv(mock.createRequest(stanza));
- /*
- *
- *
- *
- *
- * I am groot.
- *
- *
- *
- *
- */
- 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.chatboxviews.get(muc_jid);
- spyOn(converse.env.log, 'error');
- await _converse.handleMAMResult(view.model, { 'messages': [msg] });
- await u.waitUntil(() => converse.env.log.error.calls.count());
- expect(converse.env.log.error).toHaveBeenCalledWith(
- 'Invalid Stanza: MUC messages SHOULD NOT be XEP-0280 carbon copied'
- );
- expect(view.querySelectorAll('.chat-msg').length).toBe(0);
- expect(view.model.messages.length).toBe(0);
- }));
-
it("keeps track of the sender's role and affiliation",
mock.initConverse([], {}, async function (_converse) {