2018-07-14 12:46:02 +02:00
|
|
|
set_global('page_params', {
|
|
|
|
search_pills_enabled: true,
|
|
|
|
});
|
2018-06-22 12:19:40 +02:00
|
|
|
zrequire('search');
|
2018-07-14 16:10:00 +02:00
|
|
|
zrequire('search_pill');
|
|
|
|
zrequire('util');
|
|
|
|
zrequire('Filter', 'js/filter');
|
|
|
|
zrequire('search_pill_widget');
|
2018-06-22 12:19:40 +02:00
|
|
|
|
|
|
|
const noop = () => {};
|
|
|
|
const return_true = () => true;
|
|
|
|
const return_false = () => false;
|
|
|
|
|
|
|
|
set_global('$', global.make_zjquery());
|
|
|
|
set_global('narrow_state', {});
|
|
|
|
set_global('search_suggestion', {});
|
|
|
|
set_global('ui_util', {
|
|
|
|
change_tab_to: noop,
|
|
|
|
});
|
|
|
|
set_global('narrow', {});
|
2018-07-14 16:10:00 +02:00
|
|
|
|
|
|
|
search_pill.append_search_string = noop;
|
2018-06-22 12:19:40 +02:00
|
|
|
|
|
|
|
global.patch_builtin('setTimeout', func => func());
|
|
|
|
|
Simplify narrow/search interactions.
Before this change, if you hit ESC, then hotkey
code would call search.clear_search, which would
call narrow.deactivate(), which would then use
`$('#search_query')` to clear a value, but then
let search.clear_search blur the input and
disable the exit button. It was all confusing.
Things are a bit more organized now.
Now the code works like this:
hotkey.process_escape_key
Just call narrow.deactivate.
$('#search_exit').on('click', ...):
Just call narrow.deactivate.
narrow.deactivate:
Just call search.clear_search_form
search.clear_search_form:
Just do simple jquery stuff. Don't
change the entire user's narrow, not
even indirectly!
There's still a two-way interaction between
the narrow.js module and the search.js module,
but in each direction it's a one-liner.
The guiding principle here is that we only
want one top-level API, which is narrow.deactivate,
and that does the whole "kitchen sink" of
clearing searches, closing popovers, switching
in views, etc. And then all the functions it
calls out to tend to have much smaller jobs to
do.
This commit can mostly be considered a refactoring, but the
order of operations changes slightly. Basically, as
soon as you hit ESC or click on the search "X", we
clear the search widget. Most users won't notice
any difference, because we don't have to hit the
server to populate the home view. And it's arguably
an improvement to give more immediate feedback.
2018-09-10 19:36:58 +02:00
|
|
|
run_test('clear_search_form', () => {
|
|
|
|
$('#search_query').val('noise');
|
|
|
|
$('#search_query').focus();
|
|
|
|
$('.search_button').prop('disabled', false);
|
|
|
|
|
|
|
|
search.clear_search_form();
|
|
|
|
|
|
|
|
assert.equal($('#search_query').is_focused(), false);
|
|
|
|
assert.equal($('#search_query').val(), '');
|
|
|
|
assert.equal($('.search_button').prop('disabled'), true);
|
|
|
|
});
|
|
|
|
|
2018-06-22 12:19:40 +02:00
|
|
|
run_test('update_button_visibility', () => {
|
|
|
|
const search_query = $('#search_query');
|
|
|
|
const search_button = $('.search_button');
|
|
|
|
|
|
|
|
search_query.is = return_false;
|
|
|
|
search_query.val('');
|
|
|
|
narrow_state.active = return_false;
|
|
|
|
search_button.prop('disabled', false);
|
|
|
|
search.update_button_visibility();
|
|
|
|
assert(search_button.prop('disabled'));
|
|
|
|
|
|
|
|
search_query.is = return_true;
|
|
|
|
search_query.val('');
|
|
|
|
narrow_state.active = return_false;
|
|
|
|
search_button.prop('disabled', true);
|
|
|
|
search.update_button_visibility();
|
|
|
|
assert(!search_button.prop('disabled'));
|
|
|
|
|
|
|
|
search_query.is = return_false;
|
|
|
|
search_query.val('Test search term');
|
|
|
|
narrow_state.active = return_false;
|
|
|
|
search_button.prop('disabled', true);
|
|
|
|
search.update_button_visibility();
|
|
|
|
assert(!search_button.prop('disabled'));
|
|
|
|
|
|
|
|
search_query.is = return_false;
|
|
|
|
search_query.val('');
|
|
|
|
narrow_state.active = return_true;
|
|
|
|
search_button.prop('disabled', true);
|
|
|
|
search.update_button_visibility();
|
|
|
|
assert(!search_button.prop('disabled'));
|
|
|
|
});
|
|
|
|
|
|
|
|
run_test('initizalize', () => {
|
|
|
|
const search_query_box = $('#search_query');
|
|
|
|
const searchbox_form = $('#searchbox_form');
|
|
|
|
const search_button = $('.search_button');
|
2018-07-23 00:25:21 +02:00
|
|
|
const searchbox = $('#searchbox');
|
2018-06-22 12:19:40 +02:00
|
|
|
|
|
|
|
searchbox_form.on = (event, func) => {
|
|
|
|
assert.equal(event, 'compositionend');
|
|
|
|
search.is_using_input_method = false;
|
|
|
|
func();
|
|
|
|
assert(search.is_using_input_method);
|
|
|
|
};
|
|
|
|
|
2018-07-02 22:30:18 +02:00
|
|
|
search_pill.get_search_string_for_current_filter = function () {
|
|
|
|
return 'is:starred';
|
|
|
|
};
|
|
|
|
|
2018-06-22 12:19:40 +02:00
|
|
|
search_query_box.typeahead = (opts) => {
|
|
|
|
assert.equal(opts.fixed, true);
|
|
|
|
assert.equal(opts.items, 12);
|
|
|
|
assert.equal(opts.naturalSearch, true);
|
2018-12-04 22:55:55 +01:00
|
|
|
assert.equal(opts.helpOnEmptyStrings, true);
|
2018-06-22 12:19:40 +02:00
|
|
|
assert.equal(opts.matcher(), true);
|
|
|
|
|
|
|
|
{
|
|
|
|
const search_suggestions = {
|
|
|
|
lookup_table: {
|
|
|
|
'stream:Verona': {
|
|
|
|
description: 'Stream <strong>Ver</strong>ona',
|
|
|
|
search_string: 'stream:Verona',
|
|
|
|
},
|
|
|
|
ver: {
|
|
|
|
description: 'Search for ver',
|
|
|
|
search_string: 'ver',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
strings: ['ver', 'stream:Verona'],
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Test source */
|
|
|
|
search_suggestion.get_suggestions = () => search_suggestions;
|
|
|
|
const expected_source_value = search_suggestions.strings;
|
|
|
|
const source = opts.source('ver');
|
|
|
|
assert.equal(source, expected_source_value);
|
|
|
|
|
|
|
|
/* Test highlighter */
|
|
|
|
let expected_value = 'Search for ver';
|
|
|
|
assert.equal(opts.highlighter(source[0]), expected_value);
|
|
|
|
|
|
|
|
expected_value = 'Stream <strong>Ver</strong>ona';
|
|
|
|
assert.equal(opts.highlighter(source[1]), expected_value);
|
|
|
|
|
|
|
|
/* Test sorter */
|
|
|
|
assert.equal(opts.sorter(search_suggestions.strings), search_suggestions.strings);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
let operators;
|
|
|
|
let is_blurred;
|
2018-07-14 16:10:00 +02:00
|
|
|
let is_append_search_string_called;
|
2018-06-22 12:19:40 +02:00
|
|
|
search_query_box.blur = () => {
|
|
|
|
is_blurred = true;
|
|
|
|
};
|
2018-07-14 16:10:00 +02:00
|
|
|
search_pill.append_search_string = () => {
|
|
|
|
is_append_search_string_called = true;
|
|
|
|
};
|
2018-06-22 12:19:40 +02:00
|
|
|
/* Test updater */
|
|
|
|
const _setup = (search_box_val) => {
|
|
|
|
is_blurred = false;
|
2018-07-14 16:10:00 +02:00
|
|
|
is_append_search_string_called = false;
|
2018-06-22 12:19:40 +02:00
|
|
|
search_query_box.val(search_box_val);
|
|
|
|
narrow.activate = (raw_operators, options) => {
|
|
|
|
assert.deepEqual(raw_operators, operators);
|
|
|
|
assert.deepEqual(options, {trigger: 'search'});
|
|
|
|
};
|
2018-07-14 16:10:00 +02:00
|
|
|
search_pill.get_search_string_for_current_filter = () => {
|
|
|
|
return '';
|
|
|
|
};
|
2018-06-22 12:19:40 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
operators = [{
|
|
|
|
negated: false,
|
|
|
|
operator: 'search',
|
|
|
|
operand: 'ver',
|
|
|
|
}];
|
|
|
|
_setup('ver');
|
2018-07-14 16:10:00 +02:00
|
|
|
opts.updater('ver');
|
2018-06-22 12:19:40 +02:00
|
|
|
assert(is_blurred);
|
2018-07-14 16:10:00 +02:00
|
|
|
assert(is_append_search_string_called);
|
2018-06-22 12:19:40 +02:00
|
|
|
|
|
|
|
operators = [{
|
|
|
|
negated: false,
|
|
|
|
operator: 'stream',
|
|
|
|
operand: 'Verona',
|
|
|
|
}];
|
|
|
|
_setup('stream:Verona');
|
2018-07-14 16:10:00 +02:00
|
|
|
opts.updater('stream:Verona');
|
2018-06-22 12:19:40 +02:00
|
|
|
assert(is_blurred);
|
2018-07-14 16:10:00 +02:00
|
|
|
assert(is_append_search_string_called);
|
2018-06-22 12:19:40 +02:00
|
|
|
|
|
|
|
search.is_using_input_method = true;
|
|
|
|
_setup('stream:Verona');
|
2018-07-14 16:10:00 +02:00
|
|
|
opts.updater('stream:Verona');
|
2018-06-22 12:19:40 +02:00
|
|
|
assert(!is_blurred);
|
2018-07-14 16:10:00 +02:00
|
|
|
assert(is_append_search_string_called);
|
2018-06-22 12:19:40 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
searchbox_form.keydown = (func) => {
|
|
|
|
const ev = {
|
|
|
|
which: 15,
|
|
|
|
};
|
|
|
|
search_query_box.is = return_false;
|
|
|
|
assert.equal(func(ev), undefined);
|
|
|
|
|
|
|
|
ev.which = 13;
|
|
|
|
assert.equal(func(ev), undefined);
|
|
|
|
|
|
|
|
ev.which = 13;
|
|
|
|
search_query_box.is = return_true;
|
|
|
|
assert.equal(func(ev), false);
|
|
|
|
|
|
|
|
return search_query_box;
|
|
|
|
};
|
|
|
|
|
|
|
|
search_query_box.keyup = (func) => {
|
|
|
|
const ev = {};
|
|
|
|
let operators;
|
|
|
|
let is_blurred;
|
|
|
|
narrow_state.active = return_false;
|
|
|
|
search_query_box.blur = () => {
|
|
|
|
is_blurred = true;
|
|
|
|
};
|
|
|
|
|
|
|
|
const _setup = (search_box_val) => {
|
|
|
|
is_blurred = false;
|
|
|
|
search_button.prop('disabled', false);
|
|
|
|
search_query_box.val(search_box_val);
|
|
|
|
narrow.activate = (raw_operators, options) => {
|
|
|
|
assert.deepEqual(raw_operators, operators);
|
|
|
|
assert.deepEqual(options, {trigger: 'search'});
|
|
|
|
};
|
2018-07-14 16:10:00 +02:00
|
|
|
search_pill.get_search_string_for_current_filter = () => {
|
|
|
|
return '';
|
|
|
|
};
|
2018-06-22 12:19:40 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2018-07-14 16:10:00 +02:00
|
|
|
operators = [];
|
2018-06-22 12:19:40 +02:00
|
|
|
_setup('');
|
|
|
|
|
|
|
|
ev.which = 15;
|
|
|
|
search_query_box.is = return_false;
|
|
|
|
func(ev);
|
|
|
|
|
|
|
|
assert(!is_blurred);
|
|
|
|
assert(!search_button.prop('disabled'));
|
|
|
|
|
|
|
|
ev.which = 13;
|
|
|
|
search_query_box.is = return_false;
|
|
|
|
func(ev);
|
|
|
|
|
|
|
|
assert(!is_blurred);
|
|
|
|
assert(!search_button.prop('disabled'));
|
|
|
|
|
|
|
|
ev.which = 13;
|
|
|
|
search_query_box.is = return_true;
|
|
|
|
func(ev);
|
|
|
|
assert(is_blurred);
|
|
|
|
assert(search_button.prop('disabled'));
|
|
|
|
|
2018-07-14 16:10:00 +02:00
|
|
|
operators = [{
|
|
|
|
negated: false,
|
|
|
|
operator: 'search',
|
|
|
|
operand: 'ver',
|
|
|
|
}];
|
2018-06-22 12:19:40 +02:00
|
|
|
_setup('ver');
|
|
|
|
search.is_using_input_method = true;
|
|
|
|
func(ev);
|
|
|
|
// No change on enter keyup event when using input tool
|
|
|
|
assert(!is_blurred);
|
|
|
|
assert(!search_button.prop('disabled'));
|
|
|
|
|
|
|
|
|
|
|
|
_setup('ver');
|
|
|
|
ev.which = 13;
|
|
|
|
search_query_box.is = return_true;
|
|
|
|
func(ev);
|
|
|
|
assert(is_blurred);
|
|
|
|
assert(!search_button.prop('disabled'));
|
|
|
|
};
|
|
|
|
|
|
|
|
search_query_box.on = (event, callback) => {
|
|
|
|
if (event === 'focus') {
|
|
|
|
search_button.prop('disabled', true);
|
|
|
|
callback();
|
|
|
|
assert(!search_button.prop('disabled'));
|
|
|
|
} else if (event === 'blur') {
|
|
|
|
search_query_box.val("test string");
|
|
|
|
narrow_state.search_string = () => 'ver';
|
|
|
|
callback();
|
|
|
|
assert.equal(search_query_box.val(), 'ver');
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-07-23 00:25:21 +02:00
|
|
|
searchbox.on = (event, callback) => {
|
|
|
|
if (event === 'focusin') {
|
|
|
|
searchbox.css({"box-shadow": "unset"});
|
|
|
|
callback();
|
|
|
|
assert.deepEqual(searchbox.css(), {"box-shadow": "inset 0px 0px 0px 2px hsl(204, 20%, 74%)"});
|
|
|
|
} else if (event === 'focusout') {
|
|
|
|
searchbox.css({"box-shadow": "inset 0px 0px 0px 2px hsl(204, 20%, 74%)"});
|
|
|
|
callback();
|
|
|
|
assert.deepEqual(searchbox.css(), {"box-shadow": "unset"});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
Simplify narrow/search interactions.
Before this change, if you hit ESC, then hotkey
code would call search.clear_search, which would
call narrow.deactivate(), which would then use
`$('#search_query')` to clear a value, but then
let search.clear_search blur the input and
disable the exit button. It was all confusing.
Things are a bit more organized now.
Now the code works like this:
hotkey.process_escape_key
Just call narrow.deactivate.
$('#search_exit').on('click', ...):
Just call narrow.deactivate.
narrow.deactivate:
Just call search.clear_search_form
search.clear_search_form:
Just do simple jquery stuff. Don't
change the entire user's narrow, not
even indirectly!
There's still a two-way interaction between
the narrow.js module and the search.js module,
but in each direction it's a one-liner.
The guiding principle here is that we only
want one top-level API, which is narrow.deactivate,
and that does the whole "kitchen sink" of
clearing searches, closing popovers, switching
in views, etc. And then all the functions it
calls out to tend to have much smaller jobs to
do.
This commit can mostly be considered a refactoring, but the
order of operations changes slightly. Basically, as
soon as you hit ESC or click on the search "X", we
clear the search widget. Most users won't notice
any difference, because we don't have to hit the
server to populate the home view. And it's arguably
an improvement to give more immediate feedback.
2018-09-10 19:36:58 +02:00
|
|
|
let is_deactivated;
|
|
|
|
narrow.deactivate = () => {
|
|
|
|
is_deactivated = true;
|
2018-06-22 12:19:40 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
search.initialize();
|
Simplify narrow/search interactions.
Before this change, if you hit ESC, then hotkey
code would call search.clear_search, which would
call narrow.deactivate(), which would then use
`$('#search_query')` to clear a value, but then
let search.clear_search blur the input and
disable the exit button. It was all confusing.
Things are a bit more organized now.
Now the code works like this:
hotkey.process_escape_key
Just call narrow.deactivate.
$('#search_exit').on('click', ...):
Just call narrow.deactivate.
narrow.deactivate:
Just call search.clear_search_form
search.clear_search_form:
Just do simple jquery stuff. Don't
change the entire user's narrow, not
even indirectly!
There's still a two-way interaction between
the narrow.js module and the search.js module,
but in each direction it's a one-liner.
The guiding principle here is that we only
want one top-level API, which is narrow.deactivate,
and that does the whole "kitchen sink" of
clearing searches, closing popovers, switching
in views, etc. And then all the functions it
calls out to tend to have much smaller jobs to
do.
This commit can mostly be considered a refactoring, but the
order of operations changes slightly. Basically, as
soon as you hit ESC or click on the search "X", we
clear the search widget. Most users won't notice
any difference, because we don't have to hit the
server to populate the home view. And it's arguably
an improvement to give more immediate feedback.
2018-09-10 19:36:58 +02:00
|
|
|
|
|
|
|
const search_exit_callback = $('#search_exit').get_on_handler('click');
|
|
|
|
|
|
|
|
search_exit_callback();
|
|
|
|
assert(is_deactivated);
|
2018-06-22 12:19:40 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
run_test('initiate_search', () => {
|
2018-07-23 17:14:53 +02:00
|
|
|
let is_searchbox_focused = false;
|
|
|
|
$('#search_query').focus = () => {
|
|
|
|
is_searchbox_focused = true;
|
2018-06-22 12:19:40 +02:00
|
|
|
};
|
|
|
|
search.initiate_search();
|
2018-07-23 17:14:53 +02:00
|
|
|
assert(is_searchbox_focused);
|
2018-06-22 12:19:40 +02:00
|
|
|
});
|