diff --git a/package.json b/package.json
index b0b7949b..9bbb5282 100644
--- a/package.json
+++ b/package.json
@@ -74,6 +74,8 @@
"@babel/preset-env": "7.3.1",
"@fortawesome/fontawesome-free": "5.7.1",
"babel-loader": "8.0.5",
+ "@vue/server-test-utils": "1.0.0-beta.27",
+ "@vue/test-utils": "1.0.0-beta.27",
"chai": "4.2.0",
"copy-webpack-plugin": "4.6.0",
"css-loader": "0.28.11",
@@ -107,6 +109,7 @@
"undate": "0.3.0",
"vue": "2.5.17",
"vue-loader": "15.4.2",
+ "vue-server-renderer": "2.5.17",
"vue-template-compiler": "2.5.17",
"vuedraggable": "2.16.0",
"webpack": "4.29.3",
diff --git a/test/client/components/ParsedMessageTestWrapper.vue b/test/client/components/ParsedMessageTestWrapper.vue
new file mode 100644
index 00000000..9a5d9dc8
--- /dev/null
+++ b/test/client/components/ParsedMessageTestWrapper.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
diff --git a/test/client/js/libs/handlebars/parse.js b/test/client/js/libs/handlebars/parse.js
index 801d92f0..5849feef 100644
--- a/test/client/js/libs/handlebars/parse.js
+++ b/test/client/js/libs/handlebars/parse.js
@@ -1,19 +1,35 @@
"use strict";
const expect = require("chai").expect;
-const parse = require("../../../../../client/js/libs/handlebars/parse");
+
+import {renderToString} from "@vue/server-test-utils";
+import ParsedMessageTestWrapper from "../../../components/ParsedMessageTestWrapper.vue";
+
+function getParsedMessageContents(text, message) {
+ let contents = renderToString(ParsedMessageTestWrapper, {
+ propsData: {
+ text,
+ message,
+ },
+ });
+
+ // The wrapper adds a surrounding div to the message html, so we clean that out here
+ contents = contents.replace(/^
([^]+)<\/div>$/m, "$1");
+
+ return contents;
+}
describe("parse Handlebars helper", () => {
it("should not introduce xss", () => {
const testCases = [{
input: "
",
- expected: "<img onerror='location.href="
//youtube.com"'>",
+ expected: "<img onerror='location.href="
//youtube.com"'>",
}, {
input: '#&">bug',
- expected: '
#&">bug',
+ expected: '
#&">bug',
}];
- const actual = testCases.map((testCase) => parse(testCase.input));
+ const actual = testCases.map((testCase) => getParsedMessageContents(testCase.input));
const expected = testCases.map((testCase) => testCase.expected);
expect(actual).to.deep.equal(expected);
@@ -25,7 +41,7 @@ describe("parse Handlebars helper", () => {
expected: '
text\nwithcontrolcodestest',
}];
- const actual = testCases.map((testCase) => parse(testCase.input));
+ const actual = testCases.map((testCase) => getParsedMessageContents(testCase.input));
const expected = testCases.map((testCase) => testCase.expected);
expect(actual).to.deep.equal(expected);
@@ -68,7 +84,7 @@ describe("parse Handlebars helper", () => {
"",
}];
- const actual = testCases.map((testCase) => parse(testCase.input));
+ const actual = testCases.map((testCase) => getParsedMessageContents(testCase.input));
const expected = testCases.map((testCase) => testCase.expected);
expect(actual).to.deep.equal(expected);
@@ -79,11 +95,11 @@ describe("parse Handlebars helper", () => {
"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" +
+ '' +
+ "https://msdn.microsoft.com/en-us/library/windows/desktop/ms644989(v=vs.85).aspx" +
"";
- const actual = parse(input);
+ const actual = getParsedMessageContents(input);
expect(actual).to.deep.equal(correctResult);
});
@@ -119,7 +135,7 @@ describe("parse Handlebars helper", () => {
"",
}];
- const actual = testCases.map((testCase) => parse(testCase.input));
+ const actual = testCases.map((testCase) => getParsedMessageContents(testCase.input));
const expected = testCases.map((testCase) => testCase.expected);
expect(actual).to.deep.equal(expected);
@@ -134,7 +150,7 @@ describe("parse Handlebars helper", () => {
expected: "http://.",
}];
- const actual = testCases.map((testCase) => parse(testCase.input));
+ const actual = testCases.map((testCase) => getParsedMessageContents(testCase.input));
const expected = testCases.map((testCase) => testCase.expected);
expect(actual).to.deep.equal(expected);
@@ -144,45 +160,45 @@ describe("parse Handlebars helper", () => {
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 actual = testCases.map((testCase) => getParsedMessageContents(testCase.input));
const expected = testCases.map((testCase) => testCase.expected);
expect(actual).to.deep.equal(expected);
@@ -197,7 +213,7 @@ describe("parse Handlebars helper", () => {
expected: "#",
}];
- const actual = testCases.map((testCase) => parse(testCase.input));
+ const actual = testCases.map((testCase) => getParsedMessageContents(testCase.input));
const expected = testCases.map((testCase) => testCase.expected);
expect(actual).to.deep.equal(expected);
@@ -222,11 +238,11 @@ describe("parse Handlebars helper", () => {
}, {
name: "hex foreground color",
input: "\x04663399rebeccapurple",
- expected: 'rebeccapurple',
+ expected: 'rebeccapurple',
}, {
name: "hex foreground and background colors",
input: "\x04415364,ff9e18The Lounge",
- expected: 'The Lounge',
+ expected: 'The Lounge',
}, {
name: "italic",
input: "\x1ditalic",
@@ -281,22 +297,24 @@ describe("parse Handlebars helper", () => {
'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);
+ expect(getParsedMessageContents(item.input)).to.equal(item.expected);
});
});
it("should find nicks", () => {
const testCases = [{
- users: ["MaxLeiter"],
+ message: {
+ users: ["MaxLeiter"],
+ },
input: "test, MaxLeiter",
expected:
"test, " +
- '' +
+ '' +
"MaxLeiter" +
"",
}];
- const actual = testCases.map((testCase) => parse(testCase.input, testCase.users));
+ const actual = testCases.map((testCase) => getParsedMessageContents(testCase.input, testCase.message));
const expected = testCases.map((testCase) => testCase.expected);
expect(actual).to.deep.equal(expected);
@@ -307,7 +325,7 @@ describe("parse Handlebars helper", () => {
users: ["MaxLeiter, test"],
input: "#test-channelMaxLeiter",
expected:
- '' +
+ '' +
"#test-channelMaxLeiter" +
"",
},
@@ -322,7 +340,7 @@ describe("parse Handlebars helper", () => {
];
- const actual = testCases.map((testCase) => parse(testCase.input));
+ const actual = testCases.map((testCase) => getParsedMessageContents(testCase.input));
const expected = testCases.map((testCase) => testCase.expected);
expect(actual).to.deep.equal(expected);
@@ -342,13 +360,13 @@ describe("parse Handlebars helper", () => {
}, {
input: "\x02#\x038,9thelounge",
expected:
- '' +
+ '' +
'#' +
'thelounge' +
"",
}];
- const actual = testCases.map((testCase) => parse(testCase.input));
+ const actual = testCases.map((testCase) => getParsedMessageContents(testCase.input));
const expected = testCases.map((testCase) => testCase.expected);
expect(actual).to.deep.equal(expected);
@@ -358,20 +376,20 @@ describe("parse Handlebars helper", () => {
[{
name: "in text",
input: "Hello💬",
- expected: 'Hello💬',
+ expected: 'Hello💬',
}, {
name: "complicated zero-join-width emoji",
input: "🤦🏿♀️",
- expected: '🤦🏿♀️',
+ expected: '🤦🏿♀️',
}, {
name: "with modifiers",
input: "🤷♀️",
- expected: '🤷♀️',
+ expected: '🤷♀️',
}, {
// FIXME: These multiple `span`s should be optimized into a single one. See https://github.com/thelounge/thelounge/issues/1783
name: "wrapped in style",
input: "Super \x034💚 green!",
- expected: 'Super 💚 green!',
+ expected: 'Super 💚 green!',
}, {
name: "wrapped in URLs",
input: "https://i.❤️.thelounge.chat",
@@ -381,10 +399,10 @@ describe("parse Handlebars helper", () => {
name: "wrapped in channels",
input: "#i❤️thelounge",
// FIXME: Emoji in text should be `❤️`. See https://github.com/thelounge/thelounge/issues/1784
- expected: '#i❤️thelounge',
+ 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);
+ expect(getParsedMessageContents(item.input)).to.equal(item.expected);
});
});
@@ -393,12 +411,12 @@ describe("parse Handlebars helper", () => {
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 actual = testCases.map((testCase) => getParsedMessageContents(testCase.input));
const expected = testCases.map((testCase) => testCase.expected);
expect(actual).to.deep.equal(expected);
@@ -421,7 +439,7 @@ describe("parse Handlebars helper", () => {
"",
}];
- const actual = testCases.map((testCase) => parse(testCase.input));
+ const actual = testCases.map((testCase) => getParsedMessageContents(testCase.input));
const expected = testCases.map((testCase) => testCase.expected);
expect(actual).to.deep.equal(expected);
@@ -436,7 +454,7 @@ describe("parse Handlebars helper", () => {
"",
}];
- const actual = testCases.map((testCase) => parse(testCase.input));
+ const actual = testCases.map((testCase) => getParsedMessageContents(testCase.input));
const expected = testCases.map((testCase) => testCase.expected);
expect(actual).to.deep.equal(expected);
@@ -444,20 +462,20 @@ describe("parse Handlebars helper", () => {
it("should not overlap parts", () => {
const input = "Url: http://example.com/path Channel: ##channel";
- const actual = parse(input);
+ const actual = getParsedMessageContents(input);
expect(actual).to.equal(
'Url: http://example.com/path ' +
- 'Channel: ##channel'
+ 'Channel: ##channel'
);
});
it("should handle overlapping parts by using first starting", () => {
const input = "#test-https://example.com";
- const actual = parse(input);
+ const actual = getParsedMessageContents(input);
expect(actual).to.equal(
- '' +
+ '' +
"#test-https://example.com" +
""
);
diff --git a/yarn.lock b/yarn.lock
index 22d59ebd..33c33912 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -572,8 +572,8 @@
defer-to-connect "^1.0.1"
"@types/node@*":
- version "10.12.18"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67"
+ version "10.9.4"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.9.4.tgz#0f4cb2dc7c1de6096055357f70179043c33e9897"
"@types/unist@*", "@types/unist@^2.0.0":
version "2.0.2"
@@ -608,6 +608,21 @@
source-map "^0.5.6"
vue-template-es2015-compiler "^1.6.0"
+"@vue/server-test-utils@1.0.0-beta.27":
+ version "1.0.0-beta.27"
+ resolved "https://registry.yarnpkg.com/@vue/server-test-utils/-/server-test-utils-1.0.0-beta.27.tgz#0c61cb724d04bf44c70938b4a6054e73e914492a"
+ integrity sha512-HKwm1Nw180qNgknPyok/XWJF4ojBZC5vSANPf+f1WR2jYsSc0vvAxUyX2Qeq4F7KLXrfsFwdkfH3a5BxdrA9tg==
+ dependencies:
+ cheerio "^1.0.0-rc.2"
+
+"@vue/test-utils@1.0.0-beta.27":
+ version "1.0.0-beta.27"
+ resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-1.0.0-beta.27.tgz#7e5f7b7180c00e28a4ca55c0ff0a7e754377fdb2"
+ integrity sha512-Lzrd4ZBkS70Tl8JbXbDrN/NcSaH9aZT6+7emU3QhTJ+CrorJpyFDA1dkvSIhH+rDTs8sHFbGeXjXV/qorXxtRw==
+ dependencies:
+ dom-event-types "^1.0.0"
+ lodash "^4.17.4"
+
"@webassemblyjs/ast@1.7.11":
version "1.7.11"
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.7.11.tgz#b988582cafbb2b095e8b556526f30c90d057cace"
@@ -1614,6 +1629,17 @@ cheerio@0.22.0:
lodash.reject "^4.4.0"
lodash.some "^4.4.0"
+cheerio@^1.0.0-rc.2:
+ version "1.0.0-rc.2"
+ resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.2.tgz#4b9f53a81b27e4d5dac31c0ffd0cfa03cc6830db"
+ dependencies:
+ css-select "~1.2.0"
+ dom-serializer "~0.1.0"
+ entities "~1.1.1"
+ htmlparser2 "^3.9.1"
+ lodash "^4.15.0"
+ parse5 "^3.0.1"
+
chokidar@^2.0.0, chokidar@^2.0.2:
version "2.0.4"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.4.tgz#356ff4e2b0e8e43e322d18a372460bbcf3accd26"
@@ -2365,6 +2391,11 @@ doctrine@^2.1.0:
dependencies:
esutils "^2.0.2"
+dom-event-types@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/dom-event-types/-/dom-event-types-1.0.0.tgz#5830a0a29e1bf837fe50a70cd80a597232813cae"
+ integrity sha512-2G2Vwi2zXTHBGqXHsJ4+ak/iP0N8Ar+G8a7LiD2oup5o4sQWytwqqrZu/O6hIMV0KMID2PL69OhpshLO0n7UJQ==
+
dom-serializer@0, dom-serializer@~0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
@@ -4363,6 +4394,10 @@ locate-path@^3.0.0:
p-locate "^3.0.0"
path-exists "^3.0.0"
+lodash._reinterpolate@~3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
+
lodash.assignin@^4.0.9:
version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2"
@@ -4431,14 +4466,31 @@ lodash.some@^4.4.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d"
+lodash.template@^4.4.0:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.4.0.tgz#e73a0385c8355591746e020b99679c690e68fba0"
+ dependencies:
+ lodash._reinterpolate "~3.0.0"
+ lodash.templatesettings "^4.0.0"
+
+lodash.templatesettings@^4.0.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz#2b4d4e95ba440d915ff08bc899e4553666713316"
+ dependencies:
+ lodash._reinterpolate "~3.0.0"
+
lodash.uniq@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
-lodash@4.17.11, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0:
+lodash@4.17.11, lodash@^4.17.11:
version "4.17.11"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
+lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0:
+ version "4.17.10"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
+
log-symbols@^2.0.0, log-symbols@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a"
@@ -5392,6 +5444,12 @@ parse-passwd@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6"
+parse5@^3.0.1:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c"
+ dependencies:
+ "@types/node" "*"
+
parseqs@0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d"
@@ -6350,11 +6408,11 @@ resolve-url@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
-resolve@^1.3.2:
- version "1.10.0"
- resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.0.tgz#3bdaaeaf45cc07f375656dfd2e54ed0810b101ba"
+resolve@^1.2.0, resolve@^1.3.2:
+ version "1.8.1"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26"
dependencies:
- path-parse "^1.0.6"
+ path-parse "^1.0.5"
responselike@^1.0.2:
version "1.0.2"
@@ -6485,9 +6543,9 @@ send@0.16.2:
range-parser "~1.2.0"
statuses "~1.4.0"
-serialize-javascript@^1.4.0:
- version "1.6.1"
- resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.6.1.tgz#4d1f697ec49429a847ca6f442a2a755126c4d879"
+serialize-javascript@^1.3.0, serialize-javascript@^1.4.0:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.5.0.tgz#1aa336162c88a890ddad5384baebc93a655161fe"
serve-index@^1.7.2:
version "1.9.1"
@@ -6730,6 +6788,10 @@ source-map-url@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
+source-map@0.5.6:
+ version "0.5.6"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
+
source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
@@ -7649,6 +7711,19 @@ vue-loader@15.4.2:
vue-hot-reload-api "^2.3.0"
vue-style-loader "^4.1.0"
+vue-server-renderer@2.5.17:
+ version "2.5.17"
+ resolved "https://registry.yarnpkg.com/vue-server-renderer/-/vue-server-renderer-2.5.17.tgz#c1f24815a4b12a2797c154549b29b44b6be004b5"
+ dependencies:
+ chalk "^1.1.3"
+ hash-sum "^1.0.2"
+ he "^1.1.0"
+ lodash.template "^4.4.0"
+ lodash.uniq "^4.5.0"
+ resolve "^1.2.0"
+ serialize-javascript "^1.3.0"
+ source-map "0.5.6"
+
vue-style-loader@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.0.tgz#7588bd778e2c9f8d87bfc3c5a4a039638da7a863"