afterEach(function () { jasmine.clock().uninstall(); }); describe('Filters', function() { let $filter; // Ignoring page reload request beforeAll(() => window.onbeforeunload = () => null); let webClientServiceMock = { me: { id: 'MEMEMEME', displayName: 'Er' }, contacts: { get: function(id) { if (id === 'AAAAAAAA') { return { displayName: 'ContactA' } } else if (id === 'XXXXXXXX') { return { displayName: 'ContactX' } } else if (id === '*AAAAAAA') { return { displayName: 'GWContactA' } } else if (id === 'BAD0BAD1') { return { displayName: '< script >foo–< script>', } } return null; } } }; let translationMock = { instant: function(label) { return label; } }; beforeEach(function() { module('3ema.services'); module('3ema.filters'); module(function($provide) { $provide.value('WebClientService', webClientServiceMock); $provide.value('$translate', translationMock); $provide.constant('$state', null); }); // Inject the $filter function inject(function(_$filter_) { $filter = _$filter_; }); }); function testPatterns(filterName, cases) { const filter = $filter(filterName); for (let testcase of cases) { const input = testcase[0]; const expected = testcase[1]; expect(filter(input)).toEqual(expected); }; }; describe('markify', function() { this.testPatterns = (cases) => testPatterns('markify', cases); it('detects bold text', () => { this.testPatterns([ ['*bold text (not italic)*', 'bold text (not italic)'], ]); }); it('detects italic text', () => { this.testPatterns([ ['This text is not italic.', 'This text is not italic.'], ['_This text is italic._', 'This text is italic.'], ['This text is _partially_ italic', 'This text is partially italic'], ['This text has _two_ _italic_ bits', 'This text has two italic bits'], ]); }); it('detects strikethrough text', () => { this.testPatterns([ ['so ~strikethrough~', 'so strikethrough'], ]); }); it('detects mixed markup', () => { this.testPatterns([ ['*bold text with _italic_ *', 'bold text with italic '], ['*part bold,* _part italic_', 'part bold, part italic'], ['_italic text with *bold* _', 'italic text with bold '], ]); }); it('is only applied on word boundaries', () => { this.testPatterns([ ['so not_really_italic', 'so not_really_italic'], ['invalid*bold*stuff', 'invalid*bold*stuff'], ['no~strike~through', 'no~strike~through'], ['*bold_but_no~strike~through*', 'bold_but_no~strike~through'], ]); }); it('does not break URLs', () => { this.testPatterns([ ['https://en.wikipedia.org/wiki/Java_class_file *nice*', 'https://en.wikipedia.org/wiki/Java_class_file nice'], ['_Threema_', 'Threema'], ]); }); it('ignores invalid markup', () => { this.testPatterns([ ['*invalid markup (do not parse)_', '*invalid markup (do not parse)_'], ['random *asterisk', 'random *asterisk'], ]); }); it('ignores markup with \\n (newline)', () => { this.testPatterns([ ['*First line\n and a new one. (do not parse)*', '*First line\n and a new one. (do not parse)*'], ['*\nbegins with linebreak. (do not parse)*', '*\nbegins with linebreak. (do not parse)*'], ['*Just some text. But it ends with newline (do not parse)\n*', '*Just some text. But it ends with newline (do not parse)\n*'], ]); }); }); describe('escapeHtml', function() { this.testPatterns = (cases) => testPatterns('escapeHtml', cases); it('escapes html tags', () => { this.testPatterns([ ['

heading

', '<h1>heading</h1>'], ['< script >foo–< script>', '<b>< script >foo&ndash;</b>< script>'], ['a', '<a href="/">a</a>'], ]); }); }); describe('mentionify', function() { this.testPatterns = (cases) => testPatterns('mentionify', cases); it('no mentions', () => { this.testPatterns([ ['', ''], ['hello my friend', 'hello my friend'], ['@[AAAAAAA]', '@[AAAAAAA]'], ['this is not a valid @[AAAAAAA]', 'this is not a valid @[AAAAAAA]'], ['@[@@@@@@@]', '@[@@@@@@@]'], ['this is not a valid @[@@@@@@@]', 'this is not a valid @[@@@@@@@]'], ]); }); it('mention - no contacts', () => { this.testPatterns([ ['@[BBBBBBBB]', '@[BBBBBBBB]'], ['@[*BBBBBBB]', '@[*BBBBBBB]'], ]); }); it('mention - contact', () => { this.testPatterns([ ['@[AAAAAAAA]', 'ContactA'], ['hello @[AAAAAAAA]. @[AAAAAAAA] you are my friend', 'hello ContactA. ContactA you are my friend'], ['@[AAAAAAAA] @[AAAAAAAA] @[AAAAAAAA]', 'ContactA ContactA ContactA'] ]); }); it('mention - all', () => { this.testPatterns([ ['@[@@@@@@@@]', 'messenger.ALL'], ['@[@@@@@@@@] your base are belong to us', 'messenger.ALL your base are belong to us'], ['@[@@@@@@@@] @[@@@@@@@@] @[@@@@@@@@]', 'messenger.ALL messenger.ALL messenger.ALL'] ]); }); it('mention - mixed', () => { this.testPatterns([ ['@[@@@@@@@@] @[AAAAAAAA] @[BBBBBBBB]', 'messenger.ALL ContactA @[BBBBBBBB]'], ]); }); it('mention - me contact', () => { this.testPatterns([ ['@[MEMEMEME]', 'Er'], ['hello @[MEMEMEME]. @[MEMEMEME] you are my friend', 'hello Er. Er you are my friend'], ['@[MEMEMEME] @[MEMEMEME] @[MEMEMEME]', 'Er Er Er'] ]); }); it('mention - escape html parameters', () => { this.testPatterns([ ['@[BAD0BAD1]', '<b>< script >foo&ndash;</b>< script>'], ]); }); }); describe('nlToBr', function() { this.testPatterns = (cases) => testPatterns('nlToBr', cases); it('converts newlines (enabled=true)', () => { const filter = $filter('nlToBr'); expect(filter('abc \n def', true)).toEqual('abc
def'); expect(filter('a\nb\nc\\n', true)).toEqual('a
b
c\\n'); }); it('does not converts newlines (enabled=false)', () => { const filter = $filter('nlToBr'); expect(filter('abc\ndef', false)).toEqual('abc\ndef'); }); it('if enabled flag is not set, converts newlines', () => { const filter = $filter('nlToBr'); expect(filter('abc\ndef')).toEqual('abc
def'); }); }); describe('unixToTimestring', function() { this.testPatterns = (cases) => testPatterns('unixToTimestring', cases); it('shows only time for today', () => { const d1 = new Date(); d1.setHours(8); d1.setMinutes(7); const d2 = new Date(); d2.setHours(12); d2.setMinutes(14); const d3 = new Date(); d3.setHours(0); d3.setMinutes(0); this.testPatterns([ [d1.getTime() / 1000, '08:07'], [d2.getTime() / 1000, '12:14'], [d3.getTime() / 1000, '00:00'], ]); }); it('shows full date with forceFull flag', () => { const d = new Date(); const formatted = $filter('unixToTimestring')(d.getTime() / 1000, true); expect(formatted.length > 10).toBe(true); expect(formatted).toContain(d.getFullYear().toString()); }); it('shows "yesterday" for yesterday', () => { const d1 = new Date(); const ts = d1.getTime(); const d2 = new Date(ts - 1000 * 60 * 60 * 24); d2.setHours(8); d2.setMinutes(7); this.testPatterns([ [d2.getTime() / 1000, 'date.YESTERDAY, 08:07'], ]); }); it('shows full datetime for other days', () => { jasmine.clock().install(); jasmine.clock().mockDate(new Date(2018, 9, 9, 20, 42)); const now = new Date(); const d1 = new Date(2010, 1, 7, 18, 42); const d2 = new Date(now.getFullYear(), 4, 2, 23, 59); this.testPatterns([ [d1.getTime() / 1000, '7. date.month_short.FEB 2010, 18:42'], [d2.getTime() / 1000, '2. date.month_short.MAY, 23:59'], ]); }); }); describe('enlargeSingleEmoji', function() { let process = (text) => { return $filter('enlargeSingleEmoji')(text, true) }; const singleEmojiClassName = 'large-emoji'; const crazy = '🤪'; const crazyLarge = '🤪'; const copyright = '©️'; const copyrightLarge = '©️'; it('enlarges 1 emoji', () => { expect(process(crazy)).toEqual(crazyLarge); }); it('enlarges 2 emoji', () => { expect(process(crazy + copyright)).toEqual(crazyLarge + copyrightLarge); }); it('enlarges 3 emoji', () => { expect(process(crazy + copyright + crazy)).toEqual(crazyLarge + copyrightLarge + crazyLarge); }); it('does not enlarge 4 emoji', () => { expect(process(crazy + copyright + crazy + copyright)).toEqual(crazy + copyright + crazy + copyright); }); it('does not enlarge if non-emoji characters are contained', () => { expect(process(crazy + ' ')).toEqual(crazy + ' '); expect(process(crazy + 'a' + crazy)).toEqual(crazy + 'a' + crazy); }); it('does not modify non emoji text', () => { const text = 'emoji e1 e1-people hello'; expect(process(text)).toEqual(text); }); it('does nothing if enlarge flag is set to false', () => { expect($filter('enlargeSingleEmoji')(crazy, false)).toEqual(crazy); }); }); describe('linkify', function() { let process = (text) => { return $filter('linkify')(text) }; it('links http urls', () => { expect(process('hello https://threema.ch/!')) .toEqual('hello https://threema.ch!'); }); it('links e-mails', () => { expect(process('hello info@threema.ch!')) .toEqual('hello info@threema.ch!'); }); it('does not link phone numbers', () => { const input = 'hello +41791234567'; expect(process(input)).toEqual(input); }); it('does not link mentions', () => { const input = 'hello @threemaapp'; expect(process(input)).toEqual(input); }); it('does not link hashtags', () => { const input = 'hello #threema'; expect(process(input)).toEqual(input); }); }); });