styling.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. /*global mock, converse */
  2. const { u, Promise, $msg } = converse.env;
  3. describe("An incoming chat Message", function () {
  4. it("can have styling disabled via an \"unstyled\" element",
  5. mock.initConverse(['rosterContactsFetched', 'chatBoxesFetched'], {},
  6. async function (done, _converse) {
  7. const include_nick = false;
  8. await mock.waitForRoster(_converse, 'current', 2, include_nick);
  9. await mock.openControlBox(_converse);
  10. const msg_text = '> _ >';
  11. const sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@montague.lit';
  12. const msg = $msg({
  13. 'from': sender_jid,
  14. 'id': u.getUniqueId(),
  15. 'to': _converse.connection.jid,
  16. 'type': 'chat',
  17. 'xmlns': 'jabber:client'
  18. }).c('body').t(msg_text).up()
  19. .c('unstyled', {'xmlns': 'urn:xmpp:styling:0'}).tree();
  20. await _converse.handleMessageStanza(msg);
  21. const view = _converse.api.chatviews.get(sender_jid);
  22. await u.waitUntil(() => view.model.messages.length);
  23. expect(view.model.messages.models[0].get('is_unstyled')).toBe(true);
  24. setTimeout(() => {
  25. const msg_el = view.querySelector('converse-chat-message-body');
  26. expect(msg_el.innerText).toBe(msg_text);
  27. done();
  28. }, 500);
  29. }));
  30. it("can have styling disabled via the allow_message_styling setting",
  31. mock.initConverse(['rosterContactsFetched', 'chatBoxesFetched'], {'allow_message_styling': false},
  32. async function (done, _converse) {
  33. const include_nick = false;
  34. await mock.waitForRoster(_converse, 'current', 2, include_nick);
  35. await mock.openControlBox(_converse);
  36. const msg_text = '> _ >';
  37. const sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@montague.lit';
  38. const msg = $msg({
  39. 'from': sender_jid,
  40. 'id': u.getUniqueId(),
  41. 'to': _converse.connection.jid,
  42. 'type': 'chat',
  43. 'xmlns': 'jabber:client'
  44. }).c('body').t(msg_text).tree();
  45. await _converse.handleMessageStanza(msg);
  46. const view = _converse.api.chatviews.get(sender_jid);
  47. await u.waitUntil(() => view.model.messages.length);
  48. expect(view.model.messages.models[0].get('is_unstyled')).toBe(false);
  49. setTimeout(() => {
  50. const msg_el = view.querySelector('converse-chat-message-body');
  51. expect(msg_el.innerText).toBe(msg_text);
  52. done();
  53. }, 500);
  54. }));
  55. it("can be styled with span XEP-0393 message styling hints",
  56. mock.initConverse(['rosterContactsFetched', 'chatBoxesFetched'], {},
  57. async function (done, _converse) {
  58. let msg_text, msg, msg_el;
  59. await mock.waitForRoster(_converse, 'current', 1);
  60. const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
  61. await mock.openChatBoxFor(_converse, contact_jid);
  62. const view = _converse.api.chatviews.get(contact_jid);
  63. msg_text = "This *message _contains_* styling hints! \`Here's *some* code\`";
  64. msg = mock.createChatMessage(_converse, contact_jid, msg_text)
  65. await _converse.handleMessageStanza(msg);
  66. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  67. msg_el = view.querySelector('converse-chat-message-body');
  68. expect(msg_el.innerText).toBe(msg_text);
  69. await u.waitUntil(() => msg_el.innerHTML.replace(/<!---->/g, '') ===
  70. 'This <span class="styling-directive">*</span>'+
  71. '<b>message <span class="styling-directive">_</span><i>contains</i><span class="styling-directive">_</span></b>'+
  72. '<span class="styling-directive">*</span>'+
  73. ' styling hints! '+
  74. '<span class="styling-directive">`</span><code>Here\'s *some* code</code><span class="styling-directive">`</span>'
  75. );
  76. msg_text = "Here's a ~strikethrough section~";
  77. msg = mock.createChatMessage(_converse, contact_jid, msg_text)
  78. await _converse.handleMessageStanza(msg);
  79. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  80. msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
  81. expect(msg_el.innerText).toBe(msg_text);
  82. await u.waitUntil(() => msg_el.innerHTML.replace(/<!---->/g, '') ===
  83. 'Here\'s a <span class="styling-directive">~</span><del>strikethrough section</del><span class="styling-directive">~</span>');
  84. // Span directives containing hyperlinks
  85. msg_text = "~Check out this site: https://conversejs.org~"
  86. msg = mock.createChatMessage(_converse, contact_jid, msg_text)
  87. await _converse.handleMessageStanza(msg);
  88. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  89. msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
  90. expect(msg_el.innerText).toBe(msg_text);
  91. await u.waitUntil(() => msg_el.innerHTML.replace(/<!---->/g, '') ===
  92. '<span class="styling-directive">~</span>'+
  93. '<del>Check out this site: <a target="_blank" rel="noopener" href="https://conversejs.org/">https://conversejs.org</a></del>'+
  94. '<span class="styling-directive">~</span>');
  95. // Images inside directives aren't shown inline
  96. const base_url = 'https://conversejs.org';
  97. msg_text = `*${base_url}/logo/conversejs-filled.svg*`;
  98. msg = mock.createChatMessage(_converse, contact_jid, msg_text)
  99. await _converse.handleMessageStanza(msg);
  100. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  101. msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
  102. expect(msg_el.innerText).toBe(msg_text);
  103. await u.waitUntil(() => msg_el.innerHTML.replace(/<!---->/g, '') ===
  104. '<span class="styling-directive">*</span>'+
  105. '<b><a target="_blank" rel="noopener" href="https://conversejs.org/logo/conversejs-filled.svg">https://conversejs.org/logo/conversejs-filled.svg</a></b>'+
  106. '<span class="styling-directive">*</span>');
  107. // Emojis inside directives
  108. msg_text = `~ Hello! :poop: ~`;
  109. msg = mock.createChatMessage(_converse, contact_jid, msg_text)
  110. await _converse.handleMessageStanza(msg);
  111. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  112. msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
  113. expect(msg_el.innerText).toBe(msg_text);
  114. await u.waitUntil(() => msg_el.innerHTML.replace(/<!---->/g, '') ===
  115. '<span class="styling-directive">~</span><del> Hello! <span title=":poop:">💩</span> </del><span class="styling-directive">~</span>');
  116. // Span directives don't cross lines
  117. msg_text = "This *is not a styling hint \n * _But this is_!";
  118. msg = mock.createChatMessage(_converse, contact_jid, msg_text)
  119. await _converse.handleMessageStanza(msg);
  120. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  121. msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
  122. expect(msg_el.innerText).toBe(msg_text);
  123. await u.waitUntil(() => msg_el.innerHTML.replace(/<!---->/g, '') ===
  124. 'This *is not a styling hint \n'+
  125. ' * <span class="styling-directive">_</span><i>But this is</i><span class="styling-directive">_</span>!');
  126. 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~`;
  127. msg = mock.createChatMessage(_converse, contact_jid, msg_text)
  128. await _converse.handleMessageStanza(msg);
  129. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  130. msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
  131. expect(msg_el.innerText).toBe(msg_text);
  132. await u.waitUntil(() => msg_el.innerHTML.replace(/<!---->/g, '') ===
  133. '(There are three blocks in this body marked by parens,)\n'+
  134. ' (but there is no *formatting)\n'+
  135. ' (as spans* may not escape blocks.)\n'+
  136. ' <span class="styling-directive">~</span><del>strikethrough</del><span class="styling-directive">~</span>');
  137. // Some edge-case (unspecified) spans
  138. msg_text = `__ hello world _`;
  139. msg = mock.createChatMessage(_converse, contact_jid, msg_text)
  140. await _converse.handleMessageStanza(msg);
  141. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  142. msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
  143. expect(msg_el.innerText).toBe(msg_text);
  144. await u.waitUntil(() => msg_el.innerHTML.replace(/<!---->/g, '') ===
  145. '_<span class="styling-directive">_</span><i> hello world </i><span class="styling-directive">_</span>');
  146. // Directives which are parts of words aren't matched
  147. msg_text = `Go to ~https://conversejs.org~now _please_`;
  148. msg = mock.createChatMessage(_converse, contact_jid, msg_text)
  149. await _converse.handleMessageStanza(msg);
  150. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  151. msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
  152. expect(msg_el.innerText).toBe(msg_text);
  153. await u.waitUntil(() => msg_el.innerHTML.replace(/<!---->/g, '') ===
  154. 'Go to ~https://conversejs.org~now <span class="styling-directive">_</span><i>please</i><span class="styling-directive">_</span>');
  155. msg_text = `Go to _https://converse_js.org_ _please_`;
  156. msg = mock.createChatMessage(_converse, contact_jid, msg_text)
  157. await _converse.handleMessageStanza(msg);
  158. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  159. msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
  160. expect(msg_el.innerText).toBe(msg_text);
  161. await u.waitUntil(() => msg_el.innerHTML.replace(/<!---->/g, '') ===
  162. 'Go to <span class="styling-directive">_</span>'+
  163. '<i><a target="_blank" rel="noopener" href="https://converse_js.org/">https://converse_js.org</a></i>'+
  164. '<span class="styling-directive">_</span> <span class="styling-directive">_</span><i>please</i><span class="styling-directive">_</span>');
  165. done();
  166. }));
  167. it("can be styled with block XEP-0393 message styling hints",
  168. mock.initConverse(['rosterContactsFetched', 'chatBoxesFetched'], {},
  169. async function (done, _converse) {
  170. let msg_text, msg, msg_el;
  171. await mock.waitForRoster(_converse, 'current', 1);
  172. const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
  173. await mock.openChatBoxFor(_converse, contact_jid);
  174. const view = _converse.api.chatviews.get(contact_jid);
  175. msg_text = `Here's a code block: \n\`\`\`\nInside the code-block, <code>hello</code> we don't enable *styling hints* like ~these~\n\`\`\``;
  176. msg = mock.createChatMessage(_converse, contact_jid, msg_text)
  177. await _converse.handleMessageStanza(msg);
  178. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  179. msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
  180. expect(msg_el.innerText).toBe(msg_text);
  181. await u.waitUntil(() => msg_el.innerHTML.replace(/<!---->/g, '') ===
  182. 'Here\'s a code block: \n'+
  183. '<div class="styling-directive">```</div><code class="block">Inside the code-block, &lt;code&gt;hello&lt;/code&gt; we don\'t enable *styling hints* like ~these~\n'+
  184. '</code><div class="styling-directive">```</div>'
  185. );
  186. msg_text = "```\nignored\n(println \"Hello, world!\")\n```\nThis should show up as monospace, preformatted text ^";
  187. msg = mock.createChatMessage(_converse, contact_jid, msg_text)
  188. await _converse.handleMessageStanza(msg);
  189. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  190. msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
  191. expect(msg_el.innerText).toBe(msg_text);
  192. await u.waitUntil(() => msg_el.innerHTML.replace(/<!---->/g, '') ===
  193. '<div class="styling-directive">```</div>'+
  194. '<code class="block">ignored\n(println "Hello, world!")\n</code>'+
  195. '<div class="styling-directive">```</div>\n'+
  196. 'This should show up as monospace, preformatted text ^');
  197. msg_text = "```ignored\n (println \"Hello, world!\")\n ```\n\n This should not show up as monospace, *preformatted* text ^";
  198. msg = mock.createChatMessage(_converse, contact_jid, msg_text)
  199. await _converse.handleMessageStanza(msg);
  200. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  201. msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
  202. expect(msg_el.innerText).toBe(msg_text);
  203. await u.waitUntil(() => msg_el.innerHTML.replace(/<!---->/g, '') ===
  204. '```ignored\n (println "Hello, world!")\n ```\n\n'+
  205. ' This should not show up as monospace, '+
  206. '<span class="styling-directive">*</span><b>preformatted</b><span class="styling-directive">*</span> text ^');
  207. done();
  208. }));
  209. it("can be styled with quote XEP-0393 message styling hints",
  210. mock.initConverse(['rosterContactsFetched', 'chatBoxesFetched'], {},
  211. async function (done, _converse) {
  212. let msg_text, msg, msg_el;
  213. await mock.waitForRoster(_converse, 'current', 1);
  214. const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
  215. await mock.openChatBoxFor(_converse, contact_jid);
  216. const view = _converse.api.chatviews.get(contact_jid);
  217. msg_text = `> This is quoted text\n>This is also quoted\nThis is not quoted`;
  218. msg = mock.createChatMessage(_converse, contact_jid, msg_text)
  219. await _converse.handleMessageStanza(msg);
  220. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  221. msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
  222. expect(msg_el.innerText).toBe(msg_text);
  223. await u.waitUntil(() => msg_el.innerHTML.replace(/<!---->/g, '') ===
  224. '<blockquote>This is quoted text\nThis is also quoted</blockquote>\nThis is not quoted');
  225. msg_text = `> This is *quoted* text\n>This is \`also _quoted_\`\nThis is not quoted`;
  226. msg = mock.createChatMessage(_converse, contact_jid, msg_text)
  227. await _converse.handleMessageStanza(msg);
  228. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  229. msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
  230. expect(msg_el.innerText).toBe(msg_text);
  231. await u.waitUntil(() => msg_el.innerHTML.replace(/<!---->/g, '') ===
  232. '<blockquote>This is <span class="styling-directive">*</span><b>quoted</b><span class="styling-directive">*</span> text\n'+
  233. 'This is <span class="styling-directive">`</span><code>also _quoted_</code><span class="styling-directive">`</span></blockquote>\n'+
  234. 'This is not quoted');
  235. msg_text = `> > This is doubly quoted text`;
  236. msg = mock.createChatMessage(_converse, contact_jid, msg_text)
  237. await _converse.handleMessageStanza(msg);
  238. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  239. msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
  240. expect(msg_el.innerText).toBe(msg_text);
  241. await u.waitUntil(() => msg_el.innerHTML.replace(/<!---->/g, '') === "<blockquote><blockquote>This is doubly quoted text</blockquote></blockquote>");
  242. msg_text = `>> This is doubly quoted text`;
  243. msg = mock.createChatMessage(_converse, contact_jid, msg_text)
  244. await _converse.handleMessageStanza(msg);
  245. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  246. msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
  247. expect(msg_el.innerText).toBe(msg_text);
  248. await u.waitUntil(() => msg_el.innerHTML.replace(/<!---->/g, '') === "<blockquote><blockquote>This is doubly quoted text</blockquote></blockquote>");
  249. msg_text = ">```\n>ignored\n> <span></span> (println \"Hello, world!\")\n>```\n> This should show up as monospace, preformatted text ^";
  250. msg = mock.createChatMessage(_converse, contact_jid, msg_text)
  251. await _converse.handleMessageStanza(msg);
  252. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  253. msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
  254. expect(msg_el.innerText).toBe(msg_text);
  255. await u.waitUntil(() => msg_el.innerHTML.replace(/<!---->/g, '') ===
  256. '<blockquote>'+
  257. '<div class="styling-directive">```</div>'+
  258. '<code class="block">ignored\n &lt;span&gt;&lt;/span&gt; (println "Hello, world!")\n'+
  259. '</code><div class="styling-directive">```</div>\n'+
  260. ' This should show up as monospace, preformatted text ^'+
  261. '</blockquote>');
  262. msg_text = '> ```\n> (println "Hello, world!")\n\nThe entire blockquote is a preformatted text block, but this line is plaintext!';
  263. msg = mock.createChatMessage(_converse, contact_jid, msg_text)
  264. await _converse.handleMessageStanza(msg);
  265. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  266. msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
  267. expect(msg_el.innerText).toBe(msg_text);
  268. await u.waitUntil(() => msg_el.innerHTML.replace(/<!---->/g, '') ===
  269. '<blockquote>```\n (println "Hello, world!")</blockquote>\n\n'+
  270. 'The entire blockquote is a preformatted text block, but this line is plaintext!');
  271. msg_text = '> Also, icons.js is loaded from /dist, instead of dist.\nhttps://conversejs.org/docs/html/configuration.html#assets-path'
  272. msg = mock.createChatMessage(_converse, contact_jid, msg_text)
  273. await _converse.handleMessageStanza(msg);
  274. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  275. msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
  276. expect(msg_el.innerText).toBe(msg_text);
  277. await u.waitUntil(() => msg_el.innerHTML.replace(/<!---->/g, '') ===
  278. '<blockquote>Also, icons.js is loaded from /dist, instead of dist.</blockquote>\n'+
  279. '<a target="_blank" rel="noopener" href="https://conversejs.org/docs/html/configuration.html#assets-path">https://conversejs.org/docs/html/configuration.html#assets-path</a>');
  280. msg_text = '> Where is it located?\ngeo:37.786971,-122.399677';
  281. msg = mock.createChatMessage(_converse, contact_jid, msg_text)
  282. await _converse.handleMessageStanza(msg);
  283. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  284. msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
  285. expect(msg_el.innerText).toBe(msg_text);
  286. await u.waitUntil(() => msg_el.innerHTML.replace(/<!---->/g, '') ===
  287. '<blockquote>Where is it located?</blockquote>\n'+
  288. '<a target="_blank" rel="noopener" '+
  289. 'href="https://www.openstreetmap.org/?mlat=37.786971&amp;mlon=-122.399677#map=18/37.786971/-122.399677">https://www.openstreetmap.org/?mlat=37.786971&amp;mlon=-122.399677#map=18/37.786971/-122.399677</a>');
  290. msg_text = '> What do you think of it?\n :poop:';
  291. msg = mock.createChatMessage(_converse, contact_jid, msg_text)
  292. await _converse.handleMessageStanza(msg);
  293. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  294. msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
  295. expect(msg_el.innerText).toBe(msg_text);
  296. await u.waitUntil(() => msg_el.innerHTML.replace(/<!---->/g, '') ===
  297. '<blockquote>What do you think of it?</blockquote>\n <span title=":poop:">💩</span>');
  298. msg_text = '> What do you think of it?\n~hello~';
  299. msg = mock.createChatMessage(_converse, contact_jid, msg_text)
  300. await _converse.handleMessageStanza(msg);
  301. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  302. msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
  303. expect(msg_el.innerText).toBe(msg_text);
  304. await u.waitUntil(() => msg_el.innerHTML.replace(/<!---->/g, '') ===
  305. '<blockquote>What do you think of it?</blockquote>\n<span class="styling-directive">~</span><del>hello</del><span class="styling-directive">~</span>');
  306. msg_text = 'hello world > this is not a quote';
  307. msg = mock.createChatMessage(_converse, contact_jid, msg_text)
  308. await _converse.handleMessageStanza(msg);
  309. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  310. msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
  311. expect(msg_el.innerText).toBe(msg_text);
  312. await u.waitUntil(() => msg_el.innerHTML.replace(/<!---->/g, '') === 'hello world &gt; this is not a quote');
  313. msg_text = '> What do you think of it romeo?\n Did you see this romeo?';
  314. msg = $msg({
  315. from: contact_jid,
  316. to: _converse.connection.jid,
  317. type: 'chat',
  318. id: (new Date()).getTime()
  319. }).c('body').t(msg_text).up()
  320. .c('reference', {
  321. 'xmlns':'urn:xmpp:reference:0',
  322. 'begin':'26',
  323. 'end':'31',
  324. 'type':'mention',
  325. 'uri': _converse.bare_jid
  326. })
  327. .c('reference', {
  328. 'xmlns':'urn:xmpp:reference:0',
  329. 'begin':'51',
  330. 'end':'56',
  331. 'type':'mention',
  332. 'uri': _converse.bare_jid
  333. }).nodeTree;
  334. await _converse.handleMessageStanza(msg);
  335. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  336. msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
  337. expect(msg_el.innerText).toBe(msg_text);
  338. await u.waitUntil(() => msg_el.innerHTML.replace(/<!---->/g, '') ===
  339. `<blockquote>What do you think of it <span class="mention">romeo</span>?</blockquote>\n Did you see this <span class="mention">romeo</span>?`);
  340. done();
  341. }));
  342. });
  343. describe("A outgoing groupchat Message", function () {
  344. it("can be styled with span XEP-0393 message styling hints that contain mentions",
  345. mock.initConverse(['rosterContactsFetched', 'chatBoxesFetched'], {},
  346. async function (done, _converse) {
  347. const muc_jid = 'lounge@montague.lit';
  348. await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
  349. const view = _converse.api.chatviews.get(muc_jid);
  350. const msg_text = "This *message mentions romeo*";
  351. const msg = $msg({
  352. from: 'lounge@montague.lit/gibson',
  353. id: u.getUniqueId(),
  354. to: 'romeo@montague.lit',
  355. type: 'groupchat'
  356. }).c('body').t(msg_text).up()
  357. .c('reference', {'xmlns':'urn:xmpp:reference:0', 'begin':'23', 'end':'29', 'type':'mention', 'uri':'xmpp:romeo@montague.lit'}).nodeTree;
  358. await view.model.handleMessageStanza(msg);
  359. const message = await u.waitUntil(() => view.querySelector('.chat-msg__text'));
  360. expect(message.classList.length).toEqual(1);
  361. const msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
  362. expect(msg_el.innerText).toBe(msg_text);
  363. await u.waitUntil(() => msg_el.innerHTML.replace(/<!---->/g, '') ===
  364. 'This <span class="styling-directive">*</span><b>message mentions <span class="mention mention--self badge badge-info">romeo</span></b><span class="styling-directive">*</span>');
  365. done();
  366. }));
  367. });