Add message parser tests

This commit is contained in:
Bonuspunkt 2017-03-18 10:18:47 +02:00 committed by Pavel Djundik
parent 0b85ded53f
commit eb1360c3af
5 changed files with 826 additions and 0 deletions

View file

@ -0,0 +1,30 @@
"use strict";
const expect = require("chai").expect;
const anyIntersection = require("../../../../../../client/js/libs/handlebars/ircmessageparser/anyIntersection");
describe("anyIntersection", () => {
it("should not intersect on edges", () => {
const a = {start: 1, end: 2};
const b = {start: 2, end: 3};
expect(anyIntersection(a, b)).to.equal(false);
expect(anyIntersection(b, a)).to.equal(false);
});
it("should intersect on overlapping", () => {
const a = {start: 0, end: 3};
const b = {start: 1, end: 2};
expect(anyIntersection(a, b)).to.equal(true);
expect(anyIntersection(b, a)).to.equal(true);
});
it("should not intersect", () => {
const a = {start: 0, end: 1};
const b = {start: 2, end: 3};
expect(anyIntersection(a, b)).to.equal(false);
expect(anyIntersection(b, a)).to.equal(false);
});
});

View file

@ -0,0 +1,123 @@
"use strict";
const expect = require("chai").expect;
const analyseText = require("../../../../../../client/js/libs/handlebars/ircmessageparser/findChannels");
describe("findChannels", () => {
it("should find single letter channel", () => {
const input = "#a";
const expected = [{
channel: "#a",
start: 0,
end: 2
}];
const actual = analyseText(input, ["#"], ["@", "+"]);
expect(actual).to.deep.equal(expected);
});
it("should find utf8 channels", () => {
const input = "#äöü";
const expected = [{
channel: "#äöü",
start: 0,
end: 4
}];
const actual = analyseText(input, ["#"], ["@", "+"]);
expect(actual).to.deep.equal(expected);
});
it("should find inline channel", () => {
const input = "inline #channel text";
const expected = [{
channel: "#channel",
start: 7,
end: 15
}];
const actual = analyseText(input, ["#"], ["@", "+"]);
expect(actual).to.deep.equal(expected);
});
it("should stop at \\0x07", () => {
const input = "#chan\x07nel";
const expected = [{
channel: "#chan",
start: 0,
end: 5
}];
const actual = analyseText(input, ["#"], ["@", "+"]);
expect(actual).to.deep.equal(expected);
});
it("should allow classics pranks", () => {
const input = "#1,000";
const expected = [{
channel: "#1,000",
start: 0,
end: 6
}];
const actual = analyseText(input, ["#"], ["@", "+"]);
expect(actual).to.deep.equal(expected);
});
it("should work with whois reponses", () => {
const input = "@#a";
const expected = [{
channel: "#a",
start: 1,
end: 3
}];
const actual = analyseText(input, ["#"], ["@", "+"]);
expect(actual).to.deep.equal(expected);
});
it("should work with IRCv3.1 multi-prefix", () => {
const input = "!@%+#a";
const expected = [{
channel: "#a",
start: 4,
end: 6
}];
const actual = analyseText(input, ["#"], ["!", "@", "%", "+"]);
expect(actual).to.deep.equal(expected);
});
it("should work with custom channelPrefixes", () => {
const input = "@a";
const expected = [{
channel: "@a",
start: 0,
end: 2
}];
const actual = analyseText(input, ["@"], ["#", "+"]);
expect(actual).to.deep.equal(expected);
});
it("should handle multiple channelPrefix correctly", () => {
const input = "##test";
const expected = [{
channel: "##test",
start: 0,
end: 6
}];
const actual = analyseText(input, ["#"], ["@", "+"]);
expect(actual).to.deep.equal(expected);
});
});

View file

@ -0,0 +1,63 @@
"use strict";
const expect = require("chai").expect;
const merge = require("../../../../../../client/js/libs/handlebars/ircmessageparser/merge");
describe("merge", () => {
it("should split style information", () => {
const textParts = [{
start: 0,
end: 10,
flag1: true
}, {
start: 10,
end: 20,
flag2: true
}];
const styleFragments = [{
start: 0,
end: 5,
text: "01234"
}, {
start: 5,
end: 15,
text: "5678901234"
}, {
start: 15,
end: 20,
text: "56789"
}];
const expected = [{
start: 0,
end: 10,
flag1: true,
fragments: [{
start: 0,
end: 5,
text: "01234"
}, {
start: 5,
end: 10,
text: "56789"
}]
}, {
start: 10,
end: 20,
flag2: true,
fragments: [{
start: 10,
end: 15,
text: "01234"
}, {
start: 15,
end: 20,
text: "56789"
}]
}];
const actual = merge(textParts, styleFragments);
expect(actual).to.deep.equal(expected);
});
});

View file

@ -0,0 +1,274 @@
"use strict";
const expect = require("chai").expect;
const parseStyle = require("../../../../../../client/js/libs/handlebars/ircmessageparser/parseStyle");
describe("parseStyle", () => {
it("should skip control codes", () => {
const input = "text\x01with\x04control\x05codes";
const expected = [{
bold: false,
textColor: undefined,
bgColor: undefined,
reverse: false,
italic: false,
underline: false,
text: "textwithcontrolcodes",
start: 0,
end: 20
}];
const actual = parseStyle(input);
expect(actual).to.deep.equal(expected);
});
it("should parse bold", () => {
const input = "\x02bold";
const expected = [{
bold: true,
textColor: undefined,
bgColor: undefined,
reverse: false,
italic: false,
underline: false,
text: "bold",
start: 0,
end: 4
}];
const actual = parseStyle(input);
expect(actual).to.deep.equal(expected);
});
it("should parse textColor", () => {
const input = "\x038yellowText";
const expected = [{
bold: false,
textColor: 8,
bgColor: undefined,
reverse: false,
italic: false,
underline: false,
text: "yellowText",
start: 0,
end: 10
}];
const actual = parseStyle(input);
expect(actual).to.deep.equal(expected);
});
it("should parse textColor and background", () => {
const input = "\x034,8yellowBG redText";
const expected = [{
textColor: 4,
bgColor: 8,
bold: false,
reverse: false,
italic: false,
underline: false,
text: "yellowBG redText",
start: 0,
end: 16
}];
const actual = parseStyle(input);
expect(actual).to.deep.equal(expected);
});
it("should parse italic", () => {
const input = "\x1ditalic";
const expected = [{
bold: false,
textColor: undefined,
bgColor: undefined,
reverse: false,
italic: true,
underline: false,
text: "italic",
start: 0,
end: 6
}];
const actual = parseStyle(input);
expect(actual).to.deep.equal(expected);
});
it("should carry state corretly forward", () => {
const input = "\x02bold\x038yellow\x02nonBold\x03default";
const expected = [{
bold: true,
textColor: undefined,
bgColor: undefined,
reverse: false,
italic: false,
underline: false,
text: "bold",
start: 0,
end: 4
}, {
bold: true,
textColor: 8,
bgColor: undefined,
reverse: false,
italic: false,
underline: false,
text: "yellow",
start: 4,
end: 10
}, {
bold: false,
textColor: 8,
bgColor: undefined,
reverse: false,
italic: false,
underline: false,
text: "nonBold",
start: 10,
end: 17
}, {
bold: false,
textColor: undefined,
bgColor: undefined,
reverse: false,
italic: false,
underline: false,
text: "default",
start: 17,
end: 24
}];
const actual = parseStyle(input);
expect(actual).to.deep.equal(expected);
});
it("should toggle bold correctly", () => {
const input = "\x02bold\x02 \x02bold\x02";
const expected = [{
bold: true,
textColor: undefined,
bgColor: undefined,
reverse: false,
italic: false,
underline: false,
text: "bold",
start: 0,
end: 4
}, {
bold: false,
textColor: undefined,
bgColor: undefined,
reverse: false,
italic: false,
underline: false,
text: " ",
start: 4,
end: 5
}, {
bold: true,
textColor: undefined,
bgColor: undefined,
reverse: false,
italic: false,
underline: false,
text: "bold",
start: 5,
end: 9
}];
const actual = parseStyle(input);
expect(actual).to.deep.equal(expected);
});
it("should reset all styles", () => {
const input = "\x02\x034\x16\x1d\x1ffull\x0fnone";
const expected = [{
bold: true,
textColor: 4,
bgColor: undefined,
reverse: true,
italic: true,
underline: true,
text: "full",
start: 0,
end: 4
}, {
bold: false,
textColor: undefined,
bgColor: undefined,
reverse: false,
italic: false,
underline: false,
text: "none",
start: 4,
end: 8
}];
const actual = parseStyle(input);
expect(actual).to.deep.equal(expected);
});
it("should not emit empty fragments", () => {
const input = "\x031\x031,2\x031\x031,2\x031\x031,2\x03a";
const expected = [{
bold: false,
textColor: undefined,
bgColor: undefined,
reverse: false,
italic: false,
underline: false,
text: "a",
start: 0,
end: 1
}];
const actual = parseStyle(input);
expect(actual).to.deep.equal(expected);
});
it("should optimize fragments", () => {
const rawString = "oh hi test text";
const colorCode = "\x0312";
const input = colorCode + rawString.split("").join(colorCode);
const expected = [{
bold: false,
textColor: 12,
bgColor: undefined,
reverse: false,
italic: false,
underline: false,
text: rawString,
start: 0,
end: rawString.length
}];
const actual = parseStyle(input);
expect(actual).to.deep.equal(expected);
});
});

View file

@ -0,0 +1,336 @@
"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: "<img onerror='location.href=\"//youtube.com\"'>",
expected: "&lt;img onerror&#x3D;&#x27;location.href&#x3D;&quot;//youtube.com&quot;&#x27;&gt;"
}, {
input: "#&\">bug",
expected: "<span class='inline-channel' role='button' tabindex='0' data-chan='#&amp;&quot;&gt;bug'>#&amp;&quot;&gt;bug</span>"
}];
const actual = testCases.map(testCase => parse(testCase.input));
const expected = testCases.map(testCase => testCase.expected);
expect(actual).to.deep.equal(expected);
});
it("should skip control codes", () => {
const testCases = [{
input: "text\x01with\x04control\x05codes",
expected: "textwithcontrolcodes"
}];
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:
"<a href='irc://freenode.net/thelounge' target='_blank' rel='noopener'>" +
"irc://freenode.net/thelounge" +
"</a>"
}, {
input: "www.nooooooooooooooo.com",
expected:
"<a href='http://www.nooooooooooooooo.com' target='_blank' rel='noopener'>" +
"www.nooooooooooooooo.com" +
"</a>"
}, {
input: "look at https://thelounge.github.io/ for more information",
expected:
"look at " +
"<a href='https://thelounge.github.io/' target='_blank' rel='noopener'>" +
"https://thelounge.github.io/" +
"</a>" +
" for more information",
}, {
input: "use www.duckduckgo.com for privacy reasons",
expected:
"use " +
"<a href='http://www.duckduckgo.com' target='_blank' rel='noopener'>" +
"www.duckduckgo.com" +
"</a>" +
" for privacy reasons"
}, {
input: "svn+ssh://example.org",
expected:
"<a href='svn+ssh://example.org' target='_blank' rel='noopener'>" +
"svn+ssh://example.org" +
"</a>"
}];
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: " +
"<a href='https://msdn.microsoft.com/en-us/library/windows/desktop/ms644989(v&#x3D;vs.85).aspx' target='_blank' rel='noopener'>" +
"https://msdn.microsoft.com/en-us/library/windows/desktop/ms644989(v&#x3D;vs.85).aspx" +
"</a>";
const actual = parse(input);
expect(actual).to.deep.equal(correctResult);
});
it("should balance brackets", () => {
const testCases = [{
input: "<https://theos.kyriasis.com/~kyrias/stats/archlinux.html>",
expected:
"&lt;" +
"<a href='https://theos.kyriasis.com/~kyrias/stats/archlinux.html' target='_blank' rel='noopener'>" +
"https://theos.kyriasis.com/~kyrias/stats/archlinux.html" +
"</a>" +
"&gt;"
}, {
input: "abc (www.example.com)",
expected:
"abc (" +
"<a href='http://www.example.com' target='_blank' rel='noopener'>" +
"www.example.com" +
"</a>" +
")"
}, {
input: "http://example.com/Test_(Page)",
expected:
"<a href='http://example.com/Test_(Page)' target='_blank' rel='noopener'>" +
"http://example.com/Test_(Page)" +
"</a>"
}, {
input: "www.example.com/Test_(Page)",
expected:
"<a href='http://www.example.com/Test_(Page)' target='_blank' rel='noopener'>" +
"www.example.com/Test_(Page)" +
"</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 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:
"<span class='inline-channel' role='button' tabindex='0' data-chan='#a'>" +
"#a" +
"</span>"
}, {
input: "#test",
expected:
"<span class='inline-channel' role='button' tabindex='0' data-chan='#test'>" +
"#test" +
"</span>"
}, {
input: "#äöü",
expected:
"<span class='inline-channel' role='button' tabindex='0' data-chan='#äöü'>" +
"#äöü" +
"</span>"
}, {
input: "inline #channel text",
expected:
"inline " +
"<span class='inline-channel' role='button' tabindex='0' data-chan='#channel'>" +
"#channel" +
"</span>" +
" text"
}, {
input: "#1,000",
expected:
"<span class='inline-channel' role='button' tabindex='0' data-chan='#1,000'>" +
"#1,000" +
"</span>"
}, {
input: "@#a",
expected:
"@" +
"<span class='inline-channel' role='button' tabindex='0' data-chan='#a'>" +
"#a" +
"</span>"
}];
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);
});
it("should style like mirc", () => {
const testCases = [{
input: "\x02bold",
expected: "<span class='irc-bold'>bold</span>"
}, {
input: "\x038yellowText",
expected: "<span class='irc-fg8'>yellowText</span>"
}, {
input: "\x030,0white,white",
expected: "<span class='irc-fg0 irc-bg0'>white,white</span>"
}, {
input: "\x034,8yellowBGredText",
expected: "<span class='irc-fg4 irc-bg8'>yellowBGredText</span>"
}, {
input: "\x1ditalic",
expected: "<span class='irc-italic'>italic</span>"
}, {
input: "\x1funderline",
expected: "<span class='irc-underline'>underline</span>"
}, {
input: "\x02bold\x038yellow\x02nonBold\x03default",
expected:
"<span class='irc-bold'>bold</span>" +
"<span class='irc-bold irc-fg8'>yellow</span>" +
"<span class='irc-fg8'>nonBold</span>" +
"default"
}, {
input: "\x02bold\x02 \x02bold\x02",
expected:
"<span class='irc-bold'>bold</span>" +
" " +
"<span class='irc-bold'>bold</span>"
}];
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:
"<a href='irc://freenode.net/thelounge' target='_blank' rel='noopener'>" +
"<span class='irc-bold'>irc</span>" +
"://" +
"<span class='irc-italic'>freenode.net</span>" +
"/" +
"<span class='irc-fg4 irc-bg8'>thelounge</span>" +
"</a>"
}, {
input: "\x02#\x038,9thelounge",
expected:
"<span class='inline-channel' role='button' tabindex='0' data-chan='#thelounge'>" +
"<span class='irc-bold'>#</span>" +
"<span class='irc-bold irc-fg8 irc-bg9'>thelounge</span>" +
"</span>"
}];
const actual = testCases.map(testCase => parse(testCase.input));
const expected = testCases.map(testCase => testCase.expected);
expect(actual).to.deep.equal(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 " +
"<span class='inline-channel' role='button' tabindex='0' data-chan='#&quot;testa'>" +
"<span class='irc-fg12'>#&quot;testa</span>" +
"</span>"
}];
const actual = testCases.map(testCase => parse(testCase.input));
const expected = testCases.map(testCase => testCase.expected);
expect(actual).to.deep.equal(expected);
});
it("should trim commom protocols", () => {
const testCases = [{
input: "like..http://example.com",
expected:
"like.." +
"<a href='http://example.com' target='_blank' rel='noopener'>" +
"http://example.com" +
"</a>"
}, {
input: "like..HTTP://example.com",
expected:
"like.." +
"<a href='HTTP://example.com' target='_blank' rel='noopener'>" +
"HTTP://example.com" +
"</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 channel in fragment", () => {
const testCases = [{
input: "http://example.com/#hash",
expected:
"" +
"<a href='http://example.com/#hash' target='_blank' rel='noopener'>" +
"http://example.com/#hash" +
"</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 overlap parts", () => {
const input = "Url: http://example.com/path Channel: ##channel";
const actual = parse(input);
expect(actual).to.equal(
"Url: <a href='http://example.com/path' target='_blank' rel='noopener'>http://example.com/path</a> " +
"Channel: <span class='inline-channel' role='button' tabindex='0' data-chan='##channel'>##channel</span>"
);
});
});