spoilers.js 11 KB

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