diff --git a/karma.conf.js b/karma.conf.js
index 4f3e5f111..226ea90a9 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -47,6 +47,7 @@ module.exports = function(config) {
{ pattern: "src/plugins/bookmark-views/tests/bookmarks.js", type: 'module' },
{ pattern: "src/plugins/chatview/tests/chatbox.js", type: 'module' },
{ pattern: "src/plugins/chatview/tests/me-messages.js", type: 'module' },
+ { pattern: "src/plugins/chatview/tests/message-images.js", type: 'module' },
{ pattern: "src/plugins/chatview/tests/messages.js", type: 'module' },
{ pattern: "src/plugins/chatview/tests/receipts.js", type: 'module' },
{ pattern: "src/plugins/chatview/tests/spoilers.js", type: 'module' },
diff --git a/src/headless/core.js b/src/headless/core.js
index 935c25d1c..cf98d2a99 100644
--- a/src/headless/core.js
+++ b/src/headless/core.js
@@ -394,7 +394,7 @@ export const api = _converse.api = {
* Get the value of a particular user setting.
* @method _converse.api.user.settings.get
* @param {String} key - The setting name
- * @param {*} fallback - An optional fallback value if the user setting is undefined
+ * @param {*} [fallback] - An optional fallback value if the user setting is undefined
* @returns {Promise} Promise which resolves with the value of the particular configuration setting.
* @example _converse.api.user.settings.get("foo");
*/
diff --git a/src/plugins/chatview/tests/message-images.js b/src/plugins/chatview/tests/message-images.js
new file mode 100644
index 000000000..b43ab7029
--- /dev/null
+++ b/src/plugins/chatview/tests/message-images.js
@@ -0,0 +1,141 @@
+/*global mock, converse */
+
+const { sizzle, u } = converse.env;
+
+describe("A Chat Message", function () {
+
+ it("will render images from their URLs", mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
+ await mock.waitForRoster(_converse, 'current');
+ const base_url = 'https://conversejs.org';
+ let message = base_url+"/logo/conversejs-filled.svg";
+ const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
+ await mock.openChatBoxFor(_converse, contact_jid);
+ const view = _converse.api.chatviews.get(contact_jid);
+ spyOn(view.model, 'sendMessage').and.callThrough();
+ await mock.sendMessage(view, message);
+ await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-image').length, 1000)
+ expect(view.model.sendMessage).toHaveBeenCalled();
+ let msg = sizzle('.chat-content .chat-msg:last .chat-msg__text').pop();
+ expect(msg.innerHTML.replace(//g, '').trim()).toEqual(
+ ``+
+ `
`+
+ ``);
+
+ message += "?param1=val1¶m2=val2";
+ await mock.sendMessage(view, message);
+ await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-image').length === 2, 1000);
+ expect(view.model.sendMessage).toHaveBeenCalled();
+ msg = sizzle('.chat-content .chat-msg:last .chat-msg__text').pop();
+ expect(msg.innerHTML.replace(//g, '').trim()).toEqual(
+ ``+
+ `
`+
+ ``);
+
+ // Test now with two images in one message
+ message += ' hello world '+base_url+"/logo/conversejs-filled.svg";
+ await mock.sendMessage(view, message);
+ await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-image').length === 4, 1000);
+ expect(view.model.sendMessage).toHaveBeenCalled();
+ msg = sizzle('.chat-content .chat-msg:last .chat-msg__text').pop();
+ expect(msg.textContent.trim()).toEqual('hello world');
+ expect(msg.querySelectorAll('img.chat-image').length).toEqual(2);
+
+ // Configured image URLs are rendered
+ _converse.api.settings.set('image_urls_regex', /^https?:\/\/(?:www.)?(?:imgur\.com\/\w{7})\/?$/i);
+ message = 'https://imgur.com/oxymPax';
+ await mock.sendMessage(view, message);
+ await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-image').length === 5, 1000);
+ expect(view.querySelectorAll('.chat-content .chat-image').length).toBe(5);
+
+ // Check that the Imgur URL gets a .png attached to make it render
+ await u.waitUntil(() => Array.from(view.querySelectorAll('.chat-content .chat-image')).pop().src.endsWith('png'), 1000);
+ done();
+ }));
+
+ it("will render images from approved URLs only",
+ mock.initConverse(
+ ['chatBoxesFetched'], {'show_images_inline': ['conversejs.org']},
+ async function (done, _converse) {
+
+ await mock.waitForRoster(_converse, 'current');
+ const base_url = 'https://conversejs.org';
+ let message = 'https://imgur.com/oxymPax.png';
+ const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
+ await mock.openChatBoxFor(_converse, contact_jid);
+ const view = _converse.api.chatviews.get(contact_jid);
+ spyOn(view.model, 'sendMessage').and.callThrough();
+ await mock.sendMessage(view, message);
+ await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-msg').length === 1);
+
+ message = base_url+"/logo/conversejs-filled.svg";
+ await mock.sendMessage(view, message);
+ await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-msg').length === 2, 1000);
+ await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-image').length === 1, 1000)
+ expect(view.querySelectorAll('.chat-content .chat-image').length).toBe(1);
+ done();
+ }));
+
+ it("will fall back to rendering images as URLs",
+ mock.initConverse(
+ ['chatBoxesFetched'], {},
+ async function (done, _converse) {
+
+ await mock.waitForRoster(_converse, 'current');
+ const base_url = 'https://conversejs.org';
+ const message = base_url+"/logo/non-existing.svg";
+ const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
+ await mock.openChatBoxFor(_converse, contact_jid);
+ const view = _converse.api.chatviews.get(contact_jid);
+ spyOn(view.model, 'sendMessage').and.callThrough();
+ await mock.sendMessage(view, message);
+ await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-image').length, 1000)
+ expect(view.model.sendMessage).toHaveBeenCalled();
+ const msg = sizzle('.chat-content .chat-msg:last .chat-msg__text').pop();
+ await u.waitUntil(() => msg.innerHTML.replace(//g, '').trim() ==
+ `https://conversejs.org/logo/non-existing.svg`, 1000);
+ done();
+ }));
+
+ it("will fall back to rendering URLs that match image_urls_regex as URLs",
+ mock.initConverse(
+ ['rosterGroupsFetched', 'chatBoxesFetched'], {
+ 'show_images_inline': ['twimg.com'],
+ 'image_urls_regex': /^https?:\/\/(www.)?(pbs\.twimg\.com\/)/i
+ },
+ async function (done, _converse) {
+
+ await mock.waitForRoster(_converse, 'current');
+ const message = "https://pbs.twimg.com/media/string?format=jpg&name=small";
+ const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
+ await mock.openChatBoxFor(_converse, contact_jid);
+ const view = _converse.api.chatviews.get(contact_jid);
+ spyOn(view.model, 'sendMessage').and.callThrough();
+ await mock.sendMessage(view, message);
+ expect(view.model.sendMessage).toHaveBeenCalled();
+ await u.waitUntil(() => view.querySelector('.chat-content .chat-msg'), 1000);
+ const msg = view.querySelector('.chat-content .chat-msg .chat-msg__text');
+ await u.waitUntil(() => msg.innerHTML.replace(//g, '').trim() ==
+ `https://pbs.twimg.com/media/string?format=jpg&name=small`, 1000);
+ done();
+ }));
+
+ it("will respect a changed setting when re-rendered",
+ mock.initConverse(
+ ['chatBoxesFetched'], {'show_images_inline': true},
+ async function (done, _converse) {
+
+ const { api } = _converse;
+ await mock.waitForRoster(_converse, 'current');
+ const message = 'https://imgur.com/oxymPax.png';
+ const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
+ await mock.openChatBoxFor(_converse, contact_jid);
+ const view = _converse.api.chatviews.get(contact_jid);
+ await mock.sendMessage(view, message);
+ await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-msg').length === 1);
+ api.settings.set('show_images_inline', false);
+ view.querySelector('converse-chat-message-body').requestUpdate();
+ await u.waitUntil(() => view.querySelector('.chat-content .chat-image') === null);
+ expect(true).toBe(true);
+ done();
+ }));
+});
diff --git a/src/plugins/chatview/tests/messages.js b/src/plugins/chatview/tests/messages.js
index 0490e5c7e..6ad594713 100644
--- a/src/plugins/chatview/tests/messages.js
+++ b/src/plugins/chatview/tests/messages.js
@@ -1,7 +1,6 @@
-/*global mock, converse, _ */
+/*global mock, converse */
-const { Promise, Strophe, $msg, dayjs, sizzle } = converse.env;
-const u = converse.env.utils;
+const { Promise, Strophe, $msg, dayjs, sizzle, u } = converse.env;
describe("A Chat Message", function () {
@@ -614,122 +613,6 @@ describe("A Chat Message", function () {
done();
}));
- it("will render images from their URLs", mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
- await mock.waitForRoster(_converse, 'current');
- const base_url = 'https://conversejs.org';
- let message = base_url+"/logo/conversejs-filled.svg";
- const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
- await mock.openChatBoxFor(_converse, contact_jid);
- const view = _converse.api.chatviews.get(contact_jid);
- spyOn(view.model, 'sendMessage').and.callThrough();
- await mock.sendMessage(view, message);
- await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-image').length, 1000)
- expect(view.model.sendMessage).toHaveBeenCalled();
- let msg = sizzle('.chat-content .chat-msg:last .chat-msg__text').pop();
- expect(msg.innerHTML.replace(//g, '').trim()).toEqual(
- ``+
- `
`+
- ``);
-
- message += "?param1=val1¶m2=val2";
- await mock.sendMessage(view, message);
- await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-image').length === 2, 1000);
- expect(view.model.sendMessage).toHaveBeenCalled();
- msg = sizzle('.chat-content .chat-msg:last .chat-msg__text').pop();
- expect(msg.innerHTML.replace(//g, '').trim()).toEqual(
- ``+
- `
`+
- ``);
-
- // Test now with two images in one message
- message += ' hello world '+base_url+"/logo/conversejs-filled.svg";
- await mock.sendMessage(view, message);
- await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-image').length === 4, 1000);
- expect(view.model.sendMessage).toHaveBeenCalled();
- msg = sizzle('.chat-content .chat-msg:last .chat-msg__text').pop();
- expect(msg.textContent.trim()).toEqual('hello world');
- expect(msg.querySelectorAll('img.chat-image').length).toEqual(2);
-
- // Configured image URLs are rendered
- _converse.api.settings.set('image_urls_regex', /^https?:\/\/(?:www.)?(?:imgur\.com\/\w{7})\/?$/i);
- message = 'https://imgur.com/oxymPax';
- await mock.sendMessage(view, message);
- await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-image').length === 5, 1000);
- expect(view.querySelectorAll('.chat-content .chat-image').length).toBe(5);
-
- // Check that the Imgur URL gets a .png attached to make it render
- await u.waitUntil(() => Array.from(view.querySelectorAll('.chat-content .chat-image')).pop().src.endsWith('png'), 1000);
- done();
- }));
-
- it("will render images from approved URLs only",
- mock.initConverse(
- ['chatBoxesFetched'], {'show_images_inline': ['conversejs.org']},
- async function (done, _converse) {
-
- await mock.waitForRoster(_converse, 'current');
- const base_url = 'https://conversejs.org';
- let message = 'https://imgur.com/oxymPax.png';
- const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
- await mock.openChatBoxFor(_converse, contact_jid);
- const view = _converse.api.chatviews.get(contact_jid);
- spyOn(view.model, 'sendMessage').and.callThrough();
- await mock.sendMessage(view, message);
- await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-msg').length === 1);
-
- message = base_url+"/logo/conversejs-filled.svg";
- await mock.sendMessage(view, message);
- await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-msg').length === 2, 1000);
- await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-image').length === 1, 1000)
- expect(view.querySelectorAll('.chat-content .chat-image').length).toBe(1);
-
- done();
- }));
-
- it("will fall back to rendering images as URLs",
- mock.initConverse(
- ['chatBoxesFetched'], {},
- async function (done, _converse) {
-
- await mock.waitForRoster(_converse, 'current');
- const base_url = 'https://conversejs.org';
- const message = base_url+"/logo/non-existing.svg";
- const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
- await mock.openChatBoxFor(_converse, contact_jid);
- const view = _converse.api.chatviews.get(contact_jid);
- spyOn(view.model, 'sendMessage').and.callThrough();
- await mock.sendMessage(view, message);
- await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-image').length, 1000)
- expect(view.model.sendMessage).toHaveBeenCalled();
- const msg = sizzle('.chat-content .chat-msg:last .chat-msg__text').pop();
- await u.waitUntil(() => msg.innerHTML.replace(//g, '').trim() ==
- `https://conversejs.org/logo/non-existing.svg`, 1000);
- done();
- }));
-
- it("will fall back to rendering URLs that match image_urls_regex as URLs",
- mock.initConverse(
- ['rosterGroupsFetched', 'chatBoxesFetched'], {
- 'show_images_inline': ['twimg.com'],
- 'image_urls_regex': /^https?:\/\/(www.)?(pbs\.twimg\.com\/)/i
- },
- async function (done, _converse) {
-
- await mock.waitForRoster(_converse, 'current');
- const message = "https://pbs.twimg.com/media/string?format=jpg&name=small";
- const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
- await mock.openChatBoxFor(_converse, contact_jid);
- const view = _converse.api.chatviews.get(contact_jid);
- spyOn(view.model, 'sendMessage').and.callThrough();
- await mock.sendMessage(view, message);
- expect(view.model.sendMessage).toHaveBeenCalled();
- await u.waitUntil(() => view.querySelector('.chat-content .chat-msg'), 1000);
- const msg = view.querySelector('.chat-content .chat-msg .chat-msg__text');
- await u.waitUntil(() => msg.innerHTML.replace(//g, '').trim() ==
- `https://pbs.twimg.com/media/string?format=jpg&name=small`, 1000);
- done();
- }));
-
it("will render the message time as configured",
mock.initConverse(
['chatBoxesFetched'], {},
@@ -1077,7 +960,7 @@ describe("A Chat Message", function () {
await u.waitUntil(() => view.querySelector('.chat-msg__author').textContent.trim() === 'Mercutio');
let author_el = view.querySelector('.chat-msg__author');
- expect( _.includes(author_el.textContent.trim(), 'Mercutio')).toBeTruthy();
+ expect(author_el.textContent.trim().includes('Mercutio')).toBeTruthy();
await u.waitUntil(() => vcard_fetched, 100);
expect(_converse.api.vcard.get).toHaveBeenCalled();
await u.waitUntil(() => chatbox.vcard.get('fullname') === mock.cur_names[0])