From e058efcdc5303c0f4eeba4a64c80dff4c713a955 Mon Sep 17 00:00:00 2001 From: redsummernight Date: Thu, 5 Oct 2017 15:52:35 -0400 Subject: [PATCH] AO3-4976 Fix stray semicolons in autocomplete results with ampersands (#2923) Avoid highlighting an empty search term, which can split an ampersand entity, leaving a semicolon: "&;". --- features/other_a/autocomplete.feature | 32 ++++++++++++++++++- .../step_definitions/autocomplete_steps.rb | 9 ++++++ public/javascripts/jquery.tokeninput.js | 4 +++ public/javascripts/jquery.tokeninput.min.js | 2 +- 4 files changed, 45 insertions(+), 2 deletions(-) diff --git a/features/other_a/autocomplete.feature b/features/other_a/autocomplete.feature index 692ddbef084..d5224600567 100644 --- a/features/other_a/autocomplete.feature +++ b/features/other_a/autocomplete.feature @@ -16,7 +16,7 @@ Feature: Display autocomplete for tags Given I am logged in And a set of tags for testing autocomplete And I go to the new work page - Then the fandom-specific tag autocomplete fields should list only fandom-specific canonical tags + Then the fandom-specific tag autocomplete fields should list only fandom-specific canonical tags @javascript Scenario: Bookmark archive work form autocomplete should work @@ -90,3 +90,33 @@ Feature: Display autocomplete for tags Then a user account should not exist for "funny" And the pseud autocomplete should not contain "funny" And the pseud autocomplete should not contain "different_user (funny)" + + @javascript + Scenario: Search terms are highlighted in autocomplete results + Given I am logged in + And basic tags + And a canonical relationship "Cassian Andor & Jyn Erso" + And I go to the new work page + + When I enter "Jyn" in the "Relationships" autocomplete field + Then I should see HTML "Cassian Andor & Jyn Erso" in the autocomplete + + When I enter "Cass" in the "Relationships" autocomplete field + Then I should see HTML "Cassian Andor & Jyn Erso" in the autocomplete + + When I enter "erso and" in the "Relationships" autocomplete field + Then I should see HTML "Cassian Andor & Jyn Erso" in the autocomplete + + When I enter "Cassian Andor & Jyn Erso" in the "Relationships" autocomplete field + Then I should see HTML "Cassian Andor & Jyn Erso" in the autocomplete + + # AO3-4976 There should not be stray semicolons if the query has... + # ...trailing spaces + When I enter "Jyn " in the "Relationships" autocomplete field + Then I should see HTML "Cassian Andor & Jyn Erso" in the autocomplete + # ...leading spaces + When I enter " Jyn" in the "Relationships" autocomplete field + Then I should see HTML "Cassian Andor & Jyn Erso" in the autocomplete + # ...consecutive spaces + When I enter "Jyn Erso" in the "Relationships" autocomplete field + Then I should see HTML "Cassian Andor & Jyn Erso" in the autocomplete diff --git a/features/step_definitions/autocomplete_steps.rb b/features/step_definitions/autocomplete_steps.rb index 8bfb471006f..87b05b454d7 100644 --- a/features/step_definitions/autocomplete_steps.rb +++ b/features/step_definitions/autocomplete_steps.rb @@ -15,6 +15,15 @@ step %{a noncanonical freeform "alternate sundays"} end +Then /^I should see HTML "(.*)?" in the autocomplete$/ do |string| + # There should be only one visible autocomplete dropdown. + within("input + .autocomplete", visible: true) do + # Wait for results to appear, then check their HTML content + expect(current_scope).to have_selector("li") + expect(current_scope["innerHTML"]).to include(string) + end +end + Then /^I should see "([^\"]+)" in the autocomplete$/ do |string| # There should be only one visible autocomplete dropdown. expect(find("input + .autocomplete", visible: true)).to have_content(string) diff --git a/public/javascripts/jquery.tokeninput.js b/public/javascripts/jquery.tokeninput.js index b67e7f5b6ac..d0aba7174e8 100644 --- a/public/javascripts/jquery.tokeninput.js +++ b/public/javascripts/jquery.tokeninput.js @@ -699,6 +699,10 @@ $.TokenList = function (input, url_or_data, settings) { function highlight_term(value, term) { var newvalue = value; $.each(term.split(' '), function(index, termbit) { + if (!termbit) { + // AO3-4976 skip empty strings + return; + } termbit = termbit.replace(/([.?*+^$[\]\\(){}-])/g, "\\$1"); newvalue = newvalue.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + termbit + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "$1"); }); diff --git a/public/javascripts/jquery.tokeninput.min.js b/public/javascripts/jquery.tokeninput.min.js index ae4d8dbb8a2..0c6f187063c 100644 --- a/public/javascripts/jquery.tokeninput.min.js +++ b/public/javascripts/jquery.tokeninput.min.js @@ -1 +1 @@ -(function(e){var t={hintText:"Type in a search term",noResultsText:"No results",searchingText:"Searching...",deleteText:"×",searchDelay:500,minChars:1,tokenLimit:null,jsonContainer:null,method:"GET",contentType:"json",queryParam:"q",tokenDelimiter:",",preventDuplicates:false,prePopulate:null,processPrePopulate:false,makeSortable:false,escapeHTML:true,animateDropdown:true,onResult:null,onAdd:null,onDelete:null,noCache:false};var n={tokenList:"autocomplete",sortable:"sortable",token:"added tag",tokenDelete:"delete",selectedToken:"selected",highlightedToken:"highlighted",dropdown:"autocomplete dropdown",dropdownItem:"even",dropdownItem2:"odd",selectedDropdownItem:"selected",inputToken:"input",insertBefore:"selected",insertAfter:"selected"};var r={BEFORE:0,AFTER:1,END:2};var i={BACKSPACE:8,TAB:9,ENTER:13,ESCAPE:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT:37,UP:38,RIGHT:39,DOWN:40,DELETE:46,NUMPAD_ENTER:108,COMMA:188};e.fn.tokenInput=function(n,r){var i=e.extend({},t,r||{});return this.each(function(){new e.TokenList(this,n,i)})};e.TokenList=function(t,s,o){function N(e){return e>=48&&e<=90||e>=96&&e<=111||e>=186&&e<=192||e>=219&&e<=222}function C(t,n){if(n.indexOf(o.tokenDelimiter)>=0){e.each(n.split(o.tokenDelimiter),function(e,t){C(t,t)});return}var r=e("
  • "+R(n)+"
  • ").addClass(o.classes.token).insertBefore(E);if(o.makeSortable){}var i=e("").addClass(o.classes.tokenDelete).appendTo(r).click(function(){_(e(this).parent());return false});e(''+o.deleteText+"").attr("title","remove "+n).appendTo(i);var s={id:t,name:n};e.data(r.get(0),"tokeninput",s);u=u.slice(0,g).concat([s]).concat(u.slice(g));g++;var f=e.map(u,function(e){return e.id});v.val(f.join(o.tokenDelimiter));a+=1;return r}function k(t){var n;if(e.type(t)==="string"){n={id:t,name:t}}else{n=e.data(t.get(0),"tokeninput")}d.val("");var r=o.onAdd;if(a>0&&o.preventDuplicates){var i=null;w.children().each(function(){var t=e(this);var r=e.data(t.get(0),"tokeninput");if(r&&r.id===n.id){i=t;return false}});if(i){A(i);d.focus();return}}C(n.id,n.name);if(o.tokenLimit!==null&&a>=o.tokenLimit){d.hide()}else{d.focus()}D();if(e.isFunction(r)){r.call(v,n)}d.change()}function L(t){D();var n=e.data(t.get(0),"tokeninput");_(t);d.val(n.id)}function A(e){e.addClass(o.classes.selectedToken);m=e.get(0);d.val("");D()}function O(e,t){e.removeClass(o.classes.selectedToken);m=null;if(t===r.BEFORE){g--}else if(t===r.AFTER){g++}else{g=a}d.focus()}function M(t){var n=m;if(m){if(m===t.get(0)){L(t);return}O(e(m),r.END)}if(n===t.get(0)){O(t,r.END)}else{A(t)}}function _(t){var n=e.data(t.get(0),"tokeninput");var r=o.onDelete;var i=t.prevAll().length;if(i>g)i--;t.remove();m=null;d.focus();u=u.slice(0,i).concat(u.slice(i+1));if(i"+o.searchingText+"

    ");P()}}function B(){if(o.hintText){S.html("

    "+o.hintText+"

    ");P()}}function j(t,n){var r=t;e.each(n.split(" "),function(e,t){t=t.replace(/([.?*+^$[\]\\(){}-])/g,"\\$1");r=r.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)("+t+")(?![^<>]*>)(?![^&;]+;)","gi"),"$1")});return r}function F(t,n){if(n&&n.length){S.empty();var r=e('