decidim-core 0.27.5 → 0.27.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/activity_cell.rb +2 -2
  3. data/app/cells/decidim/card_cell.rb +2 -2
  4. data/app/cells/decidim/card_m/top.erb +1 -1
  5. data/app/cells/decidim/card_m_cell.rb +1 -1
  6. data/app/cells/decidim/scopes_picker/scope_picker_values.erb +1 -1
  7. data/app/cells/decidim/tags_cell.rb +3 -1
  8. data/app/cells/decidim/user_profile_cell.rb +1 -1
  9. data/app/commands/decidim/create_omniauth_registration.rb +2 -4
  10. data/app/commands/decidim/messaging/reply_to_conversation.rb +3 -0
  11. data/app/commands/decidim/messaging/start_conversation.rb +3 -0
  12. data/app/controllers/concerns/decidim/devise_authentication_methods.rb +36 -0
  13. data/app/controllers/concerns/decidim/paginable.rb +1 -1
  14. data/app/controllers/decidim/devise/omniauth_registrations_controller.rb +1 -22
  15. data/app/controllers/decidim/devise/sessions_controller.rb +1 -24
  16. data/app/controllers/decidim/widgets_controller.rb +6 -0
  17. data/app/events/decidim/welcome_notification_event.rb +6 -9
  18. data/app/helpers/decidim/cells_paginate_helper.rb +1 -1
  19. data/app/helpers/decidim/check_boxes_tree_helper.rb +4 -4
  20. data/app/helpers/decidim/newsletters_helper.rb +83 -16
  21. data/app/helpers/decidim/resource_helper.rb +1 -1
  22. data/app/helpers/decidim/sanitize_helper.rb +9 -0
  23. data/app/helpers/decidim/user_profile_helper.rb +7 -2
  24. data/app/mailers/decidim/messaging/conversation_mailer.rb +3 -72
  25. data/app/models/decidim/push_notification_message.rb +39 -0
  26. data/app/packs/images/decidim/.keep +0 -0
  27. data/app/packs/src/decidim/input_hashtags.js +1 -1
  28. data/app/packs/src/decidim/input_mentions.js +1 -1
  29. data/app/packs/src/decidim/input_multiple_mentions.js +1 -1
  30. data/app/packs/src/decidim/vizzs/index.js +1 -1
  31. data/app/packs/stylesheets/decidim/plugins/leaflet.scss +118 -114
  32. data/app/presenters/decidim/admin_log/oauth_application_resource_presenter.rb +1 -1
  33. data/app/presenters/decidim/notification_to_mailer_presenter.rb +9 -0
  34. data/app/services/decidim/events_manager.rb +6 -0
  35. data/app/services/decidim/push_notification_message_sender.rb +36 -0
  36. data/app/services/decidim/send_push_notification.rb +22 -8
  37. data/app/views/decidim/devise/registrations/new.html.erb +2 -2
  38. data/app/views/decidim/notifications_digest_mailer/_email_content.html.erb +7 -0
  39. data/app/views/decidim/shared/_address_details.html.erb +2 -2
  40. data/app/views/layouts/decidim/_js_configuration.html.erb +1 -0
  41. data/config/locales/ar.yml +4 -2
  42. data/config/locales/bg.yml +74 -0
  43. data/config/locales/ca.yml +22 -22
  44. data/config/locales/de.yml +25 -25
  45. data/config/locales/el.yml +1 -3
  46. data/config/locales/es-MX.yml +1 -1
  47. data/config/locales/es-PY.yml +1 -1
  48. data/config/locales/es.yml +20 -20
  49. data/config/locales/eu.yml +11 -2
  50. data/config/locales/fi.yml +3 -3
  51. data/config/locales/fr-CA.yml +2 -2
  52. data/config/locales/fr.yml +2 -2
  53. data/config/locales/gl.yml +3 -0
  54. data/config/locales/he-IL.yml +1 -0
  55. data/config/locales/hu.yml +41 -8
  56. data/config/locales/it.yml +1 -3
  57. data/config/locales/ja.yml +3 -2
  58. data/config/locales/lb.yml +1 -3
  59. data/config/locales/lt.yml +0 -3
  60. data/config/locales/no.yml +1 -3
  61. data/config/locales/pl.yml +137 -1
  62. data/config/locales/pt-BR.yml +113 -21
  63. data/config/locales/pt.yml +1 -3
  64. data/config/locales/ro-RO.yml +0 -3
  65. data/config/locales/ru.yml +9 -0
  66. data/config/locales/sk.yml +2 -0
  67. data/config/locales/sv.yml +22 -2
  68. data/config/locales/tr-TR.yml +1 -3
  69. data/config/locales/uk.yml +14 -0
  70. data/config/locales/zh-CN.yml +0 -3
  71. data/config/locales/zh-TW.yml +0 -3
  72. data/decidim-core.gemspec +78 -0
  73. data/lib/decidim/acts_as_tree.rb +14 -1
  74. data/lib/decidim/asset_router/storage.rb +4 -0
  75. data/lib/decidim/attribute_encryptor.rb +6 -4
  76. data/lib/decidim/core/engine.rb +7 -3
  77. data/lib/decidim/core/test/factories.rb +308 -95
  78. data/lib/decidim/core/test/shared_examples/amendable/amendment_created_event_examples.rb +6 -26
  79. data/lib/decidim/core/test/shared_examples/amendable/amendment_promoted_event_examples.rb +8 -26
  80. data/lib/decidim/core/test/shared_examples/comments_examples.rb +32 -0
  81. data/lib/decidim/core/test/shared_examples/embed_resource_examples.rb +187 -11
  82. data/lib/decidim/core/test/shared_examples/has_attachment_collections.rb +8 -6
  83. data/lib/decidim/core/test/shared_examples/has_attachments.rb +4 -4
  84. data/lib/decidim/core/test/shared_examples/has_category.rb +27 -0
  85. data/lib/decidim/core/test/shared_examples/has_reference.rb +1 -1
  86. data/lib/decidim/core/test/shared_examples/has_space_in_mcell_examples.rb +1 -2
  87. data/lib/decidim/core/test/shared_examples/resource_endorsed_event_examples.rb +6 -3
  88. data/lib/decidim/core/test/shared_examples/resource_locator_presenter_examples.rb +134 -0
  89. data/lib/decidim/core/test/shared_examples/searchable_results_examples.rb +1 -1
  90. data/lib/decidim/core/test/shared_examples/simple_event.rb +50 -2
  91. data/lib/decidim/core/test.rb +1 -0
  92. data/lib/decidim/core/version.rb +1 -1
  93. data/lib/decidim/core.rb +1 -0
  94. data/lib/decidim/engine_router.rb +17 -4
  95. data/lib/decidim/events/base_event.rb +5 -2
  96. data/lib/decidim/events/simple_event.rb +3 -17
  97. data/lib/decidim/has_category.rb +1 -1
  98. data/lib/decidim/has_conversations.rb +91 -0
  99. data/lib/decidim/participable.rb +17 -0
  100. data/lib/decidim/view_model.rb +1 -0
  101. data/lib/decidim/webpacker/webpack/.modernizrrc +9 -0
  102. data/lib/premailer/adapter/decidim.rb +5 -4
  103. data/lib/tasks/decidim_reminders_tasks.rake +1 -0
  104. data/lib/tasks/upgrade/decidim_fix_categorization.rake +15 -0
  105. metadata +31 -30
  106. data/config/environment.rb +0 -3
@@ -12,36 +12,16 @@ shared_examples "amendment created event" do
12
12
  end
13
13
 
14
14
  it_behaves_like "a simple event"
15
+ it_behaves_like "a simple event email"
16
+ it_behaves_like "a simple event notification"
15
17
 
16
18
  let(:emendation_author_nickname) { "@#{emendation.creator_author.nickname}" }
17
19
  let(:emendation_path) { Decidim::ResourceLocatorPresenter.new(emendation).path }
18
20
  let(:emendation_author_path) { Decidim::UserPresenter.new(emendation.creator_author).profile_path }
19
21
  let(:amendable_path) { Decidim::ResourceLocatorPresenter.new(amendable).path }
20
22
 
21
- describe "email_subject" do
22
- it "is generated correctly" do
23
- expect(subject.email_subject).to eq("New amendment for #{amendable_title}")
24
- end
25
- end
26
-
27
- describe "email_intro" do
28
- it "is generated correctly" do
29
- expect(subject.email_intro)
30
- .to eq("A new amendment has been created for #{amendable_title}. You can see it from this page:")
31
- end
32
- end
33
-
34
- describe "email_outro" do
35
- it "is generated correctly" do
36
- expect(subject.email_outro)
37
- .to eq("You have received this notification because you are following #{amendable_title}. You can stop receiving notifications following the previous link.")
38
- end
39
- end
40
-
41
- describe "notification_title" do
42
- it "is generated correctly" do
43
- expect(subject.notification_title)
44
- .to eq("A new amendment has been created for <a href=\"#{amendable_path}\">#{amendable_title}</a>.")
45
- end
46
- end
23
+ let(:email_subject) { "New amendment for #{amendable_title}" }
24
+ let(:email_intro) { "A new amendment has been created for #{amendable_title}. You can see it from this page:" }
25
+ let(:email_outro) { "You have received this notification because you are following #{amendable_title}. You can stop receiving notifications following the previous link." }
26
+ let(:notification_title) { "A new amendment has been created for <a href=\"#{amendable_path}\">#{amendable_title}</a>." }
47
27
  end
@@ -7,36 +7,18 @@ shared_examples "amendment promoted event" do
7
7
  let(:event_name) { "decidim.events.amendments.amendment_promoted" }
8
8
 
9
9
  it_behaves_like "a simple event"
10
+ it_behaves_like "a simple event email"
11
+ it_behaves_like "a simple event notification"
10
12
 
11
13
  let(:emendation_author_nickname) { "@#{emendation.creator_author.nickname}" }
12
14
  let(:emendation_path) { Decidim::ResourceLocatorPresenter.new(emendation).path }
13
15
  let(:emendation_author_path) { Decidim::UserPresenter.new(emendation.creator_author).profile_path }
14
16
  let(:amendable_path) { Decidim::ResourceLocatorPresenter.new(amendable).path }
15
17
 
16
- describe "email_subject" do
17
- it "is generated correctly" do
18
- expect(subject.email_subject).to eq("An amendment from #{emendation_author_nickname} has been published as a new proposal")
19
- end
20
- end
21
-
22
- describe "email_intro" do
23
- it "is generated correctly" do
24
- expect(subject.email_intro)
25
- .to eq("A rejected amendment for #{amendable_title} has been published as a new #{amendable_type}. You can see it from this page:")
26
- end
27
- end
28
-
29
- describe "email_outro" do
30
- it "is generated correctly" do
31
- expect(subject.email_outro)
32
- .to eq("You have received this notification because you are following #{amendable_title}. You can stop receiving notifications following the previous link.")
33
- end
34
- end
35
-
36
- describe "notification_title" do
37
- it "is generated correctly" do
38
- expect(subject.notification_title)
39
- .to eq("A <a href=\"#{emendation_path}\">rejected amendment</a> for <a href=\"#{amendable_path}\">#{amendable_title}</a> has been published as a new #{amendable_type} by <a href=\"#{emendation_author_path}\">#{emendation_author_nickname}</a>.") # rubocop:disable Layout/LineLength
40
- end
41
- end
18
+ let(:email_subject) { "An amendment from #{emendation_author_nickname} has been published as a new proposal" }
19
+ let(:email_intro) { "A rejected amendment for #{amendable_title} has been published as a new #{amendable_type}. You can see it from this page:" }
20
+ let(:email_outro) { "You have received this notification because you are following #{amendable_title}. You can stop receiving notifications following the previous link." }
21
+ # rubocop:disable Layout/LineLength
22
+ let(:notification_title) { "A <a href=\"#{emendation_path}\">rejected amendment</a> for <a href=\"#{amendable_path}\">#{amendable_title}</a> has been published as a new #{amendable_type} by <a href=\"#{emendation_author_path}\">#{emendation_author_nickname}</a>." }
23
+ # rubocop:enable Layout/LineLength
42
24
  end
@@ -48,6 +48,38 @@ shared_examples "comments" do
48
48
  expect(page).to have_css(".comments > div:nth-child(2)", text: "Most Rated Comment")
49
49
  end
50
50
 
51
+ context "when there are comments and replies" do
52
+ let!(:single_comment) { create(:comment, commentable: commentable) }
53
+ let!(:reply) { create(:comment, commentable: single_comment, root_commentable: commentable) }
54
+
55
+ it "displays the show replies link on comment with reply" do
56
+ visit resource_path
57
+ expect(page).not_to have_content("Comments are disabled at this time")
58
+ expect(page).to have_css(".comment", minimum: 1)
59
+
60
+ within "#comment_#{single_comment.id}" do
61
+ expect(page).to have_content "Hide replies"
62
+ end
63
+ end
64
+
65
+ context "when there is a comment with the same parent id but different type with replies" do
66
+ let!(:other_component) { create(:component, manifest_name: :dummy, organization: organization) }
67
+ let!(:other_commentable) { create(:dummy_resource, component: other_component, author: user, id: single_comment.id) }
68
+ let!(:reply) { create(:comment, commentable: other_commentable, root_commentable: other_commentable) }
69
+ let!(:other_reply) { create(:comment, commentable: reply, root_commentable: other_commentable) }
70
+
71
+ it "displays the show replies link on comment with reply" do
72
+ visit resource_path
73
+ expect(page).not_to have_content("Comments are disabled at this time")
74
+ expect(page).to have_css(".comment", minimum: 1)
75
+
76
+ within "#comment_#{single_comment.id}" do
77
+ expect(page).not_to have_content "Hide replies"
78
+ end
79
+ end
80
+ end
81
+ end
82
+
51
83
  context "when there are deleted comments" do
52
84
  let(:deleted_comment) { comments[0] }
53
85
 
@@ -1,5 +1,50 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "decidim/admin/test/admin_participatory_space_access_examples"
4
+
5
+ shared_examples "rendering the embed page correctly" do
6
+ before do
7
+ visit widget_path
8
+ end
9
+
10
+ it "renders" do
11
+ if resource.title.is_a?(Hash)
12
+ expect(page).to have_i18n_content(resource.title)
13
+ else
14
+ expect(page).to have_content(resource.title)
15
+ end
16
+
17
+ expect(page).to have_content(organization.name)
18
+ end
19
+ end
20
+
21
+ shared_examples "rendering the embed link in the resource page" do
22
+ before do
23
+ visit resource_locator(resource).path
24
+ end
25
+
26
+ it "has the embed link" do
27
+ expect(page).to have_button("Embed")
28
+ end
29
+ end
30
+
31
+ shared_examples "showing the unauthorized message in the widget_path" do
32
+ it do
33
+ visit widget_path
34
+ expect(page).to have_content "You are not authorized to perform this action"
35
+ end
36
+ end
37
+
38
+ shared_examples "not rendering the embed link in the resource page" do
39
+ before do
40
+ visit resource_locator(resource).path
41
+ end
42
+
43
+ it "does not have the embed link" do
44
+ expect(page).to have_no_button("Embed")
45
+ end
46
+ end
47
+
3
48
  shared_examples_for "an embed resource" do |options|
4
49
  if options.is_a?(Hash) && options[:skip_space_checks]
5
50
  let(:organization) { resource.organization }
@@ -11,22 +56,29 @@ shared_examples_for "an embed resource" do |options|
11
56
  include_context "with a component"
12
57
  end
13
58
 
14
- context "when visiting the embed page for a resource" do
15
- before do
16
- visit resource_locator(resource).path
17
- visit "#{current_path}/embed"
18
- end
59
+ unless options.is_a?(Hash) && options[:skip_publication_checks]
60
+ context "when the resource is not published" do
61
+ before do
62
+ resource.unpublish!
63
+ end
64
+
65
+ it_behaves_like "not rendering the embed link in the resource page"
19
66
 
20
- it "renders the page correctly" do
21
- if resource.title.is_a?(Hash)
22
- expect(page).to have_i18n_content(resource.title)
23
- else
24
- expect(page).to have_content(resource.title)
67
+ it_behaves_like "a 404 page" do
68
+ let(:target_path) { widget_path }
25
69
  end
70
+ end
71
+ end
72
+
73
+ it_behaves_like "rendering the embed link in the resource page" unless options.is_a?(Hash) && options[:skip_link_checks]
26
74
 
27
- expect(page).to have_content(organization.name)
75
+ context "when visiting the embed page for a resource" do
76
+ before do
77
+ visit widget_path
28
78
  end
29
79
 
80
+ it_behaves_like "rendering the embed page correctly"
81
+
30
82
  unless options.is_a?(Hash) && options[:skip_space_checks]
31
83
  context "when the participatory_space is a process" do
32
84
  it "shows the process name" do
@@ -47,3 +99,127 @@ shared_examples_for "an embed resource" do |options|
47
99
  end
48
100
  end
49
101
  end
102
+
103
+ shared_examples_for "a private embed resource" do
104
+ let(:organization) { resource.organization }
105
+ let!(:other_user) { create(:user, :confirmed, organization: organization) }
106
+ let!(:participatory_space_private_user) { create(:participatory_space_private_user, user: other_user, privatable_to: resource) }
107
+
108
+ before do
109
+ switch_to_host(organization.host)
110
+ end
111
+
112
+ context "when the resource is private" do
113
+ before do
114
+ resource.update!(private_space: true)
115
+ resource.update!(is_transparent: false) if resource.respond_to?(:is_transparent)
116
+ end
117
+
118
+ context "and user is a visitor" do
119
+ let(:user) { nil }
120
+
121
+ it_behaves_like "not rendering the embed link in the resource page"
122
+
123
+ it_behaves_like "a 404 page" do
124
+ let(:target_path) { widget_path }
125
+ end
126
+ end
127
+
128
+ context "and user is a registered user" do
129
+ let(:user) { create(:user, :confirmed, organization: organization) }
130
+
131
+ before do
132
+ sign_in user, scope: :user
133
+ end
134
+
135
+ it_behaves_like "not rendering the embed link in the resource page"
136
+
137
+ it_behaves_like "a 404 page" do
138
+ let(:target_path) { widget_path }
139
+ end
140
+ end
141
+
142
+ context "and user is a private user" do
143
+ let(:user) { other_user }
144
+
145
+ before do
146
+ sign_in user, scope: :user
147
+ end
148
+
149
+ it_behaves_like "a 404 page" do
150
+ let(:target_path) { widget_path }
151
+ end
152
+ end
153
+ end
154
+ end
155
+
156
+ shared_examples_for "a transparent private embed resource" do
157
+ let(:organization) { resource.organization }
158
+ let!(:other_user) { create(:user, :confirmed, organization: organization) }
159
+ let!(:participatory_space_private_user) { create(:participatory_space_private_user, user: other_user, privatable_to: resource) }
160
+
161
+ before do
162
+ switch_to_host(organization.host)
163
+ end
164
+
165
+ context "when the resource is private" do
166
+ before do
167
+ resource.update!(private_space: true)
168
+ resource.update!(is_transparent: true) if resource.respond_to?(:is_transparent)
169
+ end
170
+
171
+ context "and user is a visitor" do
172
+ let(:user) { nil }
173
+
174
+ it_behaves_like "rendering the embed page correctly"
175
+ end
176
+
177
+ context "and user is a registered user" do
178
+ let(:user) { create(:user, :confirmed, organization: organization) }
179
+
180
+ before do
181
+ sign_in user, scope: :user
182
+ end
183
+
184
+ it_behaves_like "rendering the embed page correctly"
185
+ end
186
+
187
+ context "and user is a private user" do
188
+ let(:user) { other_user }
189
+
190
+ before do
191
+ sign_in user, scope: :user
192
+ end
193
+
194
+ it_behaves_like "rendering the embed page correctly"
195
+ end
196
+ end
197
+ end
198
+
199
+ shared_examples_for "a moderated embed resource" do
200
+ include_context "with a component"
201
+
202
+ context "when the resource is moderated" do
203
+ let!(:moderation) { create(:moderation, reportable: resource, hidden_at: 2.days.ago) }
204
+
205
+ it_behaves_like "a 404 page" do
206
+ let(:target_path) { widget_path }
207
+ end
208
+ end
209
+ end
210
+
211
+ shared_examples_for "a withdrawn embed resource" do
212
+ include_context "with a component"
213
+
214
+ context "when the resource is withdrawn" do
215
+ before do
216
+ resource.update!(state: "withdrawn")
217
+ end
218
+
219
+ it_behaves_like "not rendering the embed link in the resource page"
220
+
221
+ it_behaves_like "a 404 page" do
222
+ let(:target_path) { widget_path }
223
+ end
224
+ end
225
+ end
@@ -14,14 +14,14 @@ shared_examples_for "has attachment collections" do
14
14
 
15
15
  it "shows them" do
16
16
  within ".attachments .documents" do
17
- expect(page).to have_content(/#{translated(attachment_collection.name, locale: :en)}/i)
17
+ expect(page).to have_content(translated(attachment_collection.name))
18
18
  end
19
19
  end
20
20
 
21
21
  it "show their documents" do
22
22
  within ".attachments .documents #docs-collection-#{attachment_collection.id}", visible: false do
23
- expect(page).to have_content(:all, /#{translated(document.title, locale: :en)}/i)
24
- expect(page).not_to have_content(:all, /#{translated(other_document.title, locale: :en)}/i)
23
+ expect(page).to have_content(:all, translated(document.title))
24
+ expect(page).not_to have_content(:all, translated(other_document.title))
25
25
  end
26
26
  end
27
27
  end
@@ -39,7 +39,9 @@ shared_examples_for "has attachment collections" do
39
39
 
40
40
  it "shows them ordered" do
41
41
  within ".attachments .documents" do
42
- expect(translated(first_attachment_collection.name, locale: :en)).to appear_before(translated(last_attachment_collection.name, locale: :en))
42
+ expect(decidim_escape_translated(first_attachment_collection.name).gsub("&quot;",
43
+ "\"")).to appear_before(decidim_escape_translated(last_attachment_collection.name).gsub("&quot;",
44
+ "\""))
43
45
  end
44
46
  end
45
47
  end
@@ -55,8 +57,8 @@ shared_examples_for "has attachment collections" do
55
57
 
56
58
  it "is not present" do
57
59
  within ".attachments .documents" do
58
- expect(page).to have_content(/#{translated(attachment_collection.name, locale: :en)}/i)
59
- expect(page).not_to have_content(/#{translated(empty_attachment_collection.name, locale: :en)}/i)
60
+ expect(page).to have_content(translated(attachment_collection.name))
61
+ expect(page).not_to have_content(translated(empty_attachment_collection.name))
60
62
  end
61
63
  end
62
64
  end
@@ -14,7 +14,7 @@ shared_examples_for "has attachments" do
14
14
 
15
15
  it "shows them" do
16
16
  within "div.wrapper .documents" do
17
- expect(page).to have_content(/#{translated(document.title, locale: :en)}/i)
17
+ expect(page).to have_content(translated(document.title))
18
18
  end
19
19
 
20
20
  within "div.wrapper .images" do
@@ -27,7 +27,7 @@ shared_examples_for "has attachments" do
27
27
  let!(:last_document) { create(:attachment, :with_pdf, attached_to: attached_to, weight: 2) }
28
28
  let!(:first_document) { create(:attachment, :with_pdf, attached_to: attached_to, weight: 1) }
29
29
  let!(:last_image) { create(:attachment, attached_to: attached_to, weight: 2) }
30
- let!(:fist_image) { create(:attachment, attached_to: attached_to, weight: 1) }
30
+ let!(:first_image) { create(:attachment, attached_to: attached_to, weight: 1) }
31
31
 
32
32
  before do
33
33
  visit current_path
@@ -35,11 +35,11 @@ shared_examples_for "has attachments" do
35
35
 
36
36
  it "shows them ordered" do
37
37
  within "div.wrapper .documents" do
38
- expect(translated(first_document.title, locale: :en)).to appear_before(translated(last_document.title, locale: :en))
38
+ expect(decidim_escape_translated(first_document.title).gsub("&quot;", "\"")).to appear_before(decidim_escape_translated(last_document.title).gsub("&quot;", "\""))
39
39
  end
40
40
 
41
41
  within "div.wrapper .images" do
42
- expect(strip_tags(translated(fist_image.title, locale: :en))).to appear_before(strip_tags(translated(last_image.title, locale: :en)))
42
+ expect(strip_tags(translated(first_image.title, locale: :en))).to appear_before(strip_tags(translated(last_image.title, locale: :en)))
43
43
  end
44
44
  end
45
45
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  shared_examples_for "has category" do
4
+ let(:participatory_space) { subject.participatory_space }
5
+
4
6
  context "when the category is from another organization" do
5
7
  before do
6
8
  subject.category = create(:category)
@@ -8,4 +10,29 @@ shared_examples_for "has category" do
8
10
 
9
11
  it { is_expected.not_to be_valid }
10
12
  end
13
+
14
+ context "when the category is from the same organization" do
15
+ before do
16
+ subject.category = create(:category, participatory_space: participatory_space)
17
+ end
18
+
19
+ it { is_expected.to be_valid }
20
+ end
21
+
22
+ context "when the resource is being deleted" do
23
+ before do
24
+ subject.category = create(:category, participatory_space: participatory_space)
25
+ subject.save!
26
+ end
27
+
28
+ it "persists the categorization" do
29
+ expect(subject.categorization).to be_persisted
30
+ end
31
+
32
+ it "deletes the categorization" do
33
+ expect(Decidim::Categorization.count).to eq(1)
34
+ expect { subject.destroy }.to change(Decidim::Categorization, :count).by(-1)
35
+ expect(Decidim::Categorization.count).to eq(0)
36
+ end
37
+ end
11
38
  end
@@ -8,7 +8,7 @@ shared_examples_for "has reference" do
8
8
 
9
9
  context "when there is not a custom resource reference generator present" do
10
10
  it "generates a valid reference" do
11
- expect(subject.reference).to match(/[A-z]+/)
11
+ expect(subject.reference).to match(/[a-zA-Z]+/)
12
12
  end
13
13
  end
14
14
 
@@ -8,8 +8,7 @@ shared_examples_for "has space in m-cell" do
8
8
  let(:show_space) { true }
9
9
 
10
10
  it "renders the space where the model belongs to" do
11
- expect(cell_html).to have_selector(".card__top .card__content.text-small")
12
- expect(cell_html).to have_content(translated(model.component.participatory_space.title, locale: :en))
11
+ expect(cell_html).to have_content(translated(model.component.participatory_space.title))
13
12
  end
14
13
  end
15
14
  end
@@ -35,17 +35,20 @@ shared_examples_for "resource endorsed event" do
35
35
  end
36
36
 
37
37
  describe "email_intro" do
38
+ let(:resource_title) { decidim_sanitize_translated(resource.title) }
38
39
  it "is generated correctly" do
39
40
  expect(subject.email_intro)
40
- .to eq("#{author.name} #{author_presenter.nickname}, who you are following," \
41
- " has just endorsed \"#{translated resource.title}\" and we think it may be interesting to you. Check it out and contribute:")
41
+ .to eq("#{author.name} #{author_presenter.nickname}, who you are following, " \
42
+ "has just endorsed \"#{resource_title}\" and we think it may be interesting to you. Check it out and contribute:")
42
43
  end
43
44
  end
44
45
 
45
46
  describe "notification_title" do
47
+ let(:resource_title) { decidim_sanitize_translated(resource.title) }
48
+
46
49
  it "is generated correctly" do
47
50
  expect(subject.notification_title)
48
- .to include("The <a href=\"#{resource_path}\">#{translated resource.title}</a> #{resource_type} has been endorsed by ")
51
+ .to include("The <a href=\"#{resource_path}\">#{resource_title}</a> #{resource_type} has been endorsed by ")
49
52
 
50
53
  expect(subject.notification_title)
51
54
  .to include("<a href=\"/profiles/#{author.nickname}\">#{author.name} #{author_presenter.nickname}</a>.")
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ shared_examples "generates routes without query strings on slug" do
6
+ let(:organization) { create(:organization, host: "1.lvh.me") }
7
+ let(:participatory_space) { create(factory_name, organization: organization) }
8
+ let(:component) { create(:component, id: 1, participatory_space: participatory_space) }
9
+ let(:resource) { create(:dummy_resource, id: 1, component: component) }
10
+
11
+ context "with a component resource" do
12
+ describe "#url" do
13
+ subject { described_class.new(resource).url }
14
+
15
+ it { is_expected.to eq("http://1.lvh.me:#{Capybara.server_port}/#{route_fragment}/f/1/dummy_resources/1") }
16
+
17
+ context "when specific port configured" do
18
+ before do
19
+ allow(ActionMailer::Base)
20
+ .to receive(:default_url_options)
21
+ .and_return(port: 3000)
22
+ end
23
+
24
+ it { is_expected.to eq("http://1.lvh.me:3000/#{route_fragment}/f/1/dummy_resources/1") }
25
+ end
26
+ end
27
+
28
+ describe "#path" do
29
+ subject { described_class.new(resource).path }
30
+
31
+ it { is_expected.to eq("/#{route_fragment}/f/1/dummy_resources/1") }
32
+ end
33
+
34
+ describe "#show" do
35
+ subject { described_class.new(participatory_space).show }
36
+
37
+ it { is_expected.to eq("/admin/#{admin_route_fragment}") }
38
+ end
39
+
40
+ describe "#edit" do
41
+ subject { described_class.new(participatory_space).edit }
42
+
43
+ it { is_expected.to eq("/admin/#{admin_route_fragment}/edit") }
44
+ end
45
+ end
46
+
47
+ context "with a polymorphic resource" do
48
+ let(:nested_resource) do
49
+ create(:nested_dummy_resource, id: 1, dummy_resource: resource)
50
+ end
51
+
52
+ describe "#url" do
53
+ subject { described_class.new([resource, nested_resource]).url }
54
+
55
+ it { is_expected.to eq("http://1.lvh.me:#{Capybara.server_port}/#{route_fragment}/f/1/dummy_resources/1/nested_dummy_resources/1") }
56
+
57
+ context "when specific port configured" do
58
+ before do
59
+ allow(ActionMailer::Base)
60
+ .to receive(:default_url_options)
61
+ .and_return(port: 3000)
62
+ end
63
+
64
+ it { is_expected.to eq("http://1.lvh.me:3000/#{route_fragment}/f/1/dummy_resources/1/nested_dummy_resources/1") }
65
+ end
66
+ end
67
+
68
+ describe "#path" do
69
+ subject { described_class.new([resource, nested_resource]).path }
70
+
71
+ it { is_expected.to eq("/#{route_fragment}/f/1/dummy_resources/1/nested_dummy_resources/1") }
72
+ end
73
+
74
+ describe "#index" do
75
+ subject { described_class.new([resource, nested_resource]).index }
76
+
77
+ it { is_expected.to eq("/#{route_fragment}/f/1/dummy_resources/1/nested_dummy_resources") }
78
+ end
79
+
80
+ describe "#admin_index" do
81
+ subject { described_class.new([resource, nested_resource]).admin_index }
82
+
83
+ it { is_expected.to eq("/admin/#{admin_route_fragment}/components/1/manage/dummy_resources/1/nested_dummy_resources") }
84
+ end
85
+
86
+ describe "#show" do
87
+ subject { described_class.new([resource, nested_resource]).show }
88
+
89
+ it { is_expected.to eq("/admin/#{admin_route_fragment}/components/1/manage/dummy_resources/1/nested_dummy_resources/1") }
90
+ end
91
+
92
+ describe "#edit" do
93
+ subject { described_class.new([resource, nested_resource]).edit }
94
+
95
+ it { is_expected.to eq("/admin/#{admin_route_fragment}/components/1/manage/dummy_resources/1/nested_dummy_resources/1/edit") }
96
+ end
97
+ end
98
+
99
+ context "with a participatory_space" do
100
+ describe "#url" do
101
+ subject { described_class.new(participatory_space).url }
102
+
103
+ it { is_expected.to eq("http://1.lvh.me:#{Capybara.server_port}/#{route_fragment}") }
104
+
105
+ context "when specific port configured" do
106
+ before do
107
+ allow(ActionMailer::Base)
108
+ .to receive(:default_url_options)
109
+ .and_return(port: 3000)
110
+ end
111
+
112
+ it { is_expected.to eq("http://1.lvh.me:3000/#{route_fragment}") }
113
+ end
114
+ end
115
+
116
+ describe "#path" do
117
+ subject { described_class.new(participatory_space).path }
118
+
119
+ it { is_expected.to eq("/#{route_fragment}") }
120
+ end
121
+
122
+ describe "#show" do
123
+ subject { described_class.new(participatory_space).show }
124
+
125
+ it { is_expected.to eq("/admin/#{admin_route_fragment}") }
126
+ end
127
+
128
+ describe "#edit" do
129
+ subject { described_class.new(participatory_space).edit }
130
+
131
+ it { is_expected.to eq("/admin/#{admin_route_fragment}/edit") }
132
+ end
133
+ end
134
+ end
@@ -81,7 +81,7 @@ shared_examples "searchable results" do
81
81
  find("input#term").native.send_keys :enter
82
82
 
83
83
  expect(page).to have_current_path decidim.search_path, ignore_query: true
84
- expect(page).to have_content(/results for the search: "#{term}"/i)
84
+ expect(page).to have_content(/results for the search: "#{term.upcase}"/i)
85
85
  expect(page).to have_selector(".filters__section")
86
86
  expect(page.find("#search-count .section-heading").text.to_i).not_to be_positive
87
87
  end