mirror of
https://github.com/zulip/zulip.git
synced 2025-11-02 04:53:36 +00:00
Particularly when grouping in Sentry, pushing the ids and such into the additional data section helps group like errors together better.
491 lines
16 KiB
JavaScript
491 lines
16 KiB
JavaScript
"use strict";
|
|
|
|
const {strict: assert} = require("assert");
|
|
|
|
const {mock_cjs, mock_esm, zrequire} = require("./lib/namespace");
|
|
const {run_test} = require("./lib/test");
|
|
const blueslip = require("./lib/zblueslip");
|
|
const $ = require("./lib/zjquery");
|
|
const {user_settings} = require("./lib/zpage_params");
|
|
|
|
let clipboard_args;
|
|
class Clipboard {
|
|
constructor(...args) {
|
|
clipboard_args = args;
|
|
}
|
|
on(success, show_copied_confirmation) {
|
|
show_copied_confirmation();
|
|
}
|
|
}
|
|
|
|
mock_cjs("clipboard", Clipboard);
|
|
|
|
const realm_playground = mock_esm("../src/realm_playground");
|
|
const tippyjs = mock_esm("../src/tippyjs");
|
|
user_settings.emojiset = "apple";
|
|
|
|
const rm = zrequire("rendered_markdown");
|
|
const people = zrequire("people");
|
|
const user_groups = zrequire("user_groups");
|
|
const stream_data = zrequire("stream_data");
|
|
|
|
const iago = {
|
|
email: "iago@zulip.com",
|
|
user_id: 30,
|
|
full_name: "Iago",
|
|
};
|
|
|
|
const cordelia = {
|
|
email: "cordelia@zulip.com",
|
|
user_id: 31,
|
|
full_name: "Cordelia Lear",
|
|
};
|
|
people.init();
|
|
people.add_active_user(iago);
|
|
people.add_active_user(cordelia);
|
|
people.initialize_current_user(iago.user_id);
|
|
|
|
const group_me = {
|
|
name: "my user group",
|
|
id: 1,
|
|
members: [iago.user_id, cordelia.user_id],
|
|
};
|
|
const group_other = {
|
|
name: "other user group",
|
|
id: 2,
|
|
members: [cordelia.user_id],
|
|
};
|
|
user_groups.initialize({
|
|
realm_user_groups: [group_me, group_other],
|
|
});
|
|
|
|
const stream = {
|
|
subscribed: true,
|
|
color: "yellow",
|
|
name: "test",
|
|
stream_id: 3,
|
|
is_muted: true,
|
|
invite_only: false,
|
|
};
|
|
stream_data.add_sub(stream);
|
|
|
|
const $array = (array) => {
|
|
const each = (func) => {
|
|
for (const e of array) {
|
|
func.call(e);
|
|
}
|
|
};
|
|
return {each};
|
|
};
|
|
|
|
const get_content_element = () => {
|
|
const $content = $.create("content-stub");
|
|
$content.set_find_results(".user-mention", $array([]));
|
|
$content.set_find_results(".user-group-mention", $array([]));
|
|
$content.set_find_results("a.stream", $array([]));
|
|
$content.set_find_results("a.stream-topic", $array([]));
|
|
$content.set_find_results("time", $array([]));
|
|
$content.set_find_results("span.timestamp-error", $array([]));
|
|
$content.set_find_results(".emoji", $array([]));
|
|
$content.set_find_results("div.spoiler-header", $array([]));
|
|
$content.set_find_results("div.codehilite", $array([]));
|
|
|
|
// Fend off dumb security bugs by forcing devs to be
|
|
// intentional about HTML manipulation.
|
|
/* istanbul ignore next */
|
|
function security_violation() {
|
|
throw new Error(`
|
|
Be super careful about HTML manipulation.
|
|
|
|
Make sure your test objects set up their own
|
|
functions to validate that calls to html/prepend/append
|
|
use trusted values.
|
|
`);
|
|
}
|
|
$content.html = security_violation;
|
|
$content.prepend = security_violation;
|
|
$content.append = security_violation;
|
|
return $content;
|
|
};
|
|
|
|
run_test("misc_helpers", () => {
|
|
const $elem = $.create("user-mention");
|
|
rm.set_name_in_mention_element($elem, "Aaron");
|
|
assert.equal($elem.text(), "@Aaron");
|
|
$elem.addClass("silent");
|
|
rm.set_name_in_mention_element($elem, "Aaron, but silent");
|
|
assert.equal($elem.text(), "Aaron, but silent");
|
|
});
|
|
|
|
run_test("user-mention", () => {
|
|
// Setup
|
|
const $content = get_content_element();
|
|
const $iago = $.create("user-mention(iago)");
|
|
$iago.set_find_results(".highlight", false);
|
|
$iago.attr("data-user-id", iago.user_id);
|
|
const $cordelia = $.create("user-mention(cordelia)");
|
|
$cordelia.set_find_results(".highlight", false);
|
|
$cordelia.attr("data-user-id", cordelia.user_id);
|
|
$content.set_find_results(".user-mention", $array([$iago, $cordelia]));
|
|
|
|
// Initial asserts
|
|
assert.ok(!$iago.hasClass("user-mention-me"));
|
|
assert.equal($iago.text(), "never-been-set");
|
|
assert.equal($cordelia.text(), "never-been-set");
|
|
|
|
rm.update_elements($content);
|
|
|
|
// Final asserts
|
|
assert.ok($iago.hasClass("user-mention-me"));
|
|
assert.equal($iago.text(), `@${iago.full_name}`);
|
|
assert.equal($cordelia.text(), `@${cordelia.full_name}`);
|
|
});
|
|
|
|
run_test("user-mention (wildcard)", () => {
|
|
// Setup
|
|
const $content = get_content_element();
|
|
const $mention = $.create("mention");
|
|
$mention.attr("data-user-id", "*");
|
|
$content.set_find_results(".user-mention", $array([$mention]));
|
|
|
|
assert.ok(!$mention.hasClass("user-mention-me"));
|
|
rm.update_elements($content);
|
|
assert.ok($mention.hasClass("user-mention-me"));
|
|
});
|
|
|
|
run_test("user-mention (email)", () => {
|
|
// Setup
|
|
const $content = get_content_element();
|
|
const $mention = $.create("mention");
|
|
$mention.attr("data-user-email", cordelia.email);
|
|
$mention.set_find_results(".highlight", false);
|
|
$content.set_find_results(".user-mention", $array([$mention]));
|
|
|
|
rm.update_elements($content);
|
|
assert.ok(!$mention.hasClass("user-mention-me"));
|
|
assert.equal($mention.text(), "@Cordelia Lear");
|
|
});
|
|
|
|
run_test("user-mention (missing)", () => {
|
|
const $content = get_content_element();
|
|
const $mention = $.create("mention");
|
|
$content.set_find_results(".user-mention", $array([$mention]));
|
|
|
|
rm.update_elements($content);
|
|
assert.ok(!$mention.hasClass("user-mention-me"));
|
|
});
|
|
|
|
run_test("user-group-mention", () => {
|
|
// Setup
|
|
const $content = get_content_element();
|
|
const $group_me = $.create("user-group-mention(me)");
|
|
$group_me.set_find_results(".highlight", false);
|
|
$group_me.attr("data-user-group-id", group_me.id);
|
|
const $group_other = $.create("user-group-mention(other)");
|
|
$group_other.set_find_results(".highlight", false);
|
|
$group_other.attr("data-user-group-id", group_other.id);
|
|
$content.set_find_results(".user-group-mention", $array([$group_me, $group_other]));
|
|
|
|
// Initial asserts
|
|
assert.ok(!$group_me.hasClass("user-mention-me"));
|
|
assert.equal($group_me.text(), "never-been-set");
|
|
assert.equal($group_other.text(), "never-been-set");
|
|
|
|
rm.update_elements($content);
|
|
|
|
// Final asserts
|
|
assert.ok($group_me.hasClass("user-mention-me"));
|
|
assert.equal($group_me.text(), `@${group_me.name}`);
|
|
assert.equal($group_other.text(), `@${group_other.name}`);
|
|
});
|
|
|
|
run_test("user-group-mention (error)", () => {
|
|
const $content = get_content_element();
|
|
const $group = $.create("user-group-mention(bogus)");
|
|
$group.attr("data-user-group-id", "not-even-a-number");
|
|
$content.set_find_results(".user-group-mention", $array([$group]));
|
|
|
|
rm.update_elements($content);
|
|
|
|
assert.ok(!$group.hasClass("user-mention-me"));
|
|
});
|
|
|
|
run_test("user-group-mention (missing)", () => {
|
|
const $content = get_content_element();
|
|
const $group = $.create("whatever");
|
|
$content.set_find_results(".user-group-mention", $array([$group]));
|
|
|
|
rm.update_elements($content);
|
|
|
|
assert.ok(!$group.hasClass("user-mention-me"));
|
|
});
|
|
|
|
run_test("stream-links", () => {
|
|
// Setup
|
|
const $content = get_content_element();
|
|
const $stream = $.create("a.stream");
|
|
$stream.set_find_results(".highlight", false);
|
|
$stream.attr("data-stream-id", stream.stream_id);
|
|
const $stream_topic = $.create("a.stream-topic");
|
|
$stream_topic.set_find_results(".highlight", false);
|
|
$stream_topic.attr("data-stream-id", stream.stream_id);
|
|
$stream_topic.text("#random > topic name > still the topic name");
|
|
$content.set_find_results("a.stream", $array([$stream]));
|
|
$content.set_find_results("a.stream-topic", $array([$stream_topic]));
|
|
|
|
// Initial asserts
|
|
assert.equal($stream.text(), "never-been-set");
|
|
assert.equal($stream_topic.text(), "#random > topic name > still the topic name");
|
|
|
|
rm.update_elements($content);
|
|
|
|
// Final asserts
|
|
assert.equal($stream.text(), `#${stream.name}`);
|
|
assert.equal($stream_topic.text(), `#${stream.name} > topic name > still the topic name`);
|
|
});
|
|
|
|
run_test("timestamp without time", () => {
|
|
const $content = get_content_element();
|
|
const $timestamp = $.create("timestamp without actual time");
|
|
$content.set_find_results("time", $array([$timestamp]));
|
|
|
|
rm.update_elements($content);
|
|
assert.equal($timestamp.text(), "never-been-set");
|
|
});
|
|
|
|
run_test("timestamp", ({mock_template}) => {
|
|
mock_template("markdown_timestamp.hbs", true, (data, html) => {
|
|
assert.deepEqual(data, {text: "Thu, Jan 1, 1970, 12:00 AM"});
|
|
return html;
|
|
});
|
|
|
|
// Setup
|
|
const $content = get_content_element();
|
|
const $timestamp = $.create("timestamp(valid)");
|
|
$timestamp.attr("datetime", "1970-01-01T00:00:01Z");
|
|
const $timestamp_invalid = $.create("timestamp(invalid)");
|
|
$timestamp_invalid.attr("datetime", "invalid");
|
|
$content.set_find_results("time", $array([$timestamp, $timestamp_invalid]));
|
|
blueslip.expect("error", "Could not parse datetime supplied by backend");
|
|
|
|
// Initial asserts
|
|
assert.equal($timestamp.text(), "never-been-set");
|
|
assert.equal($timestamp_invalid.text(), "never-been-set");
|
|
|
|
rm.update_elements($content);
|
|
|
|
// Final asserts
|
|
assert.equal($timestamp.html(), '<i class="fa fa-clock-o"></i>\nThu, Jan 1, 1970, 12:00 AM\n');
|
|
assert.equal($timestamp_invalid.text(), "never-been-set");
|
|
});
|
|
|
|
run_test("timestamp-twenty-four-hour-time", ({mock_template, override}) => {
|
|
mock_template("markdown_timestamp.hbs", true, (data, html) => {
|
|
// sanity check incoming data
|
|
assert.ok(data.text.startsWith("Wed, Jul 15, 2020, "));
|
|
return html;
|
|
});
|
|
|
|
const $content = get_content_element();
|
|
const $timestamp = $.create("timestamp");
|
|
$timestamp.attr("datetime", "2020-07-15T20:40:00Z");
|
|
$content.set_find_results("time", $array([$timestamp]));
|
|
|
|
// We will temporarily change the 24h setting for this test.
|
|
override(user_settings, "twenty_four_hour_time", true);
|
|
rm.update_elements($content);
|
|
assert.equal($timestamp.html(), '<i class="fa fa-clock-o"></i>\nWed, Jul 15, 2020, 20:40\n');
|
|
|
|
override(user_settings, "twenty_four_hour_time", false);
|
|
rm.update_elements($content);
|
|
assert.equal($timestamp.html(), '<i class="fa fa-clock-o"></i>\nWed, Jul 15, 2020, 8:40 PM\n');
|
|
});
|
|
|
|
run_test("timestamp-error", () => {
|
|
// Setup
|
|
const $content = get_content_element();
|
|
const $timestamp_error = $.create("timestamp-error");
|
|
$timestamp_error.text("Invalid time format: the-time-format");
|
|
$content.set_find_results("span.timestamp-error", $array([$timestamp_error]));
|
|
|
|
// Initial assert
|
|
assert.equal($timestamp_error.text(), "Invalid time format: the-time-format");
|
|
|
|
rm.update_elements($content);
|
|
|
|
// Final assert
|
|
assert.equal($timestamp_error.text(), "translated: Invalid time format: the-time-format");
|
|
});
|
|
|
|
run_test("emoji", () => {
|
|
// Setup
|
|
const $content = get_content_element();
|
|
const $emoji = $.create("emoji-stub");
|
|
$emoji.attr("title", "tada");
|
|
let called = false;
|
|
$emoji.replaceWith = (f) => {
|
|
const text = f.call($emoji);
|
|
assert.equal(":tada:", text);
|
|
called = true;
|
|
};
|
|
$content.set_find_results(".emoji", $emoji);
|
|
user_settings.emojiset = "text";
|
|
|
|
rm.update_elements($content);
|
|
|
|
assert.ok(called);
|
|
|
|
// Set page parameters back so that test run order is independent
|
|
user_settings.emojiset = "apple";
|
|
});
|
|
|
|
run_test("spoiler-header", () => {
|
|
// Setup
|
|
const $content = get_content_element();
|
|
const $header = $.create("div.spoiler-header");
|
|
$content.set_find_results("div.spoiler-header", $array([$header]));
|
|
|
|
// Test that the show/hide button gets added to a spoiler header.
|
|
const label = "My spoiler header";
|
|
const toggle_button_html =
|
|
'<span class="spoiler-button" aria-expanded="false"><span class="spoiler-arrow"></span></span>';
|
|
$header.html(label);
|
|
rm.update_elements($content);
|
|
assert.equal(toggle_button_html + label, $header.html());
|
|
});
|
|
|
|
run_test("spoiler-header-empty-fill", () => {
|
|
// Setup
|
|
const $content = get_content_element();
|
|
const $header = $.create("div.spoiler-header");
|
|
$content.set_find_results("div.spoiler-header", $array([$header]));
|
|
|
|
// Test that an empty header gets the default text applied (through i18n filter).
|
|
const toggle_button_html =
|
|
'<span class="spoiler-button" aria-expanded="false"><span class="spoiler-arrow"></span></span>';
|
|
$header.empty();
|
|
rm.update_elements($content);
|
|
assert.equal(toggle_button_html + "<p>translated HTML: Spoiler</p>", $header.html());
|
|
});
|
|
|
|
function assert_clipboard_setup() {
|
|
assert.equal(clipboard_args[0], "copy-code-stub");
|
|
const text = clipboard_args[1].text({
|
|
to_$: () => ({
|
|
siblings(arg) {
|
|
assert.equal(arg, "code");
|
|
return {
|
|
text: () => "text",
|
|
};
|
|
},
|
|
}),
|
|
});
|
|
assert.equal(text, "text");
|
|
}
|
|
|
|
function test_code_playground(mock_template, viewing_code) {
|
|
const $content = get_content_element();
|
|
const $hilite = $.create("div.codehilite");
|
|
const $pre = $.create("hilite-pre");
|
|
$content.set_find_results("div.codehilite", $array([$hilite]));
|
|
$hilite.set_find_results("pre", $pre);
|
|
|
|
$hilite.data("code-language", "javascript");
|
|
|
|
const $copy_code_button = $.create("copy_code_button", {children: ["copy-code-stub"]});
|
|
const $view_code_in_playground = $.create("view_code_in_playground");
|
|
|
|
// The code playground code prepends a few buttons
|
|
// to the <pre> section of a highlighted piece of code.
|
|
// The args to prepend should be jQuery objects (or in
|
|
// our case "fake" zjquery objects).
|
|
const prepends = [];
|
|
$pre.prepend = (arg) => {
|
|
assert.ok(arg.__zjquery, "We should only prepend jQuery objects.");
|
|
prepends.push(arg);
|
|
};
|
|
|
|
mock_template("copy_code_button.hbs", false, (data) => {
|
|
assert.equal(data, undefined);
|
|
return {to_$: () => $copy_code_button};
|
|
});
|
|
|
|
if (viewing_code) {
|
|
mock_template("view_code_in_playground.hbs", false, (data) => {
|
|
assert.equal(data, undefined);
|
|
return {to_$: () => $view_code_in_playground};
|
|
});
|
|
}
|
|
|
|
rm.update_elements($content);
|
|
|
|
return {
|
|
prepends,
|
|
$copy_code: $copy_code_button,
|
|
$view_code: $view_code_in_playground,
|
|
};
|
|
}
|
|
|
|
run_test("code playground none", ({override, mock_template}) => {
|
|
override(realm_playground, "get_playground_info_for_languages", (language) => {
|
|
assert.equal(language, "javascript");
|
|
return undefined;
|
|
});
|
|
|
|
override(tippyjs, "show_copied_confirmation", () => {});
|
|
|
|
const {prepends, $copy_code, $view_code} = test_code_playground(mock_template, false);
|
|
assert.deepEqual(prepends, [$copy_code]);
|
|
assert_clipboard_setup();
|
|
|
|
assert.equal($view_code.attr("data-tippy-content"), undefined);
|
|
assert.equal($view_code.attr("aria-label"), undefined);
|
|
});
|
|
|
|
run_test("code playground single", ({override, mock_template}) => {
|
|
override(realm_playground, "get_playground_info_for_languages", (language) => {
|
|
assert.equal(language, "javascript");
|
|
return [{name: "Some Javascript Playground"}];
|
|
});
|
|
|
|
override(tippyjs, "show_copied_confirmation", () => {});
|
|
|
|
const {prepends, $copy_code, $view_code} = test_code_playground(mock_template, true);
|
|
assert.deepEqual(prepends, [$view_code, $copy_code]);
|
|
assert_clipboard_setup();
|
|
|
|
assert.equal(
|
|
$view_code.attr("data-tippy-content"),
|
|
"translated: View in Some Javascript Playground",
|
|
);
|
|
assert.equal($view_code.attr("aria-label"), "translated: View in Some Javascript Playground");
|
|
assert.equal($view_code.attr("aria-haspopup"), undefined);
|
|
});
|
|
|
|
run_test("code playground multiple", ({override, mock_template}) => {
|
|
override(realm_playground, "get_playground_info_for_languages", (language) => {
|
|
assert.equal(language, "javascript");
|
|
return ["whatever", "whatever"];
|
|
});
|
|
|
|
override(tippyjs, "show_copied_confirmation", () => {});
|
|
|
|
const {prepends, $copy_code, $view_code} = test_code_playground(mock_template, true);
|
|
assert.deepEqual(prepends, [$view_code, $copy_code]);
|
|
assert_clipboard_setup();
|
|
|
|
assert.equal($view_code.attr("data-tippy-content"), "translated: View in playground");
|
|
assert.equal($view_code.attr("aria-label"), "translated: View in playground");
|
|
assert.equal($view_code.attr("aria-haspopup"), "true");
|
|
});
|
|
|
|
run_test("rtl", () => {
|
|
const $content = get_content_element();
|
|
|
|
$content.text("مرحبا");
|
|
|
|
assert.ok(!$content.hasClass("rtl"));
|
|
rm.update_elements($content);
|
|
assert.ok($content.hasClass("rtl"));
|
|
});
|