/*global mock, converse */
const { u, Promise, $msg } = converse.env;
describe("An incoming chat Message", function () {
it("can have styling disabled via an \"unstyled\" element",
mock.initConverse(['rosterGroupsFetched', 'chatBoxesFetched'], {},
async function (done, _converse) {
const include_nick = false;
await mock.waitForRoster(_converse, 'current', 2, include_nick);
await mock.openControlBox(_converse);
const msg_text = '> _ >';
const sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@montague.lit';
const msg = $msg({
'from': sender_jid,
'id': u.getUniqueId(),
'to': _converse.connection.jid,
'type': 'chat',
'xmlns': 'jabber:client'
}).c('body').t(msg_text).up()
.c('unstyled', {'xmlns': 'urn:xmpp:styling:0'}).tree();
await _converse.handleMessageStanza(msg);
const view = _converse.api.chatviews.get(sender_jid);
await u.waitUntil(() => view.model.messages.length);
expect(view.model.messages.models[0].get('is_unstyled')).toBe(true);
setTimeout(() => {
const msg_el = view.querySelector('converse-chat-message-body');
expect(msg_el.innerText).toBe(msg_text);
done();
}, 500);
}));
it("can have styling disabled via the allow_message_styling setting",
mock.initConverse(['rosterGroupsFetched', 'chatBoxesFetched'], {'allow_message_styling': false},
async function (done, _converse) {
const include_nick = false;
await mock.waitForRoster(_converse, 'current', 2, include_nick);
await mock.openControlBox(_converse);
const msg_text = '> _ >';
const sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@montague.lit';
const msg = $msg({
'from': sender_jid,
'id': u.getUniqueId(),
'to': _converse.connection.jid,
'type': 'chat',
'xmlns': 'jabber:client'
}).c('body').t(msg_text).tree();
await _converse.handleMessageStanza(msg);
const view = _converse.api.chatviews.get(sender_jid);
await u.waitUntil(() => view.model.messages.length);
expect(view.model.messages.models[0].get('is_unstyled')).toBe(false);
setTimeout(() => {
const msg_el = view.querySelector('converse-chat-message-body');
expect(msg_el.innerText).toBe(msg_text);
done();
}, 500);
}));
it("can be styled with span XEP-0393 message styling hints",
mock.initConverse(['rosterGroupsFetched', 'chatBoxesFetched'], {},
async function (done, _converse) {
let msg_text, msg, msg_el;
await mock.waitForRoster(_converse, 'current', 1);
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);
msg_text = "This *message _contains_* styling hints! \`Here's *some* code\`";
msg = mock.createChatMessage(_converse, contact_jid, msg_text)
await _converse.handleMessageStanza(msg);
await new Promise(resolve => view.model.messages.once('rendered', resolve));
msg_el = view.querySelector('converse-chat-message-body');
expect(msg_el.innerText).toBe(msg_text);
await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') ===
'This *'+
'message _contains_'+
'*'+
' styling hints! '+
'`Here\'s *some* code
`'
);
msg_text = "Here's a ~strikethrough section~";
msg = mock.createChatMessage(_converse, contact_jid, msg_text)
await _converse.handleMessageStanza(msg);
await new Promise(resolve => view.model.messages.once('rendered', resolve));
msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
expect(msg_el.innerText).toBe(msg_text);
await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') ===
'Here\'s a ~strikethrough section~');
// Span directives containing hyperlinks
msg_text = "~Check out this site: https://conversejs.org~"
msg = mock.createChatMessage(_converse, contact_jid, msg_text)
await _converse.handleMessageStanza(msg);
await new Promise(resolve => view.model.messages.once('rendered', resolve));
msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
expect(msg_el.innerText).toBe(msg_text);
await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') ===
'~'+
'Check out this site: https://conversejs.org'+
'~');
// Images inside directives aren't shown inline
const base_url = 'https://conversejs.org';
msg_text = `*${base_url}/logo/conversejs-filled.svg*`;
msg = mock.createChatMessage(_converse, contact_jid, msg_text)
await _converse.handleMessageStanza(msg);
await new Promise(resolve => view.model.messages.once('rendered', resolve));
msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
expect(msg_el.innerText).toBe(msg_text);
await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') ===
'*'+
'https://conversejs.org/logo/conversejs-filled.svg'+
'*');
// Emojis inside directives
msg_text = `~ Hello! :poop: ~`;
msg = mock.createChatMessage(_converse, contact_jid, msg_text)
await _converse.handleMessageStanza(msg);
await new Promise(resolve => view.model.messages.once('rendered', resolve));
msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
expect(msg_el.innerText).toBe(msg_text);
await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') ===
'~ Hello! 💩 ~');
// Span directives don't cross lines
msg_text = "This *is not a styling hint \n * _But this is_!";
msg = mock.createChatMessage(_converse, contact_jid, msg_text)
await _converse.handleMessageStanza(msg);
await new Promise(resolve => view.model.messages.once('rendered', resolve));
msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
expect(msg_el.innerText).toBe(msg_text);
await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') ===
'This *is not a styling hint \n'+
' * _But this is_!');
msg_text = `(There are three blocks in this body marked by parens,)\n (but there is no *formatting)\n (as spans* may not escape blocks.)\n ~strikethrough~`;
msg = mock.createChatMessage(_converse, contact_jid, msg_text)
await _converse.handleMessageStanza(msg);
await new Promise(resolve => view.model.messages.once('rendered', resolve));
msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
expect(msg_el.innerText).toBe(msg_text);
await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') ===
'(There are three blocks in this body marked by parens,)\n'+
' (but there is no *formatting)\n'+
' (as spans* may not escape blocks.)\n'+
' ~strikethrough~');
// Some edge-case (unspecified) spans
msg_text = `__ hello world _`;
msg = mock.createChatMessage(_converse, contact_jid, msg_text)
await _converse.handleMessageStanza(msg);
await new Promise(resolve => view.model.messages.once('rendered', resolve));
msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
expect(msg_el.innerText).toBe(msg_text);
await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') ===
'__ hello world _');
// Directives which are parts of words aren't matched
msg_text = `Go to ~https://conversejs.org~now _please_`;
msg = mock.createChatMessage(_converse, contact_jid, msg_text)
await _converse.handleMessageStanza(msg);
await new Promise(resolve => view.model.messages.once('rendered', resolve));
msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
expect(msg_el.innerText).toBe(msg_text);
await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') ===
'Go to ~https://conversejs.org~now _please_');
msg_text = `Go to _https://converse_js.org_ _please_`;
msg = mock.createChatMessage(_converse, contact_jid, msg_text)
await _converse.handleMessageStanza(msg);
await new Promise(resolve => view.model.messages.once('rendered', resolve));
msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
expect(msg_el.innerText).toBe(msg_text);
await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') ===
'Go to _'+
'https://converse_js.org'+
'_ _please_');
done();
}));
it("can be styled with block XEP-0393 message styling hints",
mock.initConverse(['rosterGroupsFetched', 'chatBoxesFetched'], {},
async function (done, _converse) {
let msg_text, msg, msg_el;
await mock.waitForRoster(_converse, 'current', 1);
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);
msg_text = `Here's a code block: \n\`\`\`\nInside the code-block, hello
we don't enable *styling hints* like ~these~\n\`\`\``;
msg = mock.createChatMessage(_converse, contact_jid, msg_text)
await _converse.handleMessageStanza(msg);
await new Promise(resolve => view.model.messages.once('rendered', resolve));
msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
expect(msg_el.innerText).toBe(msg_text);
await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') ===
'Here\'s a code block: \n'+
'
Inside the code-block, <code>hello</code> we don\'t enable *styling hints* like ~these~\n'+
'
ignored\n(println "Hello, world!")\n
'+
'This is quoted text\nThis is also quoted\nThis is not quoted'); msg_text = `> This is *quoted* text\n>This is \`also _quoted_\`\nThis is not quoted`; msg = mock.createChatMessage(_converse, contact_jid, msg_text) await _converse.handleMessageStanza(msg); await new Promise(resolve => view.model.messages.once('rendered', resolve)); msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop(); expect(msg_el.innerText).toBe(msg_text); await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') === '
This is *quoted* text\n'+
'This is `also _quoted_
`
\n'+
'This is not quoted');
msg_text = `> > This is doubly quoted text`;
msg = mock.createChatMessage(_converse, contact_jid, msg_text)
await _converse.handleMessageStanza(msg);
await new Promise(resolve => view.model.messages.once('rendered', resolve));
msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
expect(msg_el.innerText).toBe(msg_text);
await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') === ""); msg_text = `>> This is doubly quoted text`; msg = mock.createChatMessage(_converse, contact_jid, msg_text) await _converse.handleMessageStanza(msg); await new Promise(resolve => view.model.messages.once('rendered', resolve)); msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop(); expect(msg_el.innerText).toBe(msg_text); await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') === "This is doubly quoted text
"); msg_text = ">```\n>ignored\n> (println \"Hello, world!\")\n>```\n> This should show up as monospace, preformatted text ^"; msg = mock.createChatMessage(_converse, contact_jid, msg_text) await _converse.handleMessageStanza(msg); await new Promise(resolve => view.model.messages.once('rendered', resolve)); msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop(); expect(msg_el.innerText).toBe(msg_text); await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') === 'This is doubly quoted text
'+ ''); msg_text = '> ```\n> (println "Hello, world!")\n\nThe entire blockquote is a preformatted text block, but this line is plaintext!'; msg = mock.createChatMessage(_converse, contact_jid, msg_text) await _converse.handleMessageStanza(msg); await new Promise(resolve => view.model.messages.once('rendered', resolve)); msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop(); expect(msg_el.innerText).toBe(msg_text); await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') === '```'+ 'ignored\n <span></span> (println "Hello, world!")\n'+ '
```\n'+ ' This should show up as monospace, preformatted text ^'+ '
```\n (println "Hello, world!")\n\n'+ 'The entire blockquote is a preformatted text block, but this line is plaintext!'); msg_text = '> Also, icons.js is loaded from /dist, instead of dist.\nhttps://conversejs.org/docs/html/configuration.html#assets-path' msg = mock.createChatMessage(_converse, contact_jid, msg_text) await _converse.handleMessageStanza(msg); await new Promise(resolve => view.model.messages.once('rendered', resolve)); msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop(); expect(msg_el.innerText).toBe(msg_text); await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') === '
Also, icons.js is loaded from /dist, instead of dist.\n'+ 'https://conversejs.org/docs/html/configuration.html#assets-path'); msg_text = '> Where is it located?\ngeo:37.786971,-122.399677'; msg = mock.createChatMessage(_converse, contact_jid, msg_text) await _converse.handleMessageStanza(msg); await new Promise(resolve => view.model.messages.once('rendered', resolve)); msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop(); expect(msg_el.innerText).toBe(msg_text); await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') === '
Where is it located?\n'+ 'https://www.openstreetmap.org/?mlat=37.786971&mlon=-122.399677#map=18/37.786971/-122.399677'); msg_text = '> What do you think of it?\n :poop:'; msg = mock.createChatMessage(_converse, contact_jid, msg_text) await _converse.handleMessageStanza(msg); await new Promise(resolve => view.model.messages.once('rendered', resolve)); msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop(); expect(msg_el.innerText).toBe(msg_text); await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') === '
What do you think of it?\n 💩'); msg_text = '> What do you think of it?\n~hello~'; msg = mock.createChatMessage(_converse, contact_jid, msg_text) await _converse.handleMessageStanza(msg); await new Promise(resolve => view.model.messages.once('rendered', resolve)); msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop(); expect(msg_el.innerText).toBe(msg_text); await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') === '
What do you think of it?\n~
What do you think of it romeo?\n Did you see this romeo?`); done(); })); }); describe("A outgoing groupchat Message", function () { it("can be styled with span XEP-0393 message styling hints that contain mentions", mock.initConverse(['rosterGroupsFetched', 'chatBoxesFetched'], {}, async function (done, _converse) { const muc_jid = 'lounge@montague.lit'; await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo'); const view = _converse.api.chatviews.get(muc_jid); const msg_text = "This *message mentions romeo*"; const msg = $msg({ from: 'lounge@montague.lit/gibson', id: u.getUniqueId(), to: 'romeo@montague.lit', type: 'groupchat' }).c('body').t(msg_text).up() .c('reference', {'xmlns':'urn:xmpp:reference:0', 'begin':'23', 'end':'29', 'type':'mention', 'uri':'xmpp:romeo@montague.lit'}).nodeTree; await view.model.handleMessageStanza(msg); const message = await u.waitUntil(() => view.querySelector('.chat-msg__text')); expect(message.classList.length).toEqual(1); const msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop(); expect(msg_el.innerText).toBe(msg_text); await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') === 'This *message mentions romeo*'); done(); })); });