mirror of https://github.com/zulip/zulip.git
501 lines
14 KiB
JavaScript
501 lines
14 KiB
JavaScript
"use strict";
|
|
|
|
const assert = require("node:assert/strict");
|
|
|
|
const {zrequire} = require("./lib/namespace.js");
|
|
const {run_test} = require("./lib/test.js");
|
|
|
|
const all_messages_data = zrequire("../src/all_messages_data");
|
|
|
|
const {Filter} = zrequire("../src/filter");
|
|
const {MessageListData} = zrequire("../src/message_list_data");
|
|
const narrow_state = zrequire("narrow_state");
|
|
const message_view = zrequire("message_view");
|
|
const message_lists = zrequire("message_lists");
|
|
const resolved_topic = zrequire("../shared/src/resolved_topic");
|
|
|
|
function test_with(fixture) {
|
|
const filter = new Filter(fixture.filter_terms);
|
|
message_lists.set_current({
|
|
data: {
|
|
filter,
|
|
},
|
|
});
|
|
|
|
// Make sure our simulated tests data satisfies the
|
|
// invariant that the first unread message we find
|
|
// does indeed satisfy our filter.
|
|
if (fixture.unread_info.flavor === "found") {
|
|
for (const msg of fixture.all_messages) {
|
|
if (msg.id === fixture.unread_info.msg_id) {
|
|
assert.ok(filter.predicate()(msg));
|
|
}
|
|
}
|
|
}
|
|
|
|
const excludes_muted_topics = filter.excludes_muted_topics();
|
|
const msg_data = new MessageListData({
|
|
filter,
|
|
excludes_muted_topics,
|
|
});
|
|
const id_info = {
|
|
target_id: fixture.target_id,
|
|
local_select_id: undefined,
|
|
final_select_id: undefined,
|
|
};
|
|
|
|
all_messages_data.rewire_all_messages_data({
|
|
fetch_status: {
|
|
has_found_newest: () => fixture.has_found_newest,
|
|
},
|
|
visibly_empty: () => fixture.visibly_empty,
|
|
all_messages() {
|
|
assert.notEqual(fixture.all_messages, undefined);
|
|
return fixture.all_messages;
|
|
},
|
|
first() {
|
|
assert.notEqual(fixture.all_messages, undefined);
|
|
return fixture.all_messages[0];
|
|
},
|
|
last() {
|
|
assert.notEqual(fixture.all_messages, undefined);
|
|
return fixture.all_messages.at(-1);
|
|
},
|
|
});
|
|
|
|
narrow_state.rewire_get_first_unread_info(() => fixture.unread_info);
|
|
|
|
message_view.maybe_add_local_messages({
|
|
id_info,
|
|
msg_data,
|
|
superset_data: all_messages_data.all_messages_data,
|
|
});
|
|
|
|
assert.deepEqual(id_info, fixture.expected_id_info);
|
|
|
|
const msgs = msg_data.all_messages();
|
|
const msg_ids = msgs.map((message) => message.id);
|
|
assert.deepEqual(msg_ids, fixture.expected_msg_ids);
|
|
}
|
|
|
|
run_test("near after unreads", () => {
|
|
// Current near: behavior is to ignore the unreads and take you
|
|
// to the target message, with reading disabled.
|
|
const fixture = {
|
|
filter_terms: [{operator: "near", operand: 42}],
|
|
target_id: 42,
|
|
unread_info: {
|
|
flavor: "found",
|
|
msg_id: 37,
|
|
},
|
|
has_found_newest: false,
|
|
all_messages: [
|
|
{id: 37, topic: "whatever"},
|
|
{id: 42, topic: "whatever"},
|
|
{id: 44, topic: "whatever"},
|
|
],
|
|
expected_id_info: {
|
|
target_id: 42,
|
|
final_select_id: 42,
|
|
local_select_id: 42,
|
|
},
|
|
expected_msg_ids: [37, 42, 44],
|
|
};
|
|
|
|
test_with(fixture);
|
|
});
|
|
|
|
run_test("near not in message list", () => {
|
|
// Current behavior is to ignore the unreads and take you
|
|
// to the closest messages, with reading disabled.
|
|
const fixture = {
|
|
filter_terms: [{operator: "near", operand: 42}],
|
|
target_id: 42,
|
|
unread_info: {
|
|
flavor: "found",
|
|
msg_id: 46,
|
|
},
|
|
has_found_newest: false,
|
|
all_messages: [
|
|
{id: 41, topic: "whatever"},
|
|
{id: 45, topic: "whatever"},
|
|
{id: 46, topic: "whatever"},
|
|
],
|
|
expected_id_info: {
|
|
target_id: 42,
|
|
final_select_id: 42,
|
|
local_select_id: undefined,
|
|
},
|
|
expected_msg_ids: [41, 45, 46],
|
|
};
|
|
|
|
test_with(fixture);
|
|
});
|
|
|
|
run_test("near before unreads", () => {
|
|
const fixture = {
|
|
filter_terms: [{operator: "near", operand: 42}],
|
|
target_id: 42,
|
|
unread_info: {
|
|
flavor: "found",
|
|
msg_id: 43,
|
|
},
|
|
has_found_newest: false,
|
|
all_messages: [
|
|
{id: 42, topic: "whatever"},
|
|
{id: 43, topic: "whatever"},
|
|
{id: 44, topic: "whatever"},
|
|
],
|
|
expected_id_info: {
|
|
target_id: 42,
|
|
final_select_id: 42,
|
|
local_select_id: 42,
|
|
},
|
|
expected_msg_ids: [42, 43, 44],
|
|
};
|
|
|
|
test_with(fixture);
|
|
});
|
|
|
|
run_test("near with no unreads", () => {
|
|
const fixture = {
|
|
filter_terms: [{operator: "near", operand: 42}],
|
|
target_id: 42,
|
|
unread_info: {
|
|
flavor: "not_found",
|
|
},
|
|
has_found_newest: false,
|
|
visibly_empty: true,
|
|
expected_id_info: {
|
|
target_id: 42,
|
|
final_select_id: 42,
|
|
local_select_id: undefined,
|
|
},
|
|
expected_msg_ids: [],
|
|
};
|
|
|
|
test_with(fixture);
|
|
});
|
|
|
|
run_test("is private with no target", () => {
|
|
const fixture = {
|
|
filter_terms: [{operator: "is", operand: "private"}],
|
|
unread_info: {
|
|
flavor: "found",
|
|
msg_id: 550,
|
|
},
|
|
has_found_newest: true,
|
|
all_messages: [
|
|
{id: 450, type: "private", to_user_ids: "1,2"},
|
|
{id: 500, type: "private", to_user_ids: "1,2"},
|
|
{id: 550, type: "private", to_user_ids: "1,2"},
|
|
],
|
|
expected_id_info: {
|
|
target_id: undefined,
|
|
final_select_id: 550,
|
|
local_select_id: 550,
|
|
},
|
|
expected_msg_ids: [450, 500, 550],
|
|
};
|
|
|
|
test_with(fixture);
|
|
});
|
|
|
|
run_test("dm with target outside of range", () => {
|
|
const fixture = {
|
|
filter_terms: [{operator: "dm", operand: "alice@example.com"}],
|
|
target_id: 5,
|
|
unread_info: {
|
|
flavor: "not_found",
|
|
},
|
|
has_found_newest: false,
|
|
all_messages: [{id: 999}],
|
|
expected_id_info: {
|
|
target_id: 5,
|
|
final_select_id: 5,
|
|
local_select_id: undefined,
|
|
},
|
|
expected_msg_ids: [],
|
|
};
|
|
|
|
test_with(fixture);
|
|
});
|
|
|
|
run_test("is:private with no unreads before fetch", () => {
|
|
const fixture = {
|
|
filter_terms: [{operator: "is", operand: "private"}],
|
|
unread_info: {
|
|
flavor: "not_found",
|
|
},
|
|
has_found_newest: false,
|
|
visibly_empty: true,
|
|
expected_id_info: {
|
|
target_id: undefined,
|
|
final_select_id: undefined,
|
|
local_select_id: undefined,
|
|
},
|
|
expected_msg_ids: [],
|
|
};
|
|
|
|
test_with(fixture);
|
|
});
|
|
|
|
run_test("is:private with target and no unreads", () => {
|
|
const fixture = {
|
|
filter_terms: [{operator: "is", operand: "private"}],
|
|
target_id: 450,
|
|
unread_info: {
|
|
flavor: "not_found",
|
|
},
|
|
has_found_newest: true,
|
|
visibly_empty: false,
|
|
all_messages: [
|
|
{id: 350},
|
|
{id: 400, type: "private", to_user_ids: "1,2"},
|
|
{id: 450, type: "private", to_user_ids: "1,2"},
|
|
{id: 500, type: "private", to_user_ids: "1,2"},
|
|
],
|
|
expected_id_info: {
|
|
target_id: 450,
|
|
final_select_id: 450,
|
|
local_select_id: 450,
|
|
},
|
|
expected_msg_ids: [400, 450, 500],
|
|
};
|
|
|
|
test_with(fixture);
|
|
});
|
|
|
|
run_test("is:mentioned with no unreads and no matches", () => {
|
|
const fixture = {
|
|
filter_terms: [{operator: "is", operand: "mentioned"}],
|
|
unread_info: {
|
|
flavor: "not_found",
|
|
},
|
|
has_found_newest: true,
|
|
all_messages: [],
|
|
expected_id_info: {
|
|
target_id: undefined,
|
|
final_select_id: undefined,
|
|
local_select_id: undefined,
|
|
},
|
|
expected_msg_ids: [],
|
|
};
|
|
|
|
test_with(fixture);
|
|
});
|
|
|
|
run_test("is:alerted with no unreads and one match", () => {
|
|
const fixture = {
|
|
filter_terms: [{operator: "is", operand: "alerted"}],
|
|
unread_info: {
|
|
flavor: "not_found",
|
|
},
|
|
has_found_newest: true,
|
|
all_messages: [
|
|
{id: 55, topic: "whatever", alerted: true},
|
|
{id: 57, topic: "whatever", alerted: false},
|
|
],
|
|
expected_id_info: {
|
|
target_id: undefined,
|
|
final_select_id: 55,
|
|
local_select_id: 55,
|
|
},
|
|
expected_msg_ids: [55],
|
|
};
|
|
|
|
test_with(fixture);
|
|
});
|
|
|
|
run_test("is:resolved with one unread", () => {
|
|
const resolved_topic_name = resolved_topic.resolve_name("foo");
|
|
const fixture = {
|
|
filter_terms: [{operator: "is", operand: "resolved"}],
|
|
unread_info: {
|
|
flavor: "found",
|
|
msg_id: 56,
|
|
},
|
|
has_found_newest: true,
|
|
all_messages: [
|
|
{id: 55, type: "stream", topic: resolved_topic_name},
|
|
{id: 56, type: "stream", topic: resolved_topic_name},
|
|
{id: 57, type: "stream", topic: "foo"},
|
|
],
|
|
expected_id_info: {
|
|
target_id: undefined,
|
|
final_select_id: 56,
|
|
local_select_id: 56,
|
|
},
|
|
expected_msg_ids: [55, 56],
|
|
};
|
|
|
|
test_with(fixture);
|
|
});
|
|
|
|
run_test("is:resolved with no unreads", () => {
|
|
const resolved_topic_name = resolved_topic.resolve_name("foo");
|
|
const fixture = {
|
|
filter_terms: [{operator: "is", operand: "resolved"}],
|
|
unread_info: {
|
|
flavor: "not_found",
|
|
},
|
|
has_found_newest: true,
|
|
all_messages: [
|
|
{id: 55, type: "stream", topic: resolved_topic_name},
|
|
{id: 57, type: "stream", topic: "foo"},
|
|
],
|
|
expected_id_info: {
|
|
target_id: undefined,
|
|
final_select_id: 55,
|
|
local_select_id: 55,
|
|
},
|
|
expected_msg_ids: [55],
|
|
};
|
|
|
|
test_with(fixture);
|
|
});
|
|
|
|
run_test("search", () => {
|
|
const fixture = {
|
|
filter_terms: [{operator: "search", operand: "whatever"}],
|
|
unread_info: {
|
|
flavor: "cannot_compute",
|
|
},
|
|
expected_id_info: {
|
|
target_id: undefined,
|
|
final_select_id: 10000000000000000,
|
|
local_select_id: undefined,
|
|
},
|
|
expected_msg_ids: [],
|
|
};
|
|
|
|
test_with(fixture);
|
|
});
|
|
|
|
run_test("search near", () => {
|
|
const fixture = {
|
|
filter_terms: [
|
|
{operator: "search", operand: "whatever"},
|
|
{operator: "near", operand: 22},
|
|
],
|
|
target_id: 22,
|
|
unread_info: {
|
|
flavor: "cannot_compute",
|
|
},
|
|
expected_id_info: {
|
|
target_id: 22,
|
|
final_select_id: 22,
|
|
local_select_id: undefined,
|
|
},
|
|
expected_msg_ids: [],
|
|
};
|
|
|
|
test_with(fixture);
|
|
});
|
|
|
|
run_test("stream, no unread, not in all_messages", () => {
|
|
// This might be something you'd see zooming out from
|
|
// a muted topic, maybe? It's possibly this scenario
|
|
// is somewhat contrived, but we exercise fairly simple
|
|
// defensive code that just punts when messages aren't in
|
|
// our new message list. Note that our target_id is within
|
|
// the range of all_messages.
|
|
const fixture = {
|
|
filter_terms: [{operator: "stream", operand: "whatever"}],
|
|
target_id: 450,
|
|
unread_info: {
|
|
flavor: "not_found",
|
|
},
|
|
has_found_newest: true,
|
|
visibly_empty: false,
|
|
all_messages: [{id: 400}, {id: 500}],
|
|
expected_id_info: {
|
|
target_id: 450,
|
|
final_select_id: 450,
|
|
local_select_id: undefined,
|
|
},
|
|
expected_msg_ids: [],
|
|
};
|
|
|
|
test_with(fixture);
|
|
});
|
|
|
|
run_test("search, stream, not in all_messages", () => {
|
|
const fixture = {
|
|
filter_terms: [
|
|
{operator: "search", operand: "foo"},
|
|
{operator: "stream", operand: "whatever"},
|
|
],
|
|
unread_info: {
|
|
flavor: "cannot_compute",
|
|
},
|
|
has_found_newest: true,
|
|
visibly_empty: false,
|
|
all_messages: [{id: 400}, {id: 500}],
|
|
expected_id_info: {
|
|
target_id: undefined,
|
|
final_select_id: 10000000000000000,
|
|
local_select_id: undefined,
|
|
},
|
|
expected_msg_ids: [],
|
|
};
|
|
|
|
test_with(fixture);
|
|
});
|
|
|
|
run_test("stream/topic not in all_messages", () => {
|
|
// This is a bit of a corner case, but you could have a scenario
|
|
// where you've gone way back in a topic (perhaps something that
|
|
// has been muted a long time) and find an unread message that isn't
|
|
// actually in all_messages_data.
|
|
const fixture = {
|
|
filter_terms: [
|
|
{operator: "stream", operand: "one"},
|
|
{operator: "topic", operand: "whatever"},
|
|
],
|
|
target_id: 1000,
|
|
unread_info: {
|
|
flavor: "found",
|
|
msg_id: 2,
|
|
},
|
|
has_found_newest: true,
|
|
all_messages: [{id: 900}, {id: 1100}],
|
|
expected_id_info: {
|
|
target_id: 1000,
|
|
final_select_id: 2,
|
|
local_select_id: undefined,
|
|
},
|
|
expected_msg_ids: [],
|
|
};
|
|
|
|
test_with(fixture);
|
|
});
|
|
|
|
run_test("final corner case", () => {
|
|
// This tries to get all the way to the end of
|
|
// the function (as written now). The data here
|
|
// may be completely contrived.
|
|
const fixture = {
|
|
filter_terms: [{operator: "is", operand: "starred"}],
|
|
target_id: 450,
|
|
unread_info: {
|
|
flavor: "not_found",
|
|
},
|
|
has_found_newest: true,
|
|
visibly_empty: false,
|
|
all_messages: [
|
|
{id: 400, topic: "whatever"},
|
|
{id: 425, topic: "whatever", starred: true},
|
|
{id: 500, topic: "whatever"},
|
|
],
|
|
expected_id_info: {
|
|
target_id: 450,
|
|
final_select_id: 450,
|
|
local_select_id: undefined,
|
|
},
|
|
expected_msg_ids: [425],
|
|
};
|
|
|
|
test_with(fixture);
|
|
});
|