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.
- checksums.yaml +7 -0
- data/README.md +65 -0
- data/Rakefile +3 -0
- data/app/assets/config/admin/decidim_forms_manifest.js +1 -0
- data/app/assets/config/decidim_forms_manifest.js +1 -0
- data/app/assets/images/decidim/surveys/icon.svg +19 -0
- data/app/assets/javascripts/decidim/forms/admin/auto_buttons_by_min_items.component.js.es6 +25 -0
- data/app/assets/javascripts/decidim/forms/admin/auto_select_options_by_total_items.component.js.es6 +23 -0
- data/app/assets/javascripts/decidim/forms/admin/forms.js.es6 +188 -0
- data/app/assets/javascripts/decidim/forms/autosortable_checkboxes.component.js.es6 +65 -0
- data/app/assets/javascripts/decidim/forms/forms.js.es6 +20 -0
- data/app/assets/javascripts/decidim/forms/option_attached_inputs.component.js.es6 +32 -0
- data/app/commands/decidim/forms/admin/update_questionnaire.rb +86 -0
- data/app/commands/decidim/forms/answer_questionnaire.rb +54 -0
- data/app/controllers/decidim/forms/admin/concerns/has_questionnaire.rb +95 -0
- data/app/controllers/decidim/forms/concerns/has_questionnaire.rb +84 -0
- data/app/forms/decidim/forms/admin/answer_option_form.rb +23 -0
- data/app/forms/decidim/forms/admin/question_form.rb +35 -0
- data/app/forms/decidim/forms/admin/questionnaire_form.rb +27 -0
- data/app/forms/decidim/forms/answer_choice_form.rb +15 -0
- data/app/forms/decidim/forms/answer_form.rb +69 -0
- data/app/forms/decidim/forms/questionnaire_form.rb +22 -0
- data/app/helpers/decidim/forms/admin/application_helper.rb +19 -0
- data/app/models/concerns/decidim/forms/has_questionnaire.rb +20 -0
- data/app/models/decidim/forms/answer.rb +45 -0
- data/app/models/decidim/forms/answer_choice.rb +15 -0
- data/app/models/decidim/forms/answer_option.rb +11 -0
- data/app/models/decidim/forms/application_record.rb +10 -0
- data/app/models/decidim/forms/question.rb +36 -0
- data/app/models/decidim/forms/questionnaire.rb +23 -0
- data/app/queries/decidim/forms/questionnaire_user_answers.rb +28 -0
- data/app/views/decidim/forms/admin/questionnaires/_answer_option.html.erb +44 -0
- data/app/views/decidim/forms/admin/questionnaires/_form.html.erb +51 -0
- data/app/views/decidim/forms/admin/questionnaires/_question.html.erb +115 -0
- data/app/views/decidim/forms/admin/questionnaires/edit.html.erb +7 -0
- data/app/views/decidim/forms/questionnaires/_answer.html.erb +94 -0
- data/app/views/decidim/forms/questionnaires/show.html.erb +84 -0
- data/config/locales/ca.yml +79 -0
- data/config/locales/de.yml +79 -0
- data/config/locales/en.yml +79 -0
- data/config/locales/es-PY.yml +79 -0
- data/config/locales/es.yml +79 -0
- data/config/locales/eu.yml +79 -0
- data/config/locales/fi-pl.yml +79 -0
- data/config/locales/fi.yml +79 -0
- data/config/locales/fr.yml +79 -0
- data/config/locales/gl.yml +79 -0
- data/config/locales/hu.yml +79 -0
- data/config/locales/id-ID.yml +1 -0
- data/config/locales/it.yml +79 -0
- data/config/locales/nl.yml +79 -0
- data/config/locales/pl.yml +79 -0
- data/config/locales/pt-BR.yml +79 -0
- data/config/locales/pt.yml +79 -0
- data/config/locales/ru.yml +1 -0
- data/config/locales/sv.yml +79 -0
- data/config/locales/tr-TR.yml +1 -0
- data/config/locales/uk.yml +1 -0
- data/db/migrate/20170511092231_create_decidim_forms_questionnaires.rb +15 -0
- data/db/migrate/20170515090916_create_decidim_forms_questions.rb +17 -0
- data/db/migrate/20170515144119_create_decidim_forms_answers.rb +15 -0
- data/db/migrate/20180405015012_create_decidim_forms_answer_options.rb +11 -0
- data/db/migrate/20180405015147_create_decidim_forms_answer_choices.rb +13 -0
- data/lib/decidim/forms.rb +13 -0
- data/lib/decidim/forms/admin.rb +9 -0
- data/lib/decidim/forms/admin_engine.rb +21 -0
- data/lib/decidim/forms/data_portability_user_answers_serializer.rb +37 -0
- data/lib/decidim/forms/engine.rb +14 -0
- data/lib/decidim/forms/test.rb +4 -0
- data/lib/decidim/forms/test/factories.rb +55 -0
- data/lib/decidim/forms/test/shared_examples/has_questionnaire.rb +524 -0
- data/lib/decidim/forms/test/shared_examples/manage_questionnaires.rb +626 -0
- data/lib/decidim/forms/user_answers_serializer.rb +29 -0
- data/lib/decidim/forms/version.rb +10 -0
- metadata +165 -0
@@ -0,0 +1,626 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
shared_examples_for "manage questionnaires" do
|
6
|
+
let(:body) do
|
7
|
+
{
|
8
|
+
en: "This is the first question",
|
9
|
+
ca: "Aquesta es la primera pregunta",
|
10
|
+
es: "Esta es la primera pregunta"
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
it "updates the questionnaire" do
|
15
|
+
visit questionnaire_edit_path
|
16
|
+
|
17
|
+
new_description = {
|
18
|
+
en: "<p>New description</p>",
|
19
|
+
ca: "<p>Nova descripció</p>",
|
20
|
+
es: "<p>Nueva descripción</p>"
|
21
|
+
}
|
22
|
+
|
23
|
+
within "form.edit_questionnaire" do
|
24
|
+
fill_in_i18n_editor(:questionnaire_description, "#questionnaire-description-tabs", new_description)
|
25
|
+
click_button "Save"
|
26
|
+
end
|
27
|
+
|
28
|
+
expect(page).to have_admin_callout("successfully")
|
29
|
+
|
30
|
+
visit questionnaire_public_path
|
31
|
+
|
32
|
+
expect(page).to have_content("New description")
|
33
|
+
end
|
34
|
+
|
35
|
+
context "when the questionnaire is not already answered" do
|
36
|
+
before do
|
37
|
+
visit questionnaire_edit_path
|
38
|
+
end
|
39
|
+
|
40
|
+
it "adds a few questions to the questionnaire" do
|
41
|
+
questions_body = ["This is the first question", "This is the second question"]
|
42
|
+
|
43
|
+
within "form.edit_questionnaire" do
|
44
|
+
2.times { click_button "Add question" }
|
45
|
+
|
46
|
+
expect(page).to have_selector(".questionnaire-question", count: 2)
|
47
|
+
|
48
|
+
page.all(".questionnaire-question").each_with_index do |question, idx|
|
49
|
+
within question do
|
50
|
+
fill_in find_nested_form_field_locator("body_en"), with: questions_body[idx]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
click_button "Save"
|
55
|
+
end
|
56
|
+
|
57
|
+
expect(page).to have_admin_callout("successfully")
|
58
|
+
|
59
|
+
visit questionnaire_edit_path
|
60
|
+
|
61
|
+
expect(page).to have_selector("input[value='This is the first question']")
|
62
|
+
expect(page).to have_selector("input[value='This is the second question']")
|
63
|
+
end
|
64
|
+
|
65
|
+
it "adds a question with a rich text description" do
|
66
|
+
within "form.edit_questionnaire" do
|
67
|
+
click_button "Add question"
|
68
|
+
|
69
|
+
within ".questionnaire-question" do
|
70
|
+
fill_in find_nested_form_field_locator("body_en"), with: "Body"
|
71
|
+
|
72
|
+
fill_in_editor find_nested_form_field_locator("description_en", visible: false), with: "<b>Superkalifragilistic description</b>"
|
73
|
+
end
|
74
|
+
|
75
|
+
click_button "Save"
|
76
|
+
end
|
77
|
+
|
78
|
+
expect(page).to have_admin_callout("successfully")
|
79
|
+
|
80
|
+
component.update!(
|
81
|
+
step_settings: {
|
82
|
+
component.participatory_space.active_step.id => {
|
83
|
+
allow_answers: true
|
84
|
+
}
|
85
|
+
}
|
86
|
+
)
|
87
|
+
|
88
|
+
visit questionnaire_public_path
|
89
|
+
|
90
|
+
expect(page).to have_selector("strong", text: "Superkalifragilistic description")
|
91
|
+
end
|
92
|
+
|
93
|
+
it "adds a question with answer options" do
|
94
|
+
question_body = "This is the first question"
|
95
|
+
answer_options_body = ["This is the first option", "This is the second option"]
|
96
|
+
|
97
|
+
within "form.edit_questionnaire" do
|
98
|
+
click_button "Add question"
|
99
|
+
|
100
|
+
within ".questionnaire-question" do
|
101
|
+
fill_in find_nested_form_field_locator("body_en"), with: question_body
|
102
|
+
end
|
103
|
+
|
104
|
+
expect(page).to have_no_content "Add answer option"
|
105
|
+
|
106
|
+
select "Single option", from: "Type"
|
107
|
+
|
108
|
+
page.all(".questionnaire-question-answer-option").each_with_index do |question_answer_option, idx|
|
109
|
+
within question_answer_option do
|
110
|
+
fill_in find_nested_form_field_locator("body_en"), with: answer_options_body[idx]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
click_button "Save"
|
115
|
+
end
|
116
|
+
|
117
|
+
expect(page).to have_admin_callout("successfully")
|
118
|
+
|
119
|
+
visit questionnaire_edit_path
|
120
|
+
|
121
|
+
expect(page).to have_selector("input[value='This is the first question']")
|
122
|
+
expect(page).to have_selector("input[value='This is the first option']")
|
123
|
+
expect(page).to have_selector("input[value='This is the second option']")
|
124
|
+
end
|
125
|
+
|
126
|
+
it "adds a sane number of options for each attribute type" do
|
127
|
+
click_button "Add question"
|
128
|
+
|
129
|
+
select "Long answer", from: "Type"
|
130
|
+
expect(page).to have_no_selector(".questionnaire-question-answer-option")
|
131
|
+
|
132
|
+
select "Single option", from: "Type"
|
133
|
+
expect(page).to have_selector(".questionnaire-question-answer-option", count: 2)
|
134
|
+
|
135
|
+
select "Multiple option", from: "Type"
|
136
|
+
expect(page).to have_selector(".questionnaire-question-answer-option", count: 2)
|
137
|
+
|
138
|
+
select "Single option", from: "Type"
|
139
|
+
expect(page).to have_selector(".questionnaire-question-answer-option", count: 2)
|
140
|
+
|
141
|
+
select "Short answer", from: "Type"
|
142
|
+
expect(page).to have_no_selector(".questionnaire-question-answer-option")
|
143
|
+
end
|
144
|
+
|
145
|
+
it "does not incorrectly reorder when clicking answer options" do
|
146
|
+
click_button "Add question"
|
147
|
+
select "Single option", from: "Type"
|
148
|
+
2.times { click_button "Add answer option" }
|
149
|
+
|
150
|
+
within ".questionnaire-question-answer-option:first-of-type" do
|
151
|
+
fill_in find_nested_form_field_locator("body_en"), with: "Something"
|
152
|
+
end
|
153
|
+
|
154
|
+
within ".questionnaire-question-answer-option:last-of-type" do
|
155
|
+
fill_in find_nested_form_field_locator("body_en"), with: "Else"
|
156
|
+
end
|
157
|
+
|
158
|
+
# If JS events for option reordering are incorrectly bound, clicking on
|
159
|
+
# the field to gain focus can cause the options to get inverted... :S
|
160
|
+
within ".questionnaire-question-answer-option:first-of-type" do
|
161
|
+
find_nested_form_field("body_en").click
|
162
|
+
end
|
163
|
+
|
164
|
+
within ".questionnaire-question-answer-option:first-of-type" do
|
165
|
+
expect(page).to have_nested_field("body_en", with: "Something")
|
166
|
+
end
|
167
|
+
|
168
|
+
within ".questionnaire-question-answer-option:last-of-type" do
|
169
|
+
expect(page).to have_nested_field("body_en", with: "Else")
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
it "preserves question form across submission failures" do
|
174
|
+
click_button "Add question"
|
175
|
+
select "Long answer", from: "Type"
|
176
|
+
click_button "Save"
|
177
|
+
|
178
|
+
expect(page).to have_select("Type", selected: "Long answer")
|
179
|
+
end
|
180
|
+
|
181
|
+
it "does not preserve spurious answer options from previous type selections" do
|
182
|
+
click_button "Add question"
|
183
|
+
select "Single option", from: "Type"
|
184
|
+
|
185
|
+
within ".questionnaire-question-answer-option:first-of-type" do
|
186
|
+
fill_in find_nested_form_field_locator("body_en"), with: "Something"
|
187
|
+
end
|
188
|
+
|
189
|
+
select "Long answer", from: "Type"
|
190
|
+
|
191
|
+
click_button "Save"
|
192
|
+
|
193
|
+
select "Single option", from: "Type"
|
194
|
+
|
195
|
+
within ".questionnaire-question-answer-option:first-of-type" do
|
196
|
+
expect(page).to have_no_nested_field("body_en", with: "Something")
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
it "preserves answer options form across submission failures" do
|
201
|
+
click_button "Add question"
|
202
|
+
select "Multiple option", from: "Type"
|
203
|
+
|
204
|
+
within ".questionnaire-question-answer-option:first-of-type" do
|
205
|
+
fill_in find_nested_form_field_locator("body_en"), with: "Something"
|
206
|
+
end
|
207
|
+
|
208
|
+
click_button "Add answer option"
|
209
|
+
|
210
|
+
within ".questionnaire-question-answer-option:last-of-type" do
|
211
|
+
fill_in find_nested_form_field_locator("body_en"), with: "Else"
|
212
|
+
end
|
213
|
+
|
214
|
+
select "3", from: "Maximum number of choices"
|
215
|
+
|
216
|
+
click_button "Save"
|
217
|
+
|
218
|
+
within ".questionnaire-question-answer-option:first-of-type" do
|
219
|
+
expect(page).to have_nested_field("body_en", with: "Something")
|
220
|
+
end
|
221
|
+
|
222
|
+
within ".questionnaire-question-answer-option:last-of-type" do
|
223
|
+
fill_in find_nested_form_field_locator("body_en"), with: "Else"
|
224
|
+
end
|
225
|
+
|
226
|
+
expect(page).to have_select("Maximum number of choices", selected: "3")
|
227
|
+
end
|
228
|
+
|
229
|
+
it "allows switching translated field tabs after form failures" do
|
230
|
+
click_button "Add question"
|
231
|
+
click_button "Save"
|
232
|
+
|
233
|
+
within ".questionnaire-question:first-of-type" do
|
234
|
+
fill_in find_nested_form_field_locator("body_en"), with: "Bye"
|
235
|
+
click_link "Català", match: :first
|
236
|
+
|
237
|
+
fill_in find_nested_form_field_locator("body_ca"), with: "Adeu"
|
238
|
+
click_link "English", match: :first
|
239
|
+
|
240
|
+
expect(page).to have_nested_field("body_en", with: "Bye")
|
241
|
+
expect(page).to have_no_selector(nested_form_field_selector("body_ca"))
|
242
|
+
expect(page).to have_no_content("Adeu")
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
context "when adding a multiple option question" do
|
247
|
+
before do
|
248
|
+
visit questionnaire_edit_path
|
249
|
+
|
250
|
+
within "form.edit_questionnaire" do
|
251
|
+
click_button "Add question"
|
252
|
+
|
253
|
+
within ".questionnaire-question" do
|
254
|
+
fill_in find_nested_form_field_locator("body_en"), with: "This is the first question"
|
255
|
+
end
|
256
|
+
|
257
|
+
expect(page).to have_no_content "Add answer option"
|
258
|
+
expect(page).to have_no_select("Maximum number of choices")
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
it "updates the free text option selector according to the selected question type" do
|
263
|
+
expect(page).to have_no_selector("input[type=checkbox][id$=_free_text]")
|
264
|
+
|
265
|
+
select "Multiple option", from: "Type"
|
266
|
+
expect(page).to have_selector("input[type=checkbox][id$=_free_text]")
|
267
|
+
|
268
|
+
select "Short answer", from: "Type"
|
269
|
+
expect(page).to have_no_selector("input[type=checkbox][id$=_free_text]")
|
270
|
+
|
271
|
+
select "Single option", from: "Type"
|
272
|
+
expect(page).to have_selector("input[type=checkbox][id$=_free_text]")
|
273
|
+
end
|
274
|
+
|
275
|
+
it "updates the max choices selector according to the configured options" do
|
276
|
+
expect(page).to have_no_select("Maximum number of choices")
|
277
|
+
|
278
|
+
select "Multiple option", from: "Type"
|
279
|
+
expect(page).to have_select("Maximum number of choices", options: %w(Any 2))
|
280
|
+
|
281
|
+
click_button "Add answer option"
|
282
|
+
expect(page).to have_select("Maximum number of choices", options: %w(Any 2 3))
|
283
|
+
|
284
|
+
click_button "Add answer option"
|
285
|
+
expect(page).to have_select("Maximum number of choices", options: %w(Any 2 3 4))
|
286
|
+
|
287
|
+
within(".questionnaire-question-answer-option:last-of-type") { click_button "Remove" }
|
288
|
+
expect(page).to have_select("Maximum number of choices", options: %w(Any 2 3))
|
289
|
+
|
290
|
+
within(".questionnaire-question-answer-option:last-of-type") { click_button "Remove" }
|
291
|
+
expect(page).to have_select("Maximum number of choices", options: %w(Any 2))
|
292
|
+
|
293
|
+
click_button "Add question"
|
294
|
+
|
295
|
+
within(".questionnaire-question:last-of-type") do
|
296
|
+
select "Multiple option", from: "Type"
|
297
|
+
expect(page).to have_select("Maximum number of choices", options: %w(Any 2))
|
298
|
+
|
299
|
+
select "Single option", from: "Type"
|
300
|
+
expect(page).to have_no_select("Maximum number of choices")
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
context "when a questionnaire has an existing question" do
|
306
|
+
let!(:question) { create(:questionnaire_question, questionnaire: questionnaire, body: body) }
|
307
|
+
|
308
|
+
before do
|
309
|
+
visit questionnaire_edit_path
|
310
|
+
end
|
311
|
+
|
312
|
+
it "modifies the question when the information is valid" do
|
313
|
+
within "form.edit_questionnaire" do
|
314
|
+
within ".questionnaire-question" do
|
315
|
+
fill_in "questionnaire_questions_#{question.id}_body_en", with: "Modified question"
|
316
|
+
check "Mandatory"
|
317
|
+
select "Long answer", from: "Type"
|
318
|
+
end
|
319
|
+
|
320
|
+
click_button "Save"
|
321
|
+
end
|
322
|
+
|
323
|
+
expect(page).to have_admin_callout("successfully")
|
324
|
+
|
325
|
+
visit questionnaire_edit_path
|
326
|
+
|
327
|
+
expect(page).to have_selector("input[value='Modified question']")
|
328
|
+
expect(page).to have_no_selector("input[value='This is the first question']")
|
329
|
+
expect(page).to have_selector("input#questionnaire_questions_#{question.id}_mandatory[checked]")
|
330
|
+
expect(page).to have_selector("select#questionnaire_questions_#{question.id}_question_type option[value='long_answer'][selected]")
|
331
|
+
end
|
332
|
+
|
333
|
+
it "re-renders the form when the information is invalid and displays errors" do
|
334
|
+
within "form.edit_questionnaire" do
|
335
|
+
within ".questionnaire-question" do
|
336
|
+
expect(page).to have_content("Statement*")
|
337
|
+
fill_in "questionnaire_questions_#{question.id}_body_en", with: ""
|
338
|
+
check "Mandatory"
|
339
|
+
select "Multiple option", from: "Type"
|
340
|
+
select "2", from: "Maximum number of choices"
|
341
|
+
end
|
342
|
+
|
343
|
+
click_button "Save"
|
344
|
+
end
|
345
|
+
|
346
|
+
expect(page).to have_admin_callout("There's been errors")
|
347
|
+
expect(page).to have_content("can't be blank", count: 3) # emtpy question, 2 empty default answer options
|
348
|
+
|
349
|
+
expect(page).to have_selector("input[value='']")
|
350
|
+
expect(page).to have_no_selector("input[value='This is the first question']")
|
351
|
+
expect(page).to have_selector("input#questionnaire_questions_#{question.id}_mandatory[checked]")
|
352
|
+
expect(page).to have_select("Maximum number of choices", selected: "2")
|
353
|
+
expect(page).to have_selector("select#questionnaire_questions_#{question.id}_question_type option[value='multiple_option'][selected]")
|
354
|
+
end
|
355
|
+
|
356
|
+
it "preserves deleted status across submission failures" do
|
357
|
+
within "form.edit_questionnaire" do
|
358
|
+
within ".questionnaire-question" do
|
359
|
+
click_button "Remove"
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
click_button "Add question"
|
364
|
+
|
365
|
+
click_button "Save"
|
366
|
+
|
367
|
+
expect(page).to have_selector(".questionnaire-question", count: 1)
|
368
|
+
|
369
|
+
within ".questionnaire-question" do
|
370
|
+
expect(page).to have_selector(".card-title", text: "#1")
|
371
|
+
expect(page).to have_no_button("Up")
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
it "removes the question" do
|
376
|
+
within "form.edit_questionnaire" do
|
377
|
+
within ".questionnaire-question" do
|
378
|
+
click_button "Remove"
|
379
|
+
end
|
380
|
+
|
381
|
+
click_button "Save"
|
382
|
+
end
|
383
|
+
|
384
|
+
expect(page).to have_admin_callout("successfully")
|
385
|
+
|
386
|
+
visit questionnaire_edit_path
|
387
|
+
|
388
|
+
within "form.edit_questionnaire" do
|
389
|
+
expect(page).to have_selector(".questionnaire-question", count: 0)
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
it "cannot be moved up" do
|
394
|
+
within "form.edit_questionnaire" do
|
395
|
+
within ".questionnaire-question" do
|
396
|
+
expect(page).to have_no_button("Up")
|
397
|
+
end
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
it "cannot be moved down" do
|
402
|
+
within "form.edit_questionnaire" do
|
403
|
+
within ".questionnaire-question" do
|
404
|
+
expect(page).to have_no_button("Down")
|
405
|
+
end
|
406
|
+
end
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
context "when a questionnaire has an existing question with answer options" do
|
411
|
+
let!(:question) do
|
412
|
+
create(
|
413
|
+
:questionnaire_question,
|
414
|
+
questionnaire: questionnaire,
|
415
|
+
body: body,
|
416
|
+
question_type: "single_option",
|
417
|
+
answer_options: [
|
418
|
+
{ "body" => { "en" => "cacarua" } },
|
419
|
+
{ "body" => { "en" => "cat" } },
|
420
|
+
{ "body" => { "en" => "dog" } }
|
421
|
+
|
422
|
+
]
|
423
|
+
)
|
424
|
+
end
|
425
|
+
|
426
|
+
before do
|
427
|
+
visit questionnaire_edit_path
|
428
|
+
end
|
429
|
+
|
430
|
+
it "allows deleting answer options" do
|
431
|
+
within ".questionnaire-question-answer-option:last-of-type" do
|
432
|
+
click_button "Remove"
|
433
|
+
end
|
434
|
+
|
435
|
+
click_button "Save"
|
436
|
+
|
437
|
+
visit questionnaire_edit_path
|
438
|
+
|
439
|
+
expect(page).to have_selector(".questionnaire-question-answer-option", count: 2)
|
440
|
+
end
|
441
|
+
|
442
|
+
it "still removes the question even if previous editions rendered the options invalid" do
|
443
|
+
within "form.edit_questionnaire" do
|
444
|
+
expect(page).to have_selector(".questionnaire-question", count: 1)
|
445
|
+
|
446
|
+
within ".questionnaire-question-answer-option:first-of-type" do
|
447
|
+
fill_in find_nested_form_field_locator("body_en"), with: ""
|
448
|
+
end
|
449
|
+
|
450
|
+
within ".questionnaire-question" do
|
451
|
+
click_button "Remove", match: :first
|
452
|
+
end
|
453
|
+
|
454
|
+
click_button "Save"
|
455
|
+
end
|
456
|
+
|
457
|
+
expect(page).to have_admin_callout("successfully")
|
458
|
+
|
459
|
+
visit questionnaire_edit_path
|
460
|
+
|
461
|
+
within "form.edit_questionnaire" do
|
462
|
+
expect(page).to have_selector(".questionnaire-question", count: 0)
|
463
|
+
end
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
context "when a questionnaire has multiple existing questions" do
|
468
|
+
let!(:question_1) do
|
469
|
+
create(:questionnaire_question, questionnaire: questionnaire, body: first_body, position: 0)
|
470
|
+
end
|
471
|
+
|
472
|
+
let!(:question_2) do
|
473
|
+
create(:questionnaire_question, questionnaire: questionnaire, body: second_body, position: 1)
|
474
|
+
end
|
475
|
+
|
476
|
+
let(:first_body) do
|
477
|
+
{ en: "First", ca: "Primera", es: "Primera" }
|
478
|
+
end
|
479
|
+
|
480
|
+
let(:second_body) do
|
481
|
+
{ en: "Second", ca: "Segunda", es: "Segunda" }
|
482
|
+
end
|
483
|
+
|
484
|
+
before do
|
485
|
+
visit questionnaire_edit_path
|
486
|
+
end
|
487
|
+
|
488
|
+
shared_examples_for "switching questions order" do
|
489
|
+
it "properly reorders the questions" do
|
490
|
+
within ".questionnaire-question:first-of-type" do
|
491
|
+
expect(page).to have_nested_field("body_en", with: "Second")
|
492
|
+
expect(page).to look_like_first_question
|
493
|
+
end
|
494
|
+
|
495
|
+
within ".questionnaire-question:last-of-type" do
|
496
|
+
expect(page).to have_nested_field("body_en", with: "First")
|
497
|
+
expect(page).to look_like_last_question
|
498
|
+
end
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
502
|
+
context "when moving a question up" do
|
503
|
+
before do
|
504
|
+
within ".questionnaire-question:last-of-type" do
|
505
|
+
click_button "Up"
|
506
|
+
end
|
507
|
+
|
508
|
+
it_behaves_like "switching questions order"
|
509
|
+
end
|
510
|
+
end
|
511
|
+
|
512
|
+
context "when moving a question down" do
|
513
|
+
before do
|
514
|
+
within ".questionnaire-question:first-of-type" do
|
515
|
+
click_button "Down"
|
516
|
+
end
|
517
|
+
end
|
518
|
+
|
519
|
+
it_behaves_like "switching questions order"
|
520
|
+
end
|
521
|
+
|
522
|
+
it "properly decides which button to show after adding/removing questions" do
|
523
|
+
click_button "Add question"
|
524
|
+
|
525
|
+
expect(page.find(".questionnaire-question:nth-child(1)")).to look_like_first_question
|
526
|
+
expect(page.find(".questionnaire-question:nth-child(2)")).to look_like_intermediate_question
|
527
|
+
expect(page.find(".questionnaire-question:nth-child(3)")).to look_like_last_question
|
528
|
+
|
529
|
+
within ".questionnaire-question:first-of-type" do
|
530
|
+
click_button "Remove"
|
531
|
+
end
|
532
|
+
|
533
|
+
expect(page.all(".questionnaire-question").first).to look_like_first_question
|
534
|
+
expect(page.all(".questionnaire-question").last).to look_like_last_question
|
535
|
+
end
|
536
|
+
|
537
|
+
it "does not duplicate editors when adding new questions" do
|
538
|
+
expect { click_button "Add question" }.to change { page.all(".ql-toolbar").size }.by(1)
|
539
|
+
end
|
540
|
+
|
541
|
+
it "properly decides which button to show after adding/removing answer options" do
|
542
|
+
click_button "Add question"
|
543
|
+
|
544
|
+
within ".questionnaire-question:last-of-type" do
|
545
|
+
select "Single option", from: "Type"
|
546
|
+
|
547
|
+
within ".questionnaire-question-answer-options-list" do
|
548
|
+
expect(page).to have_no_button("Remove")
|
549
|
+
end
|
550
|
+
|
551
|
+
click_button "Add answer option"
|
552
|
+
|
553
|
+
expect(page.all(".questionnaire-question-answer-option")).to all(have_button("Remove"))
|
554
|
+
|
555
|
+
within ".questionnaire-question-answer-option:first-of-type" do
|
556
|
+
click_button "Remove"
|
557
|
+
end
|
558
|
+
|
559
|
+
within ".questionnaire-question-answer-options-list" do
|
560
|
+
expect(page).to have_no_button("Remove")
|
561
|
+
end
|
562
|
+
end
|
563
|
+
|
564
|
+
click_button "Save"
|
565
|
+
|
566
|
+
within ".questionnaire-question:last-of-type" do
|
567
|
+
within ".questionnaire-question-answer-options-list" do
|
568
|
+
expect(page).to have_no_button("Remove")
|
569
|
+
end
|
570
|
+
end
|
571
|
+
end
|
572
|
+
|
573
|
+
private
|
574
|
+
|
575
|
+
def look_like_first_question
|
576
|
+
have_no_button("Up").and have_button("Down")
|
577
|
+
end
|
578
|
+
|
579
|
+
def look_like_intermediate_question
|
580
|
+
have_button("Up").and have_button("Down")
|
581
|
+
end
|
582
|
+
|
583
|
+
def look_like_last_question
|
584
|
+
have_button("Up").and have_no_button("Down")
|
585
|
+
end
|
586
|
+
end
|
587
|
+
end
|
588
|
+
|
589
|
+
context "when the questionnaire is already answered" do
|
590
|
+
let!(:question) { create(:questionnaire_question, questionnaire: questionnaire, body: body, question_type: "multiple_option") }
|
591
|
+
let!(:answer) { create(:answer, questionnaire: questionnaire, question: question) }
|
592
|
+
|
593
|
+
it "cannot modify questionnaire questions" do
|
594
|
+
visit questionnaire_edit_path
|
595
|
+
|
596
|
+
expect(page).to have_no_content("Add question")
|
597
|
+
expect(page).to have_no_content("Remove")
|
598
|
+
expect(page).to have_selector("input[value='This is the first question'][disabled]")
|
599
|
+
expect(page).to have_selector("select[id$=question_type][disabled]")
|
600
|
+
expect(page).to have_selector("select[id$=max_choices][disabled]")
|
601
|
+
expect(page).to have_selector(".ql-editor[contenteditable=false]")
|
602
|
+
end
|
603
|
+
end
|
604
|
+
|
605
|
+
private
|
606
|
+
|
607
|
+
def find_nested_form_field_locator(attribute, visible: true)
|
608
|
+
find_nested_form_field(attribute, visible: visible)["id"]
|
609
|
+
end
|
610
|
+
|
611
|
+
def find_nested_form_field(attribute, visible: true)
|
612
|
+
current_scope.find(nested_form_field_selector(attribute), visible: visible)
|
613
|
+
end
|
614
|
+
|
615
|
+
def have_nested_field(attribute, with:)
|
616
|
+
have_field find_nested_form_field_locator(attribute), with: with
|
617
|
+
end
|
618
|
+
|
619
|
+
def have_no_nested_field(attribute, with:)
|
620
|
+
have_no_field(find_nested_form_field_locator(attribute), with: with)
|
621
|
+
end
|
622
|
+
|
623
|
+
def nested_form_field_selector(attribute)
|
624
|
+
"[id$=#{attribute}]"
|
625
|
+
end
|
626
|
+
end
|