Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 2649c6f

Browse files
Tests for roundtrips md<->HTML (#8696)
* Tests for roundtrips md<->HTML * Convert roundtrip tests to it.each table style * Fix lint errors * Remove extraneous "f " Co-authored-by: Travis Ralston <[email protected]> * Test __ -> ** * Test for code blocks containing markdown * Test for more ordered lists * Test for code blocks with language specifiers * Additional cases for HTML->markdown->HTML Co-authored-by: Travis Ralston <[email protected]>
1 parent 5f31fef commit 2649c6f

File tree

1 file changed

+170
-0
lines changed

1 file changed

+170
-0
lines changed

test/editor/roundtrip-test.ts

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/*
2+
Copyright 2022 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import { MatrixEvent } from 'matrix-js-sdk/src/matrix';
18+
19+
import { parseEvent } from "../../src/editor/deserialize";
20+
import EditorModel from '../../src/editor/model';
21+
import DocumentOffset from '../../src/editor/offset';
22+
import { htmlSerializeIfNeeded, textSerialize } from '../../src/editor/serialize';
23+
import { createPartCreator } from "./mock";
24+
25+
function htmlMessage(formattedBody: string, msgtype = "m.text") {
26+
return {
27+
getContent() {
28+
return {
29+
msgtype,
30+
format: "org.matrix.custom.html",
31+
formatted_body: formattedBody,
32+
};
33+
},
34+
} as unknown as MatrixEvent;
35+
}
36+
37+
async function md2html(markdown: string): Promise<string> {
38+
const pc = createPartCreator();
39+
const oldModel = new EditorModel([], pc, () => {});
40+
await oldModel.update(
41+
markdown,
42+
"insertText",
43+
new DocumentOffset(markdown.length, false),
44+
);
45+
return htmlSerializeIfNeeded(oldModel, { forceHTML: true });
46+
}
47+
48+
function html2md(html: string): string {
49+
const pc = createPartCreator();
50+
const parts = parseEvent(htmlMessage(html), pc);
51+
const newModel = new EditorModel(parts, pc);
52+
return textSerialize(newModel);
53+
}
54+
55+
async function roundTripMarkdown(markdown: string): Promise<string> {
56+
return html2md(await md2html(markdown));
57+
}
58+
59+
async function roundTripHtml(html: string): Promise<string> {
60+
return await md2html(html2md(html));
61+
}
62+
63+
describe('editor/roundtrip', function() {
64+
describe('markdown messages should round-trip if they contain', function() {
65+
test.each([
66+
["newlines", "hello\nworld"],
67+
["pills", "text message for @room"],
68+
["pills with interesting characters in mxid", "text message for @alice\\\\\\_\\]#>&:hs.example.com"],
69+
["styling", "**bold** and _emphasised_"],
70+
["bold within a word", "abso**fragging**lutely"],
71+
["escaped html", "a\\<foo>b"],
72+
["escaped markdown", "\\*\\*foo\\*\\* \\_bar\\_ \\[a\\](b)"],
73+
["escaped backslashes", "C:\\\\Program Files"],
74+
["code in backticks", "foo ->`x`"],
75+
["code blocks containing backticks", "```\nfoo ->`x`\nbar\n```"],
76+
["code blocks containing markdown", "```\n__init__.py\n```"],
77+
["nested formatting", "a<del>b **c _d_ e** f</del>g"],
78+
["an ordered list", "A\n\n1. b\n2. c\n3. d\nE"],
79+
["an ordered list starting later", "A\n\n9. b\n10. c\n11. d\nE"],
80+
["an unordered list", "A\n\n- b\n- c\n- d\nE"],
81+
["code block followed by text after a blank line", "```A\nfoo(bar).baz();\n\n3\n```\n\nB"],
82+
["just a code block", "```\nfoo(bar).baz();\n\n3\n```"],
83+
["code block with language specifier", "```bash\nmake install\n\n```"],
84+
["inline code", "there's no place `127.0.0.1` like"],
85+
["nested quotations", "saying\n\n> > foo\n\n> NO\n\nis valid"],
86+
["quotations", "saying\n\n> NO\n\nis valid"],
87+
["links", "click [this](http://example.com/)!"],
88+
])('%s', async (_name, markdown) => {
89+
expect(await roundTripMarkdown(markdown)).toEqual(markdown);
90+
});
91+
92+
test.skip.each([
93+
// Removes trailing spaces
94+
["a code block followed by newlines", "```\nfoo(bar).baz();\n\n3\n```\n\n"],
95+
// Adds a space after the code block
96+
["a code block surrounded by text", "```A\nfoo(bar).baz();\n\n3\n```\nB"],
97+
// Adds a space before the list
98+
["an unordered list directly preceded by text", "A\n- b\n- c\n- d\nE"],
99+
// Re-numbers to 1, 2, 3
100+
["an ordered list where everything is 1", "A\n\n1. b\n1. c\n1. d\nE"],
101+
// Adds a space before the list
102+
["an ordered list directly preceded by text", "A\n1. b\n2. c\n3. d\nE"],
103+
// Adds and removes spaces before the nested list
104+
["nested unordered lists", "A\n- b\n- c\n - c1\n - c2\n- d\nE"],
105+
// Adds and removes spaces before the nested list
106+
["nested ordered lists", "A\n\n1. b\n2. c\n 1. c1\n 2. c2\n3. d\nE"],
107+
// Adds and removes spaces before the nested list
108+
["nested mixed lists", "A\n\n1. b\n2. c\n - c1\n - c2\n3. d\nE"],
109+
// Backslashes get doubled
110+
["backslashes", "C:\\Program Files"],
111+
// Deletes the whitespace
112+
['newlines with trailing and leading whitespace', "hello \n world"],
113+
// Escapes the underscores
114+
["underscores within a word", "abso_fragging_lutely"],
115+
// Includes the trailing text into the quotation
116+
// https://github.com/vector-im/element-web/issues/22341
117+
["quotations without separating newlines", "saying\n> NO\nis valid"],
118+
// Removes trailing and leading whitespace
119+
["quotations with trailing and leading whitespace", "saying \n\n> NO\n\n is valid"],
120+
])('%s', async (_name, markdown) => {
121+
expect(await roundTripMarkdown(markdown)).toEqual(markdown);
122+
});
123+
124+
it('styling, but * becomes _ and __ becomes **', async function() {
125+
expect(await roundTripMarkdown("__bold__ and *emphasised*"))
126+
.toEqual("**bold** and _emphasised_");
127+
});
128+
});
129+
130+
describe('HTML messages should round-trip if they contain', function() {
131+
test.each([
132+
["backslashes", "C:\\Program Files"],
133+
[
134+
"nested blockquotes",
135+
"<blockquote>\n<p>foo</p>\n<blockquote>\n<p>bar</p>\n</blockquote>\n</blockquote>\n",
136+
],
137+
["ordered lists", "<ol>\n<li>asd</li>\n<li>fgd</li>\n</ol>\n"],
138+
["ordered lists starting later", '<ol start="3">\n<li>asd</li>\n<li>fgd</li>\n</ol>\n'],
139+
["unordered lists", "<ul>\n<li>asd</li>\n<li>fgd</li>\n</ul>\n"],
140+
["code blocks with surrounding text", "<p>a</p>\n<pre><code>a\ny;\n</code></pre>\n<p>b</p>\n"],
141+
["code blocks", "<pre><code>a\ny;\n</code></pre>\n"],
142+
["code blocks containing markdown", "<pre><code>__init__.py\n</code></pre>\n"],
143+
["code blocks with language specifier", "<pre><code class=\"language-bash\">__init__.py\n</code></pre>\n"],
144+
["paragraphs including formatting", "<p>one</p>\n<p>t <em>w</em> o</p>\n"],
145+
["paragraphs", "<p>one</p>\n<p>two</p>\n"],
146+
["links", "http://more.example.com/"],
147+
["escaped html", "This &gt;em&lt;isn't&gt;em&lt; important"],
148+
["markdown-like symbols", "You _would_ **type** [a](http://this.example.com) this."],
149+
["formatting within a word", "abso<strong>fragging</strong>lutely"],
150+
["formatting", "This <em>is</em> im<strong>port</strong>ant"],
151+
["line breaks", "one<br>two"],
152+
])('%s', async (_name, html) => {
153+
expect(await roundTripHtml(html)).toEqual(html);
154+
});
155+
156+
test.skip.each([
157+
// Strips out the pill - maybe needs some user lookup to work?
158+
["user pills", '<a href="https://matrix.to/#/@alice:hs.tld">Alice</a>'],
159+
// Appends a slash to the URL
160+
// https://github.com/vector-im/element-web/issues/22342
161+
["links without trailing slashes", 'Go <a href="http://more.example.com">here</a> to see more'],
162+
// Inserts newlines after tags
163+
["paragraphs without newlines", "<p>one</p><p>two</p>"],
164+
// Inserts a code block
165+
["nested lists", "<ol>\n<li>asd</li>\n<li>\n<ul>\n<li>fgd</li>\n<li>sdf</li>\n</ul>\n</li>\n</ol>\n"],
166+
])('%s', async (_name, html) => {
167+
expect(await roundTripHtml(html)).toEqual(html);
168+
});
169+
});
170+
});

0 commit comments

Comments
 (0)