decidim-forms 0.30.3 → 0.31.0.rc1

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 (159) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -10
  3. data/app/cells/decidim/forms/matrix_readonly/show.erb +1 -1
  4. data/app/cells/decidim/forms/matrix_readonly_cell.rb +3 -3
  5. data/app/cells/decidim/forms/question_readonly/show.erb +5 -5
  6. data/app/cells/decidim/forms/question_readonly/title_and_description.erb +3 -3
  7. data/app/cells/decidim/forms/response_readonly_cell.rb +9 -0
  8. data/app/cells/decidim/forms/step_navigation/show.erb +3 -3
  9. data/app/cells/decidim/forms/step_navigation_cell.rb +3 -3
  10. data/app/commands/decidim/forms/admin/update_questions.rb +6 -6
  11. data/app/commands/decidim/forms/{answer_questionnaire.rb → response_questionnaire.rb} +35 -35
  12. data/app/controllers/decidim/forms/admin/concerns/has_questionnaire.rb +12 -12
  13. data/app/controllers/decidim/forms/admin/concerns/{has_questionnaire_answers.rb → has_questionnaire_responses.rb} +23 -19
  14. data/app/controllers/decidim/forms/concerns/has_questionnaire.rb +38 -33
  15. data/app/forms/decidim/forms/admin/display_condition_form.rb +14 -14
  16. data/app/forms/decidim/forms/admin/question_form.rb +3 -3
  17. data/app/forms/decidim/forms/admin/{answer_option_form.rb → response_option_form.rb} +3 -3
  18. data/app/forms/decidim/forms/questionnaire_form.rb +8 -9
  19. data/app/forms/decidim/forms/{answer_choice_form.rb → response_choice_form.rb} +5 -5
  20. data/app/forms/decidim/forms/{answer_form.rb → response_form.rb} +9 -9
  21. data/app/helpers/decidim/forms/admin/application_helper.rb +2 -2
  22. data/app/helpers/decidim/forms/admin/concerns/{has_questionnaire_answers_pagination_helper.rb → has_questionnaire_responses_pagination_helper.rb} +4 -4
  23. data/app/helpers/decidim/forms/admin/concerns/{has_questionnaire_answers_url_helper.rb → has_questionnaire_responses_url_helper.rb} +5 -5
  24. data/app/helpers/decidim/forms/admin/questionnaire_responses_helper.rb +32 -0
  25. data/app/helpers/decidim/forms/application_helper.rb +1 -2
  26. data/app/jobs/decidim/forms/{export_questionnaire_answers_job.rb → export_questionnaire_responses_job.rb} +5 -5
  27. data/app/models/decidim/forms/display_condition.rb +20 -20
  28. data/app/models/decidim/forms/question.rb +14 -13
  29. data/app/models/decidim/forms/question_matrix_row.rb +1 -1
  30. data/app/models/decidim/forms/questionnaire.rb +11 -6
  31. data/app/models/decidim/forms/{answer.rb → response.rb} +9 -9
  32. data/app/models/decidim/forms/response_choice.rb +22 -0
  33. data/app/models/decidim/forms/{answer_option.rb → response_option.rb} +5 -5
  34. data/app/packs/entrypoints/decidim_forms_admin.js +3 -3
  35. data/app/packs/src/decidim/forms/admin/collapsible_questions.js +12 -10
  36. data/app/packs/src/decidim/forms/admin/forms.js +35 -35
  37. data/app/packs/src/decidim/forms/admin/{publish_answers_buttons.js → publish_responses_buttons.js} +12 -12
  38. data/app/packs/src/decidim/forms/display_conditions.component.js +12 -12
  39. data/app/packs/src/decidim/forms/forms.js +8 -29
  40. data/app/packs/stylesheets/decidim/forms/forms.scss +2 -2
  41. data/app/presenters/decidim/forms/admin/questionnaire_participant_presenter.rb +13 -13
  42. data/app/presenters/decidim/forms/admin/{questionnaire_answer_presenter.rb → questionnaire_response_presenter.rb} +15 -15
  43. data/app/presenters/decidim/forms/admin_log/question_presenter.rb +3 -3
  44. data/app/presenters/decidim/forms/{answer_option_presenter.rb → response_option_presenter.rb} +2 -2
  45. data/app/queries/decidim/forms/questionnaire_participant.rb +5 -5
  46. data/app/queries/decidim/forms/questionnaire_participants.rb +6 -6
  47. data/app/queries/decidim/forms/questionnaire_user_responses.rb +32 -0
  48. data/app/views/decidim/forms/admin/questionnaires/_display_condition.html.erb +9 -9
  49. data/app/views/decidim/forms/admin/questionnaires/_form.html.erb +2 -2
  50. data/app/views/decidim/forms/admin/questionnaires/_matrix_row.html.erb +3 -3
  51. data/app/views/decidim/forms/admin/questionnaires/_question.html.erb +21 -11
  52. data/app/views/decidim/forms/admin/questionnaires/_questions_form.html.erb +13 -41
  53. data/app/views/decidim/forms/admin/questionnaires/{_answer_option.html.erb → _response_option.html.erb} +7 -7
  54. data/app/views/decidim/forms/admin/questionnaires/_response_option_template.html.erb +7 -0
  55. data/app/views/decidim/forms/admin/questionnaires/_separator.html.erb +11 -1
  56. data/app/views/decidim/forms/admin/questionnaires/_title_and_description.html.erb +12 -2
  57. data/app/views/decidim/forms/admin/questionnaires/edit.html.erb +1 -1
  58. data/app/views/decidim/forms/admin/questionnaires/edit_questions.html.erb +8 -8
  59. data/app/views/decidim/forms/admin/questionnaires/responses/index.html.erb +80 -0
  60. data/app/views/decidim/forms/admin/questionnaires/responses/show.html.erb +43 -0
  61. data/app/views/decidim/forms/questionnaires/_questionnaire.html.erb +22 -26
  62. data/app/views/decidim/forms/questionnaires/_questionnaire_readonly.html.erb +8 -3
  63. data/app/views/decidim/forms/questionnaires/_response.html.erb +61 -0
  64. data/app/views/decidim/forms/questionnaires/edit.html.erb +2 -2
  65. data/app/views/decidim/forms/questionnaires/responses/_files.html.erb +1 -0
  66. data/app/views/decidim/forms/questionnaires/responses/_long_response.html.erb +3 -0
  67. data/app/views/decidim/forms/questionnaires/{answers → responses}/_matrix_multiple.html.erb +16 -15
  68. data/app/views/decidim/forms/questionnaires/{answers → responses}/_matrix_single.html.erb +17 -16
  69. data/app/views/decidim/forms/questionnaires/responses/_multiple_option.html.erb +26 -0
  70. data/app/views/decidim/forms/questionnaires/responses/_separator.html.erb +1 -0
  71. data/app/views/decidim/forms/questionnaires/responses/_short_response.html.erb +3 -0
  72. data/app/views/decidim/forms/questionnaires/responses/_single_option.html.erb +33 -0
  73. data/app/views/decidim/forms/questionnaires/responses/_sorting.html.erb +26 -0
  74. data/app/views/decidim/forms/questionnaires/responses/_title_and_description.html.erb +1 -0
  75. data/app/views/decidim/forms/questionnaires/show.html.erb +10 -10
  76. data/config/assets.rb +2 -2
  77. data/config/locales/ar.yml +2 -22
  78. data/config/locales/bg.yml +10 -36
  79. data/config/locales/ca-IT.yml +80 -74
  80. data/config/locales/ca.yml +80 -74
  81. data/config/locales/cs.yml +79 -76
  82. data/config/locales/de.yml +79 -72
  83. data/config/locales/el.yml +10 -36
  84. data/config/locales/en.yml +90 -84
  85. data/config/locales/es-MX.yml +82 -76
  86. data/config/locales/es-PY.yml +82 -76
  87. data/config/locales/es.yml +80 -74
  88. data/config/locales/eu.yml +78 -72
  89. data/config/locales/fi-plain.yml +78 -72
  90. data/config/locales/fi.yml +76 -70
  91. data/config/locales/fr-CA.yml +77 -68
  92. data/config/locales/fr.yml +77 -68
  93. data/config/locales/ga-IE.yml +4 -7
  94. data/config/locales/gl.yml +4 -22
  95. data/config/locales/hu.yml +4 -22
  96. data/config/locales/id-ID.yml +2 -22
  97. data/config/locales/it.yml +8 -40
  98. data/config/locales/ja.yml +87 -81
  99. data/config/locales/lb.yml +8 -36
  100. data/config/locales/lt.yml +10 -36
  101. data/config/locales/lv.yml +4 -22
  102. data/config/locales/nl.yml +10 -36
  103. data/config/locales/no.yml +10 -36
  104. data/config/locales/pl.yml +10 -36
  105. data/config/locales/pt-BR.yml +10 -36
  106. data/config/locales/pt.yml +8 -36
  107. data/config/locales/ro-RO.yml +29 -51
  108. data/config/locales/ru.yml +2 -9
  109. data/config/locales/sk.yml +2 -22
  110. data/config/locales/sl.yml +0 -5
  111. data/config/locales/sv.yml +89 -55
  112. data/config/locales/tr-TR.yml +8 -36
  113. data/config/locales/val-ES.yml +2 -0
  114. data/config/locales/zh-CN.yml +8 -36
  115. data/config/locales/zh-TW.yml +10 -36
  116. data/db/migrate/20190315203056_add_session_token_to_decidim_forms_answers.rb +1 -1
  117. data/db/migrate/20250314150250_rename_answer_to_response_in_decidim_forms.rb +30 -0
  118. data/db/migrate/20250319130003_change_question_types_in_questions.rb +14 -0
  119. data/lib/decidim/api/question_matrix_row_type.rb +13 -0
  120. data/lib/decidim/api/question_type.rb +4 -3
  121. data/lib/decidim/api/questionnaire_type.rb +1 -0
  122. data/lib/decidim/api/response_option_type.rb +13 -0
  123. data/lib/decidim/exporters/form_pdf.rb +33 -33
  124. data/lib/decidim/forms/api.rb +2 -1
  125. data/lib/decidim/forms/{download_your_data_user_answers_serializer.rb → download_your_data_user_responses_serializer.rb} +3 -3
  126. data/lib/decidim/forms/engine.rb +2 -2
  127. data/lib/decidim/forms/test/factories.rb +24 -24
  128. data/lib/decidim/forms/test/shared_examples/has_questionnaire.rb +176 -176
  129. data/lib/decidim/forms/test/shared_examples/manage_questionnaire_responses.rb +159 -0
  130. data/lib/decidim/forms/test/shared_examples/manage_questionnaires/add_display_conditions.rb +19 -19
  131. data/lib/decidim/forms/test/shared_examples/manage_questionnaires/add_questions.rb +44 -44
  132. data/lib/decidim/forms/test/shared_examples/manage_questionnaires/update_display_conditions.rb +10 -10
  133. data/lib/decidim/forms/test/shared_examples/manage_questionnaires/update_questions.rb +43 -140
  134. data/lib/decidim/forms/test/shared_examples/manage_questionnaires.rb +4 -4
  135. data/lib/decidim/forms/test.rb +1 -1
  136. data/lib/decidim/forms/user_responses_serializer.rb +110 -0
  137. data/lib/decidim/forms/version.rb +1 -1
  138. data/lib/decidim/forms.rb +2 -2
  139. metadata +48 -45
  140. data/app/cells/decidim/forms/answer_readonly_cell.rb +0 -9
  141. data/app/helpers/decidim/forms/admin/questionnaire_answers_helper.rb +0 -30
  142. data/app/models/decidim/forms/answer_choice.rb +0 -22
  143. data/app/queries/decidim/forms/questionnaire_user_answers.rb +0 -32
  144. data/app/views/decidim/forms/admin/questionnaires/_answer_option_template.html.erb +0 -7
  145. data/app/views/decidim/forms/admin/questionnaires/answers/index.html.erb +0 -49
  146. data/app/views/decidim/forms/admin/questionnaires/answers/show.html.erb +0 -43
  147. data/app/views/decidim/forms/questionnaires/_answer.html.erb +0 -61
  148. data/app/views/decidim/forms/questionnaires/answers/_files.html.erb +0 -1
  149. data/app/views/decidim/forms/questionnaires/answers/_long_answer.html.erb +0 -3
  150. data/app/views/decidim/forms/questionnaires/answers/_multiple_option.html.erb +0 -25
  151. data/app/views/decidim/forms/questionnaires/answers/_separator.html.erb +0 -1
  152. data/app/views/decidim/forms/questionnaires/answers/_short_answer.html.erb +0 -3
  153. data/app/views/decidim/forms/questionnaires/answers/_single_option.html.erb +0 -32
  154. data/app/views/decidim/forms/questionnaires/answers/_sorting.html.erb +0 -26
  155. data/app/views/decidim/forms/questionnaires/answers/_title_and_description.html.erb +0 -1
  156. data/lib/decidim/api/answer_option_type.rb +0 -13
  157. data/lib/decidim/forms/test/shared_examples/manage_questionnaire_answers.rb +0 -149
  158. data/lib/decidim/forms/user_answers_serializer.rb +0 -105
  159. /data/app/cells/decidim/forms/{answer_readonly → response_readonly}/show.erb +0 -0
@@ -16,7 +16,7 @@ shared_examples_for "update questions" do
16
16
  fill_in "questions_questions_#{question.id}_body_en", with: "Modified question"
17
17
  fill_in "questions_questions_#{question.id}_max_characters", with: 30
18
18
  check "Mandatory"
19
- select "Long answer", from: "Type"
19
+ select "Long response", from: "Type"
20
20
  end
21
21
 
22
22
  click_on "Save"
@@ -29,7 +29,7 @@ shared_examples_for "update questions" do
29
29
  expect(page).to have_no_css("input[value='This is the first question']")
30
30
  expect(page).to have_css("input#questions_questions_#{question.id}_mandatory[checked]")
31
31
  expect(page).to have_css("input#questions_questions_#{question.id}_max_characters[value='30']")
32
- expect(page).to have_css("select#questions_questions_#{question.id}_question_type option[value='long_answer'][selected]")
32
+ expect(page).to have_css("select#questions_questions_#{question.id}_question_type option[value='long_response'][selected]")
33
33
  end
34
34
 
35
35
  it "re-renders the form when the information is invalid and displays errors" do
@@ -85,7 +85,7 @@ shared_examples_for "update questions" do
85
85
 
86
86
  expect(page).to have_admin_callout("successfully")
87
87
 
88
- click_on "Manage questions"
88
+ click_on "Questions"
89
89
 
90
90
  expect(page).to have_css(".questionnaire-question", count: 0)
91
91
  end
@@ -169,7 +169,7 @@ shared_examples_for "update questions" do
169
169
 
170
170
  expect(page).to have_admin_callout("successfully")
171
171
 
172
- click_on "Manage questions"
172
+ click_on "Questions"
173
173
 
174
174
  expect(page).to have_css(".questionnaire-question", count: 0)
175
175
  end
@@ -187,7 +187,7 @@ shared_examples_for "update questions" do
187
187
  end
188
188
  end
189
189
 
190
- context "when a questionnaire has an existing question with answer options" do
190
+ context "when a questionnaire has an existing question with response options" do
191
191
  let!(:question) do
192
192
  create(
193
193
  :questionnaire_question,
@@ -205,13 +205,13 @@ shared_examples_for "update questions" do
205
205
 
206
206
  before do
207
207
  click_on "Save"
208
- click_on "Manage questions"
208
+ click_on "Questions"
209
209
  end
210
210
 
211
- it "allows deleting answer options" do
211
+ it "allows deleting response options" do
212
212
  expand_all_questions
213
213
 
214
- within ".questionnaire-question-answer-option:last-of-type" do
214
+ within ".questionnaire-question-response-option:last-of-type" do
215
215
  click_on "Remove"
216
216
  end
217
217
 
@@ -219,7 +219,7 @@ shared_examples_for "update questions" do
219
219
 
220
220
  visit_manage_questions_and_expand_all
221
221
 
222
- expect(page).to have_css(".questionnaire-question-answer-option", count: 2)
222
+ expect(page).to have_css(".questionnaire-question-response-option", count: 2)
223
223
  end
224
224
 
225
225
  it "still removes the question even if previous editions rendered the options invalid" do
@@ -227,7 +227,7 @@ shared_examples_for "update questions" do
227
227
 
228
228
  expand_all_questions
229
229
 
230
- within ".questionnaire-question-answer-option:first-of-type" do
230
+ within ".questionnaire-question-response-option:first-of-type" do
231
231
  fill_in find_nested_form_field_locator("body_en"), with: ""
232
232
  end
233
233
 
@@ -283,7 +283,7 @@ shared_examples_for "update questions" do
283
283
 
284
284
  within ".questionnaire-question:last-of-type" do
285
285
  expect(page).to have_css(".questionnaire-question-matrix-row", count: 2)
286
- expect(page).to have_css(".questionnaire-question-answer-option", count: 3)
286
+ expect(page).to have_css(".questionnaire-question-response-option", count: 3)
287
287
  end
288
288
  end
289
289
 
@@ -317,10 +317,6 @@ shared_examples_for "update questions" do
317
317
  create(:questionnaire_question, questionnaire:, body: second_body, position: 1)
318
318
  end
319
319
 
320
- let!(:question3) do
321
- create(:questionnaire_question, questionnaire:, body: third_body, position: 2)
322
- end
323
-
324
320
  let(:first_body) do
325
321
  { en: "First", ca: "Primera", es: "Primera" }
326
322
  end
@@ -329,10 +325,6 @@ shared_examples_for "update questions" do
329
325
  { en: "Second", ca: "Segona", es: "Segunda" }
330
326
  end
331
327
 
332
- let(:third_body) do
333
- { en: "Third", ca: "Tercera", es: "Tercera" }
334
- end
335
-
336
328
  before do
337
329
  click_on "Save"
338
330
  visit_manage_questions_and_expand_all
@@ -352,6 +344,26 @@ shared_examples_for "update questions" do
352
344
  end
353
345
  end
354
346
 
347
+ context "when moving a question up" do
348
+ before do
349
+ within ".questionnaire-question:last-of-type" do
350
+ click_on "Up"
351
+ end
352
+ end
353
+
354
+ it_behaves_like "switching questions order"
355
+ end
356
+
357
+ context "when moving a question down" do
358
+ before do
359
+ within ".questionnaire-question:first-of-type" do
360
+ click_on "Down"
361
+ end
362
+ end
363
+
364
+ it_behaves_like "switching questions order"
365
+ end
366
+
355
367
  describe "collapsible questions" do
356
368
  context "when clicking on Expand all button" do
357
369
  it "expands all questions" do
@@ -447,37 +459,16 @@ shared_examples_for "update questions" do
447
459
  click_on "Add question"
448
460
  expand_all_questions
449
461
 
450
- question_cards = page.all(".questionnaire-question")
451
- expect(question_cards.size).to eq(4)
452
-
453
- within question_cards[1] do
454
- expect(find("input[name*='[body_en]']").value).to eq("Second")
455
- end
456
- within question_cards[2] do
457
- expect(find("input[name*='[body_en]']").value).to eq("Third")
458
- end
462
+ expect(page.find(".questionnaire-question:nth-of-type(1)")).to look_like_first_question
463
+ expect(page.find(".questionnaire-question:nth-of-type(2)")).to look_like_intermediate_question
464
+ expect(page.find(".questionnaire-question:nth-of-type(3)")).to look_like_last_question
459
465
 
460
466
  within ".questionnaire-question:first-of-type" do
461
467
  click_on "Remove"
462
468
  end
463
469
 
464
- remaining_cards = page.all(".questionnaire-question")
465
- expect(remaining_cards.size).to eq(3)
466
-
467
- # Check that the first question is now what was previously the second
468
- within remaining_cards.first do
469
- expect(find("input[name*='[body_en]']").value).to eq("Second")
470
- end
471
-
472
- # Check that the second question is now what was previously the third
473
- within remaining_cards[1] do
474
- expect(find("input[name*='[body_en]']").value).to eq("Third")
475
- end
476
-
477
- # The last question should be the new empty question
478
- within remaining_cards.last do
479
- expect(find("input[name*='[body_en]']").value).to eq("")
480
- end
470
+ expect(page.all(".questionnaire-question").first).to look_like_first_question
471
+ expect(page.all(".questionnaire-question").last).to look_like_last_question
481
472
  end
482
473
 
483
474
  it "does not duplicate editors when adding new questions" do
@@ -487,26 +478,26 @@ shared_examples_for "update questions" do
487
478
  end.to change { page.all(".editor-toolbar").size }.by(1)
488
479
  end
489
480
 
490
- it "properly decides which button to show after adding/removing answer options" do
481
+ it "properly decides which button to show after adding/removing response options" do
491
482
  click_on "Add question"
492
483
  expand_all_questions
493
484
 
494
485
  within ".questionnaire-question:last-of-type" do
495
486
  select "Single option", from: "Type"
496
487
 
497
- within ".questionnaire-question-answer-options-list" do
488
+ within ".questionnaire-question-response-options-list" do
498
489
  expect(page).to have_no_button("Remove")
499
490
  end
500
491
 
501
- click_on "Add answer option"
492
+ click_on "Add response option"
502
493
 
503
- expect(page.all(".questionnaire-question-answer-option")).to all(have_button("Remove"))
494
+ expect(page.all(".questionnaire-question-response-option")).to all(have_button("Remove"))
504
495
 
505
- within ".questionnaire-question-answer-option:first-of-type" do
496
+ within ".questionnaire-question-response-option:first-of-type" do
506
497
  click_on "Remove"
507
498
  end
508
499
 
509
- within ".questionnaire-question-answer-options-list" do
500
+ within ".questionnaire-question-response-options-list" do
510
501
  expect(page).to have_no_button("Remove")
511
502
  end
512
503
  end
@@ -515,100 +506,12 @@ shared_examples_for "update questions" do
515
506
  expand_all_questions
516
507
 
517
508
  within ".questionnaire-question:last-of-type" do
518
- within ".questionnaire-question-answer-options-list" do
509
+ within ".questionnaire-question-response-options-list" do
519
510
  expect(page).to have_no_button("Remove")
520
511
  end
521
512
  end
522
513
  end
523
514
 
524
- context "when reordering questions with drag and drop", :js do
525
- before do
526
- expand_all_questions
527
- end
528
-
529
- it "allows moving questions using drag and drop" do
530
- question_cards = all(".questionnaire-question")
531
-
532
- # Verify initial order by checking the body field values
533
- within question_cards[0] do
534
- expect(find("input[name*='[body_en]']").value).to eq("First")
535
- end
536
- within question_cards[1] do
537
- expect(find("input[name*='[body_en]']").value).to eq("Second")
538
- end
539
- within question_cards[2] do
540
- expect(find("input[name*='[body_en]']").value).to eq("Third")
541
- end
542
-
543
- # JavaScript to simulate drag and drop.
544
- page.execute_script(<<~JS)
545
- var questions = document.querySelectorAll('.questionnaire-question');
546
- var container = questions[0].parentNode;
547
- var second = questions[1];
548
- var first = questions[0];
549
-
550
- // Move second question before first
551
- container.insertBefore(second, first);
552
-
553
- // Update position values
554
- var updatedQuestions = container.querySelectorAll('.questionnaire-question');
555
- updatedQuestions.forEach(function(question, index) {
556
- var positionInput = question.querySelector('input[name$="[position]"]');
557
- if (positionInput) positionInput.value = index;
558
- });
559
- JS
560
-
561
- sleep 0.5
562
-
563
- question_cards = all(".questionnaire-question")
564
- within question_cards[0] do
565
- expect(find("input[name*='[body_en]']").value).to eq("Second")
566
- end
567
- within question_cards[1] do
568
- expect(find("input[name*='[body_en]']").value).to eq("First")
569
- end
570
- within question_cards[2] do
571
- expect(find("input[name*='[body_en]']").value).to eq("Third")
572
- end
573
- end
574
-
575
- it "persists drag and drop changes when saving" do
576
- # Move second question to last position
577
- page.execute_script(<<~JS)
578
- var questions = document.querySelectorAll('.questionnaire-question');
579
- var container = questions[0].parentNode;
580
- var second = questions[1];
581
-
582
- container.appendChild(second);
583
-
584
- // Update the positions of questions
585
- var updatedQuestions = container.querySelectorAll('.questionnaire-question');
586
- updatedQuestions.forEach(function(question, index) {
587
- var positionInput = question.querySelector('input[name$="[position]"]');
588
- if (positionInput) positionInput.value = index;
589
- });
590
- JS
591
-
592
- sleep 0.5
593
-
594
- click_on "Save"
595
- expect(page).to have_admin_callout("successfully")
596
-
597
- visit_manage_questions_and_expand_all
598
-
599
- question_cards = all(".questionnaire-question")
600
- within question_cards[0] do
601
- expect(find("input[name*='[body_en]']").value).to eq("First")
602
- end
603
- within question_cards[1] do
604
- expect(find("input[name*='[body_en]']").value).to eq("Third")
605
- end
606
- within question_cards[2] do
607
- expect(find("input[name*='[body_en]']").value).to eq("Second")
608
- end
609
- end
610
- end
611
-
612
515
  private
613
516
 
614
517
  def look_like_first_question
@@ -24,7 +24,7 @@ shared_examples_for "manage questionnaires" do
24
24
  }
25
25
  end
26
26
 
27
- context "when the questionnaire is not already answered" do
27
+ context "when the questionnaire is not already responded" do
28
28
  before do
29
29
  visit manage_questions_path
30
30
  end
@@ -35,9 +35,9 @@ shared_examples_for "manage questionnaires" do
35
35
  it_behaves_like "update display conditions"
36
36
  end
37
37
 
38
- context "when the questionnaire is already answered" do
38
+ context "when the questionnaire is already responded" do
39
39
  let!(:question) { create(:questionnaire_question, questionnaire:, body:, question_type: "multiple_option") }
40
- let!(:answer) { create(:answer, questionnaire:, question:) }
40
+ let!(:response) { create(:response, questionnaire:, question:) }
41
41
 
42
42
  it "cannot modify questionnaire questions" do
43
43
  visit manage_questions_path
@@ -92,7 +92,7 @@ shared_examples_for "manage questionnaires" do
92
92
  end
93
93
 
94
94
  def visit_manage_questions_and_expand_all
95
- click_on "Manage questions"
95
+ click_on "Questions"
96
96
  expand_all_questions
97
97
  end
98
98
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "decidim/forms/test/shared_examples/has_questionnaire"
4
4
  require "decidim/forms/test/shared_examples/manage_questionnaires"
5
- require "decidim/forms/test/shared_examples/manage_questionnaire_answers"
5
+ require "decidim/forms/test/shared_examples/manage_questionnaire_responses"
6
6
 
7
7
  if defined? Decidim::Templates
8
8
  require "decidim/templates/test/shared_examples/uses_questionnaire_templates"
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Forms
5
+ # This class serializes the responses given by a User for questionnaire so can be
6
+ # exported to CSV, JSON or other formats.
7
+ class UserResponsesSerializer < Decidim::Exporters::Serializer
8
+ include Decidim::TranslationsHelper
9
+
10
+ # Public: Initializes the serializer with a collection of Responses.
11
+ def initialize(responses)
12
+ @responses = responses
13
+ end
14
+
15
+ # i18n-tasks-use t('decidim.forms.user_responses_serializer.body')
16
+ # i18n-tasks-use t('decidim.forms.user_responses_serializer.id')
17
+ # i18n-tasks-use t('decidim.forms.user_responses_serializer.question')
18
+ # i18n-tasks-use t('decidim.forms.user_responses_serializer.registered')
19
+ # i18n-tasks-use t('decidim.forms.user_responses_serializer.unregistered')
20
+ # Public: Exports a hash with the serialized data for the user responses.
21
+ def serialize
22
+ responses_hash = hash_for(@responses.first)
23
+ responses_hash.merge!(questions_hash)
24
+
25
+ @responses.each do |response|
26
+ responses_hash[translated_question_key(response.question.position, response.question.body)] = normalize_body(response)
27
+ end
28
+
29
+ responses_hash
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :responses
35
+ alias resource responses
36
+
37
+ def hash_for(response)
38
+ {
39
+ response_translated_attribute_name(:id) => response&.session_token,
40
+ response_translated_attribute_name(:created_at) => response&.created_at,
41
+ response_translated_attribute_name(:ip_hash) => response&.ip_hash,
42
+ response_translated_attribute_name(:user_status) => response_translated_attribute_name(response&.decidim_user_id.present? ? "registered" : "unregistered")
43
+ }
44
+ end
45
+
46
+ def questions_hash
47
+ questionnaire_id = @responses.first&.decidim_questionnaire_id
48
+ return {} unless questionnaire_id
49
+
50
+ questions = Decidim::Forms::Question.where(decidim_questionnaire_id: questionnaire_id).order(:position)
51
+ return {} if questions.none?
52
+
53
+ questions.each.inject({}) do |serialized, question|
54
+ serialized.update(
55
+ translated_question_key(question.position, question.body) => ""
56
+ )
57
+ end
58
+ end
59
+
60
+ def translated_question_key(idx, body)
61
+ "#{idx + 1}. #{translated_attribute(body)}"
62
+ end
63
+
64
+ def normalize_body(response)
65
+ response.body ||
66
+ normalize_attachments(response) ||
67
+ normalize_choices(response, response.choices)
68
+ end
69
+
70
+ def normalize_attachments(response)
71
+ return if response.attachments.blank?
72
+
73
+ response.attachments.map(&:url)
74
+ end
75
+
76
+ def normalize_choices(response, choices)
77
+ if response.question.matrix?
78
+ normalize_matrix_choices(response, choices)
79
+ else
80
+ choices.map do |choice|
81
+ format_free_text_for choice
82
+ end
83
+ end
84
+ end
85
+
86
+ def normalize_matrix_choices(response, choices)
87
+ response.question.matrix_rows.to_h do |matrix_row|
88
+ row_body = translated_attribute(matrix_row.body)
89
+
90
+ row_choices = response.question.response_options.map do |response_option|
91
+ choice = choices.find_by(matrix_row:, response_option:)
92
+ choice.try(:custom_body) || choice.try(:body)
93
+ end
94
+
95
+ [row_body, row_choices]
96
+ end
97
+ end
98
+
99
+ def response_translated_attribute_name(attribute)
100
+ I18n.t(attribute.to_sym, scope: "decidim.forms.user_responses_serializer")
101
+ end
102
+
103
+ def format_free_text_for(choice)
104
+ return choice.try(:body) if choice.try(:custom_body).blank?
105
+
106
+ "#{choice.try(:body)} (#{choice.try(:custom_body)})"
107
+ end
108
+ end
109
+ end
110
+ end
@@ -4,7 +4,7 @@ module Decidim
4
4
  # This holds the decidim-forms version.
5
5
  module Forms
6
6
  def self.version
7
- "0.30.3"
7
+ "0.31.0.rc1"
8
8
  end
9
9
  end
10
10
  end
data/lib/decidim/forms.rb CHANGED
@@ -8,8 +8,8 @@ require "decidim/forms/admin_engine"
8
8
  module Decidim
9
9
  # This namespace holds the logic of the `Forms`.
10
10
  module Forms
11
- autoload :UserAnswersSerializer, "decidim/forms/user_answers_serializer"
12
- autoload :DownloadYourDataUserAnswersSerializer, "decidim/forms/download_your_data_user_answers_serializer"
11
+ autoload :UserResponsesSerializer, "decidim/forms/user_responses_serializer"
12
+ autoload :DownloadYourDataUserResponsesSerializer, "decidim/forms/download_your_data_user_responses_serializer"
13
13
  end
14
14
 
15
15
  module Exporters