Browse Source

Render radio conditional questions inline rather than below (#235)

* Hackety hack hack

* Leave JS hooks until text conditionals are updated as well

* Fix JS errors and remove UJS

* Reload the GOV UK frontend JS

* Fix armed forces case

* Remove obsolete tests and config

* Temo fix for household members question

* Unit test

* Add helper unit test

* Correctly failing spec

* We still need stimulus to clear answers

* Fix styling issues

* Refactor style logic into helper

* Form fix

* Conditional check box questions are shown on a new page

* Update readme

* Fix date bug

* Clear conditional date fields if invalidated by top level answer

Co-authored-by: Stéphane Meny <smeny@users.noreply.github.com>
pull/245/head
baarkerlounger 3 years ago committed by GitHub
parent
commit
d761bfefbf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      README.md
  2. 7
      app/helpers/conditional_questions_helper.rb
  3. 25
      app/helpers/question_view_helper.rb
  4. 15
      app/models/form/page.rb
  5. 2
      app/models/form/question.rb
  6. 12
      app/models/validations/household_validations.rb
  7. 4
      app/views/form/_checkbox_question.html.erb
  8. 4
      app/views/form/_date_question.html.erb
  9. 4
      app/views/form/_numeric_question.html.erb
  10. 19
      app/views/form/_radio_question.html.erb
  11. 4
      app/views/form/_select_question.html.erb
  12. 4
      app/views/form/_text_question.html.erb
  13. 4
      app/views/form/_textarea_question.html.erb
  14. 6
      app/views/form/page.html.erb
  15. 30
      app/webpacker/controllers/conditional_question_controller.js
  16. 8
      app/webpacker/controllers/govukfrontend_controller.js
  17. 8
      app/webpacker/packs/application.js
  18. 31
      config/forms/2021_2022.json
  19. 1
      package.json
  20. 8
      spec/features/form/conditional_questions_spec.rb
  21. 24
      spec/fixtures/forms/2021_2022.json
  22. 12
      spec/helpers/conditional_questions_helper_spec.rb
  23. 75
      spec/helpers/question_view_helper.rb
  24. 33
      spec/models/case_log_spec.rb
  25. 10
      spec/models/form/page_spec.rb
  26. 5
      spec/models/form/question_spec.rb
  27. 5
      yarn.lock

4
README.md

@ -159,9 +159,11 @@ Assumptions made by the format:
- All pages have at least 1 question - All pages have at least 1 question
- The ActiveRecord case log model has a field for each question name (must match). In the case of checkbox questions it must have one field for every answer option (again names must match). - The ActiveRecord case log model has a field for each question name (must match). In the case of checkbox questions it must have one field for every answer option (again names must match).
- Text not required by a page/question such as a header or hint text should be passed as an empty string - Text not required by a page/question such as a header or hint text should be passed as an empty string
- For conditionally shown questions conditions that have been implemented and can be used are: - For conditionally shown questions, conditions that have been implemented and can be used are:
- Radio question answer option selected matches one of conditional e.g. ["answer-options-1-string", "answer-option-3-string"] - Radio question answer option selected matches one of conditional e.g. ["answer-options-1-string", "answer-option-3-string"]
- Numeric question value matches condition e.g. [">2"], ["<7"] or ["== 6"] - Numeric question value matches condition e.g. [">2"], ["<7"] or ["== 6"]
- When the top level question is a radio button and the conditional question is a numeric, text or date field then the conditional question is shown inline
- When the conditional question is a radio, checkbox or select field it should be displayed on it's own page and "depends_on" should be used rather than "conditional_for"
Page routing: Page routing:

7
app/helpers/conditional_questions_helper.rb

@ -3,6 +3,13 @@ module ConditionalQuestionsHelper
page.questions.map(&:conditional_for).compact.map(&:keys).flatten page.questions.map(&:conditional_for).compact.map(&:keys).flatten
end end
def find_conditional_question(page, question, answer_value)
return if question.conditional_for.nil?
conditional_key = question.conditional_for.find { |_, conditional_value| conditional_value.include? answer_value }&.first
page.questions.find { |q| q.id == conditional_key }
end
def display_question_key_div(page, question) def display_question_key_div(page, question)
"style='display:none;'".html_safe if conditional_questions_for_page(page).include?(question.id) || question.requires_js "style='display:none;'".html_safe if conditional_questions_for_page(page).include?(question.id) || question.requires_js
end end

25
app/helpers/question_view_helper.rb

@ -0,0 +1,25 @@
module QuestionViewHelper
def caption(caption_text, page_header, conditional)
return nil unless caption_text && page_header.blank? && !conditional
{ text: caption_text.html_safe, size: "l" }
end
def legend(question, page_header, conditional)
{
text: question.header.html_safe,
size: label_size(page_header, conditional),
tag: label_tag(page_header, conditional),
}
end
private
def label_size(page_header, conditional)
page_header.blank? && !conditional ? "l" : "m"
end
def label_tag(page_header, conditional)
page_header.blank? && !conditional ? "h1" : "h2"
end
end

15
app/models/form/page.rb

@ -27,8 +27,23 @@ class Form::Page
subsection.enabled?(case_log) && depends_on_met(case_log) subsection.enabled?(case_log) && depends_on_met(case_log)
end end
def non_conditional_questions
@non_conditional_questions ||= questions.reject do |q|
conditional_question_ids.include?(q.id)
end
end
private private
def conditional_question_ids
@conditional_question_ids ||= questions.flat_map { |q|
next if q.conditional_for.blank?
# TODO: remove this condition once all conditional questions no longer need JS
q.conditional_for.keys if q.type == "radio"
}.compact
end
def depends_on_met(case_log) def depends_on_met(case_log)
return true unless depends_on return true unless depends_on

2
app/models/form/question.rb

@ -35,7 +35,7 @@ class Form::Question
def answer_label(case_log) def answer_label(case_log)
return checkbox_answer_label(case_log) if type == "checkbox" return checkbox_answer_label(case_log) if type == "checkbox"
return case_log[id].to_formatted_s(:govuk_date) if type == "date" return case_log[id]&.to_formatted_s(:govuk_date).to_s if type == "date"
return case_log[id].to_s if case_log[id].present? return case_log[id].to_s if case_log[id].present?

12
app/models/validations/household_validations.rb

@ -4,10 +4,6 @@ module Validations::HouseholdValidations
def validate_reasonable_preference(record) def validate_reasonable_preference(record)
if record.homeless == "No" && record.reasonpref == "Yes" if record.homeless == "No" && record.reasonpref == "Yes"
record.errors.add :reasonpref, I18n.t("validations.household.reasonpref.not_homeless") record.errors.add :reasonpref, I18n.t("validations.household.reasonpref.not_homeless")
elsif record.reasonpref == "Yes"
if [record.rp_homeless, record.rp_insan_unsat, record.rp_medwel, record.rp_hardship, record.rp_dontknow].none? { |a| a == "Yes" }
record.errors.add :reasonable_preference_reason, I18n.t("validations.household.reasonable_preference_reason.reason_required")
end
elsif record.reasonpref == "No" elsif record.reasonpref == "No"
if [record.rp_homeless, record.rp_insan_unsat, record.rp_medwel, record.rp_hardship, record.rp_dontknow].any? { |a| a == "Yes" } if [record.rp_homeless, record.rp_insan_unsat, record.rp_medwel, record.rp_hardship, record.rp_dontknow].any? { |a| a == "Yes" }
record.errors.add :reasonable_preference_reason, I18n.t("validations.household.reasonable_preference_reason.reason_not_required") record.errors.add :reasonable_preference_reason, I18n.t("validations.household.reasonable_preference_reason.reason_not_required")
@ -26,20 +22,12 @@ module Validations::HouseholdValidations
end end
def validate_armed_forces_injured(record) def validate_armed_forces_injured(record)
if (record.armedforces == "A current or former regular in the UK Armed Forces (excluding National Service)" || record.armedforces == "A current or former reserve in the UK Armed Forces (excluding National Service)") && record.reservist.blank?
record.errors.add :reservist, I18n.t("validations.household.reservist.injury_required")
end
if (record.armedforces == "No" || record.armedforces == "Prefer not to say") && record.reservist.present? if (record.armedforces == "No" || record.armedforces == "Prefer not to say") && record.reservist.present?
record.errors.add :reservist, I18n.t("validations.household.reservist.injury_not_required") record.errors.add :reservist, I18n.t("validations.household.reservist.injury_not_required")
end end
end end
def validate_armed_forces_active_response(record) def validate_armed_forces_active_response(record)
if record.armedforces == "A current or former regular in the UK Armed Forces (excluding National Service)" && record.leftreg.blank?
record.errors.add :leftreg, I18n.t("validations.household.leftreg.question_required")
end
if record.armedforces != "A current or former regular in the UK Armed Forces (excluding National Service)" && record.leftreg.present? if record.armedforces != "A current or former regular in the UK Armed Forces (excluding National Service)" && record.leftreg.present?
record.errors.add :leftreg, I18n.t("validations.household.leftreg.question_not_required") record.errors.add :leftreg, I18n.t("validations.household.leftreg.question_not_required")
end end

4
app/views/form/_checkbox_question.html.erb

@ -1,8 +1,8 @@
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %> <%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %>
<%= f.govuk_check_boxes_fieldset question.id.to_sym, <%= f.govuk_check_boxes_fieldset question.id.to_sym,
caption: caption && !page_header.present? ? { text: caption.html_safe, size: "l" } : nil, caption: caption(caption_text, page_header, conditional),
legend: { text: question.header.html_safe, size: !page_header.present? ? "l" : "m", tag: !page_header.present? ? "h1" : "h2" }, legend: legend(question, page_header, conditional),
hint: { text: question.hint_text&.html_safe } do %> hint: { text: question.hint_text&.html_safe } do %>
<% question.answer_options.map do |key, val| %> <% question.answer_options.map do |key, val| %>

4
app/views/form/_date_question.html.erb

@ -1,8 +1,8 @@
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %> <%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %>
<%= f.govuk_date_field question.id.to_sym, <%= f.govuk_date_field question.id.to_sym,
caption: caption && !page_header.present? ? { text: caption.html_safe, size: "l" } : nil, caption: caption(caption_text, page_header, conditional),
legend: { text: question.header.html_safe, size: !page_header.present? ? "l" : "m", tag: !page_header.present? ? "h1" : "h2" }, legend: legend(question, page_header, conditional),
hint: { text: question.hint_text&.html_safe }, hint: { text: question.hint_text&.html_safe },
width: 20, width: 20,
**stimulus_html_attributes(question) **stimulus_html_attributes(question)

4
app/views/form/_numeric_question.html.erb

@ -1,8 +1,8 @@
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %> <%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %>
<%= f.govuk_number_field question.id.to_sym, <%= f.govuk_number_field question.id.to_sym,
caption: caption && !page_header.present? ? { text: caption.html_safe, size: "l" } : nil, caption: caption(caption_text, page_header, conditional),
label: { text: question.header.html_safe, size: !page_header.present? ? "l" : "m", tag: !page_header.present? ? "h1" : "h2" }, label: legend(question, page_header, conditional),
hint: { text: question.hint_text&.html_safe }, hint: { text: question.hint_text&.html_safe },
min: question.min, max: question.max, step: question.step, min: question.min, max: question.max, step: question.step,
width: question.width, :readonly => question.read_only?, width: question.width, :readonly => question.read_only?,

19
app/views/form/_radio_question.html.erb

@ -1,15 +1,28 @@
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %> <%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %>
<%= f.govuk_radio_buttons_fieldset question.id.to_sym, <%= f.govuk_radio_buttons_fieldset question.id.to_sym,
caption: caption && !page_header.present? ? { text: caption.html_safe, size: "l" } : nil, caption: caption(caption_text, page_header, conditional),
legend: { text: question.header.html_safe, size: !page_header.present? ? "l" : "m", tag: !page_header.present? ? "h1" : "h2" }, legend: legend(question, page_header, conditional),
hint: { text: question.hint_text&.html_safe } do %> hint: { text: question.hint_text&.html_safe } do %>
<% question.answer_options.map do |key, val| %> <% question.answer_options.map do |key, val| %>
<% if key.starts_with?("divider") %> <% if key.starts_with?("divider") %>
<%= f.govuk_radio_divider %> <%= f.govuk_radio_divider %>
<% else %> <% else %>
<%= f.govuk_radio_button question.id, val, label: { text: val }, **stimulus_html_attributes(question) %> <% conditional_question = find_conditional_question(@page, question, val) %>
<% if conditional_question.nil? %>
<%= f.govuk_radio_button question.id, val, label: { text: val }, **stimulus_html_attributes(question) %>
<% else %>
<%= f.govuk_radio_button question.id, val, label: { text: val }, **stimulus_html_attributes(question) do %>
<%= render partial: "#{conditional_question.type}_question", locals: {
question: conditional_question,
caption_text: caption_text,
page_header: page_header,
f: f,
conditional: true
} %>
<% end %>
<% end %>
<% end %> <% end %>
<% end %> <% end %>
<% end %> <% end %>

4
app/views/form/_select_question.html.erb

@ -6,8 +6,8 @@
answers, answers,
:id, :id,
:name, :name,
caption: caption && !page_header.present? ? { text: caption.html_safe, size: "l" } : nil, caption: caption(caption_text, page_header, conditional),
label: { text: question.header, size: !page_header.present? ? "l" : "m", tag: !page_header.present? ? "h1" : "h2" }, label: legend(question, page_header, conditional),
hint: { text: question.hint_text&.html_safe }, hint: { text: question.hint_text&.html_safe },
options: { disabled: [""], selected: selected }, options: { disabled: [""], selected: selected },
"data-controller": "accessible-autocomplete" "data-controller": "accessible-autocomplete"

4
app/views/form/_text_question.html.erb

@ -1,8 +1,8 @@
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %> <%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %>
<%= f.govuk_text_field question.id.to_sym, <%= f.govuk_text_field question.id.to_sym,
caption: caption && !page_header.present? ? { text: caption.html_safe, size: "l" } : nil, caption: caption(caption_text, page_header, conditional),
label: { text: question.header.html_safe, size: !page_header.present? ? "l" : "m", tag: !page_header.present? ? "h1" : "h2" }, label: legend(question, page_header, conditional),
hint: { text: question.hint_text&.html_safe }, hint: { text: question.hint_text&.html_safe },
width: question.width ? question.width : nil, width: question.width ? question.width : nil,
**stimulus_html_attributes(question) **stimulus_html_attributes(question)

4
app/views/form/_textarea_question.html.erb

@ -1,8 +1,8 @@
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %> <%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %>
<%= f.govuk_text_area question.id.to_sym, <%= f.govuk_text_area question.id.to_sym,
caption: caption && !page_header.present? ? { text: caption.html_safe, size: "l" } : nil, caption: caption(caption_text, page_header, conditional),
label: { text: question.header.html_safe, size: !page_header.present? ? "l" : "m", tag: !page_header.present? ? "h1" : "h2" }, label: legend(question, page_header, conditional),
hint: { text: question.hint_text&.html_safe }, hint: { text: question.hint_text&.html_safe },
width: question.width ? question.width : nil, width: question.width ? question.width : nil,
**stimulus_html_attributes(question) **stimulus_html_attributes(question)

6
app/views/form/page.html.erb

@ -11,6 +11,8 @@
) %> ) %>
<% end %> <% end %>
<div data-controller="govukfrontend"></div>
<%= turbo_frame_tag "case_log_form", target: "_top" do %> <%= turbo_frame_tag "case_log_form", target: "_top" do %>
<div class="govuk-grid-row"> <div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds-from-desktop"> <div class="govuk-grid-column-two-thirds-from-desktop">
@ -29,12 +31,12 @@
<%= form_with model: @case_log, url: form_case_log_path(@case_log), method: "post" do |f| %> <%= form_with model: @case_log, url: form_case_log_path(@case_log), method: "post" do |f| %>
<%= f.govuk_error_summary %> <%= f.govuk_error_summary %>
<% @page.questions.map do |question| %> <% @page.non_conditional_questions.map do |question| %>
<div id=<%= question.id + "_div " %><%= display_question_key_div(@page, question) %> > <div id=<%= question.id + "_div " %><%= display_question_key_div(@page, question) %> >
<% if question.read_only? %> <% if question.read_only? %>
<hr class="govuk-section-break govuk-section-break--visible govuk-section-break--m"> <hr class="govuk-section-break govuk-section-break--visible govuk-section-break--m">
<% end %> <% end %>
<%= render partial: "form/#{question.type}_question", locals: { question: question, caption: @subsection.label, page_header: @page.header, f: f } %> <%= render partial: "form/#{question.type}_question", locals: { question: question, caption_text: @subsection.label, page_header: @page.header, f: f, conditional: false } %>
</div> </div>
<% end %> <% end %>

30
app/webpacker/controllers/conditional_question_controller.js

@ -16,24 +16,30 @@ export default class extends Controller {
} }
} }
clearTextNumericInput(input) {
input.value = ""
}
clearDateInputs(inputs) {
inputs.forEach((input) => { input.value = "" })
}
displayConditionalRadio() { displayConditionalRadio() {
if(this.element.checked) { if(this.element.checked) {
let selectedValue = this.element.value let selectedValue = this.element.value
let conditional_for = JSON.parse(this.element.dataset.info) let conditional_for = JSON.parse(this.element.dataset.info)
Object.entries(conditional_for).map(([targetQuestion, conditions]) => { Object.entries(conditional_for).map(([targetQuestion, conditions]) => {
let div = document.getElementById(targetQuestion + "_div") if(conditions.includes(selectedValue)) {
if(conditions.includes(selectedValue)) { } else {
div.style.display = "block" const textNumericInput = document.getElementById(`case-log-${targetQuestion.replaceAll("_","-")}-field`)
} else { if (textNumericInput == null) {
div.style.display = "none" const dateInputs = [1,2,3].map((idx) => {
let buttons = document.getElementsByName(`case_log[${targetQuestion}]`); return document.getElementById(`case_log_${targetQuestion}_${idx}i`)
if (buttons.length == 0){ })
buttons = document.getElementsByName(`case_log[${targetQuestion}][]`); this.clearDateInputs(dateInputs)
} else {
this.clearTextNumericInput(textNumericInput)
} }
Object.entries(buttons).map(([idx, button]) => {
button.checked = false;
})
} }
}) })
} }

8
app/webpacker/controllers/govukfrontend_controller.js

@ -0,0 +1,8 @@
import { initAll } from "govuk-frontend";
import { Controller } from "@hotwired/stimulus";
export default class extends Controller {
connect() {
initAll()
}
}

8
app/webpacker/packs/application.js

@ -5,15 +5,11 @@
require.context('govuk-frontend/govuk/assets') require.context('govuk-frontend/govuk/assets')
import '../styles/application.scss' import '../styles/application.scss'
import Rails from "@rails/ujs"
import * as ActiveStorage from "@rails/activestorage" import * as ActiveStorage from "@rails/activestorage"
import "channels" import "channels"
import { initAll } from 'govuk-frontend' import "controllers"
import "@hotwired/turbo-rails" import "@hotwired/turbo-rails"
import { initAll } from 'govuk-frontend'
Rails.start()
ActiveStorage.start() ActiveStorage.start()
initAll() initAll()
import "controllers"

31
config/forms/2021_2022.json

@ -827,7 +827,7 @@
"type": "radio", "type": "radio",
"answer_options": { "answer_options": {
"0": "Yes - assessed as homeless by a local authority and owed a homelessness duty. Including if threatened with homelessness within 56 days", "0": "Yes - assessed as homeless by a local authority and owed a homelessness duty. Including if threatened with homelessness within 56 days",
"1": "Yes - other homelessness ", "1": "Yes - other homelessness",
"2": "No" "2": "No"
} }
} }
@ -924,16 +924,15 @@
"2": "A spouse / civil partner of a UK Armed Forces member who has separated or been bereaved within the last 2 years", "2": "A spouse / civil partner of a UK Armed Forces member who has separated or been bereaved within the last 2 years",
"3": "No", "3": "No",
"4": "Tenant prefers not to say" "4": "Tenant prefers not to say"
},
"conditional_for": {
"leftreg": [
"A current or former regular in the UK Armed Forces (excluding National Service)"
],
"reservist": [
"A current or former regular in the UK Armed Forces (excluding National Service)"
]
} }
}, }
}
},
"armed_forces_member": {
"header": "Experience of the UK Armed Forces",
"description": "",
"depends_on": [{ "armedforces": "A current or former regular in the UK Armed Forces (excluding National Service)" }],
"questions": {
"leftreg": { "leftreg": {
"header": "Are they still serving?", "header": "Are they still serving?",
"hint_text": "", "hint_text": "",
@ -3186,11 +3185,15 @@
"1": "No", "1": "No",
"divider": true, "divider": true,
"2": "Don’t know" "2": "Don’t know"
},
"conditional_for": {
"reasonable_preference_reason": ["Yes"]
} }
}, }
}
},
"reasonable_preference_reason": {
"header": "Reason for reasonable preference being given",
"description": "",
"depends_on": [{ "reasonpref": "Yes" }],
"questions": {
"reasonable_preference_reason": { "reasonable_preference_reason": {
"check_answer_label": "Reason for reasonable preference", "check_answer_label": "Reason for reasonable preference",
"header": "Why were they given reasonable preference?", "header": "Why were they given reasonable preference?",

1
package.json

@ -12,7 +12,6 @@
"@hotwired/turbo-rails": "^7.1.0", "@hotwired/turbo-rails": "^7.1.0",
"@rails/actioncable": "^6.0.0", "@rails/actioncable": "^6.0.0",
"@rails/activestorage": "^6.0.0", "@rails/activestorage": "^6.0.0",
"@rails/ujs": "^6.0.0",
"@rails/webpacker": "5.4.0", "@rails/webpacker": "5.4.0",
"accessible-autocomplete": "^2.0.3", "accessible-autocomplete": "^2.0.3",
"chart.js": "^3.6.0", "chart.js": "^3.6.0",

8
spec/features/form/conditional_questions_spec.rb

@ -30,13 +30,11 @@ RSpec.describe "Form Conditional Questions" do
visit("/logs/#{id}/armed-forces") visit("/logs/#{id}/armed-forces")
# Something about our styling makes the selenium webdriver think the actual radio buttons are not visible so we allow label click here # Something about our styling makes the selenium webdriver think the actual radio buttons are not visible so we allow label click here
choose("case-log-armedforces-a-current-or-former-regular-in-the-uk-armed-forces-excluding-national-service-field", allow_label_click: true) choose("case-log-armedforces-a-current-or-former-regular-in-the-uk-armed-forces-excluding-national-service-field", allow_label_click: true)
expect(page).to have_selector("#reservist_div") fill_in("case-log-leftreg-field", with: "text")
choose("case-log-reservist-no-field", allow_label_click: true)
expect(page).to have_checked_field("case-log-reservist-no-field", visible: false)
choose("case-log-armedforces-no-field", allow_label_click: true) choose("case-log-armedforces-no-field", allow_label_click: true)
expect(page).not_to have_selector("#reservist_div") expect(page).not_to have_field("case-log-leftreg-field")
choose("case-log-armedforces-a-current-or-former-regular-in-the-uk-armed-forces-excluding-national-service-field", allow_label_click: true) choose("case-log-armedforces-a-current-or-former-regular-in-the-uk-armed-forces-excluding-national-service-field", allow_label_click: true)
expect(page).to have_unchecked_field("case-log-reservist-no-field", visible: false) expect(page).to have_field("case-log-leftreg-field", with: "")
end end
end end
end end

24
spec/fixtures/forms/2021_2022.json vendored

@ -124,32 +124,14 @@
"4": "Tenant prefers not to say" "4": "Tenant prefers not to say"
}, },
"conditional_for": { "conditional_for": {
"leftreg": ["A current or former regular in the UK Armed Forces (excluding National Service)"], "leftreg": ["A current or former regular in the UK Armed Forces (excluding National Service)"]
"reservist": ["A current or former regular in the UK Armed Forces (excluding National Service)"]
} }
}, },
"leftreg": { "leftreg": {
"header": "Are they still serving?", "header": "Are they still serving?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "text",
"check_answer_label": "When did they leave the Armed Forces?", "check_answer_label": "When did they leave the Armed Forces?"
"answer_options": {
"0": "Yes",
"1": "No - they left up to 5 years ago",
"2": "No - they left more than 5 years ago",
"3": "Prefer not to say"
}
},
"reservist": {
"header": "Were they seriously injured or ill as a result of their service?",
"hint_text": "",
"type": "radio",
"check_answer_label": "Has anyone in the household been seriously injured or ill as a result of their service in the armed forces?",
"answer_options": {
"0": "Yes",
"1": "No",
"2": "Prefer not to say"
}
} }
} }
}, },

12
spec/helpers/conditional_questions_helper_spec.rb

@ -5,15 +5,23 @@ RSpec.describe ConditionalQuestionsHelper do
let(:page) { case_log.form.get_page("armed_forces") } let(:page) { case_log.form.get_page("armed_forces") }
describe "conditional questions for page" do describe "conditional questions for page" do
let(:conditional_pages) { %w[leftreg reservist] } let(:conditional_pages) { %w[leftreg] }
it "returns the question keys of all conditional questions on the given page" do it "returns the question keys of all conditional questions on the given page" do
expect(conditional_questions_for_page(page)).to eq(conditional_pages) expect(conditional_questions_for_page(page)).to eq(conditional_pages)
end end
end end
describe "find conditional question" do
let(:question) { page.questions.find { |q| q.id == "armedforces" } }
let(:answer_value) { "A current or former regular in the UK Armed Forces (excluding National Service)" }
it "returns the conditional question for a given answer option" do
expect(find_conditional_question(page, question, answer_value))
end
end
describe "display question key div" do describe "display question key div" do
let(:conditional_question) { page.questions.find { |q| q.id == "reservist" } } let(:conditional_question) { page.questions.find { |q| q.id == "leftreg" } }
it "returns a non visible div for conditional questions" do it "returns a non visible div for conditional questions" do
expect(display_question_key_div(page, conditional_question)).to match("style='display:none;'") expect(display_question_key_div(page, conditional_question)).to match("style='display:none;'")

75
spec/helpers/question_view_helper.rb

@ -0,0 +1,75 @@
require "rails_helper"
RSpec.describe QuestionViewHelper do
let(:page_header) { "Some Page Header" }
let(:conditional) { false }
describe "caption" do
let(:subject) { caption(caption_text, page_header, conditional) }
let(:caption_text) { "Some text" }
let(:caption_options_hash) { { text: caption_text.html_safe, size: "l" } }
context "a page without a header" do
let(:page_header) { nil }
it "returns an options hash" do
expect(subject).to eq(caption_options_hash)
end
end
context "a page with a header" do
it "returns nil" do
expect(subject).to be_nil
end
end
context "a conditional question" do
let(:conditional) { true }
it "returns nil" do
expect(subject).to be_nil
end
end
context "a question without a caption" do
let(:caption_text) { nil }
it "returns nil" do
expect(subject).to be_nil
end
end
end
describe "legend" do
let(:question) { OpenStruct.new(header: "Some question header") }
let(:subject) { legend(question, page_header, conditional) }
let(:size) { "m" }
let(:tag) { "h2" }
let(:legend_options_hash) do
{ text: "Some question header".html_safe, size: size, tag: tag }
end
context "a page with a header" do
it "returns an options hash with a medium question header" do
expect(subject).to eq(legend_options_hash)
end
end
context "a page without a header" do
let(:page_header) { nil }
let(:size) { "l" }
let(:tag) { "h1" }
it "returns an options hash with a large question header" do
expect(subject).to eq(legend_options_hash)
end
end
context "a conditional question" do
let(:conditional) { true }
it "returns an options hash with a medium question header" do
expect(subject).to eq(legend_options_hash)
end
end
end
end

33
spec/models/case_log_spec.rb

@ -73,19 +73,6 @@ RSpec.describe Form, type: :model do
end end
context "reasonable preference is yes" do context "reasonable preference is yes" do
it "validates a reason must be selected" do
expect {
CaseLog.create!(reasonpref: "Yes",
rp_homeless: nil,
rp_insan_unsat: nil,
rp_medwel: nil,
rp_hardship: nil,
rp_dontknow: nil,
owning_organisation: owning_organisation,
managing_organisation: managing_organisation)
}.to raise_error(ActiveRecord::RecordInvalid)
end
it "validates that previously homeless should be selected" do it "validates that previously homeless should be selected" do
expect { expect {
CaseLog.create!( CaseLog.create!(
@ -153,16 +140,7 @@ RSpec.describe Form, type: :model do
end end
context "armed forces injured validation" do context "armed forces injured validation" do
it "must be answered if tenant was a regular or reserve in armed forces" do it "must not be answered if tenant was not a regular or reserve in armed forces" do
expect {
CaseLog.create!(armedforces: "A current or former regular in the UK Armed Forces (excluding National Service)",
reservist: nil,
owning_organisation: owning_organisation,
managing_organisation: managing_organisation)
}.to raise_error(ActiveRecord::RecordInvalid)
end
it "must be answered if tenant was not a regular or reserve in armed forces" do
expect { expect {
CaseLog.create!(armedforces: "No", CaseLog.create!(armedforces: "No",
reservist: "Yes", reservist: "Yes",
@ -466,15 +444,6 @@ RSpec.describe Form, type: :model do
end end
context "armed forces active validation" do context "armed forces active validation" do
it "must be answered if ever served in the forces as a regular" do
expect {
CaseLog.create!(armedforces: "A current or former regular in the UK Armed Forces (excluding National Service)",
leftreg: nil,
owning_organisation: owning_organisation,
managing_organisation: managing_organisation)
}.to raise_error(ActiveRecord::RecordInvalid)
end
it "must not be answered if not ever served as a regular" do it "must not be answered if not ever served as a regular" do
expect { expect {
CaseLog.create!(armedforces: "No", CaseLog.create!(armedforces: "No",

10
spec/models/form/page_spec.rb

@ -44,6 +44,16 @@ RSpec.describe Form::Page, type: :model do
expect(subject.expected_responses.map(&:id)).to eq(expected_responses) expect(subject.expected_responses.map(&:id)).to eq(expected_responses)
end end
context "page with conditional questions" do
let(:page_id) { "housing_benefit" }
it "knows which questions are not conditional" do
expected_non_conditional_questions = %w[hb]
expect(subject.non_conditional_questions.map(&:id))
.to eq(expected_non_conditional_questions)
end
end
context "for a given case log" do context "for a given case log" do
let(:case_log) { FactoryBot.build(:case_log, :in_progress) } let(:case_log) { FactoryBot.build(:case_log, :in_progress) }

5
spec/models/form/question_spec.rb

@ -120,6 +120,11 @@ RSpec.describe Form::Question, type: :model do
case_log.mrcdate = Time.zone.local(2021, 10, 11) case_log.mrcdate = Time.zone.local(2021, 10, 11)
expect(subject.answer_label(case_log)).to eq("11 October 2021") expect(subject.answer_label(case_log)).to eq("11 October 2021")
end end
it "can handle nils" do
case_log.mrcdate = nil
expect(subject.answer_label(case_log)).to eq("")
end
end end
context "when type is checkbox" do context "when type is checkbox" do

5
yarn.lock

@ -970,11 +970,6 @@
dependencies: dependencies:
spark-md5 "^3.0.0" spark-md5 "^3.0.0"
"@rails/ujs@^6.0.0":
version "6.1.4"
resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-6.1.4.tgz#093d5341595a02089ed309dec40f3c37da7b1b10"
integrity sha512-O3lEzL5DYbxppMdsFSw36e4BHIlfz/xusynwXGv3l2lhSlvah41qviRpsoAlKXxl37nZAqK+UUF5cnGGK45Mfw==
"@rails/webpacker@5.4.0": "@rails/webpacker@5.4.0":
version "5.4.0" version "5.4.0"
resolved "https://registry.yarnpkg.com/@rails/webpacker/-/webpacker-5.4.0.tgz#2c64a9ea7e85d2a33e50e86319fe6751df0c47e8" resolved "https://registry.yarnpkg.com/@rails/webpacker/-/webpacker-5.4.0.tgz#2c64a9ea7e85d2a33e50e86319fe6751df0c47e8"

Loading…
Cancel
Save