filters.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. afterEach(function () {
  2. jasmine.clock().uninstall();
  3. });
  4. describe('Filters', function() {
  5. let $filter;
  6. // Ignoring page reload request
  7. beforeAll(() => window.onbeforeunload = () => null);
  8. let webClientServiceMock = {
  9. me: {
  10. id: 'MEMEMEME',
  11. displayName: 'Er'
  12. },
  13. contacts: {
  14. get: function(id) {
  15. if (id === 'AAAAAAAA') {
  16. return {
  17. displayName: 'ContactA'
  18. }
  19. }
  20. else if (id === 'XXXXXXXX') {
  21. return {
  22. displayName: 'ContactX'
  23. }
  24. }
  25. else if (id === '*AAAAAAA') {
  26. return {
  27. displayName: 'GWContactA'
  28. }
  29. }
  30. else if (id === 'BAD0BAD1') {
  31. return {
  32. displayName: '<b>< script >foo&ndash;</b>< script>',
  33. }
  34. }
  35. return null;
  36. }
  37. }
  38. };
  39. let translationMock = {
  40. instant: function(label) {
  41. return label;
  42. }
  43. };
  44. beforeEach(function() {
  45. // Load 3ema.filters module
  46. module('3ema.filters');
  47. module(function ($provide) {
  48. $provide.value('WebClientService', webClientServiceMock);
  49. $provide.value('$translate', translationMock);
  50. });
  51. // Inject the $filter function
  52. inject(function(_$filter_) {
  53. $filter = _$filter_;
  54. });
  55. });
  56. function testPatterns(filterName, cases) {
  57. const filter = $filter(filterName);
  58. for (let testcase of cases) {
  59. const input = testcase[0];
  60. const expected = testcase[1];
  61. expect(filter(input)).toEqual(expected);
  62. };
  63. };
  64. describe('markify', function() {
  65. this.testPatterns = (cases) => testPatterns('markify', cases);
  66. it('detects bold text', () => {
  67. this.testPatterns([
  68. ['*bold text (not italic)*',
  69. '<span class="text-bold">bold text (not italic)</span>'],
  70. ]);
  71. });
  72. it('detects italic text', () => {
  73. this.testPatterns([
  74. ['This text is not italic.',
  75. 'This text is not italic.'],
  76. ['_This text is italic._',
  77. '<span class="text-italic">This text is italic.</span>'],
  78. ['This text is _partially_ italic',
  79. 'This text is <span class="text-italic">partially</span> italic'],
  80. ['This text has _two_ _italic_ bits',
  81. 'This text has <span class="text-italic">two</span> <span class="text-italic">italic</span> bits'],
  82. ]);
  83. });
  84. it('detects strikethrough text', () => {
  85. this.testPatterns([
  86. ['so ~strikethrough~', 'so <span class="text-strike">strikethrough</span>'],
  87. ]);
  88. });
  89. it('detects mixed markup', () => {
  90. this.testPatterns([
  91. ['*bold text with _italic_ *',
  92. '<span class="text-bold">bold text with <span class="text-italic">italic</span> </span>'],
  93. ['*part bold,* _part italic_',
  94. '<span class="text-bold">part bold,</span> <span class="text-italic">part italic</span>'],
  95. ['_italic text with *bold* _',
  96. '<span class="text-italic">italic text with <span class="text-bold">bold</span> </span>'],
  97. ]);
  98. });
  99. it('is only applied on word boundaries', () => {
  100. this.testPatterns([
  101. ['so not_really_italic',
  102. 'so not_really_italic'],
  103. ['invalid*bold*stuff',
  104. 'invalid*bold*stuff'],
  105. ['no~strike~through',
  106. 'no~strike~through'],
  107. ['*bold_but_no~strike~through*',
  108. '<span class="text-bold">bold_but_no~strike~through</span>'],
  109. ]);
  110. });
  111. it('does not break URLs', () => {
  112. this.testPatterns([
  113. ['https://en.wikipedia.org/wiki/Java_class_file *nice*',
  114. 'https://en.wikipedia.org/wiki/Java_class_file <span class="text-bold">nice</span>'],
  115. ['<a href="https://threema.ch/>_Threema_</a>',
  116. '<a href="https://threema.ch/><span class="text-italic">Threema</span></a>'],
  117. ]);
  118. });
  119. it('ignores invalid markup', () => {
  120. this.testPatterns([
  121. ['*invalid markup (do not parse)_', '*invalid markup (do not parse)_'],
  122. ['random *asterisk', 'random *asterisk'],
  123. ]);
  124. });
  125. it('ignores markup with \\n (newline)', () => {
  126. this.testPatterns([
  127. ['*First line\n and a new one. (do not parse)*', '*First line\n and a new one. (do not parse)*'],
  128. ['*\nbegins with linebreak. (do not parse)*', '*\nbegins with linebreak. (do not parse)*'],
  129. ['*Just some text. But it ends with newline (do not parse)\n*', '*Just some text. But it ends with newline (do not parse)\n*'],
  130. ]);
  131. });
  132. });
  133. describe('escapeHtml', function() {
  134. this.testPatterns = (cases) => testPatterns('escapeHtml', cases);
  135. it('escapes html tags', () => {
  136. this.testPatterns([
  137. ['<h1>heading</h1>', '&lt;h1&gt;heading&lt;/h1&gt;'],
  138. ['<b>< script >foo&ndash;</b>< script>', '&lt;b&gt;&lt; script &gt;foo&amp;ndash;&lt;/b&gt;&lt; script&gt;'],
  139. ['<a href="/">a</a>', '&lt;a href=&quot;/&quot;&gt;a&lt;/a&gt;'],
  140. ]);
  141. });
  142. });
  143. describe('mentionify', function() {
  144. this.testPatterns = (cases) => testPatterns('mentionify', cases);
  145. it('no mentions', () => {
  146. this.testPatterns([
  147. ['', ''],
  148. ['hello my friend', 'hello my friend'],
  149. ['@[AAAAAAA]', '@[AAAAAAA]'],
  150. ['this is not a valid @[AAAAAAA]', 'this is not a valid @[AAAAAAA]'],
  151. ['@[@@@@@@@]', '@[@@@@@@@]'],
  152. ['this is not a valid @[@@@@@@@]', 'this is not a valid @[@@@@@@@]'],
  153. ]);
  154. });
  155. it('mention - no contacts', () => {
  156. this.testPatterns([
  157. ['@[BBBBBBBB]', '@[BBBBBBBB]'],
  158. ['@[*BBBBBBB]', '@[*BBBBBBB]'],
  159. ]);
  160. });
  161. it('mention - contact', () => {
  162. this.testPatterns([
  163. ['@[AAAAAAAA]', '<span class="mention id AAAAAAAA" text="@[AAAAAAAA]">ContactA</span>'],
  164. ['hello @[AAAAAAAA]. @[AAAAAAAA] you are my friend', 'hello <span class="mention id AAAAAAAA" text="@[AAAAAAAA]">ContactA</span>. <span class="mention id AAAAAAAA" text="@[AAAAAAAA]">ContactA</span> you are my friend'],
  165. ['@[AAAAAAAA] @[AAAAAAAA] @[AAAAAAAA]', '<span class="mention id AAAAAAAA" text="@[AAAAAAAA]">ContactA</span> <span class="mention id AAAAAAAA" text="@[AAAAAAAA]">ContactA</span> <span class="mention id AAAAAAAA" text="@[AAAAAAAA]">ContactA</span>']
  166. ]);
  167. });
  168. it('mention - all', () => {
  169. this.testPatterns([
  170. ['@[@@@@@@@@]', '<span class="mention all" text="@[@@@@@@@@]">messenger.ALL</span>'],
  171. ['@[@@@@@@@@] your base are belong to us', '<span class="mention all" text="@[@@@@@@@@]">messenger.ALL</span> your base are belong to us'],
  172. ['@[@@@@@@@@] @[@@@@@@@@] @[@@@@@@@@]', '<span class="mention all" text="@[@@@@@@@@]">messenger.ALL</span> <span class="mention all" text="@[@@@@@@@@]">messenger.ALL</span> <span class="mention all" text="@[@@@@@@@@]">messenger.ALL</span>']
  173. ]);
  174. });
  175. it('mention - mixed', () => {
  176. this.testPatterns([
  177. ['@[@@@@@@@@] @[AAAAAAAA] @[BBBBBBBB]', '<span class="mention all" text="@[@@@@@@@@]">messenger.ALL</span> <span class="mention id AAAAAAAA" text="@[AAAAAAAA]">ContactA</span> @[BBBBBBBB]'],
  178. ]);
  179. });
  180. it('mention - me contact', () => {
  181. this.testPatterns([
  182. ['@[MEMEMEME]', '<span class="mention me" text="@[MEMEMEME]">Er</span>'],
  183. ['hello @[MEMEMEME]. @[MEMEMEME] you are my friend', 'hello <span class="mention me" text="@[MEMEMEME]">Er</span>. <span class="mention me" text="@[MEMEMEME]">Er</span> you are my friend'],
  184. ['@[MEMEMEME] @[MEMEMEME] @[MEMEMEME]', '<span class="mention me" text="@[MEMEMEME]">Er</span> <span class="mention me" text="@[MEMEMEME]">Er</span> <span class="mention me" text="@[MEMEMEME]">Er</span>']
  185. ]);
  186. });
  187. it('mention - escape html parameters', () => {
  188. this.testPatterns([
  189. ['@[BAD0BAD1]', '<span class="mention id BAD0BAD1" text="@[BAD0BAD1]">&lt;b&gt;&lt; script &gt;foo&amp;ndash;&lt;/b&gt;&lt; script&gt;</span>'],
  190. ]);
  191. });
  192. });
  193. describe('nlToBr', function() {
  194. this.testPatterns = (cases) => testPatterns('nlToBr', cases);
  195. it('converts newlines (enabled=true)', () => {
  196. const filter = $filter('nlToBr');
  197. expect(filter('abc \n def', true)).toEqual('abc <br> def');
  198. expect(filter('a\nb\nc\\n', true)).toEqual('a<br>b<br>c\\n');
  199. });
  200. it('does not converts newlines (enabled=false)', () => {
  201. const filter = $filter('nlToBr');
  202. expect(filter('abc\ndef', false)).toEqual('abc\ndef');
  203. });
  204. it('if enabled flag is not set, converts newlines', () => {
  205. const filter = $filter('nlToBr');
  206. expect(filter('abc\ndef')).toEqual('abc<br>def');
  207. });
  208. });
  209. describe('unixToTimestring', function() {
  210. this.testPatterns = (cases) => testPatterns('unixToTimestring', cases);
  211. it('shows only time for today', () => {
  212. const d1 = new Date(); d1.setHours(8); d1.setMinutes(7);
  213. const d2 = new Date(); d2.setHours(12); d2.setMinutes(14);
  214. const d3 = new Date(); d3.setHours(0); d3.setMinutes(0);
  215. this.testPatterns([
  216. [d1.getTime() / 1000, '08:07'],
  217. [d2.getTime() / 1000, '12:14'],
  218. [d3.getTime() / 1000, '00:00'],
  219. ]);
  220. });
  221. it('shows full date with forceFull flag', () => {
  222. const d = new Date();
  223. const formatted = $filter('unixToTimestring')(d.getTime() / 1000, true);
  224. expect(formatted.length > 10).toBe(true);
  225. expect(formatted).toContain(d.getFullYear().toString());
  226. });
  227. it('shows "yesterday" for yesterday', () => {
  228. const d1 = new Date();
  229. const ts = d1.getTime();
  230. const d2 = new Date(ts - 1000 * 60 * 60 * 24);
  231. d2.setHours(8); d2.setMinutes(7);
  232. this.testPatterns([
  233. [d2.getTime() / 1000, 'date.YESTERDAY, 08:07'],
  234. ]);
  235. });
  236. it('shows full datetime for other days', () => {
  237. jasmine.clock().install();
  238. jasmine.clock().mockDate(new Date(2018, 9, 9, 20, 42));
  239. const now = new Date();
  240. const d1 = new Date(2010, 1, 7, 18, 42);
  241. const d2 = new Date(now.getFullYear(), 4, 2, 23, 59);
  242. this.testPatterns([
  243. [d1.getTime() / 1000, '7. date.month_short.FEB 2010, 18:42'],
  244. [d2.getTime() / 1000, '2. date.month_short.MAY, 23:59'],
  245. ]);
  246. });
  247. });
  248. describe('dndModeSimplified', function() {
  249. let process = (dnd, sound) => {
  250. return $filter('dndModeSimplified')({
  251. notifications: {
  252. dnd: dnd,
  253. sound: sound,
  254. },
  255. })
  256. };
  257. it('dnd enabled', () => {
  258. expect(process(
  259. {mode: 'on'},
  260. {mode: 'default'},
  261. )).toEqual('on');
  262. });
  263. it('dnd enabled (no sound)', () => {
  264. expect(process(
  265. {mode: 'on'},
  266. {mode: 'muted'},
  267. )).toEqual('on');
  268. });
  269. it('dnd disabled', () => {
  270. expect(process(
  271. {mode: 'off'},
  272. {mode: 'default'},
  273. )).toEqual('off');
  274. });
  275. it('dnd disabled (no sound)', () => {
  276. expect(process(
  277. {mode: 'off'},
  278. {mode: 'muted'},
  279. )).toEqual('off');
  280. });
  281. it('mention only', () => {
  282. expect(process(
  283. {mode: 'mention'},
  284. {mode: 'default'},
  285. )).toEqual('mention');
  286. });
  287. it('mention only (no sound)', () => {
  288. expect(process(
  289. {mode: 'mention'},
  290. {mode: 'muted'},
  291. )).toEqual('mention');
  292. });
  293. it('until (not expired)', () => {
  294. jasmine.clock().install();
  295. jasmine.clock().mockDate(new Date(2018, 9, 9, 20, 42));
  296. expect(process(
  297. {mode: 'until', until: +(new Date(2018, 9, 9, 20, 50))},
  298. {mode: 'default'},
  299. )).toEqual('on');
  300. });
  301. it('until (expired)', () => {
  302. jasmine.clock().install();
  303. jasmine.clock().mockDate(new Date(2018, 9, 9, 20, 42));
  304. expect(process(
  305. {mode: 'until', until: +(new Date(2018, 9, 9, 19, 50))},
  306. {mode: 'default'},
  307. )).toEqual('off');
  308. });
  309. });
  310. });