decidim-forms 0.23.2 → 0.24.0

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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/decidim/forms/forms.js.es6 +4 -4
  3. data/app/commands/decidim/forms/admin/update_questionnaire.rb +2 -1
  4. data/app/commands/decidim/forms/answer_questionnaire.rb +43 -1
  5. data/app/controllers/decidim/forms/admin/concerns/has_questionnaire.rb +10 -2
  6. data/app/controllers/decidim/forms/concerns/has_questionnaire.rb +10 -4
  7. data/app/forms/decidim/forms/admin/question_form.rb +2 -0
  8. data/app/forms/decidim/forms/answer_form.rb +27 -0
  9. data/app/helpers/decidim/forms/admin/application_helper.rb +1 -6
  10. data/app/helpers/decidim/forms/admin/concerns/has_questionnaire_answers_url_helper.rb +3 -3
  11. data/app/jobs/decidim/forms/export_questionnaire_answers_job.rb +1 -1
  12. data/app/models/decidim/forms/answer.rb +8 -0
  13. data/app/models/decidim/forms/question.rb +9 -3
  14. data/app/models/decidim/forms/questionnaire.rb +2 -0
  15. data/app/presenters/decidim/forms/admin/questionnaire_answer_presenter.rb +20 -0
  16. data/app/presenters/decidim/forms/admin/questionnaire_participant_presenter.rb +4 -4
  17. data/app/queries/decidim/forms/questionnaire_user_answers.rb +2 -1
  18. data/app/views/decidim/forms/admin/questionnaires/_form.html.erb +3 -3
  19. data/app/views/decidim/forms/admin/questionnaires/_question.html.erb +11 -0
  20. data/app/views/decidim/forms/admin/questionnaires/edit.html.erb +1 -1
  21. data/app/views/decidim/forms/questionnaires/_answer.html.erb +1 -1
  22. data/app/views/decidim/forms/questionnaires/answers/_files.html.erb +1 -0
  23. data/app/views/decidim/forms/questionnaires/answers/_long_answer.html.erb +1 -1
  24. data/app/views/decidim/forms/questionnaires/answers/_matrix_multiple.html.erb +2 -1
  25. data/app/views/decidim/forms/questionnaires/answers/_matrix_single.html.erb +2 -1
  26. data/app/views/decidim/forms/questionnaires/answers/_multiple_option.html.erb +2 -1
  27. data/app/views/decidim/forms/questionnaires/answers/_short_answer.html.erb +1 -1
  28. data/app/views/decidim/forms/questionnaires/answers/_single_option.html.erb +2 -1
  29. data/app/views/decidim/forms/questionnaires/show.html.erb +1 -1
  30. data/config/initializers/wicked_pdf.rb +2 -0
  31. data/config/locales/ca.yml +8 -2
  32. data/config/locales/cs.yml +8 -2
  33. data/config/locales/de.yml +8 -2
  34. data/config/locales/el.yml +1 -1
  35. data/config/locales/en.yml +8 -2
  36. data/config/locales/es-MX.yml +8 -2
  37. data/config/locales/es-PY.yml +8 -2
  38. data/config/locales/es.yml +8 -2
  39. data/config/locales/fi-plain.yml +8 -2
  40. data/config/locales/fi.yml +8 -2
  41. data/config/locales/fr-CA.yml +8 -2
  42. data/config/locales/fr.yml +9 -3
  43. data/config/locales/gl.yml +2 -1
  44. data/config/locales/it.yml +1 -2
  45. data/config/locales/ja.yml +2 -2
  46. data/config/locales/lv.yml +1 -1
  47. data/config/locales/nl.yml +4 -5
  48. data/config/locales/no.yml +1 -2
  49. data/config/locales/pl.yml +11 -2
  50. data/config/locales/pt.yml +1 -2
  51. data/config/locales/ro-RO.yml +13 -2
  52. data/config/locales/sv.yml +1 -2
  53. data/config/locales/tr-TR.yml +2 -2
  54. data/config/locales/zh-CN.yml +1 -2
  55. data/db/migrate/20210208094442_add_max_characters_to_decidim_forms_questions.rb +7 -0
  56. data/lib/decidim/api/answer_option_type.rb +13 -0
  57. data/lib/decidim/api/question_type.rb +21 -0
  58. data/lib/decidim/api/questionnaire_entity_interface.rb +5 -5
  59. data/lib/decidim/api/questionnaire_type.rb +19 -0
  60. data/lib/decidim/exporters/form_pdf_controller_helper.rb +2 -0
  61. data/lib/decidim/forms.rb +1 -1
  62. data/lib/decidim/forms/api.rb +3 -0
  63. data/lib/decidim/forms/data_portability_user_answers_serializer.rb +7 -1
  64. data/lib/decidim/forms/test/factories.rb +12 -2
  65. data/lib/decidim/forms/test/shared_examples/has_questionnaire.rb +38 -5
  66. data/lib/decidim/forms/test/shared_examples/manage_questionnaires.rb +5 -4
  67. data/lib/decidim/forms/test/shared_examples/manage_questionnaires/add_display_conditions.rb +7 -3
  68. data/lib/decidim/forms/test/shared_examples/manage_questionnaires/add_questions.rb +1 -1
  69. data/lib/decidim/forms/test/shared_examples/manage_questionnaires/update_questions.rb +5 -0
  70. data/lib/decidim/forms/user_answers_serializer.rb +9 -1
  71. data/lib/decidim/forms/version.rb +1 -1
  72. metadata +15 -13
  73. data/app/types/decidim/forms/answer_option_type.rb +0 -14
  74. data/app/types/decidim/forms/question_type.rb +0 -23
  75. data/app/types/decidim/forms/questionnaire_type.rb +0 -22
@@ -78,7 +78,6 @@ zh-CN:
78
78
  collapse: 收起所有问题
79
79
  expand: 展开所有问题
80
80
  preview: 预览
81
- title: 编辑表单
82
81
  unpublished_warning: 表单未发布。您可以修改它的问题,但这样做将删除当前答案。
83
82
  matrix_row:
84
83
  matrix_row: 行
@@ -110,7 +109,7 @@ zh-CN:
110
109
  answer:
111
110
  body: 主体不能为空
112
111
  files:
113
- extension_whitelist: '接受的格式:'
112
+ extension_allowlist: '接受的格式:'
114
113
  images:
115
114
  dimensions: "%{width} x %{height} px"
116
115
  dimensions_info: '这张图片将是:'
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddMaxCharactersToDecidimFormsQuestions < ActiveRecord::Migration[5.2]
4
+ def change
5
+ add_column :decidim_forms_questions, :max_characters, :integer, default: 0
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Forms
5
+ class AnswerOptionType < Decidim::Api::Types::BaseObject
6
+ description "An answer option for a multi-choice question in a questionnaire"
7
+
8
+ field :id, GraphQL::Types::ID, "ID of this answer option", null: false
9
+ field :body, Decidim::Core::TranslatedFieldType, "The text answer response option.", null: false
10
+ field :free_text, GraphQL::Types::Boolean, "Whether if this answer accepts any free text from the user.", null: false
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Forms
5
+ class QuestionType < Decidim::Api::Types::BaseObject
6
+ description "A question in a questionnaire"
7
+
8
+ implements Decidim::Core::TimestampsInterface
9
+
10
+ field :id, GraphQL::Types::ID, "ID of this question", null: false
11
+ field :body, Decidim::Core::TranslatedFieldType, "What is being asked in this question.", null: false
12
+ field :description, Decidim::Core::TranslatedFieldType, "The description of this question.", null: true
13
+ field :mandatory, GraphQL::Types::Boolean, "Whether if this question is mandatory.", null: false
14
+ field :position, GraphQL::Types::Int, "Order position of the question in the questionnaire", null: true
15
+ field :max_choices, GraphQL::Types::Int, "On questions with answer options, maximum number of choices the user has", null: true
16
+ field :max_characters, GraphQL::Types::Int, "On questions with free text answers, maximum number of characters the answer can have (0 if no limit)", null: false
17
+ field :question_type, GraphQL::Types::String, "Type of question.", null: true
18
+ field :answer_options, [AnswerOptionType, { null: true }], "List of answer options in multi-choice questions.", null: false
19
+ end
20
+ end
21
+ end
@@ -4,15 +4,15 @@ module Decidim
4
4
  module Forms
5
5
  # This interface should be implemented by any Type that can be linked to a questionnaire
6
6
  # The only requirement is to have an ID and the Type name be the class.name + Type
7
- QuestionnaireEntityInterface = GraphQL::InterfaceType.define do
8
- name "QuestionnaireEntityInterface"
7
+ module QuestionnaireEntityInterface
8
+ include Decidim::Api::Types::BaseInterface
9
9
  description "An interface that can be used in objects with questionnaires"
10
10
 
11
- field :id, !types.ID, "ID of this entity"
11
+ field :id, GraphQL::Types::ID, "ID of this entity", null: false
12
12
 
13
- resolve_type ->(obj, _ctx) {
13
+ def self.resolve_type(obj, _ctx)
14
14
  "#{obj.class.name}Type".constantize
15
- }
15
+ end
16
16
  end
17
17
  end
18
18
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Forms
5
+ class QuestionnaireType < Decidim::Api::Types::BaseObject
6
+ description "A questionnaire"
7
+
8
+ implements Decidim::Core::TimestampsInterface
9
+
10
+ field :id, GraphQL::Types::ID, "ID of this questionnaire", null: false
11
+ field :title, Decidim::Core::TranslatedFieldType, "The title of this questionnaire.", null: false
12
+ field :description, Decidim::Core::TranslatedFieldType, "The description of this questionnaire.", null: true
13
+ field :tos, Decidim::Core::TranslatedFieldType, "The Terms of Service for this questionnaire.", null: true
14
+ field :for_type, GraphQL::Types::String, "Type of entity using this questionnaire.", method: :questionnaire_for_type, null: true
15
+ field :for_entity, QuestionnaireEntityInterface, "Entity using this questionnaire.", method: :questionnaire_for, null: true
16
+ field :questions, [Decidim::Forms::QuestionType, { null: true }], "Questions in this questionnaire.", null: false
17
+ end
18
+ end
19
+ end
@@ -2,8 +2,10 @@
2
2
 
3
3
  module Decidim
4
4
  module Exporters
5
+ # rubocop: disable Rails/ApplicationController
5
6
  # A dummy controller to render views while exporting questionnaires
6
7
  class FormPDFControllerHelper < ActionController::Base
8
+ # rubocop: enable Rails/ApplicationController
7
9
  helper Decidim::TranslationsHelper
8
10
  helper Decidim::Forms::Admin::QuestionnaireAnswersHelper
9
11
  end
data/lib/decidim/forms.rb CHANGED
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "decidim/forms/admin"
4
+ require "decidim/forms/api"
4
5
  require "decidim/forms/engine"
5
6
  require "decidim/forms/admin_engine"
6
- require "decidim/forms/api"
7
7
 
8
8
  module Decidim
9
9
  # This namespace holds the logic of the `Forms`.
@@ -3,5 +3,8 @@
3
3
  module Decidim
4
4
  module Forms
5
5
  autoload :QuestionnaireEntityInterface, "decidim/api/questionnaire_entity_interface"
6
+ autoload :AnswerOptionType, "decidim/api/answer_option_type"
7
+ autoload :QuestionType, "decidim/api/question_type"
8
+ autoload :QuestionnaireType, "decidim/api/questionnaire_type"
6
9
  end
7
10
  end
@@ -30,7 +30,13 @@ module Decidim
30
30
  private
31
31
 
32
32
  def normalize_body(resource)
33
- resource.body || resource.choices.pluck(:body)
33
+ attachments_for(resource) || resource.body || resource.choices.pluck(:body)
34
+ end
35
+
36
+ def attachments_for(resource)
37
+ return if resource.attachments.blank?
38
+
39
+ resource.attachments.map(&:url)
34
40
  end
35
41
  end
36
42
  end
@@ -17,10 +17,13 @@ FactoryBot.define do
17
17
 
18
18
  trait :with_questions do
19
19
  questions do
20
+ position = 0
20
21
  qs = %w(short_answer long_answer).collect do |text_question_type|
21
- build(:questionnaire_question, question_type: text_question_type)
22
+ q = build(:questionnaire_question, question_type: text_question_type, position: position)
23
+ position += 1
24
+ q
22
25
  end
23
- qs << build(:questionnaire_question, :with_answer_options, question_type: :single_option)
26
+ qs << build(:questionnaire_question, :with_answer_options, question_type: :single_option, position: position)
24
27
  qs
25
28
  end
26
29
  end
@@ -91,6 +94,13 @@ FactoryBot.define do
91
94
  question { create(:questionnaire_question, questionnaire: questionnaire) }
92
95
  user { create(:user, organization: questionnaire.questionnaire_for.organization) }
93
96
  session_token { Digest::MD5.hexdigest(user.id.to_s) }
97
+
98
+ trait :with_attachments do
99
+ after(:create) do |answer, _evaluator|
100
+ create :attachment, :with_image, attached_to: answer
101
+ create :attachment, :with_pdf, attached_to: answer
102
+ end
103
+ end
94
104
  end
95
105
 
96
106
  factory :answer_option, class: "Decidim::Forms::AnswerOption" do
@@ -194,6 +194,28 @@ shared_examples_for "has questionnaire" do
194
194
  end
195
195
  end
196
196
 
197
+ shared_examples_for "question has a character limit" do
198
+ context "when max_characters value is positive" do
199
+ let(:max_characters) { 30 }
200
+
201
+ it "shows a message indicating number of characters left" do
202
+ visit questionnaire_public_path
203
+
204
+ expect(page).to have_content("30 characters left")
205
+ end
206
+ end
207
+
208
+ context "when max_characters value is 0" do
209
+ let(:max_characters) { 0 }
210
+
211
+ it "doesn't show message indicating number of characters left" do
212
+ visit questionnaire_public_path
213
+
214
+ expect(page).not_to have_content("characters left")
215
+ end
216
+ end
217
+ end
218
+
197
219
  describe "leaving a blank question (without js)", driver: :rack_test do
198
220
  include_context "when a non multiple choice question is mandatory"
199
221
 
@@ -268,12 +290,13 @@ shared_examples_for "has questionnaire" do
268
290
 
269
291
  describe "free text options" do
270
292
  let(:answer_option_bodies) { Array.new(3) { Decidim::Faker::Localized.sentence } }
271
-
293
+ let(:max_characters) { 0 }
272
294
  let!(:question) do
273
295
  create(
274
296
  :questionnaire_question,
275
297
  questionnaire: questionnaire,
276
298
  question_type: question_type,
299
+ max_characters: max_characters,
277
300
  position: 1,
278
301
  options: [
279
302
  { "body" => answer_option_bodies[0] },
@@ -346,6 +369,8 @@ shared_examples_for "has questionnaire" do
346
369
 
347
370
  expect(page).to have_field("questionnaire_responses_0_choices_2_custom_body", with: "Cacatua")
348
371
  end
372
+
373
+ it_behaves_like "question has a character limit"
349
374
  end
350
375
 
351
376
  context "when question is multiple_option type" do
@@ -392,27 +417,35 @@ shared_examples_for "has questionnaire" do
392
417
 
393
418
  expect(page).to have_field("questionnaire_responses_0_choices_2_custom_body", with: "Cacatua")
394
419
  end
420
+
421
+ it_behaves_like "question has a character limit"
395
422
  end
396
423
  end
397
424
 
398
425
  context "when question type is long answer" do
399
- let!(:question) { create(:questionnaire_question, questionnaire: questionnaire, question_type: "long_answer") }
426
+ let(:max_characters) { 0 }
427
+ let!(:question) { create(:questionnaire_question, questionnaire: questionnaire, question_type: "long_answer", max_characters: max_characters) }
400
428
 
401
429
  it "renders the answer as a textarea" do
402
430
  visit questionnaire_public_path
403
431
 
404
432
  expect(page).to have_selector("textarea#questionnaire_responses_0")
405
433
  end
434
+
435
+ it_behaves_like "question has a character limit"
406
436
  end
407
437
 
408
438
  context "when question type is short answer" do
409
- let!(:question) { create(:questionnaire_question, questionnaire: questionnaire, question_type: "short_answer") }
439
+ let(:max_characters) { 0 }
440
+ let!(:question) { create(:questionnaire_question, questionnaire: questionnaire, question_type: "short_answer", max_characters: max_characters) }
410
441
 
411
442
  it "renders the answer as a text field" do
412
443
  visit questionnaire_public_path
413
444
 
414
445
  expect(page).to have_selector("input[type=text]#questionnaire_responses_0")
415
446
  end
447
+
448
+ it_behaves_like "question has a character limit"
416
449
  end
417
450
 
418
451
  context "when question type is single option" do
@@ -665,7 +698,7 @@ shared_examples_for "has questionnaire" do
665
698
  end
666
699
 
667
700
  radio_buttons = page.all(".radio-button-collection input[type=radio]")
668
- expect(radio_buttons.map { |b| b[:checked] }).to eq([nil, "true", nil, nil])
701
+ expect(radio_buttons.pluck(:checked)).to eq([nil, "true", nil, nil])
669
702
  end
670
703
 
671
704
  context "when the question is mandatory and the answer is not complete" do
@@ -830,7 +863,7 @@ shared_examples_for "has questionnaire" do
830
863
  end
831
864
 
832
865
  checkboxes = page.all(".check-box-collection input[type=checkbox]")
833
- expect(checkboxes.map { |c| c[:checked] }).to eq(["true", "true", "true", nil, nil, "true"])
866
+ expect(checkboxes.pluck(:checked)).to eq(["true", "true", "true", nil, nil, "true"])
834
867
  end
835
868
  end
836
869
  end
@@ -2,10 +2,10 @@
2
2
 
3
3
  require "spec_helper"
4
4
 
5
- require "decidim/forms/test/shared_examples/manage_questionnaires/add_questions.rb"
6
- require "decidim/forms/test/shared_examples/manage_questionnaires/update_questions.rb"
7
- require "decidim/forms/test/shared_examples/manage_questionnaires/add_display_conditions.rb"
8
- require "decidim/forms/test/shared_examples/manage_questionnaires/update_display_conditions.rb"
5
+ require "decidim/forms/test/shared_examples/manage_questionnaires/add_questions"
6
+ require "decidim/forms/test/shared_examples/manage_questionnaires/update_questions"
7
+ require "decidim/forms/test/shared_examples/manage_questionnaires/add_display_conditions"
8
+ require "decidim/forms/test/shared_examples/manage_questionnaires/update_display_conditions"
9
9
 
10
10
  shared_examples_for "manage questionnaires" do
11
11
  let(:body) do
@@ -63,6 +63,7 @@ shared_examples_for "manage questionnaires" do
63
63
  expect(page).to have_selector("input[value='This is the first question'][disabled]")
64
64
  expect(page).to have_selector("select[id$=question_type][disabled]")
65
65
  expect(page).to have_selector("select[id$=max_choices][disabled]")
66
+ expect(page).to have_selector("input[id$=max_characters][disabled]")
66
67
  expect(page).to have_selector(".ql-editor[contenteditable=false]")
67
68
  end
68
69
  end
@@ -39,18 +39,21 @@ shared_examples_for "add display conditions" do
39
39
  context "when questionnaire has more than one question" do
40
40
  let!(:question_short_answer) do
41
41
  create(:questionnaire_question,
42
+ position: 0,
42
43
  questionnaire: questionnaire,
43
44
  body: Decidim::Faker::Localized.sentence,
44
45
  question_type: "short_answer")
45
46
  end
46
47
  let!(:question_long_answer) do
47
48
  create(:questionnaire_question,
49
+ position: 1,
48
50
  questionnaire: questionnaire,
49
51
  body: Decidim::Faker::Localized.sentence,
50
52
  question_type: "long_answer")
51
53
  end
52
54
  let!(:question_single_option) do
53
55
  create(:questionnaire_question,
56
+ position: 2,
54
57
  questionnaire: questionnaire,
55
58
  body: Decidim::Faker::Localized.sentence,
56
59
  question_type: "single_option",
@@ -58,6 +61,7 @@ shared_examples_for "add display conditions" do
58
61
  end
59
62
  let!(:question_multiple_option) do
60
63
  create(:questionnaire_question,
64
+ position: 3,
61
65
  questionnaire: questionnaire,
62
66
  body: Decidim::Faker::Localized.sentence,
63
67
  question_type: "multiple_option",
@@ -101,7 +105,7 @@ shared_examples_for "add display conditions" do
101
105
 
102
106
  within "select[id$=decidim_condition_question_id]" do
103
107
  elements = page.all("option[data-type]")
104
- expect(elements.map { |element| element[:"data-type"] }).to eq(questions.map(&:question_type))
108
+ expect(elements.map { |element| element[:"data-type"] }).to match_array(questions.map(&:question_type))
105
109
  expect(page.find("option[value='#{questions.last.id}']")).to be_disabled
106
110
  end
107
111
  end
@@ -117,7 +121,7 @@ shared_examples_for "add display conditions" do
117
121
  option_elements = page.all("select[id$=condition_type] option")
118
122
  option_elements = option_elements.to_a.reject { |option| option[:style].match? "display: none" }
119
123
 
120
- expect(option_elements.map(&:text)).to eq(options)
124
+ expect(option_elements.map(&:text)).to match_array(options)
121
125
  end
122
126
  end
123
127
  end
@@ -132,7 +136,7 @@ shared_examples_for "add display conditions" do
132
136
  option_elements = page.all("select[id$=condition_type] option")
133
137
  option_elements = option_elements.to_a.reject { |option| option[:style].match? "display: none" }
134
138
 
135
- expect(option_elements.map(&:text)).to eq(options)
139
+ expect(option_elements.map(&:text)).to match_array(options)
136
140
  end
137
141
  end
138
142
  end
@@ -41,7 +41,7 @@ shared_examples_for "add questions" do
41
41
  within ".questionnaire-question" do
42
42
  fill_in find_nested_form_field_locator("body_en"), with: "Body"
43
43
 
44
- fill_in_editor find_nested_form_field_locator("description_en", visible: false), with: "<b>Superkalifragilistic description</b>"
44
+ fill_in_editor find_nested_form_field_locator("description_en", visible: false), with: "<strong>Superkalifragilistic description</strong>"
45
45
  end
46
46
 
47
47
  click_button "Save"
@@ -15,6 +15,7 @@ shared_examples_for "update questions" do
15
15
  within "form.edit_questionnaire" do
16
16
  within ".questionnaire-question" do
17
17
  fill_in "questionnaire_questions_#{question.id}_body_en", with: "Modified question"
18
+ fill_in "questionnaire_questions_#{question.id}_max_characters", with: 30
18
19
  check "Mandatory"
19
20
  select "Long answer", from: "Type"
20
21
  end
@@ -29,6 +30,7 @@ shared_examples_for "update questions" do
29
30
  expect(page).to have_selector("input[value='Modified question']")
30
31
  expect(page).to have_no_selector("input[value='This is the first question']")
31
32
  expect(page).to have_selector("input#questionnaire_questions_#{question.id}_mandatory[checked]")
33
+ expect(page).to have_selector("input#questionnaire_questions_#{question.id}_max_characters[value='30']")
32
34
  expect(page).to have_selector("select#questionnaire_questions_#{question.id}_question_type option[value='long_answer'][selected]")
33
35
  end
34
36
 
@@ -39,6 +41,7 @@ shared_examples_for "update questions" do
39
41
  within ".questionnaire-question" do
40
42
  expect(page).to have_content("Statement*")
41
43
  fill_in "questionnaire_questions_#{question.id}_body_en", with: ""
44
+ fill_in "questionnaire_questions_#{question.id}_max_characters", with: -3
42
45
  check "Mandatory"
43
46
  select "Matrix (Multiple option)", from: "Type"
44
47
  select "2", from: "Maximum number of choices"
@@ -51,10 +54,12 @@ shared_examples_for "update questions" do
51
54
 
52
55
  expect(page).to have_admin_callout("There was a problem saving")
53
56
  expect(page).to have_content("can't be blank", count: 5) # emtpy question, 2 empty default answer options, 2 empty default matrix rows
57
+ expect(page).to have_content("must be greater than or equal to 0", count: 1)
54
58
 
55
59
  expect(page).to have_selector("input[value='']")
56
60
  expect(page).to have_no_selector("input[value='This is the first question']")
57
61
  expect(page).to have_selector("input#questionnaire_questions_#{question.id}_mandatory[checked]")
62
+ expect(page).to have_selector("input#questionnaire_questions_#{question.id}_max_characters[value='-3']")
58
63
  expect(page).to have_select("Maximum number of choices", selected: "2")
59
64
  expect(page).to have_selector("select#questionnaire_questions_#{question.id}_question_type option[value='matrix_multiple'][selected]")
60
65
  end
@@ -28,7 +28,15 @@ module Decidim
28
28
  private
29
29
 
30
30
  def normalize_body(answer)
31
- answer.body || normalize_choices(answer, answer.choices)
31
+ answer.body ||
32
+ normalize_attachments(answer) ||
33
+ normalize_choices(answer, answer.choices)
34
+ end
35
+
36
+ def normalize_attachments(answer)
37
+ return if answer.attachments.blank?
38
+
39
+ answer.attachments.map(&:url)
32
40
  end
33
41
 
34
42
  def normalize_choices(answer, choices)
@@ -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.23.2"
7
+ "0.24.0"
8
8
  end
9
9
  end
10
10
  end