mirror of
https://github.com/zulip/zulip.git
synced 2025-10-24 00:23:49 +00:00
362 lines
43 KiB
JavaScript
362 lines
43 KiB
JavaScript
"use strict";
|
||
|
||
const assert = require("node:assert/strict");
|
||
|
||
const {JSDOM} = require("jsdom");
|
||
|
||
const katex_tests = require("../../zerver/tests/fixtures/katex_test_cases.json");
|
||
const {parse} = require("../src/markdown.ts");
|
||
|
||
const {zrequire, set_global} = require("./lib/namespace.cjs");
|
||
const {run_test} = require("./lib/test.cjs");
|
||
|
||
const {window} = new JSDOM();
|
||
|
||
const compose_paste = zrequire("compose_paste");
|
||
const stream_data = zrequire("stream_data");
|
||
|
||
set_global("document", {});
|
||
stream_data.add_sub_for_tests({
|
||
stream_id: 4,
|
||
name: "Rome",
|
||
});
|
||
stream_data.add_sub_for_tests({
|
||
stream_id: 5,
|
||
name: "Romeo`s lair",
|
||
});
|
||
|
||
run_test("try_stream_topic_syntax_text", () => {
|
||
const test_cases = [
|
||
[
|
||
"http://zulip.zulipdev.com/#narrow/channel/4-Rome/topic/old.20FAILED.20EXPORT",
|
||
"#**Rome>old FAILED EXPORT**",
|
||
],
|
||
[
|
||
"http://zulip.zulipdev.com/#narrow/channel/4-Rome/topic/100.25.20profits",
|
||
"#**Rome>100% profits**",
|
||
],
|
||
[
|
||
"http://zulip.zulipdev.com/#narrow/channel/4-Rome/topic/old.20API.20wasn't.20compiling.20erratically",
|
||
"#**Rome>old API wasn't compiling erratically**",
|
||
],
|
||
|
||
["http://different.origin.com/#narrow/channel/4-Rome/topic/old.20FAILED.20EXPORT"],
|
||
[
|
||
"http://zulip.zulipdev.com/#narrow/channel/4-Rome/topic/old.20FAILED.20EXPORT/near/100",
|
||
"#**Rome>old FAILED EXPORT@100**",
|
||
],
|
||
["http://zulip.zulipdev.com/#narrow/channel/4-Rome/topic//near/100", "#**Rome>@100**"],
|
||
[
|
||
"http://zulip.zulipdev.com/#narrow/channel/4-Rome/topic/old.20FAILED.20EXPORT/with/100",
|
||
"#**Rome>old FAILED EXPORT**",
|
||
],
|
||
// malformed urls
|
||
["http://zulip.zulipdev.com/narrow/channel/4-Rome/topic/old.20FAILED.20EXPORT"],
|
||
["http://zulip.zulipdev.com/#not_narrow/channel/4-Rome/topic/old.20FAILED.20EXPORT"],
|
||
["http://zulip.zulipdev.com/#narrow/not_stream/4-Rome/topic/old.20FAILED.20EXPORT"],
|
||
["http://zulip.zulipdev.com/#narrow/channel/4-Rome/not_topic/old.20FAILED.20EXPORT"],
|
||
["http://zulip.zulipdev.com/#narrow/channel/4-Rome/", "#**Rome**"],
|
||
["http://zulip.zulipdev.com/#narrow/channel/4-Rome/topic"],
|
||
["http://zulip.zulipdev.com/#narrow/topic/cheese"],
|
||
["http://zulip.zulipdev.com/#narrow/topic/pizza/stream/Rome"],
|
||
["http://zulip.zulipdev.com/#narrow/channel/4-Rome/topic/old.20FAILED.20EXPORT/near/"],
|
||
|
||
// When a url containing characters which are known to produce broken
|
||
// #**stream>topic** urls is pasted, a normal markdown link syntax is produced.
|
||
[
|
||
"http://zulip.zulipdev.com/#narrow/stream/4-Rome/topic/100.25.20profits.60",
|
||
"[#Rome > 100% profits`](#narrow/channel/4-Rome/topic/100.25.20profits.60)",
|
||
],
|
||
[
|
||
"http://zulip.zulipdev.com/#narrow/stream/4-Rome/topic/100.25.20*profits",
|
||
"[#Rome > 100% *profits](#narrow/channel/4-Rome/topic/100.25.20*profits)",
|
||
],
|
||
[
|
||
"http://zulip.zulipdev.com/#narrow/stream/4-Rome/topic/.24.24 100.25.20profits",
|
||
"[#Rome > $$ 100% profits](#narrow/channel/4-Rome/topic/.24.24.20100.25.20profits)",
|
||
],
|
||
[
|
||
"http://zulip.zulipdev.com/#narrow/stream/4-Rome/topic/>100.25.20profits",
|
||
"[#Rome > >100% profits](#narrow/channel/4-Rome/topic/.3E100.25.20profits)",
|
||
],
|
||
[
|
||
"http://zulip.zulipdev.com/#narrow/stream/5-Romeo.60s-lair/topic/normal",
|
||
"[#Romeo`s lair > normal](#narrow/channel/5-Romeo.60s-lair/topic/normal)",
|
||
],
|
||
[
|
||
"http://zulip.zulipdev.com/#narrow/stream/4-Rome/topic/100.25.20profits.60/near/20",
|
||
"[#Rome > 100% profits` @ 💬](#narrow/channel/4-Rome/topic/100.25.20profits.60/near/20)",
|
||
],
|
||
];
|
||
|
||
for (const test_case of test_cases) {
|
||
const result = compose_paste.try_stream_topic_syntax_text(test_case[0]);
|
||
const expected = test_case[1] ?? null;
|
||
assert.equal(result, expected, "Failed for url: " + test_case[0]);
|
||
}
|
||
});
|
||
|
||
run_test("maybe_transform_html", () => {
|
||
// Copied HTML from VS Code
|
||
let paste_html = `<div style="color: #cccccc;background-color: #1f1f1f;font-family: 'Droid Sans Mono', 'monospace', monospace;font-weight: normal;font-size: 14px;line-height: 19px;white-space: pre;"><div><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> (</span><span style="color: #9cdcfe;">$preview_src</span><span style="color: #cccccc;">.</span><span style="color: #dcdcaa;">endsWith</span><span style="color: #cccccc;">(</span><span style="color: #ce9178;">"&size=full"</span><span style="color: #cccccc;">))</span></div></div>`;
|
||
let paste_text = `if ($preview_src.endsWith("&size=full"))`;
|
||
const escaped_paste_text = "if ($preview_src.endsWith("&size=full"))";
|
||
const expected_output = "<pre><code>" + escaped_paste_text + "</code></pre>";
|
||
assert.equal(compose_paste.maybe_transform_html(paste_html, paste_text), expected_output);
|
||
|
||
// Untransformed HTML
|
||
paste_html = "<div><div>Hello</div><div>World!</div></div>";
|
||
paste_text = "Hello\nWorld!";
|
||
assert.equal(compose_paste.maybe_transform_html(paste_html, paste_text), paste_html);
|
||
});
|
||
|
||
run_test("paste_handler_converter", () => {
|
||
/*
|
||
Pasting from another Zulip message
|
||
*/
|
||
global.document = window.document;
|
||
global.window = window;
|
||
global.Node = window.Node;
|
||
global.HTMLElement = window.HTMLElement;
|
||
global.HTMLAnchorElement = window.HTMLAnchorElement;
|
||
// Bold text
|
||
let input =
|
||
'<meta http-equiv="content-type" content="text/html; charset=utf-8"><span style="color: hsl(0, 0%, 13%); font-family: arial, sans-serif; font-size: 12.8px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: hsl(0, 0%, 100%); text-decoration-style: initial; text-decoration-color: initial;"><span> </span>love the<span> </span><b>Zulip</b><b> </b></span><b style="color: hsl(0, 0%, 13%); font-family: arial, sans-serif; font-size: 12.8px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: hsl(0, 0%, 100%); text-decoration-style: initial; text-decoration-color: initial;">Organization</b><span style="color: hsl(0, 0%, 13%); font-family: arial, sans-serif; font-size: 12.8px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: hsl(0, 0%, 100%); text-decoration-style: initial; text-decoration-color: initial;">.</span>';
|
||
assert.equal(
|
||
compose_paste.paste_handler_converter(input),
|
||
" love the **Zulip** **Organization**.",
|
||
);
|
||
|
||
// Inline code
|
||
input =
|
||
'<meta http-equiv="content-type" content="text/html; charset=utf-8"><span style="color: hsl(210, 12%, 16%); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: hsl(0, 0%, 100%); text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;">The<span> </span></span><code style="box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; padding: 0.2em 0.4em; margin: 0px; background-color: hsla(210, 13%, 12%, 0.05); border-radius: 3px; color: hsl(210, 12%, 16%); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">JSDOM</code><span style="color: hsl(210, 12%, 16%); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: hsl(0, 0%, 100%); text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;"><span> </span>constructor</span>';
|
||
assert.equal(compose_paste.paste_handler_converter(input), "The `JSDOM` constructor");
|
||
|
||
// A python code block
|
||
input = `<meta http-equiv="content-type" content="text/html; charset=utf-8"><p>zulip code block in python</p><div class="codehilite zulip-code-block" data-code-language="Python"><pre><span></span><code><span class="nb">print</span><span class="p">(</span><span class="s2">"hello"</span><span class="p">)</span>\n<span class="nb">print</span><span class="p">(</span><span class="s2">"world"</span><span class="p">)</span></code></pre></div></meta>`;
|
||
assert.equal(
|
||
compose_paste.paste_handler_converter(input),
|
||
'zulip code block in python\n\n```Python\nprint("hello")\nprint("world")\n```',
|
||
);
|
||
|
||
// Single line in a code block
|
||
input =
|
||
'<meta http-equiv="content-type" content="text/html; charset=utf-8"><pre><code>single line</code></pre>';
|
||
assert.equal(compose_paste.paste_handler_converter(input), "`single line`");
|
||
|
||
// No code formatting if the given text area has a backtick at the cursor position
|
||
input =
|
||
'<meta http-equiv="content-type" content="text/html; charset=utf-8"><pre><code>single line</code></pre>';
|
||
assert.equal(
|
||
compose_paste.paste_handler_converter(input, {
|
||
caret: () => 6,
|
||
val: () => "e.g. `",
|
||
}),
|
||
"single line",
|
||
);
|
||
|
||
// Yes code formatting if the given text area has a backtick but not at the cursor position
|
||
input =
|
||
'<meta http-equiv="content-type" content="text/html; charset=utf-8"><pre><code>single line</code></pre>';
|
||
assert.equal(
|
||
compose_paste.paste_handler_converter(input, {
|
||
caret: () => 0,
|
||
}),
|
||
"`single line`",
|
||
);
|
||
|
||
// Raw links without custom text
|
||
input =
|
||
'<meta http-equiv="content-type" content="text/html; charset=utf-8"><a href="https://zulip.readthedocs.io/en/latest/subsystems/logging.html" target="_blank" title="https://zulip.readthedocs.io/en/latest/subsystems/logging.html" style="color: hsl(200, 100%, 40%); text-decoration: none; cursor: pointer; font-family: "Source Sans 3", "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: hsl(0, 0%, 100%);">https://zulip.readthedocs.io/en/latest/subsystems/logging.html</a>';
|
||
assert.equal(
|
||
compose_paste.paste_handler_converter(input),
|
||
"https://zulip.readthedocs.io/en/latest/subsystems/logging.html",
|
||
);
|
||
|
||
// Links with custom text
|
||
input =
|
||
'<meta http-equiv="content-type" content="text/html; charset=utf-8"><a class="reference external" href="https://zulip.readthedocs.io/en/latest/contributing/contributing.html" style="box-sizing: border-box; color: hsl(283, 39%, 53%); text-decoration: none; cursor: pointer; outline: 0px; font-family: Lato, proxima-nova, "Helvetica Neue", Arial, sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: hsl(0, 0%, 99%);">Contributing guide</a>';
|
||
assert.equal(
|
||
compose_paste.paste_handler_converter(input),
|
||
"[Contributing guide](https://zulip.readthedocs.io/en/latest/contributing/contributing.html)",
|
||
);
|
||
|
||
// Only numbered list (list style retained)
|
||
input =
|
||
'<meta http-equiv="content-type" content="text/html; charset=utf-8"><ol><li>text</li></ol>';
|
||
assert.equal(compose_paste.paste_handler_converter(input), "1. text");
|
||
|
||
// Heading
|
||
input =
|
||
'<meta http-equiv="content-type" content="text/html; charset=utf-8"><h1 style="box-sizing: border-box; font-size: 2em; margin-top: 0px !important; margin-right: 0px; margin-bottom: 16px; margin-left: 0px; font-weight: 600; line-height: 1.25; padding-bottom: 0.3em; border-bottom: 1px solid hsl(216, 14%, 93%); color: hsl(210, 12%, 16%); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Zulip overview</h1><p>normal text</p>';
|
||
assert.equal(compose_paste.paste_handler_converter(input), "# Zulip overview\n\nnormal text");
|
||
// Only heading (strip heading style)
|
||
input =
|
||
'<meta http-equiv="content-type" content="text/html; charset=utf-8"><h1 style="box-sizing: border-box; font-size: 2em; margin-top: 0px !important; margin-right: 0px; margin-bottom: 16px; margin-left: 0px; font-weight: 600; line-height: 1.25; padding-bottom: 0.3em; border-bottom: 1px solid hsl(216, 14%, 93%); color: hsl(210, 12%, 16%); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Zulip overview</h1>';
|
||
assert.equal(compose_paste.paste_handler_converter(input), "Zulip overview");
|
||
|
||
// Italic text
|
||
input =
|
||
'<meta http-equiv="content-type" content="text/html; charset=utf-8">normal text <i style="box-sizing: inherit; color: hsl(0, 0%, 0%); font-family: Verdana, sans-serif; font-size: 15px; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: hsl(0, 0%, 100%); text-decoration-style: initial; text-decoration-color: initial;">This text is italic</i>';
|
||
assert.equal(compose_paste.paste_handler_converter(input), "normal text *This text is italic*");
|
||
|
||
// Strikethrough text
|
||
input =
|
||
'<meta http-equiv="content-type" content="text/html; charset=utf-8">normal text <del style="box-sizing: inherit; color: hsl(0, 0%, 0%); font-family: Verdana, sans-serif; font-size: 15px; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: hsl(0, 0%, 100%); text-decoration-style: initial; text-decoration-color: initial;">This text is struck through</del>';
|
||
assert.equal(
|
||
compose_paste.paste_handler_converter(input),
|
||
"normal text ~~This text is struck through~~",
|
||
);
|
||
|
||
// Emojis
|
||
input =
|
||
'<meta http-equiv="content-type" content="text/html; charset=utf-8"><span style="color: rgb(221, 222, 238); font-family: "Source Sans 3", sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(33, 45, 59); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;">emojis:<span> </span></span><span aria-label="smile" class="emoji emoji-1f604" role="img" title="smile" style="height: 20px; width: 20px; position: relative; margin-top: -7px; vertical-align: middle; top: 3px; background-position: 55% 46.667%; display: inline-block; background-image: url("http://localhost:9991/webpack/files/generated/emoji/google.webp"); background-size: 6100%; background-repeat: no-repeat; text-indent: 100%; white-space: nowrap; overflow: hidden; color: rgb(221, 222, 238); font-family: "Source Sans 3", sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(33, 45, 59); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">:smile:</span><span style="color: rgb(221, 222, 238); font-family: "Source Sans 3", sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(33, 45, 59); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;"><span> </span></span><span aria-label="family man woman girl" class="emoji emoji-1f468-200d-1f469-200d-1f467" role="img" title="family man woman girl" style="height: 20px; width: 20px; position: relative; margin-top: -7px; vertical-align: middle; top: 3px; background-position: 23.333% 75%; display: inline-block; background-image: url("http://localhost:9991/webpack/files/generated/emoji/google.webp"); background-size: 6100%; background-repeat: no-repeat; text-indent: 100%; white-space: nowrap; overflow: hidden; color: rgb(221, 222, 238); font-family: "Source Sans 3", sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(33, 45, 59); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">:family_man_woman_girl:</span>';
|
||
assert.equal(
|
||
compose_paste.paste_handler_converter(input),
|
||
"emojis: :smile: :family_man_woman_girl:",
|
||
);
|
||
|
||
// Nested lists
|
||
input =
|
||
'<meta http-equiv="content-type" content="text/html; charset=utf-8"><ul style="padding: 0px; margin: 0px 0px 5px 20px; color: rgb(221, 222, 238); font-family: "Source Sans 3", sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(33, 45, 59); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><li style="line-height: inherit;">bulleted</li><li style="line-height: inherit;">nested<ul style="padding: 0px; margin: 2px 0px 5px 20px;"><li style="line-height: inherit;">nested level 1</li><li style="line-height: inherit;">nested level 1 continue<ul style="padding: 0px; margin: 2px 0px 5px 20px;"><li style="line-height: inherit;">nested level 2</li><li style="line-height: inherit;">nested level 2 continue</li></ul></li></ul></li></ul>';
|
||
assert.equal(
|
||
compose_paste.paste_handler_converter(input),
|
||
"* bulleted\n* nested\n * nested level 1\n * nested level 1 continue\n * nested level 2\n * nested level 2 continue",
|
||
);
|
||
|
||
// Heading from https://arxiv.org/abs/1301.3191
|
||
input =
|
||
'<html><body><!--StartFragment--><h1 class="title mathjax" style="font-size: 1.8em !important; font-weight: 700; line-height: 27.9936px; display: block; margin-block: 12px; margin: 0.25em 0px 12px 20px; margin-inline: 20px 0px; color: rgb(0, 0, 0); font-family: "Lucida Grande", Helvetica, Arial, sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">Enriched categories as a free cocompletion</h1><br class="Apple-interchange-newline"><!--EndFragment--></body></html>';
|
||
assert.equal(
|
||
compose_paste.paste_handler_converter(input),
|
||
"Enriched categories as a free cocompletion",
|
||
);
|
||
|
||
// Heading from https://www.sciencedirect.com/science/article/pii/S0001870815004715
|
||
input =
|
||
'<html><body><!--StartFragment--><h2 class="section-title u-h4 u-margin-l-top u-margin-xs-bottom" style="box-sizing: border-box; margin-top: 32px !important; margin-right: 0px; margin-bottom: 8px !important; margin-left: 0px; padding: 0px; font-weight: normal !important; font-size: 20px !important; line-height: var(--sd-ui-line-height) !important; color: rgb(31, 31, 31); font-family: ElsevierGulliver, Georgia, "Times New Roman", Times, STIXGeneral, "Cambria Math", "Lucida Sans Unicode", "Microsoft Sans Serif", "Segoe UI Symbol", "Arial Unicode MS", serif, sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">Abstract</h2><div id="as0010" style="box-sizing: border-box; margin: 0px; padding: 0px; color: rgb(31, 31, 31); font-family: ElsevierGulliver, Georgia, "Times New Roman", Times, STIXGeneral, "Cambria Math", "Lucida Sans Unicode", "Microsoft Sans Serif", "Segoe UI Symbol", "Arial Unicode MS", serif, sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><br class="Apple-interchange-newline"><!--EndFragment--></body></html>';
|
||
assert.equal(compose_paste.paste_handler_converter(input), "Abstract");
|
||
|
||
// Heading from https://en.wikipedia.org/wiki/James_Madison
|
||
input =
|
||
'<html><body><!--StartFragment--><h1 id="firstHeading" class="firstHeading mw-first-heading" style="color: var(--color-emphasized,#101418); font-weight: normal; margin: 0px; padding: 0px; display: flow-root; word-break: break-word; border: 0px; font-size: 1.8em; font-family: "Linux Libertine", Georgia, Times, "Source Serif Pro", serif; line-height: 1.375; overflow-wrap: break-word; flex-grow: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><span class="mw-page-title-main">James Madison</span></h1><div id="p-lang-btn" class="vector-dropdown mw-portlet mw-portlet-lang" style="position: relative; float: right; box-sizing: border-box; flex-shrink: 0; margin-right: -12px; color: rgb(32, 33, 34); font-family: sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"></div><br class="Apple-interchange-newline"><!--EndFragment--></body></html>';
|
||
assert.equal(compose_paste.paste_handler_converter(input), "James Madison");
|
||
|
||
// Heading from https://customer-identity-access-management.hashnode.dev/from-words-to-vectors-understanding-the-magic-of-text-embedding
|
||
input = `<html><body><!--StartFragment--><div class="mt-6 break-words px-4 text-center font-heading text-3xl font-bold text-slate-900 dark:text-white md:mt-10 md:px-5 md:text-4xl lg:px-8 xl:px-20 xl:text-5xl mb-8 md:mb-14" style="box-sizing: border-box; ...">
|
||
<h1 class="leading-tight" data-query="post-title">From Words to Vectors: Understanding the Magic of Text Embedding</h1>
|
||
</div></body></html>`;
|
||
assert.equal(
|
||
compose_paste.paste_handler_converter(input),
|
||
"From Words to Vectors: Understanding the Magic of Text Embedding",
|
||
);
|
||
|
||
// Check we don't double-convert HTML to text.
|
||
input = `<del>turtles are cool</del>`;
|
||
assert.equal(compose_paste.paste_handler_converter(input), "turtles are cool");
|
||
|
||
input = `<span style="color: rgb(31, 35, 40); font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 11.9px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: break-spaces; background-color: rgba(129, 139, 152, 0.12); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;"><del>turtles are cool</del></span>`;
|
||
assert.equal(compose_paste.paste_handler_converter(input), "<del>turtles are cool</del>");
|
||
|
||
// 2 paragraphs with line break/s in between
|
||
input =
|
||
'<meta http-equiv="content-type" content="text/html; charset=utf-8"><p>paragraph 1</p><br><p>paragraph 2</p>';
|
||
assert.equal(compose_paste.paste_handler_converter(input), "paragraph 1\n\nparagraph 2");
|
||
|
||
// Pasting from external sources
|
||
// Pasting list from GitHub
|
||
input =
|
||
'<div class="preview-content"><div class="comment"><div class="comment-body markdown-body js-preview-body" style="min-height: 131px;"><p>Test list:</p><ul><li>Item 1</li><li>Item 2</li></ul></div></div></div>';
|
||
assert.equal(compose_paste.paste_handler_converter(input), "Test list:\n* Item 1\n* Item 2");
|
||
|
||
// Pasting list from VS Code
|
||
input =
|
||
'<div class="ace-line gutter-author-d-iz88z86z86za0dz67zz78zz78zz74zz68zjz80zz71z9iz90za3z66zs0z65zz65zq8z75zlaz81zcz66zj6g2mz78zz76zmz66z22z75zfcz69zz66z ace-ltr focused-line" dir="auto" id="editor-3-ace-line-41"><span>Test list:</span></div><div class="ace-line gutter-author-d-iz88z86z86za0dz67zz78zz78zz74zz68zjz80zz71z9iz90za3z66zs0z65zz65zq8z75zlaz81zcz66zj6g2mz78zz76zmz66z22z75zfcz69zz66z line-list-type-bullet ace-ltr" dir="auto" id="editor-3-ace-line-42"><ul class="listtype-bullet listindent1 list-bullet1"><li><span class="ace-line-pocket-zws" data-faketext="" data-contentcollector-ignore-space-at="end"></span><span class="ace-line-pocket" data-faketext="" contenteditable="false"></span><span class="ace-line-pocket-zws" data-faketext="" data-contentcollector-ignore-space-at="start"></span><span>Item 1</span></li></ul></div><div class="ace-line gutter-author-d-iz88z86z86za0dz67zz78zz78zz74zz68zjz80zz71z9iz90za3z66zs0z65zz65zq8z75zlaz81zcz66zj6g2mz78zz76zmz66z22z75zfcz69zz66z line-list-type-bullet ace-ltr" dir="auto" id="editor-3-ace-line-43"><ul class="listtype-bullet listindent1 list-bullet1"><li><span class="ace-line-pocket-zws" data-faketext="" data-contentcollector-ignore-space-at="end"></span><span class="ace-line-pocket" data-faketext="" contenteditable="false"></span><span class="ace-line-pocket-zws" data-faketext="" data-contentcollector-ignore-space-at="start"></span><span>Item 2</span></li></ul></div>';
|
||
assert.equal(compose_paste.paste_handler_converter(input), "Test list:\n* Item 1\n* Item 2");
|
||
|
||
// Pasting from Google Sheets (remove <style> elements completely)
|
||
input =
|
||
'<meta http-equiv="content-type" content="text/html; charset=utf-8"><style type="text/css"><!--td {border: 1px solid #cccccc;}br {mso-data-placement:same-cell;}--></style><span style="font-size:10pt;font-family:Arial;font-style:normal;text-align:right;" data-sheets-value="{"1":3,"3":123}" data-sheets-userformat="{"2":769,"3":{"1":0},"11":3,"12":0}">123</span>';
|
||
assert.equal(compose_paste.paste_handler_converter(input), "123");
|
||
|
||
// Pasting from Excel
|
||
input = `<html xmlns:v="urn:schemas-microsoft-com:vml"\nxmlns:o="urn:schemas-microsoft-com:office:office"\nxmlns:x="urn:schemas-microsoft-com:office:excel"\nxmlns="http://www.w3.org/TR/REC-html40">\n<head>\n<meta http-equiv=Content-Type content="text/html; charset=utf-8">\n<meta name=ProgId content=Excel.Sheet>\n<meta name=Generator content="Microsoft Excel 15">\n<link id=Main-File rel=Main-File\nhref="file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip.htm">\n<link rel=File-List\nhref="file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_filelist.xml">\n<style>\n<!--table\n {mso-displayed-decimal-separator:"\\.";\n mso-displayed-thousand-separator:"\\,";}\n@page\n {margin:.75in .7in .75in .7in;\n mso-header-margin:.3in;\n mso-footer-margin:.3in;}\ntr\n {mso-height-source:auto;}\ncol\n {mso-width-source:auto;}\nbr\n {mso-data-placement:same-cell;}\ntd\n {padding-top:1px;\n padding-right:1px;\n padding-left:1px;\n mso-ignore:padding;\n color:black;\n font-size:11.0pt;\n font-weight:400;\n font-style:normal;\n text-decoration:none;\n font-family:Calibri, sans-serif;\n mso-font-charset:0;\n mso-number-format:General;\n text-align:general;\n vertical-align:bottom;\n border:none;\n mso-background-source:auto;\n mso-pattern:auto;\n mso-protection:locked visible;\n white-space:nowrap;\n mso-rotate:0;}\n.xl65\n {mso-number-format:"_\\(\\0022$\\0022* \\#\\,\\#\\#0\\.00_\\)\\;_\\(\\0022$\\0022* \\\\\\(\\#\\,\\#\\#0\\.00\\\\\\)\\;_\\(\\0022$\\0022* \\0022-\\0022??_\\)\\;_\\(\\@_\\)";}\n-->\n</style>\n</head>\n<body link="#0563C1" vlink="#954F72">\n<table border=0 cellpadding=0 cellspacing=0 width=88 style='border-collapse:\n collapse;width:66pt'>\n<!--StartFragment-->\n <col width=88 style='mso-width-source:userset;mso-width-alt:3218;width:66pt'>\n <tr height=20 style='height:15.0pt'>\n <td height=20 class=xl65 width=88 style='height:15.0pt;width:66pt;font-size:\n 11.0pt;color:black;font-weight:400;text-decoration:none;text-underline-style:\n none;text-line-through:none;font-family:Calibri, sans-serif;border-top:.5pt solid #5B9BD5;\n border-right:none;border-bottom:none;border-left:none'><span\n style='mso-spacerun:yes'> </span>$<span style='mso-spacerun:yes'>\n </span>20.00 </td>\n </tr>\n <tr height=20 style='height:15.0pt'>\n <td height=20 class=xl65 style='height:15.0pt;font-size:11.0pt;color:black;\n font-weight:400;text-decoration:none;text-underline-style:none;text-line-through:\n none;font-family:Calibri, sans-serif;border-top:.5pt solid #5B9BD5;\n border-right:none;border-bottom:none;border-left:none'><span\n style='mso-spacerun:yes'> </span>$<span\n style='mso-spacerun:yes'> </span>7.00 </td>\n </tr>\n<!--EndFragment-->\n</table>\n</body>\n</html>`;
|
||
|
||
// Pasting from Excel using ^V should paste an image.
|
||
assert.ok(compose_paste.is_single_image(input));
|
||
|
||
// Pasting from Excel using ^⇧V should paste formatted text.
|
||
assert.equal(compose_paste.paste_handler_converter(input), " \n\n$ 20.00\n\n$ 7.00");
|
||
|
||
// Pasting from LibreOffice Calc should paste an image.
|
||
input = `<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><title></title><meta name="generator" content="LibreOffice 25.2.3.2 (Windows)"><style type="text/css"> body,div,table,thead,tbody,tfoot,tr,th,td,p { font-family:"Arial"; font-size:x-small } a.comment-indicator:hover + comment { background:#ffd; position:absolute; display:block; border:1px solid black; padding:0.5em; } a.comment-indicator { background:red; display:inline-block; border:1px solid black; width:0.5em; height:0.5em; } comment { display:none; } </style></head><body><table cellspacing="0" border="0"><colgroup span="4" width="107"></colgroup><tbody><tr><td height="24" align="left" data-sheets-value="{ "1": 2, "2": "Kathleen"}">Kathleen</td><td align="left" data-sheets-value="{ "1": 2, "2": "Hanner"}">Hanner</td><td align="left" data-sheets-value="{ "1": 2, "2": "Female"}">Female</td><td align="left" data-sheets-value="{ "1": 2, "2": "United States"}">United States</td></tr><tr><td height="24" align="left" data-sheets-value="{ "1": 2, "2": "Nereida"}">Nereida</td><td align="left" data-sheets-value="{ "1": 2, "2": "Magwood"}">Magwood</td><td align="left" data-sheets-value="{ "1": 2, "2": "Female"}">Female</td><td align="left" data-sheets-value="{ "1": 2, "2": "United States"}">United States</td></tr></tbody></table></body></html>`;
|
||
assert.ok(compose_paste.is_single_image(input));
|
||
|
||
// This contains three child elements inside the body tag, pasted
|
||
// from LibreOffice Writer, which is correctly classified as not an image.
|
||
input = `<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"/><title></title><meta name="generator" content="LibreOffice 25.2.3.2 (Windows)"/><style type="text/css">@page { size: 8.5in 11in; margin: 0.79in }td p { orphans: 0; widows: 0; background: transparent }p { line-height: 115%; margin-bottom: 0.1in; background: transparent }</style></head><body lang="en-US" link="#000080" vlink="#800000" dir="ltr"><p style="line-height: 100%; margin-bottom: 0in">ello world</p><table width="100%" cellpadding="0" cellspacing="0"><col width="51*"/><col width="51*"/><col width="51*"/><col width="51*"/><col width="51*"/><tr valign="top"><td width="20%" style="border: none; padding: 0in"><p>X</p></td><td width="20%" style="border: none; padding: 0in"><p>as</p></td><td width="20%" style="border: none; padding: 0in"><p>Jak</p></td><td width="20%" style="border: none; padding: 0in"><p>J</p></td><td width="20%" style="border: none; padding: 0in"><p>Nm</p></td></tr><tr valign="top"><td width="20%" style="border: none; padding: 0in"><p>,mn</p></td><td width="20%" style="border: none; padding: 0in"><p>,nnf</p></td><td width="20%" style="border: none; padding: 0in"><p>Adlk</p></td><td width="20%" style="border: none; padding: 0in"><p>Asn</p></td><td width="20%" style="border: none; padding: 0in"><p>,amns</p></td></tr><tr valign="top"><td width="20%" style="border: none; padding: 0in"><p>Nm</p></td><td width="20%" style="border: none; padding: 0in"><p>Oi</p></td><td width="20%" style="border: none; padding: 0in"><p>Poi</p></td><td width="20%" style="border: none; padding: 0in"><p>B</p></td><td width="20%" style="border: none; padding: 0in"><p>Ijo</p></td></tr><tr valign="top"><td width="20%" style="border: none; padding: 0in"><p>,mn,</p></td><td width="20%" style="border: none; padding: 0in"><p>;ih</p></td><td width="20%" style="border: none; padding: 0in"><p>Oug</p></td><td width="20%" style="border: none; padding: 0in"><p>Iu</p></td><td width="20%" style="border: none; padding: 0in"><p>G</p></td></tr><tr valign="top"><td width="20%" style="border: none; padding: 0in"><p>Ug</p></td><td width="20%" style="border: none; padding: 0in"><p>Bkjb</p></td><td width="20%" style="border: none; padding: 0in"><p>Kjbk</p></td><td width="20%" style="border: none; padding: 0in"><p>;jbj</p></td><td width="20%" style="border: none; padding: 0in"><p>;jb;</p></td></tr><tr valign="top"><td width="20%" style="border: none; padding: 0in"><p>Bkjb</p></td><td width="20%" style="border: none; padding: 0in"><p>Ugug</p></td><td width="20%" style="border: none; padding: 0in"><p>I9</p></td><td width="20%" style="border: none; padding: 0in"><p>68</p></td><td width="20%" style="border: none; padding: 0in"><p>0</p></td></tr><tr valign="top"><td width="20%" style="border: none; padding: 0in"><p>90kjb</p></td><td width="20%" style="border: none; padding: 0in"><p>,bnbiu</p></td><td width="20%" style="border: none; padding: 0in"><p>Ofif</p></td><td width="20%" style="border: none; padding: 0in"><p>P8gp</p></td><td width="20%" style="border: none; padding: 0in"><p>pugp</p></td></tr></table><p style="line-height: 100%; margin-bottom: 0in"><br/></p></body></html>`;
|
||
assert.ok(!compose_paste.is_single_image(input));
|
||
|
||
// <body> has a single child element which is not a <table> pasted
|
||
// from LibreOffice Writer should get pasted normally.
|
||
input = `<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"/><title></title><meta name="generator" content="LibreOffice 25.2.3.2 (Windows)"/><style type="text/css">@page { size: 8.5in 11in; margin: 0.79in }p { line-height: 115%; margin-bottom: 0.1in; background: transparent }</style></head><body lang="en-US" link="#000080" vlink="#800000" dir="ltr"><p style="line-height: 100%; margin-bottom: 0in">Hello world this is some random text.</p></body></html>`;
|
||
assert.ok(!compose_paste.is_single_image(input));
|
||
|
||
// A single table pasted from LibreOffice Writer is incorrectly
|
||
// detected as a LibreOffice Calc table.
|
||
// See https://github.com/zulip/zulip/pull/34752/#discussion_r2113598064
|
||
input = `<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"/><title></title><meta name="generator" content="LibreOffice 25.2.3.2 (Windows)"/><style type="text/css">@page { size: 8.5in 11in; margin: 0.79in }p { line-height: 115%; margin-bottom: 0.1in; background: transparent }</style></head><body lang="en-US" link="#000080" vlink="#800000" dir="ltr"><table width="258" cellpadding="2" cellspacing="0"><col width="83"/><col width="81"/><col width="81"/><tr valign="bottom"><td width="83" height="16" style="border: none; padding: 0in"><p align="left"><font face="Arial, serif">Melgar</font></p></td><td width="81" style="border: none; padding: 0in"><p align="left"><font face="Arial, serif">Female</font></p></td><td width="81" style="border: none; padding: 0in"><p align="left"><font face="Arial, serif">UnitedStates</font></p></td></tr><tr valign="bottom"><td width="83" height="16" style="border: none; padding: 0in"><p align="left"><font face="Arial, serif">Weiland</font></p></td><td width="81" style="border: none; padding: 0in"><p align="left"><font face="Arial, serif">Female</font></p></td><td width="81" style="border: none; padding: 0in"><p align="left"><font face="Arial, serif">UnitedStates</font></p></td></tr><tr valign="bottom"><td width="83" height="16" style="border: none; padding: 0in"><p align="left"><font face="Arial, serif">Winward</font></p></td><td width="81" style="border: none; padding: 0in"><p align="left"><font face="Arial, serif">Female</font></p></td><td width="81" style="border: none; padding: 0in"><p align="left"><font face="Arial, serif">GreatBritain</font></p></td></tr></table></body></html>`;
|
||
assert.ok(compose_paste.is_single_image(input));
|
||
|
||
// Pasting from the mac terminal
|
||
input =
|
||
'<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta http-equiv="Content-Style-Type" content="text/css"><title></title><meta name="Generator" content="Cocoa HTML Writer"><meta name="CocoaVersion" content="2575.4"><style type="text/css">p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000}span.s1 {font-variant-ligatures: no-common-ligatures}</style></head><body><p class="p1"><span class="s1">insertions</span></p></body></html>';
|
||
assert.equal(compose_paste.paste_handler_converter(input), "insertions");
|
||
|
||
// Math block tests
|
||
|
||
/*
|
||
This first batch of math block tests uses captured fixtures
|
||
(`input`). This lets us verify behavior like the empty
|
||
`.katex-display` divs in case of newlines in the
|
||
`original_markdown` See
|
||
https://github.com/zulip/zulip/pull/32629#discussion_r1883810127
|
||
*/
|
||
|
||
for (const math_block_test of katex_tests.math_block_tests) {
|
||
input = math_block_test.input;
|
||
assert.equal(compose_paste.paste_handler_converter(input), math_block_test.expected_output);
|
||
}
|
||
|
||
// This next batch of tests round-trips the LaTeX syntax through
|
||
// the Markdown processor and then the paste handler.
|
||
const dummy_helper_config = {
|
||
should_translate_emoticons: () => false,
|
||
get_linkifier_map: () => new Map(),
|
||
};
|
||
assert.equal(dummy_helper_config.should_translate_emoticons(), false);
|
||
assert.deepEqual(dummy_helper_config.get_linkifier_map(), new Map());
|
||
|
||
for (const inline_math_expression_test of katex_tests.inline_math_expression_tests) {
|
||
const paste_html = parse({
|
||
raw_content: inline_math_expression_test.original_markup,
|
||
helper_config: dummy_helper_config,
|
||
}).content;
|
||
assert.equal(
|
||
compose_paste.paste_handler_converter(paste_html),
|
||
inline_math_expression_test.expected_output,
|
||
);
|
||
}
|
||
|
||
for (const span_conversion_test of katex_tests.text_node_to_span_conversion_tests) {
|
||
const paste_html = parse({
|
||
raw_content: span_conversion_test.original_markup,
|
||
helper_config: dummy_helper_config,
|
||
}).content;
|
||
assert.equal(
|
||
compose_paste.paste_handler_converter(paste_html),
|
||
span_conversion_test.expected_output,
|
||
);
|
||
}
|
||
});
|