mirror of https://github.com/zulip/zulip.git
1940 lines
62 KiB
JavaScript
1940 lines
62 KiB
JavaScript
"use strict";
|
|
|
|
const {strict: assert} = require("assert");
|
|
|
|
const {JSDOM} = require("jsdom");
|
|
const MockDate = require("mockdate");
|
|
const rewiremock = require("rewiremock/node");
|
|
|
|
const {stub_templates} = require("../zjsunit/handlebars");
|
|
const {set_global, zrequire} = require("../zjsunit/namespace");
|
|
const {run_test} = require("../zjsunit/test");
|
|
const $ = require("../zjsunit/zjquery");
|
|
|
|
const events = require("./lib/events");
|
|
|
|
const noop = () => {};
|
|
|
|
set_global("DOMParser", new JSDOM().window.DOMParser);
|
|
set_global("compose_actions", {
|
|
update_placeholder_text: noop,
|
|
});
|
|
|
|
const _navigator = {
|
|
platform: "",
|
|
};
|
|
|
|
const _document = {
|
|
execCommand() {
|
|
return false;
|
|
},
|
|
location: {},
|
|
to_$: () => $("document-stub"),
|
|
};
|
|
|
|
const _drafts = {
|
|
__esModule: true,
|
|
delete_draft_after_send: noop,
|
|
};
|
|
|
|
const sent_messages = {
|
|
__esModule: true,
|
|
start_tracking_message: noop,
|
|
};
|
|
const _notifications = {
|
|
__esModule: true,
|
|
notify_above_composebox: noop,
|
|
clear_compose_notifications: noop,
|
|
};
|
|
const reminder = {
|
|
is_deferred_delivery: noop,
|
|
};
|
|
|
|
set_global("document", _document);
|
|
rewiremock("../../static/js/drafts").with(_drafts);
|
|
set_global("navigator", _navigator);
|
|
rewiremock("../../static/js/notifications").with(_notifications);
|
|
set_global("reminder", reminder);
|
|
rewiremock("../../static/js/sent_messages").with(sent_messages);
|
|
rewiremock("../../static/js/rendered_markdown").with({
|
|
update_elements: () => {},
|
|
});
|
|
|
|
const local_message = {__esModule: true};
|
|
rewiremock("../../static/js/local_message").with(local_message);
|
|
const transmit = {__esModule: true};
|
|
rewiremock("../../static/js/transmit").with(transmit);
|
|
const channel = {__esModule: true};
|
|
rewiremock("../../static/js/channel").with(channel);
|
|
const stream_edit = {__esModule: true};
|
|
rewiremock("../../static/js/stream_edit").with(stream_edit);
|
|
const markdown = {__esModule: true};
|
|
rewiremock("../../static/js/markdown").with(markdown);
|
|
const loading = {__esModule: true};
|
|
rewiremock("../../static/js/loading").with(loading);
|
|
const page_params = set_global("page_params", {});
|
|
const resize = {__esModule: true};
|
|
rewiremock("../../static/js/resize").with(resize);
|
|
const subs = set_global("subs", {});
|
|
const ui_util = {__esModule: true};
|
|
|
|
rewiremock("../../static/js/ui_util").with(ui_util);
|
|
|
|
// Setting these up so that we can test that links to uploads within messages are
|
|
// automatically converted to server relative links.
|
|
document.location.protocol = "https:";
|
|
document.location.host = "foo.com";
|
|
|
|
const fake_now = 555;
|
|
MockDate.set(new Date(fake_now * 1000));
|
|
|
|
rewiremock.enable();
|
|
|
|
const compose_ui = zrequire("compose_ui");
|
|
const peer_data = zrequire("peer_data");
|
|
const util = zrequire("util");
|
|
const rtl = zrequire("rtl");
|
|
const stream_data = zrequire("stream_data");
|
|
const compose_state = zrequire("compose_state");
|
|
const people = zrequire("people");
|
|
zrequire("input_pill");
|
|
const compose_pm_pill = zrequire("compose_pm_pill");
|
|
const echo = zrequire("echo");
|
|
const compose = zrequire("compose");
|
|
const upload = zrequire("upload");
|
|
const server_events_dispatch = zrequire("server_events_dispatch");
|
|
const settings_config = zrequire("settings_config");
|
|
|
|
people.small_avatar_url_for_person = () => "http://example.com/example.png";
|
|
|
|
function stub_out_video_calls() {
|
|
const elem = $("#below-compose-content .video_link");
|
|
elem.toggle = (show) => {
|
|
if (show) {
|
|
elem.show();
|
|
} else {
|
|
elem.hide();
|
|
}
|
|
};
|
|
}
|
|
|
|
function reset_jquery() {
|
|
// Avoid leaks.
|
|
$.clear_all_elements();
|
|
}
|
|
|
|
const new_user = {
|
|
email: "new_user@example.com",
|
|
user_id: 101,
|
|
full_name: "New User",
|
|
date_joined: new Date(),
|
|
};
|
|
|
|
const me = {
|
|
email: "me@example.com",
|
|
user_id: 30,
|
|
full_name: "Me Myself",
|
|
date_joined: new Date(),
|
|
};
|
|
|
|
const alice = {
|
|
email: "alice@example.com",
|
|
user_id: 31,
|
|
full_name: "Alice",
|
|
};
|
|
|
|
const bob = {
|
|
email: "bob@example.com",
|
|
user_id: 32,
|
|
full_name: "Bob",
|
|
};
|
|
|
|
people.add_active_user(new_user);
|
|
people.add_active_user(me);
|
|
people.initialize_current_user(me.user_id);
|
|
|
|
people.add_active_user(alice);
|
|
people.add_active_user(bob);
|
|
|
|
function test_ui(label, f) {
|
|
// The sloppy_$ flag lets us re-use setup from prior tests.
|
|
run_test(label, f, {sloppy_$: true});
|
|
}
|
|
|
|
test_ui("validate_stream_message_address_info", () => {
|
|
const sub = {
|
|
stream_id: 101,
|
|
name: "social",
|
|
subscribed: true,
|
|
};
|
|
stream_data.add_sub(sub);
|
|
assert(compose.validate_stream_message_address_info("social"));
|
|
|
|
sub.subscribed = false;
|
|
stream_data.add_sub(sub);
|
|
stub_templates((template_name) => {
|
|
assert.equal(template_name, "compose_not_subscribed");
|
|
return "compose_not_subscribed_stub";
|
|
});
|
|
assert(!compose.validate_stream_message_address_info("social"));
|
|
assert.equal($("#compose-error-msg").html(), "compose_not_subscribed_stub");
|
|
|
|
page_params.narrow_stream = false;
|
|
channel.post = (payload) => {
|
|
assert.equal(payload.data.stream, "social");
|
|
payload.data.subscribed = true;
|
|
payload.success(payload.data);
|
|
};
|
|
assert(compose.validate_stream_message_address_info("social"));
|
|
|
|
sub.name = "Frontend";
|
|
sub.stream_id = 102;
|
|
stream_data.add_sub(sub);
|
|
channel.post = (payload) => {
|
|
assert.equal(payload.data.stream, "Frontend");
|
|
payload.data.subscribed = false;
|
|
payload.success(payload.data);
|
|
};
|
|
assert(!compose.validate_stream_message_address_info("Frontend"));
|
|
assert.equal($("#compose-error-msg").html(), "compose_not_subscribed_stub");
|
|
|
|
channel.post = (payload) => {
|
|
assert.equal(payload.data.stream, "Frontend");
|
|
payload.error({status: 404});
|
|
};
|
|
assert(!compose.validate_stream_message_address_info("Frontend"));
|
|
assert.equal(
|
|
$("#compose-error-msg").html(),
|
|
"translated: <p>The stream <b>Frontend</b> does not exist.</p><p>Manage your subscriptions <a href='#streams/all'>on your Streams page</a>.</p>",
|
|
);
|
|
|
|
channel.post = (payload) => {
|
|
assert.equal(payload.data.stream, "social");
|
|
payload.error({status: 500});
|
|
};
|
|
assert(!compose.validate_stream_message_address_info("social"));
|
|
assert.equal($("#compose-error-msg").html(), i18n.t("Error checking subscription"));
|
|
});
|
|
|
|
test_ui("validate", () => {
|
|
function initialize_pm_pill() {
|
|
reset_jquery();
|
|
|
|
$("#compose-send-button").prop("disabled", false);
|
|
$("#compose-send-button").trigger("focus");
|
|
$("#sending-indicator").hide();
|
|
|
|
const pm_pill_container = $.create("fake-pm-pill-container");
|
|
$("#private_message_recipient")[0] = {};
|
|
$("#private_message_recipient").set_parent(pm_pill_container);
|
|
pm_pill_container.set_find_results(".input", $("#private_message_recipient"));
|
|
$("#private_message_recipient").before = noop;
|
|
|
|
compose_pm_pill.initialize();
|
|
|
|
ui_util.place_caret_at_end = noop;
|
|
|
|
$("#zephyr-mirror-error").is = noop;
|
|
|
|
stub_templates((fn) => {
|
|
assert.equal(fn, "input_pill");
|
|
return "<div>pill-html</div>";
|
|
});
|
|
}
|
|
|
|
function add_content_to_compose_box() {
|
|
$("#compose-textarea").val("foobarfoobar");
|
|
}
|
|
|
|
initialize_pm_pill();
|
|
assert(!compose.validate());
|
|
assert(!$("#sending-indicator").visible());
|
|
assert(!$("#compose-send-button").is_focused());
|
|
assert.equal($("#compose-send-button").prop("disabled"), false);
|
|
assert.equal($("#compose-error-msg").html(), i18n.t("You have nothing to send!"));
|
|
|
|
reminder.is_deferred_delivery = () => true;
|
|
compose.validate();
|
|
assert.equal($("#sending-indicator").text(), "translated: Scheduling...");
|
|
reminder.is_deferred_delivery = noop;
|
|
|
|
add_content_to_compose_box();
|
|
let zephyr_checked = false;
|
|
$("#zephyr-mirror-error").is = () => {
|
|
if (!zephyr_checked) {
|
|
zephyr_checked = true;
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
assert(!compose.validate());
|
|
assert(zephyr_checked);
|
|
assert.equal(
|
|
$("#compose-error-msg").html(),
|
|
i18n.t("You need to be running Zephyr mirroring in order to send messages!"),
|
|
);
|
|
|
|
initialize_pm_pill();
|
|
add_content_to_compose_box();
|
|
|
|
// test validating private messages
|
|
compose_state.set_message_type("private");
|
|
|
|
compose_state.private_message_recipient("");
|
|
assert(!compose.validate());
|
|
assert.equal(
|
|
$("#compose-error-msg").html(),
|
|
i18n.t("Please specify at least one valid recipient"),
|
|
);
|
|
|
|
initialize_pm_pill();
|
|
add_content_to_compose_box();
|
|
compose_state.private_message_recipient("foo@zulip.com");
|
|
|
|
assert(!compose.validate());
|
|
|
|
assert.equal(
|
|
$("#compose-error-msg").html(),
|
|
i18n.t("Please specify at least one valid recipient", {}),
|
|
);
|
|
|
|
compose_state.private_message_recipient("foo@zulip.com,alice@zulip.com");
|
|
assert(!compose.validate());
|
|
|
|
assert.equal(
|
|
$("#compose-error-msg").html(),
|
|
i18n.t("Please specify at least one valid recipient", {}),
|
|
);
|
|
|
|
people.add_active_user(bob);
|
|
compose_state.private_message_recipient("bob@example.com");
|
|
assert(compose.validate());
|
|
|
|
page_params.realm_is_zephyr_mirror_realm = true;
|
|
assert(compose.validate());
|
|
page_params.realm_is_zephyr_mirror_realm = false;
|
|
|
|
compose_state.set_message_type("stream");
|
|
compose_state.stream_name("");
|
|
assert(!compose.validate());
|
|
assert.equal($("#compose-error-msg").html(), i18n.t("Please specify a stream"));
|
|
|
|
compose_state.stream_name("Denmark");
|
|
page_params.realm_mandatory_topics = true;
|
|
compose_state.topic("");
|
|
assert(!compose.validate());
|
|
assert.equal($("#compose-error-msg").html(), i18n.t("Please specify a topic"));
|
|
});
|
|
|
|
test_ui("get_invalid_recipient_emails", (override) => {
|
|
const welcome_bot = {
|
|
email: "welcome-bot@example.com",
|
|
user_id: 124,
|
|
full_name: "Welcome Bot",
|
|
};
|
|
|
|
page_params.user_id = 30;
|
|
|
|
const params = {};
|
|
params.realm_users = [];
|
|
params.realm_non_active_users = [];
|
|
params.cross_realm_bots = [welcome_bot];
|
|
|
|
people.initialize(page_params.user_id, params);
|
|
|
|
override(compose_state, "private_message_recipient", () => "welcome-bot@example.com");
|
|
assert.deepEqual(compose.get_invalid_recipient_emails(), []);
|
|
});
|
|
|
|
test_ui("test_wildcard_mention_allowed", () => {
|
|
page_params.realm_wildcard_mention_policy =
|
|
settings_config.wildcard_mention_policy_values.by_everyone.code;
|
|
page_params.is_guest = true;
|
|
page_params.is_admin = false;
|
|
assert(compose.wildcard_mention_allowed());
|
|
|
|
page_params.realm_wildcard_mention_policy =
|
|
settings_config.wildcard_mention_policy_values.nobody.code;
|
|
page_params.is_admin = true;
|
|
assert(!compose.wildcard_mention_allowed());
|
|
|
|
page_params.realm_wildcard_mention_policy =
|
|
settings_config.wildcard_mention_policy_values.by_members.code;
|
|
page_params.is_guest = true;
|
|
page_params.is_admin = false;
|
|
assert(!compose.wildcard_mention_allowed());
|
|
|
|
page_params.is_guest = false;
|
|
assert(compose.wildcard_mention_allowed());
|
|
|
|
page_params.realm_wildcard_mention_policy =
|
|
settings_config.wildcard_mention_policy_values.by_stream_admins_only.code;
|
|
page_params.is_admin = false;
|
|
assert(!compose.wildcard_mention_allowed());
|
|
|
|
// TODO: Add a by_admins_only case when we implement stream-level administrators.
|
|
|
|
page_params.is_admin = true;
|
|
assert(compose.wildcard_mention_allowed());
|
|
|
|
page_params.realm_wildcard_mention_policy =
|
|
settings_config.wildcard_mention_policy_values.by_full_members.code;
|
|
const person = people.get_by_user_id(page_params.user_id);
|
|
person.date_joined = new Date(Date.now());
|
|
page_params.realm_waiting_period_threshold = 10;
|
|
|
|
assert(compose.wildcard_mention_allowed());
|
|
page_params.is_admin = false;
|
|
assert(!compose.wildcard_mention_allowed());
|
|
});
|
|
|
|
test_ui("validate_stream_message", () => {
|
|
// This test is in kind of continuation to test_validate but since it is
|
|
// primarily used to get coverage over functions called from validate()
|
|
// we are separating it up in different test. Though their relative position
|
|
// of execution should not be changed.
|
|
page_params.realm_mandatory_topics = false;
|
|
const sub = {
|
|
stream_id: 101,
|
|
name: "social",
|
|
subscribed: true,
|
|
};
|
|
stream_data.add_sub(sub);
|
|
compose_state.stream_name("social");
|
|
assert(compose.validate());
|
|
assert(!$("#compose-all-everyone").visible());
|
|
assert(!$("#compose-send-status").visible());
|
|
|
|
peer_data.get_subscriber_count = (stream_id) => {
|
|
assert.equal(stream_id, 101);
|
|
return 16;
|
|
};
|
|
stub_templates((template_name, data) => {
|
|
assert.equal(template_name, "compose_all_everyone");
|
|
assert.equal(data.count, 16);
|
|
return "compose_all_everyone_stub";
|
|
});
|
|
let compose_content;
|
|
$("#compose-all-everyone").append = (data) => {
|
|
compose_content = data;
|
|
};
|
|
|
|
compose.__Rewire__("wildcard_mention_allowed", () => true);
|
|
compose_state.message_content("Hey @**all**");
|
|
assert(!compose.validate());
|
|
assert.equal($("#compose-send-button").prop("disabled"), false);
|
|
assert(!$("#compose-send-status").visible());
|
|
assert.equal(compose_content, "compose_all_everyone_stub");
|
|
assert($("#compose-all-everyone").visible());
|
|
|
|
compose.__Rewire__("wildcard_mention_allowed", () => false);
|
|
assert(!compose.validate());
|
|
assert.equal(
|
|
$("#compose-error-msg").html(),
|
|
i18n.t("You do not have permission to use wildcard mentions in this stream."),
|
|
);
|
|
});
|
|
|
|
test_ui("test_validate_stream_message_post_policy_admin_only", () => {
|
|
// This test is in continuation with test_validate but it has been separated out
|
|
// for better readability. Their relative position of execution should not be changed.
|
|
// Although the position with respect to test_validate_stream_message does not matter
|
|
// as different stream is used for this test.
|
|
page_params.is_admin = false;
|
|
const sub = {
|
|
stream_id: 102,
|
|
name: "stream102",
|
|
subscribed: true,
|
|
stream_post_policy: stream_data.stream_post_policy_values.admins.code,
|
|
};
|
|
|
|
compose_state.topic("subject102");
|
|
compose_state.stream_name("stream102");
|
|
stream_data.add_sub(sub);
|
|
assert(!compose.validate());
|
|
assert.equal(
|
|
$("#compose-error-msg").html(),
|
|
i18n.t("Only organization admins are allowed to post to this stream."),
|
|
);
|
|
|
|
// Reset error message.
|
|
compose_state.stream_name("social");
|
|
|
|
page_params.is_admin = false;
|
|
page_params.is_guest = true;
|
|
|
|
compose_state.topic("subject102");
|
|
compose_state.stream_name("stream102");
|
|
assert(!compose.validate());
|
|
assert.equal(
|
|
$("#compose-error-msg").html(),
|
|
i18n.t("Only organization admins are allowed to post to this stream."),
|
|
);
|
|
});
|
|
|
|
test_ui("test_validate_stream_message_post_policy_full_members_only", () => {
|
|
page_params.is_admin = false;
|
|
page_params.is_guest = true;
|
|
const sub = {
|
|
stream_id: 103,
|
|
name: "stream103",
|
|
subscribed: true,
|
|
stream_post_policy: stream_data.stream_post_policy_values.non_new_members.code,
|
|
};
|
|
|
|
compose_state.topic("subject103");
|
|
compose_state.stream_name("stream103");
|
|
stream_data.add_sub(sub);
|
|
assert(!compose.validate());
|
|
assert.equal(
|
|
$("#compose-error-msg").html(),
|
|
i18n.t("Guests are not allowed to post to this stream."),
|
|
);
|
|
|
|
// reset compose_state.stream_name to 'social' again so that any tests occurring after this
|
|
// do not reproduce this error.
|
|
compose_state.stream_name("social");
|
|
// Reset page_params
|
|
page_params.is_guest = false;
|
|
});
|
|
|
|
test_ui("markdown_rtl", () => {
|
|
const textarea = $("#compose-textarea");
|
|
|
|
const event = {
|
|
keyCode: 65, // A
|
|
};
|
|
|
|
rtl.__Rewire__("get_direction", (text) => {
|
|
assert.equal(text, " foo");
|
|
return "rtl";
|
|
});
|
|
|
|
assert.equal(textarea.hasClass("rtl"), false);
|
|
|
|
textarea.val("```quote foo");
|
|
compose.handle_keyup(event, $("#compose-textarea"));
|
|
|
|
assert.equal(textarea.hasClass("rtl"), true);
|
|
});
|
|
|
|
// This is important for subsequent tests--put
|
|
// us back to the "normal" ltr case.
|
|
rtl.__Rewire__("get_direction", () => "ltr");
|
|
|
|
test_ui("markdown_ltr", () => {
|
|
const textarea = $("#compose-textarea");
|
|
|
|
const event = {
|
|
keyCode: 65, // A
|
|
};
|
|
|
|
assert.equal(textarea.hasClass("rtl"), true);
|
|
textarea.val("```quote foo");
|
|
compose.handle_keyup(event, textarea);
|
|
|
|
assert.equal(textarea.hasClass("rtl"), false);
|
|
});
|
|
|
|
test_ui("markdown_shortcuts", () => {
|
|
let queryCommandEnabled = true;
|
|
const event = {
|
|
keyCode: 66,
|
|
target: {
|
|
id: "compose-textarea",
|
|
},
|
|
stopPropagation: noop,
|
|
preventDefault: noop,
|
|
};
|
|
let input_text = "";
|
|
let range_start = 0;
|
|
let range_length = 0;
|
|
let compose_value = $("#compose_textarea").val();
|
|
let selected_word = "";
|
|
|
|
document.queryCommandEnabled = () => queryCommandEnabled;
|
|
document.execCommand = (cmd, bool, markdown) => {
|
|
const compose_textarea = $("#compose-textarea");
|
|
const value = compose_textarea.val();
|
|
$("#compose-textarea").val(
|
|
value.slice(0, compose_textarea.range().start) +
|
|
markdown +
|
|
value.slice(compose_textarea.range().end),
|
|
);
|
|
};
|
|
|
|
$("#compose-textarea")[0] = {};
|
|
$("#compose-textarea").range = () => ({
|
|
start: range_start,
|
|
end: range_start + range_length,
|
|
length: range_length,
|
|
range: noop,
|
|
text: $("#compose-textarea")
|
|
.val()
|
|
.slice(range_start, range_length + range_start),
|
|
});
|
|
$("#compose-textarea").caret = noop;
|
|
|
|
function test_i_typed(isCtrl, isCmd) {
|
|
// Test 'i' is typed correctly.
|
|
$("#compose-textarea").val("i");
|
|
event.keyCode = undefined;
|
|
event.which = 73;
|
|
event.metaKey = isCmd;
|
|
event.ctrlKey = isCtrl;
|
|
compose.handle_keydown(event, $("#compose-textarea"));
|
|
assert.equal("i", $("#compose-textarea").val());
|
|
}
|
|
|
|
function all_markdown_test(isCtrl, isCmd) {
|
|
input_text = "Any text.";
|
|
$("#compose-textarea").val(input_text);
|
|
compose_value = $("#compose-textarea").val();
|
|
// Select "text" word in compose box.
|
|
selected_word = "text";
|
|
range_start = compose_value.search(selected_word);
|
|
range_length = selected_word.length;
|
|
|
|
// Test bold:
|
|
// Mac env = Cmd+b
|
|
// Windows/Linux = Ctrl+b
|
|
event.keyCode = 66;
|
|
event.ctrlKey = isCtrl;
|
|
event.metaKey = isCmd;
|
|
compose.handle_keydown(event, $("#compose-textarea"));
|
|
assert.equal("Any **text**.", $("#compose-textarea").val());
|
|
// Test if no text is selected.
|
|
range_start = 0;
|
|
// Change cursor to first position.
|
|
range_length = 0;
|
|
compose.handle_keydown(event, $("#compose-textarea"));
|
|
assert.equal("****Any **text**.", $("#compose-textarea").val());
|
|
|
|
// Test italic:
|
|
// Mac = Cmd+I
|
|
// Windows/Linux = Ctrl+I
|
|
$("#compose-textarea").val(input_text);
|
|
range_start = compose_value.search(selected_word);
|
|
range_length = selected_word.length;
|
|
event.keyCode = 73;
|
|
event.shiftKey = false;
|
|
compose.handle_keydown(event, $("#compose-textarea"));
|
|
assert.equal("Any *text*.", $("#compose-textarea").val());
|
|
// Test if no text is selected.
|
|
range_length = 0;
|
|
// Change cursor to first position.
|
|
range_start = 0;
|
|
compose.handle_keydown(event, $("#compose-textarea"));
|
|
assert.equal("**Any *text*.", $("#compose-textarea").val());
|
|
|
|
// Test link insertion:
|
|
// Mac = Cmd+Shift+L
|
|
// Windows/Linux = Ctrl+Shift+L
|
|
$("#compose-textarea").val(input_text);
|
|
range_start = compose_value.search(selected_word);
|
|
range_length = selected_word.length;
|
|
event.keyCode = 76;
|
|
event.which = undefined;
|
|
event.shiftKey = true;
|
|
compose.handle_keydown(event, $("#compose-textarea"));
|
|
assert.equal("Any [text](url).", $("#compose-textarea").val());
|
|
// Test if exec command is not enabled in browser.
|
|
queryCommandEnabled = false;
|
|
compose.handle_keydown(event, $("#compose-textarea"));
|
|
}
|
|
|
|
// This function cross tests the Cmd/Ctrl + Markdown shortcuts in
|
|
// Mac and Linux/Windows environments. So in short, this tests
|
|
// that e.g. Cmd+B should be ignored on Linux/Windows and Ctrl+B
|
|
// should be ignored on Mac.
|
|
function os_specific_markdown_test(isCtrl, isCmd) {
|
|
input_text = "Any text.";
|
|
$("#compose-textarea").val(input_text);
|
|
compose_value = $("#compose-textarea").val();
|
|
selected_word = "text";
|
|
range_start = compose_value.search(selected_word);
|
|
range_length = selected_word.length;
|
|
event.metaKey = isCmd;
|
|
event.ctrlKey = isCtrl;
|
|
|
|
event.keyCode = 66;
|
|
compose.handle_keydown(event, $("#compose-textarea"));
|
|
assert.equal(input_text, $("#compose-textarea").val());
|
|
|
|
event.keyCode = 73;
|
|
event.shiftKey = false;
|
|
compose.handle_keydown(event, $("#compose-textarea"));
|
|
assert.equal(input_text, $("#compose-textarea").val());
|
|
|
|
event.keyCode = 76;
|
|
event.shiftKey = true;
|
|
compose.handle_keydown(event, $("#compose-textarea"));
|
|
assert.equal(input_text, $("#compose-textarea").val());
|
|
}
|
|
|
|
// These keyboard shortcuts differ as to what key one should use
|
|
// on MacOS vs. other platforms: Cmd (Mac) vs. Ctrl (non-Mac).
|
|
|
|
// Default (Linux/Windows) userAgent tests:
|
|
test_i_typed(false, false);
|
|
// Check all the Ctrl + Markdown shortcuts work correctly
|
|
all_markdown_test(true, false);
|
|
// The Cmd + Markdown shortcuts should do nothing on Linux/Windows
|
|
os_specific_markdown_test(false, true);
|
|
|
|
// Setting following platform to test in mac env
|
|
_navigator.platform = "MacIntel";
|
|
|
|
// Mac userAgent tests:
|
|
test_i_typed(false, false);
|
|
// The Ctrl + Markdown shortcuts should do nothing on mac
|
|
os_specific_markdown_test(true, false);
|
|
// Check all the Cmd + Markdown shortcuts work correctly
|
|
all_markdown_test(false, true);
|
|
|
|
// Reset userAgent
|
|
_navigator.userAgent = "";
|
|
});
|
|
|
|
test_ui("send_message_success", () => {
|
|
$("#compose-textarea").val("foobarfoobar");
|
|
$("#compose-textarea").trigger("blur");
|
|
$("#compose-send-status").show();
|
|
$("#compose-send-button").prop("disabled", true);
|
|
$("#sending-indicator").show();
|
|
|
|
let reify_message_id_checked;
|
|
echo.reify_message_id = (local_id, message_id) => {
|
|
assert.equal(local_id, "1001");
|
|
assert.equal(message_id, 12);
|
|
reify_message_id_checked = true;
|
|
};
|
|
|
|
compose.send_message_success("1001", 12, false);
|
|
|
|
assert.equal($("#compose-textarea").val(), "");
|
|
assert($("#compose-textarea").is_focused());
|
|
assert(!$("#compose-send-status").visible());
|
|
assert.equal($("#compose-send-button").prop("disabled"), false);
|
|
assert(!$("#sending-indicator").visible());
|
|
|
|
assert(reify_message_id_checked);
|
|
});
|
|
|
|
test_ui("send_message", () => {
|
|
// This is the common setup stuff for all of the four tests.
|
|
let stub_state;
|
|
function initialize_state_stub_dict() {
|
|
stub_state = {};
|
|
stub_state.send_msg_called = 0;
|
|
stub_state.get_events_running_called = 0;
|
|
stub_state.reify_message_id_checked = 0;
|
|
return stub_state;
|
|
}
|
|
|
|
set_global("setTimeout", (func) => {
|
|
func();
|
|
});
|
|
set_global("server_events", {
|
|
assert_get_events_running() {
|
|
stub_state.get_events_running_called += 1;
|
|
},
|
|
});
|
|
|
|
// Tests start here.
|
|
(function test_message_send_success_codepath() {
|
|
stub_state = initialize_state_stub_dict();
|
|
compose_state.topic("");
|
|
compose_state.set_message_type("private");
|
|
page_params.user_id = 101;
|
|
compose_state.__Rewire__("private_message_recipient", () => "alice@example.com");
|
|
|
|
const server_message_id = 127;
|
|
local_message.insert_message = (message) => {
|
|
assert.equal(message.timestamp, fake_now);
|
|
};
|
|
|
|
markdown.apply_markdown = () => {};
|
|
markdown.add_topic_links = () => {};
|
|
|
|
echo.try_deliver_locally = (message_request) => {
|
|
const local_id_float = 123.04;
|
|
return echo.insert_local_message(message_request, local_id_float);
|
|
};
|
|
transmit.send_message = (payload, success) => {
|
|
const single_msg = {
|
|
type: "private",
|
|
content: "[foobar](/user_uploads/123456)",
|
|
sender_id: 101,
|
|
queue_id: undefined,
|
|
stream: "",
|
|
topic: "",
|
|
to: `[${alice.user_id}]`,
|
|
reply_to: "alice@example.com",
|
|
private_message_recipient: "alice@example.com",
|
|
to_user_ids: "31",
|
|
local_id: "123.04",
|
|
locally_echoed: true,
|
|
};
|
|
|
|
assert.deepEqual(payload, single_msg);
|
|
payload.id = server_message_id;
|
|
success(payload);
|
|
stub_state.send_msg_called += 1;
|
|
};
|
|
echo.reify_message_id = (local_id, message_id) => {
|
|
assert.equal(typeof local_id, "string");
|
|
assert.equal(typeof message_id, "number");
|
|
assert.equal(message_id, server_message_id);
|
|
stub_state.reify_message_id_checked += 1;
|
|
};
|
|
|
|
// Setting message content with a host server link and we will assert
|
|
// later that this has been converted to a relative link.
|
|
$("#compose-textarea").val("[foobar](https://foo.com/user_uploads/123456)");
|
|
$("#compose-textarea").trigger("blur");
|
|
$("#compose-send-status").show();
|
|
$("#compose-send-button").prop("disabled", true);
|
|
$("#sending-indicator").show();
|
|
|
|
compose.send_message();
|
|
|
|
const state = {
|
|
get_events_running_called: 1,
|
|
reify_message_id_checked: 1,
|
|
send_msg_called: 1,
|
|
};
|
|
assert.deepEqual(stub_state, state);
|
|
assert.equal($("#compose-textarea").val(), "");
|
|
assert($("#compose-textarea").is_focused());
|
|
assert(!$("#compose-send-status").visible());
|
|
assert.equal($("#compose-send-button").prop("disabled"), false);
|
|
assert(!$("#sending-indicator").visible());
|
|
})();
|
|
|
|
// This is the additional setup which is common to both the tests below.
|
|
transmit.send_message = (payload, success, error) => {
|
|
stub_state.send_msg_called += 1;
|
|
error("Error sending message: Server says 408");
|
|
};
|
|
|
|
let echo_error_msg_checked;
|
|
|
|
echo.message_send_error = (local_id, error_response) => {
|
|
assert.equal(local_id, 123.04);
|
|
assert.equal(error_response, "Error sending message: Server says 408");
|
|
echo_error_msg_checked = true;
|
|
};
|
|
|
|
// Tests start here.
|
|
(function test_param_error_function_passed_from_send_message() {
|
|
stub_state = initialize_state_stub_dict();
|
|
|
|
compose.send_message();
|
|
|
|
const state = {
|
|
get_events_running_called: 1,
|
|
reify_message_id_checked: 0,
|
|
send_msg_called: 1,
|
|
};
|
|
assert.deepEqual(stub_state, state);
|
|
assert(echo_error_msg_checked);
|
|
})();
|
|
|
|
(function test_error_codepath_local_id_undefined() {
|
|
stub_state = initialize_state_stub_dict();
|
|
$("#compose-textarea").val("foobarfoobar");
|
|
$("#compose-textarea").trigger("blur");
|
|
$("#compose-send-status").show();
|
|
$("#compose-send-button").prop("disabled", true);
|
|
$("#sending-indicator").show();
|
|
$("#compose-textarea").off("select");
|
|
echo_error_msg_checked = false;
|
|
echo.try_deliver_locally = () => {};
|
|
|
|
sent_messages.get_new_local_id = () => "loc-55";
|
|
|
|
compose.send_message();
|
|
|
|
const state = {
|
|
get_events_running_called: 1,
|
|
reify_message_id_checked: 0,
|
|
send_msg_called: 1,
|
|
};
|
|
assert.deepEqual(stub_state, state);
|
|
assert(!echo_error_msg_checked);
|
|
assert.equal($("#compose-send-button").prop("disabled"), false);
|
|
assert.equal($("#compose-error-msg").html(), "Error sending message: Server says 408");
|
|
assert.equal($("#compose-textarea").val(), "foobarfoobar");
|
|
assert($("#compose-textarea").is_focused());
|
|
assert($("#compose-send-status").visible());
|
|
assert.equal($("#compose-send-button").prop("disabled"), false);
|
|
assert(!$("#sending-indicator").visible());
|
|
})();
|
|
});
|
|
|
|
test_ui("enter_with_preview_open", () => {
|
|
// Test sending a message with content.
|
|
compose_state.set_message_type("stream");
|
|
$("#compose-textarea").val("message me");
|
|
$("#compose-textarea").hide();
|
|
$("#undo_markdown_preview").show();
|
|
$("#preview_message_area").show();
|
|
$("#markdown_preview").hide();
|
|
page_params.enter_sends = true;
|
|
let send_message_called = false;
|
|
compose.__Rewire__("send_message", () => {
|
|
send_message_called = true;
|
|
});
|
|
compose.enter_with_preview_open();
|
|
assert($("#compose-textarea").visible());
|
|
assert(!$("#undo_markdown_preview").visible());
|
|
assert(!$("#preview_message_area").visible());
|
|
assert($("#markdown_preview").visible());
|
|
assert(send_message_called);
|
|
|
|
page_params.enter_sends = false;
|
|
$("#compose-textarea").trigger("blur");
|
|
compose.enter_with_preview_open();
|
|
assert($("#compose-textarea").is_focused());
|
|
|
|
// Test sending a message without content.
|
|
$("#compose-textarea").val("");
|
|
$("#preview_message_area").show();
|
|
$("#enter_sends").prop("checked", true);
|
|
page_params.enter_sends = true;
|
|
|
|
compose.enter_with_preview_open();
|
|
|
|
assert($("#enter_sends").prop("checked"));
|
|
assert.equal($("#compose-error-msg").html(), i18n.t("You have nothing to send!"));
|
|
});
|
|
|
|
test_ui("finish", () => {
|
|
(function test_when_compose_validation_fails() {
|
|
$("#compose_invite_users").show();
|
|
$("#compose-send-button").prop("disabled", false);
|
|
$("#compose-send-button").trigger("focus");
|
|
$("#sending-indicator").hide();
|
|
$("#compose-textarea").off("select");
|
|
$("#compose-textarea").val("");
|
|
const res = compose.finish();
|
|
assert.equal(res, false);
|
|
assert(!$("#compose_invite_users").visible());
|
|
assert(!$("#sending-indicator").visible());
|
|
assert(!$("#compose-send-button").is_focused());
|
|
assert.equal($("#compose-send-button").prop("disabled"), false);
|
|
assert.equal($("#compose-error-msg").html(), i18n.t("You have nothing to send!"));
|
|
})();
|
|
|
|
(function test_when_compose_validation_succeed() {
|
|
$("#compose-textarea").hide();
|
|
$("#undo_markdown_preview").show();
|
|
$("#preview_message_area").show();
|
|
$("#markdown_preview").hide();
|
|
$("#compose-textarea").val("foobarfoobar");
|
|
compose_state.set_message_type("private");
|
|
compose_state.__Rewire__("private_message_recipient", () => "bob@example.com");
|
|
|
|
let compose_finished_event_checked = false;
|
|
$(document).on("compose_finished.zulip", () => {
|
|
compose_finished_event_checked = true;
|
|
});
|
|
let send_message_called = false;
|
|
compose.__Rewire__("send_message", () => {
|
|
send_message_called = true;
|
|
});
|
|
assert(compose.finish());
|
|
assert($("#compose-textarea").visible());
|
|
assert(!$("#undo_markdown_preview").visible());
|
|
assert(!$("#preview_message_area").visible());
|
|
assert($("#markdown_preview").visible());
|
|
assert(send_message_called);
|
|
assert(compose_finished_event_checked);
|
|
})();
|
|
});
|
|
|
|
test_ui("warn_if_private_stream_is_linked", () => {
|
|
const test_sub = {
|
|
name: compose_state.stream_name(),
|
|
stream_id: 99,
|
|
};
|
|
|
|
stream_data.add_sub(test_sub);
|
|
peer_data.set_subscribers(test_sub.stream_id, [1, 2]);
|
|
|
|
let denmark = {
|
|
stream_id: 100,
|
|
name: "Denmark",
|
|
};
|
|
stream_data.add_sub(denmark);
|
|
|
|
peer_data.set_subscribers(denmark.stream_id, [1, 2, 3]);
|
|
|
|
function test_noop_case(invite_only) {
|
|
compose_state.set_message_type("stream");
|
|
denmark.invite_only = invite_only;
|
|
compose.warn_if_private_stream_is_linked(denmark);
|
|
assert.equal($("#compose_private_stream_alert").visible(), false);
|
|
}
|
|
|
|
test_noop_case(false);
|
|
// invite_only=true and current compose stream subscribers are a subset
|
|
// of mentioned_stream subscribers.
|
|
test_noop_case(true);
|
|
|
|
$("#compose_private").hide();
|
|
compose_state.set_message_type("stream");
|
|
|
|
const checks = [
|
|
(function () {
|
|
let called;
|
|
stub_templates((template_name, context) => {
|
|
called = true;
|
|
assert.equal(template_name, "compose_private_stream_alert");
|
|
assert.equal(context.stream_name, "Denmark");
|
|
return "fake-compose_private_stream_alert-template";
|
|
});
|
|
return function () {
|
|
assert(called);
|
|
};
|
|
})(),
|
|
|
|
(function () {
|
|
let called;
|
|
$("#compose_private_stream_alert").append = (html) => {
|
|
called = true;
|
|
assert.equal(html, "fake-compose_private_stream_alert-template");
|
|
};
|
|
return function () {
|
|
assert(called);
|
|
};
|
|
})(),
|
|
];
|
|
|
|
denmark = {
|
|
invite_only: true,
|
|
name: "Denmark",
|
|
stream_id: 22,
|
|
};
|
|
stream_data.add_sub(denmark);
|
|
|
|
compose.warn_if_private_stream_is_linked(denmark);
|
|
assert.equal($("#compose_private_stream_alert").visible(), true);
|
|
|
|
for (const f of checks) {
|
|
f();
|
|
}
|
|
});
|
|
|
|
test_ui("initialize", () => {
|
|
// In this test we mostly do the setup stuff in addition to testing the
|
|
// normal workflow of the function. All the tests for the on functions are
|
|
// done in subsequent tests directly below this test.
|
|
|
|
let resize_watch_manual_resize_checked = false;
|
|
resize.watch_manual_resize = (elem) => {
|
|
assert.equal("#compose-textarea", elem);
|
|
resize_watch_manual_resize_checked = true;
|
|
};
|
|
let xmlhttprequest_checked = false;
|
|
set_global("XMLHttpRequest", function () {
|
|
this.upload = true;
|
|
xmlhttprequest_checked = true;
|
|
});
|
|
$("#compose #attach_files").addClass("notdisplayed");
|
|
|
|
set_global("document", "document-stub");
|
|
set_global("csrf_token", "fake-csrf-token");
|
|
|
|
page_params.max_file_upload_size_mib = 512;
|
|
|
|
let setup_upload_called = false;
|
|
let uppy_cancel_all_called = false;
|
|
upload.__Rewire__("setup_upload", (config) => {
|
|
assert.equal(config.mode, "compose");
|
|
setup_upload_called = true;
|
|
return {
|
|
cancelAll: () => {
|
|
uppy_cancel_all_called = true;
|
|
},
|
|
};
|
|
});
|
|
|
|
page_params.realm_available_video_chat_providers = {
|
|
disabled: {
|
|
id: 0,
|
|
name: "disabled",
|
|
},
|
|
jitsi_meet: {
|
|
id: 1,
|
|
name: "Jitsi Meet",
|
|
},
|
|
zoom: {
|
|
id: 3,
|
|
name: "Zoom",
|
|
},
|
|
big_blue_button: {
|
|
id: 4,
|
|
name: "Big Blue Button",
|
|
},
|
|
};
|
|
|
|
page_params.realm_video_chat_provider =
|
|
page_params.realm_available_video_chat_providers.disabled.id;
|
|
|
|
stub_out_video_calls();
|
|
compose.initialize();
|
|
|
|
assert(resize_watch_manual_resize_checked);
|
|
assert(xmlhttprequest_checked);
|
|
assert(!$("#compose #attach_files").hasClass("notdisplayed"));
|
|
assert(setup_upload_called);
|
|
|
|
let compose_actions_start_checked;
|
|
|
|
function set_up_compose_start_mock(expected_opts) {
|
|
compose_actions_start_checked = false;
|
|
|
|
set_global("compose_actions", {
|
|
start(msg_type, opts) {
|
|
assert.equal(msg_type, "stream");
|
|
assert.deepEqual(opts, expected_opts);
|
|
compose_actions_start_checked = true;
|
|
},
|
|
});
|
|
}
|
|
|
|
(function test_page_params_narrow_path() {
|
|
page_params.narrow = true;
|
|
|
|
reset_jquery();
|
|
stub_out_video_calls();
|
|
set_up_compose_start_mock({});
|
|
|
|
compose.initialize();
|
|
|
|
assert(compose_actions_start_checked);
|
|
})();
|
|
|
|
(function test_page_params_narrow_topic() {
|
|
page_params.narrow_topic = "testing";
|
|
|
|
reset_jquery();
|
|
stub_out_video_calls();
|
|
set_up_compose_start_mock({topic: "testing"});
|
|
|
|
compose.initialize();
|
|
|
|
assert(compose_actions_start_checked);
|
|
})();
|
|
|
|
(function test_abort_xhr() {
|
|
$("#compose-send-button").prop("disabled", true);
|
|
|
|
reset_jquery();
|
|
stub_out_video_calls();
|
|
compose.initialize();
|
|
|
|
compose.abort_xhr();
|
|
|
|
assert.equal($("#compose-send-button").attr(), undefined);
|
|
assert(uppy_cancel_all_called);
|
|
})();
|
|
});
|
|
|
|
test_ui("update_fade", () => {
|
|
const selector =
|
|
"#stream_message_recipient_stream,#stream_message_recipient_topic,#private_message_recipient";
|
|
const keyup_handler_func = $(selector).get_on_handler("keyup");
|
|
|
|
let set_focused_recipient_checked = false;
|
|
let update_all_called = false;
|
|
|
|
rewiremock("../../static/js/compose_fade").with({
|
|
set_focused_recipient(msg_type) {
|
|
assert.equal(msg_type, "private");
|
|
set_focused_recipient_checked = true;
|
|
},
|
|
update_all() {
|
|
update_all_called = true;
|
|
},
|
|
});
|
|
|
|
compose_state.set_message_type(false);
|
|
keyup_handler_func();
|
|
assert(!set_focused_recipient_checked);
|
|
assert(!update_all_called);
|
|
|
|
compose_state.set_message_type("private");
|
|
keyup_handler_func();
|
|
assert(set_focused_recipient_checked);
|
|
assert(update_all_called);
|
|
});
|
|
|
|
test_ui("trigger_submit_compose_form", () => {
|
|
let prevent_default_checked = false;
|
|
let compose_finish_checked = false;
|
|
const e = {
|
|
preventDefault() {
|
|
prevent_default_checked = true;
|
|
},
|
|
};
|
|
compose.__Rewire__("finish", () => {
|
|
compose_finish_checked = true;
|
|
});
|
|
|
|
const submit_handler = $("#compose form").get_on_handler("submit");
|
|
|
|
submit_handler(e);
|
|
|
|
assert(prevent_default_checked);
|
|
assert(compose_finish_checked);
|
|
});
|
|
|
|
test_ui("needs_subscribe_warning", () => {
|
|
const invalid_user_id = 999;
|
|
|
|
const test_bot = {
|
|
full_name: "Test Bot",
|
|
email: "test-bot@example.com",
|
|
user_id: 135,
|
|
is_bot: true,
|
|
};
|
|
people.add_active_user(test_bot);
|
|
|
|
const sub = {
|
|
stream_id: 110,
|
|
name: "stream",
|
|
can_access_subscribers: true,
|
|
};
|
|
|
|
stream_data.add_sub(sub);
|
|
peer_data.set_subscribers(sub.stream_id, [bob.user_id, me.user_id]);
|
|
|
|
blueslip.expect("error", "Unknown user_id in get_by_user_id: 999");
|
|
// Test with an invalid user id.
|
|
assert.equal(compose.needs_subscribe_warning(invalid_user_id, sub.stream_id), false);
|
|
|
|
// Test with bot user.
|
|
assert.equal(compose.needs_subscribe_warning(test_bot.user_id, sub.stream_id), false);
|
|
|
|
// Test when user is subscribed to the stream.
|
|
assert.equal(compose.needs_subscribe_warning(bob.user_id, sub.stream_id), false);
|
|
|
|
peer_data.remove_subscriber(sub.stream_id, bob.user_id);
|
|
// Test when the user is not subscribed.
|
|
assert.equal(compose.needs_subscribe_warning(bob.user_id, sub.stream_id), true);
|
|
});
|
|
|
|
test_ui("warn_if_mentioning_unsubscribed_user", () => {
|
|
let mentioned = {
|
|
email: "foo@bar.com",
|
|
};
|
|
|
|
$("#compose_invite_users .compose_invite_user").length = 0;
|
|
|
|
function test_noop_case(is_private, is_zephyr_mirror, is_broadcast) {
|
|
const msg_type = is_private ? "private" : "stream";
|
|
compose_state.set_message_type(msg_type);
|
|
page_params.realm_is_zephyr_mirror_realm = is_zephyr_mirror;
|
|
mentioned.is_broadcast = is_broadcast;
|
|
compose.warn_if_mentioning_unsubscribed_user(mentioned);
|
|
assert.equal($("#compose_invite_users").visible(), false);
|
|
}
|
|
|
|
test_noop_case(true, false, false);
|
|
test_noop_case(false, true, false);
|
|
test_noop_case(false, false, true);
|
|
|
|
$("#compose_invite_users").hide();
|
|
compose_state.set_message_type("stream");
|
|
page_params.realm_is_zephyr_mirror_realm = false;
|
|
|
|
// Test with empty stream name in compose box. It should return noop.
|
|
assert.equal(compose_state.stream_name(), "");
|
|
compose.warn_if_mentioning_unsubscribed_user(mentioned);
|
|
assert.equal($("#compose_invite_users").visible(), false);
|
|
|
|
compose_state.stream_name("random");
|
|
const sub = {
|
|
stream_id: 111,
|
|
name: "random",
|
|
};
|
|
|
|
// Test with invalid stream in compose box. It should return noop.
|
|
compose.warn_if_mentioning_unsubscribed_user(mentioned);
|
|
assert.equal($("#compose_invite_users").visible(), false);
|
|
|
|
// Test mentioning a user that should gets a warning.
|
|
|
|
const checks = [
|
|
(function () {
|
|
let called;
|
|
compose.__Rewire__("needs_subscribe_warning", (user_id, stream_id) => {
|
|
called = true;
|
|
assert.equal(user_id, 34);
|
|
assert.equal(stream_id, 111);
|
|
return true;
|
|
});
|
|
return function () {
|
|
assert(called);
|
|
};
|
|
})(),
|
|
|
|
(function () {
|
|
let called;
|
|
stub_templates((template_name, context) => {
|
|
called = true;
|
|
assert.equal(template_name, "compose_invite_users");
|
|
assert.equal(context.user_id, 34);
|
|
assert.equal(context.stream_id, 111);
|
|
assert.equal(context.name, "Foo Barson");
|
|
return "fake-compose-invite-user-template";
|
|
});
|
|
return function () {
|
|
assert(called);
|
|
};
|
|
})(),
|
|
|
|
(function () {
|
|
let called;
|
|
$("#compose_invite_users").append = (html) => {
|
|
called = true;
|
|
assert.equal(html, "fake-compose-invite-user-template");
|
|
};
|
|
return function () {
|
|
assert(called);
|
|
};
|
|
})(),
|
|
];
|
|
|
|
mentioned = {
|
|
email: "foo@bar.com",
|
|
user_id: 34,
|
|
full_name: "Foo Barson",
|
|
};
|
|
|
|
stream_data.add_sub(sub);
|
|
compose.warn_if_mentioning_unsubscribed_user(mentioned);
|
|
assert.equal($("#compose_invite_users").visible(), true);
|
|
|
|
for (const f of checks) {
|
|
f();
|
|
}
|
|
|
|
// Simulate that the row was added to the DOM.
|
|
const warning_row = $("<warning row>");
|
|
|
|
let looked_for_existing;
|
|
warning_row.data = (field) => {
|
|
if (field === "user-id") {
|
|
looked_for_existing = true;
|
|
return "34";
|
|
}
|
|
if (field === "stream-id") {
|
|
return "111";
|
|
}
|
|
throw new Error(`Unknown field ${field}`);
|
|
};
|
|
|
|
const previous_users = $("#compose_invite_users .compose_invite_user");
|
|
previous_users.length = 1;
|
|
previous_users[0] = warning_row;
|
|
$("#compose_invite_users").hide();
|
|
|
|
// Now try to mention the same person again. The template should
|
|
// not render.
|
|
stub_templates(noop);
|
|
compose.warn_if_mentioning_unsubscribed_user(mentioned);
|
|
assert.equal($("#compose_invite_users").visible(), true);
|
|
assert(looked_for_existing);
|
|
});
|
|
|
|
test_ui("on_events", () => {
|
|
function setup_parents_and_mock_remove(container_sel, target_sel, parent) {
|
|
const container = $.create("fake " + container_sel);
|
|
let container_removed = false;
|
|
|
|
container.remove = () => {
|
|
container_removed = true;
|
|
};
|
|
|
|
const target = $.create("fake click target (" + target_sel + ")");
|
|
|
|
target.set_parents_result(parent, container);
|
|
|
|
const event = {
|
|
preventDefault: noop,
|
|
target,
|
|
};
|
|
|
|
const helper = {
|
|
event,
|
|
container,
|
|
target,
|
|
container_was_removed: () => container_removed,
|
|
};
|
|
|
|
return helper;
|
|
}
|
|
|
|
(function test_compose_all_everyone_confirm_clicked() {
|
|
const handler = $("#compose-all-everyone").get_on_handler(
|
|
"click",
|
|
".compose-all-everyone-confirm",
|
|
);
|
|
|
|
const helper = setup_parents_and_mock_remove(
|
|
"compose-all-everyone",
|
|
"compose-all-everyone",
|
|
".compose-all-everyone",
|
|
);
|
|
|
|
$("#compose-all-everyone").show();
|
|
$("#compose-send-status").show();
|
|
|
|
let compose_finish_checked = false;
|
|
compose.__Rewire__("finish", () => {
|
|
compose_finish_checked = true;
|
|
});
|
|
|
|
handler(helper.event);
|
|
|
|
assert(helper.container_was_removed());
|
|
assert(compose_finish_checked);
|
|
assert(!$("#compose-all-everyone").visible());
|
|
assert(!$("#compose-send-status").visible());
|
|
})();
|
|
|
|
(function test_compose_invite_users_clicked() {
|
|
const handler = $("#compose_invite_users").get_on_handler("click", ".compose_invite_link");
|
|
const subscription = {
|
|
stream_id: 102,
|
|
name: "test",
|
|
subscribed: true,
|
|
};
|
|
const mentioned = {
|
|
full_name: "Foo Barson",
|
|
email: "foo@bar.com",
|
|
user_id: 34,
|
|
};
|
|
people.add_active_user(mentioned);
|
|
let invite_user_to_stream_called = false;
|
|
stream_edit.invite_user_to_stream = (user_ids, sub, success) => {
|
|
invite_user_to_stream_called = true;
|
|
assert.deepEqual(user_ids, [mentioned.user_id]);
|
|
assert.equal(sub, subscription);
|
|
success(); // This will check success callback path.
|
|
};
|
|
|
|
const helper = setup_parents_and_mock_remove(
|
|
"compose_invite_users",
|
|
"compose_invite_link",
|
|
".compose_invite_user",
|
|
);
|
|
|
|
helper.container.data = (field) => {
|
|
if (field === "user-id") {
|
|
return "34";
|
|
}
|
|
if (field === "stream-id") {
|
|
return "102";
|
|
}
|
|
throw new Error(`Unknown field ${field}`);
|
|
};
|
|
helper.target.prop("disabled", false);
|
|
|
|
// !sub will result in true here and we check the success code path.
|
|
stream_data.add_sub(subscription);
|
|
$("#stream_message_recipient_stream").val("test");
|
|
let all_invite_children_called = false;
|
|
$("#compose_invite_users").children = () => {
|
|
all_invite_children_called = true;
|
|
return [];
|
|
};
|
|
$("#compose_invite_users").show();
|
|
|
|
handler(helper.event);
|
|
|
|
assert(helper.container_was_removed());
|
|
assert(!$("#compose_invite_users").visible());
|
|
assert(invite_user_to_stream_called);
|
|
assert(all_invite_children_called);
|
|
})();
|
|
|
|
(function test_compose_invite_close_clicked() {
|
|
const handler = $("#compose_invite_users").get_on_handler("click", ".compose_invite_close");
|
|
|
|
const helper = setup_parents_and_mock_remove(
|
|
"compose_invite_users_close",
|
|
"compose_invite_close",
|
|
".compose_invite_user",
|
|
);
|
|
|
|
let all_invite_children_called = false;
|
|
$("#compose_invite_users").children = () => {
|
|
all_invite_children_called = true;
|
|
return [];
|
|
};
|
|
$("#compose_invite_users").show();
|
|
|
|
handler(helper.event);
|
|
|
|
assert(helper.container_was_removed());
|
|
assert(all_invite_children_called);
|
|
assert(!$("#compose_invite_users").visible());
|
|
})();
|
|
|
|
(function test_compose_not_subscribed_clicked() {
|
|
const handler = $("#compose-send-status").get_on_handler("click", ".sub_unsub_button");
|
|
const subscription = {
|
|
stream_id: 102,
|
|
name: "test",
|
|
subscribed: false,
|
|
};
|
|
let compose_not_subscribed_called = false;
|
|
subs.sub_or_unsub = () => {
|
|
compose_not_subscribed_called = true;
|
|
};
|
|
|
|
const helper = setup_parents_and_mock_remove(
|
|
"compose-send-status",
|
|
"sub_unsub_button",
|
|
".compose_not_subscribed",
|
|
);
|
|
|
|
handler(helper.event);
|
|
|
|
assert(compose_not_subscribed_called);
|
|
|
|
stream_data.add_sub(subscription);
|
|
$("#stream_message_recipient_stream").val("test");
|
|
$("#compose-send-status").show();
|
|
|
|
handler(helper.event);
|
|
|
|
assert(!$("#compose-send-status").visible());
|
|
})();
|
|
|
|
(function test_compose_not_subscribed_close_clicked() {
|
|
const handler = $("#compose-send-status").get_on_handler(
|
|
"click",
|
|
"#compose_not_subscribed_close",
|
|
);
|
|
|
|
const helper = setup_parents_and_mock_remove(
|
|
"compose_user_not_subscribed_close",
|
|
"compose_not_subscribed_close",
|
|
".compose_not_subscribed",
|
|
);
|
|
|
|
$("#compose-send-status").show();
|
|
|
|
handler(helper.event);
|
|
|
|
assert(!$("#compose-send-status").visible());
|
|
})();
|
|
|
|
(function test_attach_files_compose_clicked() {
|
|
const handler = $("#compose").get_on_handler("click", "#attach_files");
|
|
$("#file_input").clone = (param) => {
|
|
assert(param);
|
|
};
|
|
let compose_file_input_clicked = false;
|
|
$("#compose #file_input").on("click", () => {
|
|
compose_file_input_clicked = true;
|
|
});
|
|
|
|
const event = {
|
|
preventDefault: noop,
|
|
};
|
|
|
|
handler(event);
|
|
assert(compose_file_input_clicked);
|
|
})();
|
|
|
|
(function test_no_provider_video_link_compose_clicked() {
|
|
let called = false;
|
|
|
|
const textarea = $.create("target-stub");
|
|
|
|
const ev = {
|
|
preventDefault: noop,
|
|
target: {
|
|
to_$: () => textarea,
|
|
},
|
|
};
|
|
|
|
compose_ui.insert_syntax_and_focus = () => {
|
|
called = true;
|
|
};
|
|
|
|
const handler = $("body").get_on_handler("click", ".video_link");
|
|
$("#compose-textarea").val("");
|
|
|
|
handler(ev);
|
|
assert(!called);
|
|
})();
|
|
|
|
(function test_jitsi_video_link_compose_clicked() {
|
|
let syntax_to_insert;
|
|
let called = false;
|
|
|
|
const textarea = $.create("jitsi-target-stub");
|
|
|
|
const ev = {
|
|
preventDefault: noop,
|
|
target: {
|
|
to_$: () => textarea,
|
|
},
|
|
};
|
|
|
|
compose_ui.insert_syntax_and_focus = (syntax) => {
|
|
syntax_to_insert = syntax;
|
|
called = true;
|
|
};
|
|
|
|
const handler = $("body").get_on_handler("click", ".video_link");
|
|
$("#compose-textarea").val("");
|
|
|
|
page_params.realm_video_chat_provider =
|
|
page_params.realm_available_video_chat_providers.jitsi_meet.id;
|
|
|
|
page_params.jitsi_server_url = null;
|
|
handler(ev);
|
|
assert(!called);
|
|
|
|
page_params.jitsi_server_url = "https://meet.jit.si";
|
|
handler(ev);
|
|
// video link ids consist of 15 random digits
|
|
const video_link_regex = /\[translated: Click to join video call]\(https:\/\/meet.jit.si\/\d{15}\)/;
|
|
assert(called);
|
|
assert.match(syntax_to_insert, video_link_regex);
|
|
})();
|
|
|
|
(function test_zoom_video_link_compose_clicked() {
|
|
let syntax_to_insert;
|
|
let called = false;
|
|
|
|
const textarea = $.create("zoom-target-stub");
|
|
|
|
const ev = {
|
|
preventDefault: noop,
|
|
target: {
|
|
to_$: () => textarea,
|
|
},
|
|
};
|
|
|
|
compose_ui.insert_syntax_and_focus = (syntax) => {
|
|
syntax_to_insert = syntax;
|
|
called = true;
|
|
};
|
|
|
|
const handler = $("body").get_on_handler("click", ".video_link");
|
|
$("#compose-textarea").val("");
|
|
|
|
page_params.realm_video_chat_provider =
|
|
page_params.realm_available_video_chat_providers.zoom.id;
|
|
page_params.has_zoom_token = false;
|
|
|
|
window.open = (url) => {
|
|
assert(url.endsWith("/calls/zoom/register"));
|
|
|
|
// The event here has value=true. We keep it in events.js to
|
|
// allow our tooling to verify its schema.
|
|
server_events_dispatch.dispatch_normal_event(events.fixtures.has_zoom_token);
|
|
};
|
|
|
|
channel.post = (payload) => {
|
|
assert.equal(payload.url, "/json/calls/zoom/create");
|
|
payload.success({url: "example.zoom.com"});
|
|
return {abort: () => {}};
|
|
};
|
|
|
|
handler(ev);
|
|
const video_link_regex = /\[translated: Click to join video call]\(example\.zoom\.com\)/;
|
|
assert(called);
|
|
assert.match(syntax_to_insert, video_link_regex);
|
|
})();
|
|
|
|
(function test_bbb_video_link_compose_clicked() {
|
|
let syntax_to_insert;
|
|
let called = false;
|
|
|
|
const textarea = $.create("bbb-target-stub");
|
|
|
|
const ev = {
|
|
preventDefault: noop,
|
|
target: {
|
|
to_$: () => textarea,
|
|
},
|
|
};
|
|
|
|
compose_ui.insert_syntax_and_focus = (syntax) => {
|
|
syntax_to_insert = syntax;
|
|
called = true;
|
|
};
|
|
|
|
const handler = $("body").get_on_handler("click", ".video_link");
|
|
$("#compose-textarea").val("");
|
|
|
|
page_params.realm_video_chat_provider =
|
|
page_params.realm_available_video_chat_providers.big_blue_button.id;
|
|
|
|
channel.get = (options) => {
|
|
assert(options.url === "/json/calls/bigbluebutton/create");
|
|
options.success({
|
|
url:
|
|
"/calls/bigbluebutton/join?meeting_id=%22zulip-1%22&password=%22AAAAAAAAAA%22&checksum=%2232702220bff2a22a44aee72e96cfdb4c4091752e%22",
|
|
});
|
|
};
|
|
|
|
handler(ev);
|
|
const video_link_regex = /\[translated: Click to join video call]\(\/calls\/bigbluebutton\/join\?meeting_id=%22zulip-1%22&password=%22AAAAAAAAAA%22&checksum=%2232702220bff2a22a44aee72e96cfdb4c4091752e%22\)/;
|
|
assert(called);
|
|
assert.match(syntax_to_insert, video_link_regex);
|
|
})();
|
|
|
|
(function test_markdown_preview_compose_clicked() {
|
|
// Tests setup
|
|
function setup_visibilities() {
|
|
$("#compose-textarea").show();
|
|
$("#markdown_preview").show();
|
|
$("#undo_markdown_preview").hide();
|
|
$("#preview_message_area").hide();
|
|
}
|
|
|
|
function assert_visibilities() {
|
|
assert(!$("#compose-textarea").visible());
|
|
assert(!$("#markdown_preview").visible());
|
|
assert($("#undo_markdown_preview").visible());
|
|
assert($("#preview_message_area").visible());
|
|
}
|
|
|
|
function setup_mock_markdown_contains_backend_only_syntax(msg_content, return_val) {
|
|
markdown.contains_backend_only_syntax = (msg) => {
|
|
assert.equal(msg, msg_content);
|
|
return return_val;
|
|
};
|
|
}
|
|
|
|
function setup_mock_markdown_is_status_message(msg_content, return_val) {
|
|
markdown.is_status_message = (content) => {
|
|
assert.equal(content, msg_content);
|
|
return return_val;
|
|
};
|
|
}
|
|
|
|
function test_post_success(success_callback) {
|
|
const resp = {
|
|
rendered: "Server: foobarfoobar",
|
|
};
|
|
success_callback(resp);
|
|
assert.equal($("#preview_content").html(), "Server: foobarfoobar");
|
|
}
|
|
|
|
function test_post_error(error_callback) {
|
|
error_callback();
|
|
assert.equal($("#preview_content").html(), "translated: Failed to generate preview");
|
|
}
|
|
|
|
function mock_channel_post(msg) {
|
|
channel.post = (payload) => {
|
|
assert.equal(payload.url, "/json/messages/render");
|
|
assert(payload.idempotent);
|
|
assert(payload.data);
|
|
assert.deepEqual(payload.data.content, msg);
|
|
|
|
function test(func, param) {
|
|
let destroy_indicator_called = false;
|
|
loading.destroy_indicator = (spinner) => {
|
|
assert.equal(spinner, $("#markdown_preview_spinner"));
|
|
destroy_indicator_called = true;
|
|
};
|
|
setup_mock_markdown_contains_backend_only_syntax(msg, true);
|
|
|
|
func(param);
|
|
|
|
assert(destroy_indicator_called);
|
|
}
|
|
|
|
test(test_post_error, payload.error);
|
|
test(test_post_success, payload.success);
|
|
};
|
|
}
|
|
|
|
const handler = $("#compose").get_on_handler("click", "#markdown_preview");
|
|
|
|
// Tests start here
|
|
$("#compose-textarea").val("");
|
|
setup_visibilities();
|
|
|
|
const event = {
|
|
preventDefault: noop,
|
|
};
|
|
|
|
handler(event);
|
|
|
|
assert.equal($("#preview_content").html(), "translated: Nothing to preview");
|
|
assert_visibilities();
|
|
|
|
let make_indicator_called = false;
|
|
$("#compose-textarea").val("```foobarfoobar```");
|
|
setup_visibilities();
|
|
setup_mock_markdown_contains_backend_only_syntax("```foobarfoobar```", true);
|
|
setup_mock_markdown_is_status_message("```foobarfoobar```", false);
|
|
loading.make_indicator = (spinner) => {
|
|
assert.equal(spinner.selector, "#markdown_preview_spinner");
|
|
make_indicator_called = true;
|
|
};
|
|
mock_channel_post("```foobarfoobar```");
|
|
|
|
handler(event);
|
|
|
|
assert(make_indicator_called);
|
|
assert_visibilities();
|
|
|
|
let apply_markdown_called = false;
|
|
$("#compose-textarea").val("foobarfoobar");
|
|
setup_visibilities();
|
|
setup_mock_markdown_contains_backend_only_syntax("foobarfoobar", false);
|
|
setup_mock_markdown_is_status_message("foobarfoobar", false);
|
|
mock_channel_post("foobarfoobar");
|
|
markdown.apply_markdown = (msg) => {
|
|
assert.equal(msg.raw_content, "foobarfoobar");
|
|
apply_markdown_called = true;
|
|
return msg;
|
|
};
|
|
|
|
handler(event);
|
|
|
|
assert(apply_markdown_called);
|
|
assert_visibilities();
|
|
assert.equal($("#preview_content").html(), "Server: foobarfoobar");
|
|
})();
|
|
|
|
(function test_undo_markdown_preview_clicked() {
|
|
const handler = $("#compose").get_on_handler("click", "#undo_markdown_preview");
|
|
|
|
$("#compose-textarea").hide();
|
|
$("#undo_markdown_preview").show();
|
|
$("#preview_message_area").show();
|
|
$("#markdown_preview").hide();
|
|
|
|
const event = {
|
|
preventDefault: noop,
|
|
};
|
|
|
|
handler(event);
|
|
|
|
assert($("#compose-textarea").visible());
|
|
assert(!$("#undo_markdown_preview").visible());
|
|
assert(!$("#preview_message_area").visible());
|
|
assert($("#markdown_preview").visible());
|
|
})();
|
|
});
|
|
|
|
test_ui("create_message_object", () => {
|
|
const sub = {
|
|
stream_id: 101,
|
|
name: "social",
|
|
subscribed: true,
|
|
};
|
|
stream_data.add_sub(sub);
|
|
|
|
$("#stream_message_recipient_stream").val("social");
|
|
$("#stream_message_recipient_topic").val("lunch");
|
|
$("#compose-textarea").val("burrito");
|
|
|
|
compose_state.__Rewire__("get_message_type", () => "stream");
|
|
|
|
let message = compose.create_message_object();
|
|
assert.equal(message.to, sub.stream_id);
|
|
assert.equal(message.topic, "lunch");
|
|
assert.equal(message.content, "burrito");
|
|
|
|
blueslip.expect("error", "Trying to send message with bad stream name: BOGUS STREAM");
|
|
|
|
$("#stream_message_recipient_stream").val("BOGUS STREAM");
|
|
message = compose.create_message_object();
|
|
assert.equal(message.to, "BOGUS STREAM");
|
|
assert.equal(message.topic, "lunch");
|
|
assert.equal(message.content, "burrito");
|
|
|
|
compose_state.__Rewire__("get_message_type", () => "private");
|
|
compose_state.__Rewire__(
|
|
"private_message_recipient",
|
|
() => "alice@example.com, bob@example.com",
|
|
);
|
|
|
|
message = compose.create_message_object();
|
|
assert.deepEqual(message.to, [alice.user_id, bob.user_id]);
|
|
assert.equal(message.to_user_ids, "31,32");
|
|
assert.equal(message.content, "burrito");
|
|
|
|
const {email_list_to_user_ids_string} = people;
|
|
people.email_list_to_user_ids_string = () => undefined;
|
|
message = compose.create_message_object();
|
|
assert.deepEqual(message.to, [alice.email, bob.email]);
|
|
people.email_list_to_user_ids_string = email_list_to_user_ids_string;
|
|
});
|
|
|
|
test_ui("nonexistent_stream_reply_error", () => {
|
|
reset_jquery();
|
|
|
|
const actions = [];
|
|
$("#nonexistent_stream_reply_error").show = () => {
|
|
actions.push("show");
|
|
};
|
|
$("#nonexistent_stream_reply_error").hide = () => {
|
|
actions.push("hide");
|
|
};
|
|
|
|
compose.nonexistent_stream_reply_error();
|
|
assert.equal($("#compose-reply-error-msg").html(), "There are no messages to reply to yet.");
|
|
assert.deepEqual(actions, ["show", "hide"]);
|
|
});
|
|
|
|
test_ui("narrow_button_titles", () => {
|
|
util.is_mobile = () => false;
|
|
|
|
compose.update_closed_compose_buttons_for_private();
|
|
assert.equal($("#left_bar_compose_stream_button_big").text(), i18n.t("New stream message"));
|
|
assert.equal($("#left_bar_compose_private_button_big").text(), i18n.t("New private message"));
|
|
|
|
compose.update_closed_compose_buttons_for_stream();
|
|
assert.equal($("#left_bar_compose_stream_button_big").text(), i18n.t("New topic"));
|
|
assert.equal($("#left_bar_compose_private_button_big").text(), i18n.t("New private message"));
|
|
});
|
|
|
|
test_ui("test_video_chat_button_toggle", () => {
|
|
reset_jquery();
|
|
stub_out_video_calls();
|
|
|
|
page_params.realm_video_chat_provider =
|
|
page_params.realm_available_video_chat_providers.disabled.id;
|
|
compose.initialize();
|
|
assert.equal($("#below-compose-content .video_link").visible(), false);
|
|
|
|
reset_jquery();
|
|
stub_out_video_calls();
|
|
page_params.realm_video_chat_provider =
|
|
page_params.realm_available_video_chat_providers.jitsi_meet.id;
|
|
page_params.jitsi_server_url = null;
|
|
compose.initialize();
|
|
assert.equal($("#below-compose-content .video_link").visible(), false);
|
|
|
|
reset_jquery();
|
|
stub_out_video_calls();
|
|
page_params.realm_video_chat_provider =
|
|
page_params.realm_available_video_chat_providers.jitsi_meet.id;
|
|
page_params.jitsi_server_url = "https://meet.jit.si";
|
|
compose.initialize();
|
|
assert.equal($("#below-compose-content .video_link").visible(), true);
|
|
});
|
|
|
|
MockDate.reset();
|
|
|
|
rewiremock.disable();
|