mirror of https://github.com/zulip/zulip.git
settings: Remove "User groups" panel from settings overlay.
The "User groups" panel is now removed from settings overlay and we instead use new "#groups" UI. This commit also makes some changes to tests to ensure coverage for pill_typeahead.js which was previously done by settings_user_group_legacy.test.js. We have still not got complete coverage on user_pill.ts as we have removed settings_user_group_legacy.test.js, but we just add the file to EXEMPT_FILS list for now and will handle it in future. Fixes #28012.
This commit is contained in:
parent
17d25284a2
commit
3d181a8ee1
|
@ -215,7 +215,6 @@ EXEMPT_FILES = make_set(
|
|||
"web/src/settings_streams.js",
|
||||
"web/src/settings_toggle.js",
|
||||
"web/src/settings_ui.ts",
|
||||
"web/src/settings_user_groups_legacy.js",
|
||||
"web/src/settings_user_topics.js",
|
||||
"web/src/settings_users.js",
|
||||
"web/src/setup.ts",
|
||||
|
@ -269,6 +268,7 @@ EXEMPT_FILES = make_set(
|
|||
"web/src/user_group_popover.js",
|
||||
"web/src/user_group_ui_updates.js",
|
||||
"web/src/user_groups.ts",
|
||||
"web/src/user_pill.ts",
|
||||
"web/src/user_profile.js",
|
||||
"web/src/user_settings.ts",
|
||||
"web/src/user_sort.ts",
|
||||
|
|
|
@ -77,7 +77,6 @@ function insert_tip_box() {
|
|||
$(".organization-box")
|
||||
.find(".settings-section")
|
||||
.not("#emoji-settings")
|
||||
.not("#user-groups-admin")
|
||||
.not("#organization-auth-settings")
|
||||
.not("#admin-bot-list")
|
||||
.not("#admin-invites-list")
|
||||
|
@ -116,7 +115,6 @@ export function build_page() {
|
|||
realm_inline_url_embed_preview: page_params.realm_inline_url_embed_preview,
|
||||
server_inline_url_embed_preview: page_params.server_inline_url_embed_preview,
|
||||
realm_authentication_methods: page_params.realm_authentication_methods,
|
||||
realm_user_group_edit_policy: page_params.realm_user_group_edit_policy,
|
||||
realm_name_changes_disabled: page_params.realm_name_changes_disabled,
|
||||
realm_email_changes_disabled: page_params.realm_email_changes_disabled,
|
||||
realm_avatar_changes_disabled: page_params.realm_avatar_changes_disabled,
|
||||
|
@ -174,7 +172,6 @@ export function build_page() {
|
|||
can_create_multiuse_invite: settings_data.user_can_create_multiuse_invite(),
|
||||
can_invite_users_by_email: settings_data.user_can_invite_users_by_email(),
|
||||
realm_invite_required: page_params.realm_invite_required,
|
||||
can_edit_user_groups: settings_data.user_can_edit_user_groups(),
|
||||
policy_values: settings_config.common_policy_values,
|
||||
realm_delete_own_message_policy: page_params.realm_delete_own_message_policy,
|
||||
DELETE_OWN_MESSAGE_POLICY_ADMINS_ONLY:
|
||||
|
|
|
@ -62,7 +62,6 @@ import * as settings_profile_fields from "./settings_profile_fields";
|
|||
import * as settings_realm_domains from "./settings_realm_domains";
|
||||
import * as settings_realm_user_settings_defaults from "./settings_realm_user_settings_defaults";
|
||||
import * as settings_streams from "./settings_streams";
|
||||
import * as settings_user_groups_legacy from "./settings_user_groups_legacy";
|
||||
import * as settings_users from "./settings_users";
|
||||
import * as sidebar_ui from "./sidebar_ui";
|
||||
import * as starred_messages from "./starred_messages";
|
||||
|
@ -882,7 +881,6 @@ export function dispatch_normal_event(event) {
|
|||
blueslip.error("Unexpected event type user_group/" + event.op);
|
||||
break;
|
||||
}
|
||||
settings_user_groups_legacy.reload();
|
||||
break;
|
||||
|
||||
case "user_status":
|
||||
|
|
|
@ -15,7 +15,6 @@ import * as settings_playgrounds from "./settings_playgrounds";
|
|||
import * as settings_profile_fields from "./settings_profile_fields";
|
||||
import * as settings_realm_user_settings_defaults from "./settings_realm_user_settings_defaults";
|
||||
import * as settings_streams from "./settings_streams";
|
||||
import * as settings_user_groups_legacy from "./settings_user_groups_legacy";
|
||||
import * as settings_user_topics from "./settings_user_topics";
|
||||
import * as settings_users from "./settings_users";
|
||||
|
||||
|
@ -72,7 +71,6 @@ export function initialize() {
|
|||
load_func_dict.set("linkifier-settings", settings_linkifiers.set_up);
|
||||
load_func_dict.set("playground-settings", settings_playgrounds.set_up);
|
||||
load_func_dict.set("invites-list-admin", settings_invites.set_up);
|
||||
load_func_dict.set("user-groups-admin", settings_user_groups_legacy.set_up);
|
||||
load_func_dict.set("profile-field-settings", settings_profile_fields.set_up);
|
||||
load_func_dict.set("data-exports-admin", settings_exports.set_up);
|
||||
load_func_dict.set(
|
||||
|
@ -112,7 +110,6 @@ export function reset_sections() {
|
|||
settings_org.reset();
|
||||
settings_profile_fields.reset();
|
||||
settings_streams.reset();
|
||||
settings_user_groups_legacy.reset();
|
||||
settings_user_topics.reset();
|
||||
settings_muted_users.reset();
|
||||
alert_words_ui.reset();
|
||||
|
|
|
@ -1,407 +0,0 @@
|
|||
import $ from "jquery";
|
||||
import _ from "lodash";
|
||||
|
||||
import render_confirm_delete_user from "../templates/confirm_dialog/confirm_delete_user.hbs";
|
||||
import render_add_user_group_modal from "../templates/settings/add_user_group_modal.hbs";
|
||||
import render_admin_user_group_list from "../templates/settings/admin_user_group_list.hbs";
|
||||
|
||||
import * as channel from "./channel";
|
||||
import * as confirm_dialog from "./confirm_dialog";
|
||||
import * as dialog_widget from "./dialog_widget";
|
||||
import {$t, $t_html} from "./i18n";
|
||||
import * as keydown_util from "./keydown_util";
|
||||
import {page_params} from "./page_params";
|
||||
import * as people from "./people";
|
||||
import * as pill_typeahead from "./pill_typeahead";
|
||||
import * as settings_data from "./settings_data";
|
||||
import * as ui_report from "./ui_report";
|
||||
import * as user_groups from "./user_groups";
|
||||
import * as user_pill from "./user_pill";
|
||||
|
||||
const meta = {
|
||||
loaded: false,
|
||||
};
|
||||
|
||||
export function reset() {
|
||||
meta.loaded = false;
|
||||
}
|
||||
|
||||
export function reload() {
|
||||
if (!meta.loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $user_groups_section = $("#user-groups").expectOne();
|
||||
$user_groups_section.empty();
|
||||
populate_user_groups();
|
||||
}
|
||||
|
||||
export function can_edit(group_id) {
|
||||
if (!settings_data.user_can_edit_user_groups()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Admins and moderators are allowed to edit user groups even if they
|
||||
// are not a member of that user group. Members can edit user groups
|
||||
// only if they belong to that group.
|
||||
if (page_params.is_admin || page_params.is_moderator) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return user_groups.is_direct_member_of(people.my_current_user_id(), group_id);
|
||||
}
|
||||
|
||||
export function populate_user_groups() {
|
||||
const $user_groups_section = $("#user-groups").expectOne();
|
||||
const user_groups_array = user_groups.get_realm_user_groups();
|
||||
|
||||
for (const data of user_groups_array) {
|
||||
$user_groups_section.append(
|
||||
render_admin_user_group_list({
|
||||
user_group: {
|
||||
name: data.name,
|
||||
id: data.id,
|
||||
description: data.description,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const pill_config = {
|
||||
show_user_status_emoji: false,
|
||||
};
|
||||
|
||||
const $pill_container = $(`.pill-container[data-group-pills="${CSS.escape(data.id)}"]`);
|
||||
const pills = user_pill.create_pills($pill_container, pill_config);
|
||||
|
||||
function get_pill_user_ids() {
|
||||
return user_pill.get_user_ids(pills);
|
||||
}
|
||||
|
||||
const $userg = $(`div.user-group[id="${CSS.escape(data.id)}"]`);
|
||||
for (const user_id of data.members) {
|
||||
const user = people.get_by_user_id(user_id);
|
||||
user_pill.append_user(user, pills);
|
||||
}
|
||||
|
||||
function update_membership(group_id) {
|
||||
if (can_edit(group_id)) {
|
||||
return;
|
||||
}
|
||||
$userg.find(".name").attr("contenteditable", "false");
|
||||
$userg.find(".description").attr("contenteditable", "false");
|
||||
$userg.addClass("ntm");
|
||||
$pill_container.find(".input").attr("contenteditable", "false");
|
||||
$pill_container.find(".input").css("display", "none");
|
||||
$pill_container.addClass("not-editable");
|
||||
$pill_container.off("keydown", ".pill");
|
||||
$pill_container.off("keydown", ".input");
|
||||
$pill_container.off("click");
|
||||
$pill_container.on("click", (e) => {
|
||||
e.stopPropagation();
|
||||
});
|
||||
$pill_container.find(".pill").on("mouseenter", () => {
|
||||
$pill_container.find(".pill").find(".exit").css("opacity", "0.5");
|
||||
});
|
||||
}
|
||||
update_membership(data.id);
|
||||
|
||||
function is_user_group_changed() {
|
||||
const draft_group = get_pill_user_ids();
|
||||
const group_data = user_groups.get_user_group_from_id(data.id);
|
||||
const original_group = [...group_data.members];
|
||||
const same_groups = _.isEqual(_.sortBy(draft_group), _.sortBy(original_group));
|
||||
const description = $(`#user-groups #${CSS.escape(data.id)} .description`)
|
||||
.text()
|
||||
.trim();
|
||||
const name = $(`#user-groups #${CSS.escape(data.id)} .name`)
|
||||
.text()
|
||||
.trim();
|
||||
const $user_group_status = $(`#user-groups #${CSS.escape(data.id)} .user-group-status`);
|
||||
|
||||
if ($user_group_status.is(":visible")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
group_data.description === description &&
|
||||
group_data.name === name &&
|
||||
(!draft_group.length || same_groups)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function update_cancel_button() {
|
||||
if (!can_edit(data.id)) {
|
||||
return;
|
||||
}
|
||||
const $cancel_button = $(
|
||||
`#user-groups #${CSS.escape(data.id)} .save-status.btn-danger`,
|
||||
);
|
||||
const $saved_button = $(`#user-groups #${CSS.escape(data.id)} .save-status.sea-green`);
|
||||
const $save_instructions = $(`#user-groups #${CSS.escape(data.id)} .save-instructions`);
|
||||
|
||||
if (is_user_group_changed() && !$cancel_button.is(":visible")) {
|
||||
$saved_button.fadeOut(0);
|
||||
$cancel_button.css({display: "inline-block", opacity: "0"}).fadeTo(400, 1);
|
||||
$save_instructions.css({display: "block", opacity: "0"}).fadeTo(400, 1);
|
||||
} else if (!is_user_group_changed() && $cancel_button.is(":visible")) {
|
||||
$cancel_button.fadeOut();
|
||||
$save_instructions.fadeOut();
|
||||
}
|
||||
}
|
||||
|
||||
function show_saved_button() {
|
||||
const $cancel_button = $(
|
||||
`#user-groups #${CSS.escape(data.id)} .save-status.btn-danger`,
|
||||
);
|
||||
const $saved_button = $(`#user-groups #${CSS.escape(data.id)} .save-status.sea-green`);
|
||||
const $save_instructions = $(`#user-groups #${CSS.escape(data.id)} .save-instructions`);
|
||||
if (!$saved_button.is(":visible")) {
|
||||
$cancel_button.fadeOut(0);
|
||||
$save_instructions.fadeOut(0);
|
||||
$saved_button
|
||||
.css({display: "inline-block", opacity: "0"})
|
||||
.fadeTo(400, 1)
|
||||
.delay(2000)
|
||||
.fadeTo(400, 0);
|
||||
}
|
||||
}
|
||||
|
||||
function save_members() {
|
||||
const draft_group = get_pill_user_ids();
|
||||
const group_data = user_groups.get_user_group_from_id(data.id);
|
||||
const original_group = [...group_data.members];
|
||||
const same_groups = _.isEqual(_.sortBy(draft_group), _.sortBy(original_group));
|
||||
if (!draft_group.length || same_groups) {
|
||||
return;
|
||||
}
|
||||
const added = _.difference(draft_group, original_group);
|
||||
const removed = _.difference(original_group, draft_group);
|
||||
channel.post({
|
||||
url: "/json/user_groups/" + data.id + "/members",
|
||||
data: {
|
||||
add: JSON.stringify(added),
|
||||
delete: JSON.stringify(removed),
|
||||
},
|
||||
success() {
|
||||
setTimeout(show_saved_button, 200);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function save_name_desc() {
|
||||
const $user_group_status = $(`#user-groups #${CSS.escape(data.id)} .user-group-status`);
|
||||
const group_data = user_groups.get_user_group_from_id(data.id);
|
||||
const description = $(`#user-groups #${CSS.escape(data.id)} .description`)
|
||||
.text()
|
||||
.trim();
|
||||
const name = $(`#user-groups #${CSS.escape(data.id)} .name`)
|
||||
.text()
|
||||
.trim();
|
||||
|
||||
if (group_data.description === description && group_data.name === name) {
|
||||
return;
|
||||
}
|
||||
|
||||
channel.patch({
|
||||
url: "/json/user_groups/" + data.id,
|
||||
data: {
|
||||
name,
|
||||
description,
|
||||
},
|
||||
success() {
|
||||
$user_group_status.hide();
|
||||
setTimeout(show_saved_button, 200);
|
||||
},
|
||||
error(xhr) {
|
||||
ui_report.error($t_html({defaultMessage: "Failed"}), xhr, $user_group_status);
|
||||
update_cancel_button();
|
||||
$(`#user-groups #${CSS.escape(data.id)} .name`).text(group_data.name);
|
||||
$(`#user-groups #${CSS.escape(data.id)} .description`).text(
|
||||
group_data.description,
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function do_not_blur(except_class, event) {
|
||||
// Event generated from or inside the typeahead.
|
||||
if ($(event.relatedTarget).closest(".typeahead").length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($(event.relatedTarget).closest(`#user-groups #${CSS.escape(data.id)}`).length) {
|
||||
return [".pill-container", ".name", ".description", ".input", ".delete"].some(
|
||||
(class_name) =>
|
||||
class_name !== except_class &&
|
||||
$(event.relatedTarget).closest(class_name).length,
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function auto_save(class_name, event) {
|
||||
if (!can_edit(data.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (do_not_blur(class_name, event)) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
$(event.relatedTarget).closest(`#user-groups #${CSS.escape(data.id)}`) &&
|
||||
$(event.relatedTarget).closest(".save-status.btn-danger").length
|
||||
) {
|
||||
reload();
|
||||
return;
|
||||
}
|
||||
save_name_desc();
|
||||
save_members();
|
||||
}
|
||||
|
||||
$(`#user-groups #${CSS.escape(data.id)}`).on("blur", ".input", (event) => {
|
||||
auto_save(".input", event);
|
||||
});
|
||||
|
||||
$(`#user-groups #${CSS.escape(data.id)}`).on("blur", ".name", (event) => {
|
||||
auto_save(".name", event);
|
||||
});
|
||||
$(`#user-groups #${CSS.escape(data.id)}`).on("input", ".name", () => {
|
||||
update_cancel_button();
|
||||
});
|
||||
|
||||
$(`#user-groups #${CSS.escape(data.id)}`).on("blur", ".description", (event) => {
|
||||
auto_save(".description", event);
|
||||
});
|
||||
$(`#user-groups #${CSS.escape(data.id)}`).on("input", ".description", () => {
|
||||
update_cancel_button();
|
||||
});
|
||||
|
||||
const $input = $pill_container.children(".input");
|
||||
if (can_edit(data.id)) {
|
||||
const opts = {update_func: update_cancel_button, user: true};
|
||||
pill_typeahead.set_up($input, pills, opts);
|
||||
}
|
||||
|
||||
if (can_edit(data.id)) {
|
||||
pills.onPillRemove(() => {
|
||||
// onPillRemove is fired before the pill is removed from
|
||||
// the DOM.
|
||||
update_cancel_button();
|
||||
setTimeout(() => {
|
||||
$input.trigger("focus");
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function add_user_group() {
|
||||
const $user_group_status = $("#dialog_error");
|
||||
|
||||
const group = {
|
||||
members: JSON.stringify([people.my_current_user_id()]),
|
||||
};
|
||||
|
||||
for (const obj of $("#add-user-group-form").serializeArray()) {
|
||||
if (obj.value.trim() === "") {
|
||||
continue;
|
||||
}
|
||||
group[obj.name] = obj.value;
|
||||
}
|
||||
|
||||
channel.post({
|
||||
url: "/json/user_groups/create",
|
||||
data: group,
|
||||
success() {
|
||||
$user_group_status.hide();
|
||||
ui_report.success($t_html({defaultMessage: "User group added!"}), $user_group_status);
|
||||
dialog_widget.close();
|
||||
},
|
||||
error(xhr) {
|
||||
$user_group_status.hide();
|
||||
ui_report.error($t_html({defaultMessage: "Failed"}), xhr, $user_group_status);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function show_add_user_group_modal() {
|
||||
const html_body = render_add_user_group_modal();
|
||||
|
||||
function add_user_group_post_render() {
|
||||
const $add_user_group_input_element = $("#user_group_name");
|
||||
const $add_user_group_submit_button = $("#add-user-group-modal .dialog_submit_button");
|
||||
$add_user_group_submit_button.prop("disabled", true);
|
||||
|
||||
$add_user_group_input_element.on("input", () => {
|
||||
$add_user_group_submit_button.prop(
|
||||
"disabled",
|
||||
$add_user_group_input_element.val() === "",
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
dialog_widget.launch({
|
||||
html_heading: $t_html({defaultMessage: "Add new user group"}),
|
||||
html_body,
|
||||
html_submit_button: $t_html({defaultMessage: "Save"}),
|
||||
help_link: "/help/user-groups",
|
||||
form_id: "add-user-group-form",
|
||||
id: "add-user-group-modal",
|
||||
on_click: add_user_group,
|
||||
on_shown: () => $("#user_group_name").trigger("focus"),
|
||||
post_render: add_user_group_post_render,
|
||||
});
|
||||
}
|
||||
|
||||
export function set_up() {
|
||||
meta.loaded = true;
|
||||
populate_user_groups();
|
||||
|
||||
$("#show-add-user-group-modal").on("click", (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
show_add_user_group_modal();
|
||||
});
|
||||
|
||||
$("#user-groups").on("click", ".delete", function () {
|
||||
const group_id = Number.parseInt($(this).parents(".user-group").attr("id"), 10);
|
||||
if (!can_edit(group_id)) {
|
||||
return;
|
||||
}
|
||||
const user_group = user_groups.get_user_group_from_id(group_id);
|
||||
const $btn = $(this);
|
||||
|
||||
function delete_user_group() {
|
||||
channel.del({
|
||||
url: "/json/user_groups/" + group_id,
|
||||
data: {
|
||||
id: group_id,
|
||||
},
|
||||
error() {
|
||||
$btn.text($t({defaultMessage: "Failed!"}));
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const html_body = render_confirm_delete_user({
|
||||
group_name: user_group.name,
|
||||
});
|
||||
|
||||
const user_group_name = user_group.name;
|
||||
|
||||
confirm_dialog.launch({
|
||||
html_heading: $t_html({defaultMessage: "Delete {user_group_name}?"}, {user_group_name}),
|
||||
html_body,
|
||||
on_click: delete_user_group,
|
||||
});
|
||||
});
|
||||
|
||||
$("#user-groups").on("keypress", ".user-group h4 > span", (e) => {
|
||||
if (keydown_util.is_enter_event(e)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
}
|
|
@ -651,7 +651,6 @@
|
|||
.clear_search_button:focus,
|
||||
.clear_search_button:active,
|
||||
.clear_search_button:disabled:hover,
|
||||
#user-groups .save-instructions,
|
||||
.close {
|
||||
color: hsl(236deg 33% 80%);
|
||||
}
|
||||
|
|
|
@ -687,19 +687,6 @@ input[type="checkbox"] {
|
|||
text-align: right;
|
||||
}
|
||||
|
||||
#show-add-user-group-modal {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
#add-user-group-form {
|
||||
margin: 0;
|
||||
|
||||
/* This 14px is the border and padding of the input element */
|
||||
#user_group_description {
|
||||
width: calc(100% - 14px);
|
||||
}
|
||||
}
|
||||
|
||||
.add-new-export-box {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
@ -1220,102 +1207,6 @@ $option_title_width: 180px;
|
|||
margin-right: 5px;
|
||||
}
|
||||
|
||||
#user-groups {
|
||||
.user-group {
|
||||
margin-bottom: 20px;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
|
||||
& h4 {
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: left;
|
||||
}
|
||||
|
||||
& span[contenteditable] {
|
||||
display: inline-block;
|
||||
word-break: break-all;
|
||||
|
||||
&:empty::before {
|
||||
opacity: 0.5;
|
||||
display: inline-block;
|
||||
content: attr(data-placeholder);
|
||||
}
|
||||
}
|
||||
|
||||
& span[contenteditable]:focus,
|
||||
span[contenteditable="true"]:hover {
|
||||
border-bottom: 1px solid hsl(0deg 0% 80%);
|
||||
margin-bottom: -1px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.pill-container .input[contenteditable]:empty::after {
|
||||
content: attr(data-placeholder);
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.user-group-status {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
& p {
|
||||
line-height: 2;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
.subscribers,
|
||||
.user-group h4 > .name {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.ntm {
|
||||
cursor: not-allowed;
|
||||
|
||||
& h4 > .button {
|
||||
cursor: not-allowed;
|
||||
display: none;
|
||||
|
||||
&:hover {
|
||||
border-color: hsl(4deg 56% 82%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.save-status {
|
||||
background-color: transparent;
|
||||
padding: 2px 5px;
|
||||
border-radius: 4px;
|
||||
margin-left: 10px;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
display: none;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.checkmark {
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.delete {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.save-instructions {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
color: hsl(0deg 0% 20%);
|
||||
font-size: 0.9em;
|
||||
}
|
||||
}
|
||||
|
||||
/* -- new settings overlay -- */
|
||||
#settings_page {
|
||||
height: 95vh;
|
||||
|
|
|
@ -29,8 +29,6 @@
|
|||
|
||||
{{> invites_list_admin }}
|
||||
|
||||
{{> user_groups_admin }}
|
||||
|
||||
{{> profile_field_settings_admin }}
|
||||
|
||||
{{> data_exports_admin }}
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
{{#with user_group}}
|
||||
<div class="user-group white-box" id="{{id}}">
|
||||
<div class="alert user-group-status"></div>
|
||||
<h4>
|
||||
<span class="name" data-placeholder="{{t 'Name' }}" contenteditable="true" spellcheck="false">{{name}}</span>
|
||||
<span class="spacer">—</span>
|
||||
<span class="description" data-placeholder="{{t 'Description' }}" contenteditable="true">{{description}}</span>
|
||||
<button class="button save-status sea-green small">
|
||||
<img class="checkmark" src="../../images/checkbox-green.svg" />
|
||||
{{t 'Saved' }}
|
||||
</button>
|
||||
<button class="button save-status btn-danger small">
|
||||
<i class="fa fa-undo" aria-label="{{t 'Delete' }}" title="{{t 'Delete' }}"></i>
|
||||
</button>
|
||||
<button class="button rounded small delete btn-danger">
|
||||
<i class="fa fa-trash-o" aria-label="{{t 'Delete' }}" title="{{t 'Delete' }}"></i>
|
||||
</button>
|
||||
</h4>
|
||||
<p class="subscribers">{{t 'Subscribers' }}</p>
|
||||
<div class="pill-container" data-group-pills="{{id}}">
|
||||
<div class="input" contenteditable="true" data-placeholder="{{t 'Add member…' }}"></div>
|
||||
</div>
|
||||
<p class="save-instructions">
|
||||
{{t "Click outside the input box to save. We'll automatically notify anyone that was added or removed."}}
|
||||
</p>
|
||||
</div>
|
||||
{{/with}}
|
|
@ -1,28 +0,0 @@
|
|||
<div id="user-groups-admin" class="settings-section" data-name="user-groups-admin">
|
||||
<div class="user-group-setting-tip-container {{#unless (or is_admin (not can_edit_user_groups))}}hide{{/unless}}">
|
||||
{{#if (eq realm_user_group_edit_policy policy_values.by_members.code) }}
|
||||
<div class="tip">{{t 'This organization is configured so that administrators, moderators and group members can modify user groups.' }}</div>
|
||||
{{else if (eq realm_user_group_edit_policy policy_values.by_full_members.code) }}
|
||||
<div class="tip">{{t 'This organization is configured so that administrators, moderators and full members belonging to the group can modify user groups.' }}</div>
|
||||
{{else if (eq realm_user_group_edit_policy policy_values.by_moderators_only.code) }}
|
||||
<div class="tip">{{t 'This organization is configured so that administrators and moderators can modify user groups.' }}</div>
|
||||
{{else}}
|
||||
<div class="tip">{{t 'This organization is configured so that only administrators can modify user groups.' }}</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{#unless is_guest}}
|
||||
<p>
|
||||
{{#tr}}
|
||||
User groups allow you to <z-link>mention</z-link> multiple users at once. When you mention a user group, everyone in the group is notified as if they were individually mentioned.
|
||||
{{#*inline "z-link"}}<a href="/help/mention-a-user-or-group" target="_blank" rel="noopener noreferrer">{{> @partial-block}}</a>{{/inline}}
|
||||
{{/tr}}
|
||||
</p>
|
||||
{{#if can_edit_user_groups}}
|
||||
<button id="show-add-user-group-modal" class="button rounded sea-green">
|
||||
{{t 'Add a new user group' }}
|
||||
</button>
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
|
||||
<div id="user-groups" class="new-style"></div>
|
||||
</div>
|
|
@ -86,12 +86,6 @@
|
|||
<i class="locked fa fa-lock tippy-zulip-tooltip" {{#if is_admin}}style="display: none;"{{/if}} data-tippy-content="{{t 'Only organization administrators can edit these settings.' }}"></i>
|
||||
</li>
|
||||
{{#unless is_guest}}
|
||||
<li tabindex="0" data-section="user-groups-admin">
|
||||
<i class="icon fa fa-group" aria-hidden="true"></i>
|
||||
<div class="text">{{t "User groups" }}</div>
|
||||
</li>
|
||||
{{/unless}}
|
||||
{{#unless is_guest}}
|
||||
<li tabindex="0" data-section="user-list-admin">
|
||||
<i class="icon fa fa-user" aria-hidden="true"></i>
|
||||
<div class="text">{{t "Users" }}</div>
|
||||
|
|
|
@ -63,7 +63,6 @@ const settings_realm_user_settings_defaults = mock_esm(
|
|||
);
|
||||
const settings_realm_domains = mock_esm("../src/settings_realm_domains");
|
||||
const settings_streams = mock_esm("../src/settings_streams");
|
||||
const settings_user_groups_legacy = mock_esm("../src/settings_user_groups_legacy");
|
||||
const settings_users = mock_esm("../src/settings_users");
|
||||
const sidebar_ui = mock_esm("../src/sidebar_ui");
|
||||
const stream_data = mock_esm("../src/stream_data");
|
||||
|
@ -171,7 +170,6 @@ run_test("attachments", ({override}) => {
|
|||
|
||||
run_test("user groups", ({override}) => {
|
||||
let event = event_fixtures.user_group__add;
|
||||
override(settings_user_groups_legacy, "reload", noop);
|
||||
{
|
||||
const stub = make_stub();
|
||||
const user_group_settings_ui_stub = make_stub();
|
||||
|
@ -1165,7 +1163,7 @@ run_test("realm_export", ({override}) => {
|
|||
assert.equal(args.exports, event.exports);
|
||||
});
|
||||
|
||||
run_test("server_event_dispatch_op_errors", ({override}) => {
|
||||
run_test("server_event_dispatch_op_errors", () => {
|
||||
blueslip.expect("error", "Unexpected event type subscription/other");
|
||||
server_events_dispatch.dispatch_normal_event({type: "subscription", op: "other"});
|
||||
blueslip.expect("error", "Unexpected event type reaction/other");
|
||||
|
@ -1190,7 +1188,6 @@ run_test("server_event_dispatch_op_errors", ({override}) => {
|
|||
sender: {user_id: 5},
|
||||
op: "other",
|
||||
});
|
||||
override(settings_user_groups_legacy, "reload", noop);
|
||||
blueslip.expect("error", "Unexpected event type user_group/other");
|
||||
server_events_dispatch.dispatch_normal_event({type: "user_group", op: "other"});
|
||||
});
|
||||
|
|
|
@ -124,6 +124,11 @@ run_test("set_up", ({mock_template}) => {
|
|||
get_text_from_item: noop,
|
||||
});
|
||||
|
||||
let update_func_called = false;
|
||||
function update_func() {
|
||||
update_func_called = true;
|
||||
}
|
||||
|
||||
let opts = {};
|
||||
$fake_input.typeahead = (config) => {
|
||||
assert.equal(config.items, 5);
|
||||
|
@ -297,6 +302,8 @@ run_test("set_up", ({mock_template}) => {
|
|||
assert.equal(number_of_pills(), 2);
|
||||
config.updater.call(fake_group_this, testers);
|
||||
assert.equal(number_of_pills(), 3);
|
||||
|
||||
assert.ok(update_func_called);
|
||||
}
|
||||
})();
|
||||
|
||||
|
@ -324,7 +331,7 @@ run_test("set_up", ({mock_template}) => {
|
|||
{user_group: true, stream: true},
|
||||
{user_group: true, user: true},
|
||||
{user: true, stream: true},
|
||||
{user_group: true, stream: true, user: true},
|
||||
{user_group: true, stream: true, user: true, update_func},
|
||||
];
|
||||
|
||||
for (const config of all_possible_opts) {
|
||||
|
|
|
@ -1,854 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
const {strict: assert} = require("assert");
|
||||
|
||||
const {$t} = require("./lib/i18n");
|
||||
const {mock_esm, set_global, zrequire} = require("./lib/namespace");
|
||||
const {run_test} = require("./lib/test");
|
||||
const blueslip = require("./lib/zblueslip");
|
||||
const $ = require("./lib/zjquery");
|
||||
const {page_params} = require("./lib/zpage_params");
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
const pills = {
|
||||
pill: {},
|
||||
};
|
||||
|
||||
let create_item_handler;
|
||||
|
||||
const channel = mock_esm("../src/channel");
|
||||
const confirm_dialog = mock_esm("../src/confirm_dialog");
|
||||
const dialog_widget = mock_esm("../src/dialog_widget");
|
||||
const input_pill = mock_esm("../src/input_pill");
|
||||
const settings_data = mock_esm("../src/settings_data");
|
||||
const typeahead_helper = mock_esm("../src/typeahead_helper");
|
||||
const user_groups = mock_esm("../src/user_groups", {
|
||||
get_user_group_from_id: noop,
|
||||
remove: noop,
|
||||
add: noop,
|
||||
});
|
||||
const ui_report = mock_esm("../src/ui_report");
|
||||
|
||||
const people = zrequire("people");
|
||||
const settings_user_groups_legacy = zrequire("settings_user_groups_legacy");
|
||||
const user_pill = zrequire("user_pill");
|
||||
|
||||
function reset_test_setup($pill_container_stub) {
|
||||
function input_pill_stub(opts) {
|
||||
assert.equal(opts.$container, $pill_container_stub);
|
||||
create_item_handler = opts.create_item_from_text;
|
||||
assert.ok(create_item_handler);
|
||||
return pills;
|
||||
}
|
||||
input_pill.create = input_pill_stub;
|
||||
}
|
||||
|
||||
function test_ui(label, f) {
|
||||
// The sloppy_$ flag lets us reuse setup from prior tests.
|
||||
run_test(label, f, {sloppy_$: true});
|
||||
}
|
||||
|
||||
test_ui("can_edit", ({override}) => {
|
||||
override(settings_data, "user_can_edit_user_groups", () => false);
|
||||
assert.ok(!settings_user_groups_legacy.can_edit(1));
|
||||
|
||||
override(settings_data, "user_can_edit_user_groups", () => true);
|
||||
user_groups.is_direct_member_of = (user_id, group_id) => {
|
||||
assert.equal(group_id, 1);
|
||||
assert.equal(user_id, undefined);
|
||||
return false;
|
||||
};
|
||||
assert.ok(!settings_user_groups_legacy.can_edit(1));
|
||||
|
||||
page_params.is_admin = true;
|
||||
assert.ok(settings_user_groups_legacy.can_edit(1));
|
||||
|
||||
page_params.is_admin = false;
|
||||
page_params.is_moderator = true;
|
||||
assert.ok(settings_user_groups_legacy.can_edit(1));
|
||||
|
||||
page_params.is_admin = false;
|
||||
page_params.is_moderator = false;
|
||||
user_groups.is_direct_member_of = (user_id, group_id) => {
|
||||
assert.equal(group_id, 1);
|
||||
assert.equal(user_id, undefined);
|
||||
return true;
|
||||
};
|
||||
assert.ok(settings_user_groups_legacy.can_edit(1));
|
||||
});
|
||||
|
||||
const user_group_selector = `#user-groups #${CSS.escape(1)}`;
|
||||
const cancel_selector = `#user-groups #${CSS.escape(1)} .save-status.btn-danger`;
|
||||
const saved_selector = `#user-groups #${CSS.escape(1)} .save-status.sea-green`;
|
||||
const name_selector = `#user-groups #${CSS.escape(1)} .name`;
|
||||
const description_selector = `#user-groups #${CSS.escape(1)} .description`;
|
||||
const instructions_selector = `#user-groups #${CSS.escape(1)} .save-instructions`;
|
||||
|
||||
test_ui("populate_user_groups", ({mock_template, override, override_rewire}) => {
|
||||
override(settings_data, "user_can_edit_user_groups", () => true);
|
||||
|
||||
const realm_user_group = {
|
||||
id: 1,
|
||||
name: "Mobile",
|
||||
description: "All mobile people",
|
||||
members: new Set([2, 4]),
|
||||
};
|
||||
const iago = {
|
||||
email: "iago@zulip.com",
|
||||
user_id: 2,
|
||||
full_name: "Iago",
|
||||
};
|
||||
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(iago);
|
||||
people.add_active_user(alice);
|
||||
people.add_active_user(bob);
|
||||
|
||||
override_rewire(people, "get_realm_users", () => [iago, alice, bob]);
|
||||
|
||||
user_groups.get_realm_user_groups = () => [realm_user_group];
|
||||
|
||||
let templates_render_called = false;
|
||||
const $fake_rendered_temp = $.create("fake_admin_user_group_list_template_rendered");
|
||||
mock_template("settings/admin_user_group_list.hbs", false, (args) => {
|
||||
assert.equal(args.user_group.id, 1);
|
||||
assert.equal(args.user_group.name, "Mobile");
|
||||
assert.equal(args.user_group.description, "All mobile people");
|
||||
templates_render_called = true;
|
||||
return $fake_rendered_temp;
|
||||
});
|
||||
|
||||
let user_groups_list_append_called = false;
|
||||
$("#user-groups").append = (rendered_temp) => {
|
||||
assert.equal(rendered_temp, $fake_rendered_temp);
|
||||
user_groups_list_append_called = true;
|
||||
};
|
||||
|
||||
let get_by_user_id_called = false;
|
||||
override_rewire(people, "get_by_user_id", (user_id) => {
|
||||
if (user_id === iago.user_id) {
|
||||
return iago;
|
||||
}
|
||||
assert.equal(user_id, 4);
|
||||
blueslip.expect("warn", "Undefined user in function append_user");
|
||||
get_by_user_id_called = true;
|
||||
return undefined;
|
||||
});
|
||||
override_rewire(
|
||||
people,
|
||||
"is_known_user",
|
||||
() => people.get_by_user_id !== undefined && people.get_by_user_id !== noop,
|
||||
);
|
||||
|
||||
page_params.is_admin = true;
|
||||
|
||||
const all_pills = new Map();
|
||||
|
||||
const $pill_container_stub = $(`.pill-container[data-group-pills="${CSS.escape(1)}"]`);
|
||||
pills.appendValidatedData = (item) => {
|
||||
const id = item.user_id;
|
||||
assert.ok(!all_pills.has(id));
|
||||
all_pills.set(id, item);
|
||||
};
|
||||
pills.items = () => [...all_pills.values()];
|
||||
|
||||
let text_cleared;
|
||||
pills.clear_text = () => {
|
||||
text_cleared = true;
|
||||
};
|
||||
|
||||
const $input_field_stub = $.create("fake-input-field");
|
||||
$pill_container_stub.children = () => $input_field_stub;
|
||||
|
||||
let input_typeahead_called = false;
|
||||
$input_field_stub.typeahead = (config) => {
|
||||
assert.equal(config.items, 5);
|
||||
assert.ok(config.fixed);
|
||||
assert.ok(config.dropup);
|
||||
assert.ok(config.stopAdvance);
|
||||
assert.equal(typeof config.source, "function");
|
||||
assert.equal(typeof config.highlighter, "function");
|
||||
assert.equal(typeof config.matcher, "function");
|
||||
assert.equal(typeof config.sorter, "function");
|
||||
assert.equal(typeof config.updater, "function");
|
||||
|
||||
(function test_highlighter() {
|
||||
const $fake_person = $.create("fake-person");
|
||||
typeahead_helper.render_person = () => $fake_person;
|
||||
assert.equal(config.highlighter(), $fake_person);
|
||||
})();
|
||||
|
||||
const fake_context = {
|
||||
query: "ali",
|
||||
};
|
||||
|
||||
const fake_context_for_email = {
|
||||
query: "am",
|
||||
};
|
||||
|
||||
(function test_source() {
|
||||
const result = config.source.call(fake_context, iago);
|
||||
const emails = result.map((user) => user.email).sort();
|
||||
assert.deepEqual(emails, [alice.email, bob.email]);
|
||||
})();
|
||||
|
||||
(function test_matcher() {
|
||||
/* Here the query doesn't begin with an '@' because typeahead is triggered
|
||||
by the '@' sign and thus removed in the query. */
|
||||
let result = config.matcher.call(fake_context, iago);
|
||||
assert.ok(!result);
|
||||
|
||||
result = config.matcher.call(fake_context, alice);
|
||||
assert.ok(result);
|
||||
|
||||
bob.delivery_email = null;
|
||||
result = config.matcher.call(fake_context_for_email, bob);
|
||||
assert.ok(!result);
|
||||
|
||||
bob.delivery_email = "bob-delivery@example.com";
|
||||
result = config.matcher.call(fake_context_for_email, bob);
|
||||
assert.ok(result);
|
||||
})();
|
||||
|
||||
(function test_sorter() {
|
||||
let sort_recipients_typeahead_called = false;
|
||||
typeahead_helper.sort_recipients = function () {
|
||||
sort_recipients_typeahead_called = true;
|
||||
};
|
||||
config.sorter.call(fake_context, []);
|
||||
assert.ok(sort_recipients_typeahead_called);
|
||||
})();
|
||||
|
||||
(function test_updater() {
|
||||
$input_field_stub.text("@ali");
|
||||
user_groups.get_user_group_from_id = () => realm_user_group;
|
||||
|
||||
let saved_fade_out_called = false;
|
||||
let cancel_fade_to_called = false;
|
||||
let instructions_fade_to_called = false;
|
||||
$(saved_selector).fadeOut = () => {
|
||||
saved_fade_out_called = true;
|
||||
};
|
||||
$(cancel_selector).css = (data) => {
|
||||
assert.equal(typeof data, "object");
|
||||
assert.equal(data.display, "inline-block");
|
||||
assert.equal(data.opacity, "0");
|
||||
return $(cancel_selector);
|
||||
};
|
||||
$(cancel_selector).fadeTo = () => {
|
||||
cancel_fade_to_called = true;
|
||||
};
|
||||
$(instructions_selector).css = (data) => {
|
||||
assert.equal(typeof data, "object");
|
||||
assert.equal(data.display, "block");
|
||||
assert.equal(data.opacity, "0");
|
||||
return $(instructions_selector);
|
||||
};
|
||||
$(instructions_selector).fadeTo = () => {
|
||||
instructions_fade_to_called = true;
|
||||
};
|
||||
|
||||
text_cleared = false;
|
||||
config.updater(alice);
|
||||
// update_cancel_button is called.
|
||||
assert.ok(saved_fade_out_called);
|
||||
assert.ok(cancel_fade_to_called);
|
||||
assert.ok(instructions_fade_to_called);
|
||||
assert.equal(text_cleared, true);
|
||||
})();
|
||||
input_typeahead_called = true;
|
||||
};
|
||||
|
||||
let get_by_email_called = false;
|
||||
override_rewire(people, "get_by_email", (user_email) => {
|
||||
get_by_email_called = true;
|
||||
switch (user_email) {
|
||||
case iago.email:
|
||||
return iago;
|
||||
case bob.email:
|
||||
return bob;
|
||||
/* istanbul ignore next */
|
||||
default:
|
||||
throw new Error("Expected user email to be of Iago or Bob here.");
|
||||
}
|
||||
});
|
||||
|
||||
function test_create_item(handler) {
|
||||
(function test_rejection_path() {
|
||||
const item = handler(iago.email, pills.items());
|
||||
assert.ok(get_by_email_called);
|
||||
assert.equal(item, undefined);
|
||||
})();
|
||||
|
||||
(function test_success_path() {
|
||||
get_by_email_called = false;
|
||||
const res = handler(bob.email, pills.items());
|
||||
assert.ok(get_by_email_called);
|
||||
assert.equal(typeof res, "object");
|
||||
assert.equal(res.user_id, bob.user_id);
|
||||
assert.equal(res.display_value, bob.full_name);
|
||||
})();
|
||||
|
||||
(function test_deactivated_pill() {
|
||||
people.deactivate(bob);
|
||||
get_by_email_called = false;
|
||||
const res = handler(bob.email, pills.items());
|
||||
assert.ok(get_by_email_called);
|
||||
assert.equal(typeof res, "object");
|
||||
assert.equal(res.user_id, bob.user_id);
|
||||
assert.equal(res.display_value, bob.full_name);
|
||||
assert.ok(res.deactivated);
|
||||
people.add_active_user(bob);
|
||||
})();
|
||||
}
|
||||
|
||||
pills.onPillRemove = (handler) => {
|
||||
set_global("setTimeout", (func) => {
|
||||
func();
|
||||
});
|
||||
realm_user_group.members = new Set([2, 31]);
|
||||
handler();
|
||||
};
|
||||
|
||||
reset_test_setup($pill_container_stub);
|
||||
settings_user_groups_legacy.set_up();
|
||||
assert.ok(templates_render_called);
|
||||
assert.ok(user_groups_list_append_called);
|
||||
assert.ok(get_by_user_id_called);
|
||||
assert.ok(input_typeahead_called);
|
||||
test_create_item(create_item_handler);
|
||||
|
||||
// Tests for settings_user_groups_legacy.set_up workflow.
|
||||
assert.equal(typeof $("#user-groups").get_on_handler("click", ".delete"), "function");
|
||||
assert.equal(
|
||||
typeof $("#user-groups").get_on_handler("keypress", ".user-group h4 > span"),
|
||||
"function",
|
||||
);
|
||||
});
|
||||
test_ui("with_external_user", ({disallow_rewire, override_rewire, mock_template}) => {
|
||||
const realm_user_group = {
|
||||
id: 1,
|
||||
name: "Mobile",
|
||||
description: "All mobile people",
|
||||
members: new Set([2, 4]),
|
||||
};
|
||||
|
||||
user_groups.get_realm_user_groups = () => [realm_user_group];
|
||||
|
||||
// These are already tested, so we skip them
|
||||
disallow_rewire(people, "get_realm_users");
|
||||
|
||||
mock_template(
|
||||
"settings/admin_user_group_list.hbs",
|
||||
false,
|
||||
() => "settings/admin_user_group_list.hbs",
|
||||
);
|
||||
|
||||
override_rewire(people, "get_by_user_id", () => "user stub");
|
||||
|
||||
override_rewire(user_pill, "append_person", noop);
|
||||
|
||||
let can_edit_called = 0;
|
||||
override_rewire(settings_user_groups_legacy, "can_edit", () => {
|
||||
can_edit_called += 1;
|
||||
return false;
|
||||
});
|
||||
|
||||
// Reset zjquery to test stuff with user who cannot edit
|
||||
$.clear_all_elements();
|
||||
|
||||
let user_group_find_called = 0;
|
||||
const $user_group_stub = $(`div.user-group[id="${CSS.escape(1)}"]`);
|
||||
const $name_field_stub = $.create("fake-name-field");
|
||||
const $description_field_stub = $.create("fake-description-field");
|
||||
const $input_stub = $.create("fake-input");
|
||||
$user_group_stub.find = (elem) => {
|
||||
user_group_find_called += 1;
|
||||
switch (elem) {
|
||||
case ".name":
|
||||
return $name_field_stub;
|
||||
case ".description":
|
||||
return $description_field_stub;
|
||||
/* istanbul ignore next */
|
||||
default:
|
||||
throw new Error(`Unknown element ${elem}`);
|
||||
}
|
||||
};
|
||||
|
||||
const $pill_container_stub = $(`.pill-container[data-group-pills="${CSS.escape(1)}"]`);
|
||||
const $pill_stub = $.create("fake-pill");
|
||||
let pill_container_find_called = 0;
|
||||
$pill_container_stub.find = (elem) => {
|
||||
pill_container_find_called += 1;
|
||||
switch (elem) {
|
||||
case ".input":
|
||||
return $input_stub;
|
||||
case ".pill":
|
||||
return $pill_stub;
|
||||
/* istanbul ignore next */
|
||||
default:
|
||||
throw new Error(`Unknown element ${elem}`);
|
||||
}
|
||||
};
|
||||
|
||||
$input_stub.css = (property, val) => {
|
||||
assert.equal(property, "display");
|
||||
assert.equal(val, "none");
|
||||
};
|
||||
|
||||
// Test the 'off' handlers on the pill-container
|
||||
const turned_off = {};
|
||||
$pill_container_stub.off = (event_name, sel = "whole") => {
|
||||
turned_off[event_name + "/" + sel] = true;
|
||||
};
|
||||
|
||||
const $exit_button = $.create("fake-pill-exit");
|
||||
$pill_stub.set_find_results(".exit", $exit_button);
|
||||
let exit_button_called = false;
|
||||
$exit_button.css = (property, value) => {
|
||||
exit_button_called = true;
|
||||
assert.equal(property, "opacity");
|
||||
assert.equal(value, "0.5");
|
||||
};
|
||||
|
||||
// We return [] because these are already tested, so we skip them
|
||||
$pill_container_stub.children = () => [];
|
||||
|
||||
$("#user-groups").append = noop;
|
||||
|
||||
reset_test_setup($pill_container_stub);
|
||||
|
||||
settings_user_groups_legacy.set_up();
|
||||
|
||||
let set_parents_result_called = 0;
|
||||
let set_attributes_called = 0;
|
||||
|
||||
// Test different handlers with an external user
|
||||
const delete_handler = $("#user-groups").get_on_handler("click", ".delete");
|
||||
const $fake_delete = $.create("fk-#user-groups.delete_btn");
|
||||
$fake_delete.set_parents_result(".user-group", $(".user-group"));
|
||||
set_parents_result_called += 1;
|
||||
$(".user-group").attr("id", "1");
|
||||
set_attributes_called += 1;
|
||||
|
||||
const name_update_handler = $(user_group_selector).get_on_handler("input", ".name");
|
||||
|
||||
const des_update_handler = $(user_group_selector).get_on_handler("input", ".description");
|
||||
|
||||
const member_change_handler = $(user_group_selector).get_on_handler("blur", ".input");
|
||||
|
||||
const name_change_handler = $(user_group_selector).get_on_handler("blur", ".name");
|
||||
|
||||
const des_change_handler = $(user_group_selector).get_on_handler("blur", ".description");
|
||||
|
||||
const event = {
|
||||
stopPropagation: noop,
|
||||
};
|
||||
const pill_mouseenter_handler = $pill_stub.get_on_handler("mouseenter");
|
||||
const pill_click_handler = $pill_container_stub.get_on_handler("click");
|
||||
pill_mouseenter_handler(event);
|
||||
pill_click_handler(event);
|
||||
assert.equal(delete_handler.call($fake_delete), undefined);
|
||||
assert.equal(name_update_handler(), undefined);
|
||||
assert.equal(des_update_handler(), undefined);
|
||||
assert.equal(member_change_handler(), undefined);
|
||||
assert.equal(name_change_handler(), undefined);
|
||||
assert.equal(des_change_handler(), undefined);
|
||||
assert.equal(set_parents_result_called, 1);
|
||||
assert.equal(set_attributes_called, 1);
|
||||
assert.equal(can_edit_called, 9);
|
||||
assert.ok(exit_button_called);
|
||||
assert.equal(user_group_find_called, 2);
|
||||
assert.equal(pill_container_find_called, 4);
|
||||
assert.equal(turned_off["keydown/.pill"], true);
|
||||
assert.equal(turned_off["keydown/.input"], true);
|
||||
assert.equal(turned_off["click/whole"], true);
|
||||
});
|
||||
|
||||
test_ui("reload", ({override_rewire}) => {
|
||||
$("#user-groups").html("Some text");
|
||||
let populate_user_groups_called = false;
|
||||
override_rewire(settings_user_groups_legacy, "populate_user_groups", () => {
|
||||
populate_user_groups_called = true;
|
||||
});
|
||||
settings_user_groups_legacy.reload();
|
||||
assert.ok(populate_user_groups_called);
|
||||
assert.equal($("#user-groups").html(), "");
|
||||
});
|
||||
|
||||
test_ui("reset", () => {
|
||||
settings_user_groups_legacy.reset();
|
||||
const result = settings_user_groups_legacy.reload();
|
||||
assert.equal(result, undefined);
|
||||
});
|
||||
|
||||
test_ui("on_events", ({mock_template, override, override_rewire}) => {
|
||||
override(settings_data, "user_can_edit_user_groups", () => true);
|
||||
mock_template("confirm_dialog/confirm_delete_user.hbs", false, (data) => {
|
||||
assert.deepEqual(data, {
|
||||
group_name: "Mobile",
|
||||
});
|
||||
return "stub";
|
||||
});
|
||||
|
||||
page_params.is_admin = true;
|
||||
|
||||
(function test_admin_user_group_form_submit_triggered() {
|
||||
const handler = settings_user_groups_legacy.add_user_group;
|
||||
const event = {
|
||||
stopPropagation: noop,
|
||||
preventDefault: noop,
|
||||
};
|
||||
const $fake_this = $.create("#add-user-group-form");
|
||||
const fake_object_array = [
|
||||
{
|
||||
name: "fake-name",
|
||||
value: "",
|
||||
},
|
||||
{
|
||||
name: "fake-name",
|
||||
value: "fake-value",
|
||||
},
|
||||
];
|
||||
$fake_this.serializeArray = () => fake_object_array;
|
||||
channel.post = (opts) => {
|
||||
const data = {
|
||||
members: "[null]",
|
||||
};
|
||||
data[fake_object_array[1].name] = fake_object_array[1].value;
|
||||
assert.equal(opts.url, "/json/user_groups/create");
|
||||
assert.deepEqual(opts.data, data);
|
||||
|
||||
(function test_post_success() {
|
||||
$("#dialog_error").show();
|
||||
$("#add-user-group-form input[type='text']").val("fake-content");
|
||||
ui_report.success = (text, ele) => {
|
||||
assert.equal(text, "translated HTML: User group added!");
|
||||
assert.equal(ele, $("#dialog_error"));
|
||||
};
|
||||
dialog_widget.close = () => {};
|
||||
|
||||
opts.success();
|
||||
|
||||
assert.ok(!$("#dialog_error").visible());
|
||||
})();
|
||||
(function test_post_error() {
|
||||
$("#dialog_error").show();
|
||||
ui_report.error = (error_msg, error_obj, ele) => {
|
||||
assert.equal(error_msg, "translated HTML: Failed");
|
||||
assert.deepEqual(error_obj, {responseJson: {msg: "fake-msg"}});
|
||||
assert.equal(ele, $("#dialog_error"));
|
||||
};
|
||||
opts.error({responseJson: {msg: "fake-msg"}});
|
||||
|
||||
assert.ok(!$("#dialog_error").visible());
|
||||
})();
|
||||
};
|
||||
|
||||
handler(event);
|
||||
})();
|
||||
|
||||
(function test_user_groups_delete_click_triggered() {
|
||||
const handler = $("#user-groups").get_on_handler("click", ".delete");
|
||||
const $fake_this = $.create("fake-#user-groups.delete_btn");
|
||||
$fake_this.set_parents_result(".user-group", $(".user-group"));
|
||||
$(".user-group").attr("id", "1");
|
||||
|
||||
channel.del = (opts) => {
|
||||
const data = {
|
||||
id: 1,
|
||||
};
|
||||
assert.equal(opts.url, "/json/user_groups/1");
|
||||
assert.deepEqual(opts.data, data);
|
||||
|
||||
$fake_this.text($t({defaultMessage: "fake-text"}));
|
||||
opts.error();
|
||||
assert.equal($fake_this.text(), "translated: Failed!");
|
||||
};
|
||||
|
||||
confirm_dialog.launch = (conf) => {
|
||||
conf.on_click();
|
||||
};
|
||||
|
||||
handler.call($fake_this);
|
||||
})();
|
||||
|
||||
(function test_user_groups_keypress_enter_triggered() {
|
||||
const handler = $("#user-groups").get_on_handler("keypress", ".user-group h4 > span");
|
||||
let default_action_for_enter_stopped = false;
|
||||
const event = {
|
||||
key: "Enter",
|
||||
preventDefault() {
|
||||
default_action_for_enter_stopped = true;
|
||||
},
|
||||
};
|
||||
handler(event);
|
||||
assert.ok(default_action_for_enter_stopped);
|
||||
})();
|
||||
|
||||
(function test_do_not_blur() {
|
||||
const blur_event_classes = [".name", ".description", ".input"];
|
||||
let api_endpoint_called = false;
|
||||
/* istanbul ignore next */
|
||||
channel.post = () => {
|
||||
api_endpoint_called = true;
|
||||
};
|
||||
channel.patch = noop;
|
||||
const $fake_this = $.create("fake-#user-groups_do_not_blur");
|
||||
const event = {
|
||||
// FIXME: event.relatedTarget should not be a jQuery object
|
||||
relatedTarget: $fake_this,
|
||||
};
|
||||
|
||||
// Any of the blur_exceptions trigger blur event.
|
||||
for (const class_name of blur_event_classes) {
|
||||
const handler = $(user_group_selector).get_on_handler("blur", class_name);
|
||||
|
||||
for (const blur_exception of [
|
||||
".pill-container",
|
||||
".name",
|
||||
".description",
|
||||
".input",
|
||||
".delete",
|
||||
]) {
|
||||
if (blur_exception === class_name) {
|
||||
continue;
|
||||
}
|
||||
api_endpoint_called = false;
|
||||
$fake_this.closest = (class_name) => {
|
||||
if (class_name === blur_exception || class_name === user_group_selector) {
|
||||
return [1];
|
||||
}
|
||||
return [];
|
||||
};
|
||||
handler.call($fake_this, event);
|
||||
assert.ok(!api_endpoint_called);
|
||||
}
|
||||
|
||||
api_endpoint_called = false;
|
||||
$fake_this.closest = (class_name) => {
|
||||
assert.equal(class_name, ".typeahead");
|
||||
return [1];
|
||||
};
|
||||
handler.call($fake_this, event);
|
||||
assert.ok(!api_endpoint_called);
|
||||
|
||||
// Cancel button triggers blur event.
|
||||
let settings_user_groups_legacy_reload_called = false;
|
||||
override_rewire(settings_user_groups_legacy, "reload", () => {
|
||||
settings_user_groups_legacy_reload_called = true;
|
||||
});
|
||||
api_endpoint_called = false;
|
||||
$fake_this.closest = (class_name) => {
|
||||
if (
|
||||
class_name === ".save-status.btn-danger" ||
|
||||
class_name === user_group_selector
|
||||
) {
|
||||
return [1];
|
||||
}
|
||||
return [];
|
||||
};
|
||||
handler.call($fake_this, event);
|
||||
assert.ok(!api_endpoint_called);
|
||||
assert.ok(settings_user_groups_legacy_reload_called);
|
||||
}
|
||||
})();
|
||||
|
||||
(function test_update_cancel_button() {
|
||||
const handler_name = $(user_group_selector).get_on_handler("input", ".name");
|
||||
const handler_desc = $(user_group_selector).get_on_handler("input", ".description");
|
||||
const $sib_des = $(description_selector);
|
||||
const $sib_name = $(name_selector);
|
||||
$sib_name.text($t({defaultMessage: "mobile"}));
|
||||
$sib_des.text($t({defaultMessage: "All mobile members"}));
|
||||
|
||||
const group_data = {
|
||||
name: "translated: mobile",
|
||||
description: "translated: All mobile members",
|
||||
members: new Set([2, 31]),
|
||||
};
|
||||
user_groups.get_user_group_from_id = () => group_data;
|
||||
|
||||
let cancel_fade_out_called = false;
|
||||
let instructions_fade_out_called = false;
|
||||
$(cancel_selector).show();
|
||||
$(cancel_selector).fadeOut = () => {
|
||||
cancel_fade_out_called = true;
|
||||
};
|
||||
$(instructions_selector).fadeOut = () => {
|
||||
instructions_fade_out_called = true;
|
||||
};
|
||||
|
||||
// Cancel button removed if user group if user group has no changes.
|
||||
const $fake_this = $.create("fake-#update_cancel_button");
|
||||
handler_name.call($fake_this);
|
||||
assert.ok(cancel_fade_out_called);
|
||||
assert.ok(instructions_fade_out_called);
|
||||
|
||||
// Check if cancel button removed if user group error is showing.
|
||||
$(user_group_selector + " .user-group-status").show();
|
||||
cancel_fade_out_called = false;
|
||||
instructions_fade_out_called = false;
|
||||
handler_name.call($fake_this);
|
||||
assert.ok(cancel_fade_out_called);
|
||||
assert.ok(instructions_fade_out_called);
|
||||
|
||||
// Check for handler_desc to achieve 100% coverage.
|
||||
cancel_fade_out_called = false;
|
||||
instructions_fade_out_called = false;
|
||||
handler_desc.call($fake_this);
|
||||
assert.ok(cancel_fade_out_called);
|
||||
assert.ok(instructions_fade_out_called);
|
||||
})();
|
||||
|
||||
(function test_user_groups_save_group_changes_triggered() {
|
||||
const handler_name = $(user_group_selector).get_on_handler("blur", ".name");
|
||||
const handler_desc = $(user_group_selector).get_on_handler("blur", ".description");
|
||||
const $sib_des = $(description_selector);
|
||||
const $sib_name = $(name_selector);
|
||||
$sib_name.text($t({defaultMessage: "mobile"}));
|
||||
$sib_des.text($t({defaultMessage: "All mobile members"}));
|
||||
|
||||
const group_data = {members: new Set([2, 31])};
|
||||
user_groups.get_user_group_from_id = () => group_data;
|
||||
let api_endpoint_called = false;
|
||||
let cancel_fade_out_called = false;
|
||||
let saved_fade_to_called = false;
|
||||
let instructions_fade_out_called = false;
|
||||
$(instructions_selector).fadeOut = () => {
|
||||
instructions_fade_out_called = true;
|
||||
};
|
||||
$(cancel_selector).fadeOut = () => {
|
||||
cancel_fade_out_called = true;
|
||||
};
|
||||
$(saved_selector).css = (data) => {
|
||||
assert.equal(typeof data, "object");
|
||||
assert.equal(data.display, "inline-block");
|
||||
assert.equal(data.opacity, "0");
|
||||
return $(saved_selector);
|
||||
};
|
||||
$(saved_selector).fadeTo = () => {
|
||||
saved_fade_to_called = true;
|
||||
return $(saved_selector);
|
||||
};
|
||||
|
||||
channel.patch = (opts) => {
|
||||
assert.equal(opts.url, "/json/user_groups/1");
|
||||
assert.equal(opts.data.name, "translated: mobile");
|
||||
assert.equal(opts.data.description, "translated: All mobile members");
|
||||
api_endpoint_called = true;
|
||||
(function test_post_success() {
|
||||
set_global("setTimeout", (func) => {
|
||||
func();
|
||||
});
|
||||
opts.success();
|
||||
assert.ok(cancel_fade_out_called);
|
||||
assert.ok(instructions_fade_out_called);
|
||||
assert.ok(saved_fade_to_called);
|
||||
})();
|
||||
(function test_post_error() {
|
||||
const $user_group_error = $(user_group_selector + " .user-group-status");
|
||||
$user_group_error.show();
|
||||
ui_report.error = (error_msg, error_obj, ele) => {
|
||||
assert.equal(error_msg, "translated HTML: Failed");
|
||||
assert.deepEqual(error_obj, {responseJson: {msg: "fake-msg"}});
|
||||
assert.equal(ele, $user_group_error);
|
||||
};
|
||||
opts.error({responseJson: {msg: "fake-msg"}});
|
||||
|
||||
assert.ok($user_group_error.visible());
|
||||
})();
|
||||
};
|
||||
|
||||
const $fake_this = $.create("fake-#user-groups_blur_name");
|
||||
$fake_this.closest = () => [];
|
||||
$fake_this.set_parents_result(user_group_selector, $(user_group_selector));
|
||||
const event = {
|
||||
// FIXME: event.relatedTarget should not be a jQuery object
|
||||
relatedTarget: $fake_this,
|
||||
};
|
||||
|
||||
api_endpoint_called = false;
|
||||
handler_name.call($fake_this, event);
|
||||
assert.ok(api_endpoint_called);
|
||||
|
||||
// Check API endpoint isn't called if name and desc haven't changed.
|
||||
group_data.name = "translated: mobile";
|
||||
group_data.description = "translated: All mobile members";
|
||||
api_endpoint_called = false;
|
||||
handler_name.call($fake_this, event);
|
||||
assert.ok(!api_endpoint_called);
|
||||
|
||||
// Check for handler_desc to achieve 100% coverage.
|
||||
api_endpoint_called = false;
|
||||
handler_desc.call($fake_this, event);
|
||||
assert.ok(!api_endpoint_called);
|
||||
})();
|
||||
|
||||
(function test_user_groups_save_member_changes_triggered() {
|
||||
const handler = $(user_group_selector).get_on_handler("blur", ".input");
|
||||
const realm_user_group = {
|
||||
id: 1,
|
||||
name: "Mobile",
|
||||
description: "All mobile people",
|
||||
members: new Set([2, 4]),
|
||||
};
|
||||
|
||||
user_groups.get_user_group_from_id = (id) => {
|
||||
assert.equal(id, 1);
|
||||
return realm_user_group;
|
||||
};
|
||||
|
||||
let cancel_fade_out_called = false;
|
||||
let saved_fade_to_called = false;
|
||||
let instructions_fade_out_called = false;
|
||||
$(instructions_selector).fadeOut = () => {
|
||||
instructions_fade_out_called = true;
|
||||
};
|
||||
$(cancel_selector).fadeOut = () => {
|
||||
cancel_fade_out_called = true;
|
||||
};
|
||||
$(saved_selector).css = () => $(saved_selector);
|
||||
$(saved_selector).fadeTo = () => {
|
||||
saved_fade_to_called = true;
|
||||
return $(saved_selector);
|
||||
};
|
||||
|
||||
let api_endpoint_called = false;
|
||||
channel.post = (opts) => {
|
||||
assert.equal(opts.url, "/json/user_groups/1/members");
|
||||
assert.equal(opts.data.add, "[31]");
|
||||
assert.equal(opts.data.delete, "[4]");
|
||||
api_endpoint_called = true;
|
||||
|
||||
(function test_post_success() {
|
||||
opts.success();
|
||||
assert.ok(cancel_fade_out_called);
|
||||
assert.ok(instructions_fade_out_called);
|
||||
assert.ok(saved_fade_to_called);
|
||||
})();
|
||||
};
|
||||
|
||||
const $fake_this = $.create("fake-#user-groups_blur_input");
|
||||
$fake_this.set_parents_result(user_group_selector, $(user_group_selector));
|
||||
$fake_this.closest = () => [];
|
||||
const event = {
|
||||
// FIXME: event.relatedTarget should not be a jQuery object
|
||||
relatedTarget: $fake_this,
|
||||
};
|
||||
|
||||
api_endpoint_called = false;
|
||||
handler.call($fake_this, event);
|
||||
assert.ok(api_endpoint_called);
|
||||
})();
|
||||
});
|
|
@ -4,6 +4,7 @@ const {strict: assert} = require("assert");
|
|||
|
||||
const {zrequire} = require("./lib/namespace");
|
||||
const {run_test} = require("./lib/test");
|
||||
const blueslip = require("./lib/zblueslip");
|
||||
const {page_params} = require("./lib/zpage_params");
|
||||
|
||||
const people = zrequire("people");
|
||||
|
@ -102,6 +103,9 @@ test("append", () => {
|
|||
|
||||
assert.ok(appended);
|
||||
assert.ok(cleared);
|
||||
|
||||
blueslip.expect("warn", "Undefined user in function append_user");
|
||||
user_pill.append_user(undefined, pill_widget);
|
||||
});
|
||||
|
||||
test("get_items", () => {
|
||||
|
|
Loading…
Reference in New Issue