"use strict"; const expect = require("chai").expect; const parse = require("../../../../../client/js/libs/handlebars/parse"); describe("parse Handlebars helper", () => { it("should not introduce xss", () => { const testCases = [{ input: "", expected: "<img onerror='location.href="//youtube.com"'>", }, { input: '#&">bug', expected: '#&">bug', }]; const actual = testCases.map((testCase) => parse(testCase.input)); const expected = testCases.map((testCase) => testCase.expected); expect(actual).to.deep.equal(expected); }); it("should skip all <32 ASCII codes except linefeed", () => { const testCases = [{ input: "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1B\x1D\x1D\x1E\x1Ftext\x0Awithcontrolcodestest", expected: 'text\nwithcontrolcodestest', }]; const actual = testCases.map((testCase) => parse(testCase.input)); const expected = testCases.map((testCase) => testCase.expected); expect(actual).to.deep.equal(expected); }); it("should find urls", () => { const testCases = [{ input: "irc://freenode.net/thelounge", expected: '' + "irc://freenode.net/thelounge" + "", }, { input: "www.nooooooooooooooo.com", expected: '' + "www.nooooooooooooooo.com" + "", }, { input: "look at https://thelounge.chat/ for more information", expected: "look at " + '' + "https://thelounge.chat/" + "" + " for more information", }, { input: "use www.duckduckgo.com for privacy reasons", expected: "use " + '' + "www.duckduckgo.com" + "" + " for privacy reasons", }, { input: "svn+ssh://example.org", expected: '' + "svn+ssh://example.org" + "", }]; const actual = testCases.map((testCase) => parse(testCase.input)); const expected = testCases.map((testCase) => testCase.expected); expect(actual).to.deep.equal(expected); }); it("url with a dot parsed correctly", () => { const input = "bonuspunkt: your URL parser misparses this URL: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644989(v=vs.85).aspx"; const correctResult = "bonuspunkt: your URL parser misparses this URL: " + '' + "https://msdn.microsoft.com/en-us/library/windows/desktop/ms644989(v=vs.85).aspx" + ""; const actual = parse(input); expect(actual).to.deep.equal(correctResult); }); it("should balance brackets", () => { const testCases = [{ input: "", expected: "<" + '' + "https://theos.kyriasis.com/~kyrias/stats/archlinux.html" + "" + ">", }, { input: "abc (www.example.com)", expected: "abc (" + '' + "www.example.com" + "" + ")", }, { input: "http://example.com/Test_(Page)", expected: '' + "http://example.com/Test_(Page)" + "", }, { input: "www.example.com/Test_(Page)", expected: '' + "www.example.com/Test_(Page)" + "", }]; const actual = testCases.map((testCase) => parse(testCase.input)); const expected = testCases.map((testCase) => testCase.expected); expect(actual).to.deep.equal(expected); }); it("should not find urls", () => { const testCases = [{ input: "text www. text", expected: "text www. text", }, { input: "http://.", expected: "http://.", }]; const actual = testCases.map((testCase) => parse(testCase.input)); const expected = testCases.map((testCase) => testCase.expected); expect(actual).to.deep.equal(expected); }); it("should find channels", () => { const testCases = [{ input: "#a", expected: '' + "#a" + "", }, { input: "#test", expected: '' + "#test" + "", }, { input: "#äöü", expected: '' + "#äöü" + "", }, { input: "inline #channel text", expected: "inline " + '' + "#channel" + "" + " text", }, { input: "#1,000", expected: '' + "#1,000" + "", }, { input: "@#a", expected: "@" + '' + "#a" + "", }]; const actual = testCases.map((testCase) => parse(testCase.input)); const expected = testCases.map((testCase) => testCase.expected); expect(actual).to.deep.equal(expected); }); it("should not find channels", () => { const testCases = [{ input: "hi#test", expected: "hi#test", }, { input: "#", expected: "#", }]; const actual = testCases.map((testCase) => parse(testCase.input)); const expected = testCases.map((testCase) => testCase.expected); expect(actual).to.deep.equal(expected); }); [{ name: "bold", input: "\x02bold", expected: 'bold', }, { name: "foreground color", input: "\x038yellowText", expected: 'yellowText', }, { name: "foreground and background colors (white)", input: "\x030,0white,white", expected: 'white,white', }, { name: "foreground and background colors", input: "\x034,8yellowBGredText", expected: 'yellowBGredText', }, { name: "hex foreground color", input: "\x04663399rebeccapurple", expected: 'rebeccapurple', }, { name: "hex foreground and background colors", input: "\x04415364,ff9e18The Lounge", expected: 'The Lounge', }, { name: "italic", input: "\x1ditalic", expected: 'italic', }, { name: "underline", input: "\x1funderline", expected: 'underline', }, { name: "strikethrough", input: "\x1estrikethrough", expected: 'strikethrough', }, { name: "monospace", input: "\x11monospace", expected: 'monospace', }, { name: "reverse with foreground and background colors", input: "\x0313,1fg and bg \x16and reversed", expected: 'fg and bg ' + 'and reversed', }, { name: "toggle reverse with foreground and background colors", input: "\x0305,11text \x16reversed and \x16back again and \x16reversed", expected: 'text ' + 'reversed and ' + 'back again and ' + 'reversed', }, { name: "escape reverse when new colors are applied", input: "\x0311,02text \x16 reversed \x0304,05 and new style", expected: 'text ' + ' reversed ' + ' and new style', }, { name: "resets", input: "\x02bold\x038yellow\x02nonBold\x03default", expected: 'bold' + 'yellow' + 'nonBold' + "default", }, { name: "duplicates", input: "\x02bold\x02 \x02bold\x02", expected: 'bold' + " " + 'bold', }].forEach((item) => { // TODO: In Node v6+, use `{name, input, expected}` it(`should handle style characters: ${item.name}`, function() { expect(parse(item.input)).to.equal(item.expected); }); }); it("should find nicks", () => { const testCases = [{ users: ["MaxLeiter"], input: "test, MaxLeiter", expected: "test, " + '' + "MaxLeiter" + "", }]; const actual = testCases.map((testCase) => parse(testCase.input, testCase.users)); const expected = testCases.map((testCase) => testCase.expected); expect(actual).to.deep.equal(expected); }); it("should not find nicks", () => { const testCases = [{ users: ["MaxLeiter, test"], input: "#test-channelMaxLeiter", expected: '' + "#test-channelMaxLeiter" + "", }, { users: ["MaxLeiter, test"], input: "https://www.MaxLeiter.com/test", expected: '' + "https://www.MaxLeiter.com/test" + "", }, ]; const actual = testCases.map((testCase) => parse(testCase.input)); const expected = testCases.map((testCase) => testCase.expected); expect(actual).to.deep.equal(expected); }); it("should go bonkers like mirc", () => { const testCases = [{ input: "\x02irc\x0f://\x1dfreenode.net\x0f/\x034,8thelounge", expected: '' + 'irc' + "://" + 'freenode.net' + "/" + 'thelounge' + "", }, { input: "\x02#\x038,9thelounge", expected: '' + '#' + 'thelounge' + "", }]; const actual = testCases.map((testCase) => parse(testCase.input)); const expected = testCases.map((testCase) => testCase.expected); expect(actual).to.deep.equal(expected); }); // Emoji [{ name: "in text", input: "Hello💬", expected: 'Hello💬', }, { name: "with modifiers", input: "🤷‍♀️", expected: '🤷‍♀️', }, { // FIXME: These multiple `span`s should be optimized into a single one. See https://github.com/thelounge/lounge/issues/1783 name: "wrapped in style", input: "Super \x034💚 green!", expected: 'Super 💚 green!', }, { name: "wrapped in URLs", input: "https://i.❤️.thelounge.chat", // FIXME: Emoji in text should be `❤️`. See https://github.com/thelounge/lounge/issues/1784 expected: 'https://i.❤️.thelounge.chat', }, { name: "wrapped in channels", input: "#i❤️thelounge", // FIXME: Emoji in text should be `❤️`. See https://github.com/thelounge/lounge/issues/1784 expected: '#i❤️thelounge', }].forEach((item) => { // TODO: In Node v6+, use `{name, input, expected}` it(`should find emoji: ${item.name}`, function() { expect(parse(item.input)).to.equal(item.expected); }); }); it("should optimize generated html", () => { const testCases = [{ input: 'test \x0312#\x0312\x0312"te\x0312st\x0312\x0312\x0312\x0312\x0312\x0312\x0312\x0312\x0312\x0312\x0312a', expected: "test " + '' + '#"testa' + "", }]; const actual = testCases.map((testCase) => parse(testCase.input)); const expected = testCases.map((testCase) => testCase.expected); expect(actual).to.deep.equal(expected); }); it("should trim common protocols", () => { const testCases = [{ input: "like..http://example.com", expected: "like.." + '' + "http://example.com" + "", }, { input: "like..HTTP://example.com", expected: "like.." + '' + "HTTP://example.com" + "", }]; const actual = testCases.map((testCase) => parse(testCase.input)); const expected = testCases.map((testCase) => testCase.expected); expect(actual).to.deep.equal(expected); }); it("should not find channel in fragment", () => { const testCases = [{ input: "http://example.com/#hash", expected: '' + "http://example.com/#hash" + "", }]; const actual = testCases.map((testCase) => parse(testCase.input)); const expected = testCases.map((testCase) => testCase.expected); expect(actual).to.deep.equal(expected); }); it("should not overlap parts", () => { const input = "Url: http://example.com/path Channel: ##channel"; const actual = parse(input); expect(actual).to.equal( 'Url: http://example.com/path ' + 'Channel: ##channel' ); }); it("should handle overlapping parts by using first starting", () => { const input = "#test-https://example.com"; const actual = parse(input); expect(actual).to.equal( '' + "#test-https://example.com" + "" ); }); });