mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-03 21:43:21 +00:00 
			
		
		
		
	Previously, for spectators, when a negated search operator was present, navigating forward or backward triggered the "signup/login" modal. This commit fixes this issue.
		
			
				
	
	
		
			454 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			454 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
"use strict";
 | 
						|
 | 
						|
const assert = require("node:assert/strict");
 | 
						|
 | 
						|
const {mock_esm, set_global, zrequire} = require("./lib/namespace.cjs");
 | 
						|
const {run_test} = require("./lib/test.cjs");
 | 
						|
const blueslip = require("./lib/zblueslip.cjs");
 | 
						|
const $ = require("./lib/zjquery.cjs");
 | 
						|
 | 
						|
let $window_stub;
 | 
						|
set_global("to_$", () => $window_stub);
 | 
						|
 | 
						|
set_global("document", "document-stub");
 | 
						|
const history = set_global("history", {state: null});
 | 
						|
 | 
						|
const admin = mock_esm("../src/admin");
 | 
						|
const drafts_overlay_ui = mock_esm("../src/drafts_overlay_ui");
 | 
						|
const info_overlay = mock_esm("../src/info_overlay");
 | 
						|
const message_viewport = mock_esm("../src/message_viewport");
 | 
						|
const overlays = mock_esm("../src/overlays");
 | 
						|
const popovers = mock_esm("../src/popovers");
 | 
						|
const recent_view_ui = mock_esm("../src/recent_view_ui");
 | 
						|
const settings = mock_esm("../src/settings");
 | 
						|
mock_esm("../src/settings_data", {
 | 
						|
    user_can_create_public_streams: () => true,
 | 
						|
});
 | 
						|
const stream_settings_ui = mock_esm("../src/stream_settings_ui");
 | 
						|
const ui_util = mock_esm("../src/ui_util");
 | 
						|
const ui_report = mock_esm("../src/ui_report");
 | 
						|
set_global("favicon", {});
 | 
						|
 | 
						|
const browser_history = zrequire("browser_history");
 | 
						|
const people = zrequire("people");
 | 
						|
const hash_util = zrequire("hash_util");
 | 
						|
const hashchange = zrequire("hashchange");
 | 
						|
const message_view = zrequire("../src/message_view");
 | 
						|
const stream_data = zrequire("stream_data");
 | 
						|
const {Filter} = zrequire("../src/filter");
 | 
						|
const {initialize_user_settings} = zrequire("user_settings");
 | 
						|
 | 
						|
const user_settings = {};
 | 
						|
initialize_user_settings({user_settings});
 | 
						|
 | 
						|
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_id.toString()},
 | 
						|
        {operator: "topic", operand: "algol"},
 | 
						|
    ];
 | 
						|
    hash = hash_util.search_terms_to_hash(terms);
 | 
						|
    assert.equal(hash, "#narrow/channel/100-devel/topic/algol");
 | 
						|
 | 
						|
    narrow = hash_util.parse_narrow(hash.split("/"));
 | 
						|
    assert.deepEqual(narrow, [
 | 
						|
        {operator: "channel", operand: devel_id.toString(), negated: false},
 | 
						|
        {operator: "topic", operand: "algol", negated: false},
 | 
						|
    ]);
 | 
						|
 | 
						|
    terms = [
 | 
						|
        {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/channel/100-devel/-topic/visual.20c.2B.2B");
 | 
						|
 | 
						|
    narrow = hash_util.parse_narrow(hash.split("/"));
 | 
						|
    assert.deepEqual(narrow, [
 | 
						|
        {operator: "channel", 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: florida_id,
 | 
						|
    };
 | 
						|
    stream_data.add_sub(florida_stream);
 | 
						|
    terms = [{operator: "stream", operand: florida_id.toString()}];
 | 
						|
    hash = hash_util.search_terms_to_hash(terms);
 | 
						|
    assert.equal(hash, "#narrow/channel/987-Florida.2C-USA");
 | 
						|
    narrow = hash_util.parse_narrow(hash.split("/"));
 | 
						|
    assert.deepEqual(narrow, [
 | 
						|
        {operator: "channel", operand: florida_id.toString(), negated: false},
 | 
						|
    ]);
 | 
						|
});
 | 
						|
 | 
						|
run_test("stream_to_channel_rename", () => {
 | 
						|
    let terms;
 | 
						|
    let hash;
 | 
						|
    let narrow;
 | 
						|
    let filter;
 | 
						|
 | 
						|
    // Confirm searches with "stream" and "streams" return URLs and
 | 
						|
    // Filter objects with the new "channel" and "channels" operators.
 | 
						|
    terms = [{operator: "stream", operand: devel_id.toString()}];
 | 
						|
    hash = hash_util.search_terms_to_hash(terms);
 | 
						|
    assert.equal(hash, "#narrow/channel/100-devel");
 | 
						|
    narrow = hash_util.parse_narrow(hash.split("/"));
 | 
						|
    assert.deepEqual(narrow, [{operator: "channel", operand: devel_id.toString(), negated: false}]);
 | 
						|
    filter = new Filter(narrow);
 | 
						|
    assert.deepEqual(filter.terms(), [
 | 
						|
        {operator: "channel", operand: devel_id.toString(), negated: false},
 | 
						|
    ]);
 | 
						|
 | 
						|
    terms = [{operator: "streams", operand: "public"}];
 | 
						|
    hash = hash_util.search_terms_to_hash(terms);
 | 
						|
    assert.equal(hash, "#narrow/channels/public");
 | 
						|
    narrow = hash_util.parse_narrow(hash.split("/"));
 | 
						|
    assert.deepEqual(narrow, [{operator: "channels", operand: "public", negated: false}]);
 | 
						|
    filter = new Filter(narrow);
 | 
						|
    assert.deepEqual(filter.terms(), [{operator: "channels", operand: "public", negated: false}]);
 | 
						|
 | 
						|
    // 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: 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: test_stream_id.toString(), negated: false},
 | 
						|
    ]);
 | 
						|
    filter = new Filter(narrow);
 | 
						|
    assert.deepEqual(filter.terms(), [
 | 
						|
        {operator: "channel", operand: test_stream_id.toString(), negated: false},
 | 
						|
    ]);
 | 
						|
});
 | 
						|
 | 
						|
run_test("terms_trailing_slash", () => {
 | 
						|
    const hash = "#narrow/channel/100-devel/topic/algol/";
 | 
						|
    const narrow = hash_util.parse_narrow(hash.split("/"));
 | 
						|
    assert.deepEqual(narrow, [
 | 
						|
        {operator: "channel", operand: devel_id.toString(), negated: false},
 | 
						|
        {operator: "topic", operand: "algol", negated: false},
 | 
						|
    ]);
 | 
						|
});
 | 
						|
 | 
						|
run_test("people_slugs", () => {
 | 
						|
    let terms;
 | 
						|
    let hash;
 | 
						|
    let narrow;
 | 
						|
 | 
						|
    const alice = {
 | 
						|
        email: "alice@example.com",
 | 
						|
        user_id: 42,
 | 
						|
        full_name: "Alice Smith",
 | 
						|
    };
 | 
						|
 | 
						|
    people.add_active_user(alice);
 | 
						|
    terms = [{operator: "sender", operand: "alice@example.com"}];
 | 
						|
    hash = hash_util.search_terms_to_hash(terms);
 | 
						|
    assert.equal(hash, "#narrow/sender/42-Alice-Smith");
 | 
						|
    narrow = hash_util.parse_narrow(hash.split("/"));
 | 
						|
    assert.deepEqual(narrow, [{operator: "sender", operand: "alice@example.com", negated: false}]);
 | 
						|
 | 
						|
    terms = [{operator: "dm", operand: "alice@example.com"}];
 | 
						|
    hash = hash_util.search_terms_to_hash(terms);
 | 
						|
    assert.equal(hash, "#narrow/dm/42-Alice-Smith");
 | 
						|
    narrow = hash_util.parse_narrow(hash.split("/"));
 | 
						|
    assert.deepEqual(narrow, [{operator: "dm", operand: "alice@example.com", negated: false}]);
 | 
						|
 | 
						|
    // Even though we renamed "pm-with" to "dm", preexisting
 | 
						|
    // links/URLs with "pm-with" operator are handled correctly.
 | 
						|
    terms = [{operator: "pm-with", operand: "alice@example.com"}];
 | 
						|
    hash = hash_util.search_terms_to_hash(terms);
 | 
						|
    assert.equal(hash, "#narrow/pm-with/42-Alice-Smith");
 | 
						|
    narrow = hash_util.parse_narrow(hash.split("/"));
 | 
						|
    assert.deepEqual(narrow, [{operator: "pm-with", operand: "alice@example.com", negated: false}]);
 | 
						|
});
 | 
						|
 | 
						|
function test_helper({override, override_rewire, change_tab}) {
 | 
						|
    let events = [];
 | 
						|
    let narrow_terms;
 | 
						|
 | 
						|
    function stub(module, func_name) {
 | 
						|
        module[func_name] = () => {
 | 
						|
            events.push([module, func_name]);
 | 
						|
        };
 | 
						|
    }
 | 
						|
 | 
						|
    function stub_with_args(module, func_name) {
 | 
						|
        module[func_name] = (...args) => {
 | 
						|
            events.push([module, func_name, args]);
 | 
						|
        };
 | 
						|
    }
 | 
						|
 | 
						|
    stub_with_args(admin, "launch");
 | 
						|
    stub(admin, "build_page");
 | 
						|
    stub(drafts_overlay_ui, "launch");
 | 
						|
    stub(message_viewport, "stop_auto_scrolling");
 | 
						|
    stub(overlays, "close_for_hash_change");
 | 
						|
    stub(settings, "launch");
 | 
						|
    stub(settings, "build_page");
 | 
						|
    stub(stream_settings_ui, "launch");
 | 
						|
    stub(ui_util, "blur_active_element");
 | 
						|
    stub(ui_report, "error");
 | 
						|
 | 
						|
    if (change_tab) {
 | 
						|
        override_rewire(message_view, "show", (terms) => {
 | 
						|
            narrow_terms = terms;
 | 
						|
            events.push("message_view.show");
 | 
						|
        });
 | 
						|
 | 
						|
        override(info_overlay, "show", (name) => {
 | 
						|
            events.push("info: " + name);
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
    return {
 | 
						|
        clear_events() {
 | 
						|
            events = [];
 | 
						|
        },
 | 
						|
        assert_events(expected_events) {
 | 
						|
            assert.deepEqual(events, expected_events);
 | 
						|
        },
 | 
						|
        get_narrow_terms: () => narrow_terms,
 | 
						|
    };
 | 
						|
}
 | 
						|
 | 
						|
run_test("hash_interactions", ({override, override_rewire}) => {
 | 
						|
    $window_stub = $.create("window-stub");
 | 
						|
    override(user_settings, "web_home_view", "recent_topics");
 | 
						|
 | 
						|
    const helper = test_helper({override, override_rewire, change_tab: true});
 | 
						|
 | 
						|
    let recent_view_ui_shown = false;
 | 
						|
    override(recent_view_ui, "show", () => {
 | 
						|
        recent_view_ui_shown = true;
 | 
						|
    });
 | 
						|
    let hide_all_called = false;
 | 
						|
    override(popovers, "hide_all", () => {
 | 
						|
        hide_all_called = true;
 | 
						|
    });
 | 
						|
    window.location.hash = "#unknown_hash";
 | 
						|
 | 
						|
    browser_history.clear_for_testing();
 | 
						|
    hashchange.initialize();
 | 
						|
    // If it's an unknown hash it should show the home view.
 | 
						|
    assert.equal(recent_view_ui_shown, true);
 | 
						|
    assert.equal(hide_all_called, true);
 | 
						|
    helper.assert_events([
 | 
						|
        [overlays, "close_for_hash_change"],
 | 
						|
        [message_viewport, "stop_auto_scrolling"],
 | 
						|
    ]);
 | 
						|
 | 
						|
    window.location.hash = "#feed";
 | 
						|
    hide_all_called = false;
 | 
						|
 | 
						|
    helper.clear_events();
 | 
						|
    $window_stub.trigger("hashchange");
 | 
						|
    assert.equal(hide_all_called, true);
 | 
						|
    helper.assert_events([
 | 
						|
        [overlays, "close_for_hash_change"],
 | 
						|
        [message_viewport, "stop_auto_scrolling"],
 | 
						|
        "message_view.show",
 | 
						|
    ]);
 | 
						|
 | 
						|
    helper.clear_events();
 | 
						|
    $window_stub.trigger("hashchange");
 | 
						|
    helper.assert_events([
 | 
						|
        [overlays, "close_for_hash_change"],
 | 
						|
        [message_viewport, "stop_auto_scrolling"],
 | 
						|
        "message_view.show",
 | 
						|
    ]);
 | 
						|
 | 
						|
    // Test old "#recent_topics" hash redirects to "#recent".
 | 
						|
    recent_view_ui_shown = false;
 | 
						|
    window.location.hash = "#recent_topics";
 | 
						|
 | 
						|
    helper.clear_events();
 | 
						|
    $window_stub.trigger("hashchange");
 | 
						|
    assert.equal(recent_view_ui_shown, true);
 | 
						|
    helper.assert_events([
 | 
						|
        [overlays, "close_for_hash_change"],
 | 
						|
        [message_viewport, "stop_auto_scrolling"],
 | 
						|
    ]);
 | 
						|
    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/channel/Denmark";
 | 
						|
 | 
						|
    helper.clear_events();
 | 
						|
    $window_stub.trigger("hashchange");
 | 
						|
    helper.assert_events([
 | 
						|
        [overlays, "close_for_hash_change"],
 | 
						|
        [message_viewport, "stop_auto_scrolling"],
 | 
						|
        "message_view.show",
 | 
						|
    ]);
 | 
						|
    let terms = helper.get_narrow_terms();
 | 
						|
    assert.equal(terms[0].operand, denmark_id.toString());
 | 
						|
 | 
						|
    window.location.hash = "#narrow";
 | 
						|
 | 
						|
    helper.clear_events();
 | 
						|
    $window_stub.trigger("hashchange");
 | 
						|
    helper.assert_events([
 | 
						|
        [overlays, "close_for_hash_change"],
 | 
						|
        [message_viewport, "stop_auto_scrolling"],
 | 
						|
        "message_view.show",
 | 
						|
    ]);
 | 
						|
    terms = helper.get_narrow_terms();
 | 
						|
    assert.equal(terms.length, 0);
 | 
						|
 | 
						|
    // Test an invalid narrow hash
 | 
						|
    window.location.hash = "#narrow/foo.foo";
 | 
						|
 | 
						|
    helper.clear_events();
 | 
						|
    $window_stub.trigger("hashchange");
 | 
						|
    helper.assert_events([
 | 
						|
        [overlays, "close_for_hash_change"],
 | 
						|
        [message_viewport, "stop_auto_scrolling"],
 | 
						|
        [ui_report, "error"],
 | 
						|
    ]);
 | 
						|
 | 
						|
    window.location.hash = "#channels/subscribed";
 | 
						|
 | 
						|
    helper.clear_events();
 | 
						|
    $window_stub.trigger("hashchange");
 | 
						|
    helper.assert_events([
 | 
						|
        [overlays, "close_for_hash_change"],
 | 
						|
        [stream_settings_ui, "launch"],
 | 
						|
    ]);
 | 
						|
 | 
						|
    recent_view_ui_shown = false;
 | 
						|
    window.location.hash = "#reload:send_after_reload=0...";
 | 
						|
 | 
						|
    helper.clear_events();
 | 
						|
    $window_stub.trigger("hashchange");
 | 
						|
    helper.assert_events([]);
 | 
						|
    // If it's reload hash it shouldn't show the home view.
 | 
						|
    assert.equal(recent_view_ui_shown, false);
 | 
						|
 | 
						|
    window.location.hash = "#keyboard-shortcuts/whatever";
 | 
						|
 | 
						|
    helper.clear_events();
 | 
						|
    $window_stub.trigger("hashchange");
 | 
						|
    helper.assert_events([[overlays, "close_for_hash_change"], "info: keyboard-shortcuts"]);
 | 
						|
 | 
						|
    window.location.hash = "#message-formatting/whatever";
 | 
						|
 | 
						|
    helper.clear_events();
 | 
						|
    $window_stub.trigger("hashchange");
 | 
						|
    helper.assert_events([[overlays, "close_for_hash_change"], "info: message-formatting"]);
 | 
						|
 | 
						|
    window.location.hash = "#search-operators/whatever";
 | 
						|
 | 
						|
    helper.clear_events();
 | 
						|
    $window_stub.trigger("hashchange");
 | 
						|
    helper.assert_events([[overlays, "close_for_hash_change"], "info: search-operators"]);
 | 
						|
 | 
						|
    window.location.hash = "#drafts";
 | 
						|
 | 
						|
    helper.clear_events();
 | 
						|
    $window_stub.trigger("hashchange");
 | 
						|
    helper.assert_events([
 | 
						|
        [overlays, "close_for_hash_change"],
 | 
						|
        [drafts_overlay_ui, "launch"],
 | 
						|
    ]);
 | 
						|
 | 
						|
    window.location.hash = "#settings/alert-words";
 | 
						|
 | 
						|
    helper.clear_events();
 | 
						|
    $window_stub.trigger("hashchange");
 | 
						|
    helper.assert_events([
 | 
						|
        [overlays, "close_for_hash_change"],
 | 
						|
        [settings, "build_page"],
 | 
						|
        [admin, "build_page"],
 | 
						|
        [settings, "launch"],
 | 
						|
    ]);
 | 
						|
 | 
						|
    window.location.hash = "#organization/users/active";
 | 
						|
 | 
						|
    helper.clear_events();
 | 
						|
    $window_stub.trigger("hashchange");
 | 
						|
    helper.assert_events([
 | 
						|
        [overlays, "close_for_hash_change"],
 | 
						|
        [settings, "build_page"],
 | 
						|
        [admin, "build_page"],
 | 
						|
        [admin, "launch", ["users", "active"]],
 | 
						|
    ]);
 | 
						|
 | 
						|
    window.location.hash = "#organization/user-list-admin";
 | 
						|
 | 
						|
    // Check whether `user-list-admin` is redirect to `users`, we
 | 
						|
    // cannot test the exact hashchange here, since the section url
 | 
						|
    // takes effect in `admin.launch` and that's why we're checking
 | 
						|
    // the arguments passed to `admin.launch`.
 | 
						|
    helper.clear_events();
 | 
						|
    $window_stub.trigger("hashchange");
 | 
						|
    helper.assert_events([
 | 
						|
        [overlays, "close_for_hash_change"],
 | 
						|
        [settings, "build_page"],
 | 
						|
        [admin, "build_page"],
 | 
						|
        [admin, "launch", ["users", "active"]],
 | 
						|
    ]);
 | 
						|
 | 
						|
    helper.clear_events();
 | 
						|
    browser_history.exit_overlay();
 | 
						|
 | 
						|
    helper.assert_events([[ui_util, "blur_active_element"]]);
 | 
						|
});
 | 
						|
 | 
						|
run_test("update_hash_to_match_filter", ({override, override_rewire}) => {
 | 
						|
    const helper = test_helper({override, override_rewire});
 | 
						|
 | 
						|
    let terms = [{operator: "is", operand: "dm"}];
 | 
						|
 | 
						|
    blueslip.expect("error", "browser does not support pushState");
 | 
						|
    message_view.update_hash_to_match_filter(new Filter(terms));
 | 
						|
 | 
						|
    helper.assert_events([[message_viewport, "stop_auto_scrolling"]]);
 | 
						|
    assert.equal(window.location.hash, "#narrow/is/dm");
 | 
						|
 | 
						|
    let url_pushed;
 | 
						|
    override(history, "pushState", (_state, _title, url) => {
 | 
						|
        url_pushed = url;
 | 
						|
    });
 | 
						|
 | 
						|
    terms = [{operator: "is", operand: "starred"}];
 | 
						|
 | 
						|
    helper.clear_events();
 | 
						|
    message_view.update_hash_to_match_filter(new Filter(terms));
 | 
						|
    helper.assert_events([[message_viewport, "stop_auto_scrolling"]]);
 | 
						|
    assert.equal(url_pushed, "http://zulip.zulipdev.com/#narrow/is/starred");
 | 
						|
 | 
						|
    terms = [{operator: "-is", operand: "starred"}];
 | 
						|
 | 
						|
    helper.clear_events();
 | 
						|
    message_view.update_hash_to_match_filter(new Filter(terms));
 | 
						|
    helper.assert_events([[message_viewport, "stop_auto_scrolling"]]);
 | 
						|
    assert.equal(url_pushed, "http://zulip.zulipdev.com/#narrow/-is/starred");
 | 
						|
});
 |