decidim-forms 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
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