compose: Use tippy for stream select dropdown.

Fixes #25434
This commit is contained in:
Aman Agrawal 2023-05-07 12:45:04 +00:00 committed by Tim Abbott
parent a196b949f7
commit 8ea59f7f02
25 changed files with 437 additions and 353 deletions

View File

@ -13,12 +13,11 @@ async function check_compose_form_empty(page: Page): Promise<void> {
}
async function close_compose_box(page: Page): Promise<void> {
const recipient_dropdown_visible =
(await page.$("#compose_select_recipient_widget .open")) !== null;
const recipient_dropdown_visible = (await page.$(".dropdown-list-container")) !== null;
if (recipient_dropdown_visible) {
await page.keyboard.press("Escape");
await page.waitForSelector("#id_compose_select_recipient.open", {hidden: true});
await page.waitForSelector(".dropdown-list-container", {hidden: true});
}
await page.keyboard.press("Escape");
await page.waitForSelector("#compose-textarea", {hidden: true});
@ -126,9 +125,7 @@ async function test_narrow_to_private_messages_with_cordelia(page: Page): Promis
await page.keyboard.press("KeyC");
await page.waitForSelector("#compose", {visible: true});
await page.waitForSelector(".compose_table #id_compose_select_recipient.open", {
visible: true,
});
await page.waitForSelector(`.dropdown-list-container .list-item`, {visible: true});
await close_compose_box(page);
}

View File

@ -35,7 +35,7 @@ async function create_stream_message_draft(page: Page): Promise<void> {
console.log("Creating stream message draft");
await page.keyboard.press("KeyC");
await page.waitForSelector("#stream_message_recipient_topic", {visible: true});
await common.select_item_via_dropdown(page, "#compose_select_recipient_widget", "Denmark");
await common.select_stream_in_compose_via_dropdown(page, "Denmark");
await common.fill_form(page, "form#send_message_form", {
stream_message_recipient_topic: "tests",
content: "Test stream message.",
@ -129,7 +129,7 @@ async function test_restore_message_draft_via_draft_overlay(page: Page): Promise
}
async function edit_stream_message_draft(page: Page): Promise<void> {
await common.select_item_via_dropdown(page, "#compose_select_recipient_widget", "Denmark");
await common.select_stream_in_compose_via_dropdown(page, "Denmark");
await common.fill_form(page, "form#send_message_form", {
stream_message_recipient_topic: "tests",
content: "Updated stream message",

View File

@ -384,24 +384,23 @@ export async function wait_for_fully_processed_message(page: Page, content: stri
await scroll_delay;
}
export async function select_item_via_dropdown(
export async function select_stream_in_compose_via_dropdown(
page: Page,
dropdown_selector: string,
item: string,
stream_name: string,
): Promise<void> {
console.log(`Clicking on ${dropdown_selector} to select ${item}`);
const menu_visible = (await page.$(`${dropdown_selector} .open`)) !== null;
console.log(`Clicking on 'compose_select_recipient_widget' to select ${stream_name}`);
const menu_visible = (await page.$(".dropdown-list-container")) !== null;
if (!menu_visible) {
await page.waitForSelector(dropdown_selector, {visible: true});
await page.click(`${dropdown_selector} .dropdown-toggle`);
await page.waitForSelector(`${dropdown_selector} .dropdown-menu`, {visible: true});
await page.waitForSelector("#compose_select_recipient_widget", {visible: true});
await page.click("#compose_select_recipient_widget");
await page.waitForSelector(".dropdown-list-container .list-item", {
visible: true,
});
}
const entry_selector = `xpath///*[${has_class_x(
"list_item",
)} and contains(normalize-space(), "${item}")]`;
await page.waitForSelector(entry_selector, {visible: true});
await page.click(entry_selector);
await page.waitForSelector(`.dropdown-menu`, {visible: false});
const stream_to_select = `.dropdown-list-container .list-item[data-name="${stream_name}"]`;
await page.waitForSelector(stream_to_select, {visible: true});
await page.click(stream_to_select);
assert((await page.$(".dropdown-list-container")) === null);
}
// Wait for any previous send to finish, then send a message.
@ -432,7 +431,7 @@ export async function send_message(
}
if (params.stream) {
await select_item_via_dropdown(page, "#compose_select_recipient_widget", params.stream);
await select_stream_in_compose_via_dropdown(page, params.stream);
delete params.stream;
}

View File

@ -11,7 +11,7 @@ async function test_mention(page: Page): Promise<void> {
await page.keyboard.press("KeyC");
await page.waitForSelector("#compose", {visible: true});
await common.select_item_via_dropdown(page, "#compose_select_recipient_widget", "Verona");
await common.select_stream_in_compose_via_dropdown(page, "Verona");
await common.fill_form(page, 'form[action^="/json/messages"]', {
stream_message_recipient_topic: "Test mention all",
});

View File

@ -153,17 +153,16 @@ export function create_message_object() {
} else {
const stream_name = compose_state.stream_name();
message.stream = stream_name;
const sub = stream_data.get_sub(stream_name);
if (sub) {
message.stream_id = sub.stream_id;
message.to = sub.stream_id;
if (stream_name) {
message.stream_id = compose_recipient.selected_recipient_id;
message.to = compose_recipient.selected_recipient_id;
} else {
// We should be validating streams in calling code. We'll
// try to fall back to stream_name here just in case the
// user started composing to the old stream name and
// manually entered the stream name, and it got past
// validation. We should try to kill this code off eventually.
blueslip.error("Trying to send message with bad stream name", {stream_name});
blueslip.error("Trying to send message with bad stream name");
message.to = stream_name;
}
message.topic = topic;

View File

@ -208,20 +208,19 @@ export function start(msg_type, opts) {
clear_box();
}
compose_recipient.compose_recipient_widget.render(opts.stream);
const $stream_header_colorblock = $(
"#compose_recipient_selection_dropdown .stream_header_colorblock",
);
stream_bar.decorate(opts.stream, $stream_header_colorblock);
// We set the stream/topic/private_message_recipient
// unconditionally here, which assumes the caller will have passed
// '' or undefined for these values if they are not appropriate
// for this message.
//
// TODO: Move these into a conditional on message_type, using an
// explicit "clear" function for compose_state.
if (msg_type === "private") {
compose_state.set_compose_recipient_id(compose_recipient.DIRECT_MESSAGE_ID);
} else if (opts.stream) {
compose_state.set_stream_name(opts.stream);
} else {
// Open stream selection dropdown if no stream is selected.
compose_recipient.open_compose_recipient_dropdown();
}
compose_state.topic(opts.topic);
// Set the recipients with a space after each comma, so it looks nice.

View File

@ -137,12 +137,6 @@ export function show_stream_does_not_exist_error(stream_name: string): void {
append_compose_banner_to_banner_list(new_row, $("#compose_banners"));
hide_compose_spinner();
// A copy of `compose_recipient.open_compose_stream_dropup()` that
// can't be imported due to typescript and import circles.
// TODO: Once we use stream IDs, not names, as the fundamental
// compose_state storage for streams, this error will be impossible.
if ($("#id_compose_select_recipient").hasClass("open")) {
return;
}
$("#id_compose_select_recipient button").trigger("click");
// Open stream select dropdown.
$("#compose_select_recipient_widget").trigger("click");
}

View File

@ -3,13 +3,15 @@
import $ from "jquery";
import _ from "lodash";
import render_inline_decorated_stream_name from "../templates/inline_decorated_stream_name.hbs";
import * as compose_banner from "./compose_banner";
import * as compose_fade from "./compose_fade";
import * as compose_pm_pill from "./compose_pm_pill";
import * as compose_state from "./compose_state";
import * as compose_ui from "./compose_ui";
import * as compose_validate from "./compose_validate";
import {DropdownListWidget} from "./dropdown_list_widget";
import * as dropdown_widget from "./dropdown_widget";
import {$t} from "./i18n";
import * as narrow_state from "./narrow_state";
import {page_params} from "./page_params";
@ -19,9 +21,17 @@ import * as stream_data from "./stream_data";
import * as ui_util from "./ui_util";
import * as util from "./util";
export let compose_recipient_widget;
// selected_recipient_id is the current state for the stream picker widget:
// "" -> stream message but no stream is selected
// integer -> stream id of the selected stream.
// "direct" -> Direct message is selected.
export let selected_recipient_id = "";
export const DIRECT_MESSAGE_ID = "direct";
const DIRECT_MESSAGE = "direct";
export function set_selected_recipient_id(recipient_id) {
selected_recipient_id = recipient_id;
on_compose_select_recipient_update();
}
function composing_to_current_topic_narrow() {
return (
@ -98,16 +108,6 @@ export function update_on_recipient_change() {
update_narrow_to_recipient_visibility();
}
export function open_compose_stream_dropup() {
if ($("#id_compose_select_recipient").hasClass("open")) {
return;
}
// We trigger a click rather than directly toggling the element;
// this is important to ensure the filter text gets cleared when
// reopening the widget after previous use.
$("#id_compose_select_recipient > .dropdown-toggle").trigger("click");
}
export function check_stream_posting_policy_for_compose_box(stream_name) {
const stream = stream_data.get_sub_by_name(stream_name);
if (!stream) {
@ -145,13 +145,21 @@ function switch_message_type(message_type) {
compose_ui.set_focus(message_type, opts);
}
export function update_compose_for_message_type(message_type) {
export function update_compose_for_message_type(message_type, opts) {
if (message_type === "stream") {
$("#compose-direct-recipient").hide();
$("#stream_message_recipient_topic").show();
$("#stream_toggle").addClass("active");
$("#private_message_toggle").removeClass("active");
$("#compose-recipient").removeClass("compose-recipient-direct-selected");
const stream = stream_data.get_sub_by_name(opts.stream);
if (stream === undefined) {
$("#compose_select_recipient_name").text($t({defaultMessage: "Select a stream"}));
} else {
$("#compose_select_recipient_name").html(
render_inline_decorated_stream_name({stream, show_colored_icon: true}),
);
}
} else {
$("#compose-direct-recipient").show();
$("#stream_message_recipient_topic").hide();
@ -171,9 +179,13 @@ export function update_compose_for_message_type(message_type) {
compose_banner.clear_warnings();
}
export function on_compose_select_recipient_update(new_value) {
const message_type = compose_state.get_message_type();
if (new_value === DIRECT_MESSAGE) {
export function on_compose_select_recipient_update() {
let message_type = "stream";
if (selected_recipient_id === DIRECT_MESSAGE_ID) {
message_type = "private";
}
compose_state.set_message_type(message_type);
if (message_type === "private") {
// TODO: In theory, we could do something more lightweight in
// the case it's already that value, but doing nothing would
// display the wrong and fail to update focus properly.
@ -186,28 +198,33 @@ export function on_compose_select_recipient_update(new_value) {
const $stream_header_colorblock = $(
"#compose_recipient_selection_dropdown .stream_header_colorblock",
);
stream_bar.decorate(new_value, $stream_header_colorblock);
if (message_type === "private") {
const stream_name = compose_state.stream_name();
stream_bar.decorate(stream_name, $stream_header_colorblock);
switch_message_type("stream");
}
// Always move focus to the topic input even if it's not empty,
// since it's likely the user will want to update the topic
// after updating the stream.
ui_util.place_caret_at_end($("#stream_message_recipient_topic")[0]);
check_stream_posting_policy_for_compose_box(new_value);
check_stream_posting_policy_for_compose_box(stream_name);
}
update_on_recipient_change();
}
export function update_stream_dropdown_options() {
compose_recipient_widget.replace_data(get_options_for_recipient_widget());
export function possibly_update_stream_name_in_compose(stream_id) {
if (selected_recipient_id === stream_id) {
on_compose_select_recipient_update();
}
}
export function possibly_update_dropdown_selection(old_stream_name, new_stream_name) {
const selected_stream = compose_state.stream_name();
if (selected_stream === old_stream_name) {
compose_state.set_stream_name(new_stream_name);
function item_click_callback(event, dropdown) {
let recipient_id = $(event.currentTarget).attr("data-unique-id");
if (recipient_id !== DIRECT_MESSAGE_ID) {
recipient_id = Number.parseInt(recipient_id, 10);
}
set_selected_recipient_id(recipient_id);
dropdown.hide();
event.preventDefault();
event.stopPropagation();
}
function get_options_for_recipient_widget() {
@ -215,7 +232,7 @@ function get_options_for_recipient_widget() {
.subscribed_subs()
.map((stream) => ({
name: stream.name,
value: stream.name,
unique_id: stream.stream_id,
stream,
}))
.sort((a, b) => {
@ -229,10 +246,11 @@ function get_options_for_recipient_widget() {
});
const direct_messages_option = {
name: $t({defaultMessage: "Direct message"}),
value: DIRECT_MESSAGE,
is_direct_message: true,
unique_id: DIRECT_MESSAGE_ID,
name: $t({defaultMessage: "Direct message"}),
};
if (
page_params.realm_private_message_policy ===
settings_config.private_message_policy_values.by_anyone.code
@ -244,23 +262,61 @@ function get_options_for_recipient_widget() {
return options;
}
export function initialize() {
const opts = {
widget_name: "compose_select_recipient",
data: get_options_for_recipient_widget(),
default_text: $t({defaultMessage: "Select a stream"}),
value: null,
on_update: on_compose_select_recipient_update,
};
compose_recipient_widget = new DropdownListWidget(opts);
compose_recipient_widget.setup();
function compose_recipient_dropdown_on_show(dropdown) {
// Offset to display dropdown above compose.
let top_offset = 5;
const window_height = window.innerHeight;
const search_box_and_padding_height = 50;
// pixels above compose box.
const recipient_input_top = $("#compose_recipient_selection_dropdown").offset().top;
const top_space = recipient_input_top - top_offset - search_box_and_padding_height;
// pixels below compose starting from top of compose box.
const bottom_space = window_height - recipient_input_top - search_box_and_padding_height;
// Show dropdown on top / bottom based on available space.
let placement = "top-start";
if (bottom_space > top_space) {
placement = "bottom-start";
top_offset = -30;
}
const offset = [-10, top_offset];
dropdown.setProps({placement, offset});
const height = Math.min(
dropdown_widget.DEFAULT_DROPDOWN_HEIGHT,
Math.max(top_space, bottom_space),
);
const $popper = $(dropdown.popper);
$popper.find(".dropdown-list-wrapper").css("height", height + "px");
}
$("#compose_select_recipient_widget").on("select", (e) => {
// We often focus on input fields to bring the user to fill it out.
// In this situation, a focus on the dropdown div opens the dropdown
// menu so that the user can select an option.
open_compose_stream_dropup();
export function open_compose_recipient_dropdown() {
$("#compose_select_recipient_widget").trigger("click");
}
function focus_compose_recipient() {
$("#compose_recipient_selection_dropdown").trigger("focus");
}
export function initialize() {
dropdown_widget.setup(
{
target: "#compose_select_recipient_widget",
},
get_options_for_recipient_widget,
item_click_callback,
{
on_show_callback: compose_recipient_dropdown_on_show,
on_exit_with_escape_callback: focus_compose_recipient,
// We want to focus on topic box if dropdown was closed via selecting an item.
focus_target_on_hidden: false,
},
);
$("#compose_recipient_selection_dropdown").on("keydown", (e) => {
if (e.key === "Enter") {
open_compose_recipient_dropdown();
e.stopPropagation();
e.preventDefault();
}
});
// `keyup` isn't relevant for streams since it registers as a change only

View File

@ -1,7 +1,9 @@
import $ from "jquery";
import * as blueslip from "./blueslip";
import * as compose_pm_pill from "./compose_pm_pill";
import * as compose_recipient from "./compose_recipient";
import * as stream_data from "./stream_data";
let message_type = false; // 'stream', 'private', or false-y
let recipient_edited_manually = false;
@ -67,15 +69,40 @@ function get_or_set(fieldname, keep_leading_whitespace, no_trim) {
};
}
// NOTE: See `selected_recipient_id` in compose_recipient to for
// documentation on the variable and how it is used.
export function stream_name() {
return compose_recipient.compose_recipient_widget.value();
const stream_id = compose_recipient.selected_recipient_id;
if (typeof stream_id === "number") {
return stream_data.maybe_get_stream_name(stream_id) || "";
}
return "";
}
export function set_stream_name(newval) {
if (newval !== undefined && newval !== "" && compose_recipient.compose_recipient_widget) {
compose_recipient.compose_recipient_widget.render(newval);
compose_recipient.on_compose_select_recipient_update(newval);
export function set_stream_name(stream_name) {
if (!stream_name) {
compose_recipient.set_selected_recipient_id("");
return;
}
// If we fail to select a stream that the caller expects
// us to do successfully, we should throw an error.
const stream_id = stream_data.get_stream_id(stream_name);
if (stream_id === undefined) {
blueslip.error("Unable to select stream: " + stream_name);
compose_recipient.set_selected_recipient_id("");
return;
}
compose_recipient.set_selected_recipient_id(stream_id);
}
export function set_compose_recipient_id(value) {
let recipient_id = compose_recipient.DIRECT_MESSAGE_ID;
if (typeof value === "number") {
// value is stream name
recipient_id = stream_data.maybe_get_stream_name(value) || "";
}
compose_recipient.set_selected_recipient_id(recipient_id);
}
// TODO: Break out setter and getter into their own functions.

View File

@ -281,9 +281,6 @@ export function make_compose_box_full_size() {
// Set the `top` property of compose-box.
set_compose_box_top(true);
// The compose select dropup should now open down because it's
// at the top of the screen.
$("#id_compose_select_recipient").removeClass("dropup").addClass("dropdown");
$(".collapse_composebox_button").show();
$(".expand_composebox_button").hide();
@ -298,9 +295,6 @@ export function make_compose_box_original_size() {
// Unset the `top` property of compose-box.
set_compose_box_top(false);
// The compose select dropup should now open up because it's
// near the bottom of the screen.
$("#id_compose_select_recipient").removeClass("dropdown").addClass("dropup");
// Again initialise the compose textarea as it was destroyed
// when compose box was made full screen

View File

@ -583,7 +583,7 @@ export function process_shift_tab_key() {
}
if ($("#stream_message_recipient_topic").is(":focus")) {
compose_recipient.open_compose_stream_dropup();
compose_recipient.open_compose_recipient_dropdown();
return true;
}

View File

@ -523,17 +523,19 @@ export function dispatch_normal_event(event) {
const is_narrowed_to_stream = narrow_state.is_for_stream_id(
stream.stream_id,
);
if (is_narrowed_to_stream) {
message_lists.current.update_trailing_bookend();
}
stream_data.delete_sub(stream.stream_id);
stream_settings_ui.remove_stream(stream.stream_id);
if (was_subscribed) {
stream_list.remove_sidebar_row(stream.stream_id);
compose_recipient.update_stream_dropdown_options();
if (stream.stream_id === compose_recipient.selected_recipient_id) {
compose_recipient.set_selected_recipient_id("");
}
}
settings_streams.update_default_streams_table();
stream_data.remove_default_stream(stream.stream_id);
if (is_narrowed_to_stream) {
message_lists.current.update_trailing_bookend();
}
if (page_params.realm_notifications_stream_id === stream.stream_id) {
page_params.realm_notifications_stream_id = -1;
settings_org.sync_realm_settings("notifications_stream_id");

View File

@ -63,8 +63,7 @@ export function update_property(stream_id, property, value, other_values) {
break;
case "name":
stream_settings_ui.update_stream_name(sub, value);
compose_recipient.update_stream_dropdown_options();
compose_recipient.possibly_update_dropdown_selection(sub.name, value);
compose_recipient.possibly_update_stream_name_in_compose(sub.stream_id);
break;
case "description":
stream_settings_ui.update_stream_description(
@ -86,8 +85,7 @@ export function update_property(stream_id, property, value, other_values) {
history_public_to_subscribers: other_values.history_public_to_subscribers,
is_web_public: other_values.is_web_public,
});
// Force a re-render to get the right privacy icon
compose_recipient.possibly_update_dropdown_selection(sub.name, sub.name);
compose_recipient.on_compose_select_recipient_update();
break;
case "stream_post_policy":
stream_settings_ui.update_stream_post_policy(sub, value);
@ -132,7 +130,6 @@ export function mark_subscribed(sub, subscribers, color) {
stream_settings_ui.set_color(sub.stream_id, color);
}
stream_data.subscribe_myself(sub);
compose_recipient.update_stream_dropdown_options();
if (subscribers) {
peer_data.set_subscribers(sub.stream_id, subscribers);
}
@ -162,7 +159,6 @@ export function mark_unsubscribed(sub) {
return;
} else if (sub.subscribed) {
stream_data.unsubscribe_myself(sub);
compose_recipient.update_stream_dropdown_options();
if (overlays.streams_open()) {
stream_settings_ui.update_settings_for_unsubscribed(sub);
}

View File

@ -108,7 +108,7 @@
flex: 1;
&.compose-recipient-direct-selected {
#compose_recipient_selection_dropdown .dropdown-toggle {
#compose_select_recipient_widget {
border-radius: 4px !important;
}
@ -520,11 +520,9 @@ input.recipient_box {
border-radius: 3px;
}
#compose_select_recipient_widget,
#compose_select_recipient_widget .button {
margin: 0;
min-width: 0;
flex: 1;
#compose_select_recipient_widget {
border-radius: 0 4px 4px 0;
width: auto;
}
#stream_message_recipient_topic.recipient_box {
@ -828,10 +826,6 @@ a.compose_control_button.hide {
min-width: 0;
}
#id_compose_select_recipient {
display: flex;
}
.dropdown-menu {
& ul {
list-style: none;

View File

@ -55,12 +55,15 @@
<button type="button" class="close fa fa-times" id='compose_close' data-tooltip-template-id="compose_close_tooltip_template"></button>
</div>
<div id="compose-recipient" class="order-1">
<a role="button" class="narrow_to_compose_recipients zulip-icon zulip-icon-arrow-left-circle order-1" data-tooltip-template-id="narrow_to_compose_recipients_tooltip" tabindex="0"></a>
<div id="compose_recipient_selection_dropdown" class="new-style">
<a role="button" class="narrow_to_compose_recipients zulip-icon zulip-icon-arrow-left-circle order-1" data-tooltip-template-id="narrow_to_compose_recipients_tooltip"></a>
<div id="compose_recipient_selection_dropdown" class="new-style" tabindex="0">
<div class="stream_header_colorblock"></div>
{{> settings/dropdown_list_widget
widget_name="compose_select_recipient"
list_placeholder=(t 'Filter')}}
<div id="compose_select_recipient_widget" class="dropdown-widget-button" role="button">
<span id="compose_select_recipient_name">
{{t 'Select a stream'}}
</span>
<i class="fa fa-chevron-down"></i>
</div>
</div>
<i class="fa fa-angle-right" aria-hidden="true"></i>
<input type="text" class="recipient_box" name="stream_message_recipient_topic" id="stream_message_recipient_topic" maxlength="{{ max_topic_length }}" value="" placeholder="{{t 'Topic' }}" autocomplete="off" tabindex="0" aria-label="{{t 'Topic' }}" />

View File

@ -304,18 +304,7 @@ test_ui("enter_with_preview_open", ({override, override_rewire}) => {
mock_banners();
$("#compose-textarea").toggleClass = noop;
mock_stream_header_colorblock();
compose_recipient.open_compose_stream_dropup = noop;
override_rewire(compose_recipient, "on_compose_select_recipient_update", noop);
let stream_value = "";
compose_recipient.compose_recipient_widget = {
value() {
return stream_value;
},
render(val) {
stream_value = val;
},
};
override_rewire(compose_banner, "clear_message_sent_banners", () => {});
override(document, "to_$", () => $("document-stub"));
let show_button_spinner_called = false;
@ -748,11 +737,11 @@ test_ui("create_message_object", ({override, override_rewire}) => {
assert.equal(message.topic, "lunch");
assert.equal(message.content, "burrito");
blueslip.expect("error", "Trying to send message with bad stream name");
blueslip.expect("error", "Unable to select stream: BOGUS STREAM");
compose_state.set_stream_name("BOGUS STREAM");
blueslip.expect("error", "Trying to send message with bad stream name.");
message = compose.create_message_object();
assert.equal(message.to, "BOGUS STREAM");
assert.equal(message.to, "");
assert.equal(message.topic, "lunch");
assert.equal(message.content, "burrito");

View File

@ -32,6 +32,7 @@ const compose_ui = mock_esm("../src/compose_ui", {
autosize_textarea: noop,
is_full_size: () => false,
set_focus: noop,
compute_placeholder_text: noop,
});
const hash_util = mock_esm("../src/hash_util");
const narrow_state = mock_esm("../src/narrow_state", {
@ -68,16 +69,6 @@ const message_lists = zrequire("message_lists");
const stream_data = zrequire("stream_data");
const compose_recipient = zrequire("compose_recipient");
let stream_value = "";
compose_recipient.compose_recipient_widget = {
value() {
return stream_value;
},
render(val) {
stream_value = val;
},
};
const start = compose_actions.start;
const cancel = compose_actions.cancel;
const respond_to_message = compose_actions.respond_to_message;
@ -85,6 +76,7 @@ const reply_with_mention = compose_actions.reply_with_mention;
const quote_and_reply = compose_actions.quote_and_reply;
compose_recipient.update_narrow_to_recipient_visibility = noop;
compose_recipient.on_compose_select_recipient_update = noop;
function assert_visible(sel) {
assert.ok($(sel).visible());
@ -120,7 +112,7 @@ test("initial_state", () => {
assert.equal(compose_state.has_message_content(), false);
});
test("start", ({override, override_rewire}) => {
test("start", ({override, override_rewire, mock_template}) => {
mock_banners();
override_private_message_recipient({override});
override_rewire(compose_actions, "autosize_message_content", () => {});
@ -130,6 +122,7 @@ test("start", ({override, override_rewire}) => {
override_rewire(compose_actions, "clear_textarea", () => {});
override_rewire(compose_recipient, "on_compose_select_recipient_update", () => {});
override_rewire(compose_recipient, "check_stream_posting_policy_for_compose_box", () => {});
mock_template("inline_decorated_stream_name.hbs", false, () => {});
mock_stream_header_colorblock();
let compose_defaults;
@ -137,7 +130,7 @@ test("start", ({override, override_rewire}) => {
// Start stream message
compose_defaults = {
stream: "stream1",
stream: "",
topic: "topic1",
};
@ -147,7 +140,7 @@ test("start", ({override, override_rewire}) => {
assert_visible("#stream_message_recipient_topic");
assert_hidden("#compose-direct-recipient");
assert.equal(compose_state.stream_name(), "stream1");
assert.equal(compose_state.stream_name(), "");
assert.equal(compose_state.topic(), "topic1");
assert.equal(compose_state.get_message_type(), "stream");
assert.ok(compose_state.composing());
@ -187,6 +180,7 @@ test("start", ({override, override_rewire}) => {
};
stream_data.add_sub(social);
compose_state.set_stream_name("");
// More than 1 subscription, do not autofill
opts = {};
start("stream", opts);
@ -246,12 +240,14 @@ test("start", ({override, override_rewire}) => {
assert.ok(!compose_state.composing());
});
test("respond_to_message", ({override, override_rewire}) => {
test("respond_to_message", ({override, override_rewire, mock_template}) => {
mock_banners();
override_rewire(compose_actions, "complete_starting_tasks", () => {});
override_rewire(compose_actions, "clear_textarea", () => {});
override_rewire(compose_recipient, "on_compose_select_recipient_update", noop);
override_rewire(compose_recipient, "check_stream_posting_policy_for_compose_box", noop);
override_private_message_recipient({override});
mock_template("inline_decorated_stream_name.hbs", false, () => {});
mock_stream_header_colorblock();
// Test PM
@ -278,17 +274,24 @@ test("respond_to_message", ({override, override_rewire}) => {
// Test stream
msg = {
type: "stream",
stream: "devel",
stream: "Denmark",
topic: "python",
};
const denmark = {
subscribed: true,
color: "blue",
name: "Denmark",
stream_id: 1,
};
stream_data.add_sub(denmark);
opts = {};
respond_to_message(opts);
assert.equal(compose_state.stream_name(), "devel");
assert.equal(compose_state.stream_name(), "Denmark");
});
test("reply_with_mention", ({override, override_rewire}) => {
test("reply_with_mention", ({override, override_rewire, mock_template}) => {
mock_banners();
mock_stream_header_colorblock();
compose_state.set_message_type("stream");
@ -296,10 +299,20 @@ test("reply_with_mention", ({override, override_rewire}) => {
override_rewire(compose_actions, "complete_starting_tasks", () => {});
override_rewire(compose_actions, "clear_textarea", () => {});
override_private_message_recipient({override});
override_rewire(compose_recipient, "check_stream_posting_policy_for_compose_box", noop);
mock_template("inline_decorated_stream_name.hbs", false, () => {});
const denmark = {
subscribed: true,
color: "blue",
name: "Denmark",
stream_id: 1,
};
stream_data.add_sub(denmark);
const msg = {
type: "stream",
stream: "devel",
stream: "Denmark",
topic: "python",
sender_full_name: "Bob Roberts",
sender_id: 40,
@ -314,7 +327,7 @@ test("reply_with_mention", ({override, override_rewire}) => {
const opts = {};
reply_with_mention(opts);
assert.equal(compose_state.stream_name(), "devel");
assert.equal(compose_state.stream_name(), "Denmark");
assert.equal(syntax_to_insert, "@**Bob Roberts**");
// Test for extended mention syntax
@ -332,7 +345,7 @@ test("reply_with_mention", ({override, override_rewire}) => {
people.add_active_user(bob_2);
reply_with_mention(opts);
assert.equal(compose_state.stream_name(), "devel");
assert.equal(compose_state.stream_name(), "Denmark");
assert.equal(syntax_to_insert, "@**Bob Roberts|40**");
});
@ -364,7 +377,7 @@ test("quote_and_reply", ({disallow, override, override_rewire}) => {
selected_message = {
type: "stream",
stream: "devel",
stream: "Denmark",
topic: "python",
sender_full_name: "Steve Stephenson",
sender_id: 90,
@ -403,7 +416,7 @@ test("quote_and_reply", ({disallow, override, override_rewire}) => {
selected_message = {
type: "stream",
stream: "devel",
stream: "Denmark",
topic: "test",
sender_full_name: "Steve Stephenson",
sender_id: 90,
@ -417,7 +430,7 @@ test("quote_and_reply", ({disallow, override, override_rewire}) => {
selected_message = {
type: "stream",
stream: "devel",
stream: "Denmark",
topic: "test",
sender_full_name: "Steve Stephenson",
sender_id: 90,

View File

@ -25,12 +25,10 @@ const people = zrequire("people");
const compose_fade = zrequire("compose_fade");
const compose_recipient = zrequire("compose_recipient");
const compose_fade_helper = zrequire("compose_fade_helper");
const compose_state = zrequire("compose_state");
compose_recipient.compose_recipient_widget = {
value() {
return "social";
},
};
compose_recipient.selected_stream_name = "social";
compose_recipient.is_direct_message_selected = false;
const me = {
email: "me@example.com",
@ -56,7 +54,8 @@ people.initialize_current_user(me.user_id);
people.add_active_user(alice);
people.add_active_user(bob);
run_test("set_focused_recipient", () => {
run_test("set_focused_recipient", ({override_rewire}) => {
override_rewire(compose_recipient, "on_compose_select_recipient_update", () => {});
const sub = {
stream_id: 101,
name: "social",
@ -72,6 +71,7 @@ run_test("set_focused_recipient", () => {
assert.equal(compose_fade_helper.would_receive_message(bob.user_id), true);
stream_data.add_sub(sub);
compose_state.set_stream_name("social");
peer_data.set_subscribers(sub.stream_id, [me.user_id, alice.user_id]);
compose_fade.set_focused_recipient("stream");

View File

@ -12,19 +12,10 @@ const compose_pm_pill = mock_esm("../src/compose_pm_pill");
const compose_state = zrequire("compose_state");
const compose_fade = zrequire("compose_fade");
const compose_recipient = zrequire("compose_recipient");
const stream_data = zrequire("stream_data");
const noop = () => {};
let stream_value = "";
compose_recipient.compose_recipient_widget = {
value() {
return stream_value;
},
render(val) {
stream_value = val;
},
};
run_test("private_message_recipient", ({override}) => {
let emails;
override(compose_pm_pill, "set_from_emails", (value) => {
@ -59,6 +50,7 @@ run_test("has_full_recipient", ({override, override_rewire}) => {
compose_state.topic("foo");
assert.equal(compose_state.has_full_recipient(), false);
stream_data.add_sub({name: "bar", stream_id: 99});
compose_state.set_stream_name("bar");
assert.equal(compose_state.has_full_recipient(), true);

View File

@ -11,7 +11,6 @@ const $ = require("./lib/zjquery");
const {page_params} = require("./lib/zpage_params");
const channel = mock_esm("../src/channel");
const compose_banner = zrequire("compose_banner");
const compose_pm_pill = zrequire("compose_pm_pill");
const compose_state = zrequire("compose_state");
@ -22,17 +21,7 @@ const resolved_topic = zrequire("../shared/src/resolved_topic");
const settings_config = zrequire("settings_config");
const settings_data = mock_esm("../src/settings_data");
const stream_data = zrequire("stream_data");
const compose_recipient = zrequire("compose_recipient");
let stream_value = "";
compose_recipient.compose_recipient_widget = {
value() {
return stream_value;
},
render(val) {
stream_value = val;
},
};
const compose_recipient = zrequire("/compose_recipient");
const me = {
email: "me@example.com",
@ -146,9 +135,8 @@ test_ui("validate_stream_message_address_info", ({mock_template}) => {
assert.ok(subscription_error_rendered);
});
test_ui("validate", ({mock_template}) => {
compose_recipient.update_placeholder_text = () => {};
compose_recipient.on_compose_select_recipient_update = () => {};
test_ui("validate", ({override_rewire, mock_template}) => {
override_rewire(compose_recipient, "on_compose_select_recipient_update", () => {});
function initialize_pm_pill() {
$.clear_all_elements();
@ -273,6 +261,11 @@ test_ui("validate", ({mock_template}) => {
assert.ok(!compose_validate.validate());
assert.ok(empty_stream_error_rendered);
const denmark = {
stream_id: 100,
name: "Denmark",
};
stream_data.add_sub(denmark);
compose_state.set_stream_name("Denmark");
page_params.realm_mandatory_topics = true;
compose_state.topic("");
@ -372,6 +365,7 @@ test_ui("validate_stream_message", ({override_rewire, mock_template}) => {
// we are separating it up in different test. Though their relative position
// of execution should not be changed.
mock_banners();
override_rewire(compose_recipient, "on_compose_select_recipient_update", () => {});
$("#compose_banners .wildcard_warning").length = 0;
page_params.user_id = me.user_id;
page_params.realm_mandatory_topics = false;
@ -419,12 +413,15 @@ test_ui("validate_stream_message", ({override_rewire, mock_template}) => {
assert.ok(wildcards_not_allowed_rendered);
});
test_ui("test_validate_stream_message_post_policy_admin_only", ({mock_template}) => {
test_ui(
"test_validate_stream_message_post_policy_admin_only",
({override_rewire, mock_template}) => {
// 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.
mock_banners();
override_rewire(compose_recipient, "on_compose_select_recipient_update", () => {});
page_params.is_admin = false;
const sub = {
stream_id: 102,
@ -433,9 +430,9 @@ test_ui("test_validate_stream_message_post_policy_admin_only", ({mock_template})
stream_post_policy: stream_data.stream_post_policy_values.admins.code,
};
stream_data.add_sub(sub);
compose_state.topic("topic102");
compose_state.set_stream_name("stream102");
stream_data.add_sub(sub);
let banner_rendered = false;
mock_template("compose_banner/compose_banner.hbs", false, (data) => {
@ -462,10 +459,15 @@ test_ui("test_validate_stream_message_post_policy_admin_only", ({mock_template})
banner_rendered = false;
assert.ok(!compose_validate.validate());
assert.ok(banner_rendered);
});
},
);
test_ui("test_validate_stream_message_post_policy_moderators_only", ({mock_template}) => {
test_ui(
"test_validate_stream_message_post_policy_moderators_only",
({override_rewire, mock_template}) => {
mock_banners();
override_rewire(compose_recipient, "on_compose_select_recipient_update", () => {});
page_params.is_admin = false;
page_params.is_moderator = false;
page_params.is_guest = false;
@ -477,9 +479,9 @@ test_ui("test_validate_stream_message_post_policy_moderators_only", ({mock_templ
stream_post_policy: stream_data.stream_post_policy_values.moderators.code,
};
stream_data.add_sub(sub);
compose_state.topic("topic104");
compose_state.set_stream_name("stream104");
stream_data.add_sub(sub);
let banner_rendered = false;
mock_template("compose_banner/compose_banner.hbs", false, (data) => {
assert.equal(data.classname, compose_banner.CLASSNAMES.no_post_permissions);
@ -499,10 +501,14 @@ test_ui("test_validate_stream_message_post_policy_moderators_only", ({mock_templ
page_params.is_guest = true;
assert.ok(!compose_validate.validate());
assert.ok(banner_rendered);
});
},
);
test_ui("test_validate_stream_message_post_policy_full_members_only", ({mock_template}) => {
test_ui(
"test_validate_stream_message_post_policy_full_members_only",
({override_rewire, mock_template}) => {
mock_banners();
override_rewire(compose_recipient, "on_compose_select_recipient_update", () => {});
page_params.is_admin = false;
page_params.is_guest = true;
const sub = {
@ -512,9 +518,9 @@ test_ui("test_validate_stream_message_post_policy_full_members_only", ({mock_tem
stream_post_policy: stream_data.stream_post_policy_values.non_new_members.code,
};
stream_data.add_sub(sub);
compose_state.topic("topic103");
compose_state.set_stream_name("stream103");
stream_data.add_sub(sub);
let banner_rendered = false;
mock_template("compose_banner/compose_banner.hbs", false, (data) => {
assert.equal(data.classname, compose_banner.CLASSNAMES.no_post_permissions);
@ -528,7 +534,8 @@ test_ui("test_validate_stream_message_post_policy_full_members_only", ({mock_tem
});
assert.ok(!compose_validate.validate());
assert.ok(banner_rendered);
});
},
);
test_ui("test_check_overflow_text", ({mock_template}) => {
mock_banners();
@ -665,9 +672,10 @@ test_ui("warn_if_private_stream_is_linked", ({mock_template}) => {
assert.ok(banner_rendered);
});
test_ui("warn_if_mentioning_unsubscribed_user", ({override, mock_template}) => {
test_ui("warn_if_mentioning_unsubscribed_user", ({override, override_rewire, mock_template}) => {
override_rewire(compose_recipient, "on_compose_select_recipient_update", () => {});
const $textarea = $("<textarea>").attr("id", "compose-textarea");
stream_value = "";
compose_state.set_stream_name("");
override(settings_data, "user_can_subscribe_other_users", () => true);
let mentioned_details = {
@ -707,11 +715,12 @@ test_ui("warn_if_mentioning_unsubscribed_user", ({override, mock_template}) => {
compose_validate.warn_if_mentioning_unsubscribed_user(mentioned_details, $textarea);
assert.ok(!new_banner_rendered);
compose_state.set_stream_name("random");
const sub = {
stream_id: 111,
name: "random",
};
stream_data.add_sub(sub);
compose_state.set_stream_name("random");
// Test with invalid stream in compose box. It should return noop.
new_banner_rendered = false;
@ -726,7 +735,6 @@ test_ui("warn_if_mentioning_unsubscribed_user", ({override, mock_template}) => {
};
people.add_active_user(mentioned_details);
stream_data.add_sub(sub);
new_banner_rendered = false;
const $banner_container = $("#compose_banners");
$banner_container.set_find_results(".recipient_not_subscribed", []);
@ -752,8 +760,9 @@ test_ui("warn_if_mentioning_unsubscribed_user", ({override, mock_template}) => {
assert.ok(!new_banner_rendered);
});
test_ui("test warn_if_topic_resolved", ({override, mock_template}) => {
test_ui("test warn_if_topic_resolved", ({override, override_rewire, mock_template}) => {
mock_banners();
override_rewire(compose_recipient, "on_compose_select_recipient_update", () => {});
$("#compose_banners .topic_resolved").length = 0;
override(settings_data, "user_can_move_messages_to_another_topic", () => true);
@ -778,6 +787,7 @@ test_ui("test warn_if_topic_resolved", ({override, mock_template}) => {
stream_data.add_sub(sub);
compose_state.set_message_type("stream");
blueslip.expect("error", "Unable to select stream: Do not exist");
compose_state.set_stream_name("Do not exist");
compose_state.topic(resolved_topic.resolve_name("hello"));
compose_state.message_content("content");

View File

@ -56,16 +56,6 @@ const ct = composebox_typeahead;
// broadcast-mentions/persons/groups.
ct.__Rewire__("max_num_items", 15);
let stream_value = "";
compose_recipient.compose_recipient_widget = {
value() {
return stream_value;
},
render(val) {
stream_value = val;
},
};
run_test("verify wildcard mentions typeahead for stream message", () => {
const mention_all = ct.broadcast_mentions()[0];
const mention_everyone = ct.broadcast_mentions()[1];

View File

@ -24,6 +24,7 @@ const stream_settings_ui = mock_esm("../src/stream_settings_ui");
message_lists.current = {};
const compose_recipient = zrequire("compose_recipient");
const compose_state = zrequire("compose_state");
const peer_data = zrequire("peer_data");
const people = zrequire("people");
const server_events_dispatch = zrequire("server_events_dispatch");
@ -198,9 +199,7 @@ test("stream create", ({override}) => {
assert.deepEqual(sub_store.get(102).name, "test");
});
test("stream delete (normal)", ({override, override_rewire}) => {
override_rewire(compose_recipient, "update_stream_dropdown_options", noop);
test("stream delete (normal)", ({override}) => {
const event = event_fixtures.stream__delete;
for (const stream of event.streams) {
@ -241,9 +240,7 @@ test("stream delete (normal)", ({override, override_rewire}) => {
assert.equal(removed_sidebar_rows, 1);
});
test("stream delete (special streams)", ({override, override_rewire}) => {
override_rewire(compose_recipient, "update_stream_dropdown_options", noop);
test("stream delete (special streams)", ({override}) => {
const event = event_fixtures.stream__delete;
for (const stream of event.streams) {
@ -267,3 +264,48 @@ test("stream delete (special streams)", ({override, override_rewire}) => {
assert.equal(page_params.realm_notifications_stream_id, -1);
assert.equal(page_params.realm_signup_notifications_stream_id, -1);
});
test("stream delete (stream is selected in compose)", ({override, override_rewire}) => {
override_rewire(compose_recipient, "on_compose_select_recipient_update", () => {});
const event = event_fixtures.stream__delete;
for (const stream of event.streams) {
stream_data.add_sub(stream);
}
stream_data.subscribe_myself(event.streams[0]);
compose_state.set_stream_name(event.streams[0].name);
override(settings_streams, "update_default_streams_table", noop);
narrow_state.is_for_stream_id = () => true;
let bookend_updates = 0;
override(message_lists.current, "update_trailing_bookend", () => {
bookend_updates += 1;
});
const removed_stream_ids = [];
override(stream_settings_ui, "remove_stream", (stream_id) => {
removed_stream_ids.push(stream_id);
});
let removed_sidebar_rows = 0;
override(stream_list, "remove_sidebar_row", () => {
removed_sidebar_rows += 1;
});
override(stream_list, "update_subscribe_to_more_streams_link", noop);
dispatch(event);
assert.equal(compose_state.stream_name(), "");
assert.deepEqual(removed_stream_ids, [event.streams[0].stream_id, event.streams[1].stream_id]);
// We should possibly be able to make a single call to
// update_trailing_bookend, but we currently do it for each stream.
assert.equal(bookend_updates, 2);
assert.equal(removed_sidebar_rows, 1);
});

View File

@ -19,16 +19,6 @@ const compose_recipient = zrequire("compose_recipient");
const sub_store = zrequire("sub_store");
const stream_data = zrequire("stream_data");
let stream_value = "";
compose_recipient.compose_recipient_widget = {
value() {
return stream_value;
},
render(val) {
stream_value = val;
},
};
const aaron = {
email: "aaron@zulip.com",
user_id: 6,
@ -167,6 +157,7 @@ test("snapshot_message", ({override_rewire}) => {
override_rewire(user_pill, "get_user_ids", () => [aaron.user_id]);
override_rewire(compose_pm_pill, "set_from_emails", noop);
override_rewire(compose_recipient, "on_compose_select_recipient_update", () => {});
compose_recipient.set_compose_recipient_id = () => {};
mock_banners();
stream_data.get_sub = (stream_name) => {
@ -184,11 +175,22 @@ test("snapshot_message", ({override_rewire}) => {
function set_compose_state() {
compose_state.set_message_type(curr_draft.type);
compose_state.message_content(curr_draft.content);
if (curr_draft.type === "private") {
compose_recipient.set_compose_recipient_id(compose_recipient.DIRECT_MESSAGE_ID);
} else {
compose_state.set_stream_name(curr_draft.stream);
}
compose_state.topic(curr_draft.topic);
compose_state.private_message_recipient(curr_draft.private_message_recipient);
}
const stream = {
stream_id: draft_1.stream_id,
name: draft_1.stream,
};
stream_data.add_sub(stream);
compose_state.set_stream_name("stream");
curr_draft = draft_1;
set_compose_state();
assert.deepEqual(drafts.snapshot_message(), draft_1);

View File

@ -27,16 +27,6 @@ const recent_topics_util = mock_esm("../src/recent_topics_util", {
is_visible() {},
});
let stream_value = "";
compose_recipient.compose_recipient_widget = {
value() {
return stream_value;
},
render(val) {
stream_value = val;
},
};
function empty_narrow_html(title, html, search_data) {
const opts = {
title,
@ -668,8 +658,8 @@ run_test("show_invalid_narrow_message", ({mock_template}) => {
);
});
run_test("narrow_to_compose_target errors", ({disallow_rewire}) => {
compose_recipient.on_compose_select_recipient_update = () => {};
run_test("narrow_to_compose_target errors", ({override_rewire, disallow_rewire}) => {
override_rewire(compose_recipient, "on_compose_select_recipient_update", () => {});
disallow_rewire(narrow, "activate");
// No-op when not composing.
@ -683,6 +673,7 @@ run_test("narrow_to_compose_target errors", ({disallow_rewire}) => {
});
run_test("narrow_to_compose_target streams", ({override_rewire}) => {
override_rewire(compose_recipient, "on_compose_select_recipient_update", () => {});
const args = {called: false};
override_rewire(narrow, "activate", (operators, opts) => {
args.operators = operators;

View File

@ -88,8 +88,8 @@ function test(label, f) {
}
test("update_property", ({override, override_rewire}) => {
override_rewire(compose_recipient, "update_stream_dropdown_options", noop);
override_rewire(compose_recipient, "possibly_update_dropdown_selection", noop);
override_rewire(compose_recipient, "possibly_update_stream_name_in_compose", noop);
override_rewire(compose_recipient, "on_compose_select_recipient_update", noop);
const sub = {...frontend};
stream_data.add_sub(sub);
@ -280,11 +280,10 @@ test("marked_subscribed (error)", () => {
blueslip.reset();
});
test("marked_subscribed (normal)", ({override, override_rewire}) => {
test("marked_subscribed (normal)", ({override}) => {
const sub = {...frontend};
stream_data.add_sub(sub);
override(stream_color, "update_stream_color", noop);
override_rewire(compose_recipient, "update_stream_dropdown_options", noop);
narrow_to_frontend();
@ -319,11 +318,10 @@ test("marked_subscribed (normal)", ({override, override_rewire}) => {
narrow_state.reset_current_filter();
});
test("marked_subscribed (color)", ({override, override_rewire}) => {
test("marked_subscribed (color)", ({override}) => {
override(stream_list, "add_sidebar_row", noop);
override(stream_list, "update_subscribe_to_more_streams_link", noop);
override(unread_ui, "update_unread_counts", noop);
override_rewire(compose_recipient, "update_stream_dropdown_options", noop);
const sub = {
subscribed: false,
@ -352,11 +350,10 @@ test("marked_subscribed (color)", ({override, override_rewire}) => {
}
});
test("marked_subscribed (emails)", ({override, override_rewire}) => {
test("marked_subscribed (emails)", ({override}) => {
const sub = {...frontend};
stream_data.add_sub(sub);
override(stream_color, "update_stream_color", noop);
override_rewire(compose_recipient, "update_stream_dropdown_options", noop);
// Test assigning subscriber emails
// narrow state is undefined
@ -380,7 +377,7 @@ test("marked_subscribed (emails)", ({override, override_rewire}) => {
assert.deepEqual(sub, args.sub);
});
test("mark_unsubscribed (update_settings_for_unsubscribed)", ({override, override_rewire}) => {
test("mark_unsubscribed (update_settings_for_unsubscribed)", ({override}) => {
// Test unsubscribe
const sub = {...dev_help};
stream_data.add_sub(sub);
@ -395,13 +392,12 @@ test("mark_unsubscribed (update_settings_for_unsubscribed)", ({override, overrid
$("#streams_overlay_container .stream-row:not(.notdisplayed)").length = 0;
override_rewire(compose_recipient, "update_stream_dropdown_options", noop);
stream_events.mark_unsubscribed(sub);
const args = stub.get_args("sub");
assert.deepEqual(args.sub, sub);
});
test("mark_unsubscribed (render_title_area)", ({override, override_rewire}) => {
test("mark_unsubscribed (render_title_area)", ({override}) => {
const sub = {...frontend, subscribed: true};
stream_data.add_sub(sub);
@ -422,7 +418,6 @@ test("mark_unsubscribed (render_title_area)", ({override, override_rewire}) => {
$("#streams_overlay_container .stream-row:not(.notdisplayed)").length = 0;
override_rewire(compose_recipient, "update_stream_dropdown_options", noop);
stream_events.mark_unsubscribed(sub);
assert.equal(message_view_header_stub.num_calls, 1);