diff --git a/media/js/impala/suggestions.js b/media/js/impala/suggestions.js index 74ae0ec12e..0c2ec550a2 100644 --- a/media/js/impala/suggestions.js +++ b/media/js/impala/suggestions.js @@ -60,8 +60,7 @@ $.fn.searchSuggestions = function($results) { var gestureKeys = [ $.ui.keyCode.ESCAPE, $.ui.keyCode.UP, $.ui.keyCode.DOWN, - $.ui.keyCode.PAGE_UP, $.ui.keyCode.PAGE_DOWN, - $.ui.keyCode.HOME, $.ui.keyCode.END + $.ui.keyCode.PAGE_UP, $.ui.keyCode.PAGE_DOWN ]; function pageUp() { @@ -86,16 +85,18 @@ $.fn.searchSuggestions = function($results) { } function gestureHandler(e) { - if (!$results.hasClass('visible')) { + // Bail if the results are hidden or if we have a non-gesture key + // or if we have a alt/ctrl/meta/shift keybinding. + if (!$results.hasClass('visible') || + $.inArray(e.which, gestureKeys) < 0 || + e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { + $results.trigger('keyIgnored'); return; } - if ($.inArray(e.which, gestureKeys) >= 0) { - e.preventDefault(); - } + e.preventDefault(); if (e.which == $.ui.keyCode.ESCAPE) { dismissHandler(); - } - if (e.which == $.ui.keyCode.UP || e.which == $.ui.keyCode.DOWN) { + } else if (e.which == $.ui.keyCode.UP || e.which == $.ui.keyCode.DOWN) { var $sel = $results.find('.sel'), $elems = $results.find('a'), i = $elems.index($sel.get(0)); @@ -115,12 +116,10 @@ $.fn.searchSuggestions = function($results) { $sel.removeClass('sel'); $elems.eq(i).addClass('sel'); $results.addClass('sel').trigger('selectedRowUpdate', [i]); - } else if (e.which == $.ui.keyCode.PAGE_UP || - e.which == $.ui.keyCode.HOME) { + } else if (e.which == $.ui.keyCode.PAGE_UP) { pageUp(); $results.addClass('sel').trigger('selectedRowUpdate', [0]); - } else if (e.which == $.ui.keyCode.PAGE_DOWN || - e.which == $.ui.keyCode.END) { + } else if (e.which == $.ui.keyCode.PAGE_DOWN) { pageDown(); $results.addClass('sel').trigger('selectedRowUpdate', [$results.find('a').length - 1]); diff --git a/media/js/zamboni/tests/suggestions_tests.js b/media/js/zamboni/tests/suggestions_tests.js index d5b7f461be..8c78d86383 100644 --- a/media/js/zamboni/tests/suggestions_tests.js +++ b/media/js/zamboni/tests/suggestions_tests.js @@ -62,6 +62,59 @@ module('Search Suggestions', { } $input.val(query); $input.triggerHandler(eventType); + }, + testRowSelector: function(eventWhich, rowIndex) { + var $input = this.input, + $results = this.results, + expected = null; + + this.sandbox.bind('selectedRowUpdate', function(e, row) { + expected = row; + }); + + tests.waitFor(function() { + return expected !== null; + }).thenDo(function() { + // Row index is zero-based. There are four rows: one for the + // placeholder and three results. + equal(expected, rowIndex, + 'Expected row ' + rowIndex + ' to be highlighted'); + start(); + }); + + // Let's pretend we made a query. Generate three result rows. + for (var i = 0; i < 3; i++) { + $results.append('
  • '); + } + + // Initialize highlighting. + $results.trigger('highlight', ['xxx']); + + // Simulate keystrokes. + $input.val('xxx'); + $input.triggerHandler({type: 'keydown', which: eventWhich}); + }, + testKeyIgnored: function(event) { + var $input = this.input, + $results = this.results, + expected = null; + + this.sandbox.bind('keyIgnored', function(e) { + expected = true; + }); + + tests.waitFor(function() { + return expected !== null; + }).thenDo(function() { + ok(expected, 'Key binding should have been ignored'); + start(); + }); + + // Initialize highlighting. + $results.trigger('highlight', ['xxx']); + + // Simulate keystrokes. + $input.val('xxx').triggerHandler(event); } }); @@ -76,8 +129,7 @@ test('Generated HTML tags', function() { test('Default search label', function() { - var $sandbox = this.sandbox, - $results = this.results, + var $results = this.results, $input = this.input; function check(cat, expected) { $results.attr('data-cat', cat); @@ -138,16 +190,112 @@ asyncTest('Results upon paste', function() { asyncTest('Hide results upon escape/blur', function() { - var self = this, - $input = self.input, - $results = self.results; - $input.val('xxx'); - $input.triggerHandler('blur'); + var $input = this.input, + $results = this.results; + $input.val('xxx').triggerHandler('blur'); tests.lacksClass($results, 'visible'); start(); }); +asyncTest('Key bindings: Hijacked: page up', function() { + this.testRowSelector($.ui.keyCode.PAGE_UP, 0); +}); +asyncTest('Key bindings: Hijacked: arrow up', function() { + this.testRowSelector($.ui.keyCode.UP, 0); +}); +asyncTest('Key bindings: Hijacked: arrow down', function() { + this.testRowSelector($.ui.keyCode.DOWN, 1); +}); +asyncTest('Key bindings: Hijacked: page down', function() { + this.testRowSelector($.ui.keyCode.PAGE_DOWN, 3); +}); + + +asyncTest('Key bindings: Ignored: home', function() { + this.testKeyIgnored({type: 'keydown', which: $.ui.keyCode.HOME}); +}); +asyncTest('Key bindings: Ignored: end', function() { + this.testKeyIgnored({type: 'keydown', which: $.ui.keyCode.END}); +}); + + +asyncTest('Key bindings: Ignored: alt + home', function() { + this.testKeyIgnored({type: 'keydown', which: $.ui.keyCode.HOME, + altKey: true}); +}); +asyncTest('Key bindings: Ignored: ctrl + home', function() { + this.testKeyIgnored({type: 'keydown', which: $.ui.keyCode.HOME, + ctrlKey: true}); +}); +asyncTest('Key bindings: Ignored: meta + home', function() { + this.testKeyIgnored({type: 'keydown', which: $.ui.keyCode.HOME, + metaKey: true}); +}); +asyncTest('Key bindings: Ignored: shift + home', function() { + this.testKeyIgnored({type: 'keydown', which: $.ui.keyCode.HOME, + shiftKey: true}); +}); + + +asyncTest('Key bindings: Ignored: alt + end', function() { + this.testKeyIgnored({type: 'keydown', which: $.ui.keyCode.END, + altKey: true}); +}); +asyncTest('Key bindings: Ignored: ctrl + end', function() { + this.testKeyIgnored({type: 'keydown', which: $.ui.keyCode.END, + ctrlKey: true}); +}); +asyncTest('Key bindings: Ignored: meta + end', function() { + this.testKeyIgnored({type: 'keydown', which: $.ui.keyCode.END, + metaKey: true}); +}); +asyncTest('Key bindings: Ignored: shift + end', function() { + this.testKeyIgnored({type: 'keydown', which: $.ui.keyCode.END, + shiftKey: true}); +}); + + +asyncTest('Key bindings: Ignored: alt + left', function() { + this.testKeyIgnored({type: 'keydown', which: $.ui.keyCode.LEFT, + altKey: true}); +}); +asyncTest('Key bindings: Ignored: alt + right', function() { + this.testKeyIgnored({type: 'keydown', which: $.ui.keyCode.RIGHT, + altKey: true}); +}); + + +asyncTest('Key bindings: Ignored: ctrl + left', function() { + this.testKeyIgnored({type: 'keydown', which: $.ui.keyCode.LEFT, + ctrlKey: true}); +}); +asyncTest('Key bindings: Ignored: ctrl + right', function() { + this.testKeyIgnored({type: 'keydown', which: $.ui.keyCode.RIGHT, + ctrlKey: true}); +}); + + +asyncTest('Key bindings: Ignored: meta + left', function() { + this.testKeyIgnored({type: 'keydown', which: $.ui.keyCode.LEFT, + metaKey: true}); +}); +asyncTest('Key bindings: Ignored: meta + right', function() { + this.testKeyIgnored({type: 'keydown', which: $.ui.keyCode.RIGHT, + metaKey: true}); +}); + + +asyncTest('Key bindings: Ignored: shift + left', function() { + this.testKeyIgnored({type: 'keydown', which: $.ui.keyCode.LEFT, + shiftKey: true}); +}); +asyncTest('Key bindings: Ignored: shift + right', function() { + this.testKeyIgnored({type: 'keydown', which: $.ui.keyCode.RIGHT, + shiftKey: true}); +}); + + asyncTest('Cached results do not change', function() { var self = this, $input = self.input,