"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 {make_user_group} = require("./lib/example_group.cjs"); const {make_stream} = require("./lib/example_stream.cjs"); const {make_user} = require("./lib/example_user.cjs"); const {mock_esm, zrequire, set_global} = require("./lib/namespace.cjs"); const {run_test, noop} = require("./lib/test.cjs"); const {$} = require("./lib/zjquery.cjs"); const {window} = new JSDOM(); const text_field_edit = mock_esm("text-field-edit", {insertTextIntoField: noop}); const compose_paste = zrequire("compose_paste"); const compose_ui = zrequire("compose_ui"); const linkifiers = zrequire("linkifiers"); const markdown = zrequire("markdown"); const markdown_config = zrequire("markdown_config"); const people = zrequire("people"); const stream_data = zrequire("stream_data"); const user_groups = zrequire("user_groups"); const {initialize_user_settings} = zrequire("user_settings"); set_global("document", {}); class ClipboardEvent { constructor({clipboardData} = {}) { this.clipboardData = clipboardData; } } set_global("ClipboardEvent", ClipboardEvent); initialize_user_settings({ user_settings: { translate_emoticons: false, }, }); markdown.initialize(markdown_config.get_helpers()); // User and group ids match the ids used in the mention test fixtures // below. const king_hamlet = make_user({user_id: 10, full_name: "King Hamlet"}); const desdemona = make_user({user_id: 9, full_name: "Desdemona"}); const polonius = make_user({user_id: 13, full_name: "Polonius"}); people.add_active_user(king_hamlet); people.add_active_user(desdemona); people.add_active_user(polonius); const hamletcharacters = make_user_group({name: "hamletcharacters", id: 24}); const owners = make_user_group({name: "role:owners", id: 25, is_system_group: true}); user_groups.initialize({realm_user_groups: [hamletcharacters, owners]}); stream_data.add_sub_for_tests( make_stream({ stream_id: 4, name: "Rome", }), ); stream_data.add_sub_for_tests( make_stream({ 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.2Aprofits)", ], [ "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 = `
" + escaped_paste_text + "";
assert.equal(compose_paste.maybe_transform_html(paste_html, paste_text), expected_output);
// Untransformed HTML
paste_html = "
test
https://github.com/zulip/zulip-desktop/pull/1359
JSDOM constructor';
assert.equal(compose_paste.paste_handler_converter(input), "The `JSDOM` constructor");
// A python code block
input = `zulip code block in python
print("hello")\nprint("world")single line';
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 =
'single line';
assert.equal(
compose_paste.paste_handler_converter(input, {
val() {
return "e.g. `";
},
0: {
selectionStart: 6,
value: "e.g. `",
},
length: 1,
}),
"single line",
);
// No code formatting if the given text area has a backtick at the cursor position
input =
'single line';
assert.equal(
compose_paste.paste_handler_converter(input, {
val() {
return "`e.g. ` ```hi`` ``";
},
0: {
selectionStart: 19,
value: "`e.g. ` ```hi`` ``",
},
length: 1,
}),
"single line",
);
// No code formatting if the given text area has a opening backtick before the cursor position
input =
'single line';
assert.equal(
compose_paste.paste_handler_converter(input, {
val() {
return "e.g. ` ";
},
0: {
selectionStart: 7,
value: "e.g. ` ",
},
length: 1,
}),
"single line",
);
// Yes code formatting if the given text area does not have a backtick at the cursor position.
input =
'single line';
assert.equal(
compose_paste.paste_handler_converter(input, {
val() {
return "";
},
0: {
selectionStart: 0,
value: "",
},
length: 1,
}),
"`single line`",
);
// Yes code formatting if the given text area closes the code block before the cursor position
input =
'single line';
assert.equal(
compose_paste.paste_handler_converter(input, {
val() {
return "` e.g. ` ";
},
0: {
selectionStart: 9,
value: "` e.g. ` ",
},
length: 1,
}),
"`single line`",
);
// Yes code formatting if the given text area closes the code block before the cursor position
input =
'single line';
assert.equal(
compose_paste.paste_handler_converter(input, {
val() {
return "``` e.g. ``` ``hi`` ";
},
0: {
selectionStart: 20,
value: "``` e.g. ``` ``hi`` ",
},
length: 1,
}),
"`single line`",
);
// Raw links without custom text
input =
'https://zulip.readthedocs.io/en/latest/subsystems/logging.html';
assert.equal(
compose_paste.paste_handler_converter(input),
"https://zulip.readthedocs.io/en/latest/subsystems/logging.html",
);
// Links with custom text
input =
'Contributing guide';
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 =
'normal text
'; assert.equal(compose_paste.paste_handler_converter(input), "# Zulip overview\n\nnormal text"); // Only heading (strip heading style) input = 'paragraph 1
paragraph 2
'; assert.equal(compose_paste.paste_handler_converter(input), "paragraph 1\n\nparagraph 2"); // Pasting from external sources // Pasting list from GitHub input = '| $\n 20.00 | \n
| $ 7.00 | \n
| Kathleen | Hanner | Female | United States |
| Nereida | Magwood | Female | United States |
test > LAST API has been skipping erratically | Dec 20
Othello, the Moor of Venice:test > leased keurig wasn't loading | Dec 21
King Hamlet:ello world
X | as | Jak | J | Nm |
,mn | ,nnf | Adlk | Asn | ,amns |
Nm | Oi | Poi | B | Ijo |
,mn, | ;ih | Oug | Iu | G |
Ug | Bkjb | Kjbk | ;jbj | ;jb; |
Bkjb | Ugug | I9 | 68 | 0 |
90kjb | ,bnbiu | Ofif | P8gp | pugp |
Melgar | Female | UnitedStates |
Weiland | Female | UnitedStates |
Winward | Female | GreatBritain |
insertions
'; assert.equal(compose_paste.paste_handler_converter(input), "insertions"); // Zulip timestamp followed by text. input = ' good better '; assert.equal( compose_paste.paste_handler_converter(input), "
Test list: