decidim-dev 0.20.1 → 0.23.1

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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/images/decidim/dummy.svg +1 -4
  3. data/app/assets/images/decidim/gamification/badges/test.svg +1 -145
  4. data/app/commands/decidim/dummy_resources/create_dummy_resource.rb +48 -0
  5. data/app/controllers/decidim/dummy_resources/dummy_resources_controller.rb +1 -0
  6. data/app/forms/decidim/dummy_resources/dummy_resource_form.rb +3 -0
  7. data/app/views/decidim/dummy_resources/dummy_resources/show.html.erb +4 -0
  8. data/config/locales/am-ET.yml +1 -0
  9. data/config/locales/bg-BG.yml +56 -0
  10. data/config/locales/bg.yml +56 -0
  11. data/config/locales/ca.yml +15 -0
  12. data/config/locales/cs.yml +16 -1
  13. data/config/locales/da-DK.yml +1 -0
  14. data/config/locales/da.yml +1 -0
  15. data/config/locales/de.yml +16 -0
  16. data/config/locales/el.yml +56 -0
  17. data/config/locales/en.yml +15 -0
  18. data/config/locales/eo.yml +1 -0
  19. data/config/locales/es-MX.yml +15 -0
  20. data/config/locales/es-PY.yml +15 -0
  21. data/config/locales/es.yml +15 -0
  22. data/config/locales/et-EE.yml +1 -0
  23. data/config/locales/et.yml +1 -0
  24. data/config/locales/fi-plain.yml +15 -0
  25. data/config/locales/fi.yml +15 -0
  26. data/config/locales/fr-CA.yml +56 -0
  27. data/config/locales/fr.yml +15 -0
  28. data/config/locales/ga-IE.yml +1 -0
  29. data/config/locales/hr-HR.yml +1 -0
  30. data/config/locales/hr.yml +1 -0
  31. data/config/locales/hu.yml +9 -0
  32. data/config/locales/is.yml +21 -0
  33. data/config/locales/it.yml +17 -2
  34. data/config/locales/ja-JP.yml +56 -0
  35. data/config/locales/ja.yml +56 -0
  36. data/config/locales/ko-KR.yml +1 -0
  37. data/config/locales/ko.yml +1 -0
  38. data/config/locales/lt-LT.yml +1 -0
  39. data/config/locales/lt.yml +1 -0
  40. data/config/locales/lv.yml +54 -0
  41. data/config/locales/mt-MT.yml +1 -0
  42. data/config/locales/mt.yml +1 -0
  43. data/config/locales/nl.yml +15 -0
  44. data/config/locales/om-ET.yml +1 -0
  45. data/config/locales/pl.yml +26 -10
  46. data/config/locales/pt-BR.yml +1 -1
  47. data/config/locales/pt.yml +26 -10
  48. data/config/locales/ro-RO.yml +56 -0
  49. data/config/locales/sk-SK.yml +47 -0
  50. data/config/locales/sk.yml +47 -0
  51. data/config/locales/sl.yml +5 -0
  52. data/config/locales/so-SO.yml +1 -0
  53. data/config/locales/sr-CS.yml +8 -0
  54. data/config/locales/sv.yml +15 -0
  55. data/config/locales/ti-ER.yml +1 -0
  56. data/config/locales/vi-VN.yml +1 -0
  57. data/config/locales/vi.yml +1 -0
  58. data/config/locales/zh-CN.yml +56 -0
  59. data/config/locales/zh-TW.yml +1 -0
  60. data/lib/decidim/dev.rb +2 -0
  61. data/lib/decidim/dev/assets/assemblies.json +816 -0
  62. data/lib/decidim/dev/assets/assemblies_with_null.json +135 -0
  63. data/lib/decidim/dev/assets/city.jpeg +0 -0
  64. data/lib/decidim/dev/assets/city2.jpeg +0 -0
  65. data/lib/decidim/dev/assets/city3.jpeg +0 -0
  66. data/lib/decidim/dev/assets/geocoder_result_here.json +72 -0
  67. data/lib/decidim/dev/assets/geocoder_result_osm.json +150 -0
  68. data/lib/decidim/dev/assets/import_participatory_space_private_users_nok.csv +2 -0
  69. data/lib/decidim/dev/dummy_translator.rb +32 -0
  70. data/lib/decidim/dev/test/base_spec_helper.rb +14 -0
  71. data/lib/decidim/dev/test/rspec_support/authorization.rb +3 -0
  72. data/lib/decidim/dev/test/rspec_support/capybara.rb +6 -2
  73. data/lib/decidim/dev/test/rspec_support/capybara_data_picker.rb +15 -5
  74. data/lib/decidim/dev/test/rspec_support/capybara_scopes_picker.rb +5 -5
  75. data/lib/decidim/dev/test/rspec_support/comments.rb +2 -2
  76. data/lib/decidim/dev/test/rspec_support/component.rb +104 -5
  77. data/lib/decidim/dev/test/rspec_support/component_context.rb +1 -2
  78. data/lib/decidim/dev/test/rspec_support/confirmation_helpers.rb +60 -0
  79. data/lib/decidim/dev/test/rspec_support/content_processing.rb +4 -4
  80. data/lib/decidim/dev/test/rspec_support/frontend.rb +20 -0
  81. data/lib/decidim/dev/test/rspec_support/geocoder.rb +150 -6
  82. data/lib/decidim/dev/test/rspec_support/helpers.rb +2 -2
  83. data/lib/decidim/dev/test/rspec_support/organization.rb +7 -0
  84. data/lib/decidim/dev/test/rspec_support/rake_tasks.rb +75 -0
  85. data/lib/decidim/dev/test/rspec_support/timezone.rb +7 -0
  86. data/lib/decidim/dev/test/rspec_support/translation_helpers.rb +2 -0
  87. data/lib/decidim/dev/version.rb +1 -1
  88. data/lib/tasks/generators.rake +2 -1
  89. data/lib/tasks/locale_checker.rake +1 -1
  90. metadata +91 -23
  91. data/lib/decidim/dev/test/rspec_support/capybara_proposals_picker.rb +0 -51
@@ -6,15 +6,13 @@ module Capybara
6
6
  Struct.new(:data_picker, :global_value).new(find_data_picker(id, multiple: multiple), global_value)
7
7
  end
8
8
 
9
- private
10
-
11
9
  def find_data_picker(id, multiple: nil)
12
10
  if multiple.nil?
13
- expect(page).to have_selector("div.data-picker##{id}")
11
+ expect(page).to have_selector("div.data-picker[id$='#{id}']")
14
12
  else
15
- expect(page).to have_selector("div.data-picker.picker-#{multiple ? "multiple" : "single"}##{id}")
13
+ expect(page).to have_selector("div.data-picker.picker-#{multiple ? "multiple" : "single"}[id$='#{id}']")
16
14
  end
17
- find("div.data-picker##{id}")
15
+ first("div.data-picker[id$='#{id}']")
18
16
  end
19
17
 
20
18
  def data_picker_pick_current
@@ -22,5 +20,17 @@ module Capybara
22
20
  expect(body).to have_selector("#data_picker-modal .picker-footer a[data-picker-choose]")
23
21
  body.find("#data_picker-modal .picker-footer a[data-picker-choose]").click
24
22
  end
23
+
24
+ def data_picker_choose_value(value)
25
+ body = find(:xpath, "//body")
26
+ expect(body).to have_selector("#data_picker-modal input[data-picker-choose][type=checkbox][value=\"#{value}\"]")
27
+ body.find("#data_picker-modal input[data-picker-choose][type=checkbox][value=\"#{value}\"]").click
28
+ end
29
+
30
+ def data_picker_close
31
+ body = find(:xpath, "//body")
32
+ expect(body).to have_selector("#data_picker-modal .picker-footer a[data-close]")
33
+ body.find("#data_picker-modal .picker-footer a[data-close]").click
34
+ end
25
35
  end
26
36
  end
@@ -10,7 +10,7 @@ module Capybara
10
10
  match do |scope_picker|
11
11
  data_picker = scope_picker.data_picker
12
12
  scope_name = expected ? translated(expected.name) : t("decidim.scopes.global")
13
- expect(data_picker).to have_selector(".picker-values div input[value='#{expected&.id || scope_picker.global_value}']", visible: false)
13
+ expect(data_picker).to have_selector(".picker-values div input[value='#{expected&.id || scope_picker.global_value}']", visible: :all)
14
14
  expect(data_picker).to have_selector(:xpath, "//div[contains(@class,'picker-values')]/div/a[text()[contains(.,'#{scope_name}')]]")
15
15
  end
16
16
  end
@@ -19,7 +19,7 @@ module Capybara
19
19
  match do |scope_picker|
20
20
  data_picker = scope_picker.data_picker
21
21
  scope_name = expected ? translated(expected.name) : t("decidim.scopes.global")
22
- expect(data_picker).not_to have_selector(".picker-values div input[value='#{expected&.id || scope_picker.global_value}']", visible: false)
22
+ expect(data_picker).not_to have_selector(".picker-values div input[value='#{expected&.id || scope_picker.global_value}']", visible: :all)
23
23
  expect(data_picker).not_to have_selector(:xpath, "//div[contains(@class,'picker-values')]/div/a[text()[contains(.,'#{scope_name}')]]")
24
24
  end
25
25
  end
@@ -27,7 +27,7 @@ module Capybara
27
27
  def scope_pick(scope_picker, scope)
28
28
  data_picker = scope_picker.data_picker
29
29
  # use scope_repick to change single scope picker selected scope
30
- expect(data_picker).to have_selector(".picker-values:empty", visible: false) if data_picker.has_css?(".picker-single")
30
+ expect(data_picker).to have_selector(".picker-values:empty", visible: :all) if data_picker.has_css?(".picker-single")
31
31
 
32
32
  expect(data_picker).to have_selector(".picker-prompt")
33
33
  data_picker.find(".picker-prompt").click
@@ -41,7 +41,7 @@ module Capybara
41
41
  def scope_repick(scope_picker, old_scope, new_scope)
42
42
  data_picker = scope_picker.data_picker
43
43
 
44
- expect(data_picker).to have_selector(".picker-values div input[value='#{old_scope&.id || scope_picker.global_value}']", visible: false)
44
+ expect(data_picker).to have_selector(".picker-values div input[value='#{old_scope&.id || scope_picker.global_value}']", visible: :all)
45
45
  data_picker.find(:xpath, "//div[contains(@class,'picker-values')]/div/input[@value='#{old_scope&.id || scope_picker.global_value}']/../a").click
46
46
 
47
47
  # browse to lowest common parent between old and new scope
@@ -57,7 +57,7 @@ module Capybara
57
57
  def scope_unpick(scope_picker, scope)
58
58
  data_picker = scope_picker.data_picker
59
59
 
60
- expect(data_picker).to have_selector(".picker-values div input[value='#{scope&.id || scope_picker.global_value}']", visible: false)
60
+ expect(data_picker).to have_selector(".picker-values div input[value='#{scope&.id || scope_picker.global_value}']", visible: :all)
61
61
  data_picker.find(".picker-values div input[value='#{scope&.id || scope_picker.global_value}']").click
62
62
 
63
63
  expect(scope_picker).to have_scope_not_picked(scope)
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CommentsHelpers
4
- def have_comment_from(user, text)
4
+ def have_comment_from(user, text, opts = {})
5
5
  within "#comments" do
6
- have_content(user.name) && have_content(text)
6
+ have_content(user.name, opts) && have_content(text, opts)
7
7
  end
8
8
  end
9
9
 
@@ -29,6 +29,7 @@ module Decidim
29
29
  root to: proc { [200, {}, ["DUMMY ENGINE"]] }
30
30
 
31
31
  resources :dummy_resources do
32
+ resources :nested_dummy_resources
32
33
  get :foo, on: :member
33
34
  end
34
35
  end
@@ -38,6 +39,10 @@ module Decidim
38
39
  engine_name "dummy_admin"
39
40
 
40
41
  routes do
42
+ resources :dummy_resources do
43
+ resources :nested_dummy_resources
44
+ end
45
+
41
46
  root to: proc { [200, {}, ["DUMMY ADMIN ENGINE"]] }
42
47
  end
43
48
  end
@@ -53,7 +58,7 @@ module Decidim
53
58
  include Reportable
54
59
  include Authorable
55
60
  include HasCategory
56
- include ScopableComponent
61
+ include ScopableResource
57
62
  include Decidim::Comments::Commentable
58
63
  include Followable
59
64
  include Traceable
@@ -63,8 +68,12 @@ module Decidim
63
68
  include Paddable
64
69
  include Amendable
65
70
  include Decidim::NewsletterParticipant
66
- include Hashtaggable
71
+ include ::Decidim::Endorsable
72
+ include Decidim::HasAttachments
73
+ include Decidim::ShareableWithToken
74
+ include Decidim::TranslatableResource
67
75
 
76
+ translatable_fields :title
68
77
  searchable_fields(
69
78
  scope_id: { scope: :id },
70
79
  participatory_space: { component: :participatory_space },
@@ -103,6 +112,44 @@ module Decidim
103
112
  .pluck(:decidim_author_id).flatten.compact.uniq
104
113
  end
105
114
  end
115
+
116
+ class NestedDummyResource < ApplicationRecord
117
+ include Decidim::Resourceable
118
+ belongs_to :dummy_resource
119
+ end
120
+
121
+ class CoauthorableDummyResource < ApplicationRecord
122
+ include ::Decidim::Coauthorable
123
+ include HasComponent
124
+ end
125
+
126
+ class OfficialAuthorPresenter
127
+ def name
128
+ self.class.name
129
+ end
130
+
131
+ def nickname
132
+ UserBaseEntity.nicknamize(name)
133
+ end
134
+
135
+ def deleted?
136
+ false
137
+ end
138
+
139
+ def respond_to_missing?
140
+ true
141
+ end
142
+
143
+ def method_missing(method, *args)
144
+ if method.to_s.ends_with?("?")
145
+ false
146
+ elsif [:avatar_url, :profile_path, :badge, :followers_count].include?(method)
147
+ ""
148
+ else
149
+ super
150
+ end
151
+ end
152
+ end
106
153
  end
107
154
  end
108
155
 
@@ -128,10 +175,14 @@ Decidim.register_component(:dummy) do |component|
128
175
  component.newsletter_participant_entities = ["Decidim::DummyResources::DummyResource"]
129
176
 
130
177
  component.settings(:global) do |settings|
178
+ settings.attribute :scopes_enabled, type: :boolean, default: false
179
+ settings.attribute :scope_id, type: :scope
131
180
  settings.attribute :comments_enabled, type: :boolean, default: true
181
+ settings.attribute :comments_max_length, type: :integer, required: false
132
182
  settings.attribute :resources_permissions_enabled, type: :boolean, default: true
133
183
  settings.attribute :dummy_global_attribute_1, type: :boolean
134
- settings.attribute :dummy_global_attribute_2, type: :boolean
184
+ settings.attribute :dummy_global_attribute_2, type: :boolean, readonly: ->(_context) { false }
185
+ settings.attribute :readonly_attribute, type: :boolean, default: true, readonly: ->(_context) { true }
135
186
  settings.attribute :enable_pads_creation, type: :boolean, default: false
136
187
  settings.attribute :amendments_enabled, type: :boolean, default: false
137
188
  settings.attribute :dummy_global_translatable_text, type: :text, translated: true, editor: true, required: true
@@ -140,12 +191,15 @@ Decidim.register_component(:dummy) do |component|
140
191
  component.settings(:step) do |settings|
141
192
  settings.attribute :comments_blocked, type: :boolean, default: false
142
193
  settings.attribute :dummy_step_attribute_1, type: :boolean
143
- settings.attribute :dummy_step_attribute_2, type: :boolean
194
+ settings.attribute :dummy_step_attribute_2, type: :boolean, readonly: ->(_context) { false }
144
195
  settings.attribute :dummy_step_translatable_text, type: :text, translated: true, editor: true, required: true
196
+ settings.attribute :readonly_step_attribute, type: :boolean, default: true, readonly: ->(_context) { true }
145
197
  settings.attribute :amendment_creation_enabled, type: :boolean, default: true
146
198
  settings.attribute :amendment_reaction_enabled, type: :boolean, default: true
147
199
  settings.attribute :amendment_promotion_enabled, type: :boolean, default: true
148
200
  settings.attribute :amendments_visibility, type: :string, default: "all"
201
+ settings.attribute :endorsements_enabled, type: :boolean, default: false
202
+ settings.attribute :endorsements_blocked, type: :boolean, default: false
149
203
  end
150
204
 
151
205
  component.register_resource(:dummy_resource) do |resource|
@@ -156,6 +210,19 @@ Decidim.register_component(:dummy) do |component|
156
210
  resource.searchable = true
157
211
  end
158
212
 
213
+ component.register_resource(:nested_dummy_resource) do |resource|
214
+ resource.name = :nested_dummy
215
+ resource.model_class_name = "Decidim::DummyResources::NestedDummyResource"
216
+ end
217
+
218
+ component.register_resource(:coauthorable_dummy_resource) do |resource|
219
+ resource.name = :coauthorable_dummy
220
+ resource.model_class_name = "Decidim::DummyResources::CoauthorableDummyResource"
221
+ resource.template = "decidim/coauthorabledummy_resource/linked_dummys"
222
+ resource.actions = %w(foo-coauthorable)
223
+ resource.searchable = false
224
+ end
225
+
159
226
  component.register_stat :dummies_count_high, primary: true, priority: Decidim::StatsRegistry::HIGH_PRIORITY do |components, _start_at, _end_at|
160
227
  components.count * 10
161
228
  end
@@ -179,13 +246,15 @@ RSpec.configure do |config|
179
246
  unless ActiveRecord::Base.connection.data_source_exists?("decidim_dummy_resources_dummy_resources")
180
247
  ActiveRecord::Migration.create_table :decidim_dummy_resources_dummy_resources do |t|
181
248
  t.jsonb :translatable_text
182
- t.string :title
249
+ t.jsonb :title
183
250
  t.string :body
184
251
  t.text :address
185
252
  t.float :latitude
186
253
  t.float :longitude
187
254
  t.datetime :published_at
188
255
  t.integer :coauthorships_count, null: false, default: 0
256
+ t.integer :endorsements_count, null: false, default: 0
257
+ t.integer :comments_count, null: false, default: 0
189
258
 
190
259
  t.references :decidim_component, index: false
191
260
  t.integer :decidim_author_id, index: false
@@ -195,6 +264,36 @@ RSpec.configure do |config|
195
264
  t.references :decidim_scope, index: false
196
265
  t.string :reference
197
266
 
267
+ t.timestamps
268
+ end
269
+ end
270
+ unless ActiveRecord::Base.connection.data_source_exists?("decidim_dummy_resources_nested_dummy_resources")
271
+ ActiveRecord::Migration.create_table :decidim_dummy_resources_nested_dummy_resources do |t|
272
+ t.jsonb :translatable_text
273
+ t.string :title
274
+
275
+ t.references :dummy_resource, index: false
276
+ t.timestamps
277
+ end
278
+ end
279
+ unless ActiveRecord::Base.connection.data_source_exists?("decidim_dummy_resources_coauthorable_dummy_resources")
280
+ ActiveRecord::Migration.create_table :decidim_dummy_resources_coauthorable_dummy_resources do |t|
281
+ t.jsonb :translatable_text
282
+ t.string :title
283
+ t.string :body
284
+ t.text :address
285
+ t.float :latitude
286
+ t.float :longitude
287
+ t.datetime :published_at
288
+ t.integer :coauthorships_count, null: false, default: 0
289
+ t.integer :endorsements_count, null: false, default: 0
290
+ t.integer :comments_count, null: false, default: 0
291
+
292
+ t.references :decidim_component, index: false
293
+ t.references :decidim_category, index: false
294
+ t.references :decidim_scope, index: false
295
+ t.string :reference
296
+
198
297
  t.timestamps
199
298
  end
200
299
  end
@@ -19,8 +19,7 @@ shared_context "with a component" do
19
19
  participatory_space: participatory_space)
20
20
  end
21
21
 
22
- let!(:category) { create :category, participatory_space: participatory_process }
23
-
22
+ let!(:category) { create :category, participatory_space: participatory_space }
24
23
  let!(:scope) { create :scope, organization: organization }
25
24
 
26
25
  before do
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Helpers that get automatically included in component specs.
4
+ module ConfirmationHelpers
5
+ # Overrides the Capybara default accept_confirm because we have replaced the
6
+ # system's own confirmation modal with foundation based modal.
7
+ #
8
+ # See:
9
+ # https://github.com/teamcapybara/capybara/blob/44621209496fe4dd352709799a0061a80d97d562/lib/capybara/session.rb#L647
10
+ def accept_confirm(_text = nil, **_options)
11
+ yield if block_given?
12
+
13
+ # The test can already be "within", so find the body using xpath
14
+ message = nil
15
+ body = find(:xpath, "/html/body")
16
+ within(body.find(".confirm-reveal")) do
17
+ message = find(".confirm-modal-content").text
18
+ find("a.button[data-confirm-ok]").click
19
+ end
20
+
21
+ message
22
+ end
23
+
24
+ # Overrides the Capybara default dismiss_confirm because we have replaced the
25
+ # system's own confirmation modal with foundation based modal.
26
+ #
27
+ # See:
28
+ # https://github.com/teamcapybara/capybara/blob/44621209496fe4dd352709799a0061a80d97d562/lib/capybara/session.rb#L657
29
+ def dismiss_confirm(_text = nil, **_options)
30
+ yield if block_given?
31
+
32
+ # The test can already be "within", so find the body using xpath
33
+ message = nil
34
+ body = find(:xpath, "/html/body")
35
+ within(body.find(".confirm-reveal")) do
36
+ message = find(".confirm-modal-content").text
37
+ find("a.button[data-confirm-cancel]").click
38
+ end
39
+
40
+ message
41
+ end
42
+
43
+ # Used to accept the "onbeforeunload" event's normal browser confirm modal
44
+ # as this cannot be overridden. Original confirm dismiss implementation in
45
+ # Capybara.
46
+ def accept_page_unload(text = nil, **options, &blk)
47
+ page.send(:accept_modal, :confirm, text, options, &blk)
48
+ end
49
+
50
+ # Used to dismiss the "onbeforeunload" event's normal browser confirm modal
51
+ # as this cannot be overridden. Original confirm dismiss implementation in
52
+ # Capybara.
53
+ def dismiss_page_unload(text = nil, **options, &blk)
54
+ page.send(:dismiss_modal, :confirm, text, options, &blk)
55
+ end
56
+ end
57
+
58
+ RSpec.configure do |config|
59
+ config.include ConfirmationHelpers, type: :system
60
+ end
@@ -3,7 +3,7 @@
3
3
  module Decidim
4
4
  class ContentParsers::DummyFooParser < ContentParsers::BaseParser
5
5
  def rewrite
6
- content.gsub("foo", "*lorem*")
6
+ content.gsub("foo", "%lorem%")
7
7
  end
8
8
 
9
9
  def metadata
@@ -12,8 +12,8 @@ module Decidim
12
12
  end
13
13
 
14
14
  class ContentRenderers::DummyFooRenderer < ContentRenderers::BaseRenderer
15
- def render
16
- content.gsub("*lorem*", "<em>neque dicta enim quasi</em>")
15
+ def render(_options = nil)
16
+ content.gsub("%lorem%", "<em>neque dicta enim quasi</em>")
17
17
  end
18
18
  end
19
19
 
@@ -28,7 +28,7 @@ module Decidim
28
28
  end
29
29
 
30
30
  class ContentRenderers::DummyBarRenderer < ContentRenderers::BaseRenderer
31
- def render
31
+ def render(_options = nil)
32
32
  content.gsub("*ipsum*", "<em>illo qui voluptas</em>")
33
33
  end
34
34
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FrontendHelpers
4
+ # Thanks to:
5
+ # https://medium.com/@coorasse/catch-javascript-errors-in-your-system-tests-89c2fe6773b1
6
+ def expect_no_js_errors
7
+ errors = page.driver.browser.manage.logs.get(:browser)
8
+ return if errors.blank?
9
+
10
+ aggregate_failures "javascript errors" do
11
+ errors.each do |error|
12
+ expect(error.level).not_to eq("SEVERE"), error.message
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ RSpec.configure do |config|
19
+ config.include FrontendHelpers, type: :system
20
+ end
@@ -8,6 +8,69 @@ module GeocoderHelpers
8
8
  address,
9
9
  result
10
10
  )
11
+ Decidim::Map::Provider::Autocomplete::Test.add_stub(
12
+ address,
13
+ coordinates
14
+ )
15
+ end
16
+
17
+ # Waits for the front-end geocoding request to finish in order to ensure there
18
+ # are no pending requests when proceeding.
19
+ def fill_in_geocoding(attribute, options = {})
20
+ fill_in attribute, options
21
+ expect(page).to have_selector(".tribute-container ul#results", count: 1)
22
+ end
23
+
24
+ module_function
25
+
26
+ public def configure_maps
27
+ # Set maps configuration in test mode
28
+ Decidim.maps = {
29
+ provider: :test,
30
+ api_key: "1234123412341234",
31
+ static: { url: "https://www.example.org/my_static_map" },
32
+ autocomplete: { url: "/photon_api" } # Locally drawn route for the tests
33
+ }
34
+ end
35
+ end
36
+
37
+ module Decidim::Map::Provider
38
+ module Geocoding
39
+ class Test < ::Decidim::Map::Geocoding; end
40
+ end
41
+ module Autocomplete
42
+ class Test < ::Decidim::Map::Autocomplete
43
+ def self.stubs
44
+ @stubs ||= []
45
+ end
46
+
47
+ def self.add_stub(address, coordinates)
48
+ stubs.push(
49
+ properties: address.is_a?(Hash) ? address : { street: address },
50
+ geometry: { coordinates: coordinates }
51
+ )
52
+ end
53
+
54
+ def self.clear_stubs
55
+ @stubs = []
56
+ end
57
+
58
+ def builder_options
59
+ { url: configuration.fetch(:url, nil) }.compact
60
+ end
61
+
62
+ class Builder < Decidim::Map::Autocomplete::Builder
63
+ def javascript_snippets
64
+ template.javascript_include_tag("decidim/geocoding/provider/photon")
65
+ end
66
+ end
67
+ end
68
+ end
69
+ module DynamicMap
70
+ class Test < ::Decidim::Map::DynamicMap; end
71
+ end
72
+ module StaticMap
73
+ class Test < ::Decidim::Map::StaticMap; end
11
74
  end
12
75
  end
13
76
 
@@ -15,16 +78,97 @@ RSpec.configure do |config|
15
78
  config.include GeocoderHelpers
16
79
 
17
80
  config.before(:suite) do
18
- # Set geocoder configuration in test mode
19
- Decidim.geocoder = {
20
- static_map_url: "https://www.example.org/my_static_map",
21
- here_api_key: "1234123412341234"
22
- }
23
- Geocoder.configure(lookup: :test)
81
+ GeocoderHelpers.configure_maps
82
+ end
83
+
84
+ config.after(:each, :configures_map) do
85
+ # Ensure the initializer is always re-run after the examples because
86
+ # otherwise the utilities could remain unregistered which causes issues with
87
+ # further tests.
88
+ Decidim::Core::Engine.initializers.each do |i|
89
+ next unless i.name == "decidim.maps"
90
+
91
+ i.run
92
+ break
93
+ end
94
+
95
+ # Ensure the utility configuration is reset after each example for it to be
96
+ # reloaded the next time.
97
+ Decidim::Map.reset_utility_configuration!
98
+ configure_maps
24
99
  end
25
100
 
26
101
  config.before(:each, :serves_map) do
27
102
  stub_request(:get, %r{https://www\.example\.org/my_static_map})
28
103
  .to_return(body: "map_data")
29
104
  end
105
+
106
+ config.before(:each, :serves_geocoding_autocomplete) do
107
+ # Clear the autocomplete stubs
108
+ Decidim::Map::Provider::Autocomplete::Test.clear_stubs
109
+
110
+ photon_response = lambda do
111
+ {
112
+ features: [
113
+ {
114
+ properties: {
115
+ name: "Park",
116
+ street: "Street1",
117
+ housenumber: "1",
118
+ postcode: "123456",
119
+ city: "City1",
120
+ state: "State1",
121
+ country: "Country1"
122
+ },
123
+ geometry: {
124
+ coordinates: [1.123, 2.234]
125
+ }
126
+ },
127
+ {
128
+ properties: {
129
+ street: "Street2",
130
+ postcode: "654321",
131
+ city: "City2",
132
+ country: "Country2"
133
+ },
134
+ geometry: {
135
+ coordinates: [3.345, 4.456]
136
+ }
137
+ },
138
+ {
139
+ properties: {
140
+ street: "Street3",
141
+ housenumber: "3",
142
+ postcode: "142536",
143
+ city: "City3",
144
+ country: "Country3"
145
+ },
146
+ geometry: {
147
+ coordinates: [5.567, 6.678]
148
+ }
149
+ }
150
+ ]
151
+ }.tap do |response|
152
+ Decidim::Map::Provider::Autocomplete::Test.stubs.length.positive? &&
153
+ response[:features] = Decidim::Map::Provider::Autocomplete::Test.stubs
154
+ end
155
+ end
156
+
157
+ # The Photon API path needs to be mounted in the application itself because
158
+ # otherwise we would have to run a separate server for the API itself.
159
+ # Mocking the request would not work here because the call to the Photon API
160
+ # is initialized by the front-end to the URL specified for the maps
161
+ # geocoding autocompletion configuration which is not proxied by the
162
+ # headless browser running the Capybara tests.
163
+ Rails.application.routes.disable_clear_and_finalize = true
164
+ Rails.application.routes.draw do
165
+ get "photon_api", to: ->(_) { [200, { "Content-Type" => "application/json" }, [photon_response.call.to_json.to_s]] }
166
+ end
167
+ Rails.application.routes.disable_clear_and_finalize = false
168
+ end
169
+
170
+ config.after(:each, :serves_geocoding_autocomplete) do
171
+ # Reset the routes back to original
172
+ Rails.application.reload_routes!
173
+ end
30
174
  end