blacklight 7.22.2 → 7.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +29 -8
  3. data/VERSION +1 -1
  4. data/app/assets/stylesheets/blacklight/_facets.scss +6 -0
  5. data/app/components/blacklight/advanced_search_form_component.rb +5 -5
  6. data/app/components/blacklight/constraints_component.html.erb +8 -4
  7. data/app/components/blacklight/constraints_component.rb +43 -18
  8. data/app/components/blacklight/document/action_component.html.erb +1 -1
  9. data/app/components/blacklight/document/action_component.rb +10 -3
  10. data/app/components/blacklight/document/bookmark_component.rb +2 -2
  11. data/app/components/blacklight/document/citation_component.rb +1 -1
  12. data/app/components/blacklight/document/group_component.html.erb +1 -1
  13. data/app/components/blacklight/document/group_component.rb +2 -2
  14. data/app/components/blacklight/document/more_like_this_component.html.erb +1 -1
  15. data/app/components/blacklight/document/more_like_this_component.rb +1 -1
  16. data/app/components/blacklight/document/thumbnail_component.rb +1 -1
  17. data/app/components/blacklight/document_component.rb +2 -2
  18. data/app/components/blacklight/document_title_component.rb +3 -3
  19. data/app/components/blacklight/facet_field_checkboxes_component.rb +1 -1
  20. data/app/components/blacklight/facet_field_inclusive_constraint_component.html.erb +1 -1
  21. data/app/components/blacklight/facet_field_inclusive_constraint_component.rb +1 -1
  22. data/app/components/blacklight/facet_field_list_component.html.erb +1 -1
  23. data/app/components/blacklight/facet_field_list_component.rb +1 -1
  24. data/app/components/blacklight/facet_field_pagination_component.html.erb +4 -4
  25. data/app/components/blacklight/facet_item_component.rb +2 -2
  26. data/app/components/blacklight/facet_item_pivot_component.rb +2 -2
  27. data/app/components/blacklight/metadata_field_component.rb +2 -2
  28. data/app/components/blacklight/response/facet_group_component.html.erb +1 -1
  29. data/app/components/blacklight/response/facet_group_component.rb +1 -1
  30. data/app/components/blacklight/response/pagination_component.rb +1 -1
  31. data/app/components/blacklight/response/sort_component.html.erb +1 -1
  32. data/app/components/blacklight/response/spellcheck_component.rb +14 -3
  33. data/app/components/blacklight/response/view_type_button_component.rb +3 -3
  34. data/app/components/blacklight/response/view_type_component.rb +1 -1
  35. data/app/components/blacklight/search_bar_component.rb +2 -2
  36. data/app/components/blacklight/search_context_component.rb +3 -3
  37. data/app/components/blacklight/search_history_constraint_layout_component.rb +14 -0
  38. data/app/components/blacklight/start_over_button_component.rb +20 -0
  39. data/app/components/blacklight/system/dropdown_component.rb +1 -1
  40. data/app/controllers/concerns/blacklight/bookmarks.rb +1 -1
  41. data/app/controllers/concerns/blacklight/catalog.rb +2 -2
  42. data/app/controllers/concerns/blacklight/search_context.rb +1 -1
  43. data/app/helpers/blacklight/blacklight_helper_behavior.rb +12 -4
  44. data/app/helpers/blacklight/catalog_helper_behavior.rb +18 -7
  45. data/app/helpers/blacklight/render_partials_helper_behavior.rb +12 -1
  46. data/app/helpers/blacklight/search_history_constraints_helper_behavior.rb +30 -2
  47. data/app/helpers/blacklight/url_helper_behavior.rb +3 -1
  48. data/app/javascript/blacklight/modal.js +2 -2
  49. data/app/models/concerns/blacklight/configurable.rb +1 -1
  50. data/app/models/concerns/blacklight/document/active_model_shim.rb +1 -1
  51. data/app/models/concerns/blacklight/document/extensions.rb +1 -1
  52. data/app/models/concerns/blacklight/document/semantic_fields.rb +1 -1
  53. data/app/models/concerns/blacklight/document.rb +1 -1
  54. data/app/views/catalog/_email_form.html.erb +1 -1
  55. data/app/views/catalog/_search_results.html.erb +1 -1
  56. data/app/views/catalog/_sms_form.html.erb +3 -3
  57. data/app/views/catalog/_start_over.html.erb +1 -1
  58. data/app/views/layouts/blacklight/base.html.erb +1 -0
  59. data/blacklight.gemspec +3 -4
  60. data/lib/blacklight/configuration/fields.rb +1 -1
  61. data/lib/blacklight/configuration.rb +2 -1
  62. data/lib/blacklight/deprecations/engine_configuration.rb +66 -0
  63. data/lib/blacklight/engine.rb +21 -10
  64. data/lib/blacklight/exceptions.rb +3 -0
  65. data/lib/blacklight/search_state/filter_field.rb +4 -4
  66. data/lib/blacklight/solr/repository.rb +36 -12
  67. data/lib/blacklight/solr/search_builder_behavior.rb +1 -4
  68. data/lib/generators/blacklight/assets_generator.rb +14 -10
  69. data/spec/components/blacklight/constraints_component_spec.rb +68 -0
  70. data/spec/components/blacklight/document/action_component_spec.rb +2 -1
  71. data/spec/components/blacklight/document_component_spec.rb +1 -1
  72. data/spec/components/blacklight/response/spellcheck_component_spec.rb +73 -0
  73. data/spec/components/blacklight/start_over_button_component_spec.rb +38 -0
  74. data/spec/controllers/catalog_controller_spec.rb +1 -1
  75. data/spec/features/did_you_mean_spec.rb +21 -0
  76. data/spec/features/search_context_spec.rb +3 -1
  77. data/spec/helpers/blacklight/search_history_constraints_helper_behavior_spec.rb +2 -0
  78. data/spec/helpers/blacklight/url_helper_behavior_spec.rb +3 -3
  79. data/spec/helpers/blacklight_helper_spec.rb +8 -3
  80. data/spec/helpers/catalog_helper_spec.rb +6 -2
  81. data/spec/lib/blacklight/engine_spec.rb +41 -0
  82. data/spec/models/blacklight/facet_paginator_spec.rb +60 -15
  83. data/spec/models/blacklight/solr/repository_spec.rb +29 -21
  84. data/spec/models/blacklight/solr/response/facets_spec.rb +48 -10
  85. data/spec/presenters/blacklight/facet_item_presenter_spec.rb +1 -1
  86. data/spec/presenters/blacklight/field_presenter_spec.rb +2 -2
  87. data/spec/routing/search_history_spec.rb +9 -0
  88. data/spec/services/blacklight/search_service_spec.rb +8 -0
  89. data/spec/spec_helper.rb +2 -3
  90. data/spec/support/controller_level_helpers.rb +8 -0
  91. data/spec/views/catalog/_constraints.html.erb_spec.rb +1 -1
  92. data/spec/views/catalog/_index.html.erb_spec.rb +1 -1
  93. data/spec/views/catalog/_show.html.erb_spec.rb +1 -1
  94. data/spec/views/catalog/_show_sidebar.erb_spec.rb +1 -1
  95. data/spec/views/catalog/facet.json.jbuilder_spec.rb +1 -1
  96. data/spec/views/catalog/index.atom.builder_spec.rb +1 -0
  97. data/spec/views/catalog/index.html.erb_spec.rb +1 -0
  98. data/spec/views/catalog/index.json.jbuilder_spec.rb +1 -1
  99. data/spec/views/catalog/show.json.jbuilder_spec.rb +1 -1
  100. metadata +21 -22
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
- require 'view_component/engine'
2
+ require 'blacklight/deprecations/engine_configuration'
3
+ require 'view_component'
3
4
 
4
5
  module Blacklight
5
6
  class Engine < Rails::Engine
@@ -22,14 +23,18 @@ module Blacklight
22
23
  end
23
24
  end
24
25
 
25
- initializer "blacklight.assets.precompile" do |app|
26
+ initializer "blacklight.assets.precompile" do
27
+ PRECOMPILE_ASSETS = %w(favicon.ico blacklight/blacklight.js blacklight/blacklight.js.map).freeze
28
+
26
29
  # When Rails has been generated in API mode, it does not have sprockets available
27
- if defined? Sprockets
28
- app.config.assets.precompile += %w(favicon.ico)
30
+ if Rails.application.config.respond_to?(:assets)
31
+ Rails.application.config.assets.precompile += PRECOMPILE_ASSETS
29
32
  end
30
33
  end
31
34
 
32
- Blacklight::Engine.config.sms_mappings = {
35
+ bl_global_config = OpenStructWithHashAccess.new
36
+
37
+ bl_global_config.sms_mappings = {
33
38
  'Virgin' => 'vmobl.com',
34
39
  'AT&T' => 'txt.att.net',
35
40
  'Verizon' => 'vtext.com',
@@ -41,14 +46,20 @@ module Blacklight
41
46
  'Google Fi' => 'msg.fi.google.com'
42
47
  }
43
48
 
44
- config.bookmarks_http_method = :post
49
+ bl_global_config.bookmarks_http_method = :post
45
50
 
46
- config.email_regexp = defined?(Devise) ? Devise.email_regexp : /\A[^@\s]+@[^@\s]+\z/
51
+ bl_global_config.email_regexp = defined?(Devise) ? Devise.email_regexp : /\A[^@\s]+@[^@\s]+\z/
47
52
 
48
- config.action_dispatch.rescue_responses["Blacklight::Exceptions::RecordNotFound"] = :not_found
53
+ bl_global_config.facet_missing_param = '[* TO *]'
49
54
 
50
- config.enable_search_bar_autofocus = false
55
+ # Anything that goes into Blacklight::Engine.config is stored as a class
56
+ # variable on Railtie::Configuration. we're going to encapsulate all the
57
+ # Blacklight specific stuff in this single struct:
58
+ Blacklight::Engine.config.blacklight = bl_global_config
51
59
 
52
- config.facet_missing_param = '[* TO *]'
60
+ # Deprecate top-level access to legacy engine configuration
61
+ Blacklight::Deprecations::EngineConfiguration.deprecate_in(Blacklight::Engine.config)
62
+
63
+ config.action_dispatch.rescue_responses["Blacklight::Exceptions::RecordNotFound"] = :not_found
53
64
  end
54
65
  end
@@ -15,6 +15,9 @@ module Blacklight
15
15
 
16
16
  class ECONNREFUSED < ::Errno::ECONNREFUSED; end
17
17
 
18
+ # NOTE: In Blacklight 8, the parent class will be Timeout::Error
19
+ class RepositoryTimeout < InvalidRequest; end
20
+
18
21
  class IconNotFound < StandardError
19
22
  end
20
23
  end
@@ -46,7 +46,7 @@ module Blacklight
46
46
 
47
47
  if value == Blacklight::SearchState::FilterField::MISSING
48
48
  url_key = "-#{key}"
49
- value = Blacklight::Engine.config.facet_missing_param
49
+ value = Blacklight::Engine.config.blacklight.facet_missing_param
50
50
  end
51
51
 
52
52
  param = :f_inclusive if value.is_a?(Array)
@@ -82,7 +82,7 @@ module Blacklight
82
82
 
83
83
  if value == Blacklight::SearchState::FilterField::MISSING
84
84
  url_key = "-#{key}"
85
- value = Blacklight::Engine.config.facet_missing_param
85
+ value = Blacklight::Engine.config.blacklight.facet_missing_param
86
86
  end
87
87
 
88
88
  param = :f_inclusive if value.is_a?(Array)
@@ -114,7 +114,7 @@ module Blacklight
114
114
  params = search_state.params
115
115
  f = Array(params.dig(:f, key))
116
116
  f_inclusive = [params.dig(:f_inclusive, key)] if params.dig(:f_inclusive, key).present?
117
- f_missing = [Blacklight::SearchState::FilterField::MISSING] if params.dig(:f, "-#{key}")&.any? { |v| v == Blacklight::Engine.config.facet_missing_param }
117
+ f_missing = [Blacklight::SearchState::FilterField::MISSING] if params.dig(:f, "-#{key}")&.any? { |v| v == Blacklight::Engine.config.blacklight.facet_missing_param }
118
118
 
119
119
  f + (f_inclusive || []) + (f_missing || [])
120
120
  end
@@ -133,7 +133,7 @@ module Blacklight
133
133
  if value.is_a?(Array)
134
134
  (params.dig(:f_inclusive, key) || []).to_set == value.to_set
135
135
  elsif value == Blacklight::SearchState::FilterField::MISSING
136
- (params.dig(:f, "-#{key}") || []).include?(Blacklight::Engine.config.facet_missing_param)
136
+ (params.dig(:f, "-#{key}") || []).include?(Blacklight::Engine.config.blacklight.facet_missing_param)
137
137
  else
138
138
  (params.dig(:f, key) || []).include?(value)
139
139
  end
@@ -58,30 +58,40 @@ module Blacklight::Solr
58
58
  # @return [Blacklight::Solr::Response] the solr response object
59
59
  def send_and_receive(path, solr_params = {})
60
60
  benchmark("Solr fetch", level: :debug) do
61
- res = if solr_params[:json].present?
62
- connection.send_and_receive(
63
- path,
64
- data: { params: solr_params.to_hash.except(:json) }.merge(solr_params[:json]).to_json,
65
- method: :post,
66
- headers: { 'Content-Type' => 'application/json' }
67
- )
68
- else
69
- key = blacklight_config.http_method == :post ? :data : :params
70
- connection.send_and_receive(path, { key => solr_params.to_hash, method: blacklight_config.http_method })
71
- end
72
-
61
+ res = connection.send_and_receive(path, build_solr_request(solr_params))
73
62
  solr_response = blacklight_config.response_model.new(res, solr_params, document_model: blacklight_config.document_model, blacklight_config: blacklight_config)
74
63
 
75
64
  Blacklight.logger&.debug("Solr query: #{blacklight_config.http_method} #{path} #{solr_params.to_hash.inspect}")
76
65
  Blacklight.logger&.debug("Solr response: #{solr_response.inspect}") if defined?(::BLACKLIGHT_VERBOSE_LOGGING) && ::BLACKLIGHT_VERBOSE_LOGGING
77
66
  solr_response
78
67
  end
68
+ rescue *defined_rsolr_timeout_exceptions => e
69
+ raise Blacklight::Exceptions::RepositoryTimeout, "Timeout connecting to Solr instance using #{connection.inspect}: #{e.inspect}"
79
70
  rescue Errno::ECONNREFUSED => e
71
+ # intended for and likely to be a RSolr::Error:ConnectionRefused, specifically.
80
72
  raise Blacklight::Exceptions::ECONNREFUSED, "Unable to connect to Solr instance using #{connection.inspect}: #{e.inspect}"
81
73
  rescue RSolr::Error::Http => e
82
74
  raise Blacklight::Exceptions::InvalidRequest, e.message
83
75
  end
84
76
 
77
+ # @return [Hash]
78
+ # @!visibility private
79
+ def build_solr_request(solr_params)
80
+ if solr_params[:json].present?
81
+ {
82
+ data: { params: solr_params.to_hash.except(:json) }.merge(solr_params[:json]).to_json,
83
+ method: :post,
84
+ headers: { 'Content-Type' => 'application/json' }
85
+ }
86
+ else
87
+ key = blacklight_config.http_method == :post ? :data : :params
88
+ {
89
+ key => solr_params.to_hash,
90
+ method: blacklight_config.http_method
91
+ }
92
+ end
93
+ end
94
+
85
95
  private
86
96
 
87
97
  ##
@@ -97,5 +107,19 @@ module Blacklight::Solr
97
107
  def build_connection
98
108
  RSolr.connect(connection_config.merge(adapter: connection_config[:http_adapter]))
99
109
  end
110
+
111
+ # RSolr 2.4.0+ has a RSolr::Error::Timeout that we'd like to treat specially
112
+ # instead of lumping into RSolr::Error::Http. Before that we can not rescue
113
+ # specially, so return an empty array.
114
+ #
115
+ # @return [Array<Exception>] that can be used, with a splat, as argument
116
+ # to a ruby rescue
117
+ def defined_rsolr_timeout_exceptions
118
+ if defined?(RSolr::Error::Timeout)
119
+ [RSolr::Error::Timeout]
120
+ else
121
+ []
122
+ end
123
+ end
100
124
  end
101
125
  end
@@ -355,10 +355,7 @@ module Blacklight::Solr
355
355
  solr_field ||= facet_field
356
356
 
357
357
  local_params = []
358
-
359
- if use_local_params
360
- local_params << "tag=#{facet_config.tag}" if facet_config && facet_config.tag
361
- end
358
+ local_params << "tag=#{facet_config.tag}" if use_local_params && facet_config && facet_config.tag
362
359
 
363
360
  if facet_config && facet_config.query
364
361
  if facet_config.query[value]
@@ -11,15 +11,21 @@ module Blacklight
11
11
  gem 'twitter-typeahead-rails', '0.11.1.pre.corejavascript'
12
12
  end
13
13
 
14
- # Add sprockets javascript to Rails 6.
15
- def create_sprockets_javascript
16
- return if Rails.version < '6.0.0'
14
+ def appease_rails7
15
+ return unless Rails.version > '7'
16
+
17
+ gem "sassc-rails", "~> 2.1"
17
18
 
19
+ remove_file 'app/javascript/application.js'
20
+ end
21
+
22
+ # Add sprockets javascript
23
+ def create_sprockets_javascript
18
24
  create_file 'app/assets/javascripts/application.js' do
19
25
  <<~CONTENT
20
26
  //= require jquery3
21
27
  //= require rails-ujs
22
- //= require turbolinks
28
+ #{'//= require turbolinks' if Rails.version < '7'}
23
29
  CONTENT
24
30
  end
25
31
  end
@@ -40,6 +46,7 @@ module Blacklight
40
46
  # Ensure this method is idempotent
41
47
  return if has_blacklight_assets?
42
48
 
49
+ gem 'jquery-rails'
43
50
  contents = "\n//\n// Required by Blacklight\n"
44
51
  contents += "//= require popper\n"
45
52
  contents += "// Twitter Typeahead for autocomplete\n"
@@ -62,11 +69,6 @@ module Blacklight
62
69
  end
63
70
  end
64
71
 
65
- # This is not a default in Rails 5.1+
66
- def add_jquery
67
- gem 'jquery-rails'
68
- end
69
-
70
72
  private
71
73
 
72
74
  def turbolinks?
@@ -78,7 +80,9 @@ module Blacklight
78
80
  end
79
81
 
80
82
  def application_js
81
- IO.read(File.expand_path("app/assets/javascripts/application.js", destination_root))
83
+ path = File.expand_path("app/assets/javascripts/application.js", destination_root)
84
+
85
+ File.exist?(path) ? File.read(path) : ''
82
86
  end
83
87
  end
84
88
  end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Blacklight::ConstraintsComponent, type: :component do
6
+ subject(:component) { described_class.new(**params) }
7
+
8
+ let(:rendered) { render_inline_to_capybara_node(component) }
9
+
10
+ let(:params) do
11
+ { search_state: search_state }
12
+ end
13
+
14
+ let(:blacklight_config) do
15
+ Blacklight::Configuration.new.tap do |config|
16
+ config.add_facet_field 'some_facet'
17
+ end
18
+ end
19
+
20
+ let(:search_state) { Blacklight::SearchState.new(query_params.with_indifferent_access, blacklight_config) }
21
+ let(:query_params) { {} }
22
+
23
+ context 'with a query' do
24
+ let(:query_params) { { q: 'some query' } }
25
+
26
+ it 'renders a start-over link' do
27
+ expect(rendered).to have_link 'Start Over', href: '/catalog'
28
+ end
29
+
30
+ it 'has a header' do
31
+ expect(rendered).to have_selector('h2', text: 'Search Constraints')
32
+ end
33
+
34
+ it 'wraps the output in a div' do
35
+ expect(rendered).to have_selector('div#appliedParams')
36
+ end
37
+
38
+ it 'renders the query' do
39
+ expect(rendered).to have_selector('.applied-filter.constraint', text: 'some query')
40
+ end
41
+ end
42
+
43
+ context 'with a facet' do
44
+ let(:query_params) { { f: { some_facet: ['some value'] } } }
45
+
46
+ it 'renders the query' do
47
+ expect(rendered).to have_selector('.constraint-value > .filter-name', text: 'Some Facet').and(have_selector('.constraint-value > .filter-value', text: 'some value'))
48
+ end
49
+ end
50
+
51
+ describe '.for_search_history' do
52
+ subject(:component) { described_class.for_search_history(**params) }
53
+
54
+ let(:query_params) { { q: 'some query', f: { some_facet: ['some value'] } } }
55
+
56
+ it 'wraps the output in a span' do
57
+ expect(rendered).to have_selector('span .constraint')
58
+ end
59
+
60
+ it 'renders the search state as lightly-decorated text' do
61
+ expect(rendered).to have_selector('.constraint > .filter-values', text: 'some query').and(have_selector('.constraint', text: 'Some Facet:some value'))
62
+ end
63
+
64
+ it 'omits the headers' do
65
+ expect(rendered).not_to have_selector('h2', text: 'Search Constraints')
66
+ end
67
+ end
68
+ end
@@ -24,10 +24,11 @@ RSpec.describe Blacklight::Document::ActionComponent, type: :component do
24
24
 
25
25
  it 'renders an action link' do
26
26
  if Rails.version >= '6'
27
- allow(view_context).to receive(:some_tool_solr_document_path).with(document, only_path: true).and_return('/asdf')
27
+ allow(view_context).to receive(:some_tool_solr_document_path).with(document, { only_path: true }).and_return('/asdf')
28
28
  else
29
29
  allow(view_context).to receive(:some_tool_solr_document_path).with(document).and_return('/asdf')
30
30
  end
31
+
31
32
  expect(rendered).to have_link 'Some tool', href: '/asdf'
32
33
  end
33
34
 
@@ -28,7 +28,7 @@ RSpec.describe Blacklight::DocumentComponent, type: :component do
28
28
  CatalogController.blacklight_config.deep_copy.tap do |config|
29
29
  config.track_search_session = false
30
30
  config.index.thumbnail_field = 'thumbnail_path_ss'
31
- config.index.document_actions[:bookmark].partial = '/catalog/bookmark_control.html.erb'
31
+ config.index.document_actions[:bookmark].partial = '/catalog/bookmark_control'
32
32
  end
33
33
  end
34
34
 
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Blacklight::Response::SpellcheckComponent, type: :component do
6
+ subject(:render) { render_inline(instance) }
7
+
8
+ let(:spellcheck_options) { nil }
9
+ let(:instance) { described_class.new(response: response, options: spellcheck_options) }
10
+ let(:config) do
11
+ Blacklight::Configuration.new do |config|
12
+ config.spell_max = 5
13
+ end
14
+ end
15
+
16
+ before do
17
+ allow(controller).to receive(:blacklight_config).and_return(config)
18
+ end
19
+
20
+ context 'when there are many results' do
21
+ let(:response) { instance_double(Blacklight::Solr::Response, total: 10, spelling: double(words: [1], collation: nil)) }
22
+
23
+ it "does not show suggestions" do
24
+ expect(render.to_html).to be_blank
25
+ end
26
+ end
27
+
28
+ context 'when there are only a few results' do
29
+ let(:word_suggestion) { 'yoshida' }
30
+ let(:response) { instance_double(Blacklight::Solr::Response, total: 4, spelling: double(words: [word_suggestion], collation: nil)) }
31
+
32
+ it "shows suggestions" do
33
+ expect(render.to_html).to include(word_suggestion)
34
+ end
35
+
36
+ context 'and explicit spellcheck options' do
37
+ let(:explicit_option) { 'explicit option' }
38
+ let(:spellcheck_options) { [explicit_option] }
39
+
40
+ it "shows only explicit suggestions" do
41
+ expect(render.to_html).to include(explicit_option)
42
+ expect(render.to_html).not_to include(word_suggestion)
43
+ end
44
+ end
45
+
46
+ context 'and collations are present' do
47
+ let(:word_suggestion) { 'donotuse' }
48
+ let(:collated_suggestion) { 'yoshida Hajime' }
49
+ let(:response) { instance_double(Blacklight::Solr::Response, total: 4, spelling: double(words: [word_suggestion], collation: collated_suggestion)) }
50
+
51
+ it "shows only collated suggestions" do
52
+ expect(render.to_html).to include(collated_suggestion)
53
+ expect(render.to_html).not_to include(word_suggestion)
54
+ end
55
+ end
56
+ end
57
+
58
+ context 'when there are no spelling suggestions' do
59
+ let(:response) { instance_double(Blacklight::Solr::Response, total: 4, spelling: double(words: [], collation: nil)) }
60
+
61
+ it "does not show suggestions" do
62
+ expect(render.to_html).to be_blank
63
+ end
64
+ end
65
+
66
+ context 'when spelling is not available' do
67
+ let(:response) { instance_double(Blacklight::Solr::Response, total: 4, spelling: nil) }
68
+
69
+ it "does not show suggestions" do
70
+ expect(render.to_html).to be_blank
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Blacklight::StartOverButtonComponent, type: :component do
6
+ subject(:render) { render_inline(instance) }
7
+
8
+ let(:instance) { described_class.new }
9
+ let(:blacklight_config) do
10
+ Blacklight::Configuration.new.configure do |config|
11
+ config.view = { list: nil, abc: nil }
12
+ end
13
+ end
14
+
15
+ before do
16
+ allow(controller).to receive(:blacklight_config).and_return(blacklight_config)
17
+ end
18
+
19
+ context 'with the current view type' do
20
+ before do
21
+ controller.params[:view] = 'abc'
22
+ end
23
+
24
+ it 'is the catalog path' do
25
+ expect(render.css('a').first['href']).to eq '/catalog?view=abc'
26
+ end
27
+ end
28
+
29
+ context 'when the current view type is the default' do
30
+ before do
31
+ controller.params[:view] = 'list'
32
+ end
33
+
34
+ it 'does not include the current view type' do
35
+ expect(render.css('a').first['href']).to eq '/catalog'
36
+ end
37
+ end
38
+ end
@@ -788,7 +788,7 @@ RSpec.describe CatalogController, api: true do
788
788
  describe "#add_show_tools_partial", api: false do
789
789
  before do
790
790
  described_class.blacklight_config.add_show_tools_partial(:like, callback: :perform_like, validator: :validate_like_params)
791
- allow(controller).to receive(:solr_document_url).and_return('catalog/1')
791
+ allow(controller).to receive(:solr_document_url).and_return('http://test.host/catalog/1')
792
792
  allow(controller).to receive(:action_documents).and_return(1)
793
793
  Rails.application.routes.draw do
794
794
  get 'catalog/like', as: :catalog_like
@@ -127,4 +127,25 @@ RSpec.describe "Did You Mean" do
127
127
  click_button 'search'
128
128
  expect(page).to have_content("Did you mean")
129
129
  end
130
+
131
+ context 'spellcheck collations are enabled' do
132
+ before do
133
+ CatalogController.blacklight_config[:default_solr_params]["spellcheck.collate"] = true
134
+ end
135
+
136
+ after do
137
+ CatalogController.blacklight_config[:default_solr_params].delete("spellcheck.collate")
138
+ end
139
+
140
+ it "shows suggestions if there aren't many hits" do
141
+ fill_in "q", with: 'Yoshido Hajime'
142
+ click_button 'search'
143
+
144
+ expect(page).to have_content("Did you mean")
145
+ click_link 'yoshida Hajime'
146
+ within ("#sortAndPerPage") do
147
+ expect(page).to have_content "1 - 2 of 2"
148
+ end
149
+ end
150
+ end
130
151
  end
@@ -3,13 +3,14 @@
3
3
  RSpec.describe "Search Results context", js: true do
4
4
  it "passes the current search id through" do
5
5
  search_for ''
6
- search_id = Search.last.id.to_s
7
6
  click_on 'Pluvial nectar of blessings'
7
+ search_id = Search.last.id.to_s
8
8
  expect(page).to have_content "« Previous | 10 of 30 | Next »"
9
9
  prev = page.find(".pagination-search-widgets .previous")
10
10
  expect(prev['data-context-href']).to eq "/catalog/2003546302/track?counter=9&document_id=2003546302&search_id=#{search_id}"
11
11
 
12
12
  click_on "« Previous"
13
+ expect(page).to have_content "U21.2 .W85 2003"
13
14
 
14
15
  prev = page.find(".pagination-search-widgets .previous")
15
16
  expect(prev['data-context-href']).to eq "/catalog/2004310986/track?counter=8&document_id=2004310986&search_id=#{search_id}"
@@ -48,6 +49,7 @@ RSpec.describe "Search Results context", js: true do
48
49
  find_all('.index_title a').last.click
49
50
  click_on "Next »"
50
51
 
52
+ expect(page).to have_content "Naqdī barā-yi tamām-i"
51
53
  click_on "Back to Search"
52
54
  expect(page).to have_content "11 - 20"
53
55
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RSpec.describe Blacklight::SearchHistoryConstraintsHelperBehavior do
4
+ around { |test| Deprecation.silence(described_class) { test.call } }
5
+
4
6
  before(:all) do
5
7
  @config = Blacklight::Configuration.new do |config|
6
8
  config.add_search_field 'default_search_field', label: 'Default'
@@ -133,7 +133,7 @@ RSpec.describe Blacklight::UrlHelperBehavior do
133
133
 
134
134
  it "calls url_for on the engine scope" do
135
135
  expect(my_engine).to receive(:url_for)
136
- .with(q: "query", f: "facets", controller: "catalog")
136
+ .with({ q: "query", f: "facets", controller: "catalog" })
137
137
  .and_return('link-url')
138
138
  expect(tag).to match /Back to Search/
139
139
  expect(tag).to match /link-url/
@@ -292,12 +292,12 @@ RSpec.describe Blacklight::UrlHelperBehavior do
292
292
  let(:document) { SolrDocument.new(id: 1) }
293
293
 
294
294
  it "determines the correct route for the document class" do
295
- allow(helper.main_app).to receive(:track_test_path).with(id: have_attributes(id: 1)).and_return('x')
295
+ allow(helper.main_app).to receive(:track_test_path).with({ id: have_attributes(id: 1) }).and_return('x')
296
296
  expect(helper.session_tracking_path(document)).to eq 'x'
297
297
  end
298
298
 
299
299
  it "passes through tracking parameters" do
300
- allow(helper.main_app).to receive(:track_test_path).with(id: have_attributes(id: 1), x: 1).and_return('x')
300
+ allow(helper.main_app).to receive(:track_test_path).with({ id: have_attributes(id: 1), x: 1 }).and_return('x')
301
301
  expect(helper.session_tracking_path(document, x: 1)).to eq 'x'
302
302
  end
303
303
 
@@ -82,7 +82,7 @@ RSpec.describe BlacklightHelper do
82
82
  end
83
83
 
84
84
  it "sends parameters" do
85
- expect(presenter).to receive(:link_rel_alternates).with(unique: true).and_return(result)
85
+ expect(presenter).to receive(:link_rel_alternates).with({ unique: true }).and_return(result)
86
86
  expect(helper.render_link_rel_alternates(document, unique: true)).to eq result
87
87
  end
88
88
  end
@@ -264,8 +264,13 @@ RSpec.describe BlacklightHelper do
264
264
  expect(helper.should_show_spellcheck_suggestions?(response)).to be true
265
265
  end
266
266
 
267
+ it "only shows suggestions from collations" do
268
+ response = double(total: 4, spelling: double(words: [], collation: { blah: 1 }))
269
+ expect(helper.should_show_spellcheck_suggestions?(response)).to be true
270
+ end
271
+
267
272
  it "shows suggestions only if there are spelling suggestions available" do
268
- response = double(total: 4, spelling: double(words: []))
273
+ response = double(total: 4, spelling: double(words: [], collation: nil))
269
274
  expect(helper.should_show_spellcheck_suggestions?(response)).to be false
270
275
  end
271
276
 
@@ -298,7 +303,7 @@ RSpec.describe BlacklightHelper do
298
303
  describe "#render_document_index" do
299
304
  it "renders the document index with the current view type" do
300
305
  allow(helper).to receive_messages(document_index_view_type: :current_view)
301
- allow(helper).to receive(:render_document_index_with_view).with(:current_view, [], a: 1, b: 2)
306
+ allow(helper).to receive(:render_document_index_with_view).with(:current_view, [], { a: 1, b: 2 })
302
307
  helper.render_document_index [], a: 1, b: 2
303
308
  end
304
309
  end
@@ -369,7 +369,7 @@ RSpec.describe CatalogHelper do
369
369
  end
370
370
 
371
371
  describe "#render_search_to_page_title" do
372
- subject { helper.render_search_to_page_title(params) }
372
+ subject { helper.render_search_to_page_title(Blacklight::SearchState.new(params, blacklight_config)) }
373
373
 
374
374
  before do
375
375
  allow(helper).to receive(:blacklight_config).and_return(blacklight_config)
@@ -377,7 +377,11 @@ RSpec.describe CatalogHelper do
377
377
  allow(helper).to receive(:label_for_search_field).with(nil).and_return('')
378
378
  end
379
379
 
380
- let(:blacklight_config) { Blacklight::Configuration.new }
380
+ let(:blacklight_config) do
381
+ Blacklight::Configuration.new.tap do |config|
382
+ config.add_facet_field 'format'
383
+ end
384
+ end
381
385
 
382
386
  context 'when the f param is an array' do
383
387
  let(:params) { ActionController::Parameters.new(q: 'foobar', f: { format: ["Book"] }) }
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Blacklight::Engine do
4
+ [:bookmarks_http_method, :email_regexp, :facet_missing_param, :sms_mappings].each do |dep_key|
5
+ describe "config.#{dep_key}" do
6
+ subject { described_class.config }
7
+
8
+ let(:unlikely_value) { 'unlikely value' }
9
+
10
+ it 'is deprecated' do
11
+ allow(Deprecation).to receive(:warn)
12
+ subject.send(dep_key)
13
+ expect(Deprecation).to have_received(:warn)
14
+ end
15
+
16
+ it 'delegates to config.blacklight' do
17
+ allow(subject.blacklight).to receive(dep_key).and_return(unlikely_value)
18
+ expect(subject.send(dep_key)).to eql(unlikely_value)
19
+ end
20
+ end
21
+
22
+ describe "config.#{dep_key}=" do
23
+ subject { described_class.config }
24
+
25
+ let(:unlikely_value) { 'unlikely value' }
26
+
27
+ it 'is deprecated' do
28
+ allow(Deprecation).to receive(:warn)
29
+ allow(subject.blacklight).to receive(:"#{dep_key}=").with(unlikely_value)
30
+ subject.send(:"#{dep_key}=", unlikely_value)
31
+ expect(Deprecation).to have_received(:warn)
32
+ end
33
+
34
+ it 'delegates to config.blacklight' do
35
+ allow(subject.blacklight).to receive(:"#{dep_key}=").with(unlikely_value)
36
+ subject.send(:"#{dep_key}=", unlikely_value)
37
+ expect(subject.blacklight).to have_received(:"#{dep_key}=").with(unlikely_value)
38
+ end
39
+ end
40
+ end
41
+ end