blacklight 9.0.0 → 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 (170) hide show
  1. checksums.yaml +4 -4
  2. data/.env +3 -3
  3. data/.github/matrix.json +12 -18
  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 +2 -9
  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 +128 -45
  15. data/app/assets/javascripts/blacklight/blacklight.esm.js.map +1 -1
  16. data/app/assets/javascripts/blacklight/blacklight.js +128 -45
  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/inclusive_constraint_component.html.erb +1 -1
  25. data/app/components/blacklight/icons/checkmark_component.rb +16 -0
  26. data/app/components/blacklight/icons/circle_half_component.rb +16 -0
  27. data/app/components/blacklight/icons/moon_stars_component.rb +17 -0
  28. data/app/components/blacklight/icons/sun_component.rb +16 -0
  29. data/app/components/blacklight/metadata_field_component.rb +1 -1
  30. data/app/components/blacklight/search_navbar_component.html.erb +1 -1
  31. data/app/components/blacklight/theme_switcher_component.html.erb +31 -0
  32. data/app/components/blacklight/theme_switcher_component.rb +6 -0
  33. data/app/components/blacklight/top_navbar_component.html.erb +1 -1
  34. data/app/controllers/concerns/blacklight/bookmarks.rb +8 -14
  35. data/app/controllers/concerns/blacklight/catalog.rb +9 -10
  36. data/app/controllers/concerns/blacklight/controller.rb +54 -11
  37. data/app/controllers/concerns/blacklight/guest_user.rb +51 -0
  38. data/app/controllers/concerns/blacklight/search_context.rb +1 -1
  39. data/app/controllers/concerns/blacklight/search_history.rb +3 -1
  40. data/app/controllers/concerns/blacklight/searchable.rb +18 -0
  41. data/app/helpers/blacklight/layout_helper_behavior.rb +7 -0
  42. data/app/javascript/blacklight-frontend/color_theme_switcher.js +85 -0
  43. data/app/javascript/blacklight-frontend/index.js +2 -0
  44. data/app/models/concerns/blacklight/document/dublin_core.rb +2 -2
  45. data/app/models/concerns/blacklight/document.rb +1 -1
  46. data/app/models/concerns/blacklight/user.rb +36 -0
  47. data/app/presenters/blacklight/field_presenter.rb +1 -1
  48. data/app/presenters/blacklight/rendering/microdata.rb +1 -0
  49. data/app/services/blacklight/bookmarks_search_builder.rb +4 -4
  50. data/app/services/blacklight/search_service.rb +46 -33
  51. data/app/values/blacklight/types.rb +1 -1
  52. data/app/views/catalog/_email_form.html.erb +3 -3
  53. data/app/views/catalog/_home_text.html.erb +1 -1
  54. data/app/views/catalog/_sms_form.html.erb +4 -4
  55. data/app/views/catalog/index.rss.builder +4 -4
  56. data/app/views/catalog/opensearch.json.jbuilder +3 -0
  57. data/app/views/catalog/opensearch.xml.builder +2 -2
  58. data/app/views/layouts/blacklight/base.html.erb +13 -1
  59. data/app/views/shared/_user_util_links.html.erb +18 -5
  60. data/blacklight.gemspec +3 -3
  61. data/config/locales/blacklight.ar.yml +5 -0
  62. data/config/locales/blacklight.ca.yml +5 -0
  63. data/config/locales/blacklight.de.yml +6 -2
  64. data/config/locales/blacklight.en.yml +5 -0
  65. data/config/locales/blacklight.es.yml +5 -0
  66. data/config/locales/blacklight.fr.yml +5 -0
  67. data/config/locales/blacklight.hu.yml +5 -0
  68. data/config/locales/blacklight.it.yml +6 -2
  69. data/config/locales/blacklight.nl.yml +5 -0
  70. data/config/locales/blacklight.pt-BR.yml +6 -2
  71. data/config/locales/blacklight.sq.yml +5 -0
  72. data/config/locales/blacklight.zh.yml +5 -0
  73. data/lib/blacklight/component.rb +1 -1
  74. data/lib/blacklight/configuration/action_config_map_entry.rb +31 -0
  75. data/lib/blacklight/configuration/display_field.rb +1 -0
  76. data/lib/blacklight/configuration/field.rb +6 -6
  77. data/lib/blacklight/configuration/fields.rb +1 -1
  78. data/lib/blacklight/configuration/sort_field.rb +2 -2
  79. data/lib/blacklight/configuration.rb +30 -19
  80. data/lib/blacklight/parameters.rb +1 -1
  81. data/lib/blacklight/search_builder.rb +58 -19
  82. data/lib/blacklight/search_state.rb +4 -0
  83. data/lib/blacklight/solr/request.rb +8 -1
  84. data/lib/blacklight/solr/response/group_response.rb +1 -1
  85. data/lib/blacklight/solr/response/spelling.rb +8 -8
  86. data/lib/blacklight/solr/response.rb +2 -21
  87. data/lib/blacklight/solr/search_builder_behavior.rb +3 -9
  88. data/lib/blacklight.rb +2 -2
  89. data/lib/generators/blacklight/assets/importmap_generator.rb +2 -2
  90. data/lib/generators/blacklight/assets_generator.rb +1 -1
  91. data/lib/generators/blacklight/controller_generator.rb +1 -1
  92. data/lib/generators/blacklight/document_generator.rb +1 -1
  93. data/lib/generators/blacklight/install_generator.rb +6 -4
  94. data/lib/generators/blacklight/models_generator.rb +1 -1
  95. data/lib/generators/blacklight/search_builder_generator.rb +1 -1
  96. data/lib/generators/blacklight/solr_generator.rb +1 -1
  97. data/lib/generators/blacklight/templates/catalog_controller.rb +20 -1
  98. data/lib/generators/blacklight/templates/solr/conf/schema.xml +264 -178
  99. data/lib/generators/blacklight/templates/solr/conf/solrconfig.xml +6 -14
  100. data/lib/generators/blacklight/test_support_generator.rb +1 -1
  101. data/lib/generators/blacklight/user_generator.rb +66 -5
  102. data/lib/railties/blacklight.rake +4 -4
  103. data/package.json +2 -2
  104. data/spec/components/blacklight/document/action_component_spec.rb +1 -1
  105. data/spec/components/blacklight/document/group_component_spec.rb +1 -1
  106. data/spec/components/blacklight/document_component_spec.rb +9 -9
  107. data/spec/components/blacklight/facet_component_spec.rb +2 -2
  108. data/spec/components/blacklight/facets/index_navigation_component_spec.rb +1 -2
  109. data/spec/components/blacklight/facets/list_component_spec.rb +1 -1
  110. data/spec/components/blacklight/facets/suggest_component_spec.rb +1 -2
  111. data/spec/components/blacklight/metadata_field_component_spec.rb +26 -0
  112. data/spec/components/blacklight/skip_link_component_spec.rb +2 -2
  113. data/spec/controllers/application_controller_spec.rb +71 -0
  114. data/spec/controllers/bookmarks_controller_spec.rb +18 -5
  115. data/spec/controllers/catalog_controller_spec.rb +7 -5
  116. data/spec/features/advanced_search_spec.rb +29 -16
  117. data/spec/features/alternate_controller_spec.rb +4 -4
  118. data/spec/features/axe_spec.rb +34 -22
  119. data/spec/features/bookmarks_spec.rb +13 -13
  120. data/spec/features/dark_mode_spec.rb +42 -0
  121. data/spec/features/did_you_mean_spec.rb +27 -27
  122. data/spec/features/facet_missing_spec.rb +3 -3
  123. data/spec/features/record_view_spec.rb +14 -14
  124. data/spec/features/search_context_spec.rb +8 -8
  125. data/spec/features/search_filters_spec.rb +16 -16
  126. data/spec/features/search_formats_spec.rb +2 -2
  127. data/spec/features/search_history_spec.rb +11 -11
  128. data/spec/features/search_pagination_spec.rb +17 -17
  129. data/spec/features/search_results_spec.rb +3 -3
  130. data/spec/features/search_sort_spec.rb +4 -4
  131. data/spec/features/search_spec.rb +20 -20
  132. data/spec/helpers/blacklight/url_helper_behavior_spec.rb +15 -15
  133. data/spec/helpers/blacklight_helper_spec.rb +1 -1
  134. data/spec/helpers/catalog_helper_spec.rb +7 -7
  135. data/spec/integration/generators/blacklight/user_generator_spec.rb +60 -0
  136. data/spec/lib/blacklight/search_state/pivot_filter_field_spec.rb +1 -1
  137. data/spec/models/blacklight/configurable_spec.rb +1 -1
  138. data/spec/models/blacklight/configuration_spec.rb +0 -19
  139. data/spec/models/blacklight/document/dublin_core_spec.rb +2 -2
  140. data/spec/models/blacklight/facet_paginator_spec.rb +1 -1
  141. data/spec/models/blacklight/search_builder_spec.rb +15 -3
  142. data/spec/models/blacklight/solr/repository_spec.rb +10 -2
  143. data/spec/models/blacklight/solr/search_builder_behavior_spec.rb +3 -5
  144. data/spec/models/blacklight/user_spec.rb +32 -5
  145. data/spec/models/bookmark_spec.rb +1 -1
  146. data/spec/models/record_mailer_spec.rb +8 -8
  147. data/spec/models/search_spec.rb +1 -1
  148. data/spec/models/solr_document_spec.rb +4 -0
  149. data/spec/presenters/blacklight/index_presenter_spec.rb +1 -0
  150. data/spec/presenters/blacklight/rendering/pipeline_spec.rb +1 -0
  151. data/spec/presenters/blacklight/show_presenter_spec.rb +1 -0
  152. data/spec/presenters/blacklight/thumbnail_presenter_spec.rb +1 -0
  153. data/spec/services/blacklight/search_service_spec.rb +7 -7
  154. data/spec/spec_helper.rb +3 -12
  155. data/spec/support/features/session_helpers.rb +13 -4
  156. data/spec/support/features/turbo_helpers.rb +13 -0
  157. data/spec/support/features.rb +2 -0
  158. data/spec/support/user_helpers.rb +24 -0
  159. data/spec/test_app_templates/lib/generators/test_app_generator.rb +1 -1
  160. data/spec/views/catalog/_document.html.erb_spec.rb +4 -4
  161. data/spec/views/catalog/_paginate_compact.html.erb_spec.rb +2 -2
  162. data/spec/views/catalog/_sort_and_per_page.html.erb_spec.rb +5 -5
  163. data/spec/views/catalog/index.html.erb_spec.rb +2 -2
  164. data/spec/views/catalog/show.html.erb_spec.rb +3 -4
  165. data/spec/views/shared/_user_util_links.html.erb_spec.rb +56 -0
  166. data/template.demo.rb +2 -1
  167. metadata +30 -22
  168. data/.hound.yml +0 -4
  169. data/.jshintrc +0 -22
  170. data/app/assets/images/blacklight/logo.png +0 -0
@@ -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
@@ -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 })
@@ -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
@@ -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
@@ -17,9 +17,9 @@ RSpec.describe "catalog/_document" do
17
17
  stub_template "catalog/_b_default.html.erb" => "b_partial"
18
18
  stub_template "catalog/_c_default.html.erb" => "c_partial"
19
19
  render partial: "catalog/document", locals: { document: document, document_counter: 1, view_config: blacklight_config.index }
20
- expect(rendered).to match /a_partial/
21
- expect(rendered).to match /b_partial/
22
- expect(rendered).to match /c_partial/
20
+ expect(rendered).to include('a_partial')
21
+ expect(rendered).to include('b_partial')
22
+ expect(rendered).to include('c_partial')
23
23
  end
24
24
 
25
25
  context 'with a configured document component' do
@@ -39,7 +39,7 @@ RSpec.describe "catalog/_document" do
39
39
 
40
40
  it 'renders the document component' do
41
41
  render partial: "catalog/document", locals: { document: document, document_counter: 1, view_config: blacklight_config.index }
42
- expect(rendered).to match /blah/
42
+ expect(rendered).to include('blah')
43
43
  end
44
44
  end
45
45
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RSpec.describe "catalog/_paginate_compact.html.erb" do
4
- let(:user) { User.new { |u| u.save(validate: false) } }
4
+ let(:user) { create_user }
5
5
  let(:blacklight_config) { Blacklight::Configuration.new }
6
6
 
7
7
  before do
@@ -16,7 +16,7 @@ RSpec.describe "catalog/_paginate_compact.html.erb" do
16
16
  end
17
17
 
18
18
  it "renders ActiveRecord collections" do
19
- 50.times { b = Bookmark.new; b.user = user; b.save! }
19
+ 50.times { Bookmark.create!(user: user) }
20
20
  render partial: 'catalog/paginate_compact', object: Bookmark.page(1).per(25)
21
21
  expect(rendered).to have_css ".page-entries"
22
22
  expect(rendered).to have_css "a[@rel=next]"
@@ -17,10 +17,10 @@ RSpec.describe "catalog/_sort_and_per_page" do
17
17
  stub_template "catalog/_per_page_widget.html.erb" => "per_page_widget"
18
18
  stub_template "catalog/_view_type_group.html.erb" => "view_type_group"
19
19
  render
20
- expect(rendered).to match /paginate_compact/
21
- expect(rendered).to match /sort_widget/
22
- expect(rendered).to match /per_page_widget/
23
- expect(rendered).to match /view_type_group/
20
+ expect(rendered).to include('paginate_compact')
21
+ expect(rendered).to include('sort_widget')
22
+ expect(rendered).to include('per_page_widget')
23
+ expect(rendered).to include('view_type_group')
24
24
  end
25
25
 
26
26
  it "does not render the pagination controls with bad limit values" do
@@ -30,6 +30,6 @@ RSpec.describe "catalog/_sort_and_per_page" do
30
30
  stub_template "catalog/_per_page_widget.html.erb" => "per_page_widget"
31
31
  stub_template "catalog/_view_type_group.html.erb" => "view_type_group"
32
32
  render
33
- expect(rendered).not_to match /paginate_compact/
33
+ expect(rendered).not_to include('paginate_compact')
34
34
  end
35
35
  end
@@ -12,7 +12,7 @@ RSpec.describe "catalog/index.html.erb" do
12
12
  it "renders the Search::SidebarComponent component" do
13
13
  render
14
14
 
15
- expect(sidebar).to match /Limit your search/
15
+ expect(sidebar).to include('Limit your search')
16
16
  end
17
17
  end
18
18
 
@@ -31,7 +31,7 @@ RSpec.describe "catalog/index.html.erb" do
31
31
 
32
32
  it "renders the search_header partial" do
33
33
  render
34
- expect(rendered).to match /sortAndPerPage/
34
+ expect(rendered).to include('sortAndPerPage')
35
35
  end
36
36
  end
37
37
  end
@@ -9,7 +9,6 @@ RSpec.describe "catalog/show.html.erb" do
9
9
  allow(presenter).to receive(:html_title).and_return('Heading')
10
10
  allow(document).to receive(:more_like_this).and_return([])
11
11
  allow(view).to receive_messages(has_user_authentication_provider?: false)
12
- allow(view).to receive_messages(render_document_sidebar_partial: "Sidebar")
13
12
  allow(view).to receive_messages(current_search_session: nil, search_session: {})
14
13
  assign :document, document
15
14
  allow(view).to receive_messages(document_presenter: presenter, action_name: 'show', blacklight_config: blacklight_config)
@@ -36,8 +35,8 @@ RSpec.describe "catalog/show.html.erb" do
36
35
  stub_template "catalog/_b_default.html.erb" => "b_partial"
37
36
  stub_template "catalog/_c_default.html.erb" => "c_partial"
38
37
  render
39
- expect(rendered).to match /a_partial/
40
- expect(rendered).to match /b_partial/
41
- expect(rendered).to match /c_partial/
38
+ expect(rendered).to include('a_partial')
39
+ expect(rendered).to include('b_partial')
40
+ expect(rendered).to include('c_partial')
42
41
  end
43
42
  end
@@ -7,6 +7,10 @@ RSpec.describe "shared/_user_util_links" do
7
7
  end
8
8
  end
9
9
 
10
+ before do
11
+ allow(controller).to receive(:render_bookmarks_control?).and_return false
12
+ end
13
+
10
14
  it "renders the correct bookmark count" do
11
15
  count = rand(99)
12
16
  allow(controller).to receive(:render_bookmarks_control?).and_return true
@@ -15,4 +19,56 @@ RSpec.describe "shared/_user_util_links" do
15
19
  render "shared/user_util_links"
16
20
  expect(rendered).to have_css('#bookmarks_nav span.badge[data-role=bookmark-counter]', text: count.to_s)
17
21
  end
22
+
23
+ it "renders Rails authentication session links" do
24
+ user = instance_double(User, to_s: "xyz@example.com")
25
+
26
+ allow(view).to receive_messages(
27
+ blacklight_account_path: nil,
28
+ blacklight_config: blacklight_config,
29
+ blacklight_login_path: "/session/new",
30
+ blacklight_logout_link_options: { data: { turbo_method: :delete } },
31
+ blacklight_logout_path: "/session",
32
+ current_user: user,
33
+ has_user_authentication_provider?: true
34
+ )
35
+
36
+ render "shared/user_util_links"
37
+
38
+ expect(rendered).to have_link("Log Out", href: "/session")
39
+ expect(rendered).to have_css('a[data-turbo-method="delete"]', text: "Log Out")
40
+ expect(rendered).to have_no_link("xyz@example.com")
41
+ end
42
+
43
+ it "renders Devise-compatible session and registration links" do
44
+ user = instance_double(User, to_s: "xyz@example.com")
45
+
46
+ allow(view).to receive_messages(
47
+ blacklight_account_path: "/users/edit",
48
+ blacklight_config: blacklight_config,
49
+ blacklight_login_path: "/users/sign_in",
50
+ blacklight_logout_link_options: {},
51
+ blacklight_logout_path: "/users/sign_out",
52
+ current_user: user,
53
+ has_user_authentication_provider?: true
54
+ )
55
+
56
+ render "shared/user_util_links"
57
+
58
+ expect(rendered).to have_link("Log Out", href: "/users/sign_out")
59
+ expect(rendered).to have_link("xyz@example.com", href: "/users/edit")
60
+ end
61
+
62
+ it "renders a provider-neutral login link" do
63
+ allow(view).to receive_messages(
64
+ blacklight_config: blacklight_config,
65
+ blacklight_login_path: "/session/new",
66
+ current_user: nil,
67
+ has_user_authentication_provider?: true
68
+ )
69
+
70
+ render "shared/user_util_links"
71
+
72
+ expect(rendered).to have_link("Login", href: "/session/new")
73
+ end
18
74
  end
data/template.demo.rb CHANGED
@@ -4,7 +4,8 @@ gem 'blacklight', '>= 7.0'
4
4
 
5
5
  after_bundle do
6
6
  # run the blacklight install generator
7
- options = ENV.fetch("BLACKLIGHT_INSTALL_OPTIONS", '--devise --marc')
7
+ default_authentication_option = Rails.gem_version >= Gem::Version.new('8.0.0') ? '--authentication' : '--devise'
8
+ options = ENV.fetch("BLACKLIGHT_INSTALL_OPTIONS", "#{default_authentication_option} --marc")
8
9
 
9
10
  generate 'blacklight:install', options
10
11