decidim-forms 0.16.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 +7 -0
  2. data/README.md +65 -0
  3. data/Rakefile +3 -0
  4. data/app/assets/config/admin/decidim_forms_manifest.js +1 -0
  5. data/app/assets/config/decidim_forms_manifest.js +1 -0
  6. data/app/assets/images/decidim/surveys/icon.svg +19 -0
  7. data/app/assets/javascripts/decidim/forms/admin/auto_buttons_by_min_items.component.js.es6 +25 -0
  8. data/app/assets/javascripts/decidim/forms/admin/auto_select_options_by_total_items.component.js.es6 +23 -0
  9. data/app/assets/javascripts/decidim/forms/admin/forms.js.es6 +188 -0
  10. data/app/assets/javascripts/decidim/forms/autosortable_checkboxes.component.js.es6 +65 -0
  11. data/app/assets/javascripts/decidim/forms/forms.js.es6 +20 -0
  12. data/app/assets/javascripts/decidim/forms/option_attached_inputs.component.js.es6 +32 -0
  13. data/app/commands/decidim/forms/admin/update_questionnaire.rb +86 -0
  14. data/app/commands/decidim/forms/answer_questionnaire.rb +54 -0
  15. data/app/controllers/decidim/forms/admin/concerns/has_questionnaire.rb +95 -0
  16. data/app/controllers/decidim/forms/concerns/has_questionnaire.rb +84 -0
  17. data/app/forms/decidim/forms/admin/answer_option_form.rb +23 -0
  18. data/app/forms/decidim/forms/admin/question_form.rb +35 -0
  19. data/app/forms/decidim/forms/admin/questionnaire_form.rb +27 -0
  20. data/app/forms/decidim/forms/answer_choice_form.rb +15 -0
  21. data/app/forms/decidim/forms/answer_form.rb +69 -0
  22. data/app/forms/decidim/forms/questionnaire_form.rb +22 -0
  23. data/app/helpers/decidim/forms/admin/application_helper.rb +19 -0
  24. data/app/models/concerns/decidim/forms/has_questionnaire.rb +20 -0
  25. data/app/models/decidim/forms/answer.rb +45 -0
  26. data/app/models/decidim/forms/answer_choice.rb +15 -0
  27. data/app/models/decidim/forms/answer_option.rb +11 -0
  28. data/app/models/decidim/forms/application_record.rb +10 -0
  29. data/app/models/decidim/forms/question.rb +36 -0
  30. data/app/models/decidim/forms/questionnaire.rb +23 -0
  31. data/app/queries/decidim/forms/questionnaire_user_answers.rb +28 -0
  32. data/app/views/decidim/forms/admin/questionnaires/_answer_option.html.erb +44 -0
  33. data/app/views/decidim/forms/admin/questionnaires/_form.html.erb +51 -0
  34. data/app/views/decidim/forms/admin/questionnaires/_question.html.erb +115 -0
  35. data/app/views/decidim/forms/admin/questionnaires/edit.html.erb +7 -0
  36. data/app/views/decidim/forms/questionnaires/_answer.html.erb +94 -0
  37. data/app/views/decidim/forms/questionnaires/show.html.erb +84 -0
  38. data/config/locales/ca.yml +79 -0
  39. data/config/locales/de.yml +79 -0
  40. data/config/locales/en.yml +79 -0
  41. data/config/locales/es-PY.yml +79 -0
  42. data/config/locales/es.yml +79 -0
  43. data/config/locales/eu.yml +79 -0
  44. data/config/locales/fi-pl.yml +79 -0
  45. data/config/locales/fi.yml +79 -0
  46. data/config/locales/fr.yml +79 -0
  47. data/config/locales/gl.yml +79 -0
  48. data/config/locales/hu.yml +79 -0
  49. data/config/locales/id-ID.yml +1 -0
  50. data/config/locales/it.yml +79 -0
  51. data/config/locales/nl.yml +79 -0
  52. data/config/locales/pl.yml +79 -0
  53. data/config/locales/pt-BR.yml +79 -0
  54. data/config/locales/pt.yml +79 -0
  55. data/config/locales/ru.yml +1 -0
  56. data/config/locales/sv.yml +79 -0
  57. data/config/locales/tr-TR.yml +1 -0
  58. data/config/locales/uk.yml +1 -0
  59. data/db/migrate/20170511092231_create_decidim_forms_questionnaires.rb +15 -0
  60. data/db/migrate/20170515090916_create_decidim_forms_questions.rb +17 -0
  61. data/db/migrate/20170515144119_create_decidim_forms_answers.rb +15 -0
  62. data/db/migrate/20180405015012_create_decidim_forms_answer_options.rb +11 -0
  63. data/db/migrate/20180405015147_create_decidim_forms_answer_choices.rb +13 -0
  64. data/lib/decidim/forms.rb +13 -0
  65. data/lib/decidim/forms/admin.rb +9 -0
  66. data/lib/decidim/forms/admin_engine.rb +21 -0
  67. data/lib/decidim/forms/data_portability_user_answers_serializer.rb +37 -0
  68. data/lib/decidim/forms/engine.rb +14 -0
  69. data/lib/decidim/forms/test.rb +4 -0
  70. data/lib/decidim/forms/test/factories.rb +55 -0
  71. data/lib/decidim/forms/test/shared_examples/has_questionnaire.rb +524 -0
  72. data/lib/decidim/forms/test/shared_examples/manage_questionnaires.rb +626 -0
  73. data/lib/decidim/forms/user_answers_serializer.rb +29 -0
  74. data/lib/decidim/forms/version.rb +10 -0
  75. metadata +165 -0
@@ -0,0 +1 @@
1
+ ru:
@@ -0,0 +1,79 @@
1
+ sv:
2
+ activemodel:
3
+ attributes:
4
+ answer:
5
+ body: Svar
6
+ question:
7
+ max_choices: Max antal val
8
+ question_type: Typ
9
+ questionnaire_question:
10
+ mandatory: Obligatorisk
11
+ errors:
12
+ models:
13
+ answer:
14
+ attributes:
15
+ choices:
16
+ missing: är inte fullständiga
17
+ too_many: är för många
18
+ decidim:
19
+ forms:
20
+ admin:
21
+ models:
22
+ components:
23
+ description: Beskrivning
24
+ tos: Användarvillkor
25
+ questionnaires:
26
+ answer_option:
27
+ answer_option: Svaralternativ
28
+ free_text: Fri text
29
+ remove: Ta bort
30
+ statement: Påstående
31
+ edit:
32
+ save: Spara
33
+ title: Titel
34
+ form:
35
+ add_question: Lägg till fråga
36
+ already_answered_warning: Frågeformuläret är redan besvarat av vissa användare så att du inte kan ändra sina frågor.
37
+ question:
38
+ add_answer_option: Lägg till svaralternativ
39
+ any: Vilken som helst
40
+ description: Beskrivning
41
+ down: Ner
42
+ question: Fråga
43
+ remove: Ta bort
44
+ statement: Påstående
45
+ up: Upp
46
+ update:
47
+ invalid: Det har varit fel när du sparat frågeformuläret.
48
+ success: Frågeformulär sparades framgångsrikt.
49
+ errors:
50
+ answer:
51
+ body: Kroppen kan inte vara tomt
52
+ question_types:
53
+ long_answer: Långt svar
54
+ multiple_option: Flera alternativ
55
+ short_answer: Kort svar
56
+ single_option: Enkelt alternativ
57
+ sorting: Sortering
58
+ questionnaires:
59
+ answer:
60
+ invalid: Det har varit fel när du besvarade frågeformuläret.
61
+ success: Frågeformuläret besvarades med framgång.
62
+ question:
63
+ max_choices: 'Max val: %{n}'
64
+ show:
65
+ answer_questionnaire:
66
+ anonymous_user_message: <a href="%{sign_in_link}">Logga in med ditt konto</a> eller <a href="%{sign_up_link}">registrera dig</a> att svara på frågeformuläret.
67
+ title: Besvara frågeformuläret
68
+ are_you_sure: Den här åtgärden kan inte ångras och du kommer inte att kunna redigera dina svar. Är du säker?
69
+ questionnaire_answered:
70
+ body: Du har redan besvarat det här frågeformuläret.
71
+ title: Redan svarat
72
+ questionnaire_closed:
73
+ body: Frågeformuläret är stängt och kan inte besvaras.
74
+ title: Frågeformuläret stängt
75
+ questionnaire_for_private_users:
76
+ body: Frågeformuläret är endast tillgängligt för privata användare
77
+ title: Frågeformuläret stängt
78
+ submit: Lämna
79
+ tos_agreement: Genom att acceptera accepterar du användarvillkoren
@@ -0,0 +1 @@
1
+ tr:
@@ -0,0 +1 @@
1
+ uk:
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateDecidimFormsQuestionnaires < ActiveRecord::Migration[5.0]
4
+ def change
5
+ create_table :decidim_forms_questionnaires do |t|
6
+ t.jsonb :title
7
+ t.jsonb :description
8
+ t.jsonb :tos
9
+ t.references :questionnaire_for, polymorphic: true, index: { name: "index_decidim_forms_questionnaires_questionnaire_for" }
10
+ t.datetime :published_at
11
+
12
+ t.timestamps
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateDecidimFormsQuestions < ActiveRecord::Migration[5.0]
4
+ def change
5
+ create_table :decidim_forms_questions do |t|
6
+ t.references :decidim_questionnaire, index: true
7
+ t.integer :position, index: true
8
+ t.string :question_type
9
+ t.boolean :mandatory
10
+ t.jsonb :body
11
+ t.jsonb :description
12
+ t.integer :max_choices
13
+
14
+ t.timestamps
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateDecidimFormsAnswers < ActiveRecord::Migration[5.0]
4
+ def change
5
+ create_table :decidim_forms_answers do |t|
6
+ t.jsonb :body, default: []
7
+ t.references :decidim_user, index: true
8
+ t.references :decidim_questionnaire, index: true
9
+ t.references :decidim_question, index: { name: "index_decidim_forms_answers_question_id" }
10
+ t.text :body
11
+
12
+ t.timestamps
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateDecidimFormsAnswerOptions < ActiveRecord::Migration[5.1]
4
+ def change
5
+ create_table :decidim_forms_answer_options do |t|
6
+ t.references :decidim_question, index: { name: "index_decidim_forms_answer_options_question_id" }
7
+ t.jsonb :body
8
+ t.boolean :free_text
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateDecidimFormsAnswerChoices < ActiveRecord::Migration[5.1]
4
+ def change
5
+ create_table :decidim_forms_answer_choices do |t|
6
+ t.references :decidim_answer, index: { name: "index_decidim_forms_answer_choices_answer_id" }
7
+ t.references :decidim_answer_option, index: { name: "index_decidim_forms_answer_choices_answer_option_id" }
8
+ t.integer :position
9
+ t.jsonb :body
10
+ t.text :custom_body
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "decidim/forms/admin"
4
+ require "decidim/forms/engine"
5
+ require "decidim/forms/admin_engine"
6
+
7
+ module Decidim
8
+ # This namespace holds the logic of the `Forms`.
9
+ module Forms
10
+ autoload :UserAnswersSerializer, "decidim/forms/user_answers_serializer"
11
+ autoload :DataPortabilityUserAnswersSerializer, "decidim/forms/data_portability_user_answers_serializer"
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Forms
5
+ # This module contains all the domain logic associated to Decidim's Forms admin panel.
6
+ module Admin
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Forms
5
+ # This is the engine that runs on the public interface of `Forms`.
6
+ class AdminEngine < ::Rails::Engine
7
+ isolate_namespace Decidim::Forms::Admin
8
+
9
+ paths["db/migrate"] = nil
10
+ paths["lib/tasks"] = nil
11
+
12
+ initializer "decidim_forms.admin_assets" do |app|
13
+ app.config.assets.precompile += %w(admin/decidim_forms_manifest.js)
14
+ end
15
+
16
+ def load_seed
17
+ nil
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Forms
5
+ class DataPortabilityUserAnswersSerializer < Decidim::Exporters::Serializer
6
+ include Decidim::TranslationsHelper
7
+ # Serializes an user answer for data portability
8
+ def serialize
9
+ {
10
+ id: resource.id,
11
+ user: {
12
+ name: resource.user.name,
13
+ email: resource.user.email
14
+ },
15
+ questionnaire: {
16
+ id: resource.question.questionnaire.id,
17
+ title: translated_attribute(resource.question.questionnaire.title),
18
+ description: translated_attribute(resource.question.questionnaire.description),
19
+ tos: translated_attribute(resource.question.questionnaire.tos)
20
+ },
21
+ question: {
22
+ id: resource.question.id,
23
+ body: translated_attribute(resource.question.body),
24
+ description: translated_attribute(resource.question.description)
25
+ },
26
+ answer: normalize_body(resource)
27
+ }
28
+ end
29
+
30
+ private
31
+
32
+ def normalize_body(resource)
33
+ resource.body || resource.choices.pluck(:body)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Forms
5
+ # This is the engine that runs on the public interface of `decidim-forms`.
6
+ class Engine < ::Rails::Engine
7
+ isolate_namespace Decidim::Forms
8
+
9
+ initializer "decidim_forms.assets" do |app|
10
+ app.config.assets.precompile += %w(decidim_forms_manifest.js)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "decidim/forms/test/shared_examples/has_questionnaire"
4
+ require "decidim/forms/test/shared_examples/manage_questionnaires"
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "decidim/core/test/factories"
4
+ require "decidim/participatory_processes/test/factories"
5
+
6
+ FactoryBot.define do
7
+ factory :questionnaire, class: Decidim::Forms::Questionnaire do
8
+ title { generate_localized_title }
9
+ description do
10
+ Decidim::Faker::Localized.wrapped("<p>", "</p>") do
11
+ generate_localized_title
12
+ end
13
+ end
14
+ tos { generate_localized_title }
15
+ questionnaire_for { build(:participatory_process) }
16
+ end
17
+
18
+ factory :questionnaire_question, class: Decidim::Forms::Question do
19
+ transient do
20
+ answer_options { [] }
21
+ end
22
+
23
+ body { generate_localized_title }
24
+ mandatory { false }
25
+ position { 0 }
26
+ question_type { Decidim::Forms::Question::TYPES.first }
27
+ questionnaire
28
+
29
+ before(:create) do |question, evaluator|
30
+ evaluator.answer_options.each do |answer_option|
31
+ question.answer_options.build(
32
+ body: answer_option["body"],
33
+ free_text: answer_option["free_text"]
34
+ )
35
+ end
36
+ end
37
+ end
38
+
39
+ factory :answer, class: Decidim::Forms::Answer do
40
+ body { "hola" }
41
+ questionnaire
42
+ question { create(:questionnaire_question, questionnaire: questionnaire) }
43
+ user { create(:user, organization: questionnaire.questionnaire_for.organization) }
44
+ end
45
+
46
+ factory :answer_option, class: Decidim::Forms::AnswerOption do
47
+ question { create(:questionnaire_question) }
48
+ body { generate_localized_title }
49
+ end
50
+
51
+ factory :answer_choice, class: Decidim::Forms::AnswerChoice do
52
+ answer
53
+ answer_option { create(:answer_option, question: answer.question) }
54
+ end
55
+ end
@@ -0,0 +1,524 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ shared_examples_for "has questionnaire" do
6
+ context "when the user is not logged in" do
7
+ it "does not allow answering the questionnaire" do
8
+ visit questionnaire_public_path
9
+
10
+ expect(page).to have_i18n_content(questionnaire.title, upcase: true)
11
+ expect(page).to have_i18n_content(questionnaire.description)
12
+
13
+ expect(page).to have_no_i18n_content(question.body)
14
+
15
+ expect(page).to have_content("Sign in with your account or sign up to answer the questionnaire.")
16
+ end
17
+ end
18
+
19
+ context "when the user is logged in" do
20
+ before do
21
+ login_as user, scope: :user
22
+ end
23
+
24
+ it "allows answering the questionnaire" do
25
+ visit questionnaire_public_path
26
+
27
+ expect(page).to have_i18n_content(questionnaire.title, upcase: true)
28
+ expect(page).to have_i18n_content(questionnaire.description)
29
+
30
+ fill_in question.body["en"], with: "My first answer"
31
+
32
+ check "questionnaire_tos_agreement"
33
+
34
+ accept_confirm { click_button "Submit" }
35
+
36
+ within ".success.flash" do
37
+ expect(page).to have_content("successfully")
38
+ end
39
+
40
+ visit questionnaire_public_path
41
+
42
+ expect(page).to have_content("You have already answered this questionnaire.")
43
+ expect(page).to have_no_i18n_content(question.body)
44
+ end
45
+
46
+ context "when the questionnaire has already been answered by someone else" do
47
+ let!(:question) do
48
+ create(
49
+ :questionnaire_question,
50
+ questionnaire: questionnaire,
51
+ question_type: "single_option",
52
+ position: 0,
53
+ answer_options: [
54
+ { "body" => Decidim::Faker::Localized.sentence },
55
+ { "body" => Decidim::Faker::Localized.sentence }
56
+ ]
57
+ )
58
+ end
59
+
60
+ before do
61
+ answer = create(:answer, id: 1, questionnaire: questionnaire, question: question)
62
+
63
+ answer.choices.create!(
64
+ answer_option: Decidim::Forms::AnswerOption.first,
65
+ body: "Lalalilo"
66
+ )
67
+ end
68
+
69
+ it "does not leak defaults from other answers" do
70
+ visit questionnaire_public_path
71
+
72
+ expect(page).to have_no_selector("input[type=radio]:checked")
73
+ end
74
+ end
75
+
76
+ shared_examples_for "a correctly ordered questionnaire" do
77
+ it "displays the questions ordered by position starting with one" do
78
+ form_fields = all(".answer-questionnaire .row")
79
+
80
+ expect(form_fields[0]).to have_i18n_content(question.body).and have_content("1. ")
81
+ expect(form_fields[1]).to have_i18n_content(other_question.body).and have_content("2. ")
82
+ end
83
+ end
84
+
85
+ context "and submitting a fresh form" do
86
+ let!(:other_question) { create(:questionnaire_question, questionnaire: questionnaire, position: 1) }
87
+
88
+ before do
89
+ visit questionnaire_public_path
90
+ end
91
+
92
+ it_behaves_like "a correctly ordered questionnaire"
93
+ end
94
+
95
+ context "and rendering a form after errors" do
96
+ let!(:other_question) { create(:questionnaire_question, questionnaire: questionnaire, position: 1) }
97
+
98
+ before do
99
+ visit questionnaire_public_path
100
+ accept_confirm { click_button "Submit" }
101
+ end
102
+
103
+ it_behaves_like "a correctly ordered questionnaire"
104
+ end
105
+
106
+ shared_context "when a non multiple choice question is mandatory" do
107
+ let!(:question) do
108
+ create(
109
+ :questionnaire_question,
110
+ questionnaire: questionnaire,
111
+ question_type: "short_answer",
112
+ position: 0,
113
+ mandatory: true
114
+ )
115
+ end
116
+
117
+ before do
118
+ visit questionnaire_public_path
119
+
120
+ check "questionnaire_tos_agreement"
121
+ end
122
+ end
123
+
124
+ describe "leaving a blank question (without js)", driver: :rack_test do
125
+ include_context "when a non multiple choice question is mandatory"
126
+
127
+ before do
128
+ click_button "Submit"
129
+ end
130
+
131
+ it "submits the form and shows errors" do
132
+ within ".alert.flash" do
133
+ expect(page).to have_content("error")
134
+ end
135
+
136
+ expect(page).to have_content("can't be blank")
137
+ end
138
+ end
139
+
140
+ describe "leaving a blank question (with js)" do
141
+ include_context "when a non multiple choice question is mandatory"
142
+
143
+ before do
144
+ accept_confirm { click_button "Submit" }
145
+ end
146
+
147
+ it "shows errors without submitting the form" do
148
+ expect(page).to have_no_selector ".alert.flash"
149
+
150
+ expect(page).to have_content("can't be blank")
151
+ end
152
+ end
153
+
154
+ describe "leaving a blank multiple choice question" do
155
+ let!(:question) do
156
+ create(
157
+ :questionnaire_question,
158
+ questionnaire: questionnaire,
159
+ question_type: "single_option",
160
+ position: 0,
161
+ mandatory: true,
162
+ answer_options: [
163
+ { "body" => Decidim::Faker::Localized.sentence },
164
+ { "body" => Decidim::Faker::Localized.sentence }
165
+ ]
166
+ )
167
+ end
168
+
169
+ before do
170
+ visit questionnaire_public_path
171
+
172
+ check "questionnaire_tos_agreement"
173
+
174
+ accept_confirm { click_button "Submit" }
175
+ end
176
+
177
+ it "submits the form and shows errors" do
178
+ within ".alert.flash" do
179
+ expect(page).to have_content("error")
180
+ end
181
+
182
+ expect(page).to have_content("can't be blank")
183
+ end
184
+ end
185
+
186
+ context "when a question has a rich text description" do
187
+ let!(:question) { create(:questionnaire_question, questionnaire: questionnaire, position: 0, description: "<b>This question is important</b>") }
188
+
189
+ it "properly interprets HTML descriptions" do
190
+ visit questionnaire_public_path
191
+
192
+ expect(page).to have_selector("b", text: "This question is important")
193
+ end
194
+ end
195
+
196
+ describe "free text options" do
197
+ let(:answer_option_bodies) { Array.new(3) { Decidim::Faker::Localized.sentence } }
198
+
199
+ let!(:question) do
200
+ create(
201
+ :questionnaire_question,
202
+ questionnaire: questionnaire,
203
+ question_type: question_type,
204
+ answer_options: [
205
+ { "body" => answer_option_bodies[0] },
206
+ { "body" => answer_option_bodies[1] },
207
+ { "body" => answer_option_bodies[2], "free_text" => true }
208
+ ]
209
+ )
210
+ end
211
+
212
+ let!(:other_question) do
213
+ create(
214
+ :questionnaire_question,
215
+ questionnaire: questionnaire,
216
+ question_type: "multiple_option",
217
+ max_choices: 2,
218
+ answer_options: [
219
+ { "body" => Decidim::Faker::Localized.sentence },
220
+ { "body" => Decidim::Faker::Localized.sentence },
221
+ { "body" => Decidim::Faker::Localized.sentence }
222
+ ]
223
+ )
224
+ end
225
+
226
+ before do
227
+ visit questionnaire_public_path
228
+ end
229
+
230
+ context "when question is single_option type" do
231
+ let(:question_type) { "single_option" }
232
+
233
+ it "renders them as radio buttons with attached text fields disabled by default" do
234
+ expect(page).to have_selector(".radio-button-collection input[type=radio]", count: 3)
235
+
236
+ expect(page).to have_field("questionnaire_answers_0_choices_2_custom_body", disabled: true, count: 1)
237
+
238
+ choose answer_option_bodies[2]["en"]
239
+
240
+ expect(page).to have_field("questionnaire_answers_0_choices_2_custom_body", disabled: false, count: 1)
241
+ end
242
+
243
+ it "saves the free text in a separate field if submission correct" do
244
+ choose answer_option_bodies[2]["en"]
245
+ fill_in "questionnaire_answers_0_choices_2_custom_body", with: "Cacatua"
246
+
247
+ check "questionnaire_tos_agreement"
248
+ accept_confirm { click_button "Submit" }
249
+
250
+ within ".success.flash" do
251
+ expect(page).to have_content("successfully")
252
+ end
253
+
254
+ expect(Decidim::Forms::Answer.first.choices.first.custom_body).to eq("Cacatua")
255
+ end
256
+
257
+ it "preserves the previous custom body if submission not correct" do
258
+ check other_question.answer_options.first.body["en"]
259
+ check other_question.answer_options.second.body["en"]
260
+ check other_question.answer_options.third.body["en"]
261
+
262
+ choose answer_option_bodies[2]["en"]
263
+ fill_in "questionnaire_answers_0_choices_2_custom_body", with: "Cacatua"
264
+
265
+ check "questionnaire_tos_agreement"
266
+ accept_confirm { click_button "Submit" }
267
+
268
+ within ".alert.flash" do
269
+ expect(page).to have_content("There's been errors when answering")
270
+ end
271
+
272
+ expect(page).to have_field("questionnaire_answers_0_choices_2_custom_body", with: "Cacatua")
273
+ end
274
+ end
275
+
276
+ context "when question is multiple_option type" do
277
+ let(:question_type) { "multiple_option" }
278
+
279
+ it "renders them as check boxes with attached text fields disabled by default" do
280
+ expect(page.first(".check-box-collection")).to have_selector("input[type=checkbox]", count: 3)
281
+
282
+ expect(page).to have_field("questionnaire_answers_0_choices_2_custom_body", disabled: true, count: 1)
283
+
284
+ check answer_option_bodies[2]["en"]
285
+
286
+ expect(page).to have_field("questionnaire_answers_0_choices_2_custom_body", disabled: false, count: 1)
287
+ end
288
+
289
+ it "saves the free text in a separate field if submission correct" do
290
+ check answer_option_bodies[2]["en"]
291
+ fill_in "questionnaire_answers_0_choices_2_custom_body", with: "Cacatua"
292
+
293
+ check "questionnaire_tos_agreement"
294
+ accept_confirm { click_button "Submit" }
295
+
296
+ within ".success.flash" do
297
+ expect(page).to have_content("successfully")
298
+ end
299
+
300
+ expect(Decidim::Forms::Answer.first.choices.first.custom_body).to eq("Cacatua")
301
+ end
302
+
303
+ it "preserves the previous custom body if submission not correct" do
304
+ check "questionnaire_answers_1_choices_0_body"
305
+ check "questionnaire_answers_1_choices_1_body"
306
+ check "questionnaire_answers_1_choices_2_body"
307
+
308
+ check answer_option_bodies[2]["en"]
309
+ fill_in "questionnaire_answers_0_choices_2_custom_body", with: "Cacatua"
310
+
311
+ check "questionnaire_tos_agreement"
312
+ accept_confirm { click_button "Submit" }
313
+
314
+ within ".alert.flash" do
315
+ expect(page).to have_content("There's been errors when answering")
316
+ end
317
+
318
+ expect(page).to have_field("questionnaire_answers_0_choices_2_custom_body", with: "Cacatua")
319
+ end
320
+ end
321
+ end
322
+
323
+ context "when question type is long answer" do
324
+ let!(:question) { create(:questionnaire_question, questionnaire: questionnaire, question_type: "long_answer") }
325
+
326
+ it "renders the answer as a textarea" do
327
+ visit questionnaire_public_path
328
+
329
+ expect(page).to have_selector("textarea#questionnaire_answers_0")
330
+ end
331
+ end
332
+
333
+ context "when question type is short answer" do
334
+ let!(:question) { create(:questionnaire_question, questionnaire: questionnaire, question_type: "short_answer") }
335
+
336
+ it "renders the answer as a text field" do
337
+ visit questionnaire_public_path
338
+
339
+ expect(page).to have_selector("input[type=text]#questionnaire_answers_0")
340
+ end
341
+ end
342
+
343
+ context "when question type is single option" do
344
+ let(:answer_options) { Array.new(2) { { "body" => Decidim::Faker::Localized.sentence } } }
345
+ let!(:question) { create(:questionnaire_question, questionnaire: questionnaire, question_type: "single_option", answer_options: answer_options) }
346
+
347
+ it "renders answers as a collection of radio buttons" do
348
+ visit questionnaire_public_path
349
+
350
+ expect(page).to have_selector(".radio-button-collection input[type=radio]", count: 2)
351
+
352
+ choose answer_options[0]["body"][:en]
353
+
354
+ check "questionnaire_tos_agreement"
355
+
356
+ accept_confirm { click_button "Submit" }
357
+
358
+ within ".success.flash" do
359
+ expect(page).to have_content("successfully")
360
+ end
361
+
362
+ visit questionnaire_public_path
363
+
364
+ expect(page).to have_content("You have already answered this questionnaire.")
365
+ expect(page).to have_no_i18n_content(question.body)
366
+ end
367
+ end
368
+
369
+ context "when question type is multiple option" do
370
+ let(:answer_options) { Array.new(3) { { "body" => Decidim::Faker::Localized.sentence } } }
371
+ let!(:question) { create(:questionnaire_question, questionnaire: questionnaire, question_type: "multiple_option", answer_options: answer_options) }
372
+
373
+ it "renders answers as a collection of radio buttons" do
374
+ visit questionnaire_public_path
375
+
376
+ expect(page).to have_selector(".check-box-collection input[type=checkbox]", count: 3)
377
+
378
+ expect(page).to have_no_content("Max choices:")
379
+
380
+ check answer_options[0]["body"][:en]
381
+ check answer_options[1]["body"][:en]
382
+
383
+ check "questionnaire_tos_agreement"
384
+
385
+ accept_confirm { click_button "Submit" }
386
+
387
+ within ".success.flash" do
388
+ expect(page).to have_content("successfully")
389
+ end
390
+
391
+ visit questionnaire_public_path
392
+
393
+ expect(page).to have_content("You have already answered this questionnaire.")
394
+ expect(page).to have_no_i18n_content(question.body)
395
+ end
396
+
397
+ it "respects the max number of choices" do
398
+ question.update!(max_choices: 2)
399
+
400
+ visit questionnaire_public_path
401
+
402
+ expect(page).to have_content("Max choices: 2")
403
+
404
+ check answer_options[0]["body"][:en]
405
+ check answer_options[1]["body"][:en]
406
+ check answer_options[2]["body"][:en]
407
+
408
+ check "questionnaire_tos_agreement"
409
+
410
+ accept_confirm { click_button "Submit" }
411
+
412
+ within ".alert.flash" do
413
+ expect(page).to have_content("There's been errors")
414
+ end
415
+
416
+ expect(page).to have_content("are too many")
417
+
418
+ uncheck answer_options[2]["body"][:en]
419
+
420
+ accept_confirm { click_button "Submit" }
421
+
422
+ within ".success.flash" do
423
+ expect(page).to have_content("successfully")
424
+ end
425
+ end
426
+ end
427
+
428
+ context "when question type is multiple option" do
429
+ let(:answer_options) { Array.new(2) { { "body" => Decidim::Faker::Localized.sentence } } }
430
+ let!(:question) { create(:questionnaire_question, questionnaire: questionnaire, question_type: "multiple_option", answer_options: answer_options) }
431
+
432
+ it "renders the question answers as a collection of radio buttons" do
433
+ visit questionnaire_public_path
434
+
435
+ expect(page).to have_selector(".check-box-collection input[type=checkbox]", count: 2)
436
+
437
+ check answer_options[0]["body"][:en]
438
+ check answer_options[1]["body"][:en]
439
+
440
+ check "questionnaire_tos_agreement"
441
+
442
+ accept_confirm { click_button "Submit" }
443
+
444
+ within ".success.flash" do
445
+ expect(page).to have_content("successfully")
446
+ end
447
+
448
+ visit questionnaire_public_path
449
+
450
+ expect(page).to have_content("You have already answered this questionnaire.")
451
+ expect(page).to have_no_i18n_content(question.body)
452
+ end
453
+ end
454
+
455
+ context "when question type is sorting" do
456
+ let!(:question) do
457
+ create(
458
+ :questionnaire_question,
459
+ questionnaire: questionnaire,
460
+ question_type: "sorting",
461
+ answer_options: [
462
+ { "body" => "idiotas" },
463
+ { "body" => "trates" },
464
+ { "body" => "No" },
465
+ { "body" => "por" },
466
+ { "body" => "nos" }
467
+ ]
468
+ )
469
+ end
470
+
471
+ it "renders the question answers as a collection of check boxes sortable on click" do
472
+ visit questionnaire_public_path
473
+
474
+ expect(page).to have_selector(".sortable-check-box-collection input[type=checkbox]", count: 5)
475
+
476
+ expect(page).to have_content("idiotas\ntrates\nNo\npor\nnos")
477
+
478
+ check "No"
479
+ check "nos"
480
+ check "trates"
481
+ check "por"
482
+ check "idiotas"
483
+
484
+ expect(page).to have_content("1. No\n2. nos\n3. trates\n4. por\n5. idiotas")
485
+ end
486
+
487
+ it "properly saves valid sortings" do
488
+ visit questionnaire_public_path
489
+
490
+ check "No"
491
+ check "nos"
492
+ check "trates"
493
+ check "por"
494
+ check "idiotas"
495
+
496
+ check "questionnaire_tos_agreement"
497
+
498
+ accept_confirm { click_button "Submit" }
499
+
500
+ within ".success.flash" do
501
+ expect(page).to have_content("successfully")
502
+ end
503
+
504
+ expect(Decidim::Forms::Answer.first.choices.pluck(:position, :body)).to eq(
505
+ [[0, "No"], [1, "nos"], [2, "trates"], [3, "por"], [4, "idiotas"]]
506
+ )
507
+ end
508
+
509
+ it "displays errors on incomplete sortings" do
510
+ visit questionnaire_public_path
511
+
512
+ check "No"
513
+
514
+ accept_confirm { click_button "Submit" }
515
+
516
+ within ".alert.flash" do
517
+ expect(page).to have_content("error")
518
+ end
519
+
520
+ expect(page).to have_content("are not complete")
521
+ end
522
+ end
523
+ end
524
+ end