decidim-elections 0.31.0.rc2 → 0.31.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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/app/commands/decidim/elections/admin/update_election.rb +3 -3
  3. data/app/controllers/concerns/decidim/elections/uses_votes_booth.rb +8 -2
  4. data/app/controllers/decidim/elections/per_question_votes_controller.rb +28 -1
  5. data/app/forms/decidim/elections/admin/election_form.rb +10 -0
  6. data/app/forms/decidim/elections/censuses/internal_users_form.rb +7 -13
  7. data/app/helpers/decidim/elections/application_helper.rb +12 -0
  8. data/app/models/decidim/elections/election.rb +4 -8
  9. data/app/packs/entrypoints/decidim_elections.js +1 -0
  10. data/app/packs/src/decidim/elections/live_results_update.js +22 -0
  11. data/app/packs/src/decidim/elections/question_status_checker.js +42 -0
  12. data/app/packs/stylesheets/decidim/elections/elections.scss +4 -0
  13. data/app/permissions/decidim/elections/admin/permissions.rb +3 -5
  14. data/app/presenters/decidim/elections/election_presenter.rb +8 -3
  15. data/app/views/decidim/elections/admin/census/edit.html.erb +15 -13
  16. data/app/views/decidim/elections/admin/dashboard/_questions.html.erb +1 -0
  17. data/app/views/decidim/elections/admin/dashboard/_questions_with_results.html.erb +6 -0
  18. data/app/views/decidim/elections/admin/elections/_election-tr.html.erb +1 -1
  19. data/app/views/decidim/elections/admin/elections/_form.html.erb +3 -3
  20. data/app/views/decidim/elections/admin/elections/dashboard.html.erb +4 -4
  21. data/app/views/decidim/elections/admin/elections/new.html.erb +1 -1
  22. data/app/views/decidim/elections/admin/questions/_form.html.erb +1 -30
  23. data/app/views/decidim/elections/admin/questions/edit_questions.html.erb +2 -2
  24. data/app/views/decidim/elections/censuses/_internal_users_form.html.erb +15 -0
  25. data/app/views/decidim/elections/elections/_election_aside.html.erb +2 -1
  26. data/app/views/decidim/elections/elections/_vote_results_question.html.erb +4 -0
  27. data/app/views/decidim/elections/per_question_votes/show.html.erb +6 -1
  28. data/app/views/decidim/elections/votes/receipt.html.erb +18 -1
  29. data/app/views/decidim/elections/votes/show.html.erb +4 -1
  30. data/config/locales/ca-IT.yml +7 -4
  31. data/config/locales/ca.yml +7 -4
  32. data/config/locales/cs.yml +28 -0
  33. data/config/locales/de.yml +4 -0
  34. data/config/locales/en.yml +8 -5
  35. data/config/locales/es-MX.yml +7 -4
  36. data/config/locales/es-PY.yml +7 -4
  37. data/config/locales/es.yml +7 -4
  38. data/config/locales/eu.yml +9 -6
  39. data/config/locales/fi-plain.yml +7 -4
  40. data/config/locales/fi.yml +7 -4
  41. data/config/locales/fr-CA.yml +16 -4
  42. data/config/locales/fr.yml +16 -4
  43. data/config/locales/ja.yml +6 -4
  44. data/config/locales/pt-BR.yml +194 -0
  45. data/config/locales/sv.yml +10 -4
  46. data/lib/decidim/elections/admin_engine.rb +4 -4
  47. data/lib/decidim/elections/engine.rb +6 -0
  48. data/lib/decidim/elections/test/per_question_vote_examples.rb +114 -3
  49. data/lib/decidim/elections/test/vote_examples.rb +7 -3
  50. data/lib/decidim/elections/version.rb +1 -1
  51. metadata +15 -14
@@ -10,6 +10,7 @@ shared_examples "a csv token per question votable election" do
10
10
  click_on "Access"
11
11
  expect(page).to have_current_path(election_vote_path(question1))
12
12
  expect(page).to have_content(translated_attribute(question1.body))
13
+ expect(page).to have_content(strip_tags(translated_attribute(question1.description)))
13
14
  choose translated_attribute(question1.response_options.first.body)
14
15
  click_on "Cast vote"
15
16
  expect(page).to have_content("Your vote has been successfully cast.")
@@ -18,6 +19,7 @@ shared_examples "a csv token per question votable election" do
18
19
  # wait for javascript to update the page
19
20
  sleep 2
20
21
  expect(page).to have_current_path(election_vote_path(question2))
22
+ expect(page).to have_content(strip_tags(translated_attribute(question2.description)))
21
23
  click_on "Cast vote"
22
24
  expect(page).to have_content("There was a problem casting your vote.")
23
25
  check translated_attribute(question2.response_options.first.body)
@@ -29,7 +31,7 @@ shared_examples "a csv token per question votable election" do
29
31
  expect(page).to have_current_path(election_path)
30
32
  expect(page).to have_content("You have already voted.")
31
33
  expect(election.votes.where(voter_uid:).size).to eq(3)
32
- click_on "Vote"
34
+ click_on "Edit vote"
33
35
  expect(page).to have_current_path(new_election_vote_path)
34
36
  fill_in "Email", with: election.voters.first.data["email"]
35
37
  fill_in "Token", with: election.voters.first.data["token"]
@@ -52,6 +54,7 @@ shared_examples "a per question votable election" do
52
54
  expect(page).to have_content(translated_attribute(question1.body))
53
55
  expect(page).to have_content(translated_attribute(question2.body))
54
56
  click_on "Vote"
57
+ expect(page).to have_content(strip_tags(translated_attribute(question1.description)))
55
58
  choose translated_attribute(question1.response_options.first.body)
56
59
  click_on "Cast vote"
57
60
  expect(page).to have_current_path(waiting_election_votes_path)
@@ -61,6 +64,7 @@ shared_examples "a per question votable election" do
61
64
  # wait for javascript to update the page
62
65
  sleep 2
63
66
  expect(page).to have_current_path(election_vote_path(question2))
67
+ expect(page).to have_content(strip_tags(translated_attribute(question2.description)))
64
68
  check translated_attribute(question2.response_options.first.body)
65
69
  click_on "Cast vote"
66
70
  expect(page).to have_current_path(receipt_election_votes_path)
@@ -69,7 +73,7 @@ shared_examples "a per question votable election" do
69
73
  expect(page).to have_current_path(election_path)
70
74
  expect(page).to have_content("You have already voted.")
71
75
  expect(election.votes.where(voter_uid:).size).to eq(2)
72
- click_on "Vote"
76
+ click_on "Edit vote"
73
77
  expect(find("input[value='#{question1.response_options.first.id}']")).to be_checked
74
78
  choose translated_attribute(question1.response_options.second.body)
75
79
  click_on "Cast vote"
@@ -101,6 +105,7 @@ shared_examples "a per question votable election with published results" do
101
105
  # wait for javascript to update the page
102
106
  sleep 2
103
107
  expect(page).to have_current_path(election_vote_path(question2))
108
+ expect(page).to have_content(strip_tags(translated_attribute(question2.description)))
104
109
  check translated_attribute(question2.response_options.first.body)
105
110
  click_on "Cast vote"
106
111
  expect(page).to have_current_path(receipt_election_votes_path)
@@ -109,7 +114,7 @@ shared_examples "a per question votable election with published results" do
109
114
  expect(page).to have_current_path(election_path)
110
115
  expect(page).to have_content("You have already voted.")
111
116
  expect(election.votes.where(voter_uid:).size).to eq(1)
112
- click_on "Vote"
117
+ click_on "Edit vote"
113
118
  expect(page).to have_current_path(election_vote_path(question2))
114
119
  expect(find("input[value='#{question2.response_options.first.id}']")).to be_checked
115
120
  expect(find("input[value='#{question2.response_options.second.id}']")).not_to be_checked
@@ -137,6 +142,7 @@ shared_examples "a per question votable election with already voted questions" d
137
142
  expect(page).to have_content(translated_attribute(question2.body))
138
143
  expect(page).to have_content(translated_attribute(question3.body))
139
144
  click_on "Vote"
145
+ expect(page).to have_content(strip_tags(translated_attribute(question1.description)))
140
146
  choose translated_attribute(question1.response_options.first.body)
141
147
  click_on "Cast vote"
142
148
  check translated_attribute(question2.response_options.first.body)
@@ -144,10 +150,12 @@ shared_examples "a per question votable election with already voted questions" d
144
150
  expect(page).to have_current_path(waiting_election_votes_path)
145
151
  click_on "Edit your vote"
146
152
  expect(page).to have_current_path(election_vote_path(question1))
153
+ expect(page).to have_content(strip_tags(translated_attribute(question1.description)))
147
154
  expect(find("input[value='#{question1.response_options.first.id}']")).to be_checked
148
155
  choose translated_attribute(question1.response_options.second.body)
149
156
  click_on "Cast vote"
150
157
  expect(page).to have_current_path(election_vote_path(question2))
158
+ expect(page).to have_content(strip_tags(translated_attribute(question2.description)))
151
159
  expect(find("input[value='#{question2.response_options.first.id}']")).to be_checked
152
160
  expect(find("input[value='#{question2.response_options.second.id}']")).not_to be_checked
153
161
  check translated_attribute(question2.response_options.second.body)
@@ -156,9 +164,112 @@ shared_examples "a per question votable election with already voted questions" d
156
164
  question1.update!(published_results_at: Time.current)
157
165
  click_on "Edit your vote"
158
166
  expect(page).to have_current_path(election_vote_path(question2))
167
+ expect(page).to have_content(strip_tags(translated_attribute(question2.description)))
159
168
  question2.update!(published_results_at: Time.current)
160
169
  click_on "Cast vote"
161
170
  expect(page).to have_current_path(waiting_election_votes_path)
162
171
  expect(page).to have_no_content("Edit your vote")
163
172
  end
164
173
  end
174
+
175
+ shared_examples "a per question votable election with automatic redirect when question closes" do
176
+ it "redirects to the waiting room when admin closes voting while user is on question page" do
177
+ expect(page).to have_content(translated_attribute(election.title))
178
+ click_on "Vote"
179
+ expect(page).to have_current_path(election_vote_path(question1))
180
+ expect(page).to have_content(strip_tags(translated_attribute(question1.description)))
181
+
182
+ # Simulate admin closing voting for question1 (publishing results)
183
+ question1.update!(published_results_at: Time.current)
184
+
185
+ # Wait for JavaScript polling to detect the change and redirect
186
+ expect(page).to have_current_path(waiting_election_votes_path, wait: 3)
187
+ expect(page).to have_content("Waiting for the next question")
188
+ end
189
+
190
+ it "redirects to next question when admin closes current question and next is available" do
191
+ # Enable question2 from the start
192
+ question2.update!(voting_enabled_at: Time.current)
193
+
194
+ expect(page).to have_content(translated_attribute(election.title))
195
+ click_on "Vote"
196
+ expect(page).to have_current_path(election_vote_path(question1))
197
+ expect(page).to have_content(strip_tags(translated_attribute(question1.description)))
198
+
199
+ # Simulate admin closing voting for question1
200
+ question1.update!(published_results_at: Time.current)
201
+
202
+ # Wait for JavaScript polling to detect the change and redirect
203
+ expect(page).to have_current_path(election_vote_path(question2), wait: 3)
204
+ expect(page).to have_content(strip_tags(translated_attribute(question2.description)))
205
+ end
206
+ end
207
+
208
+ shared_examples "a per question votable election with edit from receipt" do
209
+ it "allows editing votes from receipt page" do
210
+ click_on "Vote"
211
+ choose translated_attribute(question1.response_options.first.body)
212
+ click_on "Cast vote"
213
+ check translated_attribute(question2.response_options.first.body)
214
+ click_on "Cast vote"
215
+ expect(page).to have_current_path(receipt_election_votes_path)
216
+ expect(page).to have_link("Edit your vote")
217
+ expect(page).to have_link("Exit the voting booth")
218
+
219
+ click_on "Edit your vote"
220
+ expect(page).to have_current_path(election_vote_path(question2))
221
+ expect(find("input[value='#{question2.response_options.first.id}']")).to be_checked
222
+
223
+ click_on "Back"
224
+ expect(page).to have_current_path(election_vote_path(question1))
225
+ expect(find("input[value='#{question1.response_options.first.id}']")).to be_checked
226
+
227
+ choose translated_attribute(question1.response_options.second.body)
228
+ click_on "Cast vote"
229
+ expect(page).to have_current_path(election_vote_path(question2))
230
+
231
+ check translated_attribute(question2.response_options.second.body)
232
+ click_on "Cast vote"
233
+ expect(page).to have_current_path(receipt_election_votes_path)
234
+ expect(election.votes.where(voter_uid:).size).to eq(3)
235
+
236
+ click_on "Exit the voting booth"
237
+ expect(page).to have_current_path(election_path)
238
+ expect(page).to have_content("You have already voted.")
239
+ end
240
+ end
241
+
242
+ shared_examples "a per question votable election with edit from receipt when all questions enabled" do
243
+ it "allows editing any question from receipt page" do
244
+ click_on "Vote"
245
+ choose translated_attribute(question1.response_options.first.body)
246
+ click_on "Cast vote"
247
+ check translated_attribute(question2.response_options.first.body)
248
+ click_on "Cast vote"
249
+ expect(page).to have_current_path(receipt_election_votes_path)
250
+
251
+ click_on "Edit your vote"
252
+ expect(page).to have_current_path(election_vote_path(question2))
253
+
254
+ click_on "Back"
255
+ expect(page).to have_current_path(election_vote_path(question1))
256
+
257
+ choose translated_attribute(question1.response_options.second.body)
258
+ click_on "Cast vote"
259
+ expect(page).to have_current_path(election_vote_path(question2))
260
+ expect(find("input[value='#{question2.response_options.first.id}']")).to be_checked
261
+
262
+ click_on "Cast vote"
263
+ expect(page).to have_current_path(receipt_election_votes_path)
264
+
265
+ click_on "Edit your vote"
266
+ click_on "Back"
267
+ choose translated_attribute(question1.response_options.first.body)
268
+ click_on "Cast vote"
269
+ uncheck translated_attribute(question2.response_options.first.body)
270
+ check translated_attribute(question2.response_options.second.body)
271
+ click_on "Cast vote"
272
+ expect(page).to have_current_path(receipt_election_votes_path)
273
+ expect(election.votes.where(voter_uid:).size).to eq(2)
274
+ end
275
+ end
@@ -22,10 +22,12 @@ end
22
22
  def fill_in_votes
23
23
  expect(page).to have_current_path(election_vote_path(election.questions.first))
24
24
  expect(page).to have_content(translated_attribute(election.questions.first.body))
25
+ expect(page).to have_content(strip_tags(translated_attribute(election.questions.first.description)))
25
26
  choose translated_attribute(election.questions.first.response_options.first.body)
26
27
  click_on "Next"
27
28
  expect(page).to have_current_path(election_vote_path(election.questions.second))
28
29
  expect(page).to have_content(translated_attribute(election.questions.second.body))
30
+ expect(page).to have_content(strip_tags(translated_attribute(election.questions.second.description)))
29
31
  check translated_attribute(election.questions.second.response_options.first.body)
30
32
  check translated_attribute(election.questions.second.response_options.second.body)
31
33
  click_on "Next"
@@ -50,7 +52,7 @@ shared_examples "a votable election" do
50
52
  click_on "Vote"
51
53
 
52
54
  fill_in_votes
53
- click_on "Vote"
55
+ click_on "Edit vote"
54
56
  expect(page).to have_current_path(election_vote_path(election.questions.first))
55
57
  end
56
58
  end
@@ -129,7 +131,7 @@ shared_examples "a csv token votable election" do
129
131
  fill_in "Token", with: election.voters.first.data["token"]
130
132
  click_on "Access"
131
133
  fill_in_votes
132
- click_on "Vote"
134
+ click_on "Edit vote"
133
135
  expect(page).to have_current_path(new_election_vote_path)
134
136
  fill_in "Email", with: election.voters.first.data["email"]
135
137
  fill_in "Token", with: election.voters.first.data["token"]
@@ -167,7 +169,8 @@ end
167
169
 
168
170
  shared_examples "an editable votable election" do
169
171
  it "Allows the user to edit the vote" do
170
- click_on "Vote"
172
+ expect(page).to have_link("Edit vote")
173
+ click_on "Edit vote"
171
174
  expect(page).to have_current_path(election_vote_path(election.questions.first))
172
175
  expect(find("input[value='#{election.questions.first.response_options.first.id}']")).to be_checked
173
176
  expect(find("input[value='#{election.questions.first.response_options.second.id}']")).not_to be_checked
@@ -189,6 +192,7 @@ end
189
192
 
190
193
  shared_examples "a csv token editable votable election" do
191
194
  it "Allows the user to edit the vote" do
195
+ expect(page).to have_link("Vote")
192
196
  click_on "Vote"
193
197
  expect(page).to have_current_path(new_election_vote_path)
194
198
  expect(page).to have_content("Verify your identity")
@@ -3,7 +3,7 @@
3
3
  module Decidim
4
4
  module Elections
5
5
  def self.version
6
- "0.31.0.rc2"
6
+ "0.31.1"
7
7
  end
8
8
  end
9
9
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: decidim-elections
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.31.0.rc2
4
+ version: 0.31.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan Vergés
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-10-28 00:00:00.000000000 Z
11
+ date: 2026-01-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: decidim-admin
@@ -16,84 +16,84 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.31.0.rc2
19
+ version: 0.31.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 0.31.0.rc2
26
+ version: 0.31.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: decidim-core
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - '='
32
32
  - !ruby/object:Gem::Version
33
- version: 0.31.0.rc2
33
+ version: 0.31.1
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - '='
39
39
  - !ruby/object:Gem::Version
40
- version: 0.31.0.rc2
40
+ version: 0.31.1
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: decidim-forms
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - '='
46
46
  - !ruby/object:Gem::Version
47
- version: 0.31.0.rc2
47
+ version: 0.31.1
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - '='
53
53
  - !ruby/object:Gem::Version
54
- version: 0.31.0.rc2
54
+ version: 0.31.1
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: decidim-admin
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - '='
60
60
  - !ruby/object:Gem::Version
61
- version: 0.31.0.rc2
61
+ version: 0.31.1
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - '='
67
67
  - !ruby/object:Gem::Version
68
- version: 0.31.0.rc2
68
+ version: 0.31.1
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: decidim-dev
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - '='
74
74
  - !ruby/object:Gem::Version
75
- version: 0.31.0.rc2
75
+ version: 0.31.1
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - '='
81
81
  - !ruby/object:Gem::Version
82
- version: 0.31.0.rc2
82
+ version: 0.31.1
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: decidim-participatory_processes
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - '='
88
88
  - !ruby/object:Gem::Version
89
- version: 0.31.0.rc2
89
+ version: 0.31.1
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - '='
95
95
  - !ruby/object:Gem::Version
96
- version: 0.31.0.rc2
96
+ version: 0.31.1
97
97
  description: A module that aims to provide a set of tools to create elections in Decidim.
98
98
  email:
99
99
  - ivan@pokecode.net
@@ -155,6 +155,7 @@ files:
155
155
  - app/packs/src/decidim/elections/admin/census_form.js
156
156
  - app/packs/src/decidim/elections/admin/election_form.js
157
157
  - app/packs/src/decidim/elections/live_results_update.js
158
+ - app/packs/src/decidim/elections/question_status_checker.js
158
159
  - app/packs/src/decidim/elections/waiting_room.js
159
160
  - app/packs/stylesheets/decidim/elections/elections.scss
160
161
  - app/packs/stylesheets/decidim/elections/live_results_update.scss