diff --git a/web/src/compose_setup.js b/web/src/compose_setup.js index 7f410e9321..409d90990c 100644 --- a/web/src/compose_setup.js +++ b/web/src/compose_setup.js @@ -186,16 +186,16 @@ export function initialize() { // Renarrow to the unresolved topic if currently viewing the resolved topic. const current_filter = narrow_state.filter(); - const channel_name = sub_store.maybe_get_stream_name(stream_id); + const stream_id_string = stream_id.toString(); if ( current_filter && (current_filter.is_conversation_view() || current_filter.is_conversation_view_with_near()) && - current_filter.has_topic(channel_name, topic_name) + current_filter.has_topic(stream_id_string, topic_name) ) { message_view.show( [ - {operator: "channel", operand: channel_name}, + {operator: "channel", operand: stream_id_string}, {operator: "topic", operand: new_topic}, ], {}, diff --git a/web/src/copy_and_paste.ts b/web/src/copy_and_paste.ts index 48d8188367..8d26cd6a40 100644 --- a/web/src/copy_and_paste.ts +++ b/web/src/copy_and_paste.ts @@ -9,6 +9,7 @@ import * as compose_ui from "./compose_ui"; import * as hash_util from "./hash_util"; import * as message_lists from "./message_lists"; import * as rows from "./rows"; +import * as stream_data from "./stream_data"; import * as topic_link_util from "./topic_link_util"; import * as util from "./util"; @@ -662,7 +663,10 @@ export function try_stream_topic_syntax_text(text: string): string | null { return null; } - if (topic_link_util.will_produce_broken_stream_topic_link(stream_topic.stream_name)) { + const stream = stream_data.get_sub_by_id(stream_topic.stream_id); + assert(stream !== undefined); + const stream_name = stream.name; + if (topic_link_util.will_produce_broken_stream_topic_link(stream_name)) { return null; } @@ -673,7 +677,7 @@ export function try_stream_topic_syntax_text(text: string): string | null { return null; } - let syntax_text = "#**" + stream_topic.stream_name; + let syntax_text = "#**" + stream_name; if (stream_topic.topic_name) { syntax_text += ">" + stream_topic.topic_name; } diff --git a/web/src/drafts_overlay_ui.js b/web/src/drafts_overlay_ui.js index d201c3f1b1..e1b1493f6a 100644 --- a/web/src/drafts_overlay_ui.js +++ b/web/src/drafts_overlay_ui.js @@ -12,7 +12,6 @@ import * as messages_overlay_ui from "./messages_overlay_ui"; import * as overlays from "./overlays"; import * as people from "./people"; import * as rendered_markdown from "./rendered_markdown"; -import * as stream_data from "./stream_data"; import * as user_card_popover from "./user_card_popover"; import * as user_group_popover from "./user_group_popover"; @@ -30,7 +29,7 @@ function restore_draft(draft_id) { [ { operator: "channel", - operand: stream_data.get_stream_name_from_id(compose_args.stream_id), + operand: compose_args.stream_id.toString(), }, {operator: "topic", operand: compose_args.topic}, ], diff --git a/web/src/filter.ts b/web/src/filter.ts index 6c062e19d7..a069611eea 100644 --- a/web/src/filter.ts +++ b/web/src/filter.ts @@ -18,7 +18,6 @@ import type {UserPillItem} from "./search_suggestion"; import {current_user, realm} from "./state_data"; import type {NarrowTerm} from "./state_data"; import * as stream_data from "./stream_data"; -import type {StreamSubscription} from "./sub_store"; import * as unread from "./unread"; import * as user_topics from "./user_topics"; import * as util from "./util"; @@ -75,12 +74,15 @@ type ValidOrInvalidUser = const CHANNEL_SYNONYM = "stream"; const CHANNELS_SYNONYM = "streams"; -function zephyr_stream_name_match(message: Message & {type: "stream"}, operand: string): boolean { +function zephyr_stream_name_match( + message: Message & {type: "stream"}, + stream_name: string, +): boolean { // Zephyr users expect narrowing to "social" to also show messages to /^(un)*social(.d)*$/ // (unsocial, ununsocial, social.d, etc) // TODO: hoist the regex compiling out of the closure - const m = /^(?:un)*(.+?)(?:\.d)*$/i.exec(operand); - let base_stream_name = operand; + const m = /^(?:un)*(.+?)(?:\.d)*$/i.exec(stream_name); + let base_stream_name = stream_name; if (m?.[1] !== undefined) { base_stream_name = m[1]; } @@ -88,8 +90,8 @@ function zephyr_stream_name_match(message: Message & {type: "stream"}, operand: /^(un)*/.source + _.escapeRegExp(base_stream_name) + /(\.d)*$/.source, "i", ); - const stream_name = stream_data.get_stream_name_from_id(message.stream_id); - return related_regexp.test(stream_name); + const message_stream_name = stream_data.get_stream_name_from_id(message.stream_id); + return related_regexp.test(message_stream_name); } function zephyr_topic_name_match(message: Message & {type: "stream"}, operand: string): boolean { @@ -209,15 +211,12 @@ function message_matches_search_term(message: Message, operator: string, operand return false; } - operand = operand.toLowerCase(); if (realm.realm_is_zephyr_mirror_realm) { - return zephyr_stream_name_match(message, operand); + const stream = stream_data.get_sub_by_id_string(operand); + return zephyr_stream_name_match(message, stream?.name ?? ""); } - // Try to match by stream_id if have a valid sub for - // the operand. If we can't find the id, we return false. - const stream_id = stream_data.get_stream_id(operand); - return stream_id !== undefined && message.stream_id === stream_id; + return message.stream_id.toString() === operand; } case "topic": @@ -291,7 +290,6 @@ const USER_OPERATORS = new Set([ export class Filter { _terms: NarrowTerm[]; - _sub?: StreamSubscription | undefined; _sorted_term_types?: string[] = undefined; _predicate?: (message: Message) => boolean; _can_mark_messages_read?: boolean; @@ -355,7 +353,6 @@ export class Filter { break; case "channel": - operand = stream_data.get_name(operand); break; case "topic": break; @@ -495,6 +492,18 @@ export class Filter { } operand = Filter.decodeOperand(parts.join(":"), operator); + // Check for user-entered channel name. If the name is valid, + // convert it to id. + if ( + (operator === "stream" || operator === "channel") && + Number.isNaN(Number.parseInt(operand, 10)) + ) { + const sub = stream_data.get_sub(operand); + if (sub) { + operand = sub.stream_id.toString(); + } + } + // We use Filter.operator_to_prefix() to check if the // operator is known. If it is not known, then we treat // it as a search for the given string (which may contain @@ -545,7 +554,7 @@ export class Filter { return Number.isInteger(Number(term.operand)); case "channel": case "stream": - return stream_data.get_sub(term.operand) !== undefined; + return stream_data.get_sub_by_id_string(term.operand) !== undefined; case "channels": case "streams": return term.operand === "public"; @@ -715,7 +724,7 @@ export class Filter { Filter.canonicalize_operator(term.operator) === expected && !term.negated; if (is(terms[0], "channel") && is(terms[1], "topic")) { - const channel = terms[0].operand; + const channel = stream_data.get_valid_sub_by_id_string(terms[0].operand).name; const topic = terms[1].operand; parts.push({ type: "channel_topic", @@ -783,6 +792,18 @@ export class Filter { }; } if (prefix_for_operator !== "") { + if (canonicalized_operator === "channel") { + const stream = stream_data.get_sub_by_id_string(operand); + if (stream) { + return { + type: "prefix_for_operator", + prefix_for_operator, + operand: stream.name, + }; + } + // Assume the operand is a partially formed name and return + // the operator as the stream name in the next block. + } return { type: "prefix_for_operator", prefix_for_operator, @@ -863,12 +884,11 @@ export class Filter { const adjusted_term = {...term}; if ( Filter.canonicalize_operator(term.operator) === "channel" && - !util.lower_same(term.operand, message.display_recipient) + term.operand !== message.stream_id.toString() ) { - adjusted_term.operand = message.display_recipient; + adjusted_term.operand = message.stream_id.toString(); terms_changed = true; } - if ( Filter.canonicalize_operator(term.operator) === "topic" && !util.lower_same(term.operand, message.topic) @@ -890,11 +910,6 @@ export class Filter { setup_filter(terms: NarrowTerm[]): void { this._terms = this.fix_terms(terms); this.cached_sorted_terms_for_comparison = undefined; - if (this.has_operator("channel")) { - this._sub = stream_data.get_sub_by_name(this.operands("channel")[0]!); - } else { - this._sub = undefined; - } } equals(filter: Filter, excluded_operators?: string[]): boolean { @@ -948,12 +963,15 @@ export class Filter { public_terms(): NarrowTerm[] { const safe_to_return = this._terms.filter( // Filter out the embedded narrow (if any). - (term) => - !( - page_params.narrow_stream !== undefined && - term.operator === "channel" && - term.operand.toLowerCase() === page_params.narrow_stream.toLowerCase() - ), + (term) => { + // TODO(stream_id): Ideally we have `page_params.narrow_stream_id` + if (page_params.narrow_stream === undefined || term.operator !== "channel") { + return true; + } + const narrow_stream = stream_data.get_sub_by_name(page_params.narrow_stream); + assert(narrow_stream !== undefined); + return Number.parseInt(term.operand, 10) === narrow_stream.stream_id; + }, ); return safe_to_return; } @@ -1170,15 +1188,16 @@ export class Filter { return "/#narrow/has/reaction/sender/me"; } if (_.isEqual(term_types, ["channel", "topic", "search"])) { + const sub = stream_data.get_sub_by_id_string(this.operands("channel")[0]!); // if channel does not exist, redirect to home view - if (!this._sub) { + if (!sub) { return "#"; } return ( "/#narrow/" + CHANNEL_SYNONYM + "/" + - stream_data.name_to_slug(this.operands("channel")[0]!) + + stream_data.id_to_slug(sub.stream_id) + "/topic/" + this.operands("topic")[0] ); @@ -1191,17 +1210,16 @@ export class Filter { if (term_types[1] === "search") { switch (term_types[0]) { - case "channel": + case "channel": { + const sub = stream_data.get_sub_by_id_string(this.operands("channel")[0]!); // if channel does not exist, redirect to home view - if (!this._sub) { + if (!sub) { return "#"; } return ( - "/#narrow/" + - CHANNEL_SYNONYM + - "/" + - stream_data.name_to_slug(this.operands("channel")[0]!) + "/#narrow/" + CHANNEL_SYNONYM + "/" + stream_data.id_to_slug(sub.stream_id) ); + } case "is-dm": return "/#narrow/is/dm"; case "is-starred": @@ -1250,21 +1268,23 @@ export class Filter { case "in-all": icon = "home"; break; - case "channel": - if (!this._sub) { + case "channel": { + const sub = stream_data.get_sub_by_id_string(this.operands("channel")[0]!); + if (!sub) { icon = "question-circle-o"; break; } - if (this._sub.invite_only) { + if (sub.invite_only) { zulip_icon = "lock"; break; } - if (this._sub.is_web_public) { + if (sub.is_web_public) { zulip_icon = "globe"; break; } zulip_icon = "hashtag"; break; + } case "is-dm": zulip_icon = "user"; break; @@ -1302,11 +1322,11 @@ export class Filter { (term_types.length === 2 && _.isEqual(term_types, ["channel", "topic"])) || (term_types.length === 1 && _.isEqual(term_types, ["channel"])) ) { - if (!this._sub) { - const search_text = this.operands("channel")[0]; - return $t({defaultMessage: "Unknown channel #{search_text}"}, {search_text}); + const sub = stream_data.get_sub_by_id_string(this.operands("channel")[0]!); + if (!sub) { + return $t({defaultMessage: "Unknown channel"}); } - return this._sub.name; + return sub.name; } if ( (term_types.length === 2 && _.isEqual(term_types, ["dm", "near"])) || @@ -1512,8 +1532,10 @@ export class Filter { return new Filter(terms); } - has_topic(stream_name: string, topic: string): boolean { - return this.has_operand("channel", stream_name) && this.has_operand("topic", topic); + has_topic(stream_id: number, topic: string): boolean { + return ( + this.has_operand("channel", stream_id.toString()) && this.has_operand("topic", topic) + ); } sorted_term_types(): string[] { @@ -1668,12 +1690,12 @@ export class Filter { this.requires_adjustment_for_moved_with_target = false; } - can_newly_match_moved_messages(new_channel: string, new_topic: string): boolean { + can_newly_match_moved_messages(new_channel_id: string, new_topic: string): boolean { // Checks if any of the operators on this Filter object have // the property that it's possible for their true value to // change as a result of messages being moved into the // channel/topic pair provided in the parameters. - if (this.has_operand_case_insensitive("channel", new_channel)) { + if (this.has_operand_case_insensitive("channel", new_channel_id)) { return true; } diff --git a/web/src/hash_util.ts b/web/src/hash_util.ts index 38850b14aa..39fc631a3d 100644 --- a/web/src/hash_util.ts +++ b/web/src/hash_util.ts @@ -36,18 +36,19 @@ export function encode_operand(operator: string, operand: string): string { } if (operator === "stream") { - return encode_stream_name(operand); + const stream_id = Number.parseInt(operand, 10); + return encode_stream_id(stream_id); } return internal_url.encodeHashComponent(operand); } -export function encode_stream_name(operand: string): string { - // stream_data prefixes the stream id, but it does not do the +export function encode_stream_id(stream_id: number): string { + // stream_data postfixes the stream name, but it does not do the // URI encoding piece - operand = stream_data.name_to_slug(operand); + const slug = stream_data.id_to_slug(stream_id); - return internal_url.encodeHashComponent(operand); + return internal_url.encodeHashComponent(slug); } export function decode_operand(operator: string, operand: string): string { @@ -67,13 +68,7 @@ export function decode_operand(operator: string, operand: string): string { operand = internal_url.decodeHashComponent(operand); if (util.canonicalize_stream_synonyms(operator) === "stream") { - const stream_id = stream_data.slug_to_stream_id(operand); - if (stream_id) { - const stream = stream_data.get_sub_by_id(stream_id); - if (stream) { - return stream.name; - } - } + return stream_data.slug_to_stream_id(operand)?.toString() ?? ""; } return operand; @@ -294,7 +289,7 @@ export function validate_group_settings_hash(hash: string): string { export function decode_stream_topic_from_url( url_str: string, -): {stream_name: string; topic_name?: string} | null { +): {stream_id: number; topic_name?: string} | null { try { const url = new URL(url_str); if (url.origin !== window.location.origin || !url.hash.startsWith("#narrow")) { @@ -314,13 +309,18 @@ export function decode_stream_topic_from_url( if (terms[0]?.operator !== "stream" && terms[0]?.operator !== "channel") { return null; } + const stream_id = Number.parseInt(terms[0].operand, 10); + // This can happen if we don't recognize the stream operand as a valid id. + if (Number.isNaN(stream_id)) { + return null; + } if (terms.length === 1) { - return {stream_name: terms[0].operand}; + return {stream_id}; } if (terms[1]?.operator !== "topic") { return null; } - return {stream_name: terms[0].operand, topic_name: terms[1].operand}; + return {stream_id, topic_name: terms[1].operand}; } catch { return null; } diff --git a/web/src/hotkey.js b/web/src/hotkey.js index 41e9caaee7..0c6fb3f2ef 100644 --- a/web/src/hotkey.js +++ b/web/src/hotkey.js @@ -54,7 +54,6 @@ import * as sidebar_ui from "./sidebar_ui"; import * as spectators from "./spectators"; import * as starred_messages_ui from "./starred_messages_ui"; import {realm} from "./state_data"; -import * as stream_data from "./stream_data"; import * as stream_list from "./stream_list"; import * as stream_popover from "./stream_popover"; import * as stream_settings_ui from "./stream_settings_ui"; @@ -1234,7 +1233,7 @@ export function process_hotkey(e, hotkey) { [ { operator: "channel", - operand: stream_data.get_stream_name_from_id(msg.stream_id), + operand: msg.stream_id.toString(), }, {operator: "topic", operand: msg.topic}, {operator: "near", operand: msg.id}, diff --git a/web/src/message_events.js b/web/src/message_events.js index 0282becfdf..fff6593d5e 100644 --- a/web/src/message_events.js +++ b/web/src/message_events.js @@ -472,7 +472,6 @@ export function update_messages(events) { }); } - const old_stream_name = stream_archived ? undefined : old_stream.name; if ( going_forward_change && // This logic is a bit awkward. What we're trying to @@ -490,7 +489,8 @@ export function update_messages(events) { // messages within a narrow. selection_changed_topic && current_filter && - current_filter.has_topic(old_stream_name, orig_topic) + old_stream_id && + current_filter.has_topic(old_stream_id, orig_topic) ) { let new_filter = current_filter; if (new_filter && stream_changed) { @@ -501,10 +501,9 @@ export function update_messages(events) { // stream_data lookup here to fail. // // The fix is likely somewhat involved, so punting for now. - const new_stream_name = sub_store.get(new_stream_id).name; new_filter = new_filter.filter_with_new_params({ operator: "channel", - operand: new_stream_name, + operand: new_stream_id.toString(), }); changed_narrow = true; } @@ -533,10 +532,10 @@ export function update_messages(events) { // If a message was moved to the current narrow and we don't have // the message cached, we need to refresh the narrow to display the message. if (!changed_narrow && local_cache_missing_messages && current_filter) { - let moved_message_stream = old_stream_name; + let moved_message_stream_id = old_stream_id; let moved_message_topic = orig_topic; if (stream_changed) { - moved_message_stream = sub_store.get(new_stream_id).name; + moved_message_stream_id = sub_store.get(new_stream_id).stream_id.toString(); } if (topic_edited) { @@ -545,7 +544,7 @@ export function update_messages(events) { if ( current_filter.can_newly_match_moved_messages( - moved_message_stream, + moved_message_stream_id, moved_message_topic, ) ) { diff --git a/web/src/message_fetch.ts b/web/src/message_fetch.ts index e941eb1e3c..02d1086193 100644 --- a/web/src/message_fetch.ts +++ b/web/src/message_fetch.ts @@ -340,7 +340,25 @@ export function load_messages(opts: MessageFetchOptions, attempt = 1): void { if (page_params.narrow !== undefined) { terms = [...terms, ...page_params.narrow]; } - data.narrow = JSON.stringify(terms); + // TODO(stream_id): The server would ideally work with stream ids instead + // of stream names. + const server_terms = []; + for (const term of terms) { + if (term.operator === "channel") { + const sub = stream_data.get_sub_by_id_string(term.operand); + server_terms.push({ + ...term, + // If the sub is undefined, we'll get an error which is handled + // later by showing a "this channel does not exist or is private" + // notice. + operand: sub?.name, + }); + } else { + server_terms.push({...term}); + } + } + + data.narrow = JSON.stringify(server_terms); } let update_loading_indicator = diff --git a/web/src/message_view.js b/web/src/message_view.js index d635af3590..41a1d89908 100644 --- a/web/src/message_view.js +++ b/web/src/message_view.js @@ -450,10 +450,11 @@ export function show(raw_terms, opts) { // location, then we should retarget this narrow operation // to where the message is located now. const narrow_topic = filter.operands("topic")[0]; - const narrow_stream_name = filter.operands("channel")[0]; - const narrow_stream_data = stream_data.get_sub(narrow_stream_name); + const narrow_stream_data = stream_data.get_sub_by_id_string( + filter.operands("channel")[0], + ); if (!narrow_stream_data) { - // The stream name is invalid or incorrect in the URL. + // The stream id is invalid or incorrect in the URL. // We reconstruct the narrow with the data from the // target message ID that we have. const adjusted_terms = Filter.adjusted_terms_if_moved( @@ -1085,10 +1086,9 @@ export function render_message_list_with_selected_message(opts) { narrow_history.save_narrow_state_and_flush(); } -export function activate_stream_for_cycle_hotkey(stream_id) { - const stream_name = stream_data.get_stream_name_from_id(stream_id); +function activate_stream_for_cycle_hotkey(stream_id) { // This is the common code for A/D hotkeys. - const filter_expr = [{operator: "channel", operand: stream_name}]; + const filter_expr = [{operator: "channel", operand: stream_id.toString()}]; show(filter_expr, {}); } @@ -1158,9 +1158,8 @@ export function narrow_to_next_topic(opts = {}) { return; } - const stream_name = stream_data.get_stream_name_from_id(next_narrow.stream_id); const filter_expr = [ - {operator: "channel", operand: stream_name}, + {operator: "channel", operand: next_narrow.stream_id.toString()}, {operator: "topic", operand: next_narrow.topic}, ]; @@ -1218,9 +1217,8 @@ export function narrow_by_topic(target_id, opts) { unread_ops.notify_server_message_read(original); } - const stream_name = stream_data.get_stream_name_from_id(original.stream_id); const search_terms = [ - {operator: "channel", operand: stream_name}, + {operator: "channel", operand: original.stream_id.toString()}, {operator: "topic", operand: original.topic}, ]; opts = {then_select_id: target_id, ...opts}; @@ -1264,7 +1262,7 @@ export function narrow_by_recipient(target_id, opts) { [ { operator: "stream", - operand: stream_data.get_stream_name_from_id(message.stream_id), + operand: message.stream_id.toString(), }, ], opts, @@ -1289,10 +1287,9 @@ export function to_compose_target() { if (!stream_id) { return; } - const stream_name = stream_data.get_sub_by_id(stream_id).name; // If we are composing to a new topic, we narrow to the stream but // grey-out the message view instead of narrowing to an empty view. - const terms = [{operator: "channel", operand: stream_name}]; + const terms = [{operator: "channel", operand: stream_id.toString()}]; const topic = compose_state.topic(); if (topic !== "") { terms.push({operator: "topic", operand: topic}); diff --git a/web/src/message_view_header.ts b/web/src/message_view_header.ts index b2fc9fe056..fce682086a 100644 --- a/web/src/message_view_header.ts +++ b/web/src/message_view_header.ts @@ -14,6 +14,7 @@ import * as recent_view_util from "./recent_view_util"; import * as rendered_markdown from "./rendered_markdown"; import * as search from "./search"; import {current_user} from "./state_data"; +import * as stream_data from "./stream_data"; import type {SettingsSubscription} from "./stream_settings_data"; import type {StreamSubscription} from "./sub_store"; @@ -99,22 +100,21 @@ function get_message_view_header_context(filter: Filter | undefined): MessageVie is_spectator: page_params.is_spectator, }); - if (filter.has_operator("channel") && !filter._sub) { - return { - ...context, - sub_count: "0", - formatted_sub_count: "0", - rendered_narrow_description: $t({ - defaultMessage: "This channel does not exist or is private.", - }), - }; - } - - if (filter._sub) { + if (filter.has_operator("channel")) { + const current_stream = stream_data.get_sub_by_id_string(filter.operands("channel")[0]!); + if (!current_stream) { + return { + ...context, + sub_count: "0", + formatted_sub_count: "0", + rendered_narrow_description: $t({ + defaultMessage: "This channel does not exist or is private.", + }), + }; + } // We can now be certain that the narrow // involves a stream which exists and // the current user can access. - const current_stream = filter._sub; const sub_count = peer_data.get_subscriber_count(current_stream.stream_id); return { ...context, @@ -131,11 +131,15 @@ function get_message_view_header_context(filter: Filter | undefined): MessageVie export function colorize_message_view_header(): void { const filter = narrow_state.filter(); - if (filter === undefined || !filter._sub) { + let current_stream; + if (filter?.has_operator("channel")) { + current_stream = stream_data.get_valid_sub_by_id_string(filter.operands("channel")[0]!); + } + if (!current_stream) { return; } // selecting i instead of .fa because web public streams have custom icon. - $("#message_view_header a.stream i").css("color", filter._sub.color); + $("#message_view_header a.stream i").css("color", current_stream.color); } function append_and_display_title_area(context: MessageViewHeaderContext): void { @@ -175,10 +179,10 @@ export function render_title_area(): void { // This function checks if "modified_sub" which is the stream whose values // have been updated is the same as the stream which is currently -// narrowed (filter._sub) and rerenders if necessary +// narrowed and rerenders if necessary export function maybe_rerender_title_area_for_stream(modified_sub: SettingsSubscription): void { - const filter = narrow_state.filter(); - if (filter && filter._sub && filter._sub.stream_id === modified_sub.stream_id) { + const current_stream_id = narrow_state.stream_id(); + if (current_stream_id === modified_sub.stream_id) { render_title_area(); } } diff --git a/web/src/narrow_banner.ts b/web/src/narrow_banner.ts index 9405fcc632..528b2015ba 100644 --- a/web/src/narrow_banner.ts +++ b/web/src/narrow_banner.ts @@ -76,10 +76,11 @@ function retrieve_search_query_data(): SearchData { // Add in stream:foo and topic:bar if present if (current_filter.has_operator("channel") || current_filter.has_operator("topic")) { - const stream = current_filter.operands("channel")[0]; + const stream_id = current_filter.operands("channel")[0]; const topic = current_filter.operands("topic")[0]; - if (stream) { - search_string_result.stream_query = stream; + if (stream_id) { + const stream_name = stream_data.get_valid_sub_by_id_string(stream_id).name; + search_string_result.stream_query = stream_name; } if (topic) { search_string_result.topic_query = topic; @@ -184,7 +185,7 @@ export function pick_empty_narrow_banner(): NarrowBannerData { if ( page_params.is_spectator && first_operator === "channel" && - !stream_data.is_web_public_by_stream_name(first_operand) + !stream_data.is_web_public_by_stream_id(Number.parseInt(first_operand, 10)) ) { // For non web-public streams, show `login_to_access` modal. spectators.login_to_access(true); @@ -264,7 +265,7 @@ export function pick_empty_narrow_banner(): NarrowBannerData { // fallthrough to default case if no match is found break; case "channel": - if (!stream_data.is_subscribed_by_name(first_operand)) { + if (!stream_data.is_subscribed(Number.parseInt(first_operand, 10))) { // You are narrowed to a stream which does not exist or is a private stream // in which you were never subscribed. @@ -280,7 +281,7 @@ export function pick_empty_narrow_banner(): NarrowBannerData { return false; } - const stream_sub = stream_data.get_sub(first_operand); + const stream_sub = stream_data.get_sub_by_id_string(first_operand); return stream_sub && stream_data.can_toggle_subscription(stream_sub); } diff --git a/web/src/narrow_state.ts b/web/src/narrow_state.ts index e7a255b31a..8d595e7ddf 100644 --- a/web/src/narrow_state.ts +++ b/web/src/narrow_state.ts @@ -114,21 +114,26 @@ export function set_compose_defaults(): { return opts; } -export function stream_name(current_filter: Filter | undefined = filter()): string | undefined { +export function stream_id(current_filter: Filter | undefined = filter()): number | undefined { if (current_filter === undefined) { return undefined; } const stream_operands = current_filter.operands("channel"); if (stream_operands.length === 1 && stream_operands[0] !== undefined) { - const name = stream_operands[0]; - - // Use get_name() to get the most current stream - // name (considering renames and capitalization). - return stream_data.get_name(name); + return Number.parseInt(stream_operands[0], 10); } return undefined; } +export function stream_name(current_filter: Filter | undefined = filter()): string | undefined { + const id = stream_id(current_filter); + if (id === undefined) { + return undefined; + } + const sub = stream_data.get_sub_by_id(id); + return sub?.name; +} + export function stream_sub( current_filter: Filter | undefined = filter(), ): StreamSubscription | undefined { @@ -140,19 +145,7 @@ export function stream_sub( if (stream_operands.length !== 1 || stream_operands[0] === undefined) { return undefined; } - - const name = stream_operands[0]; - const sub = stream_data.get_sub_by_name(name); - - return sub; -} - -export function stream_id(filter?: Filter): number | undefined { - const sub = stream_sub(filter); - if (sub === undefined) { - return undefined; - } - return sub.stream_id; + return stream_data.get_sub_by_id_string(stream_operands[0]); } export function topic(current_filter: Filter | undefined = filter()): string | undefined { diff --git a/web/src/narrow_title.ts b/web/src/narrow_title.ts index 0437d5631a..35d7ef7e84 100644 --- a/web/src/narrow_title.ts +++ b/web/src/narrow_title.ts @@ -8,6 +8,7 @@ import * as inbox_util from "./inbox_util"; import * as people from "./people"; import * as recent_view_util from "./recent_view_util"; import {realm} from "./state_data"; +import * as stream_data from "./stream_data"; import * as unread from "./unread"; import type {FullUnreadCountsData} from "./unread"; @@ -34,10 +35,11 @@ export function compute_narrow_title(filter?: Filter): string { } if (filter.has_operator("channel")) { - if (!filter._sub) { + const sub = stream_data.get_sub_by_id_string(filter.operands("channel")[0]!); + if (!sub) { // The stream is not set because it does not currently - // exist (possibly due to a stream name change), or it - // is a private stream and the user is not subscribed. + // exist, or it is a private stream and the user is not + // subscribed. return filter_title; } if (filter.has_operator("topic")) { diff --git a/web/src/scheduled_messages_ui.js b/web/src/scheduled_messages_ui.js index e863cc91fd..4864597106 100644 --- a/web/src/scheduled_messages_ui.js +++ b/web/src/scheduled_messages_ui.js @@ -8,7 +8,6 @@ import {$t} from "./i18n"; import * as message_view from "./message_view"; import * as people from "./people"; import * as scheduled_messages from "./scheduled_messages"; -import * as stream_data from "./stream_data"; import * as timerender from "./timerender"; export function hide_scheduled_message_success_compose_banner(scheduled_message_id) { @@ -23,7 +22,7 @@ function narrow_via_edit_scheduled_message(compose_args) { [ { operator: "channel", - operand: stream_data.get_stream_name_from_id(compose_args.stream_id), + operand: compose_args.stream_id, }, {operator: "topic", operand: compose_args.topic}, ], diff --git a/web/src/search_pill.ts b/web/src/search_pill.ts index 9bd3184272..6f1fd52a9b 100644 --- a/web/src/search_pill.ts +++ b/web/src/search_pill.ts @@ -10,6 +10,7 @@ import type {InputPill, InputPillContainer} from "./input_pill"; import * as people from "./people"; import type {User} from "./people"; import type {NarrowTerm} from "./state_data"; +import * as stream_data from "./stream_data"; import * as user_status from "./user_status"; import type {UserStatusEmojiInfo} from "./user_status"; import * as util from "./util"; @@ -56,7 +57,7 @@ export function create_item_from_search_string(search_string: string): SearchPil export function get_search_string_from_item(item: SearchPill): string { const sign = item.negated ? "-" : ""; - return `${sign}${item.operator}: ${get_search_operand(item)}`; + return `${sign}${item.operator}: ${get_search_operand(item, true)}`; } // This is called when the a pill is closed. We have custom logic here @@ -215,16 +216,19 @@ export function set_search_bar_contents( } } -function get_search_operand(item: SearchPill): string { +function get_search_operand(item: SearchPill, for_display: boolean): string { if (item.type === "search_user") { return item.users.map((user) => user.email).join(","); } + if (for_display && item.operator === "channel") { + return stream_data.get_valid_sub_by_id_string(item.operand).name; + } return item.operand; } export function get_current_search_pill_terms(pill_widget: SearchPillWidget): NarrowTerm[] { return pill_widget.items().map((item) => ({ ...item, - operand: get_search_operand(item), + operand: get_search_operand(item, false), })); } diff --git a/web/src/search_suggestion.ts b/web/src/search_suggestion.ts index e9e4a2ba46..314aa85253 100644 --- a/web/src/search_suggestion.ts +++ b/web/src/search_suggestion.ts @@ -159,21 +159,23 @@ function get_channel_suggestions(last: NarrowTerm, terms: NarrowTerm[]): Suggest const query = last.operand; let channels = stream_data.subscribed_streams(); - channels = channels.filter((channel) => channel_matches_query(channel, query)); + channels = channels.filter((channel_name) => channel_matches_query(channel_name, query)); channels = typeahead_helper.sorter(query, channels, (x) => x); const regex = typeahead_helper.build_highlight_regex(query); const highlight_query = typeahead_helper.highlight_with_escaping_and_regex; - return channels.map((channel) => { + return channels.map((channel_name) => { const prefix = "channel"; - const highlighted_channel = highlight_query(regex, channel); + const highlighted_channel = highlight_query(regex, channel_name); const verb = last.negated ? "exclude " : ""; const description_html = verb + prefix + " " + highlighted_channel; + const channel = stream_data.get_sub_by_name(channel_name); + assert(channel !== undefined); const term = { operator: "channel", - operand: channel, + operand: channel.stream_id.toString(), negated: last.negated, }; const search_string = Filter.unparse([term]); @@ -473,7 +475,7 @@ function get_topic_suggestions(last: NarrowTerm, terms: NarrowTerm[]): Suggestio const operator = Filter.canonicalize_operator(last.operator); const operand = last.operand; const negated = operator === "topic" && last.negated; - let channel: string | undefined; + let channel_id: string | undefined; let guess: string | undefined; const filter = new Filter(terms); const suggest_terms: NarrowTerm[] = []; @@ -496,28 +498,28 @@ function get_topic_suggestions(last: NarrowTerm, terms: NarrowTerm[]): Suggestio switch (operator) { case "channel": guess = ""; - channel = operand; + channel_id = operand; suggest_terms.push(last); break; case "topic": case "search": guess = operand; if (filter.has_operator("channel")) { - channel = filter.operands("channel")[0]; + channel_id = filter.operands("channel")[0]; } else { - channel = narrow_state.stream_name(); - if (channel) { - suggest_terms.push({operator: "channel", operand: channel}); + channel_id = narrow_state.stream_id()?.toString(); + if (channel_id) { + suggest_terms.push({operator: "channel", operand: channel_id}); } } break; } - if (!channel) { + if (!channel_id) { return []; } - const subscription = stream_data.get_sub(channel); + const subscription = stream_data.get_sub_by_id_string(channel_id); if (!subscription) { return []; } @@ -849,12 +851,12 @@ function suggestion_search_string(suggestion_line: SuggestionLine): string { } function suggestions_for_current_filter(): SuggestionLine[] { - if (narrow_state.stream_name() && narrow_state.topic() !== "") { + if (narrow_state.stream_id() && narrow_state.topic() !== "") { return [ get_default_suggestion_line([ { operator: "channel", - operand: narrow_state.stream_name()!, + operand: narrow_state.stream_id()!.toString(), }, ]), get_default_suggestion_line(narrow_state.search_terms()), diff --git a/web/src/stream_data.ts b/web/src/stream_data.ts index 8c5e81d28a..392a1eae6b 100644 --- a/web/src/stream_data.ts +++ b/web/src/stream_data.ts @@ -1,3 +1,5 @@ +import assert from "minimalistic-assert"; + import * as blueslip from "./blueslip"; import * as color_data from "./color_data"; import {FoldDict} from "./fold_dict"; @@ -177,6 +179,18 @@ export function get_sub(stream_name: string): StreamSubscription | undefined { return undefined; } +export function get_sub_by_id_string(stream_id_string: string): StreamSubscription | undefined { + const stream_id = Number.parseInt(stream_id_string, 10); + const stream = stream_info.get(stream_id); + return stream; +} + +export function get_valid_sub_by_id_string(stream_id_string: string): StreamSubscription { + const stream = get_sub_by_id_string(stream_id_string); + assert(stream !== undefined); + return stream; +} + export function get_sub_by_id(stream_id: number): StreamSubscription | undefined { return stream_info.get(stream_id); } @@ -222,13 +236,8 @@ export function get_sub_by_name(name: string): StreamSubscription | undefined { return sub_store.get(stream_id); } -export function name_to_slug(name: string): string { - const stream_id = get_stream_id(name); - - if (!stream_id) { - return name; - } - +export function id_to_slug(stream_id: number): string { + let name = get_stream_name_from_id(stream_id); // The name part of the URL doesn't really matter, so we try to // make it pretty. name = name.replaceAll(" ", "-"); @@ -605,11 +614,6 @@ export function can_post_messages_in_stream(stream: StreamSubscription): boolean return true; } -export function is_subscribed_by_name(stream_name: string): boolean { - const sub = get_sub(stream_name); - return sub ? sub.subscribed : false; -} - export function is_subscribed(stream_id: number): boolean { const sub = sub_store.get(stream_id); return sub ? sub.subscribed : false; @@ -643,8 +647,8 @@ export function is_invite_only_by_stream_id(stream_id: number): boolean { return sub.invite_only; } -export function is_web_public_by_stream_name(stream_name: string): boolean { - const sub = get_sub(stream_name); +export function is_web_public_by_stream_id(stream_id: number): boolean { + const sub = get_sub_by_id(stream_id); if (sub === undefined) { return false; } @@ -667,22 +671,6 @@ export function is_default_stream_id(stream_id: number): boolean { return default_stream_ids.has(stream_id); } -export function get_name(stream_name: string): string { - // This returns the actual name of a stream if we are subscribed to - // it (e.g. "Denmark" vs. "denmark"), while falling thru to - // stream_name if we don't have a subscription. (Stream names - // are case-insensitive, but we try to display the actual name - // when we know it.) - // - // This function will also do the right thing if we have - // an old stream name in memory for a recently renamed stream. - const sub = get_sub_by_name(stream_name); - if (sub === undefined) { - return stream_name; - } - return sub.name; -} - export function is_user_subscribed(stream_id: number, user_id: number): boolean { const sub = sub_store.get(stream_id); if (sub === undefined || !can_view_subscribers(sub)) { diff --git a/web/src/stream_list.ts b/web/src/stream_list.ts index 253fb2d504..ee212e9397 100644 --- a/web/src/stream_list.ts +++ b/web/src/stream_list.ts @@ -682,8 +682,7 @@ export function get_sidebar_stream_topic_info(filter: Filter): { return result; } - const stream_name = op_stream[0]; - const stream_id = stream_data.get_stream_id(stream_name); + const stream_id = Number.parseInt(op_stream[0], 10); if (!stream_id) { return result; diff --git a/web/src/stream_popover.js b/web/src/stream_popover.js index 4b1d495799..3974e31e94 100644 --- a/web/src/stream_popover.js +++ b/web/src/stream_popover.js @@ -138,7 +138,7 @@ function build_stream_popover(opts) { [ { operator: "stream", - operand: sub.name, + operand: sub.stream_id.toString(), }, ], {trigger: "stream-popover"}, diff --git a/web/src/stream_settings_ui.js b/web/src/stream_settings_ui.js index baf4276840..8684d83dda 100644 --- a/web/src/stream_settings_ui.js +++ b/web/src/stream_settings_ui.js @@ -903,7 +903,7 @@ export function view_stream() { const row_data = get_row_data(active_data.$row); if (row_data) { const stream_narrow_hash = - "#narrow/stream/" + hash_util.encode_stream_name(row_data.object.name); + "#narrow/stream/" + hash_util.encode_stream_id(row_data.object.stream_id); browser_history.go_to_location(stream_narrow_hash); } } diff --git a/web/src/topic_generator.ts b/web/src/topic_generator.ts index 5d1ee7d0a6..4880b512d2 100644 --- a/web/src/topic_generator.ts +++ b/web/src/topic_generator.ts @@ -1,5 +1,4 @@ import _ from "lodash"; -import assert from "minimalistic-assert"; import * as narrow_state from "./narrow_state"; import * as pm_conversations from "./pm_conversations"; @@ -87,7 +86,6 @@ export function get_next_topic( function get_unmuted_topics(stream_id: number): string[] { const narrowed_steam_id = narrow_state.stream_id(); - assert(stream_id !== undefined); const topics = stream_topic_history.get_recent_topic_names(stream_id); const narrowed_topic = narrow_state.topic(); if ( @@ -119,16 +117,11 @@ export function get_next_topic( return topics; } - function has_unread_messages(stream_id: number, topic: string): boolean { - assert(stream_id !== undefined); - return unread.topic_has_any_unread(stream_id, topic); - } - if (only_followed_topics) { return next_topic( my_streams, get_followed_topics, - has_unread_messages, + unread.topic_has_any_unread, curr_stream_id, curr_topic, ); @@ -137,7 +130,7 @@ export function get_next_topic( return next_topic( my_streams, get_unmuted_topics, - has_unread_messages, + unread.topic_has_any_unread, curr_stream_id, curr_topic, ); diff --git a/web/src/ui_init.js b/web/src/ui_init.js index a22ff92bdb..c72ff0c3dd 100644 --- a/web/src/ui_init.js +++ b/web/src/ui_init.js @@ -549,7 +549,7 @@ export function initialize_everything(state_data) { [ { operator: "stream", - operand: sub.name, + operand: sub.stream_id.toString(), }, ], {trigger}, @@ -658,7 +658,7 @@ export function initialize_everything(state_data) { const sub = sub_store.get(stream_id); message_view.show( [ - {operator: "channel", operand: sub.name}, + {operator: "channel", operand: sub.stream_id.toString()}, {operator: "topic", operand: topic}, ], {trigger: "sidebar"}, diff --git a/web/src/unread_ops.ts b/web/src/unread_ops.ts index dd5e9d7d7f..e7f5307023 100644 --- a/web/src/unread_ops.ts +++ b/web/src/unread_ops.ts @@ -22,7 +22,6 @@ import * as overlays from "./overlays"; import * as people from "./people"; import * as recent_view_ui from "./recent_view_ui"; import type {NarrowTerm} from "./state_data"; -import * as stream_data from "./stream_data"; import * as ui_report from "./ui_report"; import * as unread from "./unread"; import * as unread_ui from "./unread_ui"; @@ -610,22 +609,20 @@ export function process_visible(): void { } export function mark_stream_as_read(stream_id: number): void { - const stream_name = stream_data.get_stream_name_from_id(stream_id); bulk_update_read_flags_for_narrow( [ {operator: "is", operand: "unread", negated: false}, - {operator: "channel", operand: stream_name}, + {operator: "channel", operand: stream_id.toString()}, ], "add", ); } export function mark_topic_as_read(stream_id: number, topic: string): void { - const stream_name = stream_data.get_stream_name_from_id(stream_id); bulk_update_read_flags_for_narrow( [ {operator: "is", operand: "unread", negated: false}, - {operator: "channel", operand: stream_name}, + {operator: "channel", operand: stream_id.toString()}, {operator: "topic", operand: topic}, ], "add", @@ -633,10 +630,9 @@ export function mark_topic_as_read(stream_id: number, topic: string): void { } export function mark_topic_as_unread(stream_id: number, topic: string): void { - const stream_name = stream_data.get_stream_name_from_id(stream_id); bulk_update_read_flags_for_narrow( [ - {operator: "channel", operand: stream_name}, + {operator: "channel", operand: stream_id.toString()}, {operator: "topic", operand: topic}, ], "remove", diff --git a/web/tests/activity.test.js b/web/tests/activity.test.js index 3275a07c5b..d6b9394bbd 100644 --- a/web/tests/activity.test.js +++ b/web/tests/activity.test.js @@ -105,7 +105,7 @@ const $fred_stub = $.create("fred stub"); const rome_sub = {name: "Rome", subscribed: true, stream_id: 1001}; function add_sub_and_set_as_current_narrow(sub) { stream_data.add_sub(sub); - const filter = new Filter([{operator: "stream", operand: sub.name}]); + const filter = new Filter([{operator: "stream", operand: sub.stream_id}]); message_lists.set_current({ data: { filter, diff --git a/web/tests/example3.test.js b/web/tests/example3.test.js index bbc5360de1..b95a17d306 100644 --- a/web/tests/example3.test.js +++ b/web/tests/example3.test.js @@ -34,7 +34,7 @@ run_test("filter", () => { stream_data.add_sub(denmark_stream); const filter_terms = [ - {operator: "stream", operand: "Denmark"}, + {operator: "stream", operand: denmark_stream.stream_id.toString()}, {operator: "topic", operand: "copenhagen"}, ]; @@ -85,7 +85,7 @@ run_test("narrow_state", () => { // Now set up a Filter object. const filter_terms = [ - {operator: "stream", operand: "Denmark"}, + {operator: "stream", operand: denmark_stream.stream_id.toString()}, {operator: "topic", operand: "copenhagen"}, ]; diff --git a/web/tests/filter.test.js b/web/tests/filter.test.js index ef7fd88ffd..a2f4feb9b5 100644 --- a/web/tests/filter.test.js +++ b/web/tests/filter.test.js @@ -75,6 +75,26 @@ function make_sub(name, stream_id) { stream_data.add_sub(sub); } +let _stream_id = 0; +function new_stream_id() { + _stream_id += 1; + return _stream_id; +} + +const foo_stream_id = new_stream_id(); +const foo_sub = { + name: "Foo", + stream_id: foo_stream_id, +}; + +const general_sub = { + name: "general", + stream_id: new_stream_id(), +}; +stream_data.add_sub(general_sub); + +const invalid_sub_id = new_stream_id(); + function test(label, f) { run_test(label, (helpers) => { stream_data.clear_subscriptions(); @@ -84,21 +104,20 @@ function test(label, f) { test("basics", () => { let terms = [ - {operator: "channel", operand: "foo"}, - {operator: "channel", operand: "exclude_me", negated: true}, + {operator: "channel", operand: foo_stream_id.toString()}, + {operator: "channel", operand: invalid_sub_id.toString(), negated: true}, // excluded {operator: "topic", operand: "bar"}, ]; let filter = new Filter(terms); assert_same_terms(filter.terms(), terms); - assert.deepEqual(filter.operands("channel"), ["foo"]); + assert.deepEqual(filter.operands("channel"), [foo_stream_id.toString()]); assert.ok(filter.has_operator("channel")); assert.ok(!filter.has_operator("search")); - assert.ok(filter.has_operand("channel", "foo")); - assert.ok(!filter.has_operand("channel", "exclude_me")); - assert.ok(!filter.has_operand("channel", "nada")); + assert.ok(filter.has_operand("channel", foo_stream_id.toString())); + assert.ok(!filter.has_operand("channel", invalid_sub_id.toString())); assert.ok(!filter.is_keyword_search()); assert.ok(!filter.can_mark_messages_read()); @@ -114,27 +133,26 @@ test("basics", () => { assert.ok(filter.can_bucket_by("channel", "topic")); // "stream" was renamed to "channel" - terms = [{operator: "stream", operand: "foo"}]; + terms = [{operator: "stream", operand: foo_stream_id.toString()}]; assert.ok(filter.has_operator("channel")); - assert.deepEqual(filter.operands("channel"), ["foo"]); + assert.deepEqual(filter.operands("channel"), [foo_stream_id.toString()]); assert.ok(filter.includes_full_stream_history()); assert.ok(filter.can_apply_locally()); terms = [ - {operator: "channel", operand: "foo"}, - {operator: "channel", operand: "exclude_me", negated: true}, + {operator: "channel", operand: foo_stream_id.toString()}, + {operator: "channel", operand: invalid_sub_id.toString(), negated: true}, // excluded {operator: "topic", operand: "bar"}, ]; filter = new Filter(terms); - assert.deepEqual(filter.operands("channel"), ["foo"]); + assert.deepEqual(filter.operands("channel"), [foo_stream_id.toString()]); assert.ok(filter.has_operator("channel")); assert.ok(!filter.has_operator("search")); - assert.ok(filter.has_operand("channel", "foo")); - assert.ok(!filter.has_operand("channel", "exclude_me")); - assert.ok(!filter.has_operand("channel", "nada")); + assert.ok(filter.has_operand("channel", foo_stream_id.toString())); + assert.ok(!filter.has_operand("channel", invalid_sub_id.toString())); assert.ok(!filter.is_keyword_search()); assert.ok(!filter.can_mark_messages_read()); @@ -147,7 +165,7 @@ test("basics", () => { assert.ok(!filter.is_conversation_view()); terms = [ - {operator: "channel", operand: "foo"}, + {operator: "channel", operand: foo_stream_id.toString()}, {operator: "topic", operand: "bar"}, {operator: "search", operand: "pizza"}, ]; @@ -165,7 +183,7 @@ test("basics", () => { assert.ok(!filter.is_conversation_view()); terms = [ - {operator: "channel", operand: "foo"}, + {operator: "channel", operand: foo_stream_id.toString()}, {operator: "topic", operand: "bar"}, {operator: "near", operand: 17}, ]; @@ -186,7 +204,7 @@ test("basics", () => { // If our only channel operator is negated, then for all intents and purposes, // we don't consider ourselves to have a channel operator, because we don't // want to have the channel in the tab bar or unsubscribe messaging, etc. - terms = [{operator: "channel", operand: "exclude", negated: true}]; + terms = [{operator: "channel", operand: invalid_sub_id.toString(), negated: true}]; filter = new Filter(terms); assert.ok(!filter.contains_only_private_messages()); assert.ok(!filter.has_operator("channel")); @@ -394,7 +412,7 @@ test("basics", () => { assert.ok(!filter.is_conversation_view()); terms = [ - {operator: "channel", operand: "foo"}, + {operator: "channel", operand: foo_stream_id.toString()}, {operator: "topic", operand: "bar"}, ]; filter = new Filter(terms); @@ -411,7 +429,7 @@ test("basics", () => { assert.ok(!filter.is_conversation_view_with_near()); terms = [ - {operator: "channel", operand: "foo"}, + {operator: "channel", operand: foo_stream_id.toString()}, {operator: "topic", operand: "bar"}, {operator: "with", operand: 17}, ]; @@ -431,7 +449,7 @@ test("basics", () => { // "stream" was renamed to "channel" terms = [ - {operator: "stream", operand: "foo"}, + {operator: "stream", operand: foo_stream_id.toString()}, {operator: "topic", operand: "bar"}, ]; filter = new Filter(terms); @@ -545,19 +563,19 @@ test("can_mark_messages_read", () => { assert_not_mark_read_with_is_operands(); assert_not_mark_read_when_searching(); - const channel_term = [{operator: "channel", operand: "foo"}]; + const channel_term = [{operator: "channel", operand: foo_stream_id.toString()}]; let filter = new Filter(channel_term); assert.ok(filter.can_mark_messages_read()); assert_not_mark_read_with_has_operands(channel_term); assert_not_mark_read_with_is_operands(channel_term); assert_not_mark_read_when_searching(channel_term); - const channel_negated_operator = [{operator: "channel", operand: "foo", negated: true}]; + const channel_negated_operator = [{operator: "channel", operand: foo_stream_id, negated: true}]; filter = new Filter(channel_negated_operator); assert.ok(!filter.can_mark_messages_read()); const channel_topic_terms = [ - {operator: "channel", operand: "foo"}, + {operator: "channel", operand: foo_stream_id.toString()}, {operator: "topic", operand: "bar"}, ]; filter = new Filter(channel_topic_terms); @@ -567,7 +585,7 @@ test("can_mark_messages_read", () => { assert_not_mark_read_when_searching(channel_topic_terms); const channel_negated_topic_terms = [ - {operator: "channel", operand: "foo"}, + {operator: "channel", operand: foo_stream_id.toString()}, {operator: "topic", operand: "bar", negated: true}, ]; filter = new Filter(channel_negated_topic_terms); @@ -665,34 +683,34 @@ test("show_first_unread", () => { test("filter_with_new_params_topic", () => { const terms = [ - {operator: "channel", operand: "foo"}, + {operator: "channel", operand: foo_stream_id.toString()}, {operator: "topic", operand: "old topic"}, ]; const filter = new Filter(terms); - assert.ok(filter.has_topic("foo", "old topic")); - assert.ok(!filter.has_topic("wrong", "old topic")); - assert.ok(!filter.has_topic("foo", "wrong")); + assert.ok(filter.has_topic(foo_stream_id.toString(), "old topic")); + assert.ok(!filter.has_topic(invalid_sub_id.toString(), "old topic")); + assert.ok(!filter.has_topic(foo_stream_id.toString(), "wrong")); const new_filter = filter.filter_with_new_params({ operator: "topic", operand: "new topic", }); - assert.deepEqual(new_filter.operands("channel"), ["foo"]); + assert.deepEqual(new_filter.operands("channel"), [foo_stream_id.toString()]); assert.deepEqual(new_filter.operands("topic"), ["new topic"]); }); test("filter_with_new_params_channel", () => { const terms = [ - {operator: "channel", operand: "foo"}, + {operator: "channel", operand: foo_stream_id.toString()}, {operator: "topic", operand: "old topic"}, ]; const filter = new Filter(terms); - assert.ok(filter.has_topic("foo", "old topic")); - assert.ok(!filter.has_topic("wrong", "old topic")); - assert.ok(!filter.has_topic("foo", "wrong")); + assert.ok(filter.has_topic(foo_stream_id.toString(), "old topic")); + assert.ok(!filter.has_topic(invalid_sub_id.toString(), "old topic")); + assert.ok(!filter.has_topic(foo_stream_id.toString(), "wrong")); const new_filter = filter.filter_with_new_params({ operator: "channel", @@ -706,33 +724,41 @@ test("filter_with_new_params_channel", () => { test("new_style_terms", () => { const term = { operator: "channel", - operand: "foo", + operand: foo_stream_id.toString(), }; const terms = [term]; const filter = new Filter(terms); - assert.deepEqual(filter.operands("channel"), ["foo"]); + assert.deepEqual(filter.operands("channel"), [foo_stream_id.toString()]); assert.ok(filter.can_bucket_by("channel")); }); -test("public_terms", ({override}) => { +test("public_terms", ({override, override_rewire}) => { stream_data.clear_subscriptions(); + const some_channel_id = new_stream_id(); let terms = [ - {operator: "channel", operand: "some_channel"}, + {operator: "channel", operand: some_channel_id}, {operator: "in", operand: "all"}, {operator: "topic", operand: "bar"}, ]; let filter = new Filter(terms); const expected_terms = [ - {operator: "channel", operand: "some_channel"}, + {operator: "channel", operand: some_channel_id}, {operator: "in", operand: "all"}, {operator: "topic", operand: "bar"}, ]; override(page_params, "narrow_stream", undefined); + override_rewire(stream_data, "get_sub_by_name", (name) => { + assert.equal(name, "default"); + return { + name, + some_channel_id, + }; + }); assert_same_terms(filter.public_terms(), expected_terms); assert.ok(filter.can_bucket_by("channel")); - terms = [{operator: "channel", operand: "default"}]; + terms = [{operator: "channel", operand: some_channel_id}]; filter = new Filter(terms); override(page_params, "narrow_stream", "default"); assert_same_terms(filter.public_terms(), []); @@ -845,34 +871,34 @@ test("predicate_basics", ({override}) => { // To keep these tests simple, we only pass objects with a few relevant attributes // rather than full-fledged message objects. - const stream_id = 42; - make_sub("Foo", stream_id); + stream_data.add_sub(foo_sub); let predicate = get_predicate([ - ["channel", "Foo"], + ["channel", foo_stream_id.toString()], ["topic", "Bar"], ]); - assert.ok(predicate({type: stream_message, stream_id, topic: "bar"})); - assert.ok(!predicate({type: stream_message, stream_id, topic: "whatever"})); + assert.ok(predicate({type: stream_message, stream_id: foo_stream_id, topic: "bar"})); + assert.ok(!predicate({type: stream_message, stream_id: foo_stream_id, topic: "whatever"})); // 9999999 doesn't exist, testing no match assert.ok(!predicate({type: stream_message, stream_id: 9999999})); assert.ok(!predicate({type: direct_message})); // For old channels that we are no longer subscribed to, we may not have // a subscription, but these should still match by channel name. + const old_sub_id = new_stream_id(); const old_sub = { name: "old-subscription", - stream_id: 5, + stream_id: old_sub_id, subscribed: false, }; stream_data.add_sub(old_sub); predicate = get_predicate([ - ["channel", "old-subscription"], + ["channel", old_sub_id.toString()], ["topic", "Bar"], ]); - assert.ok(predicate({type: stream_message, stream_id: 5, topic: "bar"})); + assert.ok(predicate({type: stream_message, stream_id: old_sub.stream_id, topic: "bar"})); // 99999 doesn't exist, testing no match - assert.ok(!predicate({type: stream_message, stream_id: 99999, topic: "whatever"})); + assert.ok(!predicate({type: stream_message, stream_id: invalid_sub_id, topic: "whatever"})); predicate = get_predicate([["search", "emoji"]]); assert.ok(predicate({})); @@ -921,7 +947,7 @@ test("predicate_basics", ({override}) => { override(user_topics, "is_topic_followed", () => true); assert.ok(predicate({type: "stream", topic: "foo", stream_id: 5})); - const unknown_stream_id = 999; + const unknown_stream_id = new_stream_id(); override(user_topics, "is_topic_muted", () => false); override(user_topics, "is_topic_unmuted_or_followed", () => false); predicate = get_predicate([["in", "home"]]); @@ -931,7 +957,7 @@ test("predicate_basics", ({override}) => { // Muted topic is not part of in-home. with_overrides(({override}) => { override(user_topics, "is_topic_muted", () => true); - assert.ok(!predicate({stream_id, topic: "bar"})); + assert.ok(!predicate({stream_id: foo_stream_id, topic: "bar"})); }); // Muted stream is not part of in-home. @@ -1141,10 +1167,10 @@ test("negated_predicates", () => { let predicate; let narrow; - const social_stream_id = 555; + const social_stream_id = new_stream_id(); make_sub("social", social_stream_id); - narrow = [{operator: "channel", operand: "social", negated: true}]; + narrow = [{operator: "channel", operand: social_stream_id.toString(), negated: true}]; predicate = new Filter(narrow).predicate(); assert.ok(predicate({type: stream_message, stream_id: 999999})); assert.ok(!predicate({type: stream_message, stream_id: social_stream_id})); @@ -1155,10 +1181,10 @@ test("negated_predicates", () => { }); function test_mit_exceptions() { - const foo_stream_id = 555; + const foo_stream_id = new_stream_id(); make_sub("Foo", foo_stream_id); let predicate = get_predicate([ - ["channel", "Foo"], + ["channel", foo_stream_id.toString()], ["topic", "personal"], ]); assert.ok(predicate({type: stream_message, stream_id: foo_stream_id, topic: "personal"})); @@ -1169,7 +1195,7 @@ function test_mit_exceptions() { assert.ok(!predicate({type: direct_message})); predicate = get_predicate([ - ["channel", "Foo"], + ["channel", foo_stream_id.toString()], ["topic", "bar"], ]); assert.ok(predicate({type: stream_message, stream_id: foo_stream_id, topic: "bar.d"})); @@ -1184,7 +1210,7 @@ function test_mit_exceptions() { // Try to get the MIT regex to explode for an empty topic. terms = [ - {operator: "channel", operand: "foo"}, + {operator: "channel", operand: foo_stream_id.toString()}, {operator: "topic", operand: ""}, ]; predicate = new Filter(terms).predicate(); @@ -1216,10 +1242,10 @@ test("predicate_edge_cases", () => { assert.ok(!predicate({})); // Exercise caching feature. - const stream_id = 101; + const stream_id = new_stream_id(); make_sub("Off topic", stream_id); const terms = [ - {operator: "channel", operand: "Off topic"}, + {operator: "channel", operand: stream_id.toString()}, {operator: "topic", operand: "Mars"}, ]; const filter = new Filter(terms); @@ -1237,18 +1263,27 @@ test("parse", () => { assert_same_terms(result, terms); } - string = "channel:Foo topic:Bar yo"; + make_sub(foo_sub.name, foo_stream_id); + string = "channel:Foo topic:bar yo"; terms = [ - {operator: "channel", operand: "Foo"}, - {operator: "topic", operand: "Bar"}, + {operator: "channel", operand: foo_stream_id.toString()}, + {operator: "topic", operand: "bar"}, + {operator: "search", operand: "yo"}, + ]; + _test(); + + string = `channel:${foo_stream_id} topic:bar yo`; + terms = [ + {operator: "channel", operand: foo_stream_id.toString()}, + {operator: "topic", operand: "bar"}, {operator: "search", operand: "yo"}, ]; _test(); // "stream" was renamed to "channel" - string = "stream:Foo topic:Bar yo"; + string = `stream:${foo_stream_id} topic:Bar yo`; terms = [ - {operator: "channel", operand: "Foo"}, + {operator: "channel", operand: foo_stream_id.toString()}, {operator: "topic", operand: "Bar"}, {operator: "search", operand: "yo"}, ]; @@ -1262,41 +1297,21 @@ test("parse", () => { terms = [{operator: "sender", operand: "leo+test@zulip.com"}]; _test(); - string = "channel:With+Space"; - terms = [{operator: "channel", operand: "With Space"}]; - _test(); - - string = 'channel:"with quoted space" topic:and separate'; - terms = [ - {operator: "channel", operand: "with quoted space"}, - {operator: "topic", operand: "and"}, - {operator: "search", operand: "separate"}, - ]; - _test(); - - string = 'channel:"unclosed quote'; - terms = [{operator: "channel", operand: "unclosed quote"}]; - _test(); - - string = 'channel:""'; - terms = [{operator: "channel", operand: ""}]; - _test(); - string = "https://www.google.com"; terms = [{operator: "search", operand: "https://www.google.com"}]; _test(); - string = "channel:foo -channel:exclude"; + string = `channel:${foo_stream_id} -channel:${invalid_sub_id}`; terms = [ - {operator: "channel", operand: "foo"}, - {operator: "channel", operand: "exclude", negated: true}, + {operator: "channel", operand: foo_stream_id.toString()}, + {operator: "channel", operand: invalid_sub_id.toString(), negated: true}, ]; _test(); - string = "text channel:foo more text"; + string = `text channel:${foo_stream_id} more text`; terms = [ {operator: "search", operand: "text"}, - {operator: "channel", operand: "foo"}, + {operator: "channel", operand: foo_stream_id.toString()}, {operator: "search", operand: "more text"}, ]; _test(); @@ -1322,25 +1337,25 @@ test("parse", () => { terms = [{operator: "channels", operand: "public"}]; _test(); - string = "channel:foo :emoji: are cool"; + string = `channel:${foo_stream_id} :emoji: are cool`; terms = [ - {operator: "channel", operand: "foo"}, + {operator: "channel", operand: foo_stream_id.toString()}, {operator: "search", operand: ":emoji: are cool"}, ]; _test(); - string = ":channel: channel:foo :emoji: are cool"; + string = `:channel: channel:${foo_stream_id} :emoji: are cool`; terms = [ {operator: "search", operand: ":channel:"}, - {operator: "channel", operand: "foo"}, + {operator: "channel", operand: foo_stream_id.toString()}, {operator: "search", operand: ":emoji: are cool"}, ]; _test(); - string = ":channel: channel:foo -:emoji: are cool"; + string = `:channel: channel:${foo_stream_id} -:emoji: are cool`; terms = [ {operator: "search", operand: ":channel:"}, - {operator: "channel", operand: "foo"}, + {operator: "channel", operand: foo_stream_id.toString()}, {operator: "search", operand: "-:emoji: are cool"}, ]; _test(); @@ -1349,10 +1364,10 @@ test("parse", () => { terms = []; _test(); - string = 'channel: separated topic: "with space"'; + string = `channel: ${foo_stream_id} topic: "separated with space"`; terms = [ - {operator: "channel", operand: "separated"}, - {operator: "topic", operand: "with space"}, + {operator: "channel", operand: foo_stream_id.toString()}, + {operator: "topic", operand: "separated with space"}, ]; _test(); }); @@ -1362,11 +1377,11 @@ test("unparse", () => { let terms; terms = [ - {operator: "channel", operand: "Foo"}, + {operator: "channel", operand: foo_stream_id.toString()}, {operator: "topic", operand: "Bar", negated: true}, {operator: "search", operand: "yo"}, ]; - string = "channel:Foo -topic:Bar yo"; + string = `channel:${foo_stream_id} -topic:Bar yo`; assert.deepEqual(Filter.unparse(terms), string); terms = [ @@ -1400,10 +1415,10 @@ test("unparse", () => { // canonical version of the operator is // used in the unparsed search string terms = [ - {operator: "stream", operand: "Foo"}, + {operator: "stream", operand: foo_stream_id.toString()}, {operator: "subject", operand: "Bar"}, ]; - string = "channel:Foo topic:Bar"; + string = `channel:${foo_stream_id} topic:Bar`; assert.deepEqual(Filter.unparse(terms), string); }); @@ -1420,22 +1435,27 @@ test("describe", ({mock_template}) => { string = "exclude channels public"; assert.equal(Filter.search_description_as_html(narrow), string); + const devel_id = new_stream_id(); + make_sub("devel", devel_id); + narrow = [ - {operator: "channel", operand: "devel"}, + {operator: "channel", operand: devel_id.toString()}, {operator: "is", operand: "starred"}, ]; string = "channel devel, starred messages"; assert.equal(Filter.search_description_as_html(narrow), string); + const river_id = new_stream_id(); + make_sub("river", river_id); narrow = [ - {operator: "channel", operand: "river"}, + {operator: "channel", operand: river_id.toString()}, {operator: "is", operand: "unread"}, ]; string = "channel river, unread messages"; assert.equal(Filter.search_description_as_html(narrow), string); narrow = [ - {operator: "channel", operand: "devel"}, + {operator: "channel", operand: devel_id.toString()}, {operator: "topic", operand: "JS"}, ]; string = "channel devel > JS"; @@ -1482,7 +1502,7 @@ test("describe", ({mock_template}) => { assert.equal(Filter.search_description_as_html(narrow), string); narrow = [ - {operator: "channel", operand: "devel"}, + {operator: "channel", operand: devel_id.toString()}, {operator: "topic", operand: "JS", negated: true}, ]; string = "channel devel, exclude topic JS"; @@ -1496,14 +1516,14 @@ test("describe", ({mock_template}) => { assert.equal(Filter.search_description_as_html(narrow), string); narrow = [ - {operator: "channel", operand: "devel"}, + {operator: "channel", operand: devel_id.toString()}, {operator: "is", operand: "starred", negated: true}, ]; string = "channel devel, exclude starred messages"; assert.equal(Filter.search_description_as_html(narrow), string); narrow = [ - {operator: "channel", operand: "devel"}, + {operator: "channel", operand: devel_id.toString()}, {operator: "has", operand: "image", negated: true}, ]; string = "channel devel, exclude messages with images"; @@ -1511,14 +1531,14 @@ test("describe", ({mock_template}) => { narrow = [ {operator: "has", operand: "abc", negated: true}, - {operator: "channel", operand: "devel"}, + {operator: "channel", operand: devel_id.toString()}, ]; string = "invalid abc operand for has operator, channel devel"; assert.equal(Filter.search_description_as_html(narrow), string); narrow = [ {operator: "has", operand: "image", negated: true}, - {operator: "channel", operand: "devel"}, + {operator: "channel", operand: devel_id.toString()}, ]; string = "exclude messages with images, channel devel"; assert.equal(Filter.search_description_as_html(narrow), string); @@ -1529,7 +1549,7 @@ test("describe", ({mock_template}) => { // canonical version of the operator is used in description narrow = [ - {operator: "stream", operand: "devel"}, + {operator: "stream", operand: devel_id.toString()}, {operator: "subject", operand: "JS", negated: true}, ]; string = "channel devel, exclude topic JS"; @@ -1537,7 +1557,9 @@ test("describe", ({mock_template}) => { }); test("can_bucket_by", () => { - let terms = [{operator: "channel", operand: "My channel"}]; + const channel_id = new_stream_id(); + make_sub("My channel", channel_id); + let terms = [{operator: "channel", operand: channel_id.toString()}]; let filter = new Filter(terms); assert.equal(filter.can_bucket_by("channel"), true); assert.equal(filter.can_bucket_by("channel", "topic"), false); @@ -1546,7 +1568,7 @@ test("can_bucket_by", () => { terms = [ // try a non-orthodox ordering {operator: "topic", operand: "My topic"}, - {operator: "channel", operand: "My channel"}, + {operator: "channel", operand: channel_id.toString()}, ]; filter = new Filter(terms); assert.equal(filter.can_bucket_by("channel"), true); @@ -1554,7 +1576,7 @@ test("can_bucket_by", () => { assert.equal(filter.can_bucket_by("dm"), false); terms = [ - {operator: "channel", operand: "My channel", negated: true}, + {operator: "channel", operand: channel_id.toString(), negated: true}, {operator: "topic", operand: "My topic"}, ]; filter = new Filter(terms); @@ -1670,7 +1692,7 @@ test("term_type", () => { const terms = [ {operator: "topic", operand: "lunch"}, {operator: "sender", operand: "steve@foo.com"}, - {operator: "channel", operand: "Verona"}, + {operator: "channel", operand: new_stream_id().toString()}, ]; let filter = new Filter(terms); const term_types = filter.sorted_term_types(); @@ -1737,8 +1759,8 @@ test("is_valid_search_term", () => { ["in: nowhere", false], ["id: 4", true], ["near: home", false], - ["channel: Denmark", true], - ["channel: GhostTown", false], + ["channel: " + denmark.stream_id, true], + [`channel: ${invalid_sub_id}`, false], ["channels: public", true], ["channels: private", false], ["topic: GhostTown", true], @@ -1778,9 +1800,25 @@ test("update_email", () => { }); test("try_adjusting_for_moved_with_target", ({override}) => { + const scotland_id = new_stream_id(); + make_sub("Scotland", scotland_id); + const verona_id = new_stream_id(); + make_sub("Verona", verona_id); const messages = { - 12: {type: "stream", display_recipient: "Scotland", topic: "Test 1", id: 12}, - 17: {type: "stream", display_recipient: "Verona", topic: "Test 2", id: 17}, + 12: { + type: "stream", + stream_id: scotland_id, + display_recipient: "Scotland", + topic: "Test 1", + id: 12, + }, + 17: { + type: "stream", + stream_id: verona_id, + display_recipient: "Verona", + topic: "Test 2", + id: 17, + }, 2: {type: "direct", id: 2, display_recipient: [{id: 3, email: "user3@zulip.com"}]}, }; @@ -1788,7 +1826,7 @@ test("try_adjusting_for_moved_with_target", ({override}) => { // When the narrow terms are correct, it returns the same terms let terms = [ - {operator: "channel", operand: "Scotland", negated: false}, + {operator: "channel", operand: scotland_id.toString(), negated: false}, {operator: "topic", operand: "Test 1", negated: false}, {operator: "with", operand: "12", negated: false}, ]; @@ -1802,7 +1840,7 @@ test("try_adjusting_for_moved_with_target", ({override}) => { // When the narrow terms are incorrect, the narrow is corrected // to the narrow of the `with` operand. const incorrect_terms = [ - {operator: "channel", operand: "Verona", negated: false}, + {operator: "channel", operand: verona_id.toString(), negated: false}, {operator: "topic", operand: "Test 2", negated: false}, {operator: "with", operand: "12", negated: false}, ]; @@ -1816,7 +1854,7 @@ test("try_adjusting_for_moved_with_target", ({override}) => { // when message specified in `with` operator does not exist in // message_store, we rather go to the server, without any updates. terms = [ - {operator: "channel", operand: "Scotland", negated: false}, + {operator: "channel", operand: scotland_id.toString(), negated: false}, {operator: "topic", operand: "Test 1", negated: false}, {operator: "with", operand: "11", negated: false}, ]; @@ -1830,7 +1868,7 @@ test("try_adjusting_for_moved_with_target", ({override}) => { // the `with` operator corresponds to that of a direct message, then // the narrow is adjusted to point to the narrow containing the message. terms = [ - {operator: "channel", operand: "Scotland", negated: false}, + {operator: "channel", operand: scotland_id.toString(), negated: false}, {operator: "topic", operand: "Test 1", negated: false}, {operator: "with", operand: "2", negated: false}, ]; @@ -1852,7 +1890,7 @@ test("try_adjusting_for_moved_with_target", ({override}) => { filter.try_adjusting_for_moved_with_target(); assert.deepEqual(filter.requires_adjustment_for_moved_with_target, false); assert.deepEqual(filter.terms(), [ - {operator: "channel", operand: "Scotland", negated: false}, + {operator: "channel", operand: scotland_id.toString(), negated: false}, {operator: "topic", operand: "Test 1", negated: false}, {operator: "with", operand: "12", negated: false}, ]); @@ -1861,7 +1899,7 @@ test("try_adjusting_for_moved_with_target", ({override}) => { // and is present in the same narrow as the original one, then // no hash change is required. terms = [ - {operator: "channel", operand: "Verona", negated: false}, + {operator: "channel", operand: verona_id.toString(), negated: false}, {operator: "topic", operand: "Test 2", negated: false}, {operator: "with", operand: "17", negated: false}, ]; @@ -1873,7 +1911,7 @@ test("try_adjusting_for_moved_with_target", ({override}) => { // locally, but messages fetched are in same narrow as // original narrow, then no hash change is required. terms = [ - {operator: "channel", operand: "Verona", negated: false}, + {operator: "channel", operand: verona_id.toString(), negated: false}, {operator: "topic", operand: "Test 2", negated: false}, {operator: "with", operand: "1", negated: false}, ]; @@ -1888,7 +1926,7 @@ test("try_adjusting_for_moved_with_target", ({override}) => { // and is not present in the same narrow as the original one, // then hash change is required. terms = [ - {operator: "channel", operand: "Verona", negated: false}, + {operator: "channel", operand: verona_id.toString(), negated: false}, {operator: "topic", operand: "Test 2", negated: false}, {operator: "with", operand: "12", negated: false}, ]; @@ -1900,7 +1938,7 @@ test("try_adjusting_for_moved_with_target", ({override}) => { // locally, and messages fetched are in different narrow from // original narrow, then hash change is required. terms = [ - {operator: "channel", operand: "Verona", negated: false}, + {operator: "channel", operand: verona_id.toString(), negated: false}, {operator: "topic", operand: "Test 2", negated: false}, {operator: "with", operand: "1", negated: false}, ]; @@ -1931,14 +1969,7 @@ function make_web_public_sub(name, stream_id) { } test("navbar_helpers", () => { - const sub = { - name: "Foo", - stream_id: 12, - }; - stream_data.add_sub(sub); - - const stream_id = 43; - make_sub("Foo", stream_id); + stream_data.add_sub(foo_sub); // make sure title has names separated with correct delimiters function properly_separated_names(names) { @@ -2007,7 +2038,7 @@ test("navbar_helpers", () => { const is_followed = [{operator: "is", operand: "followed"}]; const channels_public = [{operator: "channels", operand: "public"}]; const channel_topic_terms = [ - {operator: "channel", operand: "foo"}, + {operator: "channel", operand: foo_stream_id.toString()}, {operator: "topic", operand: "bar"}, ]; const has_reaction_sender_me = [ @@ -2015,16 +2046,19 @@ test("navbar_helpers", () => { {operator: "sender", operand: "me"}, ]; // foo channel exists - const channel_term = [{operator: "channel", operand: "foo"}]; - make_private_sub("psub", "22"); - const private_channel_term = [{operator: "channel", operand: "psub"}]; - make_web_public_sub("webPublicSub", "12"); // capitalized just to try be tricky and robust. - const web_public_channel = [{operator: "channel", operand: "webPublicSub"}]; - const non_existent_channel = [{operator: "channel", operand: "Elephant"}]; - const non_existent_channel_topic = [ - {operator: "channel", operand: "Elephant"}, - {operator: "topic", operand: "pink"}, + const channel_term = [{operator: "channel", operand: foo_stream_id.toString()}]; + const invalid_channel_id = new_stream_id(); + const invalid_channel = [{operator: "channel", operand: invalid_channel_id.toString()}]; + const invalid_channel_with_topic = [ + {operator: "channel", operand: invalid_channel_id.toString()}, + {operator: "topic", operand: "bar"}, ]; + const public_sub_id = new_stream_id(); + make_private_sub("psub", public_sub_id); + const private_channel_term = [{operator: "channel", operand: public_sub_id.toString()}]; + const web_public_sub_id = new_stream_id(); + make_web_public_sub("webPublicSub", web_public_sub_id); // capitalized just to try be tricky and robust. + const web_public_channel = [{operator: "channel", operand: web_public_sub_id.toString()}]; const dm = [{operator: "dm", operand: "joe@example.com"}]; const dm_with = [ {operator: "dm", operand: "joe@example.com"}, @@ -2042,7 +2076,7 @@ test("navbar_helpers", () => { const is_alerted = [{operator: "is", operand: "alerted"}]; const is_unread = [{operator: "is", operand: "unread"}]; const channel_topic_near = [ - {operator: "channel", operand: "foo"}, + {operator: "channel", operand: foo_stream_id.toString()}, {operator: "topic", operand: "bar"}, {operator: "near", operand: "12"}, ]; @@ -2051,7 +2085,7 @@ test("navbar_helpers", () => { {operator: "near", operand: "12"}, ]; const channel_with = [ - {operator: "channel", operand: "foo"}, + {operator: "channel", operand: foo_stream_id.toString()}, {operator: "topic", operand: "bar"}, {operator: "with", operand: "12"}, ]; @@ -2131,7 +2165,14 @@ test("navbar_helpers", () => { is_common_narrow: true, zulip_icon: "hashtag", title: "Foo", - redirect_url_with_search: "/#narrow/stream/43-Foo/topic/bar", + redirect_url_with_search: `/#narrow/stream/${foo_stream_id}-Foo/topic/bar`, + }, + { + terms: invalid_channel_with_topic, + is_common_narrow: true, + icon: "question-circle-o", + title: "translated: Unknown channel", + redirect_url_with_search: "#", }, { terms: channels_public, @@ -2145,20 +2186,13 @@ test("navbar_helpers", () => { is_common_narrow: true, zulip_icon: "hashtag", title: "Foo", - redirect_url_with_search: "/#narrow/stream/43-Foo", + redirect_url_with_search: `/#narrow/stream/${foo_stream_id}-Foo`, }, { - terms: non_existent_channel, + terms: invalid_channel, is_common_narrow: true, icon: "question-circle-o", - title: "translated: Unknown channel #Elephant", - redirect_url_with_search: "#", - }, - { - terms: non_existent_channel_topic, - is_common_narrow: true, - icon: "question-circle-o", - title: "translated: Unknown channel #Elephant", + title: "translated: Unknown channel", redirect_url_with_search: "#", }, { @@ -2166,14 +2200,14 @@ test("navbar_helpers", () => { is_common_narrow: true, zulip_icon: "lock", title: "psub", - redirect_url_with_search: "/#narrow/stream/22-psub", + redirect_url_with_search: `/#narrow/stream/${public_sub_id}-psub`, }, { terms: web_public_channel, is_common_narrow: true, zulip_icon: "globe", title: "webPublicSub", - redirect_url_with_search: "/#narrow/stream/12-webPublicSub", + redirect_url_with_search: `/#narrow/stream/${web_public_sub_id}-webPublicSub`, }, { terms: dm, @@ -2300,7 +2334,7 @@ test("navbar_helpers", () => { // incomplete and weak test cases just to restore coverage of filter.ts const complex_term = [ - {operator: "channel", operand: "foo"}, + {operator: "channel", operand: foo_stream_id.toString()}, {operator: "topic", operand: "bar"}, {operator: "sender", operand: "me"}, ]; @@ -2312,7 +2346,7 @@ test("navbar_helpers", () => { assert.equal(filter.is_common_narrow(), false); const channel_topic_search_term = [ - {operator: "channel", operand: "foo"}, + {operator: "channel", operand: foo_stream_id.toString()}, {operator: "topic", operand: "bar"}, {operator: "search", operand: "potato"}, ]; @@ -2357,7 +2391,7 @@ test("navbar_helpers", () => { // this is actually wrong, but the code is currently not robust enough to throw an error here // also, used as an example of triggering last return statement. const default_redirect = { - terms: [{operator: "channel", operand: "foo"}], + terms: [{operator: "channel", operand: foo_stream_id.toString()}], redirect_url: "#", }; @@ -2392,10 +2426,15 @@ run_test("is_spectator_compatible", () => { assert.ok( !Filter.is_spectator_compatible([{operator: "dm-including", operand: "hamlet@zulip.com"}]), ); - assert.ok(Filter.is_spectator_compatible([{operator: "channel", operand: "Denmark"}])); + + const denmark_id = new_stream_id(); + make_sub("Denmark", denmark_id); + assert.ok( + Filter.is_spectator_compatible([{operator: "channel", operand: denmark_id.toString()}]), + ); assert.ok( Filter.is_spectator_compatible([ - {operator: "channel", operand: "Denmark"}, + {operator: "channel", operand: denmark_id.toString()}, {operator: "topic", operand: "logic"}, ]), ); @@ -2438,7 +2477,7 @@ run_test("is_in_home", () => { }); run_test("equals", () => { - let terms = [{operator: "channel", operand: "Foo"}]; + let terms = [{operator: "channel", operand: foo_stream_id.toString()}]; let filter = new Filter(terms); assert.ok(filter.equals(new Filter(terms))); @@ -2447,7 +2486,7 @@ run_test("equals", () => { assert.ok(!filter.equals(new Filter([...terms, {operator: "topic", operand: "Bar"}]))); terms = [ - {operator: "channel", operand: "Foo"}, + {operator: "channel", operand: foo_stream_id.toString()}, {operator: "topic", operand: "Bar"}, ]; filter = new Filter(terms); @@ -2455,7 +2494,7 @@ run_test("equals", () => { filter.equals( new Filter([ {operator: "topic", operand: "Bar"}, - {operator: "channel", operand: "Foo"}, + {operator: "channel", operand: foo_stream_id.toString()}, ]), ), ); @@ -2468,7 +2507,7 @@ run_test("equals", () => { new Filter([ {operator: "near", operand: "10"}, {operator: "topic", operand: "Bar"}, - {operator: "channel", operand: "Foo"}, + {operator: "channel", operand: foo_stream_id.toString()}, {operator: "near", operand: "101"}, ]), ["near"], @@ -2480,7 +2519,7 @@ run_test("adjusted_terms_if_moved", () => { current_user.email = me.email; // should return null for non-stream messages containing no // `with` operator - let raw_terms = [{operator: "channel", operand: "Foo"}]; + let raw_terms = [{operator: "channel", operand: foo_stream_id.toString()}]; let message = {type: "private"}; let result = Filter.adjusted_terms_if_moved(raw_terms, message); assert.strictEqual(result, null); @@ -2496,7 +2535,7 @@ run_test("adjusted_terms_if_moved", () => { ], }; raw_terms = [ - {operator: "channel", operand: "Foo"}, + {operator: "channel", operand: foo_stream_id.toString()}, {operator: "with", operand: `${message.id}`}, ]; result = Filter.adjusted_terms_if_moved(raw_terms, message); @@ -2521,33 +2560,53 @@ run_test("adjusted_terms_if_moved", () => { ]); // should return null if no terms are changed - raw_terms = [{operator: "channel", operand: "general"}]; - message = {type: "stream", display_recipient: "general", topic: "discussion"}; + raw_terms = [{operator: "channel", operand: general_sub.stream_id.toString()}]; + message = { + type: "stream", + stream_id: general_sub.stream_id, + display_recipient: "general", + topic: "discussion", + }; result = Filter.adjusted_terms_if_moved(raw_terms, message); assert.strictEqual(result, null); // should adjust channel term to match message's display_recipient - raw_terms = [{operator: "channel", operand: "random"}]; - message = {type: "stream", display_recipient: "general", topic: "discussion"}; - let expected = [{operator: "channel", operand: "general"}]; + raw_terms = [{operator: "channel", operand: "999"}]; + message = { + type: "stream", + stream_id: general_sub.stream_id, + display_recipient: "general", + topic: "discussion", + }; + let expected = [{operator: "channel", operand: general_sub.stream_id.toString()}]; result = Filter.adjusted_terms_if_moved(raw_terms, message); assert.deepStrictEqual(result, expected); // should adjust topic term to match message's topic raw_terms = [{operator: "topic", operand: "random"}]; - message = {type: "stream", display_recipient: "general", topic: "discussion"}; + message = { + type: "stream", + stream_id: general_sub.stream_id, + display_recipient: "general", + topic: "discussion", + }; expected = [{operator: "topic", operand: "discussion"}]; result = Filter.adjusted_terms_if_moved(raw_terms, message); assert.deepStrictEqual(result, expected); // should adjust both channel and topic terms when both are different raw_terms = [ - {operator: "channel", operand: "random"}, + {operator: "channel", operand: "999"}, {operator: "topic", operand: "random"}, ]; - message = {type: "stream", display_recipient: "general", topic: "discussion"}; + message = { + type: "stream", + stream_id: general_sub.stream_id, + display_recipient: "general", + topic: "discussion", + }; expected = [ - {operator: "channel", operand: "general"}, + {operator: "channel", operand: general_sub.stream_id.toString()}, {operator: "topic", operand: "discussion"}, ]; result = Filter.adjusted_terms_if_moved(raw_terms, message); @@ -2555,13 +2614,18 @@ run_test("adjusted_terms_if_moved", () => { // should not adjust terms that are not channel or topic raw_terms = [ - {operator: "channel", operand: "random"}, + {operator: "channel", operand: "999"}, {operator: "topic", operand: "random"}, {operator: "sender", operand: "alice"}, ]; - message = {type: "stream", display_recipient: "general", topic: "discussion"}; + message = { + type: "stream", + stream_id: general_sub.stream_id, + display_recipient: "general", + topic: "discussion", + }; expected = [ - {operator: "channel", operand: "general"}, + {operator: "channel", operand: general_sub.stream_id.toString()}, {operator: "topic", operand: "discussion"}, {operator: "sender", operand: "alice"}, ]; @@ -2589,7 +2653,9 @@ run_test("can_newly_match_moved_messages", () => { filter = new Filter([{operator: "is", operand: "starred"}]); assert.deepEqual(filter.can_newly_match_moved_messages("general", "test"), false); - filter = new Filter([{negated: true, operator: "channel", operand: "general"}]); + filter = new Filter([ + {negated: true, operator: "channel", operand: general_sub.stream_id.toString()}, + ]); assert.deepEqual(filter.can_newly_match_moved_messages("something-else", "test"), true); filter = new Filter([{negated: true, operator: "is", operand: "followed"}]); diff --git a/web/tests/hash_util.test.js b/web/tests/hash_util.test.js index 4c3cdfea30..1b03a21187 100644 --- a/web/tests/hash_util.test.js +++ b/web/tests/hash_util.test.js @@ -21,8 +21,9 @@ const hamlet = { people.add_active_user(hamlet); +const frontend_id = 99; const frontend = { - stream_id: 99, + stream_id: frontend_id, name: "frontend", }; @@ -44,7 +45,7 @@ run_test("hash_util", () => { encode_decode_operand(operator, operand, "15-Hamlet"); operator = "stream"; - operand = "frontend"; + operand = frontend_id.toString(); encode_decode_operand(operator, operand, "99-frontend"); @@ -175,19 +176,19 @@ run_test("test_is_in_specified_hash_category", () => { run_test("test_parse_narrow", () => { assert.deepEqual(hash_util.parse_narrow(["narrow", "stream", "99-frontend"]), [ - {negated: false, operator: "stream", operand: "frontend"}, + {negated: false, operator: "stream", operand: frontend_id.toString()}, ]); assert.deepEqual(hash_util.parse_narrow(["narrow", "-stream", "99-frontend"]), [ - {negated: true, operator: "stream", operand: "frontend"}, + {negated: true, operator: "stream", operand: frontend_id.toString()}, ]); assert.equal(hash_util.parse_narrow(["narrow", "BOGUS"]), undefined); - // For nonexistent streams, we get the full slug. - // We possibly should remove the prefix and fix this test. + // For nonexistent streams, we get an empty string. We don't use this + // anywhere, and just show "Invalid stream" in the navbar. assert.deepEqual(hash_util.parse_narrow(["narrow", "stream", "42-bogus"]), [ - {negated: false, operator: "stream", operand: "42-bogus"}, + {negated: false, operator: "stream", operand: ""}, ]); }); diff --git a/web/tests/hashchange.test.js b/web/tests/hashchange.test.js index b7a4382668..3c583fad9d 100644 --- a/web/tests/hashchange.test.js +++ b/web/tests/hashchange.test.js @@ -38,48 +38,60 @@ const message_view = zrequire("../src/message_view"); const stream_data = zrequire("stream_data"); const {Filter} = zrequire("../src/filter"); +const devel_id = 100; +const devel = { + name: "devel", + stream_id: devel_id, + color: "blue", + subscribed: true, +}; +stream_data.add_sub(devel); + run_test("terms_round_trip", () => { let terms; let hash; let narrow; terms = [ - {operator: "stream", operand: "devel"}, + {operator: "stream", operand: devel_id.toString()}, {operator: "topic", operand: "algol"}, ]; hash = hash_util.search_terms_to_hash(terms); - assert.equal(hash, "#narrow/stream/devel/topic/algol"); + assert.equal(hash, "#narrow/stream/100-devel/topic/algol"); narrow = hash_util.parse_narrow(hash.split("/")); assert.deepEqual(narrow, [ - {operator: "stream", operand: "devel", negated: false}, + {operator: "stream", operand: devel_id.toString(), negated: false}, {operator: "topic", operand: "algol", negated: false}, ]); terms = [ - {operator: "stream", operand: "devel"}, + {operator: "stream", operand: devel_id.toString()}, {operator: "topic", operand: "visual c++", negated: true}, ]; hash = hash_util.search_terms_to_hash(terms); - assert.equal(hash, "#narrow/stream/devel/-topic/visual.20c.2B.2B"); + assert.equal(hash, "#narrow/stream/100-devel/-topic/visual.20c.2B.2B"); narrow = hash_util.parse_narrow(hash.split("/")); assert.deepEqual(narrow, [ - {operator: "stream", operand: "devel", negated: false}, + {operator: "stream", operand: devel_id.toString(), negated: false}, {operator: "topic", operand: "visual c++", negated: true}, ]); // test new encodings, where we have a stream id + const florida_id = 987; const florida_stream = { name: "Florida, USA", - stream_id: 987, + stream_id: florida_id, }; stream_data.add_sub(florida_stream); - terms = [{operator: "stream", operand: "Florida, USA"}]; + terms = [{operator: "stream", operand: florida_id.toString()}]; hash = hash_util.search_terms_to_hash(terms); assert.equal(hash, "#narrow/stream/987-Florida.2C-USA"); narrow = hash_util.parse_narrow(hash.split("/")); - assert.deepEqual(narrow, [{operator: "stream", operand: "Florida, USA", negated: false}]); + assert.deepEqual(narrow, [ + {operator: "stream", operand: florida_id.toString(), negated: false}, + ]); }); run_test("stream_to_channel_rename", () => { @@ -90,13 +102,15 @@ run_test("stream_to_channel_rename", () => { // Confirm the URLs generated from search terms use "stream" and "streams" // and that the new Filter has the new "channel" and "channels" operators. - terms = [{operator: "channel", operand: "devel"}]; + terms = [{operator: "channel", operand: devel_id.toString()}]; hash = hash_util.search_terms_to_hash(terms); - assert.equal(hash, "#narrow/stream/devel"); + assert.equal(hash, "#narrow/stream/100-devel"); narrow = hash_util.parse_narrow(hash.split("/")); - assert.deepEqual(narrow, [{operator: "stream", operand: "devel", negated: false}]); + assert.deepEqual(narrow, [{operator: "stream", operand: devel_id.toString(), negated: false}]); filter = new Filter(narrow); - assert.deepEqual(filter.terms(), [{operator: "channel", operand: "devel", negated: false}]); + assert.deepEqual(filter.terms(), [ + {operator: "channel", operand: devel_id.toString(), negated: false}, + ]); terms = [{operator: "channels", operand: "public"}]; hash = hash_util.search_terms_to_hash(terms); @@ -108,23 +122,28 @@ run_test("stream_to_channel_rename", () => { // Confirm that a narrow URL with "channel" and an enocoded stream/channel ID, // will be decoded correctly. + const test_stream_id = 34; const test_channel = { name: "decode", - stream_id: 34, + stream_id: test_stream_id, }; stream_data.add_sub(test_channel); hash = "#narrow/channel/34-decode"; narrow = hash_util.parse_narrow(hash.split("/")); - assert.deepEqual(narrow, [{operator: "channel", operand: "decode", negated: false}]); + assert.deepEqual(narrow, [ + {operator: "channel", operand: test_stream_id.toString(), negated: false}, + ]); filter = new Filter(narrow); - assert.deepEqual(filter.terms(), [{operator: "channel", operand: "decode", negated: false}]); + assert.deepEqual(filter.terms(), [ + {operator: "channel", operand: test_stream_id.toString(), negated: false}, + ]); }); run_test("terms_trailing_slash", () => { - const hash = "#narrow/stream/devel/topic/algol/"; + const hash = "#narrow/stream/100-devel/topic/algol/"; const narrow = hash_util.parse_narrow(hash.split("/")); assert.deepEqual(narrow, [ - {operator: "stream", operand: "devel", negated: false}, + {operator: "stream", operand: devel_id.toString(), negated: false}, {operator: "topic", operand: "algol", negated: false}, ]); }); @@ -270,6 +289,12 @@ run_test("hash_interactions", ({override, override_rewire}) => { ]); assert.equal(window.location.hash, "#recent"); + const denmark_id = 1; + stream_data.add_sub({ + subscribed: true, + name: "Denmark", + stream_id: denmark_id, + }); window.location.hash = "#narrow/stream/Denmark"; helper.clear_events(); @@ -280,7 +305,7 @@ run_test("hash_interactions", ({override, override_rewire}) => { "message_view.show", ]); let terms = helper.get_narrow_terms(); - assert.equal(terms[0].operand, "Denmark"); + assert.equal(terms[0].operand, denmark_id.toString()); window.location.hash = "#narrow"; diff --git a/web/tests/message_view.test.js b/web/tests/message_view.test.js index 6311bd4e1f..2dcdf965ac 100644 --- a/web/tests/message_view.test.js +++ b/web/tests/message_view.test.js @@ -242,7 +242,7 @@ run_test("show_empty_narrow_message", ({mock_template}) => { ); // for non-existent or private stream - set_filter([["stream", "Foo"]]); + set_filter([["stream", "999"]]); narrow_banner.show_empty_narrow_message(); assert.equal( $(".empty_feed_notice_main").html(), @@ -250,8 +250,9 @@ run_test("show_empty_narrow_message", ({mock_template}) => { ); // for non-subbed public stream - stream_data.add_sub({name: "ROME", stream_id: 99}); - set_filter([["stream", "Rome"]]); + const rome_id = 99; + stream_data.add_sub({name: "ROME", stream_id: rome_id}); + set_filter([["stream", rome_id.toString()]]); narrow_banner.show_empty_narrow_message(); assert.equal( $(".empty_feed_notice_main").html(), @@ -263,7 +264,7 @@ run_test("show_empty_narrow_message", ({mock_template}) => { // for non-web-public stream for spectator page_params.is_spectator = true; - set_filter([["stream", "Rome"]]); + set_filter([["stream", rome_id.toString()]]); narrow_banner.show_empty_narrow_message(); assert.equal( $(".empty_feed_notice_main").html(), @@ -274,7 +275,7 @@ run_test("show_empty_narrow_message", ({mock_template}) => { ); set_filter([ - ["stream", "Rome"], + ["stream", rome_id.toString()], ["topic", "foo"], ]); narrow_banner.show_empty_narrow_message(); @@ -287,9 +288,10 @@ run_test("show_empty_narrow_message", ({mock_template}) => { ); // for web-public stream for spectator - stream_data.add_sub({name: "web-public-stream", stream_id: 1231, is_web_public: true}); + const web_public_id = 1231; + stream_data.add_sub({name: "web-public-stream", stream_id: web_public_id, is_web_public: true}); set_filter([ - ["stream", "web-public-stream"], + ["stream", web_public_id.toString()], ["topic", "foo"], ]); narrow_banner.show_empty_narrow_message(); @@ -524,7 +526,7 @@ run_test("show_empty_narrow_message", ({mock_template}) => { set_filter([ ["sender", "alice@example.com"], - ["stream", "Rome"], + ["stream", rome_id.toString()], ]); narrow_banner.show_empty_narrow_message(); assert.equal( @@ -542,14 +544,15 @@ run_test("show_empty_narrow_message", ({mock_template}) => { ), ); + const my_stream_id = 103; const my_stream = { name: "my stream", - stream_id: 103, + stream_id: my_stream_id, }; stream_data.add_sub(my_stream); stream_data.subscribe_myself(my_stream); - set_filter([["stream", "my stream"]]); + set_filter([["stream", my_stream_id.toString()]]); narrow_banner.show_empty_narrow_message(); assert.equal( $(".empty_feed_notice_main").html(), @@ -617,6 +620,8 @@ run_test("show_search_stopwords", ({mock_template}) => { empty_narrow_html("translated: No search results.", undefined, expected_search_data), ); + const streamA_id = 88; + stream_data.add_sub({name: "streamA", stream_id: streamA_id}); const expected_stream_search_data = { has_stop_word: true, stream_query: "streamA", @@ -627,7 +632,7 @@ run_test("show_search_stopwords", ({mock_template}) => { ], }; set_filter([ - ["stream", "streamA"], + ["stream", streamA_id.toString()], ["search", "what about grail"], ]); narrow_banner.show_empty_narrow_message(); @@ -647,7 +652,7 @@ run_test("show_search_stopwords", ({mock_template}) => { ], }; set_filter([ - ["stream", "streamA"], + ["stream", streamA_id.toString()], ["topic", "topicA"], ["search", "what about grail"], ]); @@ -666,12 +671,14 @@ run_test("show_invalid_narrow_message", ({mock_template}) => { message_lists.set_current(undefined); mock_template("empty_feed_notice.hbs", true, (_data, html) => html); - stream_data.add_sub({name: "streamA", stream_id: 88}); - stream_data.add_sub({name: "streamB", stream_id: 77}); + const streamA_id = 88; + const streamB_id = 77; + stream_data.add_sub({name: "streamA", stream_id: streamA_id}); + stream_data.add_sub({name: "streamB", stream_id: streamB_id}); set_filter([ - ["stream", "streamA"], - ["stream", "streamB"], + ["stream", streamA_id.toString()], + ["stream", streamB_id.toString()], ]); narrow_banner.show_empty_narrow_message(); assert.equal( @@ -734,7 +741,8 @@ run_test("narrow_to_compose_target streams", ({override_rewire}) => { }); compose_state.set_message_type("stream"); - stream_data.add_sub({name: "ROME", stream_id: 99}); + const rome_id = 99; + stream_data.add_sub({name: "ROME", stream_id: rome_id}); compose_state.set_stream_id(99); // Test with existing topic @@ -744,7 +752,7 @@ run_test("narrow_to_compose_target streams", ({override_rewire}) => { assert.equal(args.called, true); assert.equal(args.opts.trigger, "narrow_to_compose_target"); assert.deepEqual(args.terms, [ - {operator: "channel", operand: "ROME"}, + {operator: "channel", operand: rome_id.toString()}, {operator: "topic", operand: "one"}, ]); @@ -754,7 +762,7 @@ run_test("narrow_to_compose_target streams", ({override_rewire}) => { message_view.to_compose_target(); assert.equal(args.called, true); assert.deepEqual(args.terms, [ - {operator: "channel", operand: "ROME"}, + {operator: "channel", operand: rome_id.toString()}, {operator: "topic", operand: "four"}, ]); @@ -763,14 +771,14 @@ run_test("narrow_to_compose_target streams", ({override_rewire}) => { args.called = false; message_view.to_compose_target(); assert.equal(args.called, true); - assert.deepEqual(args.terms, [{operator: "channel", operand: "ROME"}]); + assert.deepEqual(args.terms, [{operator: "channel", operand: rome_id.toString()}]); // Test with no topic compose_state.topic(undefined); args.called = false; message_view.to_compose_target(); assert.equal(args.called, true); - assert.deepEqual(args.terms, [{operator: "channel", operand: "ROME"}]); + assert.deepEqual(args.terms, [{operator: "channel", operand: rome_id.toString()}]); }); run_test("narrow_to_compose_target direct messages", ({override, override_rewire}) => { @@ -851,26 +859,24 @@ run_test("narrow_compute_title", () => { assert.equal(narrow_title.compute_narrow_title(filter), "translated: Messages sent by you"); // Stream narrows + const foo_stream_id = 43; const sub = { name: "Foo", - stream_id: 43, + stream_id: foo_stream_id, }; stream_data.add_sub(sub); filter = new Filter([ - {operator: "stream", operand: "foo"}, + {operator: "stream", operand: foo_stream_id.toString()}, {operator: "topic", operand: "bar"}, ]); assert.equal(narrow_title.compute_narrow_title(filter), "#Foo > bar"); - filter = new Filter([{operator: "stream", operand: "foo"}]); + filter = new Filter([{operator: "stream", operand: foo_stream_id.toString()}]); assert.equal(narrow_title.compute_narrow_title(filter), "#Foo"); filter = new Filter([{operator: "stream", operand: "Elephant"}]); - assert.equal( - narrow_title.compute_narrow_title(filter), - "translated: Unknown channel #Elephant", - ); + assert.equal(narrow_title.compute_narrow_title(filter), "translated: Unknown channel"); // Direct messages with narrows const joe = { diff --git a/web/tests/narrow_activate.test.js b/web/tests/narrow_activate.test.js index 3a26d262c9..06f7889734 100644 --- a/web/tests/narrow_activate.test.js +++ b/web/tests/narrow_activate.test.js @@ -171,7 +171,7 @@ run_test("basics", ({override, override_rewire}) => { override_rewire(message_view, "try_rendering_locally_for_same_narrow", noop); const helper = test_helper({override}); - const terms = [{operator: "stream", operand: "Denmark"}]; + const terms = [{operator: "stream", operand: denmark.stream_id.toString()}]; const selected_id = 1000; diff --git a/web/tests/narrow_state.test.js b/web/tests/narrow_state.test.js index cab6a9f9c8..101b3591a5 100644 --- a/web/tests/narrow_state.test.js +++ b/web/tests/narrow_state.test.js @@ -40,26 +40,27 @@ test("stream", () => { assert.ok(!narrow_state.filter()); assert.equal(narrow_state.stream_id(), undefined); - const test_stream = {name: "Test", stream_id: 15}; + const test_stream_id = 15; + const test_stream = {name: "Test", stream_id: test_stream_id}; stream_data.add_sub(test_stream); assert.ok(!narrow_state.is_for_stream_id(test_stream.stream_id)); set_filter([ - ["stream", "Test"], + ["stream", test_stream_id.toString()], ["topic", "Bar"], ["search", "yo"], ]); assert.ok(narrow_state.filter()); assert.equal(narrow_state.stream_name(), "Test"); - assert.equal(narrow_state.stream_id(), 15); + assert.equal(narrow_state.stream_id(), test_stream_id); assert.equal(narrow_state.stream_sub().stream_id, test_stream.stream_id); assert.equal(narrow_state.topic(), "Bar"); assert.ok(narrow_state.is_for_stream_id(test_stream.stream_id)); const expected_terms = [ - {negated: false, operator: "channel", operand: "Test"}, + {negated: false, operator: "channel", operand: test_stream_id.toString()}, {negated: false, operator: "topic", operand: "Bar"}, {negated: false, operator: "search", operand: "yo"}, ]; @@ -68,6 +69,8 @@ test("stream", () => { assert.deepEqual(public_terms, expected_terms); }); +const foo_stream_id = 72; +const foo_stream = {name: "Foo", stream_id: foo_stream_id}; test("narrowed", () => { assert.ok(!narrow_state.narrowed_to_pms()); assert.ok(!narrow_state.narrowed_by_reply()); @@ -76,6 +79,8 @@ test("narrowed", () => { assert.ok(!narrow_state.narrowed_by_stream_reply()); assert.equal(narrow_state.stream_sub(), undefined); + stream_data.add_sub(foo_stream); + set_filter([["stream", "Foo"]]); assert.ok(!narrow_state.narrowed_to_pms()); assert.ok(!narrow_state.narrowed_by_reply()); @@ -91,7 +96,7 @@ test("narrowed", () => { assert.ok(!narrow_state.narrowed_by_stream_reply()); set_filter([ - ["stream", "Foo"], + ["stream", foo_stream_id.toString()], ["topic", "bar"], ]); assert.ok(!narrow_state.narrowed_to_pms()); @@ -117,14 +122,14 @@ test("narrowed", () => { test("terms", () => { set_filter([ - ["stream", "Foo"], + ["stream", foo_stream_id.toString()], ["topic", "Bar"], ["search", "Yo"], ]); let result = narrow_state.search_terms(); assert.equal(result.length, 3); assert.equal(result[0].operator, "channel"); - assert.equal(result[0].operand, "Foo"); + assert.equal(result[0].operand, foo_stream_id.toString()); assert.equal(result[1].operator, "topic"); assert.equal(result[1].operand, "Bar"); @@ -136,11 +141,11 @@ test("terms", () => { result = narrow_state.search_terms(); assert.equal(result.length, 0); - page_params.narrow = [{operator: "stream", operand: "Foo"}]; + page_params.narrow = [{operator: "stream", operand: foo_stream_id.toString()}]; result = narrow_state.search_terms(); assert.equal(result.length, 1); assert.equal(result[0].operator, "channel"); - assert.equal(result[0].operand, "Foo"); + assert.equal(result[0].operand, foo_stream_id.toString()); }); test("excludes_muted_topics", () => { @@ -169,7 +174,7 @@ test("excludes_muted_topics", () => { test("set_compose_defaults", () => { set_filter([ - ["stream", "Foo"], + ["stream", foo_stream_id.toString()], ["topic", "Bar"], ]); @@ -178,10 +183,9 @@ test("set_compose_defaults", () => { assert.equal(stream_and_topic.stream_id, undefined); assert.equal(stream_and_topic.topic, "Bar"); - const test_stream = {name: "Foo", stream_id: 72}; - stream_data.add_sub(test_stream); + stream_data.add_sub(foo_stream); stream_and_topic = narrow_state.set_compose_defaults(); - assert.equal(stream_and_topic.stream_id, 72); + assert.equal(stream_and_topic.stream_id, foo_stream_id); assert.equal(stream_and_topic.topic, "Bar"); set_filter([["dm", "foo@bar.com"]]); @@ -212,11 +216,12 @@ test("set_compose_defaults", () => { ]); assert.deepEqual(narrow_state.set_compose_defaults(), {}); - stream_data.add_sub({name: "ROME", stream_id: 99}); - set_filter([["stream", "rome"]]); + const rome_id = 99; + stream_data.add_sub({name: "ROME", stream_id: rome_id}); + set_filter([["stream", rome_id.toString()]]); const stream_test = narrow_state.set_compose_defaults(); - assert.equal(stream_test.stream_id, 99); + assert.equal(stream_test.stream_id, rome_id); }); test("update_email", () => { @@ -241,7 +246,7 @@ test("update_email", () => { test("topic", () => { set_filter([ - ["stream", "Foo"], + ["stream", foo_stream.stream_id.toString()], ["topic", "Bar"], ]); assert.equal(narrow_state.topic(), "Bar"); @@ -271,14 +276,15 @@ test("stream_sub", () => { assert.equal(narrow_state.stream_sub(), undefined); set_filter([ - ["stream", "Foo"], + ["stream", "55"], ["topic", "Bar"], ]); - assert.equal(narrow_state.stream_name(), "Foo"); + assert.equal(narrow_state.stream_name(), undefined); assert.equal(narrow_state.stream_sub(), undefined); const sub = {name: "Foo", stream_id: 55}; stream_data.add_sub(sub); + assert.equal(narrow_state.stream_name(), "Foo"); assert.deepEqual(narrow_state.stream_sub(), sub); set_filter([ @@ -297,7 +303,7 @@ test("pm_ids_string", () => { assert.deepStrictEqual(narrow_state.pm_ids_set(), new Set()); set_filter([ - ["stream", "Foo"], + ["stream", foo_stream.stream_id.toString()], ["topic", "Bar"], ]); assert.equal(narrow_state.pm_ids_string(), undefined); diff --git a/web/tests/narrow_unread.test.js b/web/tests/narrow_unread.test.js index 98902d2365..7e1de5f8e2 100644 --- a/web/tests/narrow_unread.test.js +++ b/web/tests/narrow_unread.test.js @@ -25,6 +25,8 @@ const alice = { full_name: "Alice", }; +const bogus_stream_id = "999999"; + people.init(); people.add_active_user(alice); @@ -111,12 +113,12 @@ run_test("get_unread_ids", () => { assert.deepEqual(unread_ids, []); assert_unread_info({flavor: "not_found"}); - terms = [{operator: "stream", operand: "bogus"}]; + terms = [{operator: "stream", operand: bogus_stream_id}]; set_filter(terms); unread_ids = candidate_ids(); assert.deepEqual(unread_ids, []); - terms = [{operator: "stream", operand: sub.name}]; + terms = [{operator: "stream", operand: sub.stream_id.toString()}]; set_filter(terms); unread_ids = candidate_ids(); assert.deepEqual(unread_ids, []); @@ -131,7 +133,7 @@ run_test("get_unread_ids", () => { }); terms = [ - {operator: "stream", operand: "bogus"}, + {operator: "stream", operand: bogus_stream_id}, {operator: "topic", operand: "my topic"}, ]; set_filter(terms); @@ -139,7 +141,7 @@ run_test("get_unread_ids", () => { assert.deepEqual(unread_ids, []); terms = [ - {operator: "stream", operand: sub.name}, + {operator: "stream", operand: sub.stream_id.toString()}, {operator: "topic", operand: "my topic"}, ]; set_filter(terms); @@ -226,7 +228,7 @@ run_test("get_unread_ids", () => { // destination topic. unread.process_loaded_messages([other_topic_message]); terms = [ - {operator: "channel", operand: sub.name}, + {operator: "channel", operand: sub.stream_id.toString()}, {operator: "topic", operand: "another topic"}, ]; set_filter(terms); @@ -234,7 +236,7 @@ run_test("get_unread_ids", () => { assert.deepEqual(unread_ids, [other_topic_message.id]); terms = [ - {operator: "channel", operand: sub.name}, + {operator: "channel", operand: sub.stream_id.toString()}, {operator: "topic", operand: "another topic"}, {operator: "with", operand: stream_msg.id}, ]; @@ -243,7 +245,7 @@ run_test("get_unread_ids", () => { assert.deepEqual(unread_ids, [stream_msg.id]); terms = [ - {operator: "channel", operand: sub.name}, + {operator: "channel", operand: sub.stream_id.toString()}, {operator: "topic", operand: "another topic"}, {operator: "with", operand: private_msg.id}, ]; diff --git a/web/tests/peer_data.test.js b/web/tests/peer_data.test.js index 7324f4e965..dbf2034525 100644 --- a/web/tests/peer_data.test.js +++ b/web/tests/peer_data.test.js @@ -66,19 +66,19 @@ test("unsubscribe", () => { stream_data.add_sub(devel); // verify clean slate - assert.ok(!stream_data.is_subscribed_by_name("devel")); + assert.ok(!stream_data.is_subscribed(devel.stream_id)); // set up our subscription devel.subscribed = true; peer_data.set_subscribers(devel.stream_id, [me.user_id]); // ensure our setup is accurate - assert.ok(stream_data.is_subscribed_by_name("devel")); + assert.ok(stream_data.is_subscribed(devel.stream_id)); // DO THE UNSUBSCRIBE HERE stream_data.unsubscribe_myself(devel); assert.ok(!devel.subscribed); - assert.ok(!stream_data.is_subscribed_by_name("devel")); + assert.ok(!stream_data.is_subscribed(devel.stream_id)); assert.ok(!contains_sub(stream_data.subscribed_subs(), devel)); assert.ok(contains_sub(stream_data.unsubscribed_subs(), devel)); @@ -96,7 +96,7 @@ test("subscribers", () => { people.add_active_user(george); // verify setup - assert.ok(stream_data.is_subscribed_by_name(sub.name)); + assert.ok(stream_data.is_subscribed(sub.stream_id)); const stream_id = sub.stream_id; diff --git a/web/tests/search.test.js b/web/tests/search.test.js index d8b814eaa8..a5352b42a0 100644 --- a/web/tests/search.test.js +++ b/web/tests/search.test.js @@ -237,25 +237,26 @@ run_test("initialize", ({override, override_rewire, mock_template}) => { assert.equal(opts.updater("ver"), "ver"); assert.ok(!input_pill_displayed); + const verona_stream_id = verona.stream_id.toString(); terms = [ { negated: false, operator: "channel", - operand: "Verona", + operand: verona_stream_id, }, ]; expected_pill_display_value = "channel: Verona"; _setup(terms); input_pill_displayed = false; mock_pill_removes(search.search_pill_widget); - assert.equal(opts.updater("channel:Verona"), ""); + assert.equal(opts.updater(`channel:${verona_stream_id}`), ""); assert.ok(input_pill_displayed); search.__Rewire__("is_using_input_method", true); _setup(terms); input_pill_displayed = false; mock_pill_removes(search.search_pill_widget); - assert.equal(opts.updater("channel:Verona"), ""); + assert.equal(opts.updater(`channel:${verona_stream_id}`), ""); assert.ok(input_pill_displayed); } return { diff --git a/web/tests/search_suggestion.test.js b/web/tests/search_suggestion.test.js index ca53129239..5ff8f6bcf2 100644 --- a/web/tests/search_suggestion.test.js +++ b/web/tests/search_suggestion.test.js @@ -50,6 +50,12 @@ const jeff = { const example_avatar_url = "http://example.com/example.png"; +let _stream_id = 0; +function new_stream_id() { + _stream_id += 1; + return _stream_id; +} + function init() { current_user.is_admin = true; @@ -81,7 +87,7 @@ function test(label, f) { test("basic_get_suggestions", ({override}) => { const query = "fred"; - override(narrow_state, "stream_name", () => "office"); + override(narrow_state, "stream_id", noop); const suggestions = get_suggestions(query); @@ -116,14 +122,17 @@ test("get_is_suggestions_for_spectator", () => { test("subset_suggestions", ({mock_template}) => { mock_template("search_description.hbs", true, (_data, html) => html); - const query = "channel:Denmark topic:Hamlet shakespeare"; + const denmark_id = new_stream_id(); + const sub = {name: "Denmark", stream_id: denmark_id}; + stream_data.add_sub(sub); + const query = `channel:${denmark_id} topic:Hamlet shakespeare`; const suggestions = get_suggestions(query); const expected = [ - "channel:Denmark topic:Hamlet shakespeare", - "channel:Denmark topic:Hamlet", - "channel:Denmark", + `channel:${denmark_id} topic:Hamlet shakespeare`, + `channel:${denmark_id} topic:Hamlet`, + `channel:${denmark_id}`, ]; assert.deepEqual(suggestions.strings, expected); @@ -249,7 +258,7 @@ test("dm_suggestions", ({override, mock_template}) => { // "pm-with" operator returns search result // and "dm" operator as a suggestions - override(narrow_state, "stream_name", () => undefined); + override(narrow_state, "stream_id", () => undefined); query = "pm-with"; suggestions = get_suggestions(query); expected = ["pm-with", "dm:"]; @@ -330,10 +339,10 @@ test("group_suggestions", ({mock_template}) => { assert.deepEqual(suggestions.strings, expected); // Doesn't show dms because it's invalid in combination - // with a channel. - query = "channel:Denmark has:link dm:bob@zulip.com,Smit"; + // with a channel. (Random channel id.) + query = "channel:66 has:link dm:bob@zulip.com,Smit"; suggestions = get_suggestions(query); - expected = ["channel:Denmark has:link", "channel:Denmark"]; + expected = ["channel:66 has:link", "channel:66"]; assert.deepEqual(suggestions.strings, expected); // Invalid emails don't give suggestions @@ -346,8 +355,10 @@ test("group_suggestions", ({mock_template}) => { test("empty_query_suggestions", () => { const query = ""; - stream_data.add_sub({stream_id: 44, name: "devel", subscribed: true}); - stream_data.add_sub({stream_id: 77, name: "office", subscribed: true}); + const devel_id = new_stream_id(); + const office_id = new_stream_id(); + stream_data.add_sub({stream_id: devel_id, name: "devel", subscribed: true}); + stream_data.add_sub({stream_id: office_id, name: "office", subscribed: true}); const suggestions = get_suggestions(query); @@ -361,8 +372,8 @@ test("empty_query_suggestions", () => { "is:unread", "is:resolved", "sender:myself@zulip.com", - "channel:devel", - "channel:office", + `channel:${devel_id}`, + `channel:${office_id}`, "has:link", "has:image", "has:attachment", @@ -395,7 +406,7 @@ test("has_suggestions", ({override, mock_template}) => { let query = "h"; stream_data.add_sub({stream_id: 44, name: "devel", subscribed: true}); stream_data.add_sub({stream_id: 77, name: "office", subscribed: true}); - override(narrow_state, "stream_name", noop); + override(narrow_state, "stream_id", noop); let suggestions = get_suggestions(query); let expected = ["h", "has:link", "has:image", "has:attachment", "has:reaction"]; @@ -439,13 +450,10 @@ test("has_suggestions", ({override, mock_template}) => { expected = ["att", "has:attachment"]; assert.deepEqual(suggestions.strings, expected); - query = "channel:Denmark is:alerted has:lin"; + // 66 is misc channel id. + query = "channel:66 is:alerted has:lin"; suggestions = get_suggestions(query); - expected = [ - "channel:Denmark is:alerted has:link", - "channel:Denmark is:alerted", - "channel:Denmark", - ]; + expected = ["channel:66 is:alerted has:link", "channel:66 is:alerted", "channel:66"]; assert.deepEqual(suggestions.strings, expected); }); @@ -453,9 +461,7 @@ test("check_is_suggestions", ({override, mock_template}) => { mock_template("search_description.hbs", true, (_data, html) => html); mock_template("user_pill.hbs", true, (_data, html) => html); - stream_data.add_sub({stream_id: 44, name: "devel", subscribed: true}); - stream_data.add_sub({stream_id: 77, name: "office", subscribed: true}); - override(narrow_state, "stream_name", noop); + override(narrow_state, "stream_id", noop); let query = "i"; let suggestions = get_suggestions(query); @@ -541,20 +547,16 @@ test("check_is_suggestions", ({override, mock_template}) => { expected = ["st", "streams:public", "is:starred", "channel:"]; assert.deepEqual(suggestions.strings, expected); - query = "channel:Denmark has:link is:sta"; + query = "channel:66 has:link is:sta"; suggestions = get_suggestions(query); - expected = [ - "channel:Denmark has:link is:starred", - "channel:Denmark has:link", - "channel:Denmark", - ]; + expected = ["channel:66 has:link is:starred", "channel:66 has:link", "channel:66"]; assert.deepEqual(suggestions.strings, expected); }); test("sent_by_me_suggestions", ({override, mock_template}) => { mock_template("search_description.hbs", true, (_data, html) => html); - override(narrow_state, "stream_name", noop); + override(narrow_state, "stream_id", noop); let query = ""; let suggestions = get_suggestions(query); @@ -604,13 +606,16 @@ test("sent_by_me_suggestions", ({override, mock_template}) => { expected = ["-sent", "-sender:myself@zulip.com"]; assert.deepEqual(suggestions.strings, expected); - query = "channel:Denmark topic:Denmark1 sent"; + const denmark_id = new_stream_id(); + const sub = {name: "Denmark", stream_id: denmark_id}; + stream_data.add_sub(sub); + query = `channel:${denmark_id} topic:Denmark1 sent`; suggestions = get_suggestions(query); expected = [ - "channel:Denmark topic:Denmark1 sent", - "channel:Denmark topic:Denmark1 sender:myself@zulip.com", - "channel:Denmark topic:Denmark1", - "channel:Denmark", + `channel:${denmark_id} topic:Denmark1 sent`, + `channel:${denmark_id} topic:Denmark1 sender:myself@zulip.com`, + `channel:${denmark_id} topic:Denmark1`, + `channel:${denmark_id}`, ]; assert.deepEqual(suggestions.strings, expected); @@ -627,11 +632,11 @@ test("topic_suggestions", ({override, mock_template}) => { let expected; override(stream_topic_history_util, "get_server_history", noop); - stream_data.add_sub({stream_id: 77, name: "office", subscribed: true}); - override(narrow_state, "stream_name", () => "office"); + const office_id = new_stream_id(); + stream_data.add_sub({stream_id: office_id, name: "office", subscribed: true}); + override(narrow_state, "stream_id", () => office_id); - const devel_id = 44; - const office_id = 77; + const devel_id = new_stream_id(); stream_data.add_sub({stream_id: devel_id, name: "devel", subscribed: true}); stream_data.add_sub({stream_id: office_id, name: "office", subscribed: true}); @@ -657,8 +662,8 @@ test("topic_suggestions", ({override, mock_template}) => { "dm:ted@zulip.com", "sender:ted@zulip.com", "dm-including:ted@zulip.com", - "channel:office topic:team", - "channel:office topic:test", + `channel:${office_id} topic:team`, + `channel:${office_id} topic:test`, ]; assert.deepEqual(suggestions.strings, expected); @@ -666,40 +671,56 @@ test("topic_suggestions", ({override, mock_template}) => { return suggestions.lookup_table.get(q).description_html; } assert.equal(describe("te"), "Search for te"); - assert.equal(describe("channel:office topic:team"), "Channel office > team"); + assert.equal(describe(`channel:${office_id} topic:team`), "Channel office > team"); - suggestions = get_suggestions("topic:staplers channel:office"); - expected = ["topic:staplers channel:office", "topic:staplers"]; + suggestions = get_suggestions(`topic:staplers channel:${office_id}`); + expected = [`topic:staplers channel:${office_id}`, "topic:staplers"]; assert.deepEqual(suggestions.strings, expected); - suggestions = get_suggestions("channel:devel topic:"); - expected = ["channel:devel topic:", "channel:devel topic:REXX", "channel:devel"]; + suggestions = get_suggestions(`channel:${devel_id} topic:`); + expected = [ + `channel:${devel_id} topic:`, + `channel:${devel_id} topic:REXX`, + `channel:${devel_id}`, + ]; assert.deepEqual(suggestions.strings, expected); - suggestions = get_suggestions("channel:devel -topic:"); - expected = ["channel:devel -topic:", "channel:devel -topic:REXX", "channel:devel"]; + suggestions = get_suggestions(`channel:${devel_id} -topic:`); + expected = [ + `channel:${devel_id} -topic:`, + `channel:${devel_id} -topic:REXX`, + `channel:${devel_id}`, + ]; assert.deepEqual(suggestions.strings, expected); suggestions = get_suggestions("-topic:te"); - expected = ["-topic:te", "channel:office -topic:team", "channel:office -topic:test"]; + expected = [ + "-topic:te", + `channel:${office_id} -topic:team`, + `channel:${office_id} -topic:test`, + ]; assert.deepEqual(suggestions.strings, expected); - suggestions = get_suggestions("is:alerted channel:devel is:starred topic:"); + suggestions = get_suggestions(`is:alerted channel:${devel_id} is:starred topic:`); expected = [ - "is:alerted channel:devel is:starred topic:", - "is:alerted channel:devel is:starred topic:REXX", - "is:alerted channel:devel is:starred", - "is:alerted channel:devel", + `is:alerted channel:${devel_id} is:starred topic:`, + `is:alerted channel:${devel_id} is:starred topic:REXX`, + `is:alerted channel:${devel_id} is:starred`, + `is:alerted channel:${devel_id}`, "is:alerted", ]; assert.deepEqual(suggestions.strings, expected); - suggestions = get_suggestions("is:dm channel:devel topic:"); - expected = ["is:dm channel:devel topic:", "is:dm channel:devel", "is:dm"]; + suggestions = get_suggestions(`is:dm channel:${devel_id} topic:`); + expected = [`is:dm channel:${devel_id} topic:`, `is:dm channel:${devel_id}`, `is:dm`]; assert.deepEqual(suggestions.strings, expected); - suggestions = get_suggestions("topic:REXX channel:devel topic:"); - expected = ["topic:REXX channel:devel topic:", "topic:REXX channel:devel", "topic:REXX"]; + suggestions = get_suggestions(`topic:REXX channel:${devel_id} topic:`); + expected = [ + `topic:REXX channel:${devel_id} topic:`, + `topic:REXX channel:${devel_id}`, + "topic:REXX", + ]; assert.deepEqual(suggestions.strings, expected); }); @@ -748,38 +769,41 @@ test("topic_suggestions (limits)", () => { test("whitespace_glitch", ({override, mock_template}) => { mock_template("search_description.hbs", true, (_data, html) => html); + const office_stream_id = new_stream_id(); const query = "channel:office "; // note trailing space override(stream_topic_history_util, "get_server_history", noop); - stream_data.add_sub({stream_id: 77, name: "office", subscribed: true}); + stream_data.add_sub({stream_id: office_stream_id, name: "office", subscribed: true}); const suggestions = get_suggestions(query); - const expected = ["channel:office"]; + const expected = [`channel:${office_stream_id}`]; assert.deepEqual(suggestions.strings, expected); }); test("channel_completion", ({override}) => { - stream_data.add_sub({stream_id: 77, name: "office", subscribed: true}); - stream_data.add_sub({stream_id: 88, name: "dev help", subscribed: true}); + const office_stream_id = new_stream_id(); + stream_data.add_sub({stream_id: office_stream_id, name: "office", subscribed: true}); + const dev_help_stream_id = new_stream_id(); + stream_data.add_sub({stream_id: dev_help_stream_id, name: "dev help", subscribed: true}); - override(narrow_state, "stream_name", noop); + override(narrow_state, "stream_id", noop); let query = "channel:of"; let suggestions = get_suggestions(query); - let expected = ["channel:office"]; + let expected = [`channel:${office_stream_id}`]; assert.deepEqual(suggestions.strings, expected); query = "-channel:of"; suggestions = get_suggestions(query); - expected = ["-channel:office"]; + expected = [`-channel:${office_stream_id}`]; assert.deepEqual(suggestions.strings, expected); query = "hel"; suggestions = get_suggestions(query); - expected = ["hel", "channel:dev+help"]; + expected = ["hel", `channel:${dev_help_stream_id}`]; assert.deepEqual(suggestions.strings, expected); }); @@ -789,7 +813,7 @@ test("people_suggestions", ({override, mock_template}) => { let query = "te"; - override(narrow_state, "stream_name", noop); + override(narrow_state, "stream_id", noop); const ted = { email: "ted@zulip.com", @@ -959,7 +983,7 @@ test("people_suggestions", ({override, mock_template}) => { test("operator_suggestions", ({override, mock_template}) => { mock_template("search_description.hbs", true, (_data, html) => html); - override(narrow_state, "stream_name", () => undefined); + override(narrow_state, "stream_id", () => undefined); // Completed operator should return nothing let query = "channel:"; @@ -977,37 +1001,40 @@ test("operator_suggestions", ({override, mock_template}) => { expected = ["-s", "-sender:myself@zulip.com", "-sender:", "-channel:"]; assert.deepEqual(suggestions.strings, expected); - query = "channel:Denmark is:alerted -f"; + // 66 is a misc channel id. + query = "channel:66 is:alerted -f"; suggestions = get_suggestions(query); expected = [ - "channel:Denmark is:alerted -f", - "channel:Denmark is:alerted -sender:myself@zulip.com", - "channel:Denmark is:alerted -sender:", - "channel:Denmark is:alerted", - "channel:Denmark", + "channel:66 is:alerted -f", + "channel:66 is:alerted -sender:myself@zulip.com", + "channel:66 is:alerted -sender:", + "channel:66 is:alerted", + "channel:66", ]; assert.deepEqual(suggestions.strings, expected); }); test("queries_with_spaces", () => { - stream_data.add_sub({stream_id: 77, name: "office", subscribed: true}); - stream_data.add_sub({stream_id: 88, name: "dev help", subscribed: true}); + const office_id = new_stream_id(); + stream_data.add_sub({stream_id: office_id, name: "office", subscribed: true}); + const dev_help_id = new_stream_id(); + stream_data.add_sub({stream_id: dev_help_id, name: "dev help", subscribed: true}); // test allowing spaces with quotes surrounding operand let query = 'channel:"dev he"'; let suggestions = get_suggestions(query); - let expected = ["channel:dev+help"]; + let expected = [`channel:${dev_help_id}`]; assert.deepEqual(suggestions.strings, expected); // test mismatched quote query = 'channel:"dev h'; suggestions = get_suggestions(query); - expected = ["channel:dev+help"]; + expected = [`channel:${dev_help_id}`]; assert.deepEqual(suggestions.strings, expected); // test extra space after operator still works query = "channel: offi"; suggestions = get_suggestions(query); - expected = ["channel:office"]; + expected = [`channel:${office_id}`]; assert.deepEqual(suggestions.strings, expected); }); diff --git a/web/tests/stream_data.test.js b/web/tests/stream_data.test.js index 6d3ed05808..25176a0227 100644 --- a/web/tests/stream_data.test.js +++ b/web/tests/stream_data.test.js @@ -17,6 +17,7 @@ const people = zrequire("people"); const settings_config = zrequire("settings_config"); const sub_store = zrequire("sub_store"); const stream_data = zrequire("stream_data"); +const hash_util = zrequire("hash_util"); const stream_settings_data = zrequire("stream_settings_data"); const user_groups = zrequire("user_groups"); @@ -135,10 +136,8 @@ test("basics", () => { assert.deepEqual(stream_data.get_colors(), ["red", "yellow"]); assert.deepEqual(stream_data.subscribed_stream_ids(), [social.stream_id, test.stream_id]); - assert.ok(stream_data.is_subscribed_by_name("social")); - assert.ok(stream_data.is_subscribed_by_name("Social")); - assert.ok(!stream_data.is_subscribed_by_name("Denmark")); - assert.ok(!stream_data.is_subscribed_by_name("Rome")); + assert.ok(stream_data.is_subscribed(social.stream_id)); + assert.ok(!stream_data.is_subscribed(denmark.stream_id)); assert.equal(stream_data.get_stream_privacy_policy(test.stream_id), "public"); assert.equal(stream_data.get_stream_privacy_policy(social.stream_id), "invite-only"); @@ -147,9 +146,10 @@ test("basics", () => { "invite-only-public-history", ); assert.equal(stream_data.get_stream_privacy_policy(web_public_stream.stream_id), "web-public"); - assert.ok(stream_data.is_web_public_by_stream_name(web_public_stream.name)); - assert.ok(!stream_data.is_web_public_by_stream_name(social.name)); - assert.ok(!stream_data.is_web_public_by_stream_name("unknown")); + assert.ok(stream_data.is_web_public_by_stream_id(web_public_stream.stream_id)); + assert.ok(!stream_data.is_web_public_by_stream_id(social.stream_id)); + const unknown_stream_id = 9999; + assert.ok(!stream_data.is_web_public_by_stream_id(unknown_stream_id)); assert.ok(stream_data.is_invite_only_by_stream_id(social.stream_id)); // Unknown stream id @@ -159,9 +159,6 @@ test("basics", () => { assert.equal(stream_data.get_color(undefined), "#c2c2c2"); assert.equal(stream_data.get_color(1234567), "#c2c2c2"); - assert.equal(stream_data.get_name("denMARK"), "Denmark"); - assert.equal(stream_data.get_name("unknown Stream"), "unknown Stream"); - assert.ok(!stream_data.is_muted(social.stream_id)); assert.ok(stream_data.is_muted(denmark.stream_id)); @@ -176,19 +173,37 @@ test("basics", () => { // "new" correct url formats assert.equal(stream_data.slug_to_stream_id("2-social"), 2); + assert.equal(hash_util.decode_operand("channel", "2-social"), "2"); + assert.equal(stream_data.slug_to_stream_id("2"), 2); + assert.equal(hash_util.decode_operand("channel", "2"), "2"); + // we still get 2 because it's a valid stream id assert.equal(stream_data.slug_to_stream_id("2-whatever"), 2); - // invalid stream id - assert.equal(stream_data.slug_to_stream_id("999-social"), undefined); - // legacy - assert.equal(stream_data.slug_to_stream_id("social"), 2); + assert.equal(stream_data.slug_to_stream_id("2-"), 2); + + // legacy, we recognize "social" as a valid channel name + assert.equal(stream_data.slug_to_stream_id("social"), 2); + assert.equal(hash_util.decode_operand("channel", "social"), "2"); + + // These aren't prepended with valid ids nor valid channel names. We + // don't get any stream id from the slug, and the decoded operand (the + // only caller of `slug_to_stream_id`) returns an empty string (which we + // don't display anywhere, since the channel is invalid). + assert.equal(stream_data.slug_to_stream_id("999-social"), undefined); + assert.equal(hash_util.decode_operand("channel", "999-social"), ""); - // invalid formats - assert.equal(stream_data.slug_to_stream_id("25-or-6-to-4"), undefined); - assert.equal(stream_data.slug_to_stream_id("2something"), undefined); assert.equal(stream_data.slug_to_stream_id("99-whatever"), undefined); + assert.equal(hash_util.decode_operand("channel", "99-whatever"), ""); + + assert.equal(stream_data.slug_to_stream_id("25-or-6-to-4"), undefined); + assert.equal(hash_util.decode_operand("channel", "25-or-6-to-4"), ""); + + assert.equal(stream_data.slug_to_stream_id("2something"), undefined); + assert.equal(hash_util.decode_operand("channel", "2something"), ""); + assert.equal(stream_data.slug_to_stream_id("99whatever"), undefined); + assert.equal(hash_util.decode_operand("channel", "99whatever"), ""); // sub_store assert.equal(sub_store.get(-3), undefined); @@ -528,12 +543,12 @@ test("delete_sub", () => { stream_data.add_sub(canada); - assert.ok(stream_data.is_subscribed_by_name("Canada")); + assert.ok(stream_data.is_subscribed(canada.stream_id)); assert.equal(stream_data.get_sub("Canada").stream_id, canada.stream_id); assert.equal(sub_store.get(canada.stream_id).name, "Canada"); stream_data.delete_sub(canada.stream_id); - assert.ok(!stream_data.is_subscribed_by_name("Canada")); + assert.ok(!stream_data.is_subscribed(canada.stream_id)); assert.ok(!stream_data.get_sub("Canada")); assert.ok(!sub_store.get(canada.stream_id)); diff --git a/web/tests/stream_events.test.js b/web/tests/stream_events.test.js index 24fbec632b..2ceba6c010 100644 --- a/web/tests/stream_events.test.js +++ b/web/tests/stream_events.test.js @@ -83,7 +83,7 @@ const frontend = { }; function narrow_to_frontend() { - const filter = new Filter([{operator: "stream", operand: "frontend"}]); + const filter = new Filter([{operator: "stream", operand: frontend.stream_id.toString()}]); message_lists.current = { data: { filter, @@ -382,12 +382,12 @@ test("marked_subscribed (emails)", ({override}) => { $("#channels_overlay_container .stream-row:not(.notdisplayed)").length = 0; - assert.ok(!stream_data.is_subscribed_by_name(sub.name)); + assert.ok(!stream_data.is_subscribed(sub.stream_id)); const user_ids = [15, 20, 25, me.user_id]; stream_events.mark_subscribed(sub, user_ids, ""); assert.deepEqual(new Set(peer_data.get_subscribers(sub.stream_id)), new Set(user_ids)); - assert.ok(stream_data.is_subscribed_by_name(sub.name)); + assert.ok(stream_data.is_subscribed(sub.stream_id)); const args = subs_stub.get_args("sub"); assert.deepEqual(sub, args.sub); diff --git a/web/tests/stream_list.test.js b/web/tests/stream_list.test.js index b6203cc1d1..9810c74f98 100644 --- a/web/tests/stream_list.test.js +++ b/web/tests/stream_list.test.js @@ -429,19 +429,19 @@ test_ui("narrowing", ({mock_template}) => { let filter; - filter = new Filter([{operator: "stream", operand: "devel"}]); + filter = new Filter([{operator: "stream", operand: develSub.stream_id.toString()}]); stream_list.handle_narrow_activated(filter); assert.ok($("").hasClass("active-filter")); filter = new Filter([ - {operator: "stream", operand: "cars"}, + {operator: "stream", operand: carSub.stream_id.toString()}, {operator: "topic", operand: "sedans"}, ]); stream_list.handle_narrow_activated(filter); assert.ok(!$("ul.filters li").hasClass("active-filter")); assert.ok(!$("").hasClass("active-filter")); // false because of topic - filter = new Filter([{operator: "stream", operand: "cars"}]); + filter = new Filter([{operator: "stream", operand: carSub.stream_id.toString()}]); stream_list.handle_narrow_activated(filter); assert.ok(!$("ul.filters li").hasClass("active-filter")); assert.ok($("").hasClass("active-filter"));