snapshot of https://github.com/zulip/zulip.git @ 48c69911c7dd4e03b20552eb3bd77ad8151c1357 (2026-09-15)
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled
This commit is contained in:
352
web/tests/message_flags.test.cjs
Normal file
352
web/tests/message_flags.test.cjs
Normal file
@@ -0,0 +1,352 @@
|
||||
"use strict";
|
||||
|
||||
const assert = require("node:assert/strict");
|
||||
|
||||
const {clock, mock_esm, set_global, with_overrides, zrequire} = require("./lib/namespace.cjs");
|
||||
const {run_test} = require("./lib/test.cjs");
|
||||
|
||||
const channel = mock_esm("../src/channel");
|
||||
const message_live_update = mock_esm("../src/message_live_update");
|
||||
|
||||
set_global("document", {hasFocus: () => true});
|
||||
|
||||
mock_esm("../src/starred_messages", {
|
||||
add() {},
|
||||
get_count: () => 5,
|
||||
get_starred_msg_ids: () => [1, 2, 3, 4, 5],
|
||||
remove() {},
|
||||
});
|
||||
mock_esm("../src/left_sidebar_navigation_area", {
|
||||
update_starred_count() {},
|
||||
});
|
||||
|
||||
const message_flags = zrequire("message_flags");
|
||||
const starred_messages_ui = zrequire("starred_messages_ui");
|
||||
const {initialize_user_settings} = zrequire("user_settings");
|
||||
|
||||
initialize_user_settings({user_settings: {}});
|
||||
|
||||
run_test("starred", ({override}) => {
|
||||
const message = {
|
||||
id: 50,
|
||||
};
|
||||
let ui_updated;
|
||||
|
||||
override(message_live_update, "update_starred_view", () => {
|
||||
ui_updated = true;
|
||||
});
|
||||
|
||||
let posted_data;
|
||||
|
||||
override(channel, "post", (opts) => {
|
||||
assert.equal(opts.url, "/json/messages/flags");
|
||||
posted_data = opts.data;
|
||||
});
|
||||
|
||||
starred_messages_ui.toggle_starred_and_update_server(message);
|
||||
|
||||
assert.ok(ui_updated);
|
||||
|
||||
assert.deepEqual(posted_data, {
|
||||
messages: "[50]",
|
||||
flag: "starred",
|
||||
op: "add",
|
||||
});
|
||||
|
||||
assert.deepEqual(message, {
|
||||
id: 50,
|
||||
starred: true,
|
||||
});
|
||||
|
||||
ui_updated = false;
|
||||
|
||||
starred_messages_ui.toggle_starred_and_update_server(message);
|
||||
|
||||
assert.ok(ui_updated);
|
||||
|
||||
assert.deepEqual(posted_data, {
|
||||
messages: "[50]",
|
||||
flag: "starred",
|
||||
op: "remove",
|
||||
});
|
||||
|
||||
assert.deepEqual(message, {
|
||||
id: 50,
|
||||
starred: false,
|
||||
});
|
||||
});
|
||||
|
||||
run_test("starring local echo", () => {
|
||||
// verify early return for locally echoed message
|
||||
const locally_echoed_message = {
|
||||
id: 51,
|
||||
starred: false,
|
||||
locally_echoed: true,
|
||||
};
|
||||
|
||||
starred_messages_ui.toggle_starred_and_update_server(locally_echoed_message);
|
||||
|
||||
// message_live_update.update_starred_view not called
|
||||
|
||||
// channel post request not made
|
||||
|
||||
// starred flag unchanged
|
||||
assert.deepEqual(locally_echoed_message, {
|
||||
id: 51,
|
||||
locally_echoed: true,
|
||||
starred: false,
|
||||
});
|
||||
});
|
||||
|
||||
run_test("unstar_all", ({override}) => {
|
||||
// Way to capture posted info in every request
|
||||
let posted_data;
|
||||
override(channel, "post", (opts) => {
|
||||
assert.equal(opts.url, "/json/messages/flags");
|
||||
posted_data = opts.data;
|
||||
});
|
||||
|
||||
// we've set get_starred_msg_ids to return [1, 2, 3, 4, 5]
|
||||
const expected_data = {messages: "[1,2,3,4,5]", flag: "starred", op: "remove"};
|
||||
|
||||
message_flags.unstar_all_messages();
|
||||
|
||||
assert.deepEqual(posted_data, expected_data);
|
||||
});
|
||||
|
||||
run_test("unstar_all_in_topic", ({override}) => {
|
||||
// Way to capture posted info in every request
|
||||
let channel_post_opts;
|
||||
let channel_get_opts;
|
||||
|
||||
override(channel, "get", (opts) => {
|
||||
assert.equal(opts.url, "/json/messages");
|
||||
channel_get_opts = opts;
|
||||
opts.success({
|
||||
messages: [{id: 2}, {id: 3}, {id: 5}],
|
||||
});
|
||||
});
|
||||
|
||||
override(channel, "post", (opts) => {
|
||||
assert.equal(opts.url, "/json/messages/flags");
|
||||
channel_post_opts = opts;
|
||||
});
|
||||
|
||||
message_flags.unstar_all_messages_in_topic(20, "topic");
|
||||
|
||||
assert.deepEqual(channel_get_opts.data, {
|
||||
anchor: "newest",
|
||||
num_before: 1000,
|
||||
num_after: 0,
|
||||
narrow: JSON.stringify([
|
||||
{operator: "channel", operand: 20},
|
||||
{operator: "topic", operand: "topic"},
|
||||
{operator: "is", operand: "starred"},
|
||||
]),
|
||||
allow_empty_topic_name: true,
|
||||
});
|
||||
|
||||
assert.deepEqual(channel_post_opts.data, {
|
||||
messages: "[2,3,5]",
|
||||
flag: "starred",
|
||||
op: "remove",
|
||||
});
|
||||
});
|
||||
|
||||
run_test("read", ({override}) => {
|
||||
// Way to capture posted info in every request
|
||||
let channel_post_opts;
|
||||
override(channel, "post", (opts) => {
|
||||
channel_post_opts = opts;
|
||||
});
|
||||
|
||||
// For testing purpose limit the batch size value to 5 instead of 1000
|
||||
function send_read(messages) {
|
||||
with_overrides(({override_rewire}) => {
|
||||
override_rewire(message_flags, "_unread_batch_size", 5);
|
||||
message_flags.send_read(messages);
|
||||
});
|
||||
}
|
||||
|
||||
let msgs_to_flag_read = [
|
||||
{locally_echoed: false, id: 1},
|
||||
{locally_echoed: false, id: 2},
|
||||
{locally_echoed: false, id: 3},
|
||||
{locally_echoed: false, id: 4},
|
||||
{locally_echoed: false, id: 5},
|
||||
{locally_echoed: false, id: 6},
|
||||
{locally_echoed: false, id: 7},
|
||||
];
|
||||
send_read(msgs_to_flag_read);
|
||||
assert.deepEqual(channel_post_opts, {
|
||||
url: "/json/messages/flags",
|
||||
data: {
|
||||
messages: "[1,2,3,4,5]",
|
||||
op: "add",
|
||||
flag: "read",
|
||||
},
|
||||
success: channel_post_opts.success,
|
||||
});
|
||||
|
||||
// Mock successful flagging of ids
|
||||
let success_response_data = {
|
||||
messages: [1, 2, 3, 4, 5],
|
||||
};
|
||||
channel_post_opts.success(success_response_data);
|
||||
clock.runAll();
|
||||
assert.deepEqual(channel_post_opts, {
|
||||
url: "/json/messages/flags",
|
||||
data: {
|
||||
messages: "[6,7]",
|
||||
op: "add",
|
||||
flag: "read",
|
||||
},
|
||||
success: channel_post_opts.success,
|
||||
});
|
||||
success_response_data = {
|
||||
messages: [6, 7],
|
||||
};
|
||||
channel_post_opts.success(success_response_data);
|
||||
|
||||
// Don't flag locally echoed messages as read
|
||||
const local_msg_1 = {locally_echoed: true, id: 1};
|
||||
const local_msg_2 = {locally_echoed: true, id: 2};
|
||||
msgs_to_flag_read = [
|
||||
local_msg_1,
|
||||
local_msg_2,
|
||||
{locally_echoed: false, id: 3},
|
||||
{locally_echoed: false, id: 4},
|
||||
{locally_echoed: false, id: 5},
|
||||
{locally_echoed: false, id: 6},
|
||||
{locally_echoed: false, id: 7},
|
||||
];
|
||||
send_read(msgs_to_flag_read);
|
||||
assert.deepEqual(channel_post_opts, {
|
||||
url: "/json/messages/flags",
|
||||
data: {
|
||||
messages: "[3,4,5,6,7]",
|
||||
op: "add",
|
||||
flag: "read",
|
||||
},
|
||||
success: channel_post_opts.success,
|
||||
});
|
||||
|
||||
// Messages still not acked yet
|
||||
// Mock successful flagging of ids
|
||||
success_response_data = {
|
||||
messages: [3, 4, 5, 6, 7],
|
||||
};
|
||||
channel_post_opts.success(success_response_data);
|
||||
clock.tick(1100);
|
||||
|
||||
// Mark them non-local
|
||||
local_msg_1.locally_echoed = false;
|
||||
local_msg_2.locally_echoed = false;
|
||||
clock.runAll();
|
||||
|
||||
// Former locally echoed messages flagging retried
|
||||
assert.deepEqual(channel_post_opts, {
|
||||
url: "/json/messages/flags",
|
||||
data: {
|
||||
messages: "[1,2]",
|
||||
op: "add",
|
||||
flag: "read",
|
||||
},
|
||||
success: channel_post_opts.success,
|
||||
});
|
||||
|
||||
msgs_to_flag_read = [1, 2, 3, 4, 5];
|
||||
message_flags.mark_as_read(msgs_to_flag_read);
|
||||
assert.deepEqual(channel_post_opts, {
|
||||
url: "/json/messages/flags",
|
||||
data: {
|
||||
messages: "[1,2,3,4,5]",
|
||||
op: "add",
|
||||
flag: "read",
|
||||
},
|
||||
});
|
||||
|
||||
clock.reset();
|
||||
});
|
||||
|
||||
run_test("read_empty_data", ({override}) => {
|
||||
// Way to capture posted info in every request
|
||||
let channel_post_opts;
|
||||
override(channel, "post", (opts) => {
|
||||
channel_post_opts = opts;
|
||||
});
|
||||
|
||||
// For testing purpose limit the batch size value to 5 instead of 1000
|
||||
function send_read(messages) {
|
||||
with_overrides(({override_rewire}) => {
|
||||
override_rewire(message_flags, "_unread_batch_size", 5);
|
||||
message_flags.send_read(messages);
|
||||
});
|
||||
}
|
||||
|
||||
// send read to obtain success callback
|
||||
send_read([{locally_echoed: false, id: 1}]);
|
||||
|
||||
// verify early return on empty data
|
||||
const success_callback = channel_post_opts.success;
|
||||
channel_post_opts = {};
|
||||
let empty_data;
|
||||
success_callback(empty_data);
|
||||
assert.deepEqual(channel_post_opts, {});
|
||||
empty_data = {messages: undefined};
|
||||
success_callback(empty_data);
|
||||
assert.deepEqual(channel_post_opts, {});
|
||||
});
|
||||
|
||||
run_test("collapse_and_uncollapse", ({override}) => {
|
||||
// Way to capture posted info in every request
|
||||
let channel_post_opts;
|
||||
override(channel, "post", (opts) => {
|
||||
channel_post_opts = opts;
|
||||
});
|
||||
|
||||
const msg = {id: 5};
|
||||
|
||||
message_flags.save_collapsed(msg);
|
||||
|
||||
assert.deepEqual(channel_post_opts, {
|
||||
url: "/json/messages/flags",
|
||||
data: {
|
||||
messages: "[5]",
|
||||
op: "add",
|
||||
flag: "collapsed",
|
||||
},
|
||||
});
|
||||
|
||||
message_flags.save_uncollapsed(msg);
|
||||
|
||||
assert.deepEqual(channel_post_opts, {
|
||||
url: "/json/messages/flags",
|
||||
data: {
|
||||
messages: "[5]",
|
||||
op: "remove",
|
||||
flag: "collapsed",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
run_test("mark_as_unread", ({override}) => {
|
||||
// Way to capture posted info in every request
|
||||
let channel_post_opts;
|
||||
override(channel, "post", (opts) => {
|
||||
channel_post_opts = opts;
|
||||
});
|
||||
|
||||
const msg = {id: 5};
|
||||
|
||||
message_flags.mark_as_unread([msg.id]);
|
||||
|
||||
assert.deepEqual(channel_post_opts, {
|
||||
url: "/json/messages/flags",
|
||||
data: {
|
||||
messages: "[5]",
|
||||
op: "remove",
|
||||
flag: "read",
|
||||
},
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user