emojis.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. (function (root, factory) {
  2. define([
  3. "jasmine",
  4. "mock",
  5. "test-utils"
  6. ], factory);
  7. } (this, function (jasmine, mock, test_utils) {
  8. "use strict";
  9. const { Promise, $msg, $pres, sizzle } = converse.env;
  10. const u = converse.env.utils;
  11. describe("Emojis", function () {
  12. describe("The emoji picker", function () {
  13. it("can be opened by clicking a button in the chat toolbar",
  14. mock.initConverse(
  15. ['rosterGroupsFetched', 'chatBoxesFetched'], {},
  16. async function (done, _converse) {
  17. const contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@montague.lit';
  18. await test_utils.waitForRoster(_converse, 'current');
  19. await test_utils.openControlBox(_converse);
  20. await test_utils.openChatBoxFor(_converse, contact_jid);
  21. const view = _converse.chatboxviews.get(contact_jid);
  22. const toolbar = await u.waitUntil(() => view.el.querySelector('ul.chat-toolbar'));
  23. expect(toolbar.querySelectorAll('li.toggle-smiley__container').length).toBe(1);
  24. toolbar.querySelector('a.toggle-smiley').click();
  25. await u.waitUntil(() => u.isVisible(view.el.querySelector('.emoji-picker__lists')), 1000);
  26. const picker = await u.waitUntil(() => view.el.querySelector('.emoji-picker__container'), 1000);
  27. const item = await u.waitUntil(() => picker.querySelector('.emoji-picker li.insert-emoji a'), 1000);
  28. item.click()
  29. expect(view.el.querySelector('textarea.chat-textarea').value).toBe(':smiley: ');
  30. toolbar.querySelector('a.toggle-smiley').click(); // Close the panel again
  31. done();
  32. }));
  33. it("is opened to autocomplete emojis in the textarea",
  34. mock.initConverse(
  35. ['rosterGroupsFetched', 'chatBoxesFetched'], {},
  36. async function (done, _converse) {
  37. const muc_jid = 'lounge@montague.lit';
  38. await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
  39. const view = _converse.chatboxviews.get(muc_jid);
  40. const textarea = view.el.querySelector('textarea.chat-textarea');
  41. textarea.value = ':gri';
  42. // Press tab
  43. const tab_event = {
  44. 'target': textarea,
  45. 'preventDefault': function preventDefault () {},
  46. 'stopPropagation': function stopPropagation () {},
  47. 'keyCode': 9,
  48. 'key': 'Tab'
  49. }
  50. view.onKeyDown(tab_event);
  51. await u.waitUntil(() => u.isVisible(view.el.querySelector('.emoji-picker__lists')));
  52. let picker = await u.waitUntil(() => view.el.querySelector('.emoji-picker__container'));
  53. const input = picker.querySelector('.emoji-search');
  54. expect(input.value).toBe(':gri');
  55. let visible_emojis = sizzle('.emojis-lists__container--search .insert-emoji', picker);
  56. expect(visible_emojis.length).toBe(3);
  57. expect(visible_emojis[0].getAttribute('data-emoji')).toBe(':grimacing:');
  58. expect(visible_emojis[1].getAttribute('data-emoji')).toBe(':grin:');
  59. expect(visible_emojis[2].getAttribute('data-emoji')).toBe(':grinning:');
  60. // Test that TAB autocompletes the to first match
  61. view.emoji_picker_view.onKeyDown(tab_event);
  62. visible_emojis = sizzle('.emojis-lists__container--search .insert-emoji', picker);
  63. expect(visible_emojis.length).toBe(1);
  64. expect(visible_emojis[0].getAttribute('data-emoji')).toBe(':grimacing:');
  65. expect(input.value).toBe(':grimacing:');
  66. // Check that ENTER now inserts the match
  67. const enter_event = Object.assign({}, tab_event, {'keyCode': 13, 'key': 'Enter', 'target': input});
  68. view.emoji_picker_view.onKeyDown(enter_event);
  69. expect(input.value).toBe('');
  70. expect(textarea.value).toBe(':grimacing: ');
  71. // Test that username starting with : doesn't cause issues
  72. const presence = $pres({
  73. 'from': `${muc_jid}/:username`,
  74. 'id': '27C55F89-1C6A-459A-9EB5-77690145D624',
  75. 'to': _converse.jid
  76. })
  77. .c('x', { 'xmlns': 'http://jabber.org/protocol/muc#user'})
  78. .c('item', {
  79. 'jid': 'some1@montague.lit',
  80. 'affiliation': 'member',
  81. 'role': 'participant'
  82. });
  83. _converse.connection._dataRecv(test_utils.createRequest(presence));
  84. textarea.value = ':use';
  85. view.onKeyDown(tab_event);
  86. await u.waitUntil(() => u.isVisible(view.el.querySelector('.emoji-picker__lists')));
  87. picker = await u.waitUntil(() => view.el.querySelector('.emoji-picker__container'));
  88. expect(input.value).toBe(':use');
  89. visible_emojis = sizzle('.insert-emoji:not(.hidden)', picker);
  90. expect(visible_emojis.length).toBe(0);
  91. done();
  92. }));
  93. it("allows you to search for particular emojis",
  94. mock.initConverse(
  95. ['rosterGroupsFetched', 'chatBoxesFetched'], {},
  96. async function (done, _converse) {
  97. const muc_jid = 'lounge@montague.lit';
  98. await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
  99. const view = _converse.chatboxviews.get(muc_jid);
  100. const toolbar = view.el.querySelector('ul.chat-toolbar');
  101. expect(toolbar.querySelectorAll('.toggle-smiley__container').length).toBe(1);
  102. toolbar.querySelector('.toggle-smiley').click();
  103. await u.waitUntil(() => u.isVisible(view.el.querySelector('.emoji-picker__lists')));
  104. const picker = await u.waitUntil(() => view.el.querySelector('.emoji-picker__container'));
  105. const input = picker.querySelector('.emoji-search');
  106. expect(sizzle('.insert-emoji:not(.hidden)', picker).length).toBe(1589);
  107. expect(view.emoji_picker_view.model.get('query')).toBeUndefined();
  108. input.value = 'smiley';
  109. const event = {
  110. 'target': input,
  111. 'preventDefault': function preventDefault () {},
  112. 'stopPropagation': function stopPropagation () {}
  113. };
  114. view.emoji_picker_view.onKeyDown(event);
  115. await u.waitUntil(() => view.emoji_picker_view.model.get('query') === 'smiley');
  116. let visible_emojis = sizzle('.emojis-lists__container--search .insert-emoji', picker);
  117. expect(visible_emojis.length).toBe(2);
  118. expect(visible_emojis[0].getAttribute('data-emoji')).toBe(':smiley:');
  119. expect(visible_emojis[1].getAttribute('data-emoji')).toBe(':smiley_cat:');
  120. // Check that pressing enter without an unambiguous match does nothing
  121. const enter_event = Object.assign({}, event, {'keyCode': 13});
  122. view.emoji_picker_view.onKeyDown(enter_event);
  123. expect(input.value).toBe('smiley');
  124. // Test that TAB autocompletes the to first match
  125. const tab_event = Object.assign({}, event, {'keyCode': 9, 'key': 'Tab'});
  126. view.emoji_picker_view.onKeyDown(tab_event);
  127. expect(input.value).toBe(':smiley:');
  128. visible_emojis = sizzle('.emojis-lists__container--search .insert-emoji', picker);
  129. expect(visible_emojis.length).toBe(1);
  130. expect(visible_emojis[0].getAttribute('data-emoji')).toBe(':smiley:');
  131. // Check that ENTER now inserts the match
  132. view.emoji_picker_view.onKeyDown(enter_event);
  133. expect(input.value).toBe('');
  134. expect(view.el.querySelector('textarea.chat-textarea').value).toBe(':smiley: ');
  135. done();
  136. }));
  137. });
  138. describe("A Chat Message", function () {
  139. it("will display larger if it's only emojis",
  140. mock.initConverse(
  141. ['rosterGroupsFetched', 'chatBoxesFetched'], {'use_system_emojis': true},
  142. async function (done, _converse) {
  143. await test_utils.waitForRoster(_converse, 'current');
  144. const sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@montague.lit';
  145. _converse.handleMessageStanza($msg({
  146. 'from': sender_jid,
  147. 'to': _converse.connection.jid,
  148. 'type': 'chat',
  149. 'id': _converse.connection.getUniqueId()
  150. }).c('body').t('😇').up()
  151. .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree());
  152. await new Promise(resolve => _converse.on('chatBoxViewInitialized', resolve));
  153. const view = _converse.api.chatviews.get(sender_jid);
  154. await new Promise(resolve => view.once('messageInserted', resolve));
  155. const chat_content = view.el.querySelector('.chat-content');
  156. let message = chat_content.querySelector('.chat-msg__text');
  157. expect(u.hasClass('chat-msg__text--larger', message)).toBe(true);
  158. _converse.handleMessageStanza($msg({
  159. 'from': sender_jid,
  160. 'to': _converse.connection.jid,
  161. 'type': 'chat',
  162. 'id': _converse.connection.getUniqueId()
  163. }).c('body').t('😇 Hello world! 😇 😇').up()
  164. .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree());
  165. await new Promise(resolve => view.once('messageInserted', resolve));
  166. message = chat_content.querySelector('.message:last-child .chat-msg__text');
  167. expect(u.hasClass('chat-msg__text--larger', message)).toBe(false);
  168. // Test that a modified message that no longer contains only
  169. // emojis now renders normally again.
  170. const textarea = view.el.querySelector('textarea.chat-textarea');
  171. textarea.value = ':poop: :innocent:';
  172. view.onKeyDown({
  173. target: textarea,
  174. preventDefault: function preventDefault () {},
  175. keyCode: 13 // Enter
  176. });
  177. await new Promise(resolve => view.once('messageInserted', resolve));
  178. expect(view.el.querySelectorAll('.chat-msg').length).toBe(3);
  179. expect(chat_content.querySelector('.message:last-child .chat-msg__text').textContent).toBe('💩 😇');
  180. expect(textarea.value).toBe('');
  181. view.onKeyDown({
  182. target: textarea,
  183. keyCode: 38 // Up arrow
  184. });
  185. expect(textarea.value).toBe('💩 😇');
  186. expect(view.model.messages.at(2).get('correcting')).toBe(true);
  187. await u.waitUntil(() => u.hasClass('correcting', view.el.querySelector('.chat-msg:last-child')), 500);
  188. textarea.value = textarea.value += 'This is no longer an emoji-only message';
  189. view.onKeyDown({
  190. target: textarea,
  191. preventDefault: function preventDefault () {},
  192. keyCode: 13 // Enter
  193. });
  194. await new Promise(resolve => view.model.messages.once('rendered', resolve));
  195. expect(view.model.messages.models.length).toBe(3);
  196. message = chat_content.querySelector('.message:last-child .chat-msg__text');
  197. expect(u.hasClass('chat-msg__text--larger', message)).toBe(false);
  198. textarea.value = ':smile: Hello world!';
  199. view.onKeyDown({
  200. target: textarea,
  201. preventDefault: function preventDefault () {},
  202. keyCode: 13 // Enter
  203. });
  204. await new Promise(resolve => view.once('messageInserted', resolve));
  205. textarea.value = ':smile: :smiley: :imp:';
  206. view.onKeyDown({
  207. target: textarea,
  208. preventDefault: function preventDefault () {},
  209. keyCode: 13 // Enter
  210. });
  211. await new Promise(resolve => view.once('messageInserted', resolve));
  212. message = chat_content.querySelector('.message:last-child .chat-msg__text');
  213. expect(u.hasClass('chat-msg__text--larger', message)).toBe(true);
  214. done()
  215. }));
  216. });
  217. });
  218. }));