decidim-core 0.20.1 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of decidim-core might be problematic. Click here for more details.

Files changed (213) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/fonts/decidim/Roboto-Regular.eot +0 -0
  3. data/app/assets/fonts/decidim/Roboto-Regular.svg +10520 -0
  4. data/app/assets/fonts/decidim/Roboto-Regular.ttf +0 -0
  5. data/app/assets/fonts/decidim/Roboto-Regular.woff +0 -0
  6. data/app/assets/fonts/decidim/Roboto-Regular.woff2 +0 -0
  7. data/app/assets/images/decidim/brands/google.svg +1 -0
  8. data/app/assets/javascripts/decidim.js.es6 +5 -0
  9. data/app/assets/javascripts/decidim/check_boxes_tree.js.es6 +190 -0
  10. data/app/assets/javascripts/decidim/core/bundle.js +1 -1
  11. data/app/assets/javascripts/decidim/core/bundle.js.map +1 -1
  12. data/app/assets/javascripts/decidim/delayed.js.es6 +26 -0
  13. data/app/assets/javascripts/decidim/diff_mode_dropdown.js.es6 +25 -4
  14. data/app/assets/javascripts/decidim/form_filter.component.js.es6 +86 -38
  15. data/app/assets/javascripts/decidim/form_filter.component.test.js +40 -6
  16. data/app/assets/javascripts/decidim/history.js.es6 +16 -1
  17. data/app/assets/javascripts/decidim/vizzs/orgchart.js.es6 +1 -1
  18. data/app/assets/stylesheets/decidim/_variables.scss +1 -1
  19. data/app/assets/stylesheets/decidim/extras/_results-per-page.scss +0 -1
  20. data/app/assets/stylesheets/decidim/modules/_buttons.scss +76 -3
  21. data/app/assets/stylesheets/decidim/modules/_comments.scss +78 -2
  22. data/app/assets/stylesheets/decidim/modules/_filters.scss +36 -2
  23. data/app/assets/stylesheets/decidim/modules/_layout.scss +13 -0
  24. data/app/assets/stylesheets/decidim/modules/_modules.scss +1 -0
  25. data/app/assets/stylesheets/decidim/modules/_navbar.scss +11 -5
  26. data/app/assets/stylesheets/decidim/modules/_process-stats.scss +53 -0
  27. data/app/assets/stylesheets/decidim/modules/_status-labels.scss +5 -0
  28. data/app/assets/stylesheets/decidim/modules/_tags.scss +7 -1
  29. data/app/assets/stylesheets/decidim/modules/_typography.scss +49 -4
  30. data/app/assets/stylesheets/decidim/utils/_fontface.scss +10 -0
  31. data/app/assets/stylesheets/decidim/utils/_toggle-expand.scss +14 -0
  32. data/app/cells/decidim/activity/show.erb +1 -1
  33. data/app/cells/decidim/author/profile_inline.erb +2 -2
  34. data/app/cells/decidim/diff/attribute.erb +15 -5
  35. data/app/cells/decidim/diff/diff_mode_html.erb +31 -0
  36. data/app/cells/decidim/diff/diff_split.erb +1 -1
  37. data/app/cells/decidim/diff/diff_unified.erb +1 -1
  38. data/app/cells/decidim/diff/show.erb +1 -0
  39. data/app/cells/decidim/diff_cell.rb +21 -8
  40. data/app/cells/decidim/follow_button/show.erb +20 -7
  41. data/app/cells/decidim/navbar_admin_link/show.erb +6 -0
  42. data/app/cells/decidim/navbar_admin_link_cell.rb +43 -0
  43. data/app/cells/decidim/tags_cell.rb +2 -2
  44. data/app/commands/decidim/amendable/accept.rb +9 -4
  45. data/app/commands/decidim/amendable/publish_draft.rb +5 -0
  46. data/app/commands/decidim/amendable/reject.rb +5 -0
  47. data/app/commands/decidim/amendable/withdraw.rb +3 -12
  48. data/app/commands/decidim/create_registration.rb +5 -6
  49. data/app/controllers/concerns/decidim/use_organization_time_zone.rb +32 -0
  50. data/app/controllers/decidim/application_controller.rb +3 -0
  51. data/app/controllers/decidim/components/base_controller.rb +1 -0
  52. data/app/controllers/decidim/data_portability_controller.rb +12 -19
  53. data/app/controllers/decidim/devise/omniauth_registrations_controller.rb +1 -1
  54. data/app/controllers/decidim/devise/registrations_controller.rb +1 -0
  55. data/app/controllers/decidim/scopes_controller.rb +41 -7
  56. data/app/forms/decidim/registration_form.rb +5 -0
  57. data/app/functions/decidim/core/component_finder_base.rb +33 -0
  58. data/app/functions/decidim/core/component_list.rb +38 -0
  59. data/app/functions/decidim/core/component_list_base.rb +61 -0
  60. data/app/functions/decidim/core/needs_api_filter_and_order.rb +52 -0
  61. data/app/functions/decidim/core/participatory_space_finder.rb +11 -0
  62. data/app/functions/decidim/core/participatory_space_finder_base.rb +29 -0
  63. data/app/functions/decidim/core/participatory_space_list.rb +11 -0
  64. data/app/functions/decidim/core/participatory_space_list_base.rb +34 -0
  65. data/app/helpers/decidim/amendments_helper.rb +27 -1
  66. data/app/helpers/decidim/application_helper.rb +31 -3
  67. data/app/helpers/decidim/categories_helper.rb +26 -0
  68. data/app/helpers/decidim/check_boxes_tree_helper.rb +115 -0
  69. data/app/helpers/decidim/omniauth_helper.rb +6 -13
  70. data/app/helpers/decidim/resource_versions_helper.rb +29 -0
  71. data/app/helpers/decidim/rich_text_editor_helper.rb +22 -0
  72. data/app/helpers/decidim/sanitize_helper.rb +3 -1
  73. data/app/helpers/decidim/scopes_helper.rb +3 -2
  74. data/app/jobs/decidim/data_portability_export_job.rb +18 -10
  75. data/app/jobs/decidim/export_job.rb +1 -1
  76. data/app/mailers/decidim/export_mailer.rb +9 -5
  77. data/app/models/decidim/omniauth_provider.rb +28 -0
  78. data/app/models/decidim/organization.rb +41 -0
  79. data/app/models/decidim/participatory_space_role_config/admin.rb +8 -0
  80. data/app/models/decidim/participatory_space_role_config/base.rb +31 -0
  81. data/app/models/decidim/participatory_space_role_config/collaborator.rb +8 -0
  82. data/app/models/decidim/participatory_space_role_config/moderator.rb +11 -0
  83. data/app/models/decidim/participatory_space_role_config/null_object.rb +11 -0
  84. data/app/models/decidim/participatory_space_role_config/participatory_space_admin.rb +8 -0
  85. data/app/models/decidim/participatory_space_role_config/valuator.rb +11 -0
  86. data/app/models/decidim/scope.rb +4 -2
  87. data/app/models/decidim/user.rb +19 -3
  88. data/app/presenters/decidim/home_stats_presenter.rb +5 -2
  89. data/app/presenters/decidim/resource_locator_presenter.rb +9 -0
  90. data/app/serializers/decidim/exporters/participatory_space_components_serializer.rb +1 -1
  91. data/app/serializers/decidim/importers/participatory_space_components_importer.rb +14 -5
  92. data/app/services/decidim/data_portability_exporter.rb +72 -0
  93. data/app/services/decidim/resource_search.rb +29 -13
  94. data/app/services/decidim/zip_stream/zip_stream_writer.rb +56 -0
  95. data/app/types/decidim/core/amendment_type.rb +26 -0
  96. data/app/types/decidim/core/area_api_type.rb +16 -0
  97. data/app/types/decidim/core/area_type_type.rb +14 -0
  98. data/app/types/decidim/core/base_input_filter.rb +8 -0
  99. data/app/types/decidim/core/base_input_sort.rb +22 -0
  100. data/app/types/decidim/core/component_input_filter.rb +50 -0
  101. data/app/types/decidim/core/component_input_sort.rb +32 -0
  102. data/app/types/decidim/core/fingerprint_type.rb +15 -0
  103. data/app/types/decidim/core/has_hastaggable_input_filter.rb +15 -0
  104. data/app/types/decidim/core/has_localized_input_filter.rb +21 -0
  105. data/app/types/decidim/core/has_localized_input_sort.rb +21 -0
  106. data/app/types/decidim/core/has_publishable_input_filter.rb +34 -0
  107. data/app/types/decidim/core/has_publishable_input_sort.rb +13 -0
  108. data/app/types/decidim/core/participatory_space_input_filter.rb +26 -0
  109. data/app/types/decidim/core/participatory_space_input_sort.rb +14 -0
  110. data/app/types/decidim/core/participatory_space_link_type.rb +24 -0
  111. data/app/types/decidim/core/trace_version_type.rb +29 -0
  112. data/app/uploaders/decidim/data_portability_uploader.rb +2 -7
  113. data/app/validators/time_zone_validator.rb +10 -0
  114. data/app/views/decidim/amendments/_edit_form_fields.html.erb +5 -13
  115. data/app/views/decidim/amendments/preview_draft.html.erb +1 -1
  116. data/app/views/decidim/devise/shared/_omniauth_buttons.html.erb +10 -12
  117. data/app/views/decidim/devise/shared/_omniauth_buttons_mini.html.erb +6 -8
  118. data/app/views/decidim/export_mailer/data_portability_export.html.erb +2 -2
  119. data/app/views/decidim/scopes/picker.html.erb +7 -3
  120. data/app/views/decidim/shared/_check_boxes_tree.html.erb +54 -0
  121. data/app/views/decidim/shared/_extended_navigation_bar.html.erb +1 -1
  122. data/app/views/decidim/widgets/show.html.erb +4 -0
  123. data/app/views/layouts/decidim/_admin_links.html.erb +2 -0
  124. data/app/views/layouts/decidim/_wrapper.html.erb +4 -3
  125. data/app/views/layouts/decidim/widget.html.erb +1 -43
  126. data/config/initializers/browser.rb +5 -0
  127. data/config/initializers/devise.rb +0 -22
  128. data/config/initializers/omniauth.rb +50 -0
  129. data/config/locales/ar.yml +6 -3
  130. data/config/locales/ca.yml +15 -6
  131. data/config/locales/cs.yml +12 -3
  132. data/config/locales/de.yml +5 -3
  133. data/config/locales/el-GR.yml +0 -2
  134. data/config/locales/el.yml +153 -0
  135. data/config/locales/en.yml +16 -7
  136. data/config/locales/eo-UY.yml +2 -2
  137. data/config/locales/es-MX.yml +12 -3
  138. data/config/locales/es-PY.yml +12 -3
  139. data/config/locales/es.yml +15 -6
  140. data/config/locales/eu.yml +4 -3
  141. data/config/locales/fi-plain.yml +12 -3
  142. data/config/locales/fi.yml +12 -3
  143. data/config/locales/fr.yml +5 -3
  144. data/config/locales/gl.yml +4 -3
  145. data/config/locales/hu.yml +12 -3
  146. data/config/locales/id-ID.yml +4 -3
  147. data/config/locales/it.yml +11 -3
  148. data/config/locales/nl.yml +8 -3
  149. data/config/locales/no.yml +12 -3
  150. data/config/locales/pl.yml +4 -3
  151. data/config/locales/pt-BR.yml +4 -3
  152. data/config/locales/pt.yml +4 -3
  153. data/config/locales/ru.yml +5 -3
  154. data/config/locales/sv.yml +5 -3
  155. data/config/locales/tr-TR.yml +4 -3
  156. data/config/locales/uk.yml +1 -3
  157. data/db/migrate/20191113092826_add_omniauth_settings_to_decidim_organization.rb +7 -0
  158. data/db/migrate/20191113144432_add_rich_text_editor_in_public_views_to_organizations.rb +10 -0
  159. data/db/migrate/20191118123154_add_admin_terms_of_use_body_field_to_organization.rb +9 -0
  160. data/db/migrate/20200107142226_add_organization_timezone.rb +7 -0
  161. data/db/seeds.rb +2 -1
  162. data/lib/decidim/amendable.rb +7 -4
  163. data/lib/decidim/api/amendable_entity_interface.rb +18 -0
  164. data/lib/decidim/api/amendable_interface.rb +18 -0
  165. data/lib/decidim/api/attachable_interface.rb +1 -1
  166. data/lib/decidim/api/categorizable_interface.rb +1 -1
  167. data/lib/decidim/api/coauthorable_interface.rb +29 -0
  168. data/lib/decidim/api/fingerprint_interface.rb +13 -0
  169. data/lib/decidim/api/participatory_space_interface.rb +9 -9
  170. data/lib/decidim/api/participatory_space_resourceable_interface.rb +21 -0
  171. data/lib/decidim/api/scopable_interface.rb +1 -1
  172. data/lib/decidim/api/timestamps_interface.rb +21 -0
  173. data/lib/decidim/api/traceable_interface.rb +14 -0
  174. data/lib/decidim/coauthorable.rb +9 -2
  175. data/lib/decidim/component_manifest.rb +1 -1
  176. data/lib/decidim/content_processor.rb +4 -2
  177. data/lib/decidim/content_renderers/link_renderer.rb +1 -1
  178. data/lib/decidim/core.rb +3 -3
  179. data/lib/decidim/core/api.rb +7 -0
  180. data/lib/decidim/core/test.rb +1 -0
  181. data/lib/decidim/core/test/factories.rb +16 -0
  182. data/lib/decidim/core/test/shared_examples/amendable_interface_examples.rb +14 -0
  183. data/lib/decidim/core/test/shared_examples/amendable_proposals_interface_examples.rb +50 -0
  184. data/lib/decidim/core/test/shared_examples/authorable_interface_examples.rb +3 -0
  185. data/lib/decidim/core/test/shared_examples/coauthorable_interface_examples.rb +60 -0
  186. data/lib/decidim/core/test/shared_examples/comments_examples.rb +8 -8
  187. data/lib/decidim/core/test/shared_examples/fingerprintable_interface_examples.rb +17 -0
  188. data/lib/decidim/core/test/shared_examples/follows_examples.rb +16 -0
  189. data/lib/decidim/core/test/shared_examples/input_filter_examples.rb +118 -0
  190. data/lib/decidim/core/test/shared_examples/input_sort_examples.rb +105 -0
  191. data/lib/decidim/core/test/shared_examples/participatory_space_resourcable_interface_examples.rb +43 -0
  192. data/lib/decidim/core/test/shared_examples/rich_text_editor_examples.rb +59 -0
  193. data/lib/decidim/core/test/shared_examples/timestamps_interface_examples.rb +21 -0
  194. data/lib/decidim/core/test/shared_examples/traceable_interface_examples.rb +47 -0
  195. data/lib/decidim/core/version.rb +1 -1
  196. data/lib/decidim/deprecations.rb +19 -0
  197. data/lib/decidim/diffy_extension.rb +26 -0
  198. data/lib/decidim/exporters/export_manifest.rb +6 -2
  199. data/lib/decidim/filter_form_builder.rb +25 -7
  200. data/lib/decidim/form_builder.rb +2 -2
  201. data/lib/decidim/has_settings.rb +10 -4
  202. data/lib/decidim/participatory_space_manifest.rb +20 -0
  203. data/lib/decidim/participatory_space_resourceable.rb +35 -1
  204. data/lib/decidim/query_extensions.rb +9 -23
  205. data/lib/decidim/scopable.rb +10 -0
  206. data/lib/tasks/decidim_data_portability_tasks.rake +66 -5
  207. data/lib/tasks/decidim_metrics_tasks.rake +18 -7
  208. data/vendor/assets/javascripts/datepicker-locales/foundation-datepicker.el.js +14 -0
  209. metadata +142 -16
  210. data/app/models/decidim/participatory_process_user_role.rb +0 -32
  211. data/app/views/layouts/decidim/_edit_link.html.erb +0 -8
  212. data/lib/decidim/data_portability_file_reader.rb +0 -56
  213. data/lib/decidim/data_portability_file_zipper.rb +0 -67
@@ -6,9 +6,16 @@ module Decidim
6
6
  autoload :ComponentInterface, "decidim/api/component_interface"
7
7
  autoload :AuthorInterface, "decidim/api/author_interface"
8
8
  autoload :AuthorableInterface, "decidim/api/authorable_interface"
9
+ autoload :CoauthorableInterface, "decidim/api/coauthorable_interface"
9
10
  autoload :CategorizableInterface, "decidim/api/categorizable_interface"
10
11
  autoload :ScopableInterface, "decidim/api/scopable_interface"
11
12
  autoload :AttachableInterface, "decidim/api/attachable_interface"
12
13
  autoload :HashtagInterface, "decidim/api/hashtag_interface"
14
+ autoload :ParticipatorySpaceResourceableInterface, "decidim/api/participatory_space_resourceable_interface"
15
+ autoload :FingerprintInterface, "decidim/api/fingerprint_interface"
16
+ autoload :AmendableInterface, "decidim/api/amendable_interface"
17
+ autoload :AmendableEntityInterface, "decidim/api/amendable_entity_interface"
18
+ autoload :TraceableInterface, "decidim/api/traceable_interface"
19
+ autoload :TimestampsInterface, "decidim/api/timestamps_interface"
13
20
  end
14
21
  end
@@ -46,3 +46,4 @@ require "decidim/core/test/shared_examples/uncommentable_component_examples"
46
46
  require "decidim/core/test/shared_examples/searchable_resources_shared_context"
47
47
  require "decidim/core/test/shared_examples/searchable_participatory_space_examples"
48
48
  require "decidim/core/test/shared_examples/has_private_users"
49
+ require "decidim/core/test/shared_examples/rich_text_editor_examples"
@@ -69,6 +69,7 @@ FactoryBot.define do
69
69
  factory :organization, class: "Decidim::Organization" do
70
70
  name { Faker::Company.unique.name }
71
71
  reference_prefix { Faker::Name.suffix }
72
+ time_zone { "UTC" }
72
73
  twitter_handler { Faker::Hipster.word }
73
74
  facebook_handler { Faker::Hipster.word }
74
75
  instagram_handler { Faker::Hipster.word }
@@ -88,6 +89,7 @@ FactoryBot.define do
88
89
  badges_enabled { true }
89
90
  user_groups_enabled { true }
90
91
  send_welcome_notification { true }
92
+ admin_terms_of_use_body { Decidim::Faker::Localized.wrapped("<p>", "</p>") { generate_localized_title } }
91
93
  force_users_to_authenticate_before_access_organization { false }
92
94
  smtp_settings do
93
95
  {
@@ -130,12 +132,18 @@ FactoryBot.define do
130
132
  deleted_at { Time.current }
131
133
  end
132
134
 
135
+ trait :admin_terms_accepted do
136
+ admin_terms_accepted_at { Time.current }
137
+ end
138
+
133
139
  trait :admin do
134
140
  admin { true }
141
+ admin_terms_accepted
135
142
  end
136
143
 
137
144
  trait :user_manager do
138
145
  roles { ["user_manager"] }
146
+ admin_terms_accepted
139
147
  end
140
148
 
141
149
  trait :managed do
@@ -325,6 +333,14 @@ FactoryBot.define do
325
333
  }
326
334
  end
327
335
 
336
+ trait :with_one_step do
337
+ step_settings do
338
+ {
339
+ 1 => { dummy_step_setting: true }
340
+ }
341
+ end
342
+ end
343
+
328
344
  trait :unpublished do
329
345
  published_at { nil }
330
346
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ shared_examples_for "amendable interface" do
6
+ describe "amendments" do
7
+ let(:query) { "{ amendments { id } }" }
8
+
9
+ it "includes the amendments id" do
10
+ amendments_ids = response["amendments"].map { |amendment| amendment["id"].to_i }
11
+ expect(amendments_ids).to include(*model.amendments.map(&:id))
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ shared_examples_for "amendable proposals interface" do
6
+ describe "amendments" do
7
+ let(:query) do
8
+ "{ amendments {
9
+ state
10
+ amendable { ...on Proposal { title } }
11
+ amendableType
12
+ emendation { ...on Proposal { title } }
13
+ emendationType
14
+ amender { name }
15
+ } }"
16
+ end
17
+
18
+ it "includes the amendments states" do
19
+ amendments_states = response["amendments"].map { |amendment| amendment["state"] }
20
+ expect(amendments_states).to include(*model.amendments.map(&:state))
21
+ end
22
+
23
+ it "amendable types matches Proposals Type" do
24
+ response["amendments"].each do |amendment|
25
+ expect(amendment["amendableType"]).to eq("Decidim::Proposals::Proposal")
26
+ end
27
+ end
28
+
29
+ it "emendation types matches Proposals Type" do
30
+ response["amendments"].each do |amendment|
31
+ expect(amendment["emendationType"]).to eq("Decidim::Proposals::Proposal")
32
+ end
33
+ end
34
+
35
+ it "returns amendable as parent proposal" do
36
+ amendment_amendables = response["amendments"].map { |amendment| amendment["amendable"] }
37
+ expect(amendment_amendables).to include(*model.amendments.map(&:amendable).map { |p| { "title" => p.title } })
38
+ end
39
+
40
+ it "returns emendations received" do
41
+ amendment_emendations = response["amendments"].map { |amendment| amendment["emendation"] }
42
+ expect(amendment_emendations).to include(*model.amendments.map(&:emendation).map { |p| { "title" => p.title } })
43
+ end
44
+
45
+ it "returns amender as emendation author" do
46
+ amendment_amenders = response["amendments"].map { |amendment| amendment["amender"] }
47
+ expect(amendment_amenders).to include(*model.amendments.map(&:amender).map { |p| { "name" => p.name } })
48
+ end
49
+ end
50
+ end
@@ -5,6 +5,9 @@ require "spec_helper"
5
5
  shared_examples_for "authorable interface" do
6
6
  describe "author" do
7
7
  describe "when author is not present" do
8
+ let(:author) { nil }
9
+ let(:query) { "{ author { name } }" }
10
+
8
11
  before do
9
12
  model.update(author: author)
10
13
  end
@@ -27,5 +27,65 @@ shared_examples_for "coauthorable interface" do
27
27
  expect(response["author"]["name"]).to eq(user_group.name)
28
28
  end
29
29
  end
30
+
31
+ describe "with a several coauthors" do
32
+ let(:query) { "{ author { name } authors { name } authorsCount }" }
33
+ let(:coauthor) { create(:user, :confirmed, organization: model.participatory_space.organization) }
34
+
35
+ before do
36
+ model.add_coauthor coauthor
37
+ model.save!
38
+ end
39
+
40
+ context "when both are users" do
41
+ it "returns 2 total co-authors" do
42
+ expect(response["authorsCount"]).to eq(2)
43
+ end
44
+
45
+ it "returns an array of authors" do
46
+ expect(response["authors"].count).to eq(2)
47
+ expect(response["authors"]).to include("name" => author.name)
48
+ expect(response["authors"]).to include("name" => coauthor.name)
49
+ end
50
+
51
+ it "returns a main author" do
52
+ expect(response["author"]["name"]).to eq(author.name)
53
+ end
54
+ end
55
+
56
+ context "when author is the organization" do
57
+ let(:model) { create(:proposal, :official, component: component) }
58
+
59
+ it "returns 2 total co-authors" do
60
+ expect(response["authorsCount"]).to eq(2)
61
+ end
62
+
63
+ it "returns 1 author in authors array" do
64
+ expect(response["authors"].count).to eq(1)
65
+ expect(response["authors"]).to include("name" => coauthor.name)
66
+ end
67
+
68
+ it "do not return a main author" do
69
+ expect(response["author"]).to eq(nil)
70
+ end
71
+ end
72
+
73
+ context "when author is a meeting" do
74
+ let(:model) { create(:proposal, :official_meeting, component: component) }
75
+
76
+ it "returns 2 total co-authors" do
77
+ expect(response["authorsCount"]).to eq(2)
78
+ end
79
+
80
+ it "returns 1 author in authors array" do
81
+ expect(response["authors"].count).to eq(1)
82
+ expect(response["authors"]).to include("name" => coauthor.name)
83
+ end
84
+
85
+ it "do not return a main author" do
86
+ expect(response["author"]).to eq(nil)
87
+ end
88
+ end
89
+ end
30
90
  end
31
91
  end
@@ -9,7 +9,7 @@ shared_examples "comments" do
9
9
  switch_to_host(organization.host)
10
10
  end
11
11
 
12
- it "shows the list of comments for the resorce" do
12
+ it "shows the list of comments for the resource" do
13
13
  visit resource_path
14
14
 
15
15
  expect(page).to have_selector("#comments")
@@ -64,7 +64,7 @@ shared_examples "comments" do
64
64
  end
65
65
 
66
66
  it "shows comment to the user" do
67
- expect(page).to have_comment_from(user, "This is a new comment")
67
+ expect(page).to have_comment_from(user, "This is a new comment", wait: 20)
68
68
  end
69
69
  end
70
70
 
@@ -86,7 +86,7 @@ shared_examples "comments" do
86
86
  click_button "Send"
87
87
  end
88
88
 
89
- expect(page).to have_comment_from(user_group, "This is a new comment")
89
+ expect(page).to have_comment_from(user_group, "This is a new comment", wait: 20)
90
90
  end
91
91
  end
92
92
 
@@ -110,7 +110,7 @@ shared_examples "comments" do
110
110
  click_button "Send"
111
111
  end
112
112
 
113
- expect(page).to have_selector(".comment-thread .comment--nested")
113
+ expect(page).to have_selector(".comment-thread .comment--nested", wait: 20)
114
114
  expect(page).to have_selector(".comment__additionalreply")
115
115
  expect(page).to have_reply_to(comment, "This is a reply")
116
116
  end
@@ -150,7 +150,7 @@ shared_examples "comments" do
150
150
  end
151
151
 
152
152
  within "#comments" do
153
- expect(page).to have_selector "span.success.label", text: "In favor"
153
+ expect(page).to have_selector "span.success.label", text: "In favor", wait: 20
154
154
  end
155
155
  else
156
156
  expect(page).to have_no_selector(".opinion-toggle--ok")
@@ -247,7 +247,7 @@ shared_examples "comments" do
247
247
  let(:content) { "A valid user mention: @#{mentioned_user.nickname}." }
248
248
 
249
249
  it "replaces the mention with a link to the user's profile" do
250
- expect(page).to have_comment_from(user, "A valid user mention: @#{mentioned_user.nickname}")
250
+ expect(page).to have_comment_from(user, "A valid user mention: @#{mentioned_user.nickname}", wait: 20)
251
251
  expect(page).to have_link "@#{mentioned_user.nickname}", href: "/profiles/#{mentioned_user.nickname}"
252
252
  end
253
253
  end
@@ -257,7 +257,7 @@ shared_examples "comments" do
257
257
  let(:content) { "This text mentions a user outside current organization: @#{mentioned_user.nickname}" }
258
258
 
259
259
  it "ignores the mention" do
260
- expect(page).to have_comment_from(user, "This text mentions a user outside current organization: @#{mentioned_user.nickname}")
260
+ expect(page).to have_comment_from(user, "This text mentions a user outside current organization: @#{mentioned_user.nickname}", wait: 20)
261
261
  expect(page).not_to have_link "@#{mentioned_user.nickname}"
262
262
  end
263
263
  end
@@ -266,7 +266,7 @@ shared_examples "comments" do
266
266
  let(:content) { "This text mentions a @nonexistent user" }
267
267
 
268
268
  it "ignores the mention" do
269
- expect(page).to have_comment_from(user, "This text mentions a @nonexistent user")
269
+ expect(page).to have_comment_from(user, "This text mentions a @nonexistent user", wait: 20)
270
270
  expect(page).not_to have_link "@nonexistent"
271
271
  end
272
272
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ shared_examples_for "fingerprintable interface" do
6
+ describe "fingerprint" do
7
+ let(:query) { "{ fingerprint { value source } }" }
8
+
9
+ it "returns the fingerprint value" do
10
+ expect(response["fingerprint"]["value"]).to eq(model.fingerprint.value)
11
+ end
12
+
13
+ it "returns the fingerprint source" do
14
+ expect(response["fingerprint"]["source"]).to eq(model.fingerprint.source)
15
+ end
16
+ end
17
+ end
@@ -34,4 +34,20 @@ shared_examples "follows" do
34
34
  end
35
35
  end
36
36
  end
37
+
38
+ context "when the user is following the followable's participatory space" do
39
+ before do
40
+ create(:follow, followable: followable.participatory_space, user: user)
41
+ end
42
+
43
+ context "when user clicks the Follow button" do
44
+ it "makes the user follow the followable" do
45
+ visit resource_locator(followable).path
46
+ expect do
47
+ click_button "Already following the participatory space"
48
+ expect(page).to have_content "Stop following"
49
+ end.to change(Decidim::Follow, :count).by(1)
50
+ end
51
+ end
52
+ end
37
53
  end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ shared_examples_for "collection has before/since input filter" do |collection, field|
4
+ context "when date is before the past" do
5
+ let(:query) { %[{ #{collection}(filter: {#{field}Before: "#{2.days.ago.to_date}"}) { id } }] }
6
+
7
+ it "finds nothing" do
8
+ ids = response[collection].map { |item| item["id"] }
9
+ expect(ids).to eq([])
10
+ end
11
+ end
12
+
13
+ context "when date is before the future" do
14
+ let(:query) { %[{ #{collection}(filter: {#{field}Before: "#{2.days.from_now.to_date}"}) { id } }] }
15
+
16
+ it "finds the models " do
17
+ ids = response[collection].map { |item| item["id"] }
18
+ expect(ids).to match_array(models.map(&:id).map(&:to_s))
19
+ end
20
+ end
21
+
22
+ context "when date is after the future" do
23
+ let(:query) { %[{ #{collection}(filter: {#{field}Since: "#{2.days.from_now.to_date}"}) { id } }] }
24
+
25
+ it "finds nothing " do
26
+ ids = response[collection].map { |item| item["id"] }
27
+ expect(ids).to eq([])
28
+ end
29
+ end
30
+
31
+ context "when date is after the past" do
32
+ let(:query) { %[{ #{collection}(filter: {#{field}Since: "#{2.days.ago.to_date}"}) { id } }] }
33
+
34
+ it "finds the models " do
35
+ ids = response[collection].map { |item| item["id"] }
36
+ expect(ids).to match_array(models.map(&:id).map(&:to_s))
37
+ end
38
+ end
39
+ end
40
+
41
+ shared_examples_for "connection has before/since input filter" do |connection, field|
42
+ context "when date is before the past" do
43
+ let(:query) { %[{ #{connection}(filter: {#{field}Before: "#{2.days.ago.to_date}"}) { edges { node { id } } } }] }
44
+
45
+ it "finds nothing" do
46
+ ids = response[connection]["edges"].map { |edge| edge["node"]["id"] }
47
+ expect(ids).to eq([])
48
+ end
49
+ end
50
+
51
+ context "when date is before the future" do
52
+ let(:query) { %[{ #{connection}(filter: {#{field}Before: "#{2.days.from_now.to_date}"}) { edges { node { id } } } }] }
53
+
54
+ it "finds the models " do
55
+ ids = response[connection]["edges"].map { |edge| edge["node"]["id"] }
56
+ expect(ids).to match_array(models.map(&:id).map(&:to_s))
57
+ end
58
+ end
59
+
60
+ context "when date is after the future" do
61
+ let(:query) { %[{ #{connection}(filter: {#{field}Since: "#{2.days.from_now.to_date}"}) { edges { node { id } } } }] }
62
+
63
+ it "finds nothing " do
64
+ ids = response[connection]["edges"].map { |edge| edge["node"]["id"] }
65
+ expect(ids).to eq([])
66
+ end
67
+ end
68
+
69
+ context "when date is after the past" do
70
+ let(:query) { %[{ #{connection}(filter: {#{field}Since: "#{2.days.ago.to_date}"}) { edges { node { id } } } }] }
71
+
72
+ it "finds the models " do
73
+ ids = response[connection]["edges"].map { |edge| edge["node"]["id"] }
74
+ expect(ids).to match_array(models.map(&:id).map(&:to_s))
75
+ end
76
+ end
77
+ end
78
+
79
+ shared_examples_for "collection has hashtag input filter" do |collection|
80
+ let(:query) { %[{ #{collection}(filter: { hashtag: "#{hashtag}" }) { id }}] }
81
+
82
+ before do
83
+ models.first.hashtag = "#someWeirdHashtag"
84
+ models.first.save!
85
+ models.second.hashtag = "#anotherWeirdHashtag"
86
+ models.second.save!
87
+ end
88
+
89
+ context "when hashtag starts with #" do
90
+ let(:hashtag) { "#someWeirdHashtag" }
91
+
92
+ it "finds the model" do
93
+ ids = response[collection].map { |item| item["id"] }
94
+ expect(ids).to include(models.first.id.to_s)
95
+ expect(ids).not_to include(models.second.id.to_s)
96
+ end
97
+ end
98
+
99
+ context "when hashtag starts without #" do
100
+ let(:hashtag) { "anotherWeirdHashtag" }
101
+
102
+ it "finds the model" do
103
+ ids = response[collection].map { |item| item["id"] }
104
+ expect(ids).not_to include(models.first.id.to_s)
105
+ expect(ids).to include(models.second.id.to_s)
106
+ end
107
+ end
108
+
109
+ context "when hashtag is different" do
110
+ let(:hashtag) { "someEvenWeirderHashtag" }
111
+
112
+ it "does not find the model" do
113
+ ids = response[collection].map { |item| item["id"] }
114
+ expect(ids).not_to include(models.first.id.to_s)
115
+ expect(ids).not_to include(models.second.id.to_s)
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ shared_examples_for "collection has input sort" do |collection, field|
4
+ describe "ASC" do
5
+ let(:query) { %[{ #{collection}(order: {#{field}: "ASC"}) { id } }] }
6
+
7
+ it "returns expected order" do
8
+ ids = response[collection].map { |item| item["id"] }
9
+ replies_ids = models.sort_by(&field.underscore.to_sym).map(&:id).map(&:to_s)
10
+ expect(ids).to eq(replies_ids)
11
+ expect(ids).not_to eq(replies_ids.reverse)
12
+ end
13
+ end
14
+
15
+ describe "DESC" do
16
+ let(:query) { %[{ #{collection}(order: {#{field}: "DESC"}) { id } }] }
17
+
18
+ it "returns reversed order" do
19
+ ids = response[collection].map { |item| item["id"] }
20
+ replies_ids = models.sort_by(&field.underscore.to_sym).map(&:id).map(&:to_s)
21
+ expect(ids).not_to eq(replies_ids)
22
+ expect(ids).to eq(replies_ids.reverse)
23
+ end
24
+ end
25
+ end
26
+
27
+ shared_examples_for "collection has i18n input sort" do |collection, field|
28
+ context "when locale is not specified" do
29
+ describe "ASC" do
30
+ let(:query) { %[{ #{collection}(order: { #{field}: "ASC" }) { id } }] }
31
+
32
+ it "returns alphabetical order" do
33
+ response_ids = response[collection].map { |item| item["id"].to_i }
34
+ ids = models.sort_by { |item| item.public_send(field.to_sym)[current_organization.default_locale] }.map { |item| item.id.to_i }
35
+ expect(response_ids).to eq(ids)
36
+ expect(response_ids).not_to eq(ids.reverse)
37
+ end
38
+ end
39
+
40
+ describe "DESC" do
41
+ let(:query) { %[{ #{collection}(order: { #{field}: "DESC" }) { id } }] }
42
+
43
+ it "returns revered alphabetical order" do
44
+ response_ids = response[collection].map { |item| item["id"].to_i }
45
+ ids = models.sort_by { |item| item.public_send(field.to_sym)[current_organization.default_locale] }.map { |item| item.id.to_i }
46
+ expect(response_ids).not_to eq(ids)
47
+ expect(response_ids).to eq(ids.reverse)
48
+ end
49
+ end
50
+ end
51
+
52
+ context "when locale is specified" do
53
+ describe "ASC" do
54
+ let(:query) { %[{ #{collection}(order: { #{field}: "ASC", locale: "ca" }) { id } }] }
55
+
56
+ it "returns alphabetical order" do
57
+ response_ids = response[collection].map { |item| item["id"].to_i }
58
+ ids = models.sort_by { |item| item.public_send(field.to_sym)["ca"] }.map { |item| item.id.to_i }
59
+ expect(response_ids).to eq(ids)
60
+ expect(response_ids).not_to eq(ids.reverse)
61
+ end
62
+ end
63
+
64
+ describe "DESC" do
65
+ let(:query) { %[{ #{collection}(order: { #{field}: "DESC", locale: "ca" }) { id } }] }
66
+
67
+ it "returns revered alphabetical order" do
68
+ response_ids = response[collection].map { |item| item["id"].to_i }
69
+ ids = models.sort_by { |item| item.public_send(field.to_sym)["ca"] }.map { |item| item.id.to_i }
70
+ expect(response_ids).not_to eq(ids)
71
+ expect(response_ids).to eq(ids.reverse)
72
+ end
73
+ end
74
+
75
+ context "when locale does not exist in the organization" do
76
+ let(:query) { %[{ #{collection}(order: { #{field}: "DESC", locale: "de" }) { id } }] }
77
+
78
+ it "returns all the component ordered" do
79
+ expect { response }.to raise_exception(Exception)
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ shared_examples_for "connection has input sort" do |connection, field|
86
+ describe "ASC" do
87
+ let(:query) { %[{ #{connection}(order: {#{field}: "ASC"}) { edges { node { id } } } }] }
88
+
89
+ it "returns expected order" do
90
+ ids = response[connection]["edges"].map { |edge| edge["node"]["id"] }
91
+ replies_ids = models.sort_by(&field.underscore.to_sym).map(&:id).map(&:to_s)
92
+ expect(ids).to eq(replies_ids)
93
+ end
94
+ end
95
+
96
+ describe "DESC" do
97
+ let(:query) { %[{ #{connection}(order: {#{field}: "DESC"}) { edges { node { id } } } }] }
98
+
99
+ it "returns reversed order" do
100
+ ids = response[connection]["edges"].map { |edge| edge["node"]["id"] }
101
+ replies_ids = models.sort_by(&field.underscore.to_sym).map(&:id).map(&:to_s)
102
+ expect(ids).to eq(replies_ids.reverse)
103
+ end
104
+ end
105
+ end