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,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