2020-08-01 03:43:15 +02:00
|
|
|
"use strict";
|
|
|
|
|
2020-11-30 23:46:45 +01:00
|
|
|
const {strict: assert} = require("assert");
|
|
|
|
|
2021-03-10 06:10:32 +01:00
|
|
|
const {mock_esm, zrequire} = require("../zjsunit/namespace");
|
2020-12-01 00:39:47 +01:00
|
|
|
const {run_test} = require("../zjsunit/test");
|
2020-12-01 00:02:16 +01:00
|
|
|
|
2021-03-10 06:10:32 +01:00
|
|
|
const message_list = mock_esm("../../static/js/message_list");
|
2021-02-28 21:31:33 +01:00
|
|
|
|
zjsunit: Remove rewiremock dependency.
We now just use a module._load hook to inject
stubs into our code.
For conversion purposes I temporarily maintain
the API of rewiremock, apart from the enable/disable
pieces, but I will make a better wrapper in an
upcoming commit.
We can detect when rewiremock is called after
zrequire now, and I fix all the violations in
this commit, mostly by using override.
We can also detect when a mock is needlessly
created, and I fix all the violations in this
commit.
The one minor nuisance that this commit introduces
is that you can only stub out modules in the Zulip
source tree, which is now static/js. This should
not really be a problem--there are usually better
techniques to deal with third party depenencies.
In the prior commit I show a typical workaround,
which is to create a one-line wrapper in your
test code. It's often the case that you can simply
use override(), as well.
In passing I kill off `reset_modules`, and I
eliminated the second argument to zrequire,
which dates back to pre-es6 days.
2021-03-06 12:47:54 +01:00
|
|
|
const {Filter} = zrequire("../js/filter");
|
|
|
|
const {MessageListData} = zrequire("../js/message_list_data");
|
2021-02-10 04:53:22 +01:00
|
|
|
const narrow_state = zrequire("narrow_state");
|
|
|
|
const narrow = zrequire("narrow");
|
2020-07-15 01:29:15 +02:00
|
|
|
|
2018-06-01 14:17:12 +02:00
|
|
|
function test_with(fixture) {
|
|
|
|
const filter = new Filter(fixture.filter_terms);
|
|
|
|
narrow_state.set_current_filter(filter);
|
|
|
|
|
|
|
|
// Make sure our simulated tests data satisfies the
|
|
|
|
// invarariant that the first unread message we find
|
|
|
|
// does indeed satisfy our filter.
|
2020-07-15 01:29:15 +02:00
|
|
|
if (fixture.unread_info.flavor === "found") {
|
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
|
|
|
for (const msg of fixture.all_messages) {
|
2018-06-01 14:17:12 +02:00
|
|
|
if (msg.id === fixture.unread_info.msg_id) {
|
|
|
|
assert(filter.predicate()(msg));
|
|
|
|
}
|
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
|
|
|
}
|
2018-06-01 14:17:12 +02:00
|
|
|
}
|
|
|
|
|
2021-01-25 06:23:16 +01:00
|
|
|
const excludes_muted_topics = narrow_state.excludes_muted_topics();
|
2018-06-01 14:17:12 +02:00
|
|
|
const msg_data = new MessageListData({
|
2018-07-07 15:55:39 +02:00
|
|
|
filter: narrow_state.filter(),
|
2021-01-25 06:23:16 +01:00
|
|
|
excludes_muted_topics,
|
2018-06-01 14:17:12 +02:00
|
|
|
});
|
|
|
|
const id_info = {
|
|
|
|
target_id: fixture.target_id,
|
|
|
|
local_select_id: undefined,
|
|
|
|
final_select_id: undefined,
|
|
|
|
};
|
|
|
|
|
|
|
|
message_list.all = {
|
2020-05-30 09:45:12 +02:00
|
|
|
data: {
|
|
|
|
fetch_status: {
|
|
|
|
has_found_newest: () => fixture.has_found_newest,
|
|
|
|
},
|
2018-06-01 14:17:12 +02:00
|
|
|
},
|
|
|
|
empty: () => fixture.empty,
|
|
|
|
all_messages: () => {
|
|
|
|
assert(fixture.all_messages !== undefined);
|
|
|
|
return fixture.all_messages;
|
|
|
|
},
|
|
|
|
first: () => {
|
|
|
|
assert(fixture.all_messages !== undefined);
|
|
|
|
return fixture.all_messages[0];
|
|
|
|
},
|
|
|
|
last: () => {
|
|
|
|
assert(fixture.all_messages !== undefined);
|
|
|
|
return fixture.all_messages[fixture.all_messages.length - 1];
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2021-02-28 00:47:56 +01:00
|
|
|
narrow_state.__Rewire__("get_first_unread_info", () => fixture.unread_info);
|
2018-06-01 14:17:12 +02:00
|
|
|
|
|
|
|
narrow.maybe_add_local_messages({
|
2020-07-20 22:18:43 +02:00
|
|
|
id_info,
|
|
|
|
msg_data,
|
2018-06-01 14:17:12 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
assert.deepEqual(id_info, fixture.expected_id_info);
|
|
|
|
|
|
|
|
const msgs = msg_data.all_messages();
|
2020-07-02 01:39:34 +02:00
|
|
|
const msg_ids = msgs.map((message) => message.id);
|
2018-06-01 14:17:12 +02:00
|
|
|
assert.deepEqual(msg_ids, fixture.expected_msg_ids);
|
|
|
|
}
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
run_test("near after unreads", () => {
|
narrow: Use search reading behavior in all searches.
In 452e226ea25c1315edb1f31be406475d82c56a88 and
648a60baf63f9afade83148bd9ae1fc480510178, we changed how `search:`
narrows work to:
(1) Never mark messages as read inside searches (search:)
(2) Take you to the bottom, not the first unread, if a `near:` or
similar wasn't specified.
This is far better behavior for these use cases, because in these
narrows, you can't actually see all the context around the target
messages, so marking them as read is counterproductive. This is
especially important in `has:mention` where you goal is likely
specifically to keep track of which threads mentioning you haven't
been read. But in many other narrows, the current behavior is
effectively (1) setting the read bit on random messages and (2) if the
search term matches many messages in a muted stream with 1000s of
unreads, making it hard or impossible to find recent search matches.
The new behavior is that any narrow that is structurally a search of
history (including everything that that isn't a stream, topic,
pm-with, "all messages" or "private messages") gets that new behavior
of being unable to mark messages as read and narrows taking you to the
latest matching messages.
A few corner cases of interest:
* `is:private` is keeping the old behavior, because users on
chat.zulip.org found it confusing for `is:private` to not mark
messages as read when one could see them all. Possibly a more
complex answer is required here.
* `near:` narrows are getting the new behavior, even if it's a stream:
+ topic: narrow. This is debatable, but is probably better than
what was happening before.
Modified significantly by tabbott for cleanliness of implementation,
this commit message, and unit tests.
Fixes #9893. Follow-up to #12556.
2019-07-22 01:55:04 +02:00
|
|
|
// Current near: behavior is to ignore the unreads and take you
|
|
|
|
// to the target message, with reading disabled.
|
2018-06-01 14:17:12 +02:00
|
|
|
const fixture = {
|
2020-07-15 00:34:28 +02:00
|
|
|
filter_terms: [{operator: "near", operand: 42}],
|
2018-06-01 14:17:12 +02:00
|
|
|
target_id: 42,
|
|
|
|
unread_info: {
|
2020-07-15 01:29:15 +02:00
|
|
|
flavor: "found",
|
2018-06-01 14:17:12 +02:00
|
|
|
msg_id: 37,
|
|
|
|
},
|
|
|
|
has_found_newest: false,
|
|
|
|
all_messages: [
|
2020-07-15 01:29:15 +02:00
|
|
|
{id: 37, topic: "whatever"},
|
|
|
|
{id: 42, topic: "whatever"},
|
|
|
|
{id: 44, topic: "whatever"},
|
2018-06-01 14:17:12 +02:00
|
|
|
],
|
|
|
|
expected_id_info: {
|
|
|
|
target_id: 42,
|
narrow: Use search reading behavior in all searches.
In 452e226ea25c1315edb1f31be406475d82c56a88 and
648a60baf63f9afade83148bd9ae1fc480510178, we changed how `search:`
narrows work to:
(1) Never mark messages as read inside searches (search:)
(2) Take you to the bottom, not the first unread, if a `near:` or
similar wasn't specified.
This is far better behavior for these use cases, because in these
narrows, you can't actually see all the context around the target
messages, so marking them as read is counterproductive. This is
especially important in `has:mention` where you goal is likely
specifically to keep track of which threads mentioning you haven't
been read. But in many other narrows, the current behavior is
effectively (1) setting the read bit on random messages and (2) if the
search term matches many messages in a muted stream with 1000s of
unreads, making it hard or impossible to find recent search matches.
The new behavior is that any narrow that is structurally a search of
history (including everything that that isn't a stream, topic,
pm-with, "all messages" or "private messages") gets that new behavior
of being unable to mark messages as read and narrows taking you to the
latest matching messages.
A few corner cases of interest:
* `is:private` is keeping the old behavior, because users on
chat.zulip.org found it confusing for `is:private` to not mark
messages as read when one could see them all. Possibly a more
complex answer is required here.
* `near:` narrows are getting the new behavior, even if it's a stream:
+ topic: narrow. This is debatable, but is probably better than
what was happening before.
Modified significantly by tabbott for cleanliness of implementation,
this commit message, and unit tests.
Fixes #9893. Follow-up to #12556.
2019-07-22 01:55:04 +02:00
|
|
|
final_select_id: 42,
|
|
|
|
local_select_id: 42,
|
2018-06-01 14:17:12 +02:00
|
|
|
},
|
|
|
|
expected_msg_ids: [37, 42, 44],
|
|
|
|
};
|
|
|
|
|
|
|
|
test_with(fixture);
|
|
|
|
});
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
run_test("near not in message list", () => {
|
narrow: Use search reading behavior in all searches.
In 452e226ea25c1315edb1f31be406475d82c56a88 and
648a60baf63f9afade83148bd9ae1fc480510178, we changed how `search:`
narrows work to:
(1) Never mark messages as read inside searches (search:)
(2) Take you to the bottom, not the first unread, if a `near:` or
similar wasn't specified.
This is far better behavior for these use cases, because in these
narrows, you can't actually see all the context around the target
messages, so marking them as read is counterproductive. This is
especially important in `has:mention` where you goal is likely
specifically to keep track of which threads mentioning you haven't
been read. But in many other narrows, the current behavior is
effectively (1) setting the read bit on random messages and (2) if the
search term matches many messages in a muted stream with 1000s of
unreads, making it hard or impossible to find recent search matches.
The new behavior is that any narrow that is structurally a search of
history (including everything that that isn't a stream, topic,
pm-with, "all messages" or "private messages") gets that new behavior
of being unable to mark messages as read and narrows taking you to the
latest matching messages.
A few corner cases of interest:
* `is:private` is keeping the old behavior, because users on
chat.zulip.org found it confusing for `is:private` to not mark
messages as read when one could see them all. Possibly a more
complex answer is required here.
* `near:` narrows are getting the new behavior, even if it's a stream:
+ topic: narrow. This is debatable, but is probably better than
what was happening before.
Modified significantly by tabbott for cleanliness of implementation,
this commit message, and unit tests.
Fixes #9893. Follow-up to #12556.
2019-07-22 01:55:04 +02:00
|
|
|
// Current behavior is to ignore the unreads and take you
|
|
|
|
// to the closest messages, with reading disabled.
|
2018-06-01 14:17:12 +02:00
|
|
|
const fixture = {
|
2020-07-15 00:34:28 +02:00
|
|
|
filter_terms: [{operator: "near", operand: 42}],
|
2018-06-01 14:17:12 +02:00
|
|
|
target_id: 42,
|
|
|
|
unread_info: {
|
2020-07-15 01:29:15 +02:00
|
|
|
flavor: "found",
|
2018-06-01 14:17:12 +02:00
|
|
|
msg_id: 46,
|
|
|
|
},
|
|
|
|
has_found_newest: false,
|
|
|
|
all_messages: [
|
2020-07-15 01:29:15 +02:00
|
|
|
{id: 41, topic: "whatever"},
|
|
|
|
{id: 45, topic: "whatever"},
|
|
|
|
{id: 46, topic: "whatever"},
|
2018-06-01 14:17:12 +02:00
|
|
|
],
|
|
|
|
expected_id_info: {
|
|
|
|
target_id: 42,
|
|
|
|
final_select_id: 42,
|
|
|
|
local_select_id: undefined,
|
|
|
|
},
|
narrow: Use search reading behavior in all searches.
In 452e226ea25c1315edb1f31be406475d82c56a88 and
648a60baf63f9afade83148bd9ae1fc480510178, we changed how `search:`
narrows work to:
(1) Never mark messages as read inside searches (search:)
(2) Take you to the bottom, not the first unread, if a `near:` or
similar wasn't specified.
This is far better behavior for these use cases, because in these
narrows, you can't actually see all the context around the target
messages, so marking them as read is counterproductive. This is
especially important in `has:mention` where you goal is likely
specifically to keep track of which threads mentioning you haven't
been read. But in many other narrows, the current behavior is
effectively (1) setting the read bit on random messages and (2) if the
search term matches many messages in a muted stream with 1000s of
unreads, making it hard or impossible to find recent search matches.
The new behavior is that any narrow that is structurally a search of
history (including everything that that isn't a stream, topic,
pm-with, "all messages" or "private messages") gets that new behavior
of being unable to mark messages as read and narrows taking you to the
latest matching messages.
A few corner cases of interest:
* `is:private` is keeping the old behavior, because users on
chat.zulip.org found it confusing for `is:private` to not mark
messages as read when one could see them all. Possibly a more
complex answer is required here.
* `near:` narrows are getting the new behavior, even if it's a stream:
+ topic: narrow. This is debatable, but is probably better than
what was happening before.
Modified significantly by tabbott for cleanliness of implementation,
this commit message, and unit tests.
Fixes #9893. Follow-up to #12556.
2019-07-22 01:55:04 +02:00
|
|
|
expected_msg_ids: [41, 45, 46],
|
2018-06-01 14:17:12 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
test_with(fixture);
|
|
|
|
});
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
run_test("near before unreads", () => {
|
2018-06-01 14:17:12 +02:00
|
|
|
const fixture = {
|
2020-07-15 00:34:28 +02:00
|
|
|
filter_terms: [{operator: "near", operand: 42}],
|
2018-06-01 14:17:12 +02:00
|
|
|
target_id: 42,
|
|
|
|
unread_info: {
|
2020-07-15 01:29:15 +02:00
|
|
|
flavor: "found",
|
2018-06-01 14:17:12 +02:00
|
|
|
msg_id: 43,
|
|
|
|
},
|
|
|
|
has_found_newest: false,
|
|
|
|
all_messages: [
|
2020-07-15 01:29:15 +02:00
|
|
|
{id: 42, topic: "whatever"},
|
|
|
|
{id: 43, topic: "whatever"},
|
|
|
|
{id: 44, topic: "whatever"},
|
2018-06-01 14:17:12 +02:00
|
|
|
],
|
|
|
|
expected_id_info: {
|
|
|
|
target_id: 42,
|
|
|
|
final_select_id: 42,
|
|
|
|
local_select_id: 42,
|
|
|
|
},
|
|
|
|
expected_msg_ids: [42, 43, 44],
|
|
|
|
};
|
|
|
|
|
|
|
|
test_with(fixture);
|
|
|
|
});
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
run_test("near with no unreads", () => {
|
2018-06-01 14:17:12 +02:00
|
|
|
const fixture = {
|
2020-07-15 00:34:28 +02:00
|
|
|
filter_terms: [{operator: "near", operand: 42}],
|
2018-06-01 14:17:12 +02:00
|
|
|
target_id: 42,
|
|
|
|
unread_info: {
|
2020-07-15 01:29:15 +02:00
|
|
|
flavor: "not_found",
|
2018-06-01 14:17:12 +02:00
|
|
|
},
|
|
|
|
has_found_newest: false,
|
|
|
|
empty: true,
|
|
|
|
expected_id_info: {
|
|
|
|
target_id: 42,
|
|
|
|
final_select_id: 42,
|
|
|
|
local_select_id: undefined,
|
|
|
|
},
|
|
|
|
expected_msg_ids: [],
|
|
|
|
};
|
|
|
|
|
|
|
|
test_with(fixture);
|
|
|
|
});
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
run_test("is private with no target", () => {
|
2018-06-01 14:17:12 +02:00
|
|
|
const fixture = {
|
2020-07-15 00:34:28 +02:00
|
|
|
filter_terms: [{operator: "is", operand: "private"}],
|
2018-06-01 14:17:12 +02:00
|
|
|
unread_info: {
|
2020-07-15 01:29:15 +02:00
|
|
|
flavor: "found",
|
2018-06-01 14:17:12 +02:00
|
|
|
msg_id: 550,
|
|
|
|
},
|
|
|
|
has_found_newest: true,
|
|
|
|
all_messages: [
|
2020-07-15 01:29:15 +02:00
|
|
|
{id: 450, type: "private"},
|
|
|
|
{id: 500, type: "private"},
|
|
|
|
{id: 550, type: "private"},
|
2018-06-01 14:17:12 +02:00
|
|
|
],
|
|
|
|
expected_id_info: {
|
|
|
|
target_id: undefined,
|
|
|
|
final_select_id: 550,
|
|
|
|
local_select_id: 550,
|
|
|
|
},
|
|
|
|
expected_msg_ids: [450, 500, 550],
|
|
|
|
};
|
|
|
|
|
|
|
|
test_with(fixture);
|
|
|
|
});
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
run_test("pm-with with target outside of range", () => {
|
2018-06-01 14:17:12 +02:00
|
|
|
const fixture = {
|
2020-07-15 00:34:28 +02:00
|
|
|
filter_terms: [{operator: "pm-with", operand: "alice@example.com"}],
|
2018-06-01 14:17:12 +02:00
|
|
|
target_id: 5,
|
|
|
|
unread_info: {
|
2020-07-15 01:29:15 +02:00
|
|
|
flavor: "not_found",
|
2018-06-01 14:17:12 +02:00
|
|
|
},
|
|
|
|
has_found_newest: false,
|
2020-07-15 00:34:28 +02:00
|
|
|
all_messages: [{id: 999}],
|
2018-06-01 14:17:12 +02:00
|
|
|
expected_id_info: {
|
|
|
|
target_id: 5,
|
|
|
|
final_select_id: 5,
|
|
|
|
local_select_id: undefined,
|
|
|
|
},
|
|
|
|
expected_msg_ids: [],
|
|
|
|
};
|
|
|
|
|
|
|
|
test_with(fixture);
|
|
|
|
});
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
run_test("is:private with no unreads before fetch", () => {
|
2018-06-01 14:17:12 +02:00
|
|
|
const fixture = {
|
2020-07-15 00:34:28 +02:00
|
|
|
filter_terms: [{operator: "is", operand: "private"}],
|
2018-06-01 14:17:12 +02:00
|
|
|
unread_info: {
|
2020-07-15 01:29:15 +02:00
|
|
|
flavor: "not_found",
|
2018-06-01 14:17:12 +02:00
|
|
|
},
|
|
|
|
has_found_newest: false,
|
|
|
|
empty: true,
|
|
|
|
expected_id_info: {
|
|
|
|
target_id: undefined,
|
|
|
|
final_select_id: undefined,
|
|
|
|
local_select_id: undefined,
|
|
|
|
},
|
|
|
|
expected_msg_ids: [],
|
|
|
|
};
|
|
|
|
|
|
|
|
test_with(fixture);
|
|
|
|
});
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
run_test("is:private with target and no unreads", () => {
|
2018-06-01 14:17:12 +02:00
|
|
|
const fixture = {
|
2020-07-15 00:34:28 +02:00
|
|
|
filter_terms: [{operator: "is", operand: "private"}],
|
2018-06-01 14:17:12 +02:00
|
|
|
target_id: 450,
|
|
|
|
unread_info: {
|
2020-07-15 01:29:15 +02:00
|
|
|
flavor: "not_found",
|
2018-06-01 14:17:12 +02:00
|
|
|
},
|
|
|
|
has_found_newest: true,
|
|
|
|
empty: false,
|
|
|
|
all_messages: [
|
|
|
|
{id: 350},
|
2020-07-15 01:29:15 +02:00
|
|
|
{id: 400, type: "private"},
|
|
|
|
{id: 450, type: "private"},
|
|
|
|
{id: 500, type: "private"},
|
2018-06-01 14:17:12 +02:00
|
|
|
],
|
|
|
|
expected_id_info: {
|
|
|
|
target_id: 450,
|
|
|
|
final_select_id: 450,
|
|
|
|
local_select_id: 450,
|
|
|
|
},
|
|
|
|
expected_msg_ids: [400, 450, 500],
|
|
|
|
};
|
|
|
|
|
|
|
|
test_with(fixture);
|
|
|
|
});
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
run_test("is:mentioned with no unreads and no matches", () => {
|
2018-06-01 14:17:12 +02:00
|
|
|
const fixture = {
|
2020-07-15 00:34:28 +02:00
|
|
|
filter_terms: [{operator: "is", operand: "mentioned"}],
|
2018-06-01 14:17:12 +02:00
|
|
|
unread_info: {
|
2020-07-15 01:29:15 +02:00
|
|
|
flavor: "not_found",
|
2018-06-01 14:17:12 +02:00
|
|
|
},
|
|
|
|
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);
|
|
|
|
});
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
run_test("is:alerted with no unreads and one match", () => {
|
2018-06-01 14:17:12 +02:00
|
|
|
const fixture = {
|
2020-07-15 00:34:28 +02:00
|
|
|
filter_terms: [{operator: "is", operand: "alerted"}],
|
2018-06-01 14:17:12 +02:00
|
|
|
unread_info: {
|
2020-07-15 01:29:15 +02:00
|
|
|
flavor: "not_found",
|
2018-06-01 14:17:12 +02:00
|
|
|
},
|
|
|
|
has_found_newest: true,
|
|
|
|
all_messages: [
|
2020-07-15 01:29:15 +02:00
|
|
|
{id: 55, topic: "whatever", alerted: true},
|
|
|
|
{id: 57, topic: "whatever", alerted: false},
|
2018-06-01 14:17:12 +02:00
|
|
|
],
|
|
|
|
expected_id_info: {
|
|
|
|
target_id: undefined,
|
|
|
|
final_select_id: 55,
|
|
|
|
local_select_id: 55,
|
|
|
|
},
|
|
|
|
expected_msg_ids: [55],
|
|
|
|
};
|
|
|
|
|
|
|
|
test_with(fixture);
|
|
|
|
});
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
run_test("search", () => {
|
2018-06-01 14:17:12 +02:00
|
|
|
const fixture = {
|
2020-07-15 00:34:28 +02:00
|
|
|
filter_terms: [{operator: "search", operand: "whatever"}],
|
2018-06-01 14:17:12 +02:00
|
|
|
unread_info: {
|
2020-07-15 01:29:15 +02:00
|
|
|
flavor: "cannot_compute",
|
2018-06-01 14:17:12 +02:00
|
|
|
},
|
|
|
|
expected_id_info: {
|
|
|
|
target_id: undefined,
|
2019-07-21 15:55:53 +02:00
|
|
|
final_select_id: 10000000000000000,
|
2018-06-01 14:17:12 +02:00
|
|
|
local_select_id: undefined,
|
|
|
|
},
|
|
|
|
expected_msg_ids: [],
|
|
|
|
};
|
|
|
|
|
|
|
|
test_with(fixture);
|
|
|
|
});
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
run_test("search near", () => {
|
2018-06-01 14:17:12 +02:00
|
|
|
const fixture = {
|
|
|
|
filter_terms: [
|
2020-07-15 01:29:15 +02:00
|
|
|
{operator: "search", operand: "whatever"},
|
|
|
|
{operator: "near", operand: 22},
|
2018-06-01 14:17:12 +02:00
|
|
|
],
|
|
|
|
target_id: 22,
|
|
|
|
unread_info: {
|
2020-07-15 01:29:15 +02:00
|
|
|
flavor: "cannot_compute",
|
2018-06-01 14:17:12 +02:00
|
|
|
},
|
|
|
|
expected_id_info: {
|
|
|
|
target_id: 22,
|
|
|
|
final_select_id: 22,
|
|
|
|
local_select_id: undefined,
|
|
|
|
},
|
|
|
|
expected_msg_ids: [],
|
|
|
|
};
|
|
|
|
|
|
|
|
test_with(fixture);
|
|
|
|
});
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
run_test("stream, no unread, not in all_messages", () => {
|
2018-06-01 14:17:12 +02:00
|
|
|
// 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 = {
|
2020-07-15 00:34:28 +02:00
|
|
|
filter_terms: [{operator: "stream", operand: "whatever"}],
|
2018-06-01 14:17:12 +02:00
|
|
|
target_id: 450,
|
|
|
|
unread_info: {
|
2020-07-15 01:29:15 +02:00
|
|
|
flavor: "not_found",
|
2018-06-01 14:17:12 +02:00
|
|
|
},
|
|
|
|
has_found_newest: true,
|
|
|
|
empty: false,
|
2020-07-15 00:34:28 +02:00
|
|
|
all_messages: [{id: 400}, {id: 500}],
|
2018-06-01 14:17:12 +02:00
|
|
|
expected_id_info: {
|
|
|
|
target_id: 450,
|
|
|
|
final_select_id: 450,
|
|
|
|
local_select_id: undefined,
|
|
|
|
},
|
|
|
|
expected_msg_ids: [],
|
|
|
|
};
|
|
|
|
|
|
|
|
test_with(fixture);
|
|
|
|
});
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
run_test("search, stream, not in all_messages", () => {
|
2019-07-21 15:55:53 +02:00
|
|
|
const fixture = {
|
|
|
|
filter_terms: [
|
2020-07-15 01:29:15 +02:00
|
|
|
{operator: "search", operand: "foo"},
|
|
|
|
{operator: "stream", operand: "whatever"},
|
2019-07-21 15:55:53 +02:00
|
|
|
],
|
|
|
|
unread_info: {
|
2020-07-15 01:29:15 +02:00
|
|
|
flavor: "cannot_compute",
|
2019-07-21 15:55:53 +02:00
|
|
|
},
|
|
|
|
has_found_newest: true,
|
|
|
|
empty: false,
|
2020-07-15 00:34:28 +02:00
|
|
|
all_messages: [{id: 400}, {id: 500}],
|
2019-07-21 15:55:53 +02:00
|
|
|
expected_id_info: {
|
|
|
|
target_id: undefined,
|
|
|
|
final_select_id: 10000000000000000,
|
|
|
|
local_select_id: undefined,
|
|
|
|
},
|
|
|
|
expected_msg_ids: [],
|
|
|
|
};
|
|
|
|
|
|
|
|
test_with(fixture);
|
|
|
|
});
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
run_test("stream/topic not in all_messages", () => {
|
2018-06-01 14:17:12 +02:00
|
|
|
// 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 message_list.all.
|
|
|
|
const fixture = {
|
|
|
|
filter_terms: [
|
2020-07-15 01:29:15 +02:00
|
|
|
{operator: "stream", operand: "one"},
|
|
|
|
{operator: "topic", operand: "whatever"},
|
2018-06-01 14:17:12 +02:00
|
|
|
],
|
|
|
|
target_id: 1000,
|
|
|
|
unread_info: {
|
2020-07-15 01:29:15 +02:00
|
|
|
flavor: "found",
|
2018-06-01 14:17:12 +02:00
|
|
|
msg_id: 2,
|
|
|
|
},
|
|
|
|
has_found_newest: true,
|
2020-07-15 00:34:28 +02:00
|
|
|
all_messages: [{id: 900}, {id: 1100}],
|
2018-06-01 14:17:12 +02:00
|
|
|
expected_id_info: {
|
|
|
|
target_id: 1000,
|
|
|
|
final_select_id: 2,
|
|
|
|
local_select_id: undefined,
|
|
|
|
},
|
|
|
|
expected_msg_ids: [],
|
|
|
|
};
|
|
|
|
|
|
|
|
test_with(fixture);
|
|
|
|
});
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
run_test("final corner case", () => {
|
2018-06-01 14:17:12 +02:00
|
|
|
// 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 = {
|
2020-07-15 00:34:28 +02:00
|
|
|
filter_terms: [{operator: "is", operand: "starred"}],
|
2018-06-01 14:17:12 +02:00
|
|
|
target_id: 450,
|
|
|
|
unread_info: {
|
2020-07-15 01:29:15 +02:00
|
|
|
flavor: "not_found",
|
2018-06-01 14:17:12 +02:00
|
|
|
},
|
|
|
|
has_found_newest: true,
|
|
|
|
empty: false,
|
|
|
|
all_messages: [
|
2020-07-15 01:29:15 +02:00
|
|
|
{id: 400, topic: "whatever"},
|
|
|
|
{id: 425, topic: "whatever", starred: true},
|
|
|
|
{id: 500, topic: "whatever"},
|
2018-06-01 14:17:12 +02:00
|
|
|
],
|
|
|
|
expected_id_info: {
|
|
|
|
target_id: 450,
|
|
|
|
final_select_id: 450,
|
|
|
|
local_select_id: undefined,
|
|
|
|
},
|
|
|
|
expected_msg_ids: [425],
|
|
|
|
};
|
|
|
|
|
|
|
|
test_with(fixture);
|
|
|
|
});
|