zulip/frontend_tests/node_tests/dispatch.js

724 lines
19 KiB
JavaScript

var assert = require('assert');
var _ = global._;
var noop = function () {};
// The next section of cruft will go away when we can pull out
// dispatcher from server_events.
(function work_around_server_events_loading_issues() {
add_dependencies({
util: 'js/util.js'
});
set_global('document', {});
set_global('window', {
addEventListener: noop
});
global.stub_out_jquery();
}());
// These dependencies are closer to the dispatcher, and they
// apply to all tests.
set_global('tutorial', {
is_running: function () {
return false;
}
});
set_global('home_msg_list', {
select_id: noop,
selected_id: function () {return 1;}
});
set_global('echo', {
process_from_server: function (messages) {
return messages;
},
set_realm_filters: noop
});
// page_params is highly coupled to dispatching now
set_global('page_params', {test_suite: false});
var page_params = global.page_params;
// alert_words is coupled to dispatching in the sense
// that we write directly to alert_words.words
add_dependencies({alert_words: 'js/alert_words.js'});
// we also directly write to pointer
set_global('pointer', {});
var server_events = require('js/server_events.js');
// This also goes away if we can isolate the dispatcher. We
// have to call it after doing the require on server_events.js,
// so that it can set a private variable for us that bypasses
// code that queue up events and early-exits.
server_events.home_view_loaded();
// This jQuery shim can go away when we remove $.each from
// server_events.js. (It's a simple change that just
// requires some manual testing.)
$.each = function (data, f) {
_.each(data, function (value, key) {
f(key, value);
});
};
// Set up our dispatch function to point to _get_events_success
// now.
function dispatch(ev) {
server_events._get_events_success([ev]);
}
// TODO: These events are not guaranteed to be perfectly
// representative of what the server sends. For
// now we just want very basic test coverage. We
// have more mature tests for events on the backend
// side in test_events.py, and we may be able to
// re-work both sides (js/python) so that we work off
// a shared fixture.
var event_fixtures = {
alert_words: {
type: 'alert_words',
alert_words: ['fire', 'lunch']
},
default_streams: {
type: 'default_streams',
default_streams: [
{
name: 'devel',
description: 'devel',
invite_only: false,
stream_id: 1
},
{
name: 'test',
description: 'test',
invite_only: true,
stream_id: 1
}
]
},
message: {
type: 'message',
message: {
content: 'hello'
},
flags: []
},
muted_topics: {
type: 'muted_topics',
muted_topics: [['devel', 'js'], ['lunch', 'burritos']]
},
pointer: {
type: 'pointer',
pointer: 999
},
presence: {
type: 'presence',
email: 'alice@example.com',
presence: {
client_name: 'electron',
is_mirror_dummy: false
// etc.
},
server_timestamp: 999999
},
// Please keep this next section un-nested, as we want this to partly
// be simple documentation on the formats of individual events.
realm__update__create_stream_by_admins_only: {
type: 'realm',
op: 'update',
property: 'create_stream_by_admins_only',
value: false
},
realm__update__invite_by_admins_only: {
type: 'realm',
op: 'update',
property: 'invite_by_admins_only',
value: false
},
realm__update__invite_required: {
type: 'realm',
op: 'update',
property: 'invite_required',
value: false
},
realm__update__name: {
type: 'realm',
op: 'update',
property: 'name',
value: 'new_realm_name'
},
realm__update__restricted_to_domain: {
type: 'realm',
op: 'update',
property: 'restricted_to_domain',
value: false
},
realm__update_dict__default: {
type: 'realm',
op: 'update_dict',
property: 'default',
data: {
'allow_message_editing': true,
'message_content_edit_limit_seconds': 5
}
},
realm_bot__add: {
type: 'realm_bot',
op: 'add',
bot: {
email: 'the-bot@example.com',
full_name: 'The Bot'
// etc.
}
},
realm_bot__remove: {
type: 'realm_bot',
op: 'remove',
bot: {
email: 'the-bot@example.com',
full_name: 'The Bot'
}
},
realm_bot__update: {
type: 'realm_bot',
op: 'update',
bot: {
email: 'the-bot@example.com',
full_name: 'The Bot Has A New Name'
}
},
realm_emoji: {
type: 'realm_emoji',
realm_emoji: {
'airplane': {
display_url: 'some_url'
}
}
},
realm_filters: {
type: 'realm_filters',
realm_filters: [
['#[123]', 'ticket %(id)s']
]
},
realm_user__add: {
type: 'realm_user',
op: 'add',
person: {
email: 'alice@example.com',
full_name: 'Alice User'
// etc.
}
},
realm_user__remove: {
type: 'realm_user',
op: 'remove',
person: {
email: 'alice@example.com',
full_name: 'Alice User'
// etc.
}
},
realm_user__update: {
type: 'realm_user',
op: 'update',
person: {
email: 'alice@example.com',
full_name: 'Alice NewName'
// etc.
}
},
referral: {
type: 'referral',
referrals: {
granted: 10,
used: 5
}
},
restart: {
type: 'restart',
immediate: true
},
stream: {
type: 'stream',
op: 'update',
name: 'devel',
property: 'color',
value: 'blue'
},
subscription__add: {
type: 'subscription',
op: 'add',
subscriptions: [
{
name: 'devel',
stream_id: 42
// etc.
}
]
},
subscription__remove: {
type: 'subscription',
op: 'remove',
subscriptions: [
{
stream_id: 42
}
]
},
subscription__peer_add: {
type: 'subscription',
op: 'peer_add',
user_email: 'bob@example.com',
subscriptions: [
{
name: 'devel',
stream_id: 42
// etc.
}
]
},
subscription__peer_remove: {
type: 'subscription',
op: 'peer_remove',
user_email: 'bob@example.com',
subscriptions: [
{
stream_id: 42
// etc.
}
]
},
subscription__update: {
type: 'subscription',
op: 'update',
name: 'devel',
property: 'color',
value: 'black'
},
update_display_settings__default_language: {
type: 'update_display_settings',
setting_name: 'default_language',
default_language: 'fr'
},
update_display_settings__left_side_userlist: {
type: 'update_display_settings',
setting_name: 'left_side_userlist',
left_side_userlist: true
},
update_display_settings__twenty_four_hour_time: {
type: 'update_display_settings',
setting_name: 'twenty_four_hour_time',
twenty_four_hour_time: true
},
update_global_notifications: {
type: 'update_global_notifications',
notification_name: 'enable_stream_sounds',
setting: true
},
update_message_flags__read: {
type: 'update_message_flags',
operation: 'add',
flag: 'read',
messages: [5, 999]
},
update_message_flags__starred: {
type: 'update_message_flags',
operation: 'add',
flag: 'starred',
messages: [7, 99]
}
};
function assert_same(actual, expected) {
// This helper prevents us from getting false positives
// where actual and expected are both undefined.
assert(expected);
assert.deepEqual(actual, expected);
}
// TODO: move this into library
function capture_args(res, arg_names) {
// This function returns a function that, when
// arg_names are ['foo', 'bar'] sets res.foo
// to the first arg passed in and res.bar to
// the second args passed in. (It's basically
// a mock.)
_.each(res, function (value, key) {
delete res[key];
});
return function () {
var my_arguments = _.clone(arguments);
_.each(arg_names, function (name, i) {
res[name] = my_arguments[i];
});
};
}
// This test suite is different than most, because
// most modules we test are dependent on a few
// set of modules, and it's useful for tests to
// all share the same stubs. For a dispatcher,
// we want a higher level of isolation between
// our tests, so we wrap them with a run() method.
var run = (function () {
var wrapper = function (f) {
// We only ever mock one function at a time,
// so we can have a little helper.
var args = {}; // for stubs to capture args
function capture(names) {
return capture_args(args, names);
}
var clobber_callbacks = [];
var override = function (module, func_name, f) {
var impl = {};
impl[func_name] = f;
set_global(module, impl);
clobber_callbacks.push(function () {
// If you get a failure from this, you probably just
// need to have your test do its own overrides and
// not cherry-pick off of the prior test's setup.
set_global(module, 'UNCLEAN MODULE FROM PRIOR TEST');
});
};
f(override, capture, args);
_.each(clobber_callbacks, function (f) {
f();
});
};
return wrapper;
}());
run(function (override, capture, args) {
// alert_words
var event = event_fixtures.alert_words;
dispatch(event);
assert_same(global.alert_words.words, ['fire', 'lunch']);
});
run(function (override, capture, args) {
// default_streams
var event = event_fixtures.default_streams;
override('admin', 'update_default_streams_table', noop);
dispatch(event);
assert_same(page_params.realm_default_streams, event.default_streams);
});
run(function (override, capture, args) {
// message
var event = event_fixtures.message;
override('message_store', 'insert_new_messages', capture(['messages']));
server_events._get_events_success([event]);
dispatch(event);
assert_same(args.messages[0].content, event.message.content);
});
run(function (override, capture, args) {
// muted_topics
var event = event_fixtures.muted_topics;
override('muting_ui', 'handle_updates', capture(['muted_topics']));
dispatch(event);
assert_same(args.muted_topics, event.muted_topics);
});
run(function (override, capture, args) {
// pointer
var event = event_fixtures.pointer;
global.pointer.furthest_read = 0;
global.pointer.server_furthest_read = 0;
dispatch(event);
assert_same(global.pointer.furthest_read, event.pointer);
assert_same(global.pointer.server_furthest_read, event.pointer);
});
run(function (override, capture, args) {
// presence
var event = event_fixtures.presence;
override('activity', 'set_user_statuses', capture(['users', 'server_time']));
dispatch(event);
assert_same(args.users, {'alice@example.com': event.presence});
assert_same(args.server_time, event.server_timestamp);
});
run(function (override, capture, args) {
// realm
function test_realm_boolean(event, parameter_name) {
page_params[parameter_name] = true;
event = _.clone(event);
event.value = false;
dispatch(event);
assert.equal(page_params[parameter_name], false);
event = _.clone(event);
event.value = true;
dispatch(event);
assert.equal(page_params[parameter_name], true);
}
var event = event_fixtures.realm__update__create_stream_by_admins_only;
test_realm_boolean(event, 'realm_create_stream_by_admins_only');
event = event_fixtures.realm__update__invite_by_admins_only;
test_realm_boolean(event, 'realm_invite_by_admins_only');
event = event_fixtures.realm__update__invite_required;
test_realm_boolean(event, 'realm_invite_required');
event = event_fixtures.realm__update__name;
override('notifications', 'redraw_title', noop);
dispatch(event);
assert_same(page_params.realm_name, 'new_realm_name');
event = event_fixtures.realm__update__restricted_to_domain;
test_realm_boolean(event, 'realm_restricted_to_domain');
event = event_fixtures.realm__update_dict__default;
page_params.realm_allow_message_editing = false;
page_params.realm_message_content_edit_limit_seconds = 0;
dispatch(event);
assert_same(page_params.realm_allow_message_editing, true);
assert_same(page_params.realm_message_content_edit_limit_seconds, 5);
});
run(function (override, capture, args) {
// realm_bot
var event = event_fixtures.realm_bot__add;
override('bot_data', 'add', capture(['bot']));
dispatch(event);
assert_same(args.bot, event.bot);
event = event_fixtures.realm_bot__remove;
override('bot_data', 'remove', capture(['email']));
dispatch(event);
assert_same(args.email, event.bot.email);
event = event_fixtures.realm_bot__update;
override('bot_data', 'update', capture(['email', 'bot']));
dispatch(event);
assert_same(args.email, event.bot.email);
assert_same(args.bot, event.bot);
});
run(function (override, capture, args) {
// realm_emoji
var event = event_fixtures.realm_emoji;
override('emoji', 'update_emojis', capture(['realm_emoji']));
override('admin', 'populate_emoji', noop);
dispatch(event);
assert_same(args.realm_emoji, event.realm_emoji);
});
run(function (override, capture, args) {
// realm_filters
var event = event_fixtures.realm_filters;
page_params.realm_filters = [];
dispatch(event);
assert_same(page_params.realm_filters, event.realm_filters);
});
run(function (override, capture, args) {
// realm_user
var event = event_fixtures.realm_user__add;
override('people', 'add_in_realm', capture(['person']));
dispatch(event);
assert_same(args.person, event.person);
event = event_fixtures.realm_user__remove;
override('people', 'remove', capture(['person']));
dispatch(event);
assert_same(args.person, event.person);
event = event_fixtures.realm_user__update;
override('people', 'update', capture(['person']));
dispatch(event);
assert_same(args.person, event.person);
});
run(function (override, capture, args) {
// referral
var event = event_fixtures.referral;
override('referral', 'update_state', capture(['granted', 'used']));
dispatch(event);
assert_same(args.granted, event.referrals.granted);
assert_same(args.used, event.referrals.used);
});
run(function (override, capture, args) {
// restart
var event = event_fixtures.restart;
override('reload', 'initiate', capture(['options']));
dispatch(event);
assert.equal(args.options.save_pointer, true);
assert.equal(args.options.immediate, true);
});
run(function (override, capture, args) {
// stream
var event = event_fixtures.stream;
override(
'subs',
'update_subscription_properties',
capture(['name', 'property', 'value'])
);
override('admin', 'update_default_streams_table', noop);
dispatch(event);
assert_same(args.name, event.name);
assert_same(args.property, event.property);
assert_same(args.value, event.value);
});
run(function (override, capture, args) {
// subscription
var event = event_fixtures.subscription__add;
override('subs', 'mark_subscribed', capture(['name', 'sub']));
dispatch(event);
assert_same(args.name, 'devel');
assert_same(args.sub, event.subscriptions[0]);
event = event_fixtures.subscription__peer_add;
override('stream_data', 'add_subscriber', capture(['sub', 'email']));
dispatch(event);
assert_same(args.sub, event.subscriptions[0]);
assert_same(args.email, event.user_email);
event = event_fixtures.subscription__peer_remove;
override('stream_data', 'remove_subscriber', capture(['sub', 'email']));
dispatch(event);
assert_same(args.sub, event.subscriptions[0]);
assert_same(args.email, event.user_email);
event = event_fixtures.subscription__remove;
var stream_id_looked_up;
var sub_stub = 'stub';
override('stream_data', 'get_sub_by_id', function (stream_id) {
stream_id_looked_up = stream_id;
return sub_stub;
});
override('subs', 'mark_sub_unsubscribed', capture(['sub']));
dispatch(event);
assert_same(stream_id_looked_up, event.subscriptions[0].stream_id);
assert_same(args.sub, sub_stub);
event = event_fixtures.subscription__update;
override(
'subs',
'update_subscription_properties',
capture(['name', 'property', 'value'])
);
dispatch(event);
assert_same(args.name, event.name);
assert_same(args.property, event.property);
assert_same(args.value, event.value);
});
run(function (override, capture, args) {
// update_display_settings
var event = event_fixtures.update_display_settings__default_language;
page_params.default_language = 'en';
dispatch(event);
assert_same(page_params.default_language, 'fr');
event = event_fixtures.update_display_settings__left_side_userlist;
page_params.left_side_userlist = false;
dispatch(event);
assert_same(page_params.left_side_userlist, true);
event = event_fixtures.update_display_settings__twenty_four_hour_time;
page_params.twenty_four_hour_time = false;
dispatch(event);
assert_same(page_params.twenty_four_hour_time, true);
});
run(function (override, capture, args) {
// update_global_notifications
var event = event_fixtures.update_global_notifications;
override(
'notifications',
'handle_global_notification_updates',
capture(['name', 'setting'])
);
dispatch(event);
assert_same(args.name, event.notification_name);
assert_same(args.setting, event.setting);
});
run(function (override, capture, args) {
// update_message_flags__read
var event = event_fixtures.update_message_flags__read;
override('message_store', 'get', capture(['message_id']));
override('unread', 'mark_messages_as_read', noop);
dispatch(event);
assert_same(args.message_id, 999);
});
run(function (override, capture, args) {
// update_message_flags__starred
var event = event_fixtures.update_message_flags__starred;
override('ui', 'update_starred', capture(['message_id', 'new_value']));
dispatch(event);
assert_same(args.message_id, 99);
assert_same(args.new_value, true); // for 'add'
});