mirror of
https://github.com/zulip/zulip.git
synced 2025-11-12 01:47:41 +00:00
Zulip's search typeahead had a security bug, where when autocompleting a specially crafted stream name, and then hitting space, code within the stream name would be executed. Zulip was doing HTML escaping correctly in the main code path using Filter.describe to describe a narrow, but the escaping function was not called in a few parallel code paths. We fix this in a way that should protect all of these code paths, by making Filter.describe return properly escaped HTML, rather than depending on its callers to do so. Thanks to w2w for reporting this issue.
908 lines
24 KiB
JavaScript
908 lines
24 KiB
JavaScript
zrequire('util');
|
|
zrequire('typeahead_helper');
|
|
zrequire('Handlebars', 'handlebars');
|
|
zrequire('Filter', 'js/filter');
|
|
zrequire('narrow_state');
|
|
zrequire('stream_data');
|
|
zrequire('topic_data');
|
|
zrequire('people');
|
|
var search = zrequire('search_suggestion');
|
|
|
|
var bob = {
|
|
email: 'bob@zulip.com',
|
|
full_name: 'Bob Roberts',
|
|
user_id: 42,
|
|
};
|
|
|
|
|
|
function init() {
|
|
people.init();
|
|
people.add(bob);
|
|
people.initialize_current_user(bob.user_id);
|
|
}
|
|
init();
|
|
|
|
set_global('narrow', {});
|
|
|
|
topic_data.reset();
|
|
|
|
(function test_basic_get_suggestions() {
|
|
var query = 'fred';
|
|
|
|
global.stream_data.subscribed_streams = function () {
|
|
return [];
|
|
};
|
|
|
|
global.narrow_state.stream = function () {
|
|
return 'office';
|
|
};
|
|
|
|
var suggestions = search.get_suggestions(query);
|
|
|
|
var expected = [
|
|
'fred',
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
}());
|
|
|
|
(function test_subset_suggestions() {
|
|
var query = 'stream:Denmark topic:Hamlet shakespeare';
|
|
|
|
global.stream_data.subscribed_streams = function () {
|
|
return [];
|
|
};
|
|
|
|
global.narrow_state.stream = function () {
|
|
return;
|
|
};
|
|
|
|
var suggestions = search.get_suggestions(query);
|
|
|
|
var expected = [
|
|
"stream:Denmark topic:Hamlet shakespeare",
|
|
"stream:Denmark topic:Hamlet",
|
|
"stream:Denmark",
|
|
];
|
|
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
}());
|
|
|
|
(function test_private_suggestions() {
|
|
global.stream_data.subscribed_streams = function () {
|
|
return [];
|
|
};
|
|
|
|
global.narrow_state.stream = function () {
|
|
return;
|
|
};
|
|
|
|
var ted =
|
|
{
|
|
email: 'ted@zulip.com',
|
|
user_id: 101,
|
|
full_name: 'Ted Smith',
|
|
};
|
|
|
|
var alice =
|
|
{
|
|
email: 'alice@zulip.com',
|
|
user_id: 102,
|
|
full_name: 'Alice Ignore',
|
|
};
|
|
|
|
people.add(ted);
|
|
people.add(alice);
|
|
|
|
var query = 'is:private';
|
|
var suggestions = search.get_suggestions(query);
|
|
var expected = [
|
|
"is:private",
|
|
"pm-with:alice@zulip.com",
|
|
"pm-with:bob@zulip.com",
|
|
"pm-with:ted@zulip.com",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = 'is:private al';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"is:private al",
|
|
"is:private is:alerted",
|
|
"is:private pm-with:alice@zulip.com",
|
|
"is:private sender:alice@zulip.com",
|
|
"is:private group-pm-with:alice@zulip.com",
|
|
"is:private",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = 'pm-with:t';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"pm-with:t",
|
|
"pm-with:ted@zulip.com",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = '-pm-with:t';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"-pm-with:t",
|
|
"is:private -pm-with:ted@zulip.com",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = 'pm-with:ted@zulip.com';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"pm-with:ted@zulip.com",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = 'sender:ted';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"sender:ted",
|
|
"sender:ted@zulip.com",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = 'sender:te';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"sender:te",
|
|
"sender:ted@zulip.com",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = '-sender:te';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"-sender:te",
|
|
"-sender:ted@zulip.com",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = 'sender:ted@zulip.com';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"sender:ted@zulip.com",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = 'is:unread from:ted';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"is:unread from:ted",
|
|
"is:unread from:ted@zulip.com",
|
|
"is:unread",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
|
|
// Users can enter bizarre queries, and if they do, we want to
|
|
// be conservative with suggestions.
|
|
query = 'is:private near:3';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"is:private near:3",
|
|
"is:private",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = 'pm-with:ted@zulip.com near:3';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"pm-with:ted@zulip.com near:3",
|
|
"pm-with:ted@zulip.com",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
// Make sure suggestions still work if preceding tokens
|
|
query = 'is:alerted sender:ted@zulip.com';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"is:alerted sender:ted@zulip.com",
|
|
"is:alerted",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = 'is:starred has:link is:private al';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"is:starred has:link is:private al",
|
|
"is:starred has:link is:private is:alerted",
|
|
"is:starred has:link is:private pm-with:alice@zulip.com",
|
|
"is:starred has:link is:private sender:alice@zulip.com",
|
|
"is:starred has:link is:private group-pm-with:alice@zulip.com",
|
|
"is:starred has:link is:private",
|
|
"is:starred has:link",
|
|
"is:starred",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
// Make sure it handles past context correctly
|
|
query = 'stream:Denmark pm-with:';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
'stream:Denmark pm-with:',
|
|
'stream:Denmark',
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = 'sender:ted@zulip.com sender:';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
'sender:ted@zulip.com sender:',
|
|
'sender:ted@zulip.com',
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
}());
|
|
|
|
(function test_group_suggestions() {
|
|
global.stream_data.subscribed_streams = function () {
|
|
return [];
|
|
};
|
|
|
|
global.narrow_state.stream = function () {
|
|
return;
|
|
};
|
|
|
|
set_global('activity', {
|
|
get_huddles: function () {
|
|
return [];
|
|
},
|
|
});
|
|
|
|
var ted =
|
|
{
|
|
email: 'ted@zulip.com',
|
|
user_id: 101,
|
|
full_name: 'Ted Smith',
|
|
};
|
|
|
|
var alice =
|
|
{
|
|
email: 'alice@zulip.com',
|
|
user_id: 102,
|
|
full_name: 'Alice Ignore',
|
|
};
|
|
|
|
var jeff =
|
|
{
|
|
email: 'jeff@zulip.com',
|
|
user_id: 103,
|
|
full_name: 'Jeff Zoolipson',
|
|
};
|
|
|
|
people.add(ted);
|
|
people.add(alice);
|
|
people.add(jeff);
|
|
|
|
// Entering a comma in a pm-with query should immediately generate
|
|
// suggestions for the next person.
|
|
var query = 'pm-with:bob@zulip.com,';
|
|
var suggestions = search.get_suggestions(query);
|
|
var expected = [
|
|
"pm-with:bob@zulip.com,",
|
|
"pm-with:bob@zulip.com,alice@zulip.com",
|
|
"pm-with:bob@zulip.com,jeff@zulip.com",
|
|
"pm-with:bob@zulip.com,ted@zulip.com",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
// Only the last part of a comma-separated pm-with query should be used to
|
|
// generate suggestions.
|
|
query = 'pm-with:bob@zulip.com,t';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"pm-with:bob@zulip.com,t",
|
|
"pm-with:bob@zulip.com,ted@zulip.com",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
// Smit should also generate ted@zulip.com (Ted Smith) as a suggestion.
|
|
query = 'pm-with:bob@zulip.com,Smit';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"pm-with:bob@zulip.com,Smit",
|
|
"pm-with:bob@zulip.com,ted@zulip.com",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
// Do not suggest "bob@zulip.com" (the name of the current user)
|
|
query = 'pm-with:ted@zulip.com,bo';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"pm-with:ted@zulip.com,bo",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
// No superfluous suggestions should be generated.
|
|
query = 'pm-with:bob@zulip.com,red';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"pm-with:bob@zulip.com,red",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
// is:private should be properly prepended to each suggestion if the pm-with
|
|
// operator is negated.
|
|
|
|
query = '-pm-with:bob@zulip.com,';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"-pm-with:bob@zulip.com,",
|
|
"is:private -pm-with:bob@zulip.com,alice@zulip.com",
|
|
"is:private -pm-with:bob@zulip.com,jeff@zulip.com",
|
|
"is:private -pm-with:bob@zulip.com,ted@zulip.com",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = '-pm-with:bob@zulip.com,t';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"-pm-with:bob@zulip.com,t",
|
|
"is:private -pm-with:bob@zulip.com,ted@zulip.com",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = '-pm-with:bob@zulip.com,Smit';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"-pm-with:bob@zulip.com,Smit",
|
|
"is:private -pm-with:bob@zulip.com,ted@zulip.com",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = '-pm-with:bob@zulip.com,red';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"-pm-with:bob@zulip.com,red",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
// Test multiple operators
|
|
query = 'is:starred has:link pm-with:bob@zulip.com,Smit';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"is:starred has:link pm-with:bob@zulip.com,Smit",
|
|
"is:starred has:link pm-with:bob@zulip.com,ted@zulip.com",
|
|
"is:starred has:link",
|
|
"is:starred",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = 'stream:Denmark has:link pm-with:bob@zulip.com,Smit';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"stream:Denmark has:link pm-with:bob@zulip.com,Smit",
|
|
"stream:Denmark has:link",
|
|
"stream:Denmark",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
set_global('activity', {
|
|
get_huddles: function () {
|
|
return ['101,42', '101,103,42'];
|
|
},
|
|
});
|
|
|
|
// Simulate a past huddle which should now prioritize ted over alice
|
|
query = 'pm-with:bob@zulip.com,';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"pm-with:bob@zulip.com,",
|
|
"pm-with:bob@zulip.com,ted@zulip.com",
|
|
"pm-with:bob@zulip.com,alice@zulip.com",
|
|
"pm-with:bob@zulip.com,jeff@zulip.com",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
// bob,ted,jeff is already an existing huddle, so prioritize this one
|
|
query = 'pm-with:bob@zulip.com,ted@zulip.com,';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"pm-with:bob@zulip.com,ted@zulip.com,",
|
|
"pm-with:bob@zulip.com,ted@zulip.com,jeff@zulip.com",
|
|
"pm-with:bob@zulip.com,ted@zulip.com,alice@zulip.com",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
// bob,ted,jeff is already an existing huddle, but if we start with just bob,
|
|
// then don't prioritize ted over alice because it doesn't complete the full huddle.
|
|
query = 'pm-with:jeff@zulip.com,';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"pm-with:jeff@zulip.com,",
|
|
"pm-with:jeff@zulip.com,alice@zulip.com",
|
|
"pm-with:jeff@zulip.com,ted@zulip.com",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
}());
|
|
|
|
init();
|
|
|
|
(function test_empty_query_suggestions() {
|
|
var query = '';
|
|
|
|
global.stream_data.subscribed_streams = function () {
|
|
return ['devel', 'office'];
|
|
};
|
|
|
|
global.narrow_state.stream = function () {
|
|
return;
|
|
};
|
|
|
|
var suggestions = search.get_suggestions(query);
|
|
|
|
var expected = [
|
|
"",
|
|
"is:private",
|
|
"is:starred",
|
|
"is:mentioned",
|
|
"is:alerted",
|
|
"is:unread",
|
|
"sender:bob@zulip.com",
|
|
"stream:devel",
|
|
"stream:office",
|
|
];
|
|
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
function describe(q) {
|
|
return suggestions.lookup_table[q].description;
|
|
}
|
|
assert.equal(describe('is:private'), 'Private messages');
|
|
assert.equal(describe('is:starred'), 'Starred messages');
|
|
assert.equal(describe('is:mentioned'), '@-mentions');
|
|
assert.equal(describe('is:alerted'), 'Alerted messages');
|
|
assert.equal(describe('is:unread'), 'Unread messages');
|
|
assert.equal(describe('sender:bob@zulip.com'), 'Sent by me');
|
|
}());
|
|
|
|
(function test_sent_by_me_suggestions() {
|
|
global.stream_data.subscribed_streams = function () {
|
|
return [];
|
|
};
|
|
|
|
global.narrow_state.stream = function () {
|
|
return;
|
|
};
|
|
|
|
var query = '';
|
|
var suggestions = search.get_suggestions(query);
|
|
assert(suggestions.strings.indexOf('sender:bob@zulip.com') !== -1);
|
|
assert.equal(suggestions.lookup_table['sender:bob@zulip.com'].description,
|
|
'Sent by me');
|
|
|
|
query = 'sender';
|
|
suggestions = search.get_suggestions(query);
|
|
var expected = [
|
|
"sender",
|
|
"sender:bob@zulip.com",
|
|
"sender:",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = 'from';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"from",
|
|
"from:bob@zulip.com",
|
|
"from:",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = 'sender:bob@zulip.com';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"sender:bob@zulip.com",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = 'from:bob@zulip.com';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"from:bob@zulip.com",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = 'sent';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"sent",
|
|
"sender:bob@zulip.com",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = 'stream:Denmark topic:Denmark1 sent';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"stream:Denmark topic:Denmark1 sent",
|
|
"stream:Denmark topic:Denmark1 sender:bob@zulip.com",
|
|
"stream:Denmark topic:Denmark1",
|
|
"stream:Denmark",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = 'is:starred sender:m';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"is:starred sender:m",
|
|
"is:starred sender:bob@zulip.com",
|
|
"is:starred",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = 'sender:alice@zulip.com sender:';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"sender:alice@zulip.com sender:",
|
|
"sender:alice@zulip.com",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
}());
|
|
|
|
(function test_topic_suggestions() {
|
|
var suggestions;
|
|
var expected;
|
|
|
|
global.stream_data.subscribed_streams = function () {
|
|
return ['office'];
|
|
};
|
|
|
|
global.narrow_state.stream = function () {
|
|
return 'office';
|
|
};
|
|
|
|
var devel_id = 44;
|
|
var office_id = 77;
|
|
|
|
global.stream_data.get_stream_id = function (stream_name) {
|
|
switch (stream_name) {
|
|
case 'office': return office_id;
|
|
case 'devel': return devel_id;
|
|
}
|
|
};
|
|
|
|
topic_data.reset();
|
|
suggestions = search.get_suggestions('te');
|
|
expected = [
|
|
"te",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
topic_data.add_message({
|
|
stream_id: devel_id,
|
|
topic_name: 'REXX',
|
|
});
|
|
|
|
_.each(['team', 'ignore', 'test'], function (topic_name) {
|
|
topic_data.add_message({
|
|
stream_id: office_id,
|
|
topic_name: topic_name,
|
|
});
|
|
});
|
|
|
|
suggestions = search.get_suggestions('te');
|
|
expected = [
|
|
"te",
|
|
"stream:office topic:team",
|
|
"stream:office topic:test",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
function describe(q) {
|
|
return suggestions.lookup_table[q].description;
|
|
}
|
|
assert.equal(describe('te'), "Search for te");
|
|
assert.equal(describe('stream:office topic:team'), "Stream office > team");
|
|
|
|
suggestions = search.get_suggestions('topic:staplers stream:office');
|
|
expected = [
|
|
'topic:staplers stream:office',
|
|
'topic:staplers',
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
suggestions = search.get_suggestions('stream:devel topic:');
|
|
expected = [
|
|
'stream:devel topic:',
|
|
'stream:devel topic:REXX',
|
|
'stream:devel',
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
suggestions = search.get_suggestions('stream:devel -topic:');
|
|
expected = [
|
|
'stream:devel -topic:',
|
|
'stream:devel -topic:REXX',
|
|
'stream:devel',
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
suggestions = search.get_suggestions('-topic:te');
|
|
expected = [
|
|
'-topic:te',
|
|
'stream:office -topic:team',
|
|
'stream:office -topic:test',
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
suggestions = search.get_suggestions('is:alerted stream:devel is:starred topic:');
|
|
expected = [
|
|
'is:alerted stream:devel is:starred topic:',
|
|
'is:alerted stream:devel is:starred topic:REXX',
|
|
'is:alerted stream:devel is:starred',
|
|
'is:alerted stream:devel',
|
|
'is:alerted',
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
suggestions = search.get_suggestions('is:private stream:devel topic:');
|
|
expected = [
|
|
'is:private stream:devel topic:',
|
|
'is:private stream:devel',
|
|
'is:private',
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
suggestions = search.get_suggestions('topic:REXX stream:devel topic:');
|
|
expected = [
|
|
'topic:REXX stream:devel topic:',
|
|
'topic:REXX stream:devel',
|
|
'topic:REXX',
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
}());
|
|
|
|
(function test_whitespace_glitch() {
|
|
var query = 'stream:office '; // note trailing space
|
|
|
|
global.stream_data.subscribed_streams = function () {
|
|
return ['office'];
|
|
};
|
|
|
|
global.narrow_state.stream = function () {
|
|
return;
|
|
};
|
|
|
|
topic_data.reset();
|
|
|
|
var suggestions = search.get_suggestions(query);
|
|
|
|
var expected = [
|
|
"stream:office",
|
|
];
|
|
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
}());
|
|
|
|
(function test_stream_completion() {
|
|
global.stream_data.subscribed_streams = function () {
|
|
return ['office', 'dev help'];
|
|
};
|
|
|
|
global.narrow_state.stream = function () {
|
|
return;
|
|
};
|
|
|
|
topic_data.reset();
|
|
|
|
var query = 'stream:of';
|
|
var suggestions = search.get_suggestions(query);
|
|
var expected = [
|
|
"stream:of",
|
|
"stream:office",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = 'hel';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"hel",
|
|
"stream:dev+help",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
}());
|
|
|
|
(function test_people_suggestions() {
|
|
var query = 'te';
|
|
|
|
global.stream_data.subscribed_streams = function () {
|
|
return [];
|
|
};
|
|
|
|
global.narrow_state.stream = function () {
|
|
return;
|
|
};
|
|
|
|
var ted = {
|
|
email: 'ted@zulip.com',
|
|
user_id: 201,
|
|
full_name: 'Ted Smith',
|
|
};
|
|
|
|
var bob = {
|
|
email: 'bob@zulip.com',
|
|
user_id: 202,
|
|
full_name: 'Bob Térry',
|
|
};
|
|
|
|
var alice = {
|
|
email: 'alice@zulip.com',
|
|
user_id: 203,
|
|
full_name: 'Alice Ignore',
|
|
};
|
|
people.add(ted);
|
|
people.add(bob);
|
|
people.add(alice);
|
|
|
|
|
|
topic_data.reset();
|
|
|
|
var suggestions = search.get_suggestions(query);
|
|
|
|
var expected = [
|
|
"te",
|
|
"pm-with:bob@zulip.com", // bob térry
|
|
"pm-with:ted@zulip.com",
|
|
"sender:bob@zulip.com",
|
|
"sender:ted@zulip.com",
|
|
"group-pm-with:bob@zulip.com",
|
|
"group-pm-with:ted@zulip.com",
|
|
];
|
|
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
function describe(q) {
|
|
return suggestions.lookup_table[q].description;
|
|
}
|
|
assert.equal(describe('pm-with:ted@zulip.com'),
|
|
"Private messages with <strong>Te</strong>d Smith <<strong>te</strong>d@zulip.com>");
|
|
assert.equal(describe('sender:ted@zulip.com'),
|
|
"Sent by <strong>Te</strong>d Smith <<strong>te</strong>d@zulip.com>");
|
|
|
|
suggestions = search.get_suggestions('Ted '); // note space
|
|
|
|
expected = [
|
|
"Ted",
|
|
"pm-with:ted@zulip.com",
|
|
"sender:ted@zulip.com",
|
|
"group-pm-with:ted@zulip.com",
|
|
];
|
|
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
}());
|
|
|
|
(function test_contains_suggestions() {
|
|
var query = 'has:';
|
|
var suggestions = search.get_suggestions(query);
|
|
var expected = [
|
|
'has:',
|
|
'has:link',
|
|
'has:image',
|
|
'has:attachment',
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = 'has:im';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
'has:im',
|
|
'has:image',
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = '-has:im';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
'-has:im',
|
|
'-has:image',
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = 'att';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
'att',
|
|
'has:attachment',
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = 'stream:Denmark is:alerted has:lin';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
'stream:Denmark is:alerted has:lin',
|
|
'stream:Denmark is:alerted has:link',
|
|
'stream:Denmark is:alerted',
|
|
'stream:Denmark',
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
}());
|
|
|
|
(function test_operator_suggestions() {
|
|
// Completed operator should return nothing
|
|
var query = 'stream:';
|
|
var suggestions = search.get_suggestions(query);
|
|
var expected = [
|
|
'stream:',
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = 'st';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
'st',
|
|
'is:starred',
|
|
'stream:',
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = 'group-';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
'group-',
|
|
'group-pm-with:',
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = '-s';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
'-s',
|
|
'-stream:',
|
|
'-sender:',
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
query = 'stream:Denmark is:alerted -f';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
'stream:Denmark is:alerted -f',
|
|
'stream:Denmark is:alerted -from:',
|
|
'stream:Denmark is:alerted',
|
|
'stream:Denmark',
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
}());
|
|
|
|
(function test_queries_with_spaces() {
|
|
global.stream_data.subscribed_streams = function () {
|
|
return ['office', 'dev help'];
|
|
};
|
|
|
|
global.narrow_state.stream = function () {
|
|
return;
|
|
};
|
|
|
|
topic_data.reset();
|
|
|
|
// test allowing spaces with quotes surrounding operand
|
|
var query = 'stream:"dev he"';
|
|
var suggestions = search.get_suggestions(query);
|
|
var expected = [
|
|
"stream:dev+he",
|
|
"stream:dev+help",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
// test mismatched quote
|
|
query = 'stream:"dev h';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"stream:dev+h",
|
|
"stream:dev+help",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
|
|
// test extra space after operator still works
|
|
query = 'stream: offi';
|
|
suggestions = search.get_suggestions(query);
|
|
expected = [
|
|
"stream:offi",
|
|
"stream:office",
|
|
];
|
|
assert.deepEqual(suggestions.strings, expected);
|
|
}());
|