converse.js
Version:
Browser based XMPP chat client
239 lines (199 loc) • 13.3 kB
JavaScript
/*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 (_converse) {
await mock.waitForRoster(_converse, 'current');
const base_url = `${window.origin}/base`;
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.chatboxviews.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(
`<a class="chat-image__link" target="_blank" rel="noopener" href="${base_url}/logo/conversejs-filled.svg">`+
`<img class="chat-image img-thumbnail" loading="lazy" src="${base_url}/logo/conversejs-filled.svg">`+
`</a>`);
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(
`<a class="chat-image__link" target="_blank" rel="noopener" href="${base_url}/logo/conversejs-filled.svg?param1=val1&param2=val2">`+
`<img class="chat-image img-thumbnail" loading="lazy" src="${message.replace(/&/g, '&')}">`+
`</a>`);
// 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);
}));
it("will not render images if render_media is false",
mock.initConverse(['chatBoxesFetched'], {'render_media': false}, async function (_converse) {
await mock.waitForRoster(_converse, 'current');
const base_url = 'https://conversejs.org';
const 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.chatboxviews.get(contact_jid);
await mock.sendMessage(view, message);
const sel = '.chat-content .chat-msg:last .chat-msg__text';
await u.waitUntil(() => sizzle(sel).pop().innerHTML.replace(/<!-.*?->/g, '').trim() === message);
expect(true).toBe(true);
}));
it("will automatically render images from approved URLs only",
mock.initConverse(
['chatBoxesFetched'], {'render_media': ['imgur.com']},
async function (_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.chatboxviews.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);
}));
it("will automatically update its rendering of media and the message actions when settings change",
mock.initConverse(
['chatBoxesFetched'], {'render_media': ['conversejs.org']},
async function (_converse) {
const { api } = _converse;
await mock.waitForRoster(_converse, 'current');
const message = "https://conversejs.org/logo/conversejs-filled-192.svg";
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
await mock.openChatBoxFor(_converse, contact_jid);
const view = _converse.chatboxviews.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);
const actions_el = view.querySelector('converse-message-actions');
await u.waitUntil(() => actions_el.textContent.includes('Hide media'));
actions_el.querySelector('.chat-msg__action-hide-previews').click();
await u.waitUntil(() => !view.querySelector('converse-chat-message-body img'));
await u.waitUntil(() => actions_el.textContent.includes('Show media'));
actions_el.querySelector('.chat-msg__action-hide-previews').click();
await u.waitUntil(() => actions_el.textContent.includes('Hide media'));
api.settings.set('render_media', false);
await u.waitUntil(() => actions_el.textContent.includes('Show media'));
await u.waitUntil(() => !view.querySelector('converse-chat-message-body img'));
actions_el.querySelector('.chat-msg__action-hide-previews').click();
await u.waitUntil(() => actions_el.textContent.includes('Hide media'));
api.settings.set('render_media', ['conversejs.org']);
await u.waitUntil(() => actions_el.textContent.includes('Hide media'));
await u.waitUntil(() => view.querySelector('converse-chat-message-body img'));
api.settings.set('render_media', ['imgur.com']);
await u.waitUntil(() => actions_el.textContent.includes('Show media'));
await u.waitUntil(() => !view.querySelector('converse-chat-message-body img'));
api.settings.set('allowed_image_domains', ['imgur.com']);
await u.waitUntil(() => !actions_el.textContent.includes('Show media'));
expect(actions_el.textContent.includes('Hide media')).toBe(false);
api.settings.set('render_media', ['conversejs.org']);
return new Promise(resolve => setTimeout(() => {
expect(actions_el.textContent.includes('Hide media')).toBe(false);
expect(actions_el.textContent.includes('Show media')).toBe(false);
expect(view.querySelector('converse-chat-message-body img')).toBe(null);
resolve();
}, 500));
}));
it("will fall back to rendering images as URLs",
mock.initConverse(
['chatBoxesFetched'], {},
async function (_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.chatboxviews.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() ==
`<a target="_blank" rel="noopener" href="https://conversejs.org/logo/non-existing.svg">https://conversejs.org/logo/non-existing.svg</a>`, 1000);
}));
it("will fall back to rendering URLs that match image_urls_regex as URLs",
mock.initConverse(
['rosterGroupsFetched', 'chatBoxesFetched'], {
'render_media': true,
'image_urls_regex': /^https?:\/\/(www.)?(pbs\.twimg\.com\/)/i
},
async function (_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.chatboxviews.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'));
const msg = view.querySelector('.chat-content .chat-msg .chat-msg__text');
await u.waitUntil(() => msg.innerHTML.replace(/<!-.*?->/g, '').trim() ==
`<a target="_blank" rel="noopener" href="https://pbs.twimg.com/media/string?format=jpg&name=small">https://pbs.twimg.com/media/string?format=jpg&name=small</a>`, 1000);
}));
it("will respect a changed allowed_image_domains setting when re-rendered",
mock.initConverse(
['chatBoxesFetched'], {'render_media': true},
async function (_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.chatboxviews.get(contact_jid);
await mock.sendMessage(view, message);
await u.waitUntil(() => view.querySelectorAll('converse-chat-message-body .chat-image').length === 1);
expect(view.querySelector('.chat-msg__action-hide-previews')).not.toBe(null);
api.settings.set('allowed_image_domains', []);
await u.waitUntil(() => view.querySelector('converse-chat-message-body .chat-image') === null);
expect(view.querySelector('.chat-msg__action-hide-previews')).toBe(null);
api.settings.set('allowed_image_domains', null);
await u.waitUntil(() => view.querySelector('converse-chat-message-body .chat-image'));
expect(view.querySelector('.chat-msg__action-hide-previews')).not.toBe(null);
}));
it("will allow the user to toggle visibility of rendered images",
mock.initConverse(['chatBoxesFetched'], {'render_media': true}, async function (_converse) {
await mock.waitForRoster(_converse, 'current');
// let message = "https://i.imgur.com/Py9ifJE.mp4";
const base_url = 'https://conversejs.org';
const 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.chatboxviews.get(contact_jid);
await mock.sendMessage(view, message);
const sel = '.chat-content .chat-msg:last .chat-msg__text';
await u.waitUntil(() => sizzle(sel).pop().innerHTML.replace(/<!-.*?->/g, '').trim() === message);
const actions_el = view.querySelector('converse-message-actions');
await u.waitUntil(() => actions_el.textContent.includes('Hide media'));
await u.waitUntil(() => view.querySelector('converse-chat-message-body img'));
actions_el.querySelector('.chat-msg__action-hide-previews').click();
await u.waitUntil(() => actions_el.textContent.includes('Show media'));
await u.waitUntil(() => !view.querySelector('converse-chat-message-body img'));
expect(view.querySelector('converse-chat-message-body').innerHTML.replace(/<!-.*?->/g, '').trim())
.toBe(`<a target="_blank" rel="noopener" href="${message}">${message}</a>`)
}));
});