decidim-dev 0.20.1 → 0.23.1

Sign up to get free protection for your applications and to get access to all the features.
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