zulip/frontend_tests/node_tests/ui_init.js

202 lines
5.1 KiB
JavaScript
Raw Normal View History

const rewiremock = require("rewiremock/node");
/*
This test suite is designed to find errors
in our initialization sequence. It doesn't
really validate any behavior, other than just
making sure things don't fail. For more
directed testing of individual modules, you
should create dedicated test suites.
Also, we stub a lot of initialization here that
is tricky to test due to dependencies on things
like jQuery. A good project is to work through
ignore_modules and try to make this test more
complete.
Also, it's good to be alert here for things
that can be cleaned up in the code--for example,
not everything needs to happen in `initialization`--
some things can happen later in a `launch` method.
*/
const util = zrequire("util");
set_global("document", {
location: {
protocol: "http",
},
});
set_global("csrf_token", "whatever");
set_global("$", () => {});
set_global("resize", {});
set_global("page_params", {});
const ignore_modules = [
"activity",
"click_handlers",
"compose_pm_pill",
"copy_and_paste",
"drafts",
"emoji",
"emoji_picker",
"gear_menu",
"hashchange",
"hotspots",
// Accesses home_msg_list, which is a lot of complexity to setup
"message_fetch",
"message_scroll",
"message_viewport",
"panels",
"popovers",
"reload",
"scroll_bar",
"server_events",
"settings_sections",
"settings_panel_menu",
"settings_toggle",
"subs",
"timerender",
"ui",
"unread_ui",
];
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 mod of ignore_modules) {
set_global(mod, {
initialize: () => {},
});
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
}
emoji.emojis_by_name = new Map();
util.is_mobile = () => false;
global.stub_templates(() => "some-html");
ui.get_scroll_element = (element) => element;
zrequire("alert_words");
zrequire("hash_util");
zrequire("echo");
zrequire("colorspace");
zrequire("stream_color");
zrequire("stream_edit");
zrequire("color_data");
zrequire("stream_data");
zrequire("muting");
zrequire("condense");
zrequire("spoilers");
zrequire("lightbox");
zrequire("overlays");
zrequire("invite");
zrequire("tab_bar");
zrequire("narrow_state");
zrequire("people");
zrequire("presence");
zrequire("search_pill_widget");
zrequire("user_groups");
zrequire("unread");
zrequire("bot_data");
set_global("marked", zrequire("marked", "third/marked/lib/marked"));
zrequire("fenced_code");
zrequire("markdown");
zrequire("upload");
zrequire("compose");
zrequire("composebox_typeahead");
zrequire("narrow");
zrequire("search_suggestion");
zrequire("search");
zrequire("tutorial");
zrequire("notifications");
zrequire("pm_conversations");
zrequire("pm_list");
zrequire("list_cursor");
zrequire("keydown_util");
zrequire("stream_sort");
zrequire("stream_list");
zrequire("topic_list");
zrequire("topic_zoom");
zrequire("sent_messages");
zrequire("typing");
zrequire("top_left_corner");
zrequire("starred_messages");
zrequire("user_status");
zrequire("user_status_ui");
const ui_init = rewiremock.proxy(
() => zrequire("ui_init"),
{
"../../static/js/emojisets": {
initialize: () => {},
},
},
);
set_global("$", global.make_zjquery());
const document_stub = $.create("document-stub");
document.to_$ = () => document_stub;
document_stub.on = () => {};
document_stub.idle = () => {};
const window_stub = $.create("window-stub");
set_global("to_$", () => window_stub);
window_stub.idle = () => {};
ui_init.initialize_kitchen_sink_stuff = () => {};
page_params.realm_default_streams = [];
page_params.subscriptions = [];
page_params.unsubscribed = [];
page_params.never_subscribed = [];
page_params.realm_notifications_stream_id = -1;
page_params.unread_msgs = {
huddles: [],
pms: [],
streams: [],
mentions: [],
};
page_params.recent_private_conversations = [];
page_params.user_status = {};
page_params.realm_users = [];
page_params.realm_non_active_users = [];
page_params.cross_realm_bots = [];
page_params.muted_topics = [];
page_params.realm_user_groups = [];
page_params.realm_bots = [];
page_params.realm_filters = [];
page_params.starred_messages = [];
page_params.presences = [];
const $tab_bar = $.create("#tab_bar");
$tab_bar.append = () => {};
upload.setup_upload = () => {};
server_events.home_view_loaded = () => true;
resize.watch_manual_resize = () => {};
$("#stream_message_recipient_stream").typeahead = () => {};
$("#stream_message_recipient_topic").typeahead = () => {};
$("#private_message_recipient").typeahead = () => {};
$("#compose-textarea").typeahead = () => {};
$("#search_query").typeahead = () => {};
const value_stub = $.create("value");
const count_stub = $.create("count");
count_stub.set_find_results(".value", value_stub);
$(".top_left_starred_messages").set_find_results(".count", count_stub);
$("#tab_bar .stream").length = 0;
// set find results doesn't work here since we call .empty() in the code.
$tab_bar.find = () => false;
compose.compute_show_video_chat_button = () => {};
$("#below-compose-content .video_link").toggle = () => {};
$("#tab_bar .narrow_description > a").hover = () => {};
run_test("initialize_everything", () => {
ui_init.initialize_everything();
});