diff --git a/app/models/form/page.rb b/app/models/form/page.rb index 1a300f081..a2bac3bf2 100644 --- a/app/models/form/page.rb +++ b/app/models/form/page.rb @@ -46,10 +46,16 @@ private depends_on.any? do |conditions_set| conditions_set.all? do |question, value| - parts = question.split(".") - case_log_value = send_chain(parts, case_log) - - value.nil? ? case_log_value == value : !case_log_value.nil? && case_log_value == value + if value.is_a?(Hash) && value.key?("operator") + operator = value["operator"] + operand = value["operand"] + case_log[question]&.send(operator, operand) + else + parts = question.split(".") + case_log_value = send_chain(parts, case_log) + + value.nil? ? case_log_value == value : !case_log_value.nil? && case_log_value == value + end end end end diff --git a/app/models/form/question.rb b/app/models/form/question.rb index 03210ba0f..30b0ad160 100644 --- a/app/models/form/question.rb +++ b/app/models/form/question.rb @@ -60,7 +60,7 @@ class Form::Question def enabled?(case_log) return true if conditional_on.blank? - conditional_on.map { |condition| evaluate_condition(condition, case_log) }.all? + conditional_on.all? { |condition| evaluate_condition(condition, case_log) } end def hidden_in_check_answers? @@ -73,6 +73,12 @@ class Form::Question false end + def displayed_answer_options + answer_options.select do |_key, val| + !val.is_a?(Hash) || !val["derived"] + end + end + def update_answer_link_name(case_log) link_type = if has_inferred_check_answers_value?(case_log) "Change" diff --git a/app/views/form/_checkbox_question.html.erb b/app/views/form/_checkbox_question.html.erb index f39200e4b..3c1bb0da9 100644 --- a/app/views/form/_checkbox_question.html.erb +++ b/app/views/form/_checkbox_question.html.erb @@ -6,7 +6,7 @@ hint: { text: question.hint_text&.html_safe } do %> <% after_divider = false %> - <% question.answer_options.map do |key, options| %> + <% question.displayed_answer_options.map do |key, options| %> <% if key.starts_with?("divider") %> <% after_divider = true %> <%= f.govuk_check_box_divider %> diff --git a/app/views/form/_radio_question.html.erb b/app/views/form/_radio_question.html.erb index b5e5526f2..12a8052b1 100644 --- a/app/views/form/_radio_question.html.erb +++ b/app/views/form/_radio_question.html.erb @@ -5,7 +5,7 @@ legend: legend(question, page_header, conditional), hint: { text: question.hint_text&.html_safe } do %> - <% question.answer_options.map do |key, options| %> + <% question.displayed_answer_options.map do |key, options| %> <% if key.starts_with?("divider") %> <%= f.govuk_radio_divider %> <% else %> diff --git a/app/views/form/_select_question.html.erb b/app/views/form/_select_question.html.erb index bcc588ba5..88a080bda 100644 --- a/app/views/form/_select_question.html.erb +++ b/app/views/form/_select_question.html.erb @@ -1,7 +1,7 @@ <%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %> <% selected = @case_log.public_send(question.id) || "" %> -<%= answers = question.answer_options.map { |key, value| OpenStruct.new(id: key, name: value) } +<%= answers = question.displayed_answer_options.map { |key, value| OpenStruct.new(id: key, name: value) } f.govuk_collection_select question.id.to_sym, answers, :id, diff --git a/config/forms/2021_2022.json b/config/forms/2021_2022.json index a69b5916a..0090afa1e 100644 --- a/config/forms/2021_2022.json +++ b/config/forms/2021_2022.json @@ -1731,7 +1731,8 @@ "value": "Retired" }, "9": { - "value": "Child under 16" + "value": "Child under 16", + "derived": true }, "0": { "value": "Other" @@ -1747,7 +1748,8 @@ }, "depends_on": [ { - "details_known_2": 0 + "details_known_2": 0, + "age2": { "operator": ">", "operand": 15 } } ] }, @@ -1939,7 +1941,8 @@ "value": "Retired" }, "9": { - "value": "Child under 16" + "value": "Child under 16", + "derived": true }, "0": { "value": "Other" @@ -1955,7 +1958,8 @@ }, "depends_on": [ { - "details_known_3": 0 + "details_known_3": 0, + "age3": { "operator": ">", "operand": 15 } } ] }, @@ -2144,7 +2148,8 @@ "value": "Retired" }, "9": { - "value": "Child under 16" + "value": "Child under 16", + "derived": true }, "0": { "value": "Other" @@ -2160,7 +2165,8 @@ }, "depends_on": [ { - "details_known_4": 0 + "details_known_4": 0, + "age4": { "operator": ">", "operand": 15 } } ] }, @@ -2346,7 +2352,8 @@ "value": "Retired" }, "9": { - "value": "Child under 16" + "value": "Child under 16", + "derived": true }, "0": { "value": "Other" @@ -2362,7 +2369,8 @@ }, "depends_on": [ { - "details_known_5": 0 + "details_known_5": 0, + "age5": { "operator": ">", "operand": 15 } } ] }, @@ -2545,7 +2553,8 @@ "value": "Retired" }, "9": { - "value": "Child under 16" + "value": "Child under 16", + "derived": true }, "0": { "value": "Other" @@ -2561,7 +2570,8 @@ }, "depends_on": [ { - "details_known_6": 0 + "details_known_6": 0, + "age6": { "operator": ">", "operand": 15 } } ] }, @@ -2741,7 +2751,8 @@ "value": "Retired" }, "9": { - "value": "Child under 16" + "value": "Child under 16", + "derived": true }, "0": { "value": "Other" @@ -2757,7 +2768,8 @@ }, "depends_on": [ { - "details_known_7": 0 + "details_known_7": 0, + "age7": { "operator": ">", "operand": 15 } } ] }, @@ -2934,7 +2946,8 @@ "value": "Retired" }, "9": { - "value": "Child under 16" + "value": "Child under 16", + "derived": true }, "0": { "value": "Other" @@ -2950,7 +2963,8 @@ }, "depends_on": [ { - "details_known_8": 0 + "details_known_8": 0, + "age8": { "operator": ">", "operand": 15 } } ] } diff --git a/spec/factories/case_log.rb b/spec/factories/case_log.rb index e869d8e7f..1aa462667 100644 --- a/spec/factories/case_log.rb +++ b/spec/factories/case_log.rb @@ -15,6 +15,7 @@ FactoryBot.define do postcode_full { "PO5 3TE" } ppostcode_full { "SW2 6HI" } age1 { 17 } + age2 { 19 } end trait :soft_validations_triggered do status { 1 } diff --git a/spec/features/form/check_answers_page_spec.rb b/spec/features/form/check_answers_page_spec.rb index 34125ee41..6a06991cc 100644 --- a/spec/features/form/check_answers_page_spec.rb +++ b/spec/features/form/check_answers_page_spec.rb @@ -72,7 +72,7 @@ RSpec.describe "Form Check Answers Page" do # This way only the links in the table will get picked up it "has an answer link for questions missing an answer" do visit("/logs/#{empty_case_log.id}/#{subsection}/check-answers?referrer=check_answers") - assert_selector "a", text: /Answer (?!the missing questions)/, count: 5 + assert_selector "a", text: /Answer (?!the missing questions)/, count: 4 assert_selector "a", text: "Change", count: 0 expect(page).to have_link("Answer", href: "/logs/#{empty_case_log.id}/person-1-age?referrer=check_answers") end @@ -80,7 +80,7 @@ RSpec.describe "Form Check Answers Page" do it "has a change link for answered questions" do fill_in_number_question(empty_case_log.id, "age1", 28, "person-1-age") visit("/logs/#{empty_case_log.id}/#{subsection}/check-answers") - assert_selector "a", text: /Answer (?!the missing questions)/, count: 4 + assert_selector "a", text: /Answer (?!the missing questions)/, count: 3 assert_selector "a", text: "Change", count: 1 expect(page).to have_link("Change", href: "/logs/#{empty_case_log.id}/person-1-age?referrer=check_answers") end diff --git a/spec/fixtures/forms/2021_2022.json b/spec/fixtures/forms/2021_2022.json index c61582c2f..425b774a3 100644 --- a/spec/fixtures/forms/2021_2022.json +++ b/spec/fixtures/forms/2021_2022.json @@ -181,12 +181,21 @@ "0": { "value": "Other" }, + "9": { + "value": "Child under 16", + "derived": true + }, "1": { "value": "Prefer not to say" } } } - } + }, + "depends_on": [ + { + "age2": { "operator": ">", "operand": 15 } + } + ] }, "propcode": { "questions": { diff --git a/spec/models/case_log_spec.rb b/spec/models/case_log_spec.rb index 0c751be4e..63051a7a0 100644 --- a/spec/models/case_log_spec.rb +++ b/spec/models/case_log_spec.rb @@ -1339,21 +1339,21 @@ RSpec.describe CaseLog do context "when validating household members derived vars" do let!(:household_case_log) do - described_class.create({ + described_class.create!({ managing_organisation: organisation, owning_organisation: organisation, other_hhmemb: 4, - relat2: "C", + relat2: "X", relat3: "C", relat4: "X", relat5: "C", - relat7: "X", + relat7: "C", relat8: "X", age1: 22, - age2: 14, + age2: 16, age4: 60, age6: 88, - age7: 16, + age7: 14, age8: 42, }) end diff --git a/spec/models/form/page_spec.rb b/spec/models/form/page_spec.rb index 1b8a5189b..54f8a38ca 100644 --- a/spec/models/form/page_spec.rb +++ b/spec/models/form/page_spec.rb @@ -55,12 +55,28 @@ RSpec.describe Form::Page, type: :model do expect(page.routed_to?(case_log)).to be false end - it "evaluates not conditions correctly" do + it "evaluates met conditions correctly" do case_log.incfreq = "Weekly" expect(page.routed_to?(case_log)).to be true end end + context "with expression routing conditions" do + let(:section_id) { "household" } + let(:subsection_id) { "household_characteristics" } + let(:page_id) { "household_number_of_other_members" } + + it "evaluates not met conditions correctly" do + case_log.age2 = 12 + expect(page.routed_to?(case_log)).to be false + end + + it "evaluates met conditions correctly" do + case_log.age2 = 17 + expect(page.routed_to?(case_log)).to be true + end + end + context "when the page's subsection has routing conditions" do let(:section_id) { "submission" } let(:subsection_id) { "declaration" } diff --git a/spec/models/form/question_spec.rb b/spec/models/form/question_spec.rb index 2f8535d14..a8d0429f8 100644 --- a/spec/models/form/question_spec.rb +++ b/spec/models/form/question_spec.rb @@ -116,6 +116,26 @@ RSpec.describe Form::Question, type: :model do end end + context "when answer options do not include derived options" do + it "displays all answer options" do + expect(question.displayed_answer_options).to match(question.answer_options) + end + end + + context "when answer options include derived options" do + let(:section_id) { "household" } + let(:subsection_id) { "household_characteristics" } + let(:page_id) { "household_number_of_other_members" } + let(:question_id) { "ecstat2" } + let(:expected_answer_options) do + { "0" => { "value" => "Other" }, "1" => { "value" => "Prefer not to say" } } + end + + it "does not include those options in the displayed options" do + expect(question.displayed_answer_options).to match(expected_answer_options) + end + end + context "when the saved answer is not in the value map" do it "displays the saved answer umapped" do expect(question.label_from_value(9999)).to eq("9999")