2020-08-01 03:43:15 +02:00
|
|
|
"use strict";
|
|
|
|
|
2024-10-09 00:25:41 +02:00
|
|
|
const assert = require("node:assert/strict");
|
2020-11-30 23:46:45 +01:00
|
|
|
|
2020-07-25 02:02:35 +02:00
|
|
|
const _ = require("lodash");
|
|
|
|
|
2023-02-22 23:04:10 +01:00
|
|
|
const {mock_esm, set_global, zrequire} = require("./lib/namespace");
|
2023-12-14 23:51:33 +01:00
|
|
|
const {run_test, noop} = require("./lib/test");
|
2023-02-22 23:04:10 +01:00
|
|
|
const $ = require("./lib/zjquery");
|
2020-12-01 00:02:16 +01:00
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
set_global("document", "document-stub");
|
2018-03-21 23:56:30 +01:00
|
|
|
|
2014-03-30 03:42:48 +02:00
|
|
|
// timerender calls setInterval when imported
|
2023-02-22 23:04:10 +01:00
|
|
|
mock_esm("../src/timerender", {
|
2022-09-29 19:17:17 +02:00
|
|
|
render_date(time) {
|
2024-09-04 21:06:34 +02:00
|
|
|
return {outerHTML: String(time.getTime())};
|
2016-12-03 23:17:57 +01:00
|
|
|
},
|
2020-07-20 22:18:43 +02:00
|
|
|
stringify_time(time) {
|
2020-07-15 01:29:15 +02:00
|
|
|
return time.toString("h:mm TT");
|
2017-12-25 21:43:06 +01:00
|
|
|
},
|
2014-03-30 03:42:48 +02:00
|
|
|
});
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2023-02-22 23:04:10 +01:00
|
|
|
mock_esm("../src/people", {
|
2022-02-09 20:51:57 +01:00
|
|
|
sender_is_bot: () => false,
|
|
|
|
sender_is_guest: () => false,
|
2023-09-13 19:30:52 +02:00
|
|
|
should_add_guest_user_indicator: () => false,
|
2022-02-09 20:51:57 +01:00
|
|
|
small_avatar_url: () => "fake/small/avatar/url",
|
|
|
|
});
|
|
|
|
|
2023-02-22 23:03:47 +01:00
|
|
|
const {Filter} = zrequire("../src/filter");
|
|
|
|
const {MessageListView} = zrequire("../src/message_list_view");
|
2020-12-01 23:21:38 +01:00
|
|
|
const message_list = zrequire("message_list");
|
2024-09-25 01:28:50 +02:00
|
|
|
const {MessageListData} = zrequire("message_list_data");
|
2021-06-27 21:38:26 +02:00
|
|
|
const muted_users = zrequire("muted_users");
|
2020-12-01 23:21:38 +01:00
|
|
|
|
2020-02-01 04:46:54 +01:00
|
|
|
let next_timestamp = 1500000000;
|
|
|
|
|
2021-05-19 17:15:23 +02:00
|
|
|
function test(label, f) {
|
2024-01-17 07:53:40 +01:00
|
|
|
run_test(label, ({override, mock_template}) => {
|
2021-06-27 21:38:26 +02:00
|
|
|
muted_users.set_muted_users([]);
|
2024-04-04 00:53:08 +02:00
|
|
|
mock_template("message_list.hbs", false, () => "<message-list-stub>");
|
2024-01-17 07:53:40 +01:00
|
|
|
f({override, mock_template});
|
2021-05-19 17:15:23 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-12-09 18:10:26 +01:00
|
|
|
test("msg_moved_var", () => {
|
|
|
|
// This is a test to verify that when the stream or topic is changed
|
|
|
|
// (and the content is not), the message says "MOVED" rather than "EDITED."
|
|
|
|
// See the end of the test for the list of cases verified.
|
|
|
|
|
|
|
|
function build_message_context(message = {}, message_context = {}) {
|
|
|
|
message_context = {
|
|
|
|
...message_context,
|
|
|
|
};
|
2022-02-09 02:52:39 +01:00
|
|
|
if ("edit_history" in message) {
|
|
|
|
message_context.msg = {
|
|
|
|
last_edit_timestamp: (next_timestamp += 1),
|
|
|
|
...message,
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
message_context.msg = {
|
|
|
|
...message,
|
|
|
|
};
|
|
|
|
}
|
2021-12-09 18:10:26 +01:00
|
|
|
return message_context;
|
|
|
|
}
|
|
|
|
|
|
|
|
function build_message_group(messages) {
|
|
|
|
return {message_containers: messages};
|
|
|
|
}
|
|
|
|
|
|
|
|
function build_list(message_groups) {
|
2024-01-17 07:53:40 +01:00
|
|
|
const list = new MessageListView(
|
|
|
|
{
|
|
|
|
id: 1,
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
true,
|
|
|
|
);
|
2021-12-09 18:10:26 +01:00
|
|
|
list._message_groups = message_groups;
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
function assert_moved_true(message_container) {
|
|
|
|
assert.equal(message_container.moved, true);
|
|
|
|
}
|
|
|
|
function assert_moved_false(message_container) {
|
|
|
|
assert.equal(message_container.moved, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
(function test_msg_moved_var() {
|
|
|
|
const messages = [
|
2022-02-09 02:52:39 +01:00
|
|
|
// no edit history: NO LABEL
|
|
|
|
build_message_context({}),
|
|
|
|
// stream changed: MOVED
|
2021-12-09 18:10:26 +01:00
|
|
|
build_message_context({
|
2022-02-09 02:52:39 +01:00
|
|
|
edit_history: [{prev_stream: 1, timestamp: 1000, user_id: 1}],
|
2021-12-09 18:10:26 +01:00
|
|
|
}),
|
2022-02-09 02:52:39 +01:00
|
|
|
// topic changed (not resolved/unresolved): MOVED
|
2021-12-09 18:10:26 +01:00
|
|
|
build_message_context({
|
2022-02-09 02:52:39 +01:00
|
|
|
edit_history: [
|
|
|
|
{prev_topic: "test_topic", topic: "new_topic", timestamp: 1000, user_id: 1},
|
|
|
|
],
|
2021-12-09 18:10:26 +01:00
|
|
|
}),
|
2022-02-09 02:52:39 +01:00
|
|
|
// content edited: EDITED
|
2021-12-09 18:10:26 +01:00
|
|
|
build_message_context({
|
|
|
|
edit_history: [{prev_content: "test_content", timestamp: 1000, user_id: 1}],
|
|
|
|
}),
|
2022-02-09 02:52:39 +01:00
|
|
|
// stream and topic edited: MOVED
|
2021-12-09 18:10:26 +01:00
|
|
|
build_message_context({
|
|
|
|
edit_history: [
|
2022-02-09 02:52:39 +01:00
|
|
|
{
|
|
|
|
prev_stream: 1,
|
|
|
|
prev_topic: "test_topic",
|
|
|
|
topic: "new_topic",
|
|
|
|
timestamp: 1000,
|
|
|
|
user_id: 1,
|
|
|
|
},
|
2021-12-09 18:10:26 +01:00
|
|
|
],
|
|
|
|
}),
|
2022-02-09 02:52:39 +01:00
|
|
|
// topic and content changed: EDITED
|
2021-12-09 18:10:26 +01:00
|
|
|
build_message_context({
|
|
|
|
edit_history: [
|
2022-02-09 02:52:39 +01:00
|
|
|
{
|
|
|
|
prev_topic: "test_topic",
|
|
|
|
topic: "new_topic",
|
|
|
|
prev_content: "test_content",
|
|
|
|
timestamp: 1000,
|
|
|
|
user_id: 1,
|
|
|
|
},
|
2021-12-09 18:10:26 +01:00
|
|
|
],
|
|
|
|
}),
|
2022-02-09 02:52:39 +01:00
|
|
|
// only topic resolved: NO LABEL
|
2021-12-09 18:10:26 +01:00
|
|
|
build_message_context({
|
|
|
|
edit_history: [
|
2022-02-09 02:52:39 +01:00
|
|
|
{prev_topic: "test_topic", topic: "✔ test_topic", timestamp: 1000, user_id: 1},
|
2021-12-09 18:10:26 +01:00
|
|
|
],
|
|
|
|
}),
|
2022-02-09 02:52:39 +01:00
|
|
|
// only topic unresolved: NO LABEL
|
2021-12-09 18:10:26 +01:00
|
|
|
build_message_context({
|
|
|
|
edit_history: [
|
2022-02-09 02:52:39 +01:00
|
|
|
{prev_topic: "✔ test_topic", topic: "test_topic", timestamp: 1000, user_id: 1},
|
|
|
|
],
|
|
|
|
}),
|
|
|
|
// multiple edit history logs, with at least one content edit: EDITED
|
|
|
|
build_message_context({
|
|
|
|
edit_history: [
|
|
|
|
{prev_stream: 1, timestamp: 1000, user_id: 1},
|
|
|
|
{prev_topic: "old_topic", topic: "test_topic", timestamp: 1001, user_id: 1},
|
2021-12-09 18:10:26 +01:00
|
|
|
{prev_content: "test_content", timestamp: 1002, user_id: 1},
|
2022-02-09 02:52:39 +01:00
|
|
|
{prev_topic: "test_topic", topic: "✔ test_topic", timestamp: 1003, user_id: 1},
|
|
|
|
],
|
|
|
|
}),
|
|
|
|
// multiple edit history logs with no content edit: MOVED
|
|
|
|
build_message_context({
|
|
|
|
edit_history: [
|
|
|
|
{prev_stream: 1, timestamp: 1000, user_id: 1},
|
|
|
|
{prev_topic: "old_topic", topic: "test_topic", timestamp: 1001, user_id: 1},
|
|
|
|
{prev_topic: "test_topic", topic: "✔ test_topic", timestamp: 1002, user_id: 1},
|
|
|
|
{prev_topic: "✔ test_topic", topic: "test_topic", timestamp: 1003, user_id: 1},
|
2021-12-09 18:10:26 +01:00
|
|
|
],
|
|
|
|
}),
|
|
|
|
];
|
|
|
|
|
|
|
|
const message_group = build_message_group(messages);
|
|
|
|
const list = build_list([message_group]);
|
|
|
|
|
|
|
|
for (const message_container of messages) {
|
2024-08-22 01:40:27 +02:00
|
|
|
Object.assign(
|
|
|
|
message_container,
|
|
|
|
list._maybe_get_me_message(message_container.is_hidden, message_container.msg),
|
2024-09-05 05:45:35 +02:00
|
|
|
list._get_message_edited_vars(message_container.msg),
|
2024-08-22 01:40:27 +02:00
|
|
|
);
|
2021-12-09 18:10:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const result = list._message_groups[0].message_containers;
|
|
|
|
|
2024-09-12 00:04:13 +02:00
|
|
|
// no edit history: false
|
|
|
|
assert_moved_false(result[0]);
|
2021-12-09 18:10:26 +01:00
|
|
|
// stream changed: true
|
|
|
|
assert_moved_true(result[1]);
|
|
|
|
// topic changed: true
|
|
|
|
assert_moved_true(result[2]);
|
|
|
|
// content edited: false
|
|
|
|
assert_moved_false(result[3]);
|
|
|
|
// stream and topic edited: true
|
|
|
|
assert_moved_true(result[4]);
|
|
|
|
// topic and content changed: false
|
|
|
|
assert_moved_false(result[5]);
|
2024-09-12 00:04:13 +02:00
|
|
|
// only topic resolved: false
|
|
|
|
assert_moved_false(result[6]);
|
|
|
|
// only topic unresolved: false
|
|
|
|
assert_moved_false(result[7]);
|
2022-02-09 02:52:39 +01:00
|
|
|
// multiple edits with content edit: false
|
|
|
|
assert_moved_false(result[8]);
|
|
|
|
// multiple edits without content edit: true
|
|
|
|
assert_moved_true(result[9]);
|
2021-12-09 18:10:26 +01:00
|
|
|
})();
|
|
|
|
});
|
|
|
|
|
2024-09-05 05:45:35 +02:00
|
|
|
test("message_edited_vars", () => {
|
2019-03-15 19:42:24 +01:00
|
|
|
// This is a test to verify that only one of the three bools,
|
2024-02-09 15:14:29 +01:00
|
|
|
// `message_edit_notices_in_left_col`, `message_edit_notices_alongside_sender`,
|
|
|
|
// `message_edit_notices_for_status_message` is not false; Tests for three
|
|
|
|
// different kinds of messages:
|
2019-03-15 19:42:24 +01:00
|
|
|
// * "/me" message
|
|
|
|
// * message that includes sender
|
|
|
|
// * message without sender
|
|
|
|
|
2021-03-24 21:44:43 +01:00
|
|
|
function build_message_context(message = {}, message_context = {}) {
|
2020-02-09 04:21:30 +01:00
|
|
|
message_context = {
|
2019-03-15 19:42:24 +01:00
|
|
|
include_sender: true,
|
2020-02-09 04:21:30 +01:00
|
|
|
...message_context,
|
|
|
|
};
|
|
|
|
message_context.msg = {
|
2019-03-15 19:42:24 +01:00
|
|
|
is_me_message: false,
|
2020-07-15 00:34:28 +02:00
|
|
|
last_edit_timestamp: (next_timestamp += 1),
|
2022-02-09 02:52:39 +01:00
|
|
|
edit_history: [{prev_content: "test_content", timestamp: 1000, user_id: 1}],
|
2020-02-09 04:21:30 +01:00
|
|
|
...message,
|
|
|
|
};
|
2019-03-15 19:42:24 +01:00
|
|
|
return message_context;
|
|
|
|
}
|
|
|
|
|
|
|
|
function build_message_group(messages) {
|
2020-07-16 22:40:18 +02:00
|
|
|
return {message_containers: messages};
|
2019-03-15 19:42:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function build_list(message_groups) {
|
2024-01-17 07:53:40 +01:00
|
|
|
const list = new MessageListView(
|
|
|
|
{
|
|
|
|
id: 1,
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
true,
|
|
|
|
);
|
2019-03-15 19:42:24 +01:00
|
|
|
list._message_groups = message_groups;
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
function assert_left_col(message_container) {
|
2024-02-09 15:14:29 +01:00
|
|
|
assert.equal(message_container.modified, true);
|
|
|
|
assert.equal(message_container.message_edit_notices_in_left_col, true);
|
|
|
|
assert.equal(message_container.message_edit_notices_alongside_sender, false);
|
|
|
|
assert.equal(message_container.message_edit_notices_for_status_message, false);
|
2019-03-15 19:42:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function assert_alongside_sender(message_container) {
|
2024-02-09 15:14:29 +01:00
|
|
|
assert.equal(message_container.modified, true);
|
|
|
|
assert.equal(message_container.message_edit_notices_in_left_col, false);
|
|
|
|
assert.equal(message_container.message_edit_notices_alongside_sender, true);
|
|
|
|
assert.equal(message_container.message_edit_notices_for_status_message, false);
|
2019-03-15 19:42:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function assert_status_msg(message_container) {
|
2024-02-09 15:14:29 +01:00
|
|
|
assert.equal(message_container.modified, true);
|
|
|
|
assert.equal(message_container.message_edit_notices_in_left_col, false);
|
|
|
|
assert.equal(message_container.message_edit_notices_alongside_sender, false);
|
|
|
|
assert.equal(message_container.message_edit_notices_for_status_message, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
function set_edited_notice_locations(message_container) {
|
|
|
|
const include_sender = message_container.include_sender;
|
|
|
|
const is_hidden = message_container.is_hidden;
|
|
|
|
const status_message = Boolean(message_container.status_message);
|
|
|
|
message_container.message_edit_notices_in_left_col = !include_sender && !is_hidden;
|
|
|
|
message_container.message_edit_notices_alongside_sender = include_sender && !status_message;
|
|
|
|
message_container.message_edit_notices_for_status_message =
|
|
|
|
include_sender && status_message;
|
2019-03-15 19:42:24 +01:00
|
|
|
}
|
|
|
|
|
2024-09-05 05:45:35 +02:00
|
|
|
(function test_message_edited_vars() {
|
2019-11-02 00:06:25 +01:00
|
|
|
const messages = [
|
2019-03-15 19:42:24 +01:00
|
|
|
build_message_context(),
|
2020-07-16 22:40:18 +02:00
|
|
|
build_message_context({}, {include_sender: false}),
|
|
|
|
build_message_context({is_me_message: true, content: "<p>/me test</p>"}),
|
2019-03-15 19:42:24 +01:00
|
|
|
];
|
2019-11-02 00:06:25 +01:00
|
|
|
const message_group = build_message_group(messages);
|
|
|
|
const list = build_list([message_group]);
|
2019-03-15 19:42:24 +01:00
|
|
|
|
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 message_container of messages) {
|
2024-08-22 01:40:27 +02:00
|
|
|
Object.assign(
|
|
|
|
message_container,
|
|
|
|
list._maybe_get_me_message(message_container.is_hidden, message_container.msg),
|
2024-09-05 05:45:35 +02:00
|
|
|
list._get_message_edited_vars(message_container.msg),
|
2024-08-22 01:40:27 +02:00
|
|
|
);
|
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
|
|
|
}
|
2019-03-15 19:42:24 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const result = list._message_groups[0].message_containers;
|
2019-03-15 19:42:24 +01:00
|
|
|
|
2024-02-09 15:14:29 +01:00
|
|
|
set_edited_notice_locations(result[0]);
|
2019-03-15 19:42:24 +01:00
|
|
|
assert_alongside_sender(result[0]);
|
2024-02-09 15:14:29 +01:00
|
|
|
|
|
|
|
set_edited_notice_locations(result[1]);
|
2019-03-15 19:42:24 +01:00
|
|
|
assert_left_col(result[1]);
|
2024-02-09 15:14:29 +01:00
|
|
|
|
|
|
|
set_edited_notice_locations(result[2]);
|
2019-03-15 19:42:24 +01:00
|
|
|
assert_status_msg(result[2]);
|
2020-07-16 22:35:58 +02:00
|
|
|
})();
|
2019-03-15 19:42:24 +01:00
|
|
|
});
|
|
|
|
|
2021-05-19 17:15:23 +02:00
|
|
|
test("muted_message_vars", () => {
|
message view: Hide messages sent by muted users.
* We hide the sender and reactions on messages sent by muted
users, and replace the content with a "This message was hidden"
dialog.
* Ideally, we should collapse a series of consequetive
messages sent by muted users into one such dialog, but
that could break the cursor behaviour and `near/<message_id`
links, so we as of now show one dialog per muted message.
* Because we hide the sender, there is a chance of the first
hidden message in a group looking like it was sent by the
author of the message above it. To tackle this, we intentionally
make the hidden message dialog float-left, so that it is clear
that this is a special type of message.
* For context, we still show the timestamp of the message.
* Starring, editing, deleting etc a message still work just like
before.
A further commit will add the ability to reveal a
hidden message.
2021-05-04 09:01:47 +02:00
|
|
|
// This verifies that the variables for muted/hidden messages are set
|
|
|
|
// correctly.
|
|
|
|
|
|
|
|
function build_message_context(message = {}, message_context = {}) {
|
|
|
|
message_context = {
|
|
|
|
...message_context,
|
|
|
|
};
|
|
|
|
message_context.msg = {
|
|
|
|
...message,
|
|
|
|
};
|
|
|
|
return message_context;
|
|
|
|
}
|
|
|
|
|
|
|
|
function build_message_group(messages) {
|
|
|
|
return {message_containers: messages};
|
|
|
|
}
|
|
|
|
|
|
|
|
function build_list(message_groups) {
|
2024-01-17 07:53:40 +01:00
|
|
|
const list = new MessageListView(
|
|
|
|
{
|
|
|
|
id: 1,
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
true,
|
|
|
|
);
|
message view: Hide messages sent by muted users.
* We hide the sender and reactions on messages sent by muted
users, and replace the content with a "This message was hidden"
dialog.
* Ideally, we should collapse a series of consequetive
messages sent by muted users into one such dialog, but
that could break the cursor behaviour and `near/<message_id`
links, so we as of now show one dialog per muted message.
* Because we hide the sender, there is a chance of the first
hidden message in a group looking like it was sent by the
author of the message above it. To tackle this, we intentionally
make the hidden message dialog float-left, so that it is clear
that this is a special type of message.
* For context, we still show the timestamp of the message.
* Starring, editing, deleting etc a message still work just like
before.
A further commit will add the ability to reveal a
hidden message.
2021-05-04 09:01:47 +02:00
|
|
|
list._message_groups = message_groups;
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
2024-09-05 07:03:11 +02:00
|
|
|
function calculate_variables(list, message_containers, is_revealed) {
|
|
|
|
for (const container of message_containers) {
|
|
|
|
Object.assign(
|
|
|
|
container,
|
|
|
|
list.get_calculated_message_container_variables(
|
|
|
|
container.msg,
|
|
|
|
container.include_sender,
|
|
|
|
is_revealed,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
message view: Hide messages sent by muted users.
* We hide the sender and reactions on messages sent by muted
users, and replace the content with a "This message was hidden"
dialog.
* Ideally, we should collapse a series of consequetive
messages sent by muted users into one such dialog, but
that could break the cursor behaviour and `near/<message_id`
links, so we as of now show one dialog per muted message.
* Because we hide the sender, there is a chance of the first
hidden message in a group looking like it was sent by the
author of the message above it. To tackle this, we intentionally
make the hidden message dialog float-left, so that it is clear
that this is a special type of message.
* For context, we still show the timestamp of the message.
* Starring, editing, deleting etc a message still work just like
before.
A further commit will add the ability to reveal a
hidden message.
2021-05-04 09:01:47 +02:00
|
|
|
return list._message_groups[0].message_containers;
|
|
|
|
}
|
|
|
|
|
|
|
|
(function test_hidden_message_variables() {
|
2022-09-03 20:48:38 +02:00
|
|
|
// We want to have no search results, which apparently works like this.
|
2024-06-06 21:48:31 +02:00
|
|
|
// See https://chat.zulip.org/#narrow/channel/6-frontend/topic/set_find_results.20with.20no.20results/near/1414799
|
2022-09-03 20:48:38 +02:00
|
|
|
const empty_list_stub = $.create("empty-stub", {children: []});
|
|
|
|
$("<message-stub-1>").set_find_results(".user-mention:not(.silent)", empty_list_stub);
|
|
|
|
$("<message-stub2>").set_find_results(".user-mention:not(.silent)", empty_list_stub);
|
|
|
|
$("<message-stub-3>").set_find_results(".user-mention:not(.silent)", empty_list_stub);
|
message view: Hide messages sent by muted users.
* We hide the sender and reactions on messages sent by muted
users, and replace the content with a "This message was hidden"
dialog.
* Ideally, we should collapse a series of consequetive
messages sent by muted users into one such dialog, but
that could break the cursor behaviour and `near/<message_id`
links, so we as of now show one dialog per muted message.
* Because we hide the sender, there is a chance of the first
hidden message in a group looking like it was sent by the
author of the message above it. To tackle this, we intentionally
make the hidden message dialog float-left, so that it is clear
that this is a special type of message.
* For context, we still show the timestamp of the message.
* Starring, editing, deleting etc a message still work just like
before.
A further commit will add the ability to reveal a
hidden message.
2021-05-04 09:01:47 +02:00
|
|
|
// Make a representative message group of three messages.
|
|
|
|
const messages = [
|
2022-09-03 20:48:38 +02:00
|
|
|
build_message_context(
|
|
|
|
{sender_id: 10, content: "<message-stub-1>"},
|
|
|
|
{include_sender: true},
|
|
|
|
),
|
|
|
|
build_message_context(
|
|
|
|
{mentioned: true, sender_id: 10, content: "<message-stub2>"},
|
|
|
|
{include_sender: false},
|
|
|
|
),
|
|
|
|
build_message_context(
|
|
|
|
{sender_id: 10, content: "<message-stub-3>"},
|
|
|
|
{include_sender: false},
|
|
|
|
),
|
message view: Hide messages sent by muted users.
* We hide the sender and reactions on messages sent by muted
users, and replace the content with a "This message was hidden"
dialog.
* Ideally, we should collapse a series of consequetive
messages sent by muted users into one such dialog, but
that could break the cursor behaviour and `near/<message_id`
links, so we as of now show one dialog per muted message.
* Because we hide the sender, there is a chance of the first
hidden message in a group looking like it was sent by the
author of the message above it. To tackle this, we intentionally
make the hidden message dialog float-left, so that it is clear
that this is a special type of message.
* For context, we still show the timestamp of the message.
* Starring, editing, deleting etc a message still work just like
before.
A further commit will add the ability to reveal a
hidden message.
2021-05-04 09:01:47 +02:00
|
|
|
];
|
|
|
|
const message_group = build_message_group(messages);
|
|
|
|
const list = build_list([message_group]);
|
2024-09-05 05:45:35 +02:00
|
|
|
list._get_message_edited_vars = noop;
|
message view: Hide messages sent by muted users.
* We hide the sender and reactions on messages sent by muted
users, and replace the content with a "This message was hidden"
dialog.
* Ideally, we should collapse a series of consequetive
messages sent by muted users into one such dialog, but
that could break the cursor behaviour and `near/<message_id`
links, so we as of now show one dialog per muted message.
* Because we hide the sender, there is a chance of the first
hidden message in a group looking like it was sent by the
author of the message above it. To tackle this, we intentionally
make the hidden message dialog float-left, so that it is clear
that this is a special type of message.
* For context, we still show the timestamp of the message.
* Starring, editing, deleting etc a message still work just like
before.
A further commit will add the ability to reveal a
hidden message.
2021-05-04 09:01:47 +02:00
|
|
|
|
|
|
|
// Sender is not muted.
|
|
|
|
let result = calculate_variables(list, messages);
|
|
|
|
|
2022-02-09 20:51:57 +01:00
|
|
|
// sanity check on mocked values
|
|
|
|
assert.equal(result[1].sender_is_bot, false);
|
|
|
|
assert.equal(result[1].sender_is_guest, false);
|
|
|
|
assert.equal(result[1].small_avatar_url, "fake/small/avatar/url");
|
|
|
|
|
message view: Hide messages sent by muted users.
* We hide the sender and reactions on messages sent by muted
users, and replace the content with a "This message was hidden"
dialog.
* Ideally, we should collapse a series of consequetive
messages sent by muted users into one such dialog, but
that could break the cursor behaviour and `near/<message_id`
links, so we as of now show one dialog per muted message.
* Because we hide the sender, there is a chance of the first
hidden message in a group looking like it was sent by the
author of the message above it. To tackle this, we intentionally
make the hidden message dialog float-left, so that it is clear
that this is a special type of message.
* For context, we still show the timestamp of the message.
* Starring, editing, deleting etc a message still work just like
before.
A further commit will add the ability to reveal a
hidden message.
2021-05-04 09:01:47 +02:00
|
|
|
// Check that `is_hidden` is false on all messages, and `include_sender` has not changed.
|
|
|
|
assert.equal(result[0].is_hidden, false);
|
|
|
|
assert.equal(result[1].is_hidden, false);
|
|
|
|
assert.equal(result[2].is_hidden, false);
|
|
|
|
|
|
|
|
assert.equal(result[0].include_sender, true);
|
|
|
|
assert.equal(result[1].include_sender, false);
|
|
|
|
assert.equal(result[2].include_sender, false);
|
|
|
|
|
2022-09-03 20:48:38 +02:00
|
|
|
// Additionally test that the message with a mention is marked as such.
|
|
|
|
assert.equal(result[1].mention_classname, "group_mention");
|
message view: Hide messages sent by muted users.
* We hide the sender and reactions on messages sent by muted
users, and replace the content with a "This message was hidden"
dialog.
* Ideally, we should collapse a series of consequetive
messages sent by muted users into one such dialog, but
that could break the cursor behaviour and `near/<message_id`
links, so we as of now show one dialog per muted message.
* Because we hide the sender, there is a chance of the first
hidden message in a group looking like it was sent by the
author of the message above it. To tackle this, we intentionally
make the hidden message dialog float-left, so that it is clear
that this is a special type of message.
* For context, we still show the timestamp of the message.
* Starring, editing, deleting etc a message still work just like
before.
A further commit will add the ability to reveal a
hidden message.
2021-05-04 09:01:47 +02:00
|
|
|
|
|
|
|
// Now, mute the sender.
|
2021-06-27 21:38:26 +02:00
|
|
|
muted_users.add_muted_user(10);
|
message view: Hide messages sent by muted users.
* We hide the sender and reactions on messages sent by muted
users, and replace the content with a "This message was hidden"
dialog.
* Ideally, we should collapse a series of consequetive
messages sent by muted users into one such dialog, but
that could break the cursor behaviour and `near/<message_id`
links, so we as of now show one dialog per muted message.
* Because we hide the sender, there is a chance of the first
hidden message in a group looking like it was sent by the
author of the message above it. To tackle this, we intentionally
make the hidden message dialog float-left, so that it is clear
that this is a special type of message.
* For context, we still show the timestamp of the message.
* Starring, editing, deleting etc a message still work just like
before.
A further commit will add the ability to reveal a
hidden message.
2021-05-04 09:01:47 +02:00
|
|
|
result = calculate_variables(list, messages);
|
|
|
|
|
|
|
|
// Check that `is_hidden` is true and `include_sender` is false on all messages.
|
|
|
|
assert.equal(result[0].is_hidden, true);
|
|
|
|
assert.equal(result[1].is_hidden, true);
|
|
|
|
assert.equal(result[2].is_hidden, true);
|
|
|
|
|
|
|
|
assert.equal(result[0].include_sender, false);
|
|
|
|
assert.equal(result[1].include_sender, false);
|
|
|
|
assert.equal(result[2].include_sender, false);
|
|
|
|
|
2022-09-03 20:48:38 +02:00
|
|
|
// Additionally test that, both there is no mention classname even on that message
|
|
|
|
// which has a mention, since we don't want to display muted mentions so visibly.
|
2024-09-12 05:12:16 +02:00
|
|
|
assert.equal(result[1].mention_classname, undefined);
|
2021-05-04 19:17:18 +02:00
|
|
|
|
|
|
|
// Now, reveal the hidden messages.
|
2021-05-20 11:37:57 +02:00
|
|
|
let is_revealed = true;
|
2021-05-04 19:17:18 +02:00
|
|
|
result = calculate_variables(list, messages, is_revealed);
|
|
|
|
|
|
|
|
// Check that `is_hidden` is false and `include_sender` is true on all messages.
|
|
|
|
assert.equal(result[0].is_hidden, false);
|
|
|
|
assert.equal(result[1].is_hidden, false);
|
|
|
|
assert.equal(result[2].is_hidden, false);
|
|
|
|
|
|
|
|
assert.equal(result[0].include_sender, true);
|
|
|
|
assert.equal(result[1].include_sender, true);
|
|
|
|
assert.equal(result[2].include_sender, true);
|
|
|
|
|
2022-09-03 20:48:38 +02:00
|
|
|
// Additionally test that the message with a mention is marked as such.
|
|
|
|
assert.equal(result[1].mention_classname, "group_mention");
|
2021-05-20 11:37:57 +02:00
|
|
|
|
2021-10-18 16:30:46 +02:00
|
|
|
// Now test rehiding muted user's message
|
2021-05-20 11:37:57 +02:00
|
|
|
is_revealed = false;
|
|
|
|
result = calculate_variables(list, messages, is_revealed);
|
|
|
|
|
|
|
|
// Check that `is_hidden` is false and `include_sender` is false on all messages.
|
|
|
|
assert.equal(result[0].is_hidden, true);
|
|
|
|
assert.equal(result[1].is_hidden, true);
|
|
|
|
assert.equal(result[2].is_hidden, true);
|
|
|
|
|
|
|
|
assert.equal(result[0].include_sender, false);
|
|
|
|
assert.equal(result[1].include_sender, false);
|
|
|
|
assert.equal(result[2].include_sender, false);
|
|
|
|
|
2022-09-03 20:48:38 +02:00
|
|
|
// Additionally test that, both there is no mention classname even on that message
|
|
|
|
// which has a mention, since we don't want to display hidden mentions so visibly.
|
2024-09-12 05:12:16 +02:00
|
|
|
assert.equal(result[1].mention_classname, undefined);
|
message view: Hide messages sent by muted users.
* We hide the sender and reactions on messages sent by muted
users, and replace the content with a "This message was hidden"
dialog.
* Ideally, we should collapse a series of consequetive
messages sent by muted users into one such dialog, but
that could break the cursor behaviour and `near/<message_id`
links, so we as of now show one dialog per muted message.
* Because we hide the sender, there is a chance of the first
hidden message in a group looking like it was sent by the
author of the message above it. To tackle this, we intentionally
make the hidden message dialog float-left, so that it is clear
that this is a special type of message.
* For context, we still show the timestamp of the message.
* Starring, editing, deleting etc a message still work just like
before.
A further commit will add the ability to reveal a
hidden message.
2021-05-04 09:01:47 +02:00
|
|
|
})();
|
|
|
|
});
|
|
|
|
|
2024-01-17 07:53:40 +01:00
|
|
|
test("merge_message_groups", ({mock_template}) => {
|
2024-04-04 00:53:08 +02:00
|
|
|
mock_template("message_list.hbs", false, () => "<message-list-stub>");
|
2017-01-14 03:15:22 +01:00
|
|
|
// MessageListView has lots of DOM code, so we are going to test the message
|
2022-02-08 00:13:33 +01:00
|
|
|
// group merging logic on its own.
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2021-03-24 21:44:43 +01:00
|
|
|
function build_message_context(message = {}, message_context = {}) {
|
2020-02-09 04:21:30 +01:00
|
|
|
message_context = {
|
2016-12-03 23:17:57 +01:00
|
|
|
include_sender: true,
|
2020-02-09 04:21:30 +01:00
|
|
|
...message_context,
|
|
|
|
};
|
|
|
|
message_context.msg = {
|
2020-07-15 01:29:15 +02:00
|
|
|
id: _.uniqueId("test_message_"),
|
2014-03-17 19:38:35 +01:00
|
|
|
status_message: false,
|
2020-07-15 01:29:15 +02:00
|
|
|
type: "stream",
|
2022-10-16 22:06:23 +02:00
|
|
|
stream_id: 2,
|
2022-09-28 21:42:53 +02:00
|
|
|
topic: "Test topic 1",
|
2020-07-15 01:29:15 +02:00
|
|
|
sender_email: "test@example.com",
|
2020-07-15 00:34:28 +02:00
|
|
|
timestamp: (next_timestamp += 1),
|
2020-02-09 04:21:30 +01:00
|
|
|
...message,
|
|
|
|
};
|
2014-03-17 19:38:35 +01:00
|
|
|
return message_context;
|
2014-03-08 08:59:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function build_message_group(messages) {
|
|
|
|
return {
|
2014-03-17 19:38:35 +01:00
|
|
|
message_containers: messages,
|
2020-07-15 01:29:15 +02:00
|
|
|
message_group_id: _.uniqueId("test_message_group_"),
|
2014-03-08 08:59:38 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function build_list(message_groups) {
|
2022-08-09 23:20:48 +02:00
|
|
|
const filter = new Filter([{operator: "stream", operand: "foo"}]);
|
|
|
|
|
|
|
|
const list = new message_list.MessageList({
|
2024-09-25 01:28:50 +02:00
|
|
|
data: new MessageListData({
|
|
|
|
excludes_muted_topics: false,
|
|
|
|
filter,
|
|
|
|
}),
|
2024-01-17 07:53:40 +01:00
|
|
|
is_node_test: true,
|
2022-08-09 23:20:48 +02:00
|
|
|
});
|
|
|
|
|
2024-01-17 07:53:40 +01:00
|
|
|
const view = new MessageListView(list, true, true);
|
2022-08-09 23:20:48 +02:00
|
|
|
view._message_groups = message_groups;
|
2023-12-14 23:51:33 +01:00
|
|
|
view.list.unsubscribed_bookend_content = noop;
|
|
|
|
view.list.subscribed_bookend_content = noop;
|
2022-08-09 23:20:48 +02:00
|
|
|
return view;
|
2014-03-08 08:59:38 +01:00
|
|
|
}
|
|
|
|
|
2018-12-25 17:46:19 +01:00
|
|
|
function extract_message_ids(lst) {
|
2020-07-02 01:39:34 +02:00
|
|
|
return lst.map((item) => item.msg.id);
|
2018-12-25 17:46:19 +01:00
|
|
|
}
|
|
|
|
|
2014-03-08 08:59:38 +01:00
|
|
|
function assert_message_list_equal(list1, list2) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const ids1 = extract_message_ids(list1);
|
|
|
|
const ids2 = extract_message_ids(list2);
|
2021-06-10 08:32:54 +02:00
|
|
|
assert.ok(ids1.length);
|
2018-12-25 17:46:19 +01:00
|
|
|
assert.deepEqual(ids1, ids2);
|
|
|
|
}
|
|
|
|
|
|
|
|
function extract_group(group) {
|
|
|
|
return extract_message_ids(group.message_containers);
|
2014-03-08 08:59:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function assert_message_groups_list_equal(list1, list2) {
|
2021-01-23 02:36:54 +01:00
|
|
|
const ids1 = list1.map((group) => extract_group(group));
|
|
|
|
const ids2 = list2.map((group) => extract_group(group));
|
2021-06-10 08:32:54 +02:00
|
|
|
assert.ok(ids1.length);
|
2018-12-25 17:46:19 +01:00
|
|
|
assert.deepEqual(ids1, ids2);
|
2014-03-08 08:59:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
(function test_empty_list_bottom() {
|
2019-11-02 00:06:25 +01:00
|
|
|
const list = build_list([]);
|
2020-07-15 00:34:28 +02:00
|
|
|
const message_group = build_message_group([build_message_context()]);
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
const result = list.merge_message_groups([message_group], "bottom");
|
2014-03-08 08:59:38 +01:00
|
|
|
|
|
|
|
assert_message_groups_list_equal(list._message_groups, [message_group]);
|
|
|
|
assert_message_groups_list_equal(result.append_groups, [message_group]);
|
2018-12-25 17:46:19 +01:00
|
|
|
assert.deepEqual(result.prepend_groups, []);
|
|
|
|
assert.deepEqual(result.rerender_groups, []);
|
|
|
|
assert.deepEqual(result.append_messages, []);
|
2020-07-16 22:35:58 +02:00
|
|
|
})();
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2022-09-28 21:42:53 +02:00
|
|
|
(function test_append_message_same_topic() {
|
2019-11-02 00:06:25 +01:00
|
|
|
const message1 = build_message_context();
|
2020-07-15 00:34:28 +02:00
|
|
|
const message_group1 = build_message_group([message1]);
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const message2 = build_message_context();
|
2020-07-15 00:34:28 +02:00
|
|
|
const message_group2 = build_message_group([message2]);
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const list = build_list([message_group1]);
|
2020-07-15 01:29:15 +02:00
|
|
|
const result = list.merge_message_groups([message_group2], "bottom");
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2020-07-15 00:34:28 +02:00
|
|
|
assert_message_groups_list_equal(list._message_groups, [
|
|
|
|
build_message_group([message1, message2]),
|
|
|
|
]);
|
2018-12-25 17:46:19 +01:00
|
|
|
assert.deepEqual(result.append_groups, []);
|
|
|
|
assert.deepEqual(result.prepend_groups, []);
|
|
|
|
assert.deepEqual(result.rerender_groups, []);
|
2014-03-08 08:59:38 +01:00
|
|
|
assert_message_list_equal(result.append_messages, [message2]);
|
2020-07-16 22:35:58 +02:00
|
|
|
})();
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2022-09-28 21:42:53 +02:00
|
|
|
(function test_append_message_different_topic() {
|
2019-11-02 00:06:25 +01:00
|
|
|
const message1 = build_message_context();
|
2020-07-15 00:34:28 +02:00
|
|
|
const message_group1 = build_message_group([message1]);
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2022-09-28 21:42:53 +02:00
|
|
|
const message2 = build_message_context({topic: "Test topic 2"});
|
2020-07-15 00:34:28 +02:00
|
|
|
const message_group2 = build_message_group([message2]);
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const list = build_list([message_group1]);
|
2020-07-15 01:29:15 +02:00
|
|
|
const result = list.merge_message_groups([message_group2], "bottom");
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2023-10-17 02:21:17 +02:00
|
|
|
assert.ok(!message_group2.date_unchanged, true);
|
2020-07-15 00:34:28 +02:00
|
|
|
assert_message_groups_list_equal(list._message_groups, [message_group1, message_group2]);
|
2014-03-08 08:59:38 +01:00
|
|
|
assert_message_groups_list_equal(result.append_groups, [message_group2]);
|
2018-12-25 17:46:19 +01:00
|
|
|
assert.deepEqual(result.prepend_groups, []);
|
|
|
|
assert.deepEqual(result.rerender_groups, []);
|
|
|
|
assert.deepEqual(result.append_messages, []);
|
2020-07-16 22:35:58 +02:00
|
|
|
})();
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2022-09-28 21:42:53 +02:00
|
|
|
(function test_append_message_different_topic_and_days() {
|
2019-11-02 00:06:25 +01:00
|
|
|
const message1 = build_message_context({timestamp: 1000});
|
2020-07-15 00:34:28 +02:00
|
|
|
const message_group1 = build_message_group([message1]);
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2022-09-28 21:42:53 +02:00
|
|
|
const message2 = build_message_context({topic: "Test topic 2", timestamp: 900000});
|
2020-07-15 00:34:28 +02:00
|
|
|
const message_group2 = build_message_group([message2]);
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const list = build_list([message_group1]);
|
2020-07-15 01:29:15 +02:00
|
|
|
const result = list.merge_message_groups([message_group2], "bottom");
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2020-07-15 00:34:28 +02:00
|
|
|
assert_message_groups_list_equal(list._message_groups, [message_group1, message_group2]);
|
2014-03-08 08:59:38 +01:00
|
|
|
assert_message_groups_list_equal(result.append_groups, [message_group2]);
|
2018-12-25 17:46:19 +01:00
|
|
|
assert.deepEqual(result.prepend_groups, []);
|
|
|
|
assert.deepEqual(result.rerender_groups, []);
|
|
|
|
assert.deepEqual(result.append_messages, []);
|
2023-10-17 02:21:17 +02:00
|
|
|
assert.equal(message_group2.date_unchanged, false);
|
2020-07-16 22:35:58 +02:00
|
|
|
})();
|
2019-02-08 21:11:55 +01:00
|
|
|
|
|
|
|
(function test_append_message_different_day() {
|
2019-11-02 00:06:25 +01:00
|
|
|
const message1 = build_message_context({timestamp: 1000});
|
2020-07-15 00:34:28 +02:00
|
|
|
const message_group1 = build_message_group([message1]);
|
2019-02-08 21:11:55 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const message2 = build_message_context({timestamp: 900000});
|
2020-07-15 00:34:28 +02:00
|
|
|
const message_group2 = build_message_group([message2]);
|
2019-02-08 21:11:55 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const list = build_list([message_group1]);
|
2020-07-15 01:29:15 +02:00
|
|
|
const result = list.merge_message_groups([message_group2], "bottom");
|
2019-02-08 21:11:55 +01:00
|
|
|
|
|
|
|
assert_message_groups_list_equal(list._message_groups, [message_group1]);
|
|
|
|
assert.deepEqual(result.append_groups, []);
|
|
|
|
assert.deepEqual(result.prepend_groups, []);
|
|
|
|
assert.deepEqual(result.rerender_groups, []);
|
|
|
|
assert.deepEqual(result.append_messages, [message2]);
|
2021-06-10 08:32:54 +02:00
|
|
|
assert.ok(list._message_groups[0].message_containers[1].want_date_divider);
|
2020-07-16 22:35:58 +02:00
|
|
|
})();
|
2014-03-08 08:59:38 +01:00
|
|
|
|
|
|
|
(function test_append_message_historical() {
|
2019-11-02 00:06:25 +01:00
|
|
|
const message1 = build_message_context({historical: false});
|
2020-07-15 00:34:28 +02:00
|
|
|
const message_group1 = build_message_group([message1]);
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const message2 = build_message_context({historical: true});
|
2020-07-15 00:34:28 +02:00
|
|
|
const message_group2 = build_message_group([message2]);
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const list = build_list([message_group1]);
|
2020-07-15 01:29:15 +02:00
|
|
|
const result = list.merge_message_groups([message_group2], "bottom");
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2021-06-10 08:32:54 +02:00
|
|
|
assert.ok(message_group2.bookend_top);
|
2020-07-15 00:34:28 +02:00
|
|
|
assert_message_groups_list_equal(list._message_groups, [message_group1, message_group2]);
|
2014-03-08 08:59:38 +01:00
|
|
|
assert_message_groups_list_equal(result.append_groups, [message_group2]);
|
2018-12-25 17:46:19 +01:00
|
|
|
assert.deepEqual(result.prepend_groups, []);
|
|
|
|
assert.deepEqual(result.rerender_groups, []);
|
|
|
|
assert.deepEqual(result.append_messages, []);
|
2020-07-16 22:35:58 +02:00
|
|
|
})();
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2022-09-28 21:42:53 +02:00
|
|
|
(function test_append_message_same_topic_me_message() {
|
2019-11-02 00:06:25 +01:00
|
|
|
const message1 = build_message_context();
|
2020-07-15 00:34:28 +02:00
|
|
|
const message_group1 = build_message_group([message1]);
|
2014-03-16 19:06:51 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const message2 = build_message_context({is_me_message: true});
|
2020-07-15 00:34:28 +02:00
|
|
|
const message_group2 = build_message_group([message2]);
|
2014-03-16 19:06:51 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const list = build_list([message_group1]);
|
2020-07-15 01:29:15 +02:00
|
|
|
const result = list.merge_message_groups([message_group2], "bottom");
|
2014-03-16 19:06:51 +01:00
|
|
|
|
2021-06-10 08:32:54 +02:00
|
|
|
assert.ok(message2.include_sender);
|
2020-07-15 00:34:28 +02:00
|
|
|
assert_message_groups_list_equal(list._message_groups, [
|
|
|
|
build_message_group([message1, message2]),
|
|
|
|
]);
|
2018-12-25 17:46:19 +01:00
|
|
|
assert.deepEqual(result.append_groups, []);
|
|
|
|
assert.deepEqual(result.prepend_groups, []);
|
|
|
|
assert.deepEqual(result.rerender_groups, []);
|
2014-03-16 19:06:51 +01:00
|
|
|
assert_message_list_equal(result.append_messages, [message2]);
|
2020-07-16 22:35:58 +02:00
|
|
|
})();
|
2014-03-16 19:06:51 +01:00
|
|
|
|
2022-09-28 21:42:53 +02:00
|
|
|
(function test_prepend_message_same_topic() {
|
2019-11-02 00:06:25 +01:00
|
|
|
const message1 = build_message_context();
|
2020-07-15 00:34:28 +02:00
|
|
|
const message_group1 = build_message_group([message1]);
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const message2 = build_message_context();
|
2020-07-15 00:34:28 +02:00
|
|
|
const message_group2 = build_message_group([message2]);
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const list = build_list([message_group1]);
|
2020-07-15 01:29:15 +02:00
|
|
|
const result = list.merge_message_groups([message_group2], "top");
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2020-07-15 00:34:28 +02:00
|
|
|
assert_message_groups_list_equal(list._message_groups, [
|
|
|
|
build_message_group([message2, message1]),
|
|
|
|
]);
|
2018-12-25 17:46:19 +01:00
|
|
|
assert.deepEqual(result.append_groups, []);
|
|
|
|
assert.deepEqual(result.prepend_groups, []);
|
2020-07-15 00:34:28 +02:00
|
|
|
assert_message_groups_list_equal(result.rerender_groups, [
|
|
|
|
build_message_group([message2, message1]),
|
|
|
|
]);
|
2018-12-25 17:46:19 +01:00
|
|
|
assert.deepEqual(result.append_messages, []);
|
2020-07-16 22:35:58 +02:00
|
|
|
})();
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2022-09-28 21:42:53 +02:00
|
|
|
(function test_prepend_message_different_topic() {
|
2019-11-02 00:06:25 +01:00
|
|
|
const message1 = build_message_context();
|
2020-07-15 00:34:28 +02:00
|
|
|
const message_group1 = build_message_group([message1]);
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2022-09-28 21:42:53 +02:00
|
|
|
const message2 = build_message_context({topic: "Test topic 2"});
|
2020-07-15 00:34:28 +02:00
|
|
|
const message_group2 = build_message_group([message2]);
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const list = build_list([message_group1]);
|
2020-07-15 01:29:15 +02:00
|
|
|
const result = list.merge_message_groups([message_group2], "top");
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2020-07-15 00:34:28 +02:00
|
|
|
assert_message_groups_list_equal(list._message_groups, [message_group2, message_group1]);
|
2018-12-25 17:46:19 +01:00
|
|
|
assert.deepEqual(result.append_groups, []);
|
2014-03-08 08:59:38 +01:00
|
|
|
assert_message_groups_list_equal(result.prepend_groups, [message_group2]);
|
2018-12-25 17:46:19 +01:00
|
|
|
assert.deepEqual(result.rerender_groups, []);
|
|
|
|
assert.deepEqual(result.append_messages, []);
|
2020-07-16 22:35:58 +02:00
|
|
|
})();
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2022-09-28 21:42:53 +02:00
|
|
|
(function test_prepend_message_different_topic_and_day() {
|
2019-11-02 00:06:25 +01:00
|
|
|
const message1 = build_message_context({timestamp: 900000});
|
2020-07-15 00:34:28 +02:00
|
|
|
const message_group1 = build_message_group([message1]);
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2022-09-28 21:42:53 +02:00
|
|
|
const message2 = build_message_context({topic: "Test topic 2", timestamp: 1000});
|
2020-07-15 00:34:28 +02:00
|
|
|
const message_group2 = build_message_group([message2]);
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const list = build_list([message_group1]);
|
2020-07-15 01:29:15 +02:00
|
|
|
const result = list.merge_message_groups([message_group2], "top");
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2023-10-17 02:21:17 +02:00
|
|
|
assert.equal(message_group1.date_unchanged, false);
|
2020-07-15 00:34:28 +02:00
|
|
|
assert_message_groups_list_equal(list._message_groups, [message_group2, message_group1]);
|
2018-12-25 17:46:19 +01:00
|
|
|
assert.deepEqual(result.append_groups, []);
|
2014-03-08 08:59:38 +01:00
|
|
|
assert_message_groups_list_equal(result.prepend_groups, [message_group2]);
|
2019-02-08 21:11:55 +01:00
|
|
|
assert.deepEqual(result.rerender_groups, [message_group1]);
|
|
|
|
assert.deepEqual(result.append_messages, []);
|
2020-07-16 22:35:58 +02:00
|
|
|
})();
|
2019-02-08 21:11:55 +01:00
|
|
|
|
|
|
|
(function test_prepend_message_different_day() {
|
2019-11-02 00:06:25 +01:00
|
|
|
const message1 = build_message_context({timestamp: 900000});
|
2020-07-15 00:34:28 +02:00
|
|
|
const message_group1 = build_message_group([message1]);
|
2019-02-08 21:11:55 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const message2 = build_message_context({timestamp: 1000});
|
2020-07-15 00:34:28 +02:00
|
|
|
const message_group2 = build_message_group([message2]);
|
2019-02-08 21:11:55 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const list = build_list([message_group1]);
|
2020-07-15 01:29:15 +02:00
|
|
|
const result = list.merge_message_groups([message_group2], "top");
|
2019-02-08 21:11:55 +01:00
|
|
|
|
2022-09-29 19:17:17 +02:00
|
|
|
assert.equal(message_group2.message_containers[1].date_divider_html, "900000000");
|
2020-07-15 00:34:28 +02:00
|
|
|
assert_message_groups_list_equal(list._message_groups, [message_group2]);
|
2019-02-08 21:11:55 +01:00
|
|
|
assert.deepEqual(result.append_groups, []);
|
|
|
|
assert.deepEqual(result.prepend_groups, []);
|
|
|
|
assert_message_groups_list_equal(result.rerender_groups, [message_group2]);
|
2018-12-25 17:46:19 +01:00
|
|
|
assert.deepEqual(result.append_messages, []);
|
2020-07-16 22:35:58 +02:00
|
|
|
})();
|
2014-03-08 08:59:38 +01:00
|
|
|
|
|
|
|
(function test_prepend_message_historical() {
|
2019-11-02 00:06:25 +01:00
|
|
|
const message1 = build_message_context({historical: false});
|
2020-07-15 00:34:28 +02:00
|
|
|
const message_group1 = build_message_group([message1]);
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const message2 = build_message_context({historical: true});
|
2020-07-15 00:34:28 +02:00
|
|
|
const message_group2 = build_message_group([message2]);
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const list = build_list([message_group1]);
|
2020-07-15 01:29:15 +02:00
|
|
|
const result = list.merge_message_groups([message_group2], "top");
|
2014-03-08 08:59:38 +01:00
|
|
|
|
2021-06-10 08:32:54 +02:00
|
|
|
assert.ok(message_group1.bookend_top);
|
2020-07-15 00:34:28 +02:00
|
|
|
assert_message_groups_list_equal(list._message_groups, [message_group2, message_group1]);
|
2018-12-25 17:46:19 +01:00
|
|
|
assert.deepEqual(result.append_groups, []);
|
2014-03-08 08:59:38 +01:00
|
|
|
assert_message_groups_list_equal(result.prepend_groups, [message_group2]);
|
2018-12-25 17:46:19 +01:00
|
|
|
assert.deepEqual(result.rerender_groups, []);
|
|
|
|
assert.deepEqual(result.append_messages, []);
|
2020-07-16 22:35:58 +02:00
|
|
|
})();
|
2018-05-15 12:40:07 +02:00
|
|
|
});
|
2018-03-21 23:56:30 +01:00
|
|
|
|
2024-01-17 07:53:40 +01:00
|
|
|
test("render_windows", ({mock_template}) => {
|
2024-04-04 00:53:08 +02:00
|
|
|
mock_template("message_list.hbs", false, () => "<message-list-stub>");
|
2018-03-21 23:56:30 +01:00
|
|
|
// We only render up to 400 messages at a time in our message list,
|
|
|
|
// and we only change the window (which is a range, really, with
|
|
|
|
// start/end) when the pointer moves outside of the window or close
|
|
|
|
// to the edges.
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const view = (function make_view() {
|
2024-01-07 16:38:29 +01:00
|
|
|
const filter = new Filter([]);
|
2018-03-21 23:56:30 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const list = new message_list.MessageList({
|
2024-09-25 01:28:50 +02:00
|
|
|
data: new MessageListData({
|
|
|
|
excludes_muted_topics: false,
|
|
|
|
filter,
|
|
|
|
}),
|
2024-01-17 07:53:40 +01:00
|
|
|
is_node_test: true,
|
2018-05-14 15:46:25 +02:00
|
|
|
});
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const view = list.view;
|
2018-03-21 23:56:30 +01:00
|
|
|
|
|
|
|
// Stub out functionality that is not core to the rendering window
|
|
|
|
// logic.
|
2021-02-23 14:37:26 +01:00
|
|
|
list.data.unmuted_messages = (messages) => messages;
|
2018-03-21 23:56:30 +01:00
|
|
|
|
|
|
|
// We don't need to actually render the DOM. The windowing logic
|
|
|
|
// sits above that layer.
|
|
|
|
view.render = noop;
|
|
|
|
view.rerender_preserving_scrolltop = noop;
|
|
|
|
|
|
|
|
return view;
|
2020-07-16 22:35:58 +02:00
|
|
|
})();
|
2018-03-21 23:56:30 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const list = view.list;
|
2018-03-21 23:56:30 +01:00
|
|
|
|
|
|
|
(function test_with_empty_list() {
|
|
|
|
// The function should early exit here.
|
2019-11-02 00:06:25 +01:00
|
|
|
const rendered = view.maybe_rerender();
|
2018-03-21 23:56:30 +01:00
|
|
|
assert.equal(rendered, false);
|
2020-07-16 22:35:58 +02:00
|
|
|
})();
|
2018-03-21 23:56:30 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
let messages;
|
2018-03-21 23:56:30 +01:00
|
|
|
|
|
|
|
function reset_list(opts) {
|
2020-07-02 01:39:34 +02:00
|
|
|
messages = _.range(opts.count).map((i) => ({
|
js: Convert _.map(a, …) to a.map(…).
And convert the corresponding function expressions to arrow style
while we’re here.
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 K from "ast-types/gen/kinds";
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);
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;
recast.visit(ast, {
visitCallExpression(path) {
const { callee, arguments: args } = path.node;
if (
n.MemberExpression.check(callee) &&
!callee.computed &&
n.Identifier.check(callee.object) &&
callee.object.name === "_" &&
n.Identifier.check(callee.property) &&
callee.property.name === "map" &&
args.length === 2 &&
checkExpression(args[0]) &&
checkExpression(args[1])
) {
const [arr, fn] = args;
path.replace(
b.callExpression(b.memberExpression(arr, b.identifier("map")), [
n.FunctionExpression.check(fn) ||
n.ArrowFunctionExpression.check(fn)
? b.arrowFunctionExpression(
fn.params,
n.BlockStatement.check(fn.body) &&
fn.body.body.length === 1 &&
n.ReturnStatement.check(fn.body.body[0])
? fn.body.body[0].argument || b.identifier("undefined")
: fn.body
)
: fn,
])
);
changed = true;
}
this.traverse(path);
},
});
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-08 02:43:49 +01:00
|
|
|
id: i,
|
|
|
|
}));
|
2021-02-23 14:37:26 +01:00
|
|
|
list.selected_idx = () => 0;
|
2024-01-17 07:53:40 +01:00
|
|
|
list.view.clear_table = noop;
|
2018-03-21 23:56:30 +01:00
|
|
|
list.clear();
|
|
|
|
|
|
|
|
list.add_messages(messages, {});
|
|
|
|
}
|
|
|
|
|
|
|
|
function verify_no_move_range(start, end) {
|
2024-07-24 03:09:57 +02:00
|
|
|
// In our render window, there are up to 150 positions in
|
2024-07-24 19:33:22 +02:00
|
|
|
// the list (with potentially 50 at the start if the range
|
|
|
|
// starts with 0) where we can move the pointer without forcing
|
2018-03-21 23:56:30 +01:00
|
|
|
// a re-render. The code avoids hasty re-renders for
|
|
|
|
// performance reasons.
|
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 idx of _.range(start, end)) {
|
2021-02-23 14:37:26 +01:00
|
|
|
list.selected_idx = () => idx;
|
2019-11-02 00:06:25 +01:00
|
|
|
const rendered = view.maybe_rerender();
|
2018-03-21 23:56:30 +01:00
|
|
|
assert.equal(rendered, false);
|
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-03-21 23:56:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function verify_move(idx, range) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const start = range[0];
|
|
|
|
const end = range[1];
|
2018-03-21 23:56:30 +01:00
|
|
|
|
2021-02-23 14:37:26 +01:00
|
|
|
list.selected_idx = () => idx;
|
2019-11-02 00:06:25 +01:00
|
|
|
const rendered = view.maybe_rerender();
|
2018-03-21 23:56:30 +01:00
|
|
|
assert.equal(rendered, true);
|
|
|
|
assert.equal(view._render_win_start, start);
|
|
|
|
assert.equal(view._render_win_end, end);
|
|
|
|
}
|
|
|
|
|
2024-07-24 19:33:22 +02:00
|
|
|
function verify_move_and_no_move_range(move_target, opts = {}) {
|
2024-07-24 03:09:57 +02:00
|
|
|
// When we move to position X, we expect 250/2 = 125 messages on
|
2024-07-24 19:33:22 +02:00
|
|
|
// either side, unless that goes outside the `count`, in which
|
|
|
|
// case we'll specify it in `opts`.
|
2024-07-24 03:09:57 +02:00
|
|
|
const move_start = opts.move_start ?? move_target - 125;
|
|
|
|
const move_end = opts.move_end ?? move_target + 125;
|
2024-07-24 19:33:22 +02:00
|
|
|
verify_move(move_target, [move_start, move_end]);
|
|
|
|
// the no-move range is a 50 buffer on each side
|
|
|
|
const no_move_start = opts.no_move_start ?? move_start + 50;
|
|
|
|
const no_move_end = move_end - 50;
|
|
|
|
verify_no_move_range(no_move_start, no_move_end);
|
|
|
|
}
|
|
|
|
|
2018-03-21 23:56:30 +01:00
|
|
|
reset_list({count: 51});
|
2024-07-24 19:33:22 +02:00
|
|
|
verify_no_move_range(0, 51); // This is the whole list
|
2018-03-21 23:56:30 +01:00
|
|
|
|
2024-08-16 03:24:43 +02:00
|
|
|
// Start a new list with more messages. Note that the order of
|
|
|
|
// these checks matters; each time we call `verify_move` or
|
|
|
|
// `verify_move_and_no_move_range`, we are moving the currently
|
|
|
|
// selected position in the list.
|
2018-03-21 23:56:30 +01:00
|
|
|
reset_list({count: 450});
|
2024-08-16 03:24:43 +02:00
|
|
|
|
2024-07-24 03:09:57 +02:00
|
|
|
// 250 messages rendered, with the last 50 in the move range
|
|
|
|
verify_no_move_range(0, 200);
|
2018-03-21 23:56:30 +01:00
|
|
|
|
2024-07-24 19:33:22 +02:00
|
|
|
verify_move_and_no_move_range(350, {
|
|
|
|
// top maxes out at 450
|
|
|
|
move_end: 450,
|
|
|
|
});
|
2018-03-21 23:56:30 +01:00
|
|
|
|
2024-07-24 03:09:57 +02:00
|
|
|
// We load more than 125 on the upper end, because we load the full 250
|
|
|
|
// messages and 124 is less than half of that.
|
|
|
|
verify_move_and_no_move_range(124, {
|
2024-07-24 19:33:22 +02:00
|
|
|
move_start: 0,
|
2024-07-24 03:09:57 +02:00
|
|
|
move_end: 250,
|
2024-07-24 19:33:22 +02:00
|
|
|
});
|
2018-03-21 23:56:30 +01:00
|
|
|
|
2024-08-16 03:24:43 +02:00
|
|
|
// If we now jump to a message ID close enough to the end of the
|
|
|
|
// range, the render window is limited.
|
2024-07-24 19:33:22 +02:00
|
|
|
verify_move_and_no_move_range(350, {
|
|
|
|
move_end: 450,
|
|
|
|
});
|
2018-03-21 23:56:30 +01:00
|
|
|
|
2024-08-16 03:24:43 +02:00
|
|
|
// Now jump the selected ID close to the start again.
|
2024-07-24 03:09:57 +02:00
|
|
|
verify_move_and_no_move_range(124, {
|
2024-07-24 19:33:22 +02:00
|
|
|
move_start: 0,
|
2024-07-24 03:09:57 +02:00
|
|
|
move_end: 250,
|
2024-07-24 19:33:22 +02:00
|
|
|
// The first 50 aren't in a move range, because we can't load earlier
|
|
|
|
// messages than 0.
|
|
|
|
no_move_start: 0,
|
|
|
|
});
|
2018-03-21 23:56:30 +01:00
|
|
|
|
2024-07-24 19:33:22 +02:00
|
|
|
verify_move_and_no_move_range(400, {
|
|
|
|
// top maxes out at 450
|
|
|
|
move_end: 450,
|
|
|
|
});
|
2018-03-21 23:56:30 +01:00
|
|
|
|
|
|
|
reset_list({count: 800});
|
2024-07-24 03:09:57 +02:00
|
|
|
verify_no_move_range(0, 200);
|
2018-03-21 23:56:30 +01:00
|
|
|
|
2024-07-24 19:33:22 +02:00
|
|
|
verify_move_and_no_move_range(350);
|
2018-03-21 23:56:30 +01:00
|
|
|
|
2024-07-24 19:33:22 +02:00
|
|
|
verify_move_and_no_move_range(500);
|
2018-03-21 23:56:30 +01:00
|
|
|
|
2024-07-24 03:09:57 +02:00
|
|
|
verify_move_and_no_move_range(750, {
|
2024-07-24 19:33:22 +02:00
|
|
|
// top maxes out at 800
|
|
|
|
move_end: 800,
|
|
|
|
});
|
2018-03-21 23:56:30 +01:00
|
|
|
|
2024-07-24 19:33:22 +02:00
|
|
|
verify_move_and_no_move_range(499);
|
2018-03-21 23:56:30 +01:00
|
|
|
|
2024-07-24 19:33:22 +02:00
|
|
|
verify_move_and_no_move_range(348);
|
2018-03-21 23:56:30 +01:00
|
|
|
|
2024-07-24 03:09:57 +02:00
|
|
|
// We load more than 125 on the upper end, because we load the full 250
|
|
|
|
// messages and 122 is less than half of that.
|
|
|
|
verify_move_and_no_move_range(122, {
|
2024-07-24 19:33:22 +02:00
|
|
|
move_start: 0,
|
2024-07-24 03:09:57 +02:00
|
|
|
move_end: 250,
|
2024-07-24 19:33:22 +02:00
|
|
|
no_move_start: 0,
|
|
|
|
});
|
2018-05-15 12:40:07 +02:00
|
|
|
});
|