blacklight 7.18.1 → 7.20.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/.env +1 -1
  3. data/.github/workflows/ruby.yml +19 -1
  4. data/.rubocop.yml +4 -0
  5. data/README.md +1 -1
  6. data/VERSION +1 -1
  7. data/app/assets/javascripts/blacklight/blacklight.js +8 -4
  8. data/app/assets/stylesheets/blacklight/_balanced_list.scss +2 -2
  9. data/app/assets/stylesheets/blacklight/_bootstrap_overrides.scss +1 -1
  10. data/app/assets/stylesheets/blacklight/_constraints.scss +8 -5
  11. data/app/assets/stylesheets/blacklight/_controls.scss +9 -1
  12. data/app/assets/stylesheets/blacklight/_facets.scss +5 -3
  13. data/app/assets/stylesheets/blacklight/_header.scss +6 -1
  14. data/app/assets/stylesheets/blacklight/_pagination.scss +1 -1
  15. data/app/assets/stylesheets/blacklight/_twitter_typeahead.scss +1 -0
  16. data/app/components/blacklight/constraint_layout_component.html.erb +1 -1
  17. data/app/components/blacklight/constraints_component.html.erb +2 -2
  18. data/app/components/blacklight/constraints_component.rb +6 -2
  19. data/app/components/blacklight/content_areas_shim.rb +2 -1
  20. data/app/components/blacklight/document_component.rb +10 -10
  21. data/app/components/blacklight/facet_field_component.html.erb +3 -1
  22. data/app/components/blacklight/facet_field_no_layout_component.rb +1 -1
  23. data/app/components/blacklight/facet_item_component.rb +4 -4
  24. data/app/components/blacklight/facet_item_pivot_component.rb +2 -2
  25. data/app/components/blacklight/metadata_field_layout_component.rb +2 -2
  26. data/app/components/blacklight/response/facet_group_component.html.erb +2 -0
  27. data/app/components/blacklight/response/pagination_component.html.erb +1 -1
  28. data/app/components/blacklight/response/view_type_component.html.erb +1 -1
  29. data/app/components/blacklight/search_bar_component.html.erb +3 -3
  30. data/app/components/blacklight/system/dropdown_component.rb +1 -1
  31. data/app/components/blacklight/system/flash_message_component.html.erb +1 -1
  32. data/app/components/blacklight/system/modal_component.html.erb +1 -1
  33. data/app/controllers/concerns/blacklight/bookmarks.rb +0 -3
  34. data/app/controllers/concerns/blacklight/catalog.rb +3 -0
  35. data/app/controllers/concerns/blacklight/controller.rb +9 -5
  36. data/app/controllers/concerns/blacklight/search_context.rb +1 -1
  37. data/app/helpers/blacklight/catalog_helper_behavior.rb +1 -3
  38. data/app/helpers/blacklight/component_helper_behavior.rb +1 -1
  39. data/app/helpers/blacklight/render_partials_helper_behavior.rb +5 -1
  40. data/app/javascript/blacklight/core.js +5 -1
  41. data/app/javascript/blacklight/modal.js +1 -1
  42. data/app/javascript/blacklight/search_context.js +5 -2
  43. data/app/views/blacklight/nav/_bookmark.html.erb +1 -1
  44. data/app/views/bookmarks/index.html.erb +1 -1
  45. data/app/views/catalog/_constraints.html.erb +2 -2
  46. data/app/views/catalog/_document.html.erb +4 -3
  47. data/app/views/catalog/_document_list.html.erb +3 -2
  48. data/app/views/catalog/_home_text.html.erb +3 -3
  49. data/app/views/catalog/_paginate_compact.html.erb +1 -0
  50. data/app/views/catalog/_per_page_widget.html.erb +1 -1
  51. data/app/views/catalog/_search_results.html.erb +2 -2
  52. data/app/views/search_history/index.html.erb +1 -1
  53. data/app/views/shared/_header_navbar.html.erb +3 -3
  54. data/blacklight.gemspec +4 -2
  55. data/config/locales/blacklight.ar.yml +2 -2
  56. data/config/locales/blacklight.ca.yml +2 -2
  57. data/config/locales/blacklight.de.yml +6 -4
  58. data/config/locales/blacklight.en.yml +8 -4
  59. data/config/locales/blacklight.es.yml +5 -3
  60. data/config/locales/blacklight.fr.yml +5 -3
  61. data/config/locales/blacklight.hu.yml +2 -2
  62. data/config/locales/blacklight.it.yml +5 -3
  63. data/config/locales/blacklight.nl.yml +2 -2
  64. data/config/locales/blacklight.pt-BR.yml +1 -1
  65. data/config/locales/blacklight.sq.yml +2 -2
  66. data/config/locales/blacklight.zh.yml +2 -2
  67. data/lib/blacklight/configuration/view_config.rb +3 -1
  68. data/lib/blacklight/configuration.rb +7 -1
  69. data/lib/blacklight/nested_open_struct_with_hash_access.rb +14 -11
  70. data/lib/blacklight/open_struct_with_hash_access.rb +1 -1
  71. data/lib/blacklight/search_builder.rb +1 -1
  72. data/lib/blacklight/search_state/filter_field.rb +9 -0
  73. data/lib/blacklight/solr/request.rb +10 -7
  74. data/lib/blacklight/solr/response/group_response.rb +3 -2
  75. data/lib/blacklight/solr/response/pagination_methods.rb +1 -1
  76. data/lib/blacklight/solr/search_builder_behavior.rb +38 -18
  77. data/lib/blacklight.rb +5 -1
  78. data/lib/generators/blacklight/assets_generator.rb +4 -2
  79. data/lib/generators/blacklight/install_generator.rb +4 -1
  80. data/lib/generators/blacklight/user_generator.rb +1 -1
  81. data/package.json +2 -2
  82. data/spec/components/blacklight/constraint_layout_component_spec.rb +3 -7
  83. data/spec/components/blacklight/facet_field_checkboxes_component_spec.rb +3 -7
  84. data/spec/components/blacklight/facet_field_list_component_spec.rb +3 -7
  85. data/spec/components/blacklight/facet_item_component_spec.rb +8 -8
  86. data/spec/components/blacklight/facet_item_pivot_component_spec.rb +3 -7
  87. data/spec/components/blacklight/hidden_search_state_component_spec.rb +7 -8
  88. data/spec/components/blacklight/metadata_field_component_spec.rb +4 -8
  89. data/spec/features/axe_spec.rb +34 -0
  90. data/spec/features/facet_missing_spec.rb +59 -0
  91. data/spec/features/facets_spec.rb +3 -3
  92. data/spec/helpers/blacklight/configuration_helper_behavior_spec.rb +3 -3
  93. data/spec/helpers/blacklight/facets_helper_behavior_spec.rb +2 -2
  94. data/spec/helpers/blacklight_helper_spec.rb +18 -0
  95. data/spec/lib/blacklight/nested_open_struct_with_hash_access_spec.rb +23 -1
  96. data/spec/lib/blacklight/search_state/filter_field_spec.rb +27 -0
  97. data/spec/models/blacklight/configuration_spec.rb +10 -0
  98. data/spec/models/blacklight/solr/request_spec.rb +0 -1
  99. data/spec/models/blacklight/solr/response/group_response_spec.rb +3 -2
  100. data/spec/models/blacklight/solr/search_builder_spec.rb +27 -1
  101. data/spec/spec_helper.rb +16 -9
  102. data/spec/support/view_component_capybara_test_helpers.rb +8 -0
  103. data/spec/test_app_templates/lib/generators/test_app_generator.rb +0 -3
  104. data/spec/views/catalog/_document.html.erb_spec.rb +9 -0
  105. data/spec/views/catalog/_document_list.html.erb_spec.rb +1 -1
  106. data/spec/views/catalog/_facet_layout.html.erb_spec.rb +2 -2
  107. metadata +45 -11
@@ -45,7 +45,7 @@ module Blacklight
45
45
  ##
46
46
  # Update the :q (query) parameter
47
47
  def where(conditions)
48
- Deprecation.warn("SearchBuilder#where must be called with a hash, received #{conditions.inspect}.") unless conditions.is_a? Hash
48
+ Deprecation.warn(Blacklight::SearchBuilder, "SearchBuilder#where must be called with a hash, received #{conditions.inspect}.") unless conditions.is_a? Hash
49
49
  params_will_change!
50
50
  @search_state = @search_state.reset(@search_state.params.merge(q: conditions))
51
51
  @blacklight_params = @search_state.params.dup
@@ -83,10 +83,19 @@ module Blacklight
83
83
  Deprecation.warn(self, 'Normalizing parameters in FilterField#remove is deprecated')
84
84
  collection = collection.values
85
85
  end
86
+
86
87
  params[param][key] = collection - Array(value)
87
88
  params[param].delete(key) if params[param][key].empty?
88
89
  params.delete(param) if params[param].empty?
89
90
 
91
+ # Handle missing field queries.
92
+ missing = I18n.t("blacklight.search.facets.missing")
93
+ if (item.respond_to?(:fq) && item.fq == "-#{key}:[* TO *]") ||
94
+ item == missing
95
+ params[param].delete("-#{key}:")
96
+ params[param].delete(key) if params[param][key] == [""]
97
+ end
98
+
90
99
  new_state.reset(params)
91
100
  end
92
101
 
@@ -2,7 +2,10 @@
2
2
  class Blacklight::Solr::InvalidParameter < ArgumentError; end
3
3
 
4
4
  class Blacklight::Solr::Request < ActiveSupport::HashWithIndifferentAccess
5
+ # @deprecated
5
6
  SINGULAR_KEYS = %w(facet fl q qt rows start spellcheck spellcheck.q sort per_page wt hl group defType)
7
+
8
+ # @deprecated
6
9
  ARRAY_KEYS = %w(facet.field facet.query facet.pivot fq hl.fl)
7
10
 
8
11
  def initialize(constructor = {})
@@ -12,9 +15,6 @@ class Blacklight::Solr::Request < ActiveSupport::HashWithIndifferentAccess
12
15
  else
13
16
  super(constructor)
14
17
  end
15
- ARRAY_KEYS.each do |key|
16
- self[key] ||= []
17
- end
18
18
  end
19
19
 
20
20
  def append_query(query)
@@ -49,26 +49,29 @@ class Blacklight::Solr::Request < ActiveSupport::HashWithIndifferentAccess
49
49
  end
50
50
 
51
51
  def append_filter_query(query)
52
+ self['fq'] ||= []
53
+ self['fq'] = Array(self['fq']) if self['fq'].is_a? String
54
+
52
55
  self['fq'] << query
53
56
  end
54
57
 
55
58
  def append_facet_fields(values)
59
+ self['facet.field'] ||= []
56
60
  self['facet.field'] += Array(values)
57
61
  end
58
62
 
59
63
  def append_facet_query(values)
64
+ self['facet.query'] ||= []
60
65
  self['facet.query'] += Array(values)
61
66
  end
62
67
 
63
68
  def append_facet_pivot(query)
69
+ self['facet.pivot'] ||= []
64
70
  self['facet.pivot'] << query
65
71
  end
66
72
 
67
73
  def append_highlight_field(query)
74
+ self['hl.fl'] ||= []
68
75
  self['hl.fl'] << query
69
76
  end
70
-
71
- def to_hash
72
- reject { |key, value| ARRAY_KEYS.include?(key) && value.blank? }
73
- end
74
77
  end
@@ -45,8 +45,9 @@ class Blacklight::Solr::Response::GroupResponse
45
45
  def entry_name(options)
46
46
  I18n.t(
47
47
  "blacklight.entry_name.grouped.#{key}",
48
- default: :'blacklight.entry_name.grouped.default'
49
- ).pluralize(options[:count])
48
+ default: :'blacklight.entry_name.grouped.default',
49
+ count: options[:count]
50
+ )
50
51
  end
51
52
 
52
53
  def method_missing meth, *args, &block
@@ -24,6 +24,6 @@ module Blacklight::Solr::Response::PaginationMethods
24
24
  ##
25
25
  # Meant to have the same signature as Kaminari::PaginatableArray#entry_name
26
26
  def entry_name(options)
27
- I18n.t('blacklight.entry_name.default').pluralize(options[:count])
27
+ I18n.t('blacklight.entry_name.default', count: options[:count])
28
28
  end
29
29
  end
@@ -5,10 +5,12 @@ module Blacklight::Solr
5
5
 
6
6
  included do
7
7
  self.default_processor_chain = [
8
- :default_solr_parameters, :add_query_to_solr, :add_facet_fq_to_solr,
8
+ :default_solr_parameters, :add_search_field_default_parameters,
9
+ :add_query_to_solr, :add_facet_fq_to_solr,
9
10
  :add_facetting_to_solr, :add_solr_fields_to_query, :add_paging_to_solr,
10
11
  :add_sorting_to_solr, :add_group_config_to_solr,
11
12
  :add_facet_paging_to_solr, :add_adv_search_clauses,
13
+ :add_missing_field_query,
12
14
  :add_additional_filters
13
15
  ]
14
16
  end
@@ -18,22 +20,17 @@ module Blacklight::Solr
18
20
  # merge to dup values, to avoid later mutating the original by mistake.
19
21
  def default_solr_parameters(solr_parameters)
20
22
  blacklight_config.default_solr_params.each do |key, value|
21
- solr_parameters[key] = if value.respond_to? :deep_dup
22
- value.deep_dup
23
- elsif value.respond_to?(:dup) && value.duplicable?
24
- value.dup
25
- else
26
- value
27
- end
23
+ solr_parameters[key] ||= if value.respond_to? :deep_dup
24
+ value.deep_dup
25
+ elsif value.respond_to?(:dup) && value.duplicable?
26
+ value.dup
27
+ else
28
+ value
29
+ end
28
30
  end
29
31
  end
30
32
 
31
- ##
32
- # Take the user-entered query, and put it in the solr params,
33
- # including config's "search field" params for current search field.
34
- # also include setting spellcheck.q.
35
- # rubocop:disable Metrics/CyclomaticComplexity
36
- def add_query_to_solr(solr_parameters)
33
+ def add_search_field_default_parameters(solr_parameters)
37
34
  ###
38
35
  # legacy behavior of user param :qt is passed through, but over-ridden
39
36
  # by actual search field config if present. We might want to remove
@@ -47,10 +44,21 @@ module Blacklight::Solr
47
44
  ###
48
45
  # Merge in search field configured values, if present, over-writing general
49
46
  # defaults
50
-
51
47
  if search_field
52
48
  solr_parameters[:qt] = search_field.qt if search_field.qt
53
- solr_parameters.merge!(search_field.solr_parameters) if search_field.solr_parameters
49
+
50
+ solr_parameters.deep_merge!(search_field.solr_parameters) if search_field.solr_parameters
51
+ end
52
+ end
53
+
54
+ ##
55
+ # Take the user-entered query, and put it in the solr params,
56
+ # including config's "search field" params for current search field.
57
+ # also include setting spellcheck.q.
58
+ def add_query_to_solr(solr_parameters)
59
+ unless processor_chain.include?(:add_search_field_default_parameters)
60
+ Deprecation.warn(Blacklight::Solr::SearchBuilderBehavior, 'Please include :add_search_field_default_parameters in your process chain')
61
+ add_search_field_default_parameters(solr_parameters)
54
62
  end
55
63
 
56
64
  ##
@@ -66,14 +74,26 @@ module Blacklight::Solr
66
74
  add_search_field_with_local_parameters(solr_parameters)
67
75
  elsif search_state.query_param.is_a? Hash
68
76
  if search_state.query_param == @additional_filters && !processor_chain.include?(:add_additional_filters)
69
- Deprecation.warn('Expecting to see the processor step add_additional_filters; falling back to legacy query handling')
77
+ Deprecation.warn(Blacklight::Solr::SearchBuilderBehavior, 'Expecting to see the processor step add_additional_filters; falling back to legacy query handling')
70
78
  add_additional_filters(solr_parameters, search_state.query_param)
71
79
  end
72
80
  elsif search_state.query_param
73
81
  solr_parameters.append_query search_state.query_param
74
82
  end
75
83
  end
76
- # rubocop:enable Metrics/CyclomaticComplexity
84
+
85
+ ##
86
+ # Build and append a missing field query.
87
+ ##
88
+ def add_missing_field_query(solr_parameters)
89
+ return unless solr_parameters["facet.missing"]
90
+
91
+ solr_parameters[:fq] = [] if solr_parameters[:fq].blank?
92
+
93
+ solr_parameters[:fq].append(*(blacklight_params["f"] || [])
94
+ .select { |f| f.match(/^-/) }
95
+ .map { |k, _v| "#{k}[* TO *]" })
96
+ end
77
97
 
78
98
  def add_additional_filters(solr_parameters, additional_filters = nil)
79
99
  q = additional_filters || @additional_filters
data/lib/blacklight.rb CHANGED
@@ -96,7 +96,11 @@ module Blacklight
96
96
  end
97
97
 
98
98
  begin
99
- @blacklight_yml = YAML.safe_load(blacklight_erb)
99
+ @blacklight_yml = if RUBY_VERSION > '2.6'
100
+ YAML.safe_load(blacklight_erb, aliases: true)
101
+ else
102
+ YAML.safe_load(blacklight_erb, [], [], true)
103
+ end
100
104
  rescue => e
101
105
  raise("#{blacklight_config_file} was found, but could not be parsed.\n#{e.inspect}")
102
106
  end
@@ -3,9 +3,11 @@ module Blacklight
3
3
  class Assets < Rails::Generators::Base
4
4
  source_root File.expand_path('../templates', __FILE__)
5
5
 
6
+ class_option :'bootstrap-version', type: :string, default: ENV.fetch('BOOTSTRAP_VERSION', '~> 4.0'), desc: "Set the generated app's bootstrap version"
7
+
6
8
  # This could be skipped if you want to use webpacker
7
9
  def add_javascript_dependencies
8
- gem 'bootstrap', '~> 4.0'
10
+ gem 'bootstrap', options[:'bootstrap-version']
9
11
  gem 'twitter-typeahead-rails', '0.11.1.pre.corejavascript'
10
12
  end
11
13
 
@@ -15,7 +17,7 @@ module Blacklight
15
17
 
16
18
  create_file 'app/assets/javascripts/application.js' do
17
19
  <<~CONTENT
18
- //= require jquery
20
+ //= require jquery3
19
21
  //= require rails-ujs
20
22
  //= require turbolinks
21
23
  CONTENT
@@ -10,6 +10,7 @@ module Blacklight
10
10
 
11
11
  class_option :devise, type: :boolean, default: false, aliases: "-d", desc: "Use Devise as authentication logic."
12
12
  class_option :marc, type: :boolean, default: false, aliases: "-m", desc: "Generate MARC-based demo."
13
+ class_option :'bootstrap-version', type: :string, default: nil, desc: "Set the generated app's bootstrap version"
13
14
  class_option :'skip-assets', type: :boolean, default: !defined?(Sprockets), desc: "Skip generating javascript and css assets into the application"
14
15
  class_option :'skip-solr', type: :boolean, default: false, desc: "Skip generating solr configurations."
15
16
 
@@ -33,7 +34,9 @@ module Blacklight
33
34
  # Call external generator in AssetsGenerator, so we can
34
35
  # leave that callable seperately too.
35
36
  def copy_public_assets
36
- generate "blacklight:assets" unless options[:'skip-assets']
37
+ generated_options = "--bootstrap-version #{options[:'bootstrap-version']}" if options[:'bootstrap-version']
38
+
39
+ generate "blacklight:assets", generated_options unless options[:'skip-assets']
37
40
  end
38
41
 
39
42
  def bundle_install
@@ -22,7 +22,7 @@ module Blacklight
22
22
  return unless options[:devise]
23
23
 
24
24
  gem "devise"
25
- gem "devise-guests", "~> 0.6"
25
+ gem "devise-guests", "~> 0.8"
26
26
 
27
27
  inside destination_root do
28
28
  Bundler.with_clean_env do
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blacklight-frontend",
3
- "version": "7.10.0",
3
+ "version": "7.20.0",
4
4
  "description": "[![Build Status](https://travis-ci.com/projectblacklight/blacklight.png?branch=master)](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=master)](https://coveralls.io/github/projectblacklight/blacklight?branch=master)",
5
5
  "main": "app/assets/javascripts/blacklight",
6
6
  "scripts": {
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "bloodhound-js": "^1.2.3",
30
- "bootstrap": "^4.3.1",
30
+ "bootstrap": ">=4.3.1 <6.0.0",
31
31
  "jquery": "^3.5.1",
32
32
  "typeahead.js": "^0.11.1"
33
33
  }
@@ -3,12 +3,8 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  RSpec.describe Blacklight::ConstraintLayoutComponent, type: :component do
6
- subject(:render) do
7
- render_inline(described_class.new(**params))
8
- end
9
-
10
- let(:rendered) do
11
- Capybara::Node::Simple.new(render)
6
+ subject(:rendered) do
7
+ render_inline_to_capybara_node(described_class.new(**params))
12
8
  end
13
9
 
14
10
  describe "for simple display" do
@@ -39,7 +35,7 @@ RSpec.describe Blacklight::ConstraintLayoutComponent, type: :component do
39
35
 
40
36
  it "has an accessible remove label" do
41
37
  expect(rendered).to have_selector(".remove") do |s|
42
- expect(s).to have_selector('.sr-only', text: 'Remove constraint my label: my value')
38
+ expect(s).to have_selector('.visually-hidden', text: 'Remove constraint my label: my value')
43
39
  end
44
40
  end
45
41
  end
@@ -3,12 +3,8 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  RSpec.describe Blacklight::FacetFieldCheckboxesComponent, type: :component do
6
- subject(:render) do
7
- render_inline(described_class.new(facet_field: facet_field))
8
- end
9
-
10
- let(:rendered) do
11
- Capybara::Node::Simple.new(render)
6
+ subject(:rendered) do
7
+ render_inline_to_capybara_node(described_class.new(facet_field: facet_field))
12
8
  end
13
9
 
14
10
  let(:facet_field) do
@@ -40,7 +36,7 @@ RSpec.describe Blacklight::FacetFieldCheckboxesComponent, type: :component do
40
36
  it 'renders a collapsible card' do
41
37
  expect(rendered).to have_selector '.card'
42
38
  expect(rendered).to have_button 'Field'
43
- expect(rendered).to have_selector 'button[data-target="#facet-field"]'
39
+ expect(rendered).to have_selector 'button[data-bs-target="#facet-field"]'
44
40
  expect(rendered).to have_selector '#facet-field.collapse.show'
45
41
  end
46
42
 
@@ -3,12 +3,8 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  RSpec.describe Blacklight::FacetFieldListComponent, type: :component do
6
- subject(:render) do
7
- render_inline(described_class.new(facet_field: facet_field))
8
- end
9
-
10
- let(:rendered) do
11
- Capybara::Node::Simple.new(render)
6
+ subject(:rendered) do
7
+ render_inline_to_capybara_node(described_class.new(facet_field: facet_field))
12
8
  end
13
9
 
14
10
  let(:facet_field) do
@@ -35,7 +31,7 @@ RSpec.describe Blacklight::FacetFieldListComponent, type: :component do
35
31
  it 'renders a collapsible card' do
36
32
  expect(rendered).to have_selector '.card'
37
33
  expect(rendered).to have_button 'Field'
38
- expect(rendered).to have_selector 'button[data-target="#facet-field"]'
34
+ expect(rendered).to have_selector 'button[data-bs-target="#facet-field"]'
39
35
  expect(rendered).to have_selector '#facet-field.collapse.show'
40
36
  end
41
37
 
@@ -3,12 +3,8 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  RSpec.describe Blacklight::FacetItemComponent, type: :component do
6
- subject(:render) do
7
- render_inline(described_class.new(facet_item: facet_item))
8
- end
9
-
10
- let(:rendered) do
11
- Capybara::Node::Simple.new(render)
6
+ subject(:rendered) do
7
+ render_inline_to_capybara_node(described_class.new(facet_item: facet_item))
12
8
  end
13
9
 
14
10
  let(:facet_item) do
@@ -24,7 +20,9 @@ RSpec.describe Blacklight::FacetItemComponent, type: :component do
24
20
 
25
21
  it 'links to the facet and shows the number of hits' do
26
22
  expect(rendered).to have_selector 'li'
27
- expect(rendered).to have_link 'x', href: '/catalog?f=x'
23
+ expect(rendered).to have_link 'x', href: '/catalog?f=x' do |link|
24
+ link['rel'] == 'nofollow'
25
+ end
28
26
  expect(rendered).to have_selector '.facet-count', text: '10'
29
27
  end
30
28
 
@@ -43,7 +41,9 @@ RSpec.describe Blacklight::FacetItemComponent, type: :component do
43
41
  it 'links to the facet and shows the number of hits' do
44
42
  expect(rendered).to have_selector 'li'
45
43
  expect(rendered).to have_selector '.selected', text: 'x'
46
- expect(rendered).to have_link '[remove]', href: '/catalog'
44
+ expect(rendered).to have_link '[remove]', href: '/catalog' do |link|
45
+ link['rel'] == 'nofollow'
46
+ end
47
47
  expect(rendered).to have_selector '.selected.facet-count', text: '10'
48
48
  end
49
49
  end
@@ -3,12 +3,8 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  RSpec.describe Blacklight::FacetItemPivotComponent, type: :component do
6
- subject(:render) do
7
- render_inline(described_class.new(facet_item: facet_item))
8
- end
9
-
10
- let(:rendered) do
11
- Capybara::Node::Simple.new(render)
6
+ subject(:rendered) do
7
+ render_inline_to_capybara_node(described_class.new(facet_item: facet_item))
12
8
  end
13
9
 
14
10
  let(:search_state) do
@@ -31,7 +27,7 @@ RSpec.describe Blacklight::FacetItemPivotComponent, type: :component do
31
27
 
32
28
  it 'links to the facet and shows the number of hits' do
33
29
  expect(rendered).to have_selector 'li'
34
- expect(rendered).to have_link 'x', href: '/catalog?f[z]=x'
30
+ expect(rendered).to have_link 'x', href: '/catalog?f%5Bz%5D=x'
35
31
  expect(rendered).to have_selector '.facet-count', text: '10'
36
32
  end
37
33
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RSpec.describe Blacklight::HiddenSearchStateComponent, type: :component do
4
- subject(:render) { render_inline(instance) }
4
+ subject(:rendered) { render_inline_to_capybara_node(instance) }
5
5
 
6
6
  let(:params) do
7
7
  { q: "query",
@@ -11,14 +11,13 @@ RSpec.describe Blacklight::HiddenSearchStateComponent, type: :component do
11
11
  f: { field1: %w[a b], field2: ["z"] } }
12
12
  end
13
13
  let(:instance) { described_class.new(params: params) }
14
- let(:generated) { Capybara::Node::Simple.new("<div>#{render.to_html}</div>") }
15
14
 
16
15
  it "converts a hash with nested complex data to Rails-style hidden form fields" do
17
- expect(generated).to have_selector("input[type='hidden'][name='q'][value='query']", visible: :hidden)
18
- expect(generated).to have_selector("input[type='hidden'][name='per_page'][value='10']", visible: :hidden)
19
- expect(generated).to have_selector("input[type='hidden'][name='extra_arbitrary_key'][value='arbitrary_value']", visible: :hidden)
20
- expect(generated).to have_selector("input[type='hidden'][name='f[field2][]'][value='z']", visible: :hidden)
21
- expect(generated).to have_selector("input[type='hidden'][name='f[field1][]'][value='a']", visible: :hidden)
22
- expect(generated).to have_selector("input[type='hidden'][name='f[field1][]'][value='b']", visible: :hidden)
16
+ expect(rendered).to have_selector("input[type='hidden'][name='q'][value='query']", visible: :hidden)
17
+ expect(rendered).to have_selector("input[type='hidden'][name='per_page'][value='10']", visible: :hidden)
18
+ expect(rendered).to have_selector("input[type='hidden'][name='extra_arbitrary_key'][value='arbitrary_value']", visible: :hidden)
19
+ expect(rendered).to have_selector("input[type='hidden'][name='f[field2][]'][value='z']", visible: :hidden)
20
+ expect(rendered).to have_selector("input[type='hidden'][name='f[field1][]'][value='a']", visible: :hidden)
21
+ expect(rendered).to have_selector("input[type='hidden'][name='f[field1][]'][value='b']", visible: :hidden)
23
22
  end
24
23
  end
@@ -3,8 +3,8 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  RSpec.describe Blacklight::MetadataFieldComponent, type: :component do
6
- subject(:render) do
7
- render_inline(described_class.new(field: field))
6
+ subject(:rendered) do
7
+ render_inline_to_capybara_node(described_class.new(field: field))
8
8
  end
9
9
 
10
10
  let(:view_context) { controller.view_context }
@@ -15,10 +15,6 @@ RSpec.describe Blacklight::MetadataFieldComponent, type: :component do
15
15
  Blacklight::FieldPresenter.new(view_context, document, field_config)
16
16
  end
17
17
 
18
- let(:rendered) do
19
- Capybara::Node::Simple.new(render)
20
- end
21
-
22
18
  it 'renders the field label' do
23
19
  expect(rendered).to have_selector 'dt.blacklight-field', text: 'Field label'
24
20
  end
@@ -28,8 +24,8 @@ RSpec.describe Blacklight::MetadataFieldComponent, type: :component do
28
24
  end
29
25
 
30
26
  context 'from a show view' do
31
- subject(:render) do
32
- render_inline(described_class.new(field: field, show: true))
27
+ subject(:rendered) do
28
+ render_inline_to_capybara_node(described_class.new(field: field, show: true))
33
29
  end
34
30
 
35
31
  it 'renders the right field label' do
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe 'Accessibility testing', api: false, js: true do
4
+ xit 'validates the home page' do
5
+ visit root_path
6
+ expect(page).to be_accessible
7
+ end
8
+
9
+ xit 'validates the catalog page' do
10
+ visit root_path
11
+ fill_in "q", with: 'history'
12
+ click_button 'search'
13
+
14
+ # aria-allowed-role doesn't like nav[role="region"]
15
+ expect(page).to be_accessible(skipping: ['aria-allowed-role'])
16
+
17
+ within '.card.blacklight-language_ssim' do
18
+ click_button 'Language'
19
+ click_link "Tibetan"
20
+ end
21
+
22
+ expect(page).to be_accessible(skipping: ['aria-allowed-role'])
23
+ end
24
+
25
+ xit 'validates the single results page' do
26
+ visit solr_document_path('2007020969')
27
+ expect(page).to be_accessible
28
+ end
29
+
30
+ def be_accessible(skipping: [])
31
+ # typeahead does funny things with the search bar
32
+ be_axe_clean.excluding('.tt-hint').skipping(skipping + [('color-contrast' if Bootstrap::VERSION < '5')])
33
+ end
34
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe "Facet missing" do
4
+ before do
5
+ CatalogController.blacklight_config[:default_solr_params]["facet.missing"] = true
6
+ end
7
+
8
+ after do
9
+ CatalogController.blacklight_config[:default_solr_params].delete("facet.missing")
10
+ end
11
+
12
+ context "selecting missing field in facets" do
13
+ it "adds facet missing query and constraints" do
14
+ visit root_path
15
+
16
+ within "#facet-subject_geo_ssim" do
17
+ click_link "[Missing]"
18
+ end
19
+
20
+ within "#facet-subject_geo_ssim" do
21
+ expect(page).to have_selector("span.selected", text: "[Missing")
22
+ expect(page).to have_selector("span.facet-count.selected", text: "13")
23
+ end
24
+
25
+ within "#sortAndPerPage" do
26
+ expect(page).to have_content "1 - 10 of 13"
27
+ end
28
+
29
+ expect(page).to have_css(".constraint-value", text: "Region")
30
+ expect(page).to have_css(".constraint-value", text: "[Missing]")
31
+ end
32
+ end
33
+
34
+ context "unselecting the facet missing facet" do
35
+ it "unselects the missig field facet" do
36
+ visit root_path + "?f[-subject_geo_ssim:[* TO *]][]=&f[subject_geo_ssim][]="
37
+
38
+ within "#facet-subject_geo_ssim" do
39
+ click_link "remove"
40
+ end
41
+
42
+ expect(page).not_to have_link "remove"
43
+ expect(page).to have_content("Welcome!")
44
+ end
45
+ end
46
+
47
+ context "unselecting the facet missing constraint" do
48
+ it "unselects the missig field facet" do
49
+ visit root_path + "?f[-subject_geo_ssim:[* TO *]][]=&f[subject_geo_ssim][]="
50
+
51
+ within ".filter-subject_geo_ssim" do
52
+ click_link "Remove constraint Region: [Missing]"
53
+ end
54
+
55
+ expect(page).not_to have_link "remove"
56
+ expect(page).to have_content("Welcome!")
57
+ end
58
+ end
59
+ end
@@ -69,7 +69,7 @@ RSpec.describe "Facets" do
69
69
  page.find('h3.facet-field-heading button', text: 'Pivot Field').click
70
70
 
71
71
  within '#facet-example_pivot_field' do
72
- expect(page).to have_css('.facet-leaf-node', text: "Book\t30")
72
+ expect(page).to have_css('.facet-leaf-node', text: "Book 30")
73
73
  expect(page).not_to have_css('.facet-select', text: 'Tibetan')
74
74
  page.find('.facet-toggle-handle').click
75
75
  click_link 'Tibetan'
@@ -84,13 +84,13 @@ RSpec.describe "Facets" do
84
84
  pending 'Capybara::NotSupportedByDriverError: Capybara::Driver::Base#evaluate_script'
85
85
  visit root_path
86
86
  page.find('h3.facet-field-heading button', text: 'Format').click
87
- focused_element_data_target = page.evaluate_script("document.activeElement")['data-target']
87
+ focused_element_data_target = page.evaluate_script("document.activeElement")['data-bs-target']
88
88
  expect(focused_element_data_target).to eq '#facet-format'
89
89
  end
90
90
  end
91
91
 
92
92
  describe '"More" links' do
93
- it 'has default more link with sr-only text' do
93
+ it 'has default more link with visually-hidden text' do
94
94
  visit root_path
95
95
  within '#facet-language_ssim' do
96
96
  expect(page).to have_css 'div.more_facets', text: 'more Language'
@@ -194,9 +194,9 @@ RSpec.describe Blacklight::ConfigurationHelperBehavior do
194
194
  describe "#per_page_options_for_select" do
195
195
  it "is the per-page values formatted as options_for_select" do
196
196
  allow(helper).to receive_messages(blacklight_config: double(per_page: [11, 22, 33]))
197
- expect(helper.per_page_options_for_select).to include ["11<span class=\"sr-only\"> per page</span>", 11]
198
- expect(helper.per_page_options_for_select).to include ["22<span class=\"sr-only\"> per page</span>", 22]
199
- expect(helper.per_page_options_for_select).to include ["33<span class=\"sr-only\"> per page</span>", 33]
197
+ expect(helper.per_page_options_for_select).to include ["11<span class=\"sr-only visually-hidden\"> per page</span>", 11]
198
+ expect(helper.per_page_options_for_select).to include ["22<span class=\"sr-only visually-hidden\"> per page</span>", 22]
199
+ expect(helper.per_page_options_for_select).to include ["33<span class=\"sr-only visually-hidden\"> per page</span>", 33]
200
200
  end
201
201
  end
202
202
 
@@ -314,7 +314,7 @@ RSpec.describe Blacklight::FacetsHelperBehavior do
314
314
  end
315
315
 
316
316
  describe "simple case" do
317
- let(:expected_html) { '<span class="facet-label"><a class="facet-select" href="/catalog">Z</a></span><span class="facet-count">10</span>' }
317
+ let(:expected_html) { '<span class="facet-label"><a class="facet-select" rel="nofollow" href="/catalog">Z</a></span><span class="facet-count">10</span>' }
318
318
 
319
319
  it "uses facet_display_value" do
320
320
  result = helper.render_facet_value('simple_field', item)
@@ -323,7 +323,7 @@ RSpec.describe Blacklight::FacetsHelperBehavior do
323
323
  end
324
324
 
325
325
  describe "when :url_method is set" do
326
- let(:expected_html) { '<span class="facet-label"><a class="facet-select" href="/blabla">Z</a></span><span class="facet-count">10</span>' }
326
+ let(:expected_html) { '<span class="facet-label"><a class="facet-select" rel="nofollow" href="/blabla">Z</a></span><span class="facet-count">10</span>' }
327
327
 
328
328
  it "uses that method" do
329
329
  allow(helper).to receive(:facet_configuration_for_field).with('simple_field').and_return(Blacklight::Configuration::FacetField.new(key: 'simple_field', query: nil, date: nil, helper_method: nil, single: false, url_method: :test_method))