blacklight 9.0.0.beta8 → 9.1.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 (185) hide show
  1. checksums.yaml +4 -4
  2. data/.env +3 -3
  3. data/.github/matrix.json +18 -34
  4. data/.github/workflows/lint.yml +1 -1
  5. data/.github/workflows/test.yml +4 -3
  6. data/.rubocop.yml +72 -7
  7. data/.rubocop_todo.yml +142 -268
  8. data/Gemfile +1 -1
  9. data/README.md +4 -11
  10. data/VERSION +1 -1
  11. data/app/assets/builds/blacklight.css +37 -23
  12. data/app/assets/images/blacklight/logo-dark.svg +184 -0
  13. data/app/assets/images/blacklight/logo.svg +184 -0
  14. data/app/assets/javascripts/blacklight/blacklight.esm.js +134 -48
  15. data/app/assets/javascripts/blacklight/blacklight.esm.js.map +1 -1
  16. data/app/assets/javascripts/blacklight/blacklight.js +134 -48
  17. data/app/assets/javascripts/blacklight/blacklight.js.map +1 -1
  18. data/app/assets/stylesheets/blacklight/_balanced_list.scss +1 -1
  19. data/app/assets/stylesheets/blacklight/_bootstrap_overrides.scss +17 -0
  20. data/app/assets/stylesheets/blacklight/_constraints.scss +2 -2
  21. data/app/assets/stylesheets/blacklight/_facets.scss +14 -10
  22. data/app/assets/stylesheets/blacklight/_header.scss +0 -4
  23. data/app/assets/stylesheets/blacklight/blacklight_defaults.scss +5 -9
  24. data/app/components/blacklight/facets/filters_component.rb +1 -1
  25. data/app/components/blacklight/facets/inclusive_constraint_component.html.erb +1 -1
  26. data/app/components/blacklight/facets/suggest_component.html.erb +17 -12
  27. data/app/components/blacklight/icons/checkmark_component.rb +16 -0
  28. data/app/components/blacklight/icons/circle_half_component.rb +16 -0
  29. data/app/components/blacklight/icons/moon_stars_component.rb +17 -0
  30. data/app/components/blacklight/icons/sun_component.rb +16 -0
  31. data/app/components/blacklight/metadata_field_component.rb +1 -1
  32. data/app/components/blacklight/search_navbar_component.html.erb +1 -1
  33. data/app/components/blacklight/theme_switcher_component.html.erb +31 -0
  34. data/app/components/blacklight/theme_switcher_component.rb +6 -0
  35. data/app/components/blacklight/top_navbar_component.html.erb +1 -1
  36. data/app/controllers/concerns/blacklight/bookmarks.rb +8 -14
  37. data/app/controllers/concerns/blacklight/catalog.rb +12 -14
  38. data/app/controllers/concerns/blacklight/controller.rb +54 -11
  39. data/app/controllers/concerns/blacklight/guest_user.rb +51 -0
  40. data/app/controllers/concerns/blacklight/search_context.rb +1 -1
  41. data/app/controllers/concerns/blacklight/search_history.rb +3 -1
  42. data/app/controllers/concerns/blacklight/searchable.rb +18 -0
  43. data/app/helpers/blacklight/layout_helper_behavior.rb +7 -0
  44. data/app/javascript/blacklight-frontend/checkbox_submit.js +2 -2
  45. data/app/javascript/blacklight-frontend/color_theme_switcher.js +85 -0
  46. data/app/javascript/blacklight-frontend/facet_suggest.js +4 -1
  47. data/app/javascript/blacklight-frontend/index.js +2 -0
  48. data/app/models/concerns/blacklight/document/dublin_core.rb +2 -2
  49. data/app/models/concerns/blacklight/document.rb +1 -1
  50. data/app/models/concerns/blacklight/user.rb +36 -0
  51. data/app/presenters/blacklight/field_presenter.rb +1 -1
  52. data/app/presenters/blacklight/rendering/microdata.rb +1 -0
  53. data/app/services/blacklight/bookmarks_search_builder.rb +4 -4
  54. data/app/services/blacklight/search_service.rb +50 -35
  55. data/app/values/blacklight/types.rb +1 -1
  56. data/app/views/catalog/_email_form.html.erb +3 -3
  57. data/app/views/catalog/_home_text.html.erb +1 -1
  58. data/app/views/catalog/_sms_form.html.erb +4 -4
  59. data/app/views/catalog/index.rss.builder +4 -4
  60. data/app/views/catalog/opensearch.json.jbuilder +3 -0
  61. data/app/views/catalog/opensearch.xml.builder +2 -2
  62. data/app/views/layouts/blacklight/base.html.erb +13 -1
  63. data/app/views/shared/_user_util_links.html.erb +18 -5
  64. data/blacklight.gemspec +3 -3
  65. data/config/locales/blacklight.ar.yml +5 -0
  66. data/config/locales/blacklight.ca.yml +5 -0
  67. data/config/locales/blacklight.de.yml +6 -2
  68. data/config/locales/blacklight.en.yml +5 -0
  69. data/config/locales/blacklight.es.yml +5 -0
  70. data/config/locales/blacklight.fr.yml +5 -0
  71. data/config/locales/blacklight.hu.yml +5 -0
  72. data/config/locales/blacklight.it.yml +6 -2
  73. data/config/locales/blacklight.nl.yml +5 -0
  74. data/config/locales/blacklight.pt-BR.yml +6 -2
  75. data/config/locales/blacklight.sq.yml +5 -0
  76. data/config/locales/blacklight.zh.yml +5 -0
  77. data/lib/blacklight/component.rb +1 -1
  78. data/lib/blacklight/configuration/action_config_map_entry.rb +31 -0
  79. data/lib/blacklight/configuration/display_field.rb +1 -0
  80. data/lib/blacklight/configuration/field.rb +6 -6
  81. data/lib/blacklight/configuration/fields.rb +1 -1
  82. data/lib/blacklight/configuration/sort_field.rb +2 -2
  83. data/lib/blacklight/configuration.rb +30 -23
  84. data/lib/blacklight/parameters.rb +1 -1
  85. data/lib/blacklight/routes/searchable.rb +1 -1
  86. data/lib/blacklight/search_builder.rb +208 -10
  87. data/lib/blacklight/search_state.rb +4 -0
  88. data/lib/blacklight/solr/request.rb +8 -1
  89. data/lib/blacklight/solr/response/group_response.rb +1 -1
  90. data/lib/blacklight/solr/response/spelling.rb +8 -8
  91. data/lib/blacklight/solr/response.rb +2 -21
  92. data/lib/blacklight/solr/search_builder_behavior.rb +59 -16
  93. data/lib/blacklight.rb +2 -2
  94. data/lib/generators/blacklight/assets/importmap_generator.rb +2 -2
  95. data/lib/generators/blacklight/assets_generator.rb +1 -1
  96. data/lib/generators/blacklight/controller_generator.rb +1 -1
  97. data/lib/generators/blacklight/document_generator.rb +1 -1
  98. data/lib/generators/blacklight/install_generator.rb +6 -4
  99. data/lib/generators/blacklight/models_generator.rb +1 -1
  100. data/lib/generators/blacklight/search_builder_generator.rb +1 -1
  101. data/lib/generators/blacklight/solr_generator.rb +1 -1
  102. data/lib/generators/blacklight/templates/catalog_controller.rb +21 -2
  103. data/lib/generators/blacklight/templates/solr/conf/schema.xml +264 -178
  104. data/lib/generators/blacklight/templates/solr/conf/solrconfig.xml +6 -14
  105. data/lib/generators/blacklight/test_support_generator.rb +1 -1
  106. data/lib/generators/blacklight/user_generator.rb +66 -5
  107. data/lib/railties/blacklight.rake +4 -4
  108. data/package.json +2 -2
  109. data/spec/components/blacklight/document/action_component_spec.rb +1 -1
  110. data/spec/components/blacklight/document/group_component_spec.rb +1 -1
  111. data/spec/components/blacklight/document_component_spec.rb +9 -9
  112. data/spec/components/blacklight/facet_component_spec.rb +2 -2
  113. data/spec/components/blacklight/facets/filters_component_spec.rb +1 -1
  114. data/spec/components/blacklight/facets/index_navigation_component_spec.rb +1 -2
  115. data/spec/components/blacklight/facets/list_component_spec.rb +1 -1
  116. data/spec/components/blacklight/facets/suggest_component_spec.rb +2 -3
  117. data/spec/components/blacklight/metadata_field_component_spec.rb +26 -0
  118. data/spec/components/blacklight/skip_link_component_spec.rb +2 -2
  119. data/spec/controllers/application_controller_spec.rb +71 -0
  120. data/spec/controllers/bookmarks_controller_spec.rb +18 -5
  121. data/spec/controllers/catalog_controller_spec.rb +7 -5
  122. data/spec/features/advanced_search_spec.rb +29 -16
  123. data/spec/features/alternate_controller_spec.rb +4 -4
  124. data/spec/features/axe_spec.rb +34 -22
  125. data/spec/features/bookmarks_spec.rb +13 -13
  126. data/spec/features/dark_mode_spec.rb +42 -0
  127. data/spec/features/did_you_mean_spec.rb +27 -27
  128. data/spec/features/facet_missing_spec.rb +3 -3
  129. data/spec/features/facets_spec.rb +9 -0
  130. data/spec/features/record_view_spec.rb +14 -14
  131. data/spec/features/search_context_spec.rb +8 -8
  132. data/spec/features/search_filters_spec.rb +16 -16
  133. data/spec/features/search_formats_spec.rb +2 -2
  134. data/spec/features/search_history_spec.rb +11 -11
  135. data/spec/features/search_pagination_spec.rb +17 -17
  136. data/spec/features/search_results_spec.rb +3 -3
  137. data/spec/features/search_sort_spec.rb +4 -4
  138. data/spec/features/search_spec.rb +20 -20
  139. data/spec/helpers/blacklight/url_helper_behavior_spec.rb +15 -15
  140. data/spec/helpers/blacklight_helper_spec.rb +1 -1
  141. data/spec/helpers/catalog_helper_spec.rb +7 -7
  142. data/spec/integration/generators/blacklight/user_generator_spec.rb +60 -0
  143. data/spec/lib/blacklight/search_state/pivot_filter_field_spec.rb +1 -1
  144. data/spec/models/blacklight/configurable_spec.rb +1 -1
  145. data/spec/models/blacklight/configuration_spec.rb +0 -19
  146. data/spec/models/blacklight/document/dublin_core_spec.rb +2 -2
  147. data/spec/models/blacklight/facet_paginator_spec.rb +1 -1
  148. data/spec/models/blacklight/search_builder_spec.rb +26 -4
  149. data/spec/models/blacklight/solr/repository_spec.rb +10 -2
  150. data/spec/models/blacklight/solr/search_builder_behavior_spec.rb +165 -7
  151. data/spec/models/blacklight/user_spec.rb +32 -5
  152. data/spec/models/bookmark_spec.rb +1 -1
  153. data/spec/models/record_mailer_spec.rb +8 -8
  154. data/spec/models/search_spec.rb +1 -1
  155. data/spec/models/solr_document_spec.rb +4 -0
  156. data/spec/presenters/blacklight/index_presenter_spec.rb +1 -0
  157. data/spec/presenters/blacklight/rendering/pipeline_spec.rb +1 -0
  158. data/spec/presenters/blacklight/show_presenter_spec.rb +1 -0
  159. data/spec/presenters/blacklight/thumbnail_presenter_spec.rb +1 -0
  160. data/spec/services/blacklight/search_service_spec.rb +7 -7
  161. data/spec/spec_helper.rb +3 -12
  162. data/spec/support/features/session_helpers.rb +13 -4
  163. data/spec/support/features/turbo_helpers.rb +13 -0
  164. data/spec/support/features.rb +2 -0
  165. data/spec/support/user_helpers.rb +24 -0
  166. data/spec/test_app_templates/lib/generators/test_app_generator.rb +1 -1
  167. data/spec/views/catalog/_document.html.erb_spec.rb +4 -4
  168. data/spec/views/catalog/_paginate_compact.html.erb_spec.rb +2 -2
  169. data/spec/views/catalog/_sort_and_per_page.html.erb_spec.rb +5 -5
  170. data/spec/views/catalog/index.html.erb_spec.rb +2 -2
  171. data/spec/views/catalog/show.html.erb_spec.rb +3 -4
  172. data/spec/views/shared/_user_util_links.html.erb_spec.rb +56 -0
  173. data/template.demo.rb +2 -1
  174. metadata +30 -29
  175. data/.hound.yml +0 -4
  176. data/.jshintrc +0 -22
  177. data/app/assets/images/blacklight/logo.png +0 -0
  178. data/app/controllers/concerns/blacklight/facetable.rb +0 -34
  179. data/app/models/facet_search_builder.rb +0 -5
  180. data/app/services/blacklight/facet_search_service.rb +0 -44
  181. data/lib/blacklight/abstract_search_builder.rb +0 -154
  182. data/lib/blacklight/facet_search_builder.rb +0 -18
  183. data/lib/blacklight/solr/facet_search_builder_behavior.rb +0 -62
  184. data/spec/models/blacklight/facet_search_builder_spec.rb +0 -19
  185. data/spec/models/blacklight/solr/facet_search_builder_behavior_spec.rb +0 -929
@@ -125,7 +125,7 @@ RSpec.describe Blacklight::SearchBuilder, :api do
125
125
  end
126
126
 
127
127
  subject.with(a: 1)
128
- expect(subject.send(:processed_parameters)).to include step_1: 'builder'
128
+ expect(subject.processed_parameters).to include step_1: 'builder'
129
129
  end
130
130
  end
131
131
 
@@ -137,15 +137,23 @@ RSpec.describe Blacklight::SearchBuilder, :api do
137
137
 
138
138
  describe "#page" do
139
139
  it "is the current user parameter page number" do
140
- expect(subject.with(page: 2).send(:page)).to eq 2
140
+ expect(subject.with(page: 2).page).to eq 2
141
141
  end
142
142
 
143
143
  it "is page 1 if not page number given" do
144
- expect(subject.send(:page)).to eq 1
144
+ expect(subject.page).to eq 1
145
145
  end
146
146
 
147
147
  it "coerces parameters to integers" do
148
- expect(subject.with(page: '2b').send(:page)).to eq 2
148
+ expect(subject.with(page: '2b').page).to eq 2
149
+ end
150
+
151
+ it 'sets the page number' do
152
+ expect(subject.page(3).page).to eq 3
153
+ end
154
+
155
+ it 'gracefully handles setting the page number with a nil value' do
156
+ expect(subject.page(nil).page).to eq 1
149
157
  end
150
158
  end
151
159
 
@@ -160,6 +168,10 @@ RSpec.describe Blacklight::SearchBuilder, :api do
160
168
  expect(subject.rows(17).rows).to eq 17
161
169
  end
162
170
 
171
+ it "gracefully hnadles setting the number of rows to a nil value" do
172
+ expect(subject.rows(nil).rows).to eq 10
173
+ end
174
+
163
175
  it "is the per_page parameter" do
164
176
  expect(subject.with(per_page: 5).rows).to eq 5
165
177
  end
@@ -225,6 +237,16 @@ RSpec.describe Blacklight::SearchBuilder, :api do
225
237
  end
226
238
  end
227
239
 
240
+ describe "#facet_suggestion_query" do
241
+ it "is nil if no value is set" do
242
+ expect(subject.facet_suggestion_query).to be_nil
243
+ end
244
+
245
+ it "sets facet_suggestion_query value" do
246
+ expect(subject.facet_suggestion_query('antel').facet_suggestion_query).to eq 'antel'
247
+ end
248
+ end
249
+
228
250
  describe "#search_field" do
229
251
  it "uses the requested search field" do
230
252
  blacklight_config.add_search_field 'x'
@@ -160,7 +160,11 @@ RSpec.describe Blacklight::Solr::Repository, :api do
160
160
  end
161
161
 
162
162
  describe "setting to post" do
163
- let (:blacklight_config) { config = Blacklight::Configuration.new; config.http_method = :post; config }
163
+ let(:blacklight_config) do
164
+ config = Blacklight::Configuration.new
165
+ config.http_method = :post
166
+ config
167
+ end
164
168
 
165
169
  it "keep value set to post" do
166
170
  expect(blacklight_config.http_method).to eq :post
@@ -210,7 +214,11 @@ RSpec.describe Blacklight::Solr::Repository, :api do
210
214
  end
211
215
 
212
216
  describe "http_method configuration", :integration do
213
- let (:blacklight_config) { config = Blacklight::Configuration.new; config.http_method = :post; config }
217
+ let(:blacklight_config) do
218
+ config = Blacklight::Configuration.new
219
+ config.http_method = :post
220
+ config
221
+ end
214
222
 
215
223
  it "sends a post request to solr and get a response back" do
216
224
  response = subject.search(params: { q: all_docs_query })
@@ -43,7 +43,7 @@ RSpec.describe Blacklight::Solr::SearchBuilderBehavior, :api do
43
43
  end
44
44
 
45
45
  context "with a complex parameter environment" do
46
- subject { search_builder.with(user_params).send(:processed_parameters) }
46
+ subject { search_builder.with(user_params).processed_parameters }
47
47
 
48
48
  let(:user_params) do
49
49
  { :search_field => "test_field", :q => "test query", "facet.field" => "extra_facet" }
@@ -90,7 +90,7 @@ RSpec.describe Blacklight::Solr::SearchBuilderBehavior, :api do
90
90
  # SPECS for actual search parameter generation
91
91
  describe "#processed_parameters" do
92
92
  subject do
93
- search_builder.with(user_params).send(:processed_parameters)
93
+ search_builder.with(user_params).processed_parameters
94
94
  end
95
95
 
96
96
  context "when search_params_logic is customized" do
@@ -524,13 +524,11 @@ RSpec.describe Blacklight::Solr::SearchBuilderBehavior, :api do
524
524
  end
525
525
 
526
526
  describe "#add_facets_for_advanced_search_form" do
527
+ let(:user_params) do
528
+ Blacklight::SearchState.new({}, blacklight_config, mock_controller)
529
+ end
527
530
  let(:solr_parameters) { Blacklight::Solr::Request.new }
528
531
  let(:mock_controller) { instance_double(CatalogController, action_name: 'advanced_search') }
529
- let(:mock_search_state) { instance_double(Blacklight::SearchState, controller: mock_controller) }
530
-
531
- before do
532
- allow(search_builder).to receive(:search_state).and_return(mock_search_state)
533
- end
534
532
 
535
533
  context "when action is advanced_search and form_solr_parameters are configured" do
536
534
  before do
@@ -718,6 +716,166 @@ RSpec.describe Blacklight::Solr::SearchBuilderBehavior, :api do
718
716
  end
719
717
  end
720
718
 
719
+ describe "#add_facet_paging_to_solr" do
720
+ let(:facet_field) { 'format' }
721
+ let(:sort_key) { Blacklight::Solr::FacetPaginator.request_keys[:sort] }
722
+ let(:page_key) { Blacklight::Solr::FacetPaginator.request_keys[:page] }
723
+ let(:prefix_key) { Blacklight::Solr::FacetPaginator.request_keys[:prefix] }
724
+
725
+ let(:blacklight_config) do
726
+ Blacklight::Configuration.new do |config|
727
+ config.add_facet_fields_to_solr_request!
728
+ config.add_facet_field 'format'
729
+ config.add_facet_field 'format_ordered', sort: :count
730
+ config.add_facet_field 'format_limited', limit: 5
731
+ config.add_facet_field 'format_more_limited', limit: 5, more_limit: 50
732
+ end
733
+ end
734
+
735
+ let(:solr_parameters) do
736
+ solr_parameters = Blacklight::Solr::Request.new
737
+ subject.facet(facet_field).add_facet_paging_to_solr(solr_parameters)
738
+ solr_parameters
739
+ end
740
+
741
+ it 'sets rows to 0' do
742
+ expect(solr_parameters[:rows]).to eq 0
743
+ end
744
+
745
+ it 'sets facets requested to facet_field argument' do
746
+ expect(solr_parameters[:'facet.field']).to eq facet_field
747
+ end
748
+
749
+ it 'defaults offset to 0' do
750
+ expect(solr_parameters[:"f.#{facet_field}.facet.offset"]).to eq 0
751
+ end
752
+
753
+ context 'when offset is manually set' do
754
+ let(:user_params) { { page_key => 2 } }
755
+
756
+ it 'uses offset manually set, and converts it to an integer' do
757
+ expect(solr_parameters[:"f.#{facet_field}.facet.offset"]).to eq 20
758
+ end
759
+ end
760
+
761
+ context 'for a field with a configured more_limit' do
762
+ let(:facet_field) { 'format_more_limited' }
763
+
764
+ it 'uses the more_limit configuration' do
765
+ expect(solr_parameters[:"f.#{facet_field}.facet.limit"]).to eq 51
766
+ end
767
+ end
768
+
769
+ context 'for a field with a param key different from the field name' do
770
+ let(:user_params) { { page_key => 2, 'facet.sort': 'index', 'facet.prefix': 'x' } }
771
+ let(:facet_field) { 'param_key' }
772
+
773
+ let(:blacklight_config) do
774
+ Blacklight::Configuration.new.tap do |config|
775
+ config.add_facet_field key: 'param_key', field: 'solr_field', more_limit: 50, ex: 'other'
776
+
777
+ config.add_facet_fields_to_solr_request!
778
+ end
779
+ end
780
+
781
+ it "uses the configured solr field name in queries" do
782
+ expect(solr_parameters).to include 'f.solr_field.facet.limit': 51,
783
+ 'f.solr_field.facet.offset': 50,
784
+ 'f.solr_field.facet.sort': 'index',
785
+ 'f.solr_field.facet.prefix': 'x',
786
+ 'facet.field': '{!ex=other}solr_field'
787
+ end
788
+ end
789
+
790
+ it 'defaults limit to 20' do
791
+ expect(solr_parameters[:"f.#{facet_field}.facet.limit"]).to eq 21
792
+ end
793
+
794
+ it 'uses the default sort' do
795
+ expect(solr_parameters[:"f.#{facet_field}.facet.sort"]).to be_blank
796
+ end
797
+
798
+ context 'when sort is provided' do
799
+ let(:user_params) { { sort_key => 'index' } }
800
+
801
+ it 'uses sort provided in the parameters' do
802
+ expect(solr_parameters[:"f.#{facet_field}.facet.sort"]).to eq 'index'
803
+ end
804
+ end
805
+
806
+ context 'when a prefix is provided' do
807
+ let(:user_params) { { prefix_key => 'A' } }
808
+
809
+ it 'includes the prefix in the query' do
810
+ expect(solr_parameters[:"f.#{facet_field}.facet.prefix"]).to eq 'A'
811
+ end
812
+ end
813
+ end
814
+
815
+ describe "#add_facet_suggestion_parameters" do
816
+ it "does not add anything when the builder has no facet_suggestion_query and no facet" do
817
+ expect(subject.facet).to be_nil
818
+ expect(subject.facet_suggestion_query).to be_nil
819
+ solr_params = Blacklight::Solr::Request.new
820
+
821
+ expect do
822
+ subject.add_facet_suggestion_parameters(solr_params)
823
+ end.not_to(change { solr_params })
824
+ end
825
+
826
+ it "does not add anything when the builder has a facet_suggestion_query but no facet" do
827
+ subject.facet_suggestion_query = 'artic'
828
+ expect(subject.facet_suggestion_query).to eq 'artic'
829
+ expect(subject.facet).to be_nil
830
+ solr_params = Blacklight::Solr::Request.new
831
+
832
+ expect do
833
+ subject.add_facet_suggestion_parameters(solr_params)
834
+ end.not_to(change { solr_params })
835
+ end
836
+
837
+ it "does not add anything when the builder has a facet but no facet_suggestion_query" do
838
+ subject.facet = 'subject_facet'
839
+ expect(subject.facet_suggestion_query).to be_nil
840
+ expect(subject.facet).to eq 'subject_facet'
841
+ solr_params = Blacklight::Solr::Request.new
842
+
843
+ expect do
844
+ subject.add_facet_suggestion_parameters(solr_params)
845
+ end.not_to(change { solr_params })
846
+ end
847
+
848
+ it "adds the facet_suggestion_query to facet.contains" do
849
+ subject.facet = 'subject_facet'
850
+ subject.facet_suggestion_query = 'artic'
851
+ solr_params = Blacklight::Solr::Request.new
852
+
853
+ subject.add_facet_suggestion_parameters(solr_params)
854
+
855
+ expect(solr_params[:'facet.contains']).to eq 'artic'
856
+ end
857
+
858
+ it "adds the first part of facet_suggestion_query to facet.contains if it is extremely long" do
859
+ subject.facet = 'subject_facet'
860
+ subject.facet_suggestion_query = 'Call me Ishmael. Some years ago—never mind how long precisely'
861
+ solr_params = Blacklight::Solr::Request.new
862
+
863
+ subject.add_facet_suggestion_parameters(solr_params)
864
+
865
+ expect(solr_params[:'facet.contains']).to eq 'Call me Ishmael. Some years ago—never mind how long'
866
+ end
867
+
868
+ it "adds facet.contains.ignoreCase" do
869
+ subject.facet = 'subject_facet'
870
+ subject.facet_suggestion_query = 'artic'
871
+ solr_params = Blacklight::Solr::Request.new
872
+
873
+ subject.add_facet_suggestion_parameters(solr_params)
874
+
875
+ expect(solr_params[:'facet.contains.ignoreCase']).to be true
876
+ end
877
+ end
878
+
721
879
  describe "#with_tag_ex" do
722
880
  it "adds an !ex local parameter if the facet configuration requests it" do
723
881
  expect(subject.with_ex_local_param("xyz", "some-value")).to eq "{!ex=xyz}some-value"
@@ -1,12 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RSpec.describe "Blacklight::User", :api do
4
- subject { User.create! email: 'xyz@example.com', password: 'xyz12345' }
4
+ subject { create_user email: 'xyz@example.com', password: 'xyz12345' }
5
5
 
6
6
  def mock_bookmark document_id
7
7
  Bookmark.new document_id: document_id, document_type: SolrDocument.to_s
8
8
  end
9
9
 
10
+ describe ".blacklight_email_column" do
11
+ it "returns the email column the model actually has" do
12
+ expected = User.column_names.include?("email_address") ? :email_address : :email
13
+ expect(User.blacklight_email_column).to eq expected
14
+ end
15
+ end
16
+
17
+ describe ".create_guest_user!" do
18
+ it "creates a persisted user with a generated, unique identifier" do
19
+ guest = User.create_guest_user!
20
+
21
+ expect(guest).to be_persisted
22
+ expect(guest.public_send(User.blacklight_email_column)).to match(/^guest_.*@example\.com$/)
23
+ end
24
+
25
+ it "generates a distinct identifier each time" do
26
+ one = User.create_guest_user!
27
+ two = User.create_guest_user!
28
+
29
+ expect(one.public_send(User.blacklight_email_column))
30
+ .not_to eq(two.public_send(User.blacklight_email_column))
31
+ end
32
+ end
33
+
10
34
  describe "#bookmarks_for_documents" do
11
35
  before do
12
36
  subject.bookmarks << mock_bookmark(1)
@@ -47,12 +71,15 @@ RSpec.describe "Blacklight::User", :api do
47
71
  end
48
72
 
49
73
  describe '#to_s' do
50
- it 'is the email by default' do
51
- expect(subject.to_s).to eq subject.email
74
+ it 'is the configured display key by default' do
75
+ display_key = User.column_names.include?("email_address") ? :email_address : :email
76
+
77
+ expect(subject.to_s).to eq subject.public_send(display_key)
52
78
  end
53
79
 
54
- context 'when no email method is provided' do
55
- let(:old_method) { subject.class.instance_method(:email) }
80
+ context 'when no configured display key method is provided' do
81
+ let(:display_key) { User.column_names.include?("email_address") ? :email_address : :email }
82
+ let(:old_method) { subject.class.instance_method(display_key) }
56
83
 
57
84
  before do
58
85
  subject.class.send(:undef_method, old_method.name)
@@ -7,7 +7,7 @@ RSpec.describe Bookmark do
7
7
  b
8
8
  end
9
9
 
10
- let(:user) { User.create! email: 'xyz@example.com', password: 'xyz12345' }
10
+ let(:user) { create_user email: 'xyz@example.com', password: 'xyz12345' }
11
11
 
12
12
  it "is valid" do
13
13
  expect(subject).to be_valid
@@ -37,11 +37,11 @@ RSpec.describe RecordMailer do
37
37
  end
38
38
 
39
39
  it "starts the subject w/ Item Record:" do
40
- expect(email.subject).to match /^Item Record:/
40
+ expect(email.subject).to match(/^Item Record:/)
41
41
  end
42
42
 
43
43
  it "puts the title of the item in the subject" do
44
- expect(email.subject).to match /The horn/
44
+ expect(email.subject).to include('The horn')
45
45
  end
46
46
 
47
47
  it "has the correct from address (w/o the port number)" do
@@ -49,9 +49,9 @@ RSpec.describe RecordMailer do
49
49
  end
50
50
 
51
51
  it "prints out the correct body" do
52
- expect(email.body).to match /Title: The horn/
53
- expect(email.body).to match /Author: Janetzky, Kurt/
54
- expect(email.body).to match /projectblacklight.org/
52
+ expect(email.body).to include('Title: The horn')
53
+ expect(email.body).to include('Author: Janetzky, Kurt')
54
+ expect(email.body).to match(/projectblacklight.org/)
55
55
  end
56
56
 
57
57
  it "uses https URLs when protocol is set" do
@@ -104,9 +104,9 @@ RSpec.describe RecordMailer do
104
104
  end
105
105
 
106
106
  it "prints out the correct body" do
107
- expect(@sms.body).to match /Title: The horn/
108
- expect(@sms.body).to match /Author: Janetzky, Kurt/
109
- expect(@sms.body).to match /projectblacklight.org:3000/
107
+ expect(@sms.body).to include('Title: The horn')
108
+ expect(@sms.body).to include('Author: Janetzky, Kurt')
109
+ expect(@sms.body).to match(/projectblacklight.org:3000/)
110
110
  end
111
111
 
112
112
  it "uses https URL when protocol is set" do
@@ -2,7 +2,7 @@
2
2
 
3
3
  RSpec.describe Search do
4
4
  let(:search) { described_class.new(user: user) }
5
- let(:user) { User.create! email: 'xyz@example.com', password: 'xyz12345' }
5
+ let(:user) { create_user email: 'xyz@example.com', password: 'xyz12345' }
6
6
  let(:hash_params) { { q: "query", f: { facet: "filter" } } }
7
7
  let(:query_params) { hash_params }
8
8
 
@@ -54,6 +54,7 @@ RSpec.describe SolrDocument, :api do
54
54
  attribute :first_author, :select, 'author_tesim', by: :min
55
55
  attribute :date, :date, field: 'date_dtsi'
56
56
  attribute :time, :time, field: 'date_dtsi'
57
+ attribute :details, :json, 'details_ssi'
57
58
  attribute :whatever, :string, default: ->(*) { 'default_value' }
58
59
  end
59
60
  end
@@ -61,6 +62,7 @@ RSpec.describe SolrDocument, :api do
61
62
  doc_class.new(id: '123',
62
63
  title_tesim: ['Good Omens'],
63
64
  author_tesim: ['Neil Gaiman', 'Terry Pratchett'],
65
+ details_ssi: '{"pages":288}',
64
66
  date_dtsi: '1990-01-01T17:23:13Z')
65
67
  end
66
68
 
@@ -72,6 +74,8 @@ RSpec.describe SolrDocument, :api do
72
74
  expect(document.date.to_s).to eq '1990-01-01'
73
75
  expect(document.time).to be_a Time
74
76
  expect(document.time.to_s).to eq '1990-01-01 17:23:13 UTC'
77
+ expect(document.details).to be_a Hash
78
+ expect(document.details).to eq({ 'pages' => 288 })
75
79
  expect(document.whatever).to eq 'default_value'
76
80
  end
77
81
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  RSpec.describe Blacklight::IndexPresenter, :api do
4
4
  include Capybara::RSpecMatchers
5
+
5
6
  subject { presenter }
6
7
 
7
8
  let(:request_context) { double(document_index_view_type: 'list') }
@@ -2,6 +2,7 @@
2
2
 
3
3
  RSpec.describe Blacklight::Rendering::Pipeline do
4
4
  include Capybara::RSpecMatchers
5
+
5
6
  let(:document) { instance_double(SolrDocument) }
6
7
  let(:context) { double }
7
8
  let(:options) { {} }
@@ -2,6 +2,7 @@
2
2
 
3
3
  RSpec.describe Blacklight::ShowPresenter, :api do
4
4
  include Capybara::RSpecMatchers
5
+
5
6
  subject { presenter }
6
7
 
7
8
  let(:request_context) { double }
@@ -2,6 +2,7 @@
2
2
 
3
3
  RSpec.describe Blacklight::ThumbnailPresenter do
4
4
  include Capybara::RSpecMatchers
5
+
5
6
  let(:view_context) { double "View context" }
6
7
  let(:config) { Blacklight::Configuration.new.view_config(:index) }
7
8
  let(:presenter) { described_class.new(document, view_context, config) }
@@ -145,7 +145,7 @@ RSpec.describe Blacklight::SearchService, :api do
145
145
  end
146
146
 
147
147
  it 'has all facets specified in initializer' do
148
- expect(@facets.keys).to include *blacklight_config.facet_fields.keys
148
+ expect(@facets.keys).to include(*blacklight_config.facet_fields.keys)
149
149
  expect(@facets.none? { |_k, v| v.nil? }).to be true
150
150
  end
151
151
 
@@ -379,40 +379,40 @@ RSpec.describe Blacklight::SearchService, :api do
379
379
  end
380
380
 
381
381
  it "returns the previous and next documents for a search" do
382
- _response, docs = service.previous_and_next_documents_for_search(4, q: '')
382
+ _response, docs = service.previous_and_next_documents_for_search(4, { q: '' })
383
383
 
384
384
  expect(docs.first.id).to eq @full_response.documents[3].id
385
385
  expect(docs.last.id).to eq @full_response.documents[5].id
386
386
  end
387
387
 
388
388
  it "returns only the next document if the counter is 0" do
389
- _response, docs = service.previous_and_next_documents_for_search(0, q: '')
389
+ _response, docs = service.previous_and_next_documents_for_search(0, { q: '' })
390
390
 
391
391
  expect(docs.first).to be_nil
392
392
  expect(docs.last.id).to eq @full_response.documents[1].id
393
393
  end
394
394
 
395
395
  it "returns only the previous document if the counter is the total number of documents" do
396
- _response, docs = service.previous_and_next_documents_for_search(@full_response.total - 1, q: '')
396
+ _response, docs = service.previous_and_next_documents_for_search(@full_response.total - 1, { q: '' })
397
397
  expect(docs.first.id).to eq @full_response.documents.slice(-2).id
398
398
  expect(docs.last).to be_nil
399
399
  end
400
400
 
401
401
  it "returns an array of nil values if there is only one result" do
402
- _response, docs = service.previous_and_next_documents_for_search(0, q: 'id:2007020969')
402
+ _response, docs = service.previous_and_next_documents_for_search(0, { q: 'id:2007020969' })
403
403
  expect(docs.last).to be_nil
404
404
  expect(docs.first).to be_nil
405
405
  end
406
406
 
407
407
  it 'returns only the unique key by default' do
408
- _response, docs = service.previous_and_next_documents_for_search(0, q: '')
408
+ _response, docs = service.previous_and_next_documents_for_search(0, { q: '' })
409
409
  expect(docs.last.to_h).to eq 'id' => @full_response.documents[1].id
410
410
  end
411
411
 
412
412
  it 'allows the query parameters to be customized using configuration' do
413
413
  blacklight_config.document_pagination_params[:fl] = 'id,format'
414
414
 
415
- _response, docs = service.previous_and_next_documents_for_search(0, q: '')
415
+ _response, docs = service.previous_and_next_documents_for_search(0, { q: '' })
416
416
 
417
417
  expect(docs.last.to_h).to eq @full_response.documents[1].to_h.slice('id', 'format')
418
418
  end
data/spec/spec_helper.rb CHANGED
@@ -48,25 +48,16 @@ RSpec.configure do |config|
48
48
  # When we're testing the API, only run the api tests
49
49
  config.filter_run api: true if ENV['BLACKLIGHT_API_TEST'].present?
50
50
 
51
- if Rails.version.to_f >= 7.1
52
- config.fixture_paths = [Rails.root.join("spec/fixtures")]
53
- else
54
- config.fixture_path = Rails.root.join("spec/fixtures")
55
- end
51
+ config.fixture_paths = [Rails.root.join("spec/fixtures")]
56
52
 
57
53
  # If you're not using ActiveRecord, or you'd prefer not to run each of your
58
54
  # examples within a transaction, remove the following line or assign false
59
55
  # instead of true.
60
56
  config.use_transactional_fixtures = true
61
57
 
62
- if defined? Devise::Test::ControllerHelpers
63
- config.include Devise::Test::ControllerHelpers, type: :controller
64
- else
65
- config.include Devise::TestHelpers, type: :controller
66
- config.include Devise::TestHelpers, type: :i18n
67
- end
68
-
69
58
  config.infer_spec_type_from_file_location!
59
+ config.include UserHelpers
60
+ config.include Devise::Test::ControllerHelpers, type: :controller if defined?(Devise)
70
61
  config.include PresenterTestHelpers, type: :presenter
71
62
  config.include ViewComponent::TestHelpers, type: :component
72
63
  config.include ViewComponentTestHelpers, type: :component
@@ -4,6 +4,8 @@
4
4
  module Features
5
5
  module SessionHelpers
6
6
  def sign_up_with(email, password)
7
+ return create_user(email: email, password: password) if respond_to?(:new_session_path)
8
+
7
9
  Capybara.exact = true
8
10
  visit new_user_registration_path
9
11
  fill_in 'Email', with: email
@@ -14,10 +16,17 @@ module Features
14
16
 
15
17
  def sign_in(login = 'user1')
16
18
  email = "#{login}@#{login}.com"
17
- User.create(email: email, password: "password", password_confirmation: "password")
18
- visit new_user_session_path
19
- fill_in("user_email", with: email)
20
- fill_in("user_password", with: "password")
19
+ create_user(email: email, password: "password")
20
+
21
+ if respond_to?(:new_session_path)
22
+ visit new_session_path
23
+ fill_in("email_address", with: email)
24
+ fill_in("password", with: "password")
25
+ else
26
+ visit new_user_session_path
27
+ fill_in("user_email", with: email)
28
+ fill_in("user_password", with: "password")
29
+ end
21
30
 
22
31
  if has_button? "Sign in"
23
32
  click_on("Sign in")
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # spec/support/features/turbo_helpers.rb
4
+ module Features
5
+ module TurboHelpers
6
+ # Turbo Drive marks the document as busy for the duration of a visit. Capybara
7
+ # clicks return before the visit completes, so wait for the busy state to clear
8
+ # before making assertions that don't wait on their own (e.g. be_axe_clean).
9
+ def wait_for_turbo
10
+ expect(page).to have_no_css('html[aria-busy]', visible: :all)
11
+ end
12
+ end
13
+ end
@@ -4,8 +4,10 @@
4
4
 
5
5
  require File.expand_path('features/search_helpers.rb', __dir__)
6
6
  require File.expand_path('features/session_helpers.rb', __dir__)
7
+ require File.expand_path('features/turbo_helpers.rb', __dir__)
7
8
 
8
9
  RSpec.configure do |config|
9
10
  config.include Features::SearchHelpers, type: :feature
10
11
  config.include Features::SessionHelpers, type: :feature
12
+ config.include Features::TurboHelpers, type: :feature
11
13
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UserHelpers
4
+ def build_user(attributes = {})
5
+ User.new(user_attributes(attributes))
6
+ end
7
+
8
+ def create_user(attributes = {})
9
+ User.create!(user_attributes(attributes))
10
+ end
11
+
12
+ def user_attributes(attributes = {})
13
+ attributes = attributes.dup
14
+ password = attributes.delete(:password) || "password"
15
+ email = attributes.delete(:email) || attributes.delete(:email_address) || "user@example.com"
16
+ email_attribute = User.column_names.include?("email_address") ? :email_address : :email
17
+
18
+ {
19
+ email_attribute => email,
20
+ password: password,
21
+ password_confirmation: attributes.delete(:password_confirmation) || password
22
+ }.merge(attributes)
23
+ end
24
+ end
@@ -15,7 +15,7 @@ class TestAppGenerator < Rails::Generators::Base
15
15
  Bundler.with_unbundled_env do
16
16
  run "bundle install"
17
17
  end
18
- options = '--devise'
18
+ options = Rails.gem_version >= Gem::Version.new('8.0.0') ? '--authentication' : '--devise'
19
19
  if ENV['BLACKLIGHT_API_TEST'].present?
20
20
  options += ' --skip-assets'
21
21
  end