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