blacklight 8.0.0 → 8.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.env +1 -1
  3. data/.github/workflows/ruby.yml +11 -0
  4. data/.rubocop.yml +4 -0
  5. data/.rubocop_todo.yml +67 -73
  6. data/VERSION +1 -1
  7. data/app/builders/blacklight/action_builder.rb +1 -1
  8. data/app/components/blacklight/advanced_search_form_component.html.erb +1 -1
  9. data/app/components/blacklight/advanced_search_form_component.rb +2 -2
  10. data/app/components/blacklight/document/sidebar_component.html.erb +1 -1
  11. data/app/components/blacklight/document/sidebar_component.rb +12 -0
  12. data/app/components/blacklight/response/pagination_component.html.erb +1 -1
  13. data/app/components/blacklight/response/pagination_component.rb +11 -2
  14. data/app/components/blacklight/search_bar_component.html.erb +1 -1
  15. data/app/controllers/concerns/blacklight/bookmarks.rb +1 -1
  16. data/app/helpers/blacklight/blacklight_helper_behavior.rb +10 -0
  17. data/app/models/concerns/blacklight/document/active_model_shim.rb +10 -0
  18. data/app/models/search.rb +6 -1
  19. data/app/services/blacklight/field_retriever.rb +13 -11
  20. data/app/views/catalog/_show_tools.html.erb +1 -0
  21. data/app/views/layouts/blacklight/base.html.erb +0 -3
  22. data/blacklight.gemspec +1 -2
  23. data/config/locales/blacklight.en.yml +1 -0
  24. data/lib/blacklight/component.rb +1 -1
  25. data/lib/blacklight/configuration.rb +10 -1
  26. data/lib/blacklight/engine.rb +12 -0
  27. data/lib/blacklight/solr/repository.rb +14 -2
  28. data/lib/blacklight/solr/search_builder_behavior.rb +2 -1
  29. data/lib/generators/blacklight/assets_generator.rb +1 -3
  30. data/lib/generators/blacklight/install_generator.rb +1 -3
  31. data/lib/generators/blacklight/templates/catalog_controller.rb +1 -0
  32. data/lib/generators/blacklight/templates/solr/conf/solrconfig.xml +69 -0
  33. data/lib/railties/blacklight.rake +4 -4
  34. data/package.json +2 -2
  35. data/spec/components/blacklight/document/sidebar_component_spec.rb +63 -0
  36. data/spec/components/blacklight/facet_component_spec.rb +11 -1
  37. data/spec/components/blacklight/facet_item_pivot_component_spec.rb +2 -2
  38. data/spec/components/blacklight/response/pagination_component_spec.rb +53 -0
  39. data/spec/components/blacklight/search_context/server_applied_params_component_spec.rb +11 -1
  40. data/spec/features/advanced_search_spec.rb +55 -0
  41. data/spec/features/axe_spec.rb +5 -0
  42. data/spec/helpers/blacklight_helper_spec.rb +10 -5
  43. data/spec/models/blacklight/configurable_spec.rb +1 -1
  44. data/spec/models/blacklight/solr/repository_spec.rb +27 -0
  45. data/spec/models/blacklight/solr/search_builder_spec.rb +8 -0
  46. data/spec/presenters/blacklight/show_presenter_spec.rb +4 -10
  47. data/spec/services/blacklight/field_retriever_spec.rb +17 -0
  48. data/spec/spec_helper.rb +29 -2
  49. data/spec/support/view_component_test_helpers.rb +14 -0
  50. data/spec/views/catalog/_paginate_compact.html.erb_spec.rb +2 -0
  51. metadata +11 -19
data/blacklight.gemspec CHANGED
@@ -31,14 +31,13 @@ Gem::Specification.new do |s|
31
31
  s.add_dependency "kaminari", ">= 0.15" # the pagination (page 1,2,3, etc..) of our search results
32
32
  s.add_dependency "i18n", '>= 1.7.0' # added named parameters
33
33
  s.add_dependency "ostruct", '>= 0.3.2'
34
- s.add_dependency "view_component", '>= 2.66', '< 3.1'
34
+ s.add_dependency "view_component", '>= 2.66', '< 4'
35
35
 
36
36
  s.add_development_dependency "rsolr", ">= 1.0.6", "< 3" # Library for interacting with rSolr.
37
37
  s.add_development_dependency "rspec-rails", "~> 6.0"
38
38
  s.add_development_dependency "rspec-collection_matchers", ">= 1.0"
39
39
  s.add_development_dependency 'axe-core-rspec'
40
40
  s.add_development_dependency "capybara", '~> 3'
41
- s.add_development_dependency 'webdrivers'
42
41
  s.add_development_dependency 'selenium-webdriver'
43
42
  s.add_development_dependency 'engine_cart', '~> 2.1'
44
43
  s.add_development_dependency "equivalent-xml"
@@ -244,6 +244,7 @@ en:
244
244
  more_options: More options
245
245
  any_of: 'Any of:'
246
246
  op:
247
+ label: search operator
247
248
  must: all
248
249
  should: any
249
250
  page_title: Advanced search - %{application_name}
@@ -47,7 +47,7 @@ module Blacklight
47
47
 
48
48
  component_class.sidecar_files(extensions).each_with_object([]) do |path, memo|
49
49
  pieces = File.basename(path).split(".")
50
- app_path = "#{Rails.root}/#{path.slice(path.index(component_class.view_component_path)..-1)}"
50
+ app_path = Rails.root.join(path.slice(path.index(component_class.view_component_path)..-1).to_s).to_s
51
51
 
52
52
  memo << {
53
53
  path: File.exist?(app_path) ? app_path : path,
@@ -87,6 +87,10 @@ module Blacklight
87
87
  # @since v5.2.0
88
88
  # @return [String] The url path (relative to the solr base url) to use when requesting only a single document
89
89
  property :document_solr_path, default: 'get'
90
+ # @!attribute json_solr_path
91
+ # @since v7.34.0
92
+ # @return [String] The url path (relative to the solr base url) to use when using Solr's JSON Query DSL (as with the advanced search)
93
+ property :json_solr_path, default: 'advanced'
90
94
  # @!attribute document_unique_id_param
91
95
  # @since v5.2.0
92
96
  # @return [Symbol] The solr query parameter used for sending the unique identifiers for one or more documents
@@ -171,7 +175,9 @@ module Blacklight
171
175
  # component class used to render the search bar
172
176
  search_bar_component: nil,
173
177
  # component class used to render the header above the documents
174
- search_header_component: Blacklight::SearchHeaderComponent
178
+ search_header_component: Blacklight::SearchHeaderComponent,
179
+ # pagination parameters to pass to kaminari
180
+ pagination_options: Blacklight::Engine.config.blacklight.default_pagination_options.dup
175
181
  )
176
182
 
177
183
  # @!attribute show
@@ -181,6 +187,9 @@ module Blacklight
181
187
  # document presenter class used by helpers and views
182
188
  document_presenter_class: nil,
183
189
  document_component: Blacklight::DocumentComponent,
190
+ # in Blacklight 9, the default show_tools_component configuration will
191
+ # be Blacklight::Document::ShowToolsComponent
192
+ show_tools_component: nil,
184
193
  sidebar_component: Blacklight::Document::SidebarComponent,
185
194
  display_type_field: nil,
186
195
  # the "field access" key to use to look up the document display fields
@@ -6,6 +6,18 @@ module Blacklight
6
6
  class Engine < Rails::Engine
7
7
  engine_name "blacklight"
8
8
 
9
+ config.before_configuration do
10
+ # see https://github.com/fxn/zeitwerk#for_gem
11
+ # Blacklight puts a generator into LOCAL APP lib/generators, so tell
12
+ # zeitwerk to ignore the whole directory? If we're using a recent
13
+ # enough version of Rails to have zeitwerk config
14
+ #
15
+ # See: https://github.com/cbeer/engine_cart/issues/117
16
+ if Rails.try(:autoloaders).try(:main).respond_to?(:ignore)
17
+ Rails.autoloaders.main.ignore(Rails.root.join('lib/generators'))
18
+ end
19
+ end
20
+
9
21
  config.after_initialize do
10
22
  Blacklight::Configuration.initialize_default_configuration
11
23
  end
@@ -21,7 +21,7 @@ module Blacklight::Solr
21
21
  # Execute a search query against solr
22
22
  # @param [Hash] params solr query parameters
23
23
  def search params = {}
24
- send_and_receive blacklight_config.solr_path, params.reverse_merge(qt: blacklight_config.qt)
24
+ send_and_receive search_path(params), params.reverse_merge(qt: blacklight_config.qt)
25
25
  end
26
26
 
27
27
  # @param [Hash] request_params
@@ -78,7 +78,7 @@ module Blacklight::Solr
78
78
  # @return [Hash]
79
79
  # @!visibility private
80
80
  def build_solr_request(solr_params)
81
- if solr_params[:json].present?
81
+ if uses_json_query_dsl?(solr_params)
82
82
  {
83
83
  data: { params: solr_params.to_hash.except(:json) }.merge(solr_params[:json]).to_json,
84
84
  method: :post,
@@ -122,5 +122,17 @@ module Blacklight::Solr
122
122
  []
123
123
  end
124
124
  end
125
+
126
+ # @return [String]
127
+ def search_path(solr_params)
128
+ return blacklight_config.json_solr_path if blacklight_config.json_solr_path && uses_json_query_dsl?(solr_params)
129
+
130
+ blacklight_config.solr_path
131
+ end
132
+
133
+ # @return [Boolean]
134
+ def uses_json_query_dsl?(solr_params)
135
+ solr_params[:json].present?
136
+ end
125
137
  end
126
138
  end
@@ -83,8 +83,9 @@ module Blacklight::Solr
83
83
  end
84
84
 
85
85
  def add_search_field_with_json_query_parameters(solr_parameters)
86
- bool_query = search_field.clause_params.transform_values { |v| v.merge(query: search_state.query_param) }
86
+ return unless search_state.query_param
87
87
 
88
+ bool_query = search_field.clause_params.transform_values { |v| v.merge(query: search_state.query_param) }
88
89
  solr_parameters.append_boolean_query(:must, bool_query)
89
90
  end
90
91
 
@@ -1,13 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'shellwords'
4
-
5
3
  module Blacklight
6
4
  class AssetsGenerator < Rails::Generators::Base
7
5
  class_option :'bootstrap-version', type: :string, default: ENV.fetch('BOOTSTRAP_VERSION', '~> 5.1'), desc: "Set the generated app's bootstrap version"
8
6
 
9
7
  def run_asset_pipeline_specific_generator
10
- generated_options = "--bootstrap-version='#{Shellwords.escape(options[:'bootstrap-version'])}'" if options[:'bootstrap-version']
8
+ generated_options = "--bootstrap-version='#{options[:'bootstrap-version']}'" if options[:'bootstrap-version']
11
9
 
12
10
  generator = if defined?(Propshaft)
13
11
  'blacklight:assets:propshaft'
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'shellwords'
4
-
5
3
  module Blacklight
6
4
  class Install < Rails::Generators::Base
7
5
  source_root File.expand_path('../templates', __FILE__)
@@ -37,7 +35,7 @@ module Blacklight
37
35
  # Call external generator in AssetsGenerator, so we can
38
36
  # leave that callable seperately too.
39
37
  def copy_public_assets
40
- generated_options = "--bootstrap-version='#{Shellwords.escape(options[:'bootstrap-version'])}'" if options[:'bootstrap-version']
38
+ generated_options = "--bootstrap-version='#{options[:'bootstrap-version']}'" if options[:'bootstrap-version']
41
39
 
42
40
  generate "blacklight:assets", generated_options unless options[:'skip-assets']
43
41
  end
@@ -38,6 +38,7 @@ class <%= controller_name.classify %>Controller < ApplicationController
38
38
  # solr path which will be added to solr base url before the other solr params.
39
39
  #config.solr_path = 'select'
40
40
  #config.document_solr_path = 'get'
41
+ #config.json_solr_path = 'advanced'
41
42
 
42
43
  # items to show per page, each number in the array represent another option to choose from.
43
44
  #config.per_page = [10,20,50,100]
@@ -100,6 +100,75 @@
100
100
  </arr>
101
101
  </requestHandler>
102
102
 
103
+ <requestHandler name="/advanced" class="solr.SearchHandler">
104
+ <!-- a lucene request handler for using the JSON Query DSL,
105
+ specifically for advanced search.
106
+ Using a separate requestHandler is a workaround to
107
+ https://issues.apache.org/jira/browse/SOLR-16916, although
108
+ it could be desirable for other reasons as well.
109
+ -->
110
+ <lst name="defaults">
111
+ <str name="defType">lucene</str>
112
+ <str name="echoParams">explicit</str>
113
+ <str name="df">title_tsim</str>
114
+ <str name="qf">
115
+ id
116
+ full_title_tsim
117
+ short_title_tsim
118
+ alternative_title_tsim
119
+ active_fedora_model_ssi
120
+ title_tsim
121
+ author_tsim
122
+ subject_tsim
123
+ all_text_timv
124
+ </str>
125
+ <str name="pf">
126
+ all_text_timv^10
127
+ </str>
128
+
129
+ <str name="author_qf">
130
+ author_tsim
131
+ </str>
132
+ <str name="author_pf">
133
+ </str>
134
+ <str name="title_qf">
135
+ title_tsim
136
+ full_title_tsim
137
+ short_title_tsim
138
+ alternative_title_tsim
139
+ </str>
140
+ <str name="title_pf">
141
+ </str>
142
+ <str name="subject_qf">
143
+ subject_tsim
144
+ </str>
145
+ <str name="subject_pf">
146
+ </str>
147
+
148
+ <str name="fl">
149
+ *,
150
+ score
151
+ </str>
152
+
153
+ <str name="facet">true</str>
154
+ <str name="facet.mincount">1</str>
155
+ <str name="facet.limit">10</str>
156
+ <str name="facet.field">active_fedora_model_ssi</str>
157
+ <str name="facet.field">subject_ssim</str>
158
+
159
+ <str name="spellcheck">true</str>
160
+ <str name="spellcheck.dictionary">default</str>
161
+ <str name="spellcheck.onlyMorePopular">true</str>
162
+ <str name="spellcheck.extendedResults">true</str>
163
+ <str name="spellcheck.collate">false</str>
164
+ <str name="spellcheck.count">5</str>
165
+
166
+ </lst>
167
+ <arr name="last-components">
168
+ <str>spellcheck</str>
169
+ </arr>
170
+ </requestHandler>
171
+
103
172
  <requestHandler name="permissions" class="solr.SearchHandler" >
104
173
  <lst name="defaults">
105
174
  <str name="facet">off</str>
@@ -40,7 +40,7 @@ namespace :blacklight do
40
40
  exit 1
41
41
  end
42
42
  rescue => e
43
- puts e.to_s
43
+ puts e
44
44
  exit 1
45
45
  end
46
46
 
@@ -70,7 +70,7 @@ namespace :blacklight do
70
70
  end
71
71
  rescue => e
72
72
  errors += 1
73
- puts e.to_s
73
+ puts e
74
74
  end
75
75
 
76
76
  print " - search_results: "
@@ -92,7 +92,7 @@ namespace :blacklight do
92
92
  end
93
93
  rescue => e
94
94
  errors += 1
95
- puts e.to_s
95
+ puts e
96
96
  end
97
97
 
98
98
  print " - fetch: "
@@ -112,7 +112,7 @@ namespace :blacklight do
112
112
  end
113
113
  rescue => e
114
114
  errors += 1
115
- puts e.to_s
115
+ puts e
116
116
  end
117
117
 
118
118
  exit 1 if errors > 0
data/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "blacklight-frontend",
3
- "version": "8.0.0",
4
- "description": "[![Build Status](https://travis-ci.com/projectblacklight/blacklight.png?branch=main)](https://travis-ci.com/projectblacklight/blacklight) [![Gem Version](https://badge.fury.io/rb/blacklight.png)](http://badge.fury.io/rb/blacklight) [![Coverage Status](https://coveralls.io/repos/github/projectblacklight/blacklight/badge.svg?branch=main)](https://coveralls.io/github/projectblacklight/blacklight?branch=main)",
3
+ "version": "8.0.1",
4
+ "description": "The frontend code and styles for Blacklight",
5
5
  "main": "app/assets/javascripts/blacklight",
6
6
  "module": "app/assets/javascripts/blacklight/blacklight.esm.js",
7
7
  "scripts": {
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Blacklight::Document::SidebarComponent, type: :component do
6
+ subject(:component) { described_class.new(presenter: document) }
7
+
8
+ let(:view_context) { controller.view_context }
9
+ let(:render) do
10
+ component.render_in(view_context)
11
+ end
12
+
13
+ let(:rendered) do
14
+ Capybara::Node::Simple.new(render)
15
+ end
16
+
17
+ let(:document) { view_context.document_presenter(presented_document) }
18
+
19
+ let(:presented_document) { SolrDocument.new(id: 'x', title_tsim: 'Title') }
20
+
21
+ let(:blacklight_config) do
22
+ CatalogController.blacklight_config.deep_copy
23
+ end
24
+
25
+ let(:expected_html) { "<div class=\"expected-show_tools\">Expected Content</div>".html_safe }
26
+
27
+ before do
28
+ # Every call to view_context returns a different object. This ensures it stays stable.
29
+ allow(controller).to receive(:view_context).and_return(view_context)
30
+ allow(controller).to receive(:blacklight_config).and_return(blacklight_config)
31
+ end
32
+
33
+ describe '#render_show_tools' do
34
+ # rubocop:disable RSpec/SubjectStub
35
+ before do
36
+ allow(component).to receive(:render).with(an_instance_of(Blacklight::Document::MoreLikeThisComponent)).and_return("")
37
+ end
38
+
39
+ context "without a configured ShowTools component" do
40
+ before do
41
+ allow(component).to receive(:render).with('show_tools', document: presented_document, silence_deprecation: false).and_return(expected_html)
42
+ end
43
+
44
+ it 'renders show_tools partial' do
45
+ expect(rendered).to have_selector 'div[@class="expected-show_tools"]'
46
+ end
47
+ end
48
+
49
+ context "with a configured ShowTools component" do
50
+ let(:show_tools_component) { Class.new(Blacklight::Document::ShowToolsComponent) }
51
+
52
+ before do
53
+ blacklight_config.show.show_tools_component = show_tools_component
54
+ allow(component).to receive(:render).with(an_instance_of(show_tools_component)).and_return(expected_html)
55
+ end
56
+
57
+ it 'renders configured show_tools component' do
58
+ expect(rendered).to have_selector 'div[@class="expected-show_tools"]'
59
+ end
60
+ end
61
+ # rubocop:enable RSpec/SubjectStub
62
+ end
63
+ end
@@ -45,8 +45,18 @@ RSpec.describe Blacklight::FacetComponent, type: :component do
45
45
  Blacklight::Configuration::FacetField.new(key: 'field', partial: 'catalog/facet_partial').normalize!
46
46
  end
47
47
 
48
+ # Not sure why we need to re-implement rspec's stub_template, but
49
+ # we already were, and need a Rails 7.1+ safe alternate too
50
+ # https://github.com/rspec/rspec-rails/commit/4d65bea0619955acb15023b9c3f57a3a53183da8
51
+ # https://github.com/rspec/rspec-rails/issues/2696
48
52
  before do
49
- controller.view_context.view_paths.unshift(RSpec::Rails::ViewExampleGroup::StubResolverCache.resolver_for('catalog/_facet_partial.html.erb' => 'facet partial'))
53
+ replace_hash = { 'catalog/_facet_partial.html.erb' => 'facet partial' }
54
+
55
+ if ::Rails.version.to_f >= 7.1
56
+ controller.prepend_view_path(RSpec::Rails::ViewExampleGroup::StubResolverCache.resolver_for(replace_hash))
57
+ else
58
+ controller.view_context.view_paths.unshift(RSpec::Rails::ViewExampleGroup::StubResolverCache.resolver_for(replace_hash))
59
+ end
50
60
  end
51
61
 
52
62
  it 'renders the partial' do
@@ -35,13 +35,13 @@ RSpec.describe Blacklight::FacetItemPivotComponent, type: :component do
35
35
 
36
36
  it 'links to the facet and shows the number of hits' do
37
37
  expect(rendered).to have_selector 'li'
38
- expect(rendered).to have_link 'x', href: '/catalog?f%5Bz%5D=x'
38
+ expect(rendered).to have_link 'x', href: nokogiri_mediated_href(facet_item.href)
39
39
  expect(rendered).to have_selector '.facet-count', text: '10'
40
40
  end
41
41
 
42
42
  it 'has the facet hierarchy' do
43
43
  expect(rendered).to have_selector 'li ul.pivot-facet'
44
- expect(rendered).to have_link 'x:1', href: /f%5Bz%5D%5B%5D=x:1/
44
+ expect(rendered).to have_link 'x:1', href: nokogiri_mediated_href(facet_item.facet_item_presenters.first.href)
45
45
  end
46
46
 
47
47
  context 'with a selected facet' do
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Blacklight::Response::PaginationComponent, type: :component do
6
+ let(:render) do
7
+ with_request_url '/catalog?q=foo' do
8
+ render_inline(instance)
9
+ end
10
+ end
11
+
12
+ let(:instance) { described_class.new(response: response) }
13
+
14
+ context 'when there are many results' do
15
+ let(:response) { instance_double(Blacklight::Solr::Response, total: 10, current_page: 5, limit_value: 10_000, total_pages: 100) }
16
+
17
+ context 'with default config' do
18
+ before { render }
19
+
20
+ it "has links to deep pages" do
21
+ expect(page).not_to have_link '98'
22
+ expect(page).to have_link '99'
23
+ expect(page).to have_link '100'
24
+ expect(page).not_to have_link '101'
25
+ end
26
+ end
27
+
28
+ context 'when a different configuration that removes deep links is passed as a parameter' do
29
+ let(:instance) { described_class.new(response: response, left: 5, right: 0, outer_window: nil) }
30
+
31
+ before { render }
32
+
33
+ it "does not link to deep pages" do
34
+ expect(page).to have_link '1'
35
+ expect(page).not_to have_link '100'
36
+ end
37
+ end
38
+
39
+ context 'when a different configuration that removes deep links is configured in the controller' do
40
+ before do
41
+ allow(controller.blacklight_config.index)
42
+ .to receive(:pagination_options)
43
+ .and_return(theme: 'blacklight', left: 5, right: 0)
44
+ render
45
+ end
46
+
47
+ it "does not link to deep pages" do
48
+ expect(page).to have_link '1'
49
+ expect(page).not_to have_link '100'
50
+ end
51
+ end
52
+ end
53
+ end
@@ -10,7 +10,17 @@ RSpec.describe Blacklight::SearchContext::ServerAppliedParamsComponent, type: :c
10
10
  let(:view_context) { controller.view_context }
11
11
 
12
12
  before do
13
- view_context.view_paths.unshift(RSpec::Rails::ViewExampleGroup::StubResolverCache.resolver_for('application/_start_over.html.erb' => 'start over'))
13
+ # Not sure why we need to re-implement rspec's stub_template, but
14
+ # we already were, and need a Rails 7.1+ safe alternate too
15
+ # https://github.com/rspec/rspec-rails/commit/4d65bea0619955acb15023b9c3f57a3a53183da8
16
+ # https://github.com/rspec/rspec-rails/issues/2696
17
+ replace_hash = { 'application/_start_over.html.erb' => 'start over' }
18
+ if ::Rails.version.to_f >= 7.1
19
+ controller.prepend_view_path(RSpec::Rails::ViewExampleGroup::StubResolverCache.resolver_for(replace_hash))
20
+ else
21
+ view_context.view_paths.unshift(RSpec::Rails::ViewExampleGroup::StubResolverCache.resolver_for(replace_hash))
22
+ end
23
+
14
24
  allow(view_context).to receive(:current_search_session).and_return current_search_session
15
25
  allow(view_context).to receive(:link_back_to_catalog).with(any_args)
16
26
  end
@@ -5,9 +5,27 @@ require 'spec_helper'
5
5
  RSpec.describe "Blacklight Advanced Search Form" do
6
6
  describe "advanced search form" do
7
7
  before do
8
+ CatalogController.blacklight_config.search_fields['all_fields']['clause_params'] = {
9
+ edismax: {}
10
+ }
11
+ CatalogController.blacklight_config.search_fields['author']['clause_params'] = {
12
+ edismax: { qf: '${author_qf}' }
13
+ }
14
+ CatalogController.blacklight_config.search_fields['title']['clause_params'] = {
15
+ edismax: { qf: '${title_qf}' }
16
+ }
17
+ CatalogController.blacklight_config.search_fields['subject']['clause_params'] = {
18
+ edismax: { qf: '${subject_qf}' }
19
+ }
8
20
  visit '/catalog/advanced?hypothetical_existing_param=true&q=ignore+this+existing+query'
9
21
  end
10
22
 
23
+ after do
24
+ %w[all_fields author title subject].each do |field|
25
+ CatalogController.blacklight_config.search_fields[field].delete(:clause_params)
26
+ end
27
+ end
28
+
11
29
  it "has field and facet blocks" do
12
30
  expect(page).to have_selector('.query-criteria')
13
31
  expect(page).to have_selector('.limit-criteria')
@@ -45,6 +63,43 @@ RSpec.describe "Blacklight Advanced Search Form" do
45
63
  click_on 'advanced-search-submit'
46
64
  expect(page).to have_content 'Remove constraint Title: Medicine'
47
65
  expect(page).to have_content 'Strong Medicine speaks'
66
+ expect(page).to have_selector('article.document', count: 1)
67
+ end
68
+
69
+ it 'can limit to facets' do
70
+ fill_in 'Subject', with: 'Women'
71
+ click_on 'Language'
72
+ check 'Urdu 3'
73
+ click_on 'advanced-search-submit'
74
+ expect(page).to have_content 'Pākistānī ʻaurat dorāhe par'
75
+ expect(page).not_to have_content 'Ajikto kŭrŏk chŏrŏk sasimnikka : and 아직도 그럭 저럭 사십니까'
76
+ expect(page).to have_selector('article.document', count: 1)
77
+ end
78
+
79
+ it 'handles boolean queries' do
80
+ fill_in 'All Fields', with: 'history NOT strong'
81
+ click_on 'advanced-search-submit'
82
+ expect(page).to have_content('Ci an zhou bian')
83
+ expect(page).not_to have_content('Strong Medicine speaks')
84
+ expect(page).to have_selector('article.document', count: 10)
85
+ end
86
+
87
+ it 'handles queries in multiple fields with the ALL operator' do
88
+ fill_in 'All Fields', with: 'history'
89
+ fill_in 'Author', with: 'hearth'
90
+ click_on 'advanced-search-submit'
91
+ expect(page).to have_content('Strong Medicine speaks')
92
+ expect(page).to have_selector('article.document', count: 1)
93
+ end
94
+
95
+ it 'handles queries in multiple fields with the ANY operator' do
96
+ select 'any', from: 'op'
97
+ fill_in 'All Fields', with: 'history'
98
+ fill_in 'Subject', with: 'women'
99
+ click_on 'advanced-search-submit'
100
+ expect(page).to have_content('Ci an zhou bian')
101
+ expect(page).to have_content('Pākistānī ʻaurat dorāhe par')
102
+ expect(page).to have_selector('article.document', count: 10)
48
103
  end
49
104
  end
50
105
 
@@ -21,6 +21,11 @@ RSpec.describe 'Accessibility testing', api: false, js: true do
21
21
  expect(page).to be_accessible
22
22
  end
23
23
 
24
+ it 'validates the advanced search form' do
25
+ visit advanced_search_catalog_path
26
+ expect(page).to be_accessible
27
+ end
28
+
24
29
  it 'validates the single results page' do
25
30
  visit solr_document_path('2007020969')
26
31
  expect(page).to be_accessible
@@ -185,12 +185,17 @@ RSpec.describe BlacklightHelper do
185
185
  blacklight_config.view.gallery(template: '/my/partial')
186
186
  end
187
187
 
188
- def stub_template(hash)
189
- view.view_paths.unshift(ActionView::FixtureResolver.new(hash))
190
- end
191
-
192
188
  it 'renders that template' do
193
- stub_template 'my/_partial.html.erb' => 'some content'
189
+ # Not sure why we need to re-implement rspec's stub_template, but
190
+ # we already were, and need a Rails 7.1+ safe alternate too
191
+ # https://github.com/rspec/rspec-rails/commit/4d65bea0619955acb15023b9c3f57a3a53183da8
192
+ # https://github.com/rspec/rspec-rails/issues/2696
193
+ replace_hash = { 'my/_partial.html.erb' => 'some content' }
194
+ if ::Rails.version.to_f >= 7.1
195
+ controller.prepend_view_path(RSpec::Rails::ViewExampleGroup::StubResolverCache.resolver_for(replace_hash))
196
+ else
197
+ view.view_paths.unshift(ActionView::FixtureResolver.new(replace_hash))
198
+ end
194
199
 
195
200
  response = helper.render_document_index_with_view :gallery, [obj1, obj1]
196
201
 
@@ -93,7 +93,7 @@ RSpec.describe "Blacklight::Configurable", api: true do
93
93
  instance.blacklight_config.bar << "123"
94
94
  expect(instance.blacklight_config).not_to eq klass.blacklight_config
95
95
  expect(klass.blacklight_config.foo).to eq "bar"
96
- expect(instance.blacklight_config.foo).to eq "bar"
96
+ expect(instance.blacklight_config.foo).to eq "bar"
97
97
  expect(klass.blacklight_config.bar).not_to include("123")
98
98
  expect(instance.blacklight_config.bar).to include("asd", "123")
99
99
  end
@@ -153,6 +153,33 @@ RSpec.describe Blacklight::Solr::Repository, api: true do
153
153
  expect(JSON.parse(actual_params[:data]).with_indifferent_access).to include(query: { bool: {} })
154
154
  expect(actual_params[:headers]).to include({ 'Content-Type' => 'application/json' })
155
155
  end
156
+
157
+ context "without a json solr path configured" do
158
+ before do
159
+ blacklight_config.json_solr_path = nil
160
+ end
161
+
162
+ it "uses the default solr path" do
163
+ blacklight_config.solr_path = 'xyz'
164
+ allow(subject.connection).to receive(:send_and_receive) do |path|
165
+ expect(path).to eq 'xyz'
166
+ end
167
+ subject.search(input_params)
168
+ end
169
+ end
170
+
171
+ context "with a json solr path configured" do
172
+ before do
173
+ blacklight_config.json_solr_path = 'my-great-json'
174
+ end
175
+
176
+ it "uses the configured json_solr_path" do
177
+ allow(subject.connection).to receive(:send_and_receive) do |path|
178
+ expect(path).to eq 'my-great-json'
179
+ end
180
+ subject.search(input_params)
181
+ end
182
+ end
156
183
  end
157
184
  end
158
185
 
@@ -406,6 +406,14 @@ RSpec.describe Blacklight::Solr::SearchBuilderBehavior, api: true do
406
406
  it 'includes addtional clause parameters for the field' do
407
407
  expect(subject.dig(:json, :query, :bool, :must, 0, :edismax)).to include another: :parameter
408
408
  end
409
+
410
+ context 'with an empty search' do
411
+ let(:subject_search_params) { { commit: "search", search_field: "subject", action: "index", controller: "catalog", rows: "10", q: nil } }
412
+
413
+ it 'does not add nil query value clauses to json query' do
414
+ expect(subject).not_to have_key :json
415
+ end
416
+ end
409
417
  end
410
418
 
411
419
  describe "sorting" do