/*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.el.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.el.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.el.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.el.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.el.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.el.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.el.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.el.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.el.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.el.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.el.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.el.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.el.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'+ '
```
' ); msg_text = "```\nignored\n(println \"Hello, world!\")\n```\nThis 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.el.querySelectorAll('converse-chat-message-body')).pop(); expect(msg_el.innerText).toBe(msg_text); await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') === '
```
'+ 'ignored\n(println "Hello, world!")\n'+ '
```
\n'+ 'This should show up as monospace, preformatted text ^'); msg_text = "```ignored\n (println \"Hello, world!\")\n ```\n\n This should not 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.el.querySelectorAll('converse-chat-message-body')).pop(); expect(msg_el.innerText).toBe(msg_text); await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') === '```ignored\n (println "Hello, world!")\n ```\n\n'+ ' This should not show up as monospace, '+ '*preformatted* text ^'); done(); })); it("can be styled with quote 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 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.el.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\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.el.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.el.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 = `>> 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.el.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.el.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 ^'+ '
'); 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.el.querySelectorAll('converse-chat-message-body')).pop(); expect(msg_el.innerText).toBe(msg_text); await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') === '
```\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.el.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.el.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.el.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.el.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~hello~'); msg_text = 'hello world > this is not a quote'; 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.el.querySelectorAll('converse-chat-message-body')).pop(); expect(msg_el.innerText).toBe(msg_text); await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') === 'hello world > this is not a quote'); msg_text = '> What do you think of it romeo?\n Did you see this romeo?'; msg = $msg({ from: contact_jid, to: _converse.connection.jid, type: 'chat', id: (new Date()).getTime() }).c('body').t(msg_text).up() .c('reference', { 'xmlns':'urn:xmpp:reference:0', 'begin':'26', 'end':'31', 'type':'mention', 'uri': _converse.bare_jid }) .c('reference', { 'xmlns':'urn:xmpp:reference:0', 'begin':'51', 'end':'56', 'type':'mention', 'uri': _converse.bare_jid }).nodeTree; await _converse.handleMessageStanza(msg); await new Promise(resolve => view.model.messages.once('rendered', resolve)); msg_el = Array.from(view.el.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 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.el.querySelector('.chat-msg__text')); expect(message.classList.length).toEqual(1); const msg_el = Array.from(view.el.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(); })); });