geoblacklight 6.0.0.pre.alpha.7 → 6.0.0.pre.beta.2
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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +14 -3
- data/README.md +2 -2
- data/Rakefile +1 -17
- data/app/assets/images/geoblacklight/logo-dark.svg +31 -0
- data/app/assets/images/geoblacklight/logo.svg +10 -10
- data/app/assets/stylesheets/geoblacklight/global.css +44 -7
- data/app/assets/stylesheets/geoblacklight/index_maps.css +0 -7
- data/app/assets/stylesheets/geoblacklight/record.css +33 -9
- data/app/assets/stylesheets/geoblacklight/search.css +66 -17
- data/app/assets/stylesheets/geoblacklight/viewers.css +18 -111
- data/app/assets/stylesheets/geoblacklight.css +0 -5
- data/app/components/blacklight/icons/georeferenced_component.rb +23 -0
- data/app/components/geoblacklight/document/download_link_component.html.erb +1 -1
- data/app/components/geoblacklight/document/preview_component.html.erb +0 -2
- data/app/components/geoblacklight/document_component.html.erb +0 -4
- data/app/components/geoblacklight/facets/bbox_field_component.html.erb +2 -1
- data/app/components/geoblacklight/georeferenced_header_badge_component.rb +16 -0
- data/app/components/geoblacklight/header_badge_component.html.erb +4 -2
- data/app/components/geoblacklight/header_badge_component.rb +1 -1
- data/app/components/geoblacklight/header_badges_component.html.erb +5 -0
- data/app/components/geoblacklight/header_badges_component.rb +34 -0
- data/app/components/geoblacklight/iiif_drag_drop_component.html.erb +1 -1
- data/app/components/geoblacklight/item_map_viewer_component.html.erb +0 -58
- data/app/components/geoblacklight/item_map_viewer_component.rb +19 -70
- data/app/components/geoblacklight/locator_map_component.rb +31 -0
- data/app/components/geoblacklight/login_link_component.html.erb +1 -1
- data/app/components/geoblacklight/login_link_component.rb +17 -1
- data/app/components/geoblacklight/metadata_description_markdown_component.rb +3 -9
- data/app/components/geoblacklight/overview_map_component.html.erb +2 -0
- data/app/components/geoblacklight/overview_map_component.rb +86 -0
- data/app/components/geoblacklight/relations/relation_component.html.erb +12 -0
- data/app/components/geoblacklight/relations/relation_component.rb +30 -0
- data/app/components/geoblacklight/relations/relations_component.html.erb +15 -0
- data/app/components/geoblacklight/relations/relations_component.rb +59 -0
- data/app/components/geoblacklight/resource_header_badge_component.rb +1 -1
- data/app/components/geoblacklight/search_result_component.html.erb +14 -33
- data/app/components/geoblacklight/search_result_component.rb +2 -14
- data/app/components/geoblacklight/static_map_component.rb +3 -14
- data/app/components/geoblacklight/truncatable_metadata_field_layout_component.html.erb +7 -0
- data/app/components/geoblacklight/truncatable_metadata_field_layout_component.rb +45 -0
- data/app/controllers/{relation_controller.rb → relations_controller.rb} +4 -2
- data/app/controllers/viewer_record_controller.rb +28 -0
- data/app/helpers/geoblacklight_helper.rb +57 -6
- data/app/javascript/geoblacklight/controllers/overview_map_controller.js +143 -0
- data/app/javascript/geoblacklight/core.js +8 -10
- data/app/javascript/geoblacklight/index.js +9 -12
- data/app/javascript/geoblacklight/initializers/field_truncation.js +62 -0
- data/app/javascript/geoblacklight/initializers/metadata_download.js +3 -8
- data/app/javascript/geoblacklight/initializers/truncation.js +7 -3
- data/app/javascript/geoblacklight/initializers/viewer_requests.js +61 -0
- data/app/javascript/geoblacklight/initializers/viewer_theme.js +28 -0
- data/app/models/concerns/geoblacklight/solr_document/thumbnail.rb +38 -0
- data/app/models/concerns/geoblacklight/solr_document.rb +2 -0
- data/app/models/concerns/geoblacklight/suppressed_records_search_behavior.rb +1 -1
- data/app/views/catalog/_document_split.html.erb +10 -10
- data/app/views/catalog/_home_text.html.erb +1 -1
- data/app/views/catalog/metadata.html.erb +1 -1
- data/app/views/{relation → relations}/index.html.erb +1 -1
- data/app/views/relations/index.json.jbuilder +9 -0
- data/config/importmap.rb +8 -8
- data/config/locales/geoblacklight.en.yml +48 -99
- data/geoblacklight.gemspec +3 -3
- data/lib/generators/geoblacklight/assets_generator.rb +2 -2
- data/lib/generators/geoblacklight/install_generator.rb +3 -6
- data/lib/generators/geoblacklight/templates/assets/customizations.css +5 -1
- data/lib/generators/geoblacklight/templates/catalog_controller.rb +17 -27
- data/lib/generators/geoblacklight/templates/settings.yml +15 -95
- data/lib/geoblacklight/configuration/display_note_shown_config.rb +3 -0
- data/lib/geoblacklight/configuration/relationship_config.rb +7 -1
- data/lib/geoblacklight/configuration/relationships_config.rb +15 -15
- data/lib/geoblacklight/configuration/settings_attributes.rb +85 -0
- data/lib/geoblacklight/configuration/settings_builder.rb +3 -14
- data/lib/geoblacklight/configuration.rb +23 -62
- data/lib/geoblacklight/exceptions.rb +4 -0
- data/lib/geoblacklight/reference.rb +0 -2
- data/lib/geoblacklight/references.rb +17 -5
- data/lib/geoblacklight/relations/ancestors.rb +12 -0
- data/lib/geoblacklight/relations/descendants.rb +11 -0
- data/lib/geoblacklight/relations/query.rb +39 -0
- data/lib/geoblacklight/{relation → relations}/relation_response.rb +5 -4
- data/lib/geoblacklight/routes/exportable.rb +2 -1
- data/lib/geoblacklight/version.rb +1 -1
- data/lib/geoblacklight/viewer_record.rb +39 -0
- data/lib/geoblacklight.rb +1 -1
- data/lib/tasks/geoblacklight.rake +1 -17
- data/lib/tasks/solr.rb +37 -0
- data/schema/readme.md +1 -1
- data/script/wait-for-solr +36 -0
- data/solr/conf/solrconfig.xml +10 -16
- data/spec/components/geoblacklight/document/sidebar_component_spec.rb +3 -3
- data/spec/components/geoblacklight/document_component_spec.rb +8 -34
- data/spec/components/geoblacklight/{header_icons_component_spec.rb → header_badges_component_spec.rb} +20 -2
- data/spec/components/geoblacklight/item_map_viewer_component_spec.rb +65 -15
- data/spec/components/geoblacklight/locator_map_component_spec.rb +54 -0
- data/spec/components/geoblacklight/login_link_component_spec.rb +20 -12
- data/spec/components/geoblacklight/metadata_description_markdown_component_spec.rb +1 -6
- data/spec/components/geoblacklight/overview_map_component_spec.rb +136 -0
- data/spec/components/geoblacklight/relations/relation_component_spec.rb +98 -0
- data/spec/components/geoblacklight/relations/relations_component_spec.rb +146 -0
- data/spec/components/geoblacklight/search_result_component_spec.rb +66 -39
- data/spec/components/geoblacklight/static_map_component_spec.rb +8 -2
- data/spec/components/geoblacklight/truncatable_metadata_field_layout_component_spec.rb +83 -0
- data/spec/controllers/catalog_controller_spec.rb +22 -7
- data/spec/controllers/{relation_controller_spec.rb → relations_controller_spec.rb} +1 -1
- data/spec/factories/user.rb +6 -1
- data/spec/features/axe_spec.rb +5 -3
- data/spec/features/home_page_spec.rb +8 -7
- data/spec/features/index_view_spec.rb +6 -20
- data/spec/features/layer_preview_spec.rb +4 -11
- data/spec/features/locator_map_spec.rb +31 -0
- data/spec/features/relations_spec.rb +22 -11
- data/spec/features/restricted_viewer_spec.rb +14 -0
- data/spec/features/search_results_map_spec.rb +89 -13
- data/spec/features/search_spec.rb +6 -2
- data/spec/features/sign_in_spec.rb +26 -0
- data/spec/features/tilejson_spec.rb +1 -3
- data/spec/features/tms_spec.rb +2 -2
- data/spec/features/truncated_metadata_spec.rb +77 -0
- data/spec/features/viewer_requests_spec.rb +40 -0
- data/spec/features/viewer_theme_spec.rb +51 -0
- data/spec/features/wmts_spec.rb +6 -6
- data/spec/features/xyz_spec.rb +3 -3
- data/spec/fixtures/solr_documents/README.md +18 -14
- data/spec/fixtures/solr_documents/actual-polygon1.json +22 -18
- data/spec/fixtures/solr_documents/b1g_wabash_child_15.json +14 -15
- data/spec/fixtures/solr_documents/b1g_wabash_child_16.json +14 -15
- data/spec/fixtures/solr_documents/b1g_wabash_child_17.json +14 -15
- data/spec/fixtures/solr_documents/b1g_wabash_child_18.json +14 -15
- data/spec/fixtures/solr_documents/b1g_wabash_parent.json +14 -15
- data/spec/fixtures/solr_documents/esri-dynamic-layer-single-layer.json +2 -2
- data/spec/fixtures/solr_documents/esri-feature-layer.json +1 -1
- data/spec/fixtures/solr_documents/esri-image-map-layer.json +20 -20
- data/spec/fixtures/solr_documents/esri-wms-layer.json +22 -24
- data/spec/fixtures/solr_documents/index_map_point.json +3 -3
- data/spec/fixtures/solr_documents/public_polygon.json +28 -0
- data/spec/helpers/geoblacklight_helper_spec.rb +103 -25
- data/spec/lib/geoblacklight/bounding_box_spec.rb +16 -0
- data/spec/lib/geoblacklight/configuration/relationships_config_spec.rb +121 -0
- data/spec/lib/geoblacklight/configuration/settings_attributes_spec.rb +113 -0
- data/spec/lib/geoblacklight/configuration/settings_builder_spec.rb +33 -4
- data/spec/lib/geoblacklight/references_spec.rb +35 -0
- data/spec/lib/geoblacklight/{relation → relations}/ancestors_spec.rb +17 -6
- data/spec/lib/geoblacklight/{relation → relations}/descendants_spec.rb +14 -7
- data/spec/lib/geoblacklight/{relation → relations}/relation_response_spec.rb +31 -17
- data/spec/lib/geoblacklight/viewer_record_spec.rb +69 -0
- data/spec/models/concerns/geoblacklight/solr_document/thumbnail_spec.rb +149 -0
- data/spec/models/concerns/geoblacklight/solr_document_spec.rb +1 -1
- data/spec/requests/bookmarks_spec.rb +2 -1
- data/spec/requests/catalog_show_spec.rb +24 -2
- data/spec/requests/index_view_spec.rb +50 -2
- data/spec/requests/missing_metadata_spec.rb +1 -1
- data/spec/requests/relations_spec.rb +9 -2
- data/spec/requests/search_results_spec.rb +6 -6
- data/spec/requests/viewer_record_spec.rb +37 -0
- data/spec/requests/viewer_requests_spec.rb +52 -0
- data/spec/solr_config_spec.rb +41 -0
- data/spec/solr_fixtures_spec.rb +23 -0
- data/spec/spec_helper.rb +13 -3
- data/spec/support/authentication.rb +37 -0
- data/spec/support/features/map_helpers.rb +68 -0
- data/spec/support/features/session_helpers.rb +18 -13
- data/spec/support/features/viewer_helpers.rb +20 -0
- data/spec/support/features.rb +4 -0
- data/spec/test_app_templates/lib/generators/test_app_generator.rb +1 -1
- data/template.rb +1 -1
- metadata +72 -84
- data/app/components/geoblacklight/accordion_component.html.erb +0 -16
- data/app/components/geoblacklight/accordion_component.rb +0 -15
- data/app/components/geoblacklight/attribute_table_component.html.erb +0 -15
- data/app/components/geoblacklight/attribute_table_component.rb +0 -17
- data/app/components/geoblacklight/header_icons_component.html.erb +0 -3
- data/app/components/geoblacklight/header_icons_component.rb +0 -27
- data/app/components/geoblacklight/index_map_inspect_component.html.erb +0 -12
- data/app/components/geoblacklight/index_map_inspect_component.rb +0 -15
- data/app/components/geoblacklight/index_map_legend_component.html.erb +0 -14
- data/app/components/geoblacklight/index_map_legend_component.rb +0 -24
- data/app/components/geoblacklight/location_leaflet_map_component.rb +0 -68
- data/app/components/geoblacklight/relations_component.html.erb +0 -23
- data/app/components/geoblacklight/relations_component.rb +0 -25
- data/app/components/geoblacklight/viewer_help_text_component.html.erb +0 -1
- data/app/components/geoblacklight/viewer_help_text_component.rb +0 -46
- data/app/controllers/wms_controller.rb +0 -17
- data/app/javascript/geoblacklight/controllers/leaflet_viewer_controller.js +0 -240
- data/app/javascript/geoblacklight/controllers/openlayers_viewer_controller.js +0 -74
- data/app/javascript/geoblacklight/controllers/search_results_controller.js +0 -50
- data/app/javascript/geoblacklight/initializers/popovers.js +0 -11
- data/app/javascript/geoblacklight/initializers/tooltips.js +0 -51
- data/app/javascript/geoblacklight/leaflet/basemaps.js +0 -73
- data/app/javascript/geoblacklight/leaflet/constants.js +0 -18
- data/app/javascript/geoblacklight/leaflet/controls/geosearch.js +0 -168
- data/app/javascript/geoblacklight/leaflet/controls/layer_opacity.js +0 -121
- data/app/javascript/geoblacklight/leaflet/controls/sidebar.js +0 -49
- data/app/javascript/geoblacklight/leaflet/controls/sleep.js +0 -101
- data/app/javascript/geoblacklight/leaflet/inspection.js +0 -210
- data/app/javascript/geoblacklight/leaflet/layer_index_map.js +0 -128
- data/app/javascript/geoblacklight/leaflet/layer_wmts.js +0 -57
- data/app/javascript/geoblacklight/leaflet/layers.js +0 -146
- data/app/javascript/geoblacklight/leaflet/utils.js +0 -83
- data/app/javascript/geoblacklight/openlayers/basemaps.js +0 -55
- data/app/javascript/geoblacklight/openlayers/inspection.js +0 -42
- data/app/javascript/geoblacklight/openlayers/layers.js +0 -44
- data/app/views/relation/index.json.jbuilder +0 -8
- data/lib/geoblacklight/configuration/layer_config.rb +0 -28
- data/lib/geoblacklight/configuration/leaflet_config.rb +0 -73
- data/lib/geoblacklight/configuration/leaflet_layers_config.rb +0 -33
- data/lib/geoblacklight/configuration/leaflet_sleep_config.rb +0 -20
- data/lib/geoblacklight/relation/ancestors.rb +0 -31
- data/lib/geoblacklight/relation/descendants.rb +0 -31
- data/lib/geoblacklight/routes/wms.rb +0 -15
- data/lib/geoblacklight/wms_layer/feature_info_response.rb +0 -37
- data/lib/geoblacklight/wms_layer.rb +0 -34
- data/spec/components/geoblacklight/attribute_table_component_spec.rb +0 -36
- data/spec/components/geoblacklight/index_map_legend_component_spec.rb +0 -29
- data/spec/components/geoblacklight/viewer_help_text_component_spec.rb +0 -27
- data/spec/controllers/wms_controller_spec.rb +0 -34
- data/spec/features/configurable_basemap_spec.rb +0 -40
- data/spec/features/full_screen_control_spec.rb +0 -15
- data/spec/features/layer_inspection_spec.rb +0 -12
- data/spec/features/layer_opacity_spec.rb +0 -16
- data/spec/fixtures/solr_documents/harvard_raster.json +0 -39
- data/spec/fixtures/solr_documents/public_polygon_mit.json +0 -23
- data/spec/fixtures/solr_documents/uva_slug_colon.json +0 -29
- data/spec/javascripts/geoblacklight_spec.js +0 -11
- data/spec/lib/geoblacklight/configuration/layer_config_spec.rb +0 -71
- data/spec/lib/geoblacklight/configuration/leaflet_config_spec.rb +0 -129
- data/spec/lib/geoblacklight/configuration/leaflet_layers_config_spec.rb +0 -63
- data/spec/lib/geoblacklight/configuration_spec.rb +0 -48
- data/spec/lib/geoblacklight/wms_layer/feature_info_response_spec.rb +0 -54
- data/spec/lib/geoblacklight/wms_layer_spec.rb +0 -115
- /data/app/components/geoblacklight/{location_leaflet_map_component.html.erb → locator_map_component.html.erb} +0 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "Viewer record", type: :request do
|
|
6
|
+
let(:references) { JSON.parse(response.parsed_body[Geoblacklight.configuration.fields.references]) }
|
|
7
|
+
|
|
8
|
+
# Restricted, and provided by this app's own institution, so signing in makes the difference
|
|
9
|
+
let(:restricted) { "stanford-dp018hs9766" }
|
|
10
|
+
|
|
11
|
+
it "withholds a restricted record's data from a reader who isn't signed in" do
|
|
12
|
+
get viewer_solr_document_path(restricted)
|
|
13
|
+
|
|
14
|
+
expect(response).to have_http_status(:ok)
|
|
15
|
+
expect(references.keys).not_to include("https://github.com/cogeotiff/cog-spec", "http://schema.org/downloadUrl")
|
|
16
|
+
expect(references.keys).to include("http://www.isotc211.org/schemas/2005/gmd/")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "hands over the same record once that reader signs in" do
|
|
20
|
+
sign_in
|
|
21
|
+
get viewer_solr_document_path(restricted)
|
|
22
|
+
|
|
23
|
+
expect(references.keys).to include("https://github.com/cogeotiff/cog-spec", "http://schema.org/downloadUrl")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "hands over a public record in full" do
|
|
27
|
+
get viewer_solr_document_path("berkeley-s7pq31")
|
|
28
|
+
|
|
29
|
+
expect(references.keys).to include("http://www.opengis.net/def/serviceType/ogc/wms")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "reports a record that isn't there as missing" do
|
|
33
|
+
get viewer_solr_document_path("no-such-record")
|
|
34
|
+
|
|
35
|
+
expect(response).to have_http_status(:not_found)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "Viewer requests", type: :request do
|
|
6
|
+
let(:origins) { ["https://stacks.stanford.edu/"] }
|
|
7
|
+
|
|
8
|
+
# Restricted, and provided by this app's own institution, so signing in makes the difference
|
|
9
|
+
let(:restricted) { "stanford-dp018hs9766" }
|
|
10
|
+
|
|
11
|
+
around do |example|
|
|
12
|
+
configured = Geoblacklight.configuration.restricted_origins
|
|
13
|
+
Geoblacklight.configuration.restricted_origins = origins
|
|
14
|
+
example.run
|
|
15
|
+
Geoblacklight.configuration.restricted_origins = configured
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def restricted_origins
|
|
19
|
+
Capybara.string(response.body).find("ogm-viewer")["data-restricted-origins"]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "names the services a signed-in reader's requests need to carry cookies to" do
|
|
23
|
+
sign_in
|
|
24
|
+
get solr_document_path(restricted)
|
|
25
|
+
|
|
26
|
+
expect(JSON.parse(restricted_origins)).to eq origins
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "names none for a reader who isn't signed in" do
|
|
30
|
+
get solr_document_path(restricted)
|
|
31
|
+
|
|
32
|
+
expect(restricted_origins).to be_nil
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "names none for a public record, which has nothing that needs authorizing" do
|
|
36
|
+
sign_in
|
|
37
|
+
get solr_document_path("berkeley-s7pq31")
|
|
38
|
+
|
|
39
|
+
expect(restricted_origins).to be_nil
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
context "when no services are configured" do
|
|
43
|
+
let(:origins) { [] }
|
|
44
|
+
|
|
45
|
+
it "names none" do
|
|
46
|
+
sign_in
|
|
47
|
+
get solr_document_path(restricted)
|
|
48
|
+
|
|
49
|
+
expect(restricted_origins).to be_nil
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "Solr configuration" do
|
|
6
|
+
def parse(name)
|
|
7
|
+
Nokogiri::XML(File.read(File.expand_path("../solr/conf/#{name}", __dir__)))
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
let(:schema) { parse("schema.xml") }
|
|
11
|
+
let(:solrconfig) { parse("solrconfig.xml") }
|
|
12
|
+
|
|
13
|
+
# Every *_ti and *_tmi name matches a dynamicField, so boosting a field that no
|
|
14
|
+
# copyField populates is legal, queryable, and always empty -- Solr never
|
|
15
|
+
# complains, the boost just silently contributes nothing to scoring. That has
|
|
16
|
+
# happened twice now: renaming a copyField destination from *_ti to *_tmi
|
|
17
|
+
# (multiValued sources need multiValued destinations) left the matching qf/pf
|
|
18
|
+
# entry behind. Deliberately ignores dynamicFields, since resolving through one
|
|
19
|
+
# is exactly the bug being guarded against.
|
|
20
|
+
def populated_fields
|
|
21
|
+
schema.xpath("//fields/field/@name").map(&:value) +
|
|
22
|
+
schema.xpath("//copyField/@dest").map(&:value)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Reads the /text() children rather than the <str> node's own text, so the XML
|
|
26
|
+
# comment inside <str name="pf"> cannot leak words into the field list.
|
|
27
|
+
def boosted_fields(param)
|
|
28
|
+
solrconfig.xpath("//requestHandler[@name='/select']//str[@name='#{param}']/text()")
|
|
29
|
+
.map(&:text).join(" ")
|
|
30
|
+
.split.map { |entry| entry.split("^").first }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
%w[qf pf].each do |param|
|
|
34
|
+
it "only boosts #{param} fields that something populates" do
|
|
35
|
+
dead = boosted_fields(param) - populated_fields
|
|
36
|
+
|
|
37
|
+
expect(dead).to be_empty,
|
|
38
|
+
"#{param} boosts fields no copyField or <field> populates: #{dead.join(", ")}"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "Solr fixture documents" do
|
|
6
|
+
def documents
|
|
7
|
+
Dir[File.expand_path("fixtures/solr_documents/*.json", __dir__)].flat_map do |path|
|
|
8
|
+
parsed = JSON.parse(File.read(path))
|
|
9
|
+
Array.wrap(parsed).map { |document| [File.basename(path), document] }
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# geoblacklight:index:seed adds these documents to Solr, which upserts on id. Two
|
|
14
|
+
# fixtures sharing an id overwrite one another silently, so the index comes up
|
|
15
|
+
# short and the losing record is simply absent from every spec that expects it.
|
|
16
|
+
it "gives every document a unique id" do
|
|
17
|
+
duplicates = documents.group_by { |_file, document| document["id"] }
|
|
18
|
+
.select { |_id, entries| entries.length > 1 }
|
|
19
|
+
.transform_values { |entries| entries.map(&:first).sort }
|
|
20
|
+
|
|
21
|
+
expect(duplicates).to be_empty, "fixtures share an id: #{duplicates.inspect}"
|
|
22
|
+
end
|
|
23
|
+
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -16,7 +16,7 @@ SimpleCov.start "rails" do
|
|
|
16
16
|
add_filter "lib/geoblacklight/version.rb"
|
|
17
17
|
add_filter "lib/geoblacklight/engine.rb"
|
|
18
18
|
add_filter "lib/generators"
|
|
19
|
-
add_filter "lib/tasks
|
|
19
|
+
add_filter "lib/tasks"
|
|
20
20
|
add_filter "/spec"
|
|
21
21
|
add_filter ".internal_test_app/"
|
|
22
22
|
minimum_coverage 100
|
|
@@ -40,10 +40,20 @@ WebMock.allow_net_connect!(net_http_connect_on_start: true)
|
|
|
40
40
|
Capybara.register_driver :chrome_headless do |app|
|
|
41
41
|
# Set up Chrome options
|
|
42
42
|
options = Selenium::WebDriver::Chrome::Options.new
|
|
43
|
-
options.add_argument("--headless")
|
|
43
|
+
options.add_argument("--headless=new")
|
|
44
44
|
options.add_argument("--disable-gpu")
|
|
45
45
|
options.add_argument("--no-sandbox")
|
|
46
46
|
options.add_argument("--window-size=1280,1024")
|
|
47
|
+
# Scroll without animating. Bootstrap puts `scroll-behavior: smooth` on :root, so when
|
|
48
|
+
# ChromeDriver sets scrollTop to bring an element into view before clicking it, the page only
|
|
49
|
+
# starts to move and ChromeDriver reads back the element's old, off-screen position. The click
|
|
50
|
+
# then fails and is left to Capybara's retry, which a busy page can outlast.
|
|
51
|
+
options.add_argument("--disable-smooth-scrolling")
|
|
52
|
+
# Software WebGL, which the viewer and the overview map both need: they draw with MapLibre, and
|
|
53
|
+
# headless Chrome has no GPU to give them. SwiftShader is Chrome's own CPU renderer, and since
|
|
54
|
+
# Chrome 128 it has to be asked for by name before WebGL will fall back to it at all.
|
|
55
|
+
options.add_argument("--enable-unsafe-swiftshader")
|
|
56
|
+
options.add_argument("--use-angle=swiftshader")
|
|
47
57
|
# Return from #visit at DOMContentLoaded instead of waiting for the full
|
|
48
58
|
# window `load` event. GeoBlacklight pages pull basemap tiles and WMS/TMS
|
|
49
59
|
# layers from external hosts; in CI those requests can hang and prevent
|
|
@@ -72,6 +82,6 @@ RSpec.configure do |config|
|
|
|
72
82
|
|
|
73
83
|
config.use_transactional_fixtures = true
|
|
74
84
|
|
|
75
|
-
config.include Devise::Test::ControllerHelpers, type: :controller
|
|
85
|
+
config.include Devise::Test::ControllerHelpers, type: :controller if defined?(Devise)
|
|
76
86
|
config.include ViewComponent::TestHelpers, type: :component
|
|
77
87
|
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# The app can be generated using Devise auth, Rails auth, or no auth. These
|
|
4
|
+
# are for signing in and/or skipping tests depending on which is the case.
|
|
5
|
+
module AuthenticationHelpers
|
|
6
|
+
# :devise, :rails, or nil if the app has no authentication
|
|
7
|
+
def self.provider
|
|
8
|
+
if defined?(Devise)
|
|
9
|
+
:devise
|
|
10
|
+
elsif ApplicationController.respond_to?(:allow_unauthenticated_access)
|
|
11
|
+
:rails
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def skip_without_authentication
|
|
16
|
+
skip "the test app has no authentication" unless AuthenticationHelpers.provider
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
module Requests
|
|
20
|
+
# Sign a new user in by posting the sign-in form
|
|
21
|
+
def sign_in
|
|
22
|
+
skip_without_authentication
|
|
23
|
+
user = FactoryBot.create(:user)
|
|
24
|
+
|
|
25
|
+
if AuthenticationHelpers.provider == :devise
|
|
26
|
+
post user_session_path, params: {user: {email: user.email, password: user.password}}
|
|
27
|
+
else
|
|
28
|
+
post session_path, params: {email_address: user.email_address, password: user.password}
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
RSpec.configure do |config|
|
|
35
|
+
config.include AuthenticationHelpers
|
|
36
|
+
config.include AuthenticationHelpers::Requests, type: :request
|
|
37
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Features
|
|
4
|
+
module MapHelpers
|
|
5
|
+
# An overview or locator map, once it has something on it. Both are handed what to draw a moment
|
|
6
|
+
# after the page arrives - a locator fetches its own record, and an overview's controller imports
|
|
7
|
+
# the library it draws with - so anything that reads or points at a map has to wait for that
|
|
8
|
+
# rather than for the page.
|
|
9
|
+
def map_ready(selector = "#overview-map", wait: 20)
|
|
10
|
+
map = find(selector)
|
|
11
|
+
expect(map.shadow_root).to have_css("canvas.maplibregl-canvas", wait: wait)
|
|
12
|
+
|
|
13
|
+
# And until there is something to draw, which may well be nothing: a map with no results to
|
|
14
|
+
# put on it - the home page's - has still been given its records.
|
|
15
|
+
page.document.synchronize(wait) do
|
|
16
|
+
given = page.evaluate_script(
|
|
17
|
+
"arguments[0].record !== undefined || arguments[0].previewers !== undefined || arguments[0].previewer !== undefined", map
|
|
18
|
+
)
|
|
19
|
+
raise(Capybara::ElementNotFound) unless given
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
map
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# What the map has been given to draw, as a west/south, east/north pair per result, in the order
|
|
26
|
+
# their numbers put them, with nil where a result had nowhere to be drawn. A locator holds one
|
|
27
|
+
# record rather than a list, and reports the same shape around it: a single-entry array, worked
|
|
28
|
+
# out from the record's own geometry rather than asked of a previewer, since none is exposed.
|
|
29
|
+
def drawn_extents(selector = "#overview-map")
|
|
30
|
+
page.evaluate_async_script(<<~JS, map_ready(selector))
|
|
31
|
+
const [map, done] = arguments
|
|
32
|
+
|
|
33
|
+
if (map.record) {
|
|
34
|
+
const geometry = map.record.getGeometry()
|
|
35
|
+
const points = geometry?.coordinates?.[0] ?? []
|
|
36
|
+
if (!points.length) return done([null])
|
|
37
|
+
|
|
38
|
+
const lngs = points.map(([lng]) => lng)
|
|
39
|
+
const lats = points.map(([, lat]) => lat)
|
|
40
|
+
return done([[[Math.min(...lngs), Math.min(...lats)], [Math.max(...lngs), Math.max(...lats)]]])
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const drawn = map.previewers ?? [map.previewer]
|
|
44
|
+
Promise.all(Array.from(drawn, (previewer) => previewer?.getBounds() ?? null)).then(done)
|
|
45
|
+
JS
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Search the map the way a reader does: hold shift and drag a box over the middle of it. There is
|
|
49
|
+
# no control to click - the gesture is the whole of it - so this is a real mouse drag.
|
|
50
|
+
def search_map_area(selector = "#overview-map", size: 80)
|
|
51
|
+
map = map_ready(selector)
|
|
52
|
+
|
|
53
|
+
page.driver.browser.action
|
|
54
|
+
.move_to(map.native, -size / 2, -size / 2)
|
|
55
|
+
.key_down(:shift)
|
|
56
|
+
.click_and_hold
|
|
57
|
+
.move_by(size, size)
|
|
58
|
+
.release
|
|
59
|
+
.key_up(:shift)
|
|
60
|
+
.perform
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# The area the query on screen asked after, as the bbox parameter states it
|
|
64
|
+
def searched_bbox
|
|
65
|
+
URI.decode_www_form(URI(page.current_url).query.to_s).to_h["bbox"]
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -2,21 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
module Features
|
|
4
4
|
module SessionHelpers
|
|
5
|
-
def
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
def sign_in
|
|
6
|
+
skip_without_authentication
|
|
7
|
+
visit (AuthenticationHelpers.provider == :devise) ? new_user_session_path : new_session_path
|
|
8
|
+
fill_in_sign_in_form FactoryBot.create(:user)
|
|
9
|
+
# Wait for the persistent "Log Out" nav link rather than the one-shot
|
|
10
|
+
# "Signed in successfully." flash, which can render and then become
|
|
11
|
+
# non-visible before Capybara's default wait time elapses.
|
|
12
|
+
expect(page).to have_link "Log Out"
|
|
10
13
|
end
|
|
11
14
|
|
|
12
|
-
def
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
def fill_in_sign_in_form(user)
|
|
16
|
+
if AuthenticationHelpers.provider == :devise
|
|
17
|
+
fill_in "user_email", with: user.email
|
|
18
|
+
fill_in "user_password", with: user.password
|
|
19
|
+
click_button "Log in"
|
|
20
|
+
else
|
|
21
|
+
fill_in "email_address", with: user.email_address
|
|
22
|
+
fill_in "password", with: user.password
|
|
23
|
+
click_button "Sign in"
|
|
24
|
+
end
|
|
20
25
|
end
|
|
21
26
|
end
|
|
22
27
|
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Features
|
|
4
|
+
module ViewerHelpers
|
|
5
|
+
# What the item viewer offers to show, as the labels on its tabs. It works these out from the
|
|
6
|
+
# record's references once it has fetched the record, inside its own shadow root, so this waits
|
|
7
|
+
# for them rather than expecting them to be there when the page arrives.
|
|
8
|
+
def preview_tabs(wait: 20)
|
|
9
|
+
page.document.synchronize(wait) do
|
|
10
|
+
page.evaluate_script(<<~JS) || raise(Capybara::ElementNotFound)
|
|
11
|
+
(() => {
|
|
12
|
+
const previews = document.querySelector("ogm-viewer")?.shadowRoot?.querySelector("ogm-previews")
|
|
13
|
+
const tabs = previews?.shadowRoot?.querySelectorAll("wa-tab")
|
|
14
|
+
return tabs?.length ? Array.from(tabs).map((tab) => tab.textContent.trim()) : null
|
|
15
|
+
})()
|
|
16
|
+
JS
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
data/spec/support/features.rb
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require File.expand_path("../features/session_helpers.rb", __FILE__)
|
|
4
|
+
require File.expand_path("../features/map_helpers.rb", __FILE__)
|
|
5
|
+
require File.expand_path("../features/viewer_helpers.rb", __FILE__)
|
|
4
6
|
|
|
5
7
|
RSpec.configure do |config|
|
|
6
8
|
config.include Features::SessionHelpers, type: :feature
|
|
9
|
+
config.include Features::MapHelpers, type: :feature
|
|
10
|
+
config.include Features::ViewerHelpers, type: :feature
|
|
7
11
|
end
|
|
@@ -16,7 +16,7 @@ class TestAppGenerator < Rails::Generators::Base
|
|
|
16
16
|
# Install blacklight
|
|
17
17
|
def run_blacklight_generator
|
|
18
18
|
say_status("warning", "GENERATING BL", :yellow)
|
|
19
|
-
generate "blacklight:install", "
|
|
19
|
+
generate "blacklight:install", ENV["BLACKLIGHT_INSTALL_OPTIONS"].presence || "--devise --skip-solr"
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
# Install geoblacklight
|
data/template.rb
CHANGED
|
@@ -10,7 +10,7 @@ end
|
|
|
10
10
|
run "bundle install"
|
|
11
11
|
|
|
12
12
|
after_bundle do
|
|
13
|
-
generate "blacklight:install", ENV
|
|
13
|
+
generate "blacklight:install", ENV["BLACKLIGHT_INSTALL_OPTIONS"].presence || "--devise --skip-solr"
|
|
14
14
|
generate "geoblacklight:install"
|
|
15
15
|
rails_command "db:migrate"
|
|
16
16
|
end
|