blacklight 7.22.2 → 7.23.0

Sign up to get free protection for your applications and to get access to all the features.
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