Files
zulip/frontend_tests/node_tests/reactions.js
Tim Abbott 9081f2cf44 reactions: Store the emoji codepoint in the database.
This is the first part of a larger migration to convert Zulip's
reactions storage to something based on the codepoint, not the emoji
name that the user typed in, so that we don't need to worry about
changes in the names we're using breaking the emoji storage.
2017-08-15 09:29:27 -07:00

512 lines
14 KiB
JavaScript

set_global('document', 'document-stub');
set_global('$', global.make_zjquery());
add_dependencies({
people: 'js/people.js',
});
set_global('emoji', {
emojis_name_to_css_class: {
frown: 'frown-css',
octopus: 'octopus-css',
smile: 'smile-css',
},
emojis_by_name: {
alien: '1f47d',
smile: '1f604',
},
all_realm_emojis: {
realm_emoji: {
emoji_name: 'realm_emoji',
emoji_url: 'TBD',
deactivated: false,
},
},
active_realm_emojis: {
realm_emoji: {
emoji_name: 'realm_emoji',
emoji_url: 'TBD',
},
},
});
set_global('blueslip', {
warn: function () {},
});
set_global('page_params', {user_id: 5});
set_global('channel', {});
set_global('templates', {});
set_global('emoji_picker', {
hide_emoji_popover: function () {},
});
var reactions = require("js/reactions.js");
var alice = {
email: 'alice@example.com',
user_id: 5,
full_name: 'Alice',
};
var bob = {
email: 'bob@example.com',
user_id: 6,
full_name: 'Bob van Roberts',
};
var cali = {
email: 'cali@example.com',
user_id: 7,
full_name: 'Cali',
};
people.add_in_realm(alice);
people.add_in_realm(bob);
people.add_in_realm(cali);
var message = {
id: 1001,
reactions: [
{emoji_name: 'smile', user: {id: 5}, reaction_type: 'unicode_emoji', emoji_code: '1'},
{emoji_name: 'smile', user: {id: 6}, reaction_type: 'unicode_emoji', emoji_code: '1'},
{emoji_name: 'frown', user: {id: 7}, reaction_type: 'unicode_emoji', emoji_code: '2'},
// add some bogus user_ids
{emoji_name: 'octopus', user: {id: 8888}, reaction_type: 'unicode_emoji', emoji_code: '3'},
{emoji_name: 'frown', user: {id: 9999}, reaction_type: 'unicode_emoji', emoji_code: '2'},
],
};
set_global('message_store', {
get: function (message_id) {
assert.equal(message_id, 1001);
return message;
},
});
(function test_basics() {
var result = reactions.get_message_reactions(message);
assert(reactions.current_user_has_reacted_to_emoji(message, 'smile'));
assert(!reactions.current_user_has_reacted_to_emoji(message, 'frown'));
result.sort(function (a, b) { return a.count - b.count; });
var expected_result = [
{
emoji_name: 'frown',
reaction_type: 'unicode_emoji',
emoji_code: '2',
emoji_name_css_class: '2',
count: 1,
user_ids: [ 7 ],
title: 'Cali reacted with :frown:',
emoji_alt_code: undefined,
class: 'message_reaction',
},
{
emoji_name: 'smile',
reaction_type: 'unicode_emoji',
emoji_code: '1',
emoji_name_css_class: '1',
count: 2,
user_ids: [ 5, 6 ],
title: 'You (click to remove) and Bob van Roberts reacted with :smile:',
emoji_alt_code: undefined,
class: 'message_reaction reacted',
},
];
assert.deepEqual(result, expected_result);
}());
(function test_sending() {
var message_id = 1001; // see above for setup
var emoji_name = 'smile'; // should be a current reaction
global.with_stub(function (stub) {
global.channel.del = stub.f;
reactions.toggle_emoji_reaction(message_id, emoji_name);
var args = stub.get_args('args').args;
assert.equal(args.url, '/json/messages/1001/emoji_reactions/smile');
// args.success() does nothing; just make sure it doesn't crash
args.success();
// similarly, we only exercise the failure codepath
global.channel.xhr_error_message = function () {};
args.error();
});
emoji_name = 'alien'; // not set yet
global.with_stub(function (stub) {
global.channel.put = stub.f;
reactions.toggle_emoji_reaction(message_id, emoji_name);
var args = stub.get_args('args').args;
assert.equal(args.url, '/json/messages/1001/emoji_reactions/alien');
});
emoji_name = 'unknown-emoji';
reactions.toggle_emoji_reaction(message_id, emoji_name);
}());
(function test_set_reaction_count() {
var count_element = $.create('count-stub');
var reaction_element = $.create('reaction-stub');
reaction_element.set_find_results('.message_reaction_count', count_element);
reactions.set_reaction_count(reaction_element, 5);
assert.equal(count_element.html(), '5');
}());
(function test_get_reaction_section() {
var message_table = $.create('.message_table');
var message_row = $.create('some-message-row');
var message_reactions = $.create('our-reactions-section');
message_table.set_find_results("[zid='555']", message_row);
message_row.set_find_results('.message_reactions', message_reactions);
var section = reactions.get_reaction_section(555);
assert.equal(section, message_reactions);
}());
(function test_add_and_remove_reaction() {
// Insert 8ball for Alice.
var alice_event = {
message_id: 1001,
emoji_name: '8ball',
user: {
user_id: alice.user_id,
},
};
var message_reactions = $.create('our-reactions');
reactions.get_reaction_section = function (message_id) {
assert.equal(message_id, 1001);
return message_reactions;
};
message_reactions.find = function (selector) {
assert.equal(selector, '.reaction_button');
return 'reaction-button-stub';
};
var template_called;
global.templates.render = function (template_name, data) {
template_called = true;
assert.equal(template_name, 'message_reaction');
assert.equal(data.class, 'message_reaction reacted');
assert(!data.is_realm_emoji);
assert.equal(data.message_id, 1001);
assert.equal(data.title, 'You (click to remove) reacted with :8ball:');
return '<new reaction html>';
};
var insert_called;
$('<new reaction html>').insertBefore = function (element) {
assert.equal(element, 'reaction-button-stub');
insert_called = true;
};
reactions.add_reaction(alice_event);
assert(template_called);
assert(insert_called);
// Now, have Bob react to the same emoji (update).
var bob_event = {
message_id: 1001,
emoji_name: '8ball',
user: {
user_id: bob.user_id,
},
};
var count_element = $.create('count-element');
var reaction_element = $.create('reaction-element');
reaction_element.set_find_results('.message_reaction_count', count_element);
var title_set;
reaction_element.prop = function (prop_name, value) {
assert.equal(prop_name, 'title');
var expected_msg = 'You (click to remove)' +
' and Bob van Roberts reacted with :8ball:';
assert.equal(value, expected_msg);
title_set = true;
};
message_reactions.find = function (selector) {
assert.equal(selector, "[data-emoji-name='8ball']");
return reaction_element;
};
reactions.add_reaction(bob_event);
assert(title_set);
assert.equal(count_element.html(), '2');
// Now, remove Bob's 8ball emoji. The event has the same exact
// structure as the add event.
title_set = false;
reaction_element.prop = function (prop_name, value) {
assert.equal(prop_name, 'title');
var expected_msg = 'You (click to remove) reacted with :8ball:';
assert.equal(value, expected_msg);
title_set = true;
};
reactions.remove_reaction(bob_event);
assert(title_set);
assert.equal(count_element.html(), '1');
var current_emojis = reactions.get_emojis_used_by_user_for_message_id(1001);
assert.deepEqual(current_emojis, ['smile', '8ball']);
// Next, remove Alice's reaction, which exercises removing the
// emoji icon.
var removed;
reaction_element.remove = function () {
removed = true;
};
reactions.remove_reaction(alice_event);
assert(removed);
current_emojis = reactions.get_emojis_used_by_user_for_message_id(1001);
assert.deepEqual(current_emojis, ['smile']);
// Now add Cali's realm_emoji reaction.
var cali_event = {
message_id: 1001,
emoji_name: 'realm_emoji',
user: {
user_id: cali.user_id,
},
};
template_called = false;
global.templates.render = function (template_name, data) {
assert.equal(data.class, 'message_reaction');
assert(data.is_realm_emoji);
template_called = true;
return '<new reaction html>';
};
message_reactions.find = function (selector) {
assert.equal(selector, '.reaction_button');
return 'reaction-button-stub';
};
reactions.add_reaction(cali_event);
assert(template_called);
assert(!reaction_element.hasClass('reacted'));
// And then have Alice update it.
alice_event = {
message_id: 1001,
emoji_name: 'realm_emoji',
user: {
user_id: alice.user_id,
},
};
message_reactions.find = function (selector) {
assert.equal(selector, "[data-emoji-name='realm_emoji']");
return reaction_element;
};
reaction_element.prop = function () {};
reactions.add_reaction(alice_event);
assert(reaction_element.hasClass('reacted'));
var result = reactions.get_message_reactions(message);
var realm_emoji_data = _.filter(result, function (v) {
return v.emoji_name === 'realm_emoji';
})[0];
assert.equal(realm_emoji_data.count, 2);
assert.equal(realm_emoji_data.is_realm_emoji, true);
// And then remove Alice's reaction.
reactions.remove_reaction(alice_event);
assert(!reaction_element.hasClass('reacted'));
}());
(function test_with_view_stubs() {
// This function tests reaction events by mocking out calls to
// the view.
var message = {
id: 2001,
reactions: [],
};
message_store.get = function () {
return message;
};
function test_view_calls(test_params) {
var calls = [];
function add_call_func(name) {
return function (opts) {
calls.push({
name: name,
opts: opts,
});
};
}
reactions.view = {
insert_new_reaction: add_call_func('insert_new_reaction'),
update_existing_reaction: add_call_func('update_existing_reaction'),
remove_reaction: add_call_func('remove_reaction'),
};
test_params.run_code();
assert.deepEqual(calls, test_params.expected_view_calls);
}
var alice_8ball_event = {
message_id: 2001,
emoji_name: '8ball',
user: {
user_id: alice.user_id,
},
};
var bob_8ball_event = {
message_id: 2001,
emoji_name: '8ball',
user: {
user_id: bob.user_id,
},
};
var cali_airplane_event = {
message_id: 2001,
emoji_name: 'airplane',
user: {
user_id: cali.user_id,
},
};
test_view_calls({
run_code: function () {
reactions.add_reaction(alice_8ball_event);
},
expected_view_calls: [
{
name: 'insert_new_reaction',
opts: {
message_id: 2001,
emoji_name: '8ball',
user_id: alice.user_id,
},
},
],
});
test_view_calls({
run_code: function () {
reactions.add_reaction(bob_8ball_event);
},
expected_view_calls: [
{
name: 'update_existing_reaction',
opts: {
message_id: 2001,
emoji_name: '8ball',
user_id: bob.user_id,
user_list: [alice.user_id, bob.user_id],
},
},
],
});
test_view_calls({
run_code: function () {
reactions.add_reaction(cali_airplane_event);
},
expected_view_calls: [
{
name: 'insert_new_reaction',
opts: {
message_id: 2001,
emoji_name: 'airplane',
user_id: cali.user_id,
},
},
],
});
test_view_calls({
run_code: function () {
reactions.remove_reaction(bob_8ball_event);
},
expected_view_calls: [
{
name: 'remove_reaction',
opts: {
message_id: 2001,
emoji_name: '8ball',
user_id: bob.user_id,
user_list: [alice.user_id],
},
},
],
});
test_view_calls({
run_code: function () {
reactions.remove_reaction(alice_8ball_event);
},
expected_view_calls: [
{
name: 'remove_reaction',
opts: {
message_id: 2001,
emoji_name: '8ball',
user_id: alice.user_id,
user_list: [],
},
},
],
});
}());
(function test_error_handling() {
var error_msg;
global.message_store.get = function () {
return;
};
global.blueslip.error = function (msg) {
error_msg = msg;
};
var bogus_event = {
message_id: 55,
emoji_name: 'realm_emoji',
user: {
user_id: 99,
},
};
reactions.toggle_emoji_reaction(55);
assert.equal(error_msg, 'reactions: Bad message id: 55');
error_msg = undefined;
reactions.add_reaction(bogus_event);
assert.equal(error_msg, undefined);
reactions.remove_reaction(bogus_event);
assert.equal(error_msg, undefined);
}());