spoilers.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. /* global mock, converse */
  2. const original_timeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
  3. describe("A spoiler message", function () {
  4. beforeEach(() => (jasmine.DEFAULT_TIMEOUT_INTERVAL = 7000));
  5. afterEach(() => (jasmine.DEFAULT_TIMEOUT_INTERVAL = original_timeout));
  6. it("can be received with a hint",
  7. mock.initConverse(['chatBoxesFetched'], {}, async (done, _converse) => {
  8. await mock.waitForRoster(_converse, 'current');
  9. const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
  10. /* <message to='romeo@montague.net/orchard' from='juliet@capulet.net/balcony' id='spoiler2'>
  11. * <body>And at the end of the story, both of them die! It is so tragic!</body>
  12. * <spoiler xmlns='urn:xmpp:spoiler:0'>Love story end</spoiler>
  13. * </message>
  14. */
  15. const spoiler_hint = "Love story end"
  16. const spoiler = "And at the end of the story, both of them die! It is so tragic!";
  17. const $msg = converse.env.$msg;
  18. const u = converse.env.utils;
  19. const msg = $msg({
  20. 'xmlns': 'jabber:client',
  21. 'to': _converse.bare_jid,
  22. 'from': sender_jid,
  23. 'type': 'chat'
  24. }).c('body').t(spoiler).up()
  25. .c('spoiler', {
  26. 'xmlns': 'urn:xmpp:spoiler:0',
  27. }).t(spoiler_hint)
  28. .tree();
  29. _converse.connection._dataRecv(mock.createRequest(msg));
  30. await new Promise(resolve => _converse.api.listen.once('chatBoxViewInitialized', resolve));
  31. const view = _converse.chatboxviews.get(sender_jid);
  32. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  33. await u.waitUntil(() => view.model.vcard.get('fullname') === 'Mercutio')
  34. expect(view.querySelector('.chat-msg__author').textContent.trim()).toBe('Mercutio');
  35. const message_content = view.querySelector('.chat-msg__text');
  36. await u.waitUntil(() => message_content.textContent === spoiler);
  37. const spoiler_hint_el = view.querySelector('.spoiler-hint');
  38. expect(spoiler_hint_el.textContent).toBe(spoiler_hint);
  39. done();
  40. }));
  41. it("can be received without a hint",
  42. mock.initConverse(['chatBoxesFetched'], {}, async (done, _converse) => {
  43. await mock.waitForRoster(_converse, 'current');
  44. const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
  45. /* <message to='romeo@montague.net/orchard' from='juliet@capulet.net/balcony' id='spoiler2'>
  46. * <body>And at the end of the story, both of them die! It is so tragic!</body>
  47. * <spoiler xmlns='urn:xmpp:spoiler:0'>Love story end</spoiler>
  48. * </message>
  49. */
  50. const $msg = converse.env.$msg;
  51. const u = converse.env.utils;
  52. const spoiler = "And at the end of the story, both of them die! It is so tragic!";
  53. const msg = $msg({
  54. 'xmlns': 'jabber:client',
  55. 'to': _converse.bare_jid,
  56. 'from': sender_jid,
  57. 'type': 'chat'
  58. }).c('body').t(spoiler).up()
  59. .c('spoiler', {
  60. 'xmlns': 'urn:xmpp:spoiler:0',
  61. }).tree();
  62. _converse.connection._dataRecv(mock.createRequest(msg));
  63. await new Promise(resolve => _converse.api.listen.once('chatBoxViewInitialized', resolve));
  64. const view = _converse.chatboxviews.get(sender_jid);
  65. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  66. await u.waitUntil(() => u.isVisible(view));
  67. await u.waitUntil(() => view.model.vcard.get('fullname') === 'Mercutio')
  68. await u.waitUntil(() => u.isVisible(view.querySelector('.chat-msg__author')));
  69. expect(view.querySelector('.chat-msg__author').textContent.includes('Mercutio')).toBeTruthy();
  70. const message_content = view.querySelector('.chat-msg__text');
  71. await u.waitUntil(() => message_content.textContent === spoiler);
  72. const spoiler_hint_el = view.querySelector('.spoiler-hint');
  73. expect(spoiler_hint_el.textContent).toBe('');
  74. done();
  75. }));
  76. it("can be sent without a hint",
  77. mock.initConverse(['chatBoxesFetched'], {}, async (done, _converse) => {
  78. await mock.waitForRoster(_converse, 'current', 1);
  79. mock.openControlBox(_converse);
  80. const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
  81. const { $pres, Strophe} = converse.env;
  82. const u = converse.env.utils;
  83. // XXX: We need to send a presence from the contact, so that we
  84. // have a resource, that resource is then queried to see
  85. // whether Strophe.NS.SPOILER is supported, in which case
  86. // the spoiler button will appear.
  87. const presence = $pres({
  88. 'from': contact_jid+'/phone',
  89. 'to': 'romeo@montague.lit'
  90. });
  91. _converse.connection._dataRecv(mock.createRequest(presence));
  92. await mock.openChatBoxFor(_converse, contact_jid);
  93. await mock.waitUntilDiscoConfirmed(_converse, contact_jid+'/phone', [], [Strophe.NS.SPOILER]);
  94. const view = _converse.api.chatviews.get(contact_jid);
  95. spyOn(_converse.connection, 'send');
  96. await u.waitUntil(() => view.querySelector('.toggle-compose-spoiler'));
  97. let spoiler_toggle = view.querySelector('.toggle-compose-spoiler');
  98. spoiler_toggle.click();
  99. const textarea = view.querySelector('.chat-textarea');
  100. textarea.value = 'This is the spoiler';
  101. const bottom_panel = view.querySelector('converse-chat-bottom-panel');
  102. bottom_panel.onKeyDown({
  103. target: textarea,
  104. preventDefault: function preventDefault () {},
  105. keyCode: 13
  106. });
  107. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  108. /* Test the XML stanza
  109. *
  110. * <message from="romeo@montague.lit/orchard"
  111. * to="max.frankfurter@montague.lit"
  112. * type="chat"
  113. * id="4547c38b-d98b-45a5-8f44-b4004dbc335e"
  114. * xmlns="jabber:client">
  115. * <body>This is the spoiler</body>
  116. * <active xmlns="http://jabber.org/protocol/chatstates"/>
  117. * <spoiler xmlns="urn:xmpp:spoiler:0"/>
  118. * </message>"
  119. */
  120. const stanza = _converse.connection.send.calls.argsFor(0)[0].tree();
  121. const spoiler_el = stanza.querySelector('spoiler[xmlns="urn:xmpp:spoiler:0"]');
  122. expect(spoiler_el === null).toBeFalsy();
  123. expect(spoiler_el.textContent).toBe('');
  124. const spoiler = 'This is the spoiler';
  125. const body_el = stanza.querySelector('body');
  126. expect(body_el.textContent).toBe(spoiler);
  127. /* Test the HTML spoiler message */
  128. expect(view.querySelector('.chat-msg__author').textContent.trim()).toBe('Romeo Montague');
  129. const message_content = view.querySelector('.chat-msg__text');
  130. await u.waitUntil(() => message_content.textContent === spoiler);
  131. const spoiler_msg_el = view.querySelector('.chat-msg__text.spoiler');
  132. expect(Array.from(spoiler_msg_el.classList).includes('hidden')).toBeTruthy();
  133. spoiler_toggle = view.querySelector('.spoiler-toggle');
  134. expect(spoiler_toggle.textContent.trim()).toBe('Show more');
  135. spoiler_toggle.click();
  136. await u.waitUntil(() => !Array.from(spoiler_msg_el.classList).includes('hidden'));
  137. expect(spoiler_toggle.textContent.trim()).toBe('Show less');
  138. spoiler_toggle.click();
  139. await u.waitUntil(() => Array.from(spoiler_msg_el.classList).includes('hidden'));
  140. done();
  141. }));
  142. it("can be sent with a hint",
  143. mock.initConverse(['chatBoxesFetched'], {}, async (done, _converse) => {
  144. await mock.waitForRoster(_converse, 'current', 1);
  145. mock.openControlBox(_converse);
  146. const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
  147. const { $pres, Strophe} = converse.env;
  148. const u = converse.env.utils;
  149. // XXX: We need to send a presence from the contact, so that we
  150. // have a resource, that resource is then queried to see
  151. // whether Strophe.NS.SPOILER is supported, in which case
  152. // the spoiler button will appear.
  153. const presence = $pres({
  154. 'from': contact_jid+'/phone',
  155. 'to': 'romeo@montague.lit'
  156. });
  157. _converse.connection._dataRecv(mock.createRequest(presence));
  158. await mock.openChatBoxFor(_converse, contact_jid);
  159. await mock.waitUntilDiscoConfirmed(_converse, contact_jid+'/phone', [], [Strophe.NS.SPOILER]);
  160. const view = _converse.api.chatviews.get(contact_jid);
  161. await u.waitUntil(() => view.querySelector('.toggle-compose-spoiler'));
  162. let spoiler_toggle = view.querySelector('.toggle-compose-spoiler');
  163. spoiler_toggle.click();
  164. spyOn(_converse.connection, 'send');
  165. const textarea = view.querySelector('.chat-textarea');
  166. textarea.value = 'This is the spoiler';
  167. const hint_input = view.querySelector('.spoiler-hint');
  168. hint_input.value = 'This is the hint';
  169. const bottom_panel = view.querySelector('converse-chat-bottom-panel');
  170. bottom_panel.onKeyDown({
  171. target: textarea,
  172. preventDefault: function preventDefault () {},
  173. keyCode: 13
  174. });
  175. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  176. const stanza = _converse.connection.send.calls.argsFor(0)[0].tree();
  177. expect(Strophe.serialize(stanza)).toBe(
  178. `<message from="romeo@montague.lit/orchard" ` +
  179. `id="${stanza.getAttribute('id')}" `+
  180. `to="mercutio@montague.lit" `+
  181. `type="chat" `+
  182. `xmlns="jabber:client">`+
  183. `<body>This is the spoiler</body>`+
  184. `<active xmlns="http://jabber.org/protocol/chatstates"/>`+
  185. `<request xmlns="urn:xmpp:receipts"/>`+
  186. `<spoiler xmlns="urn:xmpp:spoiler:0">This is the hint</spoiler>`+
  187. `<origin-id id="${stanza.querySelector('origin-id').getAttribute('id')}" xmlns="urn:xmpp:sid:0"/>`+
  188. `</message>`
  189. );
  190. await u.waitUntil(() => stanza.querySelector('spoiler[xmlns="urn:xmpp:spoiler:0"]')?.textContent === 'This is the hint');
  191. const spoiler = 'This is the spoiler'
  192. const body_el = stanza.querySelector('body');
  193. expect(body_el.textContent).toBe(spoiler);
  194. expect(view.querySelector('.chat-msg__author').textContent.trim()).toBe('Romeo Montague');
  195. const message_content = view.querySelector('.chat-msg__text');
  196. await u.waitUntil(() => message_content.textContent === spoiler);
  197. const spoiler_msg_el = view.querySelector('.chat-msg__text.spoiler');
  198. expect(Array.from(spoiler_msg_el.classList).includes('hidden')).toBeTruthy();
  199. spoiler_toggle = view.querySelector('.spoiler-toggle');
  200. expect(spoiler_toggle.textContent.trim()).toBe('Show more');
  201. spoiler_toggle.click();
  202. await u.waitUntil(() => !Array.from(spoiler_msg_el.classList).includes('hidden'));
  203. expect(spoiler_toggle.textContent.trim()).toBe('Show less');
  204. spoiler_toggle.click();
  205. await u.waitUntil(() => Array.from(spoiler_msg_el.classList).includes('hidden'));
  206. done();
  207. }));
  208. });