diff --git a/Gemfile b/Gemfile index 8308b7274..ed2af8973 100644 --- a/Gemfile +++ b/Gemfile @@ -29,6 +29,8 @@ gem "discard" gem "activeadmin" # Admin charts gem "chartkick" +#Json Schema +gem "json-schema" gem "uk_postcode" group :development, :test do diff --git a/Gemfile.lock b/Gemfile.lock index c7ce5acfe..d2a3b4a15 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -196,6 +196,8 @@ GEM rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) + json-schema (2.8.1) + addressable (>= 2.4) kaminari (1.2.1) activesupport (>= 4.1.0) kaminari-actionview (= 1.2.1) @@ -399,6 +401,7 @@ DEPENDENCIES govuk_design_system_formbuilder hotwire-rails jbuilder (~> 2.7) + json-schema listen (~> 3.3) overcommit (>= 0.37.0) pg (~> 1.1) diff --git a/README.md b/README.md index 5af61d33e..b637e606e 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,7 @@ The JSON should follow the structure: "[snake_case_question_name_string]": { "header": String, "hint_text": String, + "check_answer_label": String, "type": "text" / "numeric" / "radio" / "checkbox" / "date", "min": Integer, // numeric only "max": Integer, // numeric only @@ -133,6 +134,10 @@ The JSON should follow the structure: "[snake_case_question_to_enable_2_name_string]": ["condition-that-enables"] } } + }, + "conditional_route_to": { + "[page_name_to_route_to]": {"question_name": "expected_answer"}, + "[page_name_to_route_to]": {"question_name": "expected_answer"} } } } @@ -155,6 +160,16 @@ Assumptions made by the format: - 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"] +## JSON Form Validation against Schema + +To validate the form JSON against the schema you can run: +`rake form_definition:validate` + +This will validate all forms in: +directories = ["config/forms", "spec/fixtures/forms"] + +against the schema in (config/forms/schema/generic.json) + ## Useful documentation (external dependencies) ### GOV.UK Design System Form Builder for Rails diff --git a/config/forms/schema/2021_2022.json b/config/forms/schema/2021_2022.json new file mode 100644 index 000000000..cd8f13530 --- /dev/null +++ b/config/forms/schema/2021_2022.json @@ -0,0 +1,112 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "$id": "https://example.com/product.schema.json", + "title": "Form", + "description": "A form", + "type": "object", + "required": ["form_type", "start_year", "end_year", "sections"], + "properties": { + "form_type": { + "description": "", + "type": "string" + }, + "start_year": { + "description": "", + "type": "integer" + }, + "end_year": { + "description": "", + "type": "integer" + }, + "sections": { + "type": "object", + "patternProperties": { + "[a-z_]+": { + "description": "", + "type": "object", + "properties": { + "label": { + "description": "", + "type": "string" + }, + "subsections": { + "type": "object", + "patternProperties": { + "[a-z_]+": { + "description": "", + "type": "object", + "required": ["label"], + "properties": { + "label": { + "description": "", + "type": "string" + }, + "pages": { + "type": "object", + "patternProperties": { + "[a-z_]+": { + "description": "", + "type": "object", + "properties": { + "header": { + "description": "", + "type": "string" + }, + "description": { + "description": "", + "type": "string" + }, + "questions": { + "type": "object", + "patternProperties": { + "[a-z_]+": { + "description": "", + "type": "object", + "required": ["header", "check_answer_label"], + "properties": { + "check_answer_label": { + "description": "", + "type": "string" + }, + "header": { + "description": "", + "type": "string" + }, + "type": { + "description": "", + "type": "string" + }, + "hint_text": { + "description": "", + "type": "string" + }, + "answer_options": { + "description": "", + "type": "object" + }, + "conditional_for": { + "description": "", + "type": "object" + } + } + } + } + } + }, + "minProperties": 1 + } + } + } + }, + "minProperties": 1 + } + } + } + }, + "minProperties": 2 + } + }, + "minProperties": 1 + } + } +} \ No newline at end of file diff --git a/config/forms/schema/generic.json b/config/forms/schema/generic.json new file mode 100644 index 000000000..1499878f0 --- /dev/null +++ b/config/forms/schema/generic.json @@ -0,0 +1,129 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "$id": "https://example.com/product.schema.json", + "title": "Form", + "description": "A form", + "type": "object", + "required": ["form_type", "start_year", "end_year", "sections"], + "properties": { + "form_type": { + "description": "", + "type": "string" + }, + "start_year": { + "description": "", + "type": "integer" + }, + "end_year": { + "description": "", + "type": "integer" + }, + "sections": { + "type": "object", + "patternProperties": { + "[a-z_]+": { + "description": "Section Name", + "type": "object", + "properties": { + "label": { + "description": "", + "type": "string" + }, + "subsections": { + "type": "object", + "patternProperties": { + "[a-z_]+": { + "description": "SubSection Name", + "type": "object", + "required": ["label"], + "properties": { + "label": { + "description": "", + "type": "string" + }, + "pages": { + "type": "object", + "patternProperties": { + "^(?!(conditional_route_to))[a-z_]+$": { + "description": "Page Name", + "type": "object", + "required": ["header", "questions"], + "properties": { + "header": { + "description": "", + "type": "string" + }, + "description": { + "description": "", + "type": "string" + }, + "questions": { + "type": "object", + "patternProperties": { + "[a-z_]+": { + "description": "Question Name", + "type": "object", + "required": ["header", "type"], + "properties": { + "header": { + "description": "", + "type": "string" + }, + "type": { + "description": "", + "type": "string" + }, + "check_answer_label": { + "description": "", + "type": "string", + "optional": "true" + } + }, + "additionalProperties": { + "hint_text": { + "optional": "true", + "description": "", + "type": "string" + }, + "answer_options": { + "optional": "true", + "description": "", + "type": "object" + }, + "check_answer_label": { + "description": "", + "type": "string" + }, + "conditional_for": { + "description": "", + "type": "object" + } + }, + "minProperties": 1 + } + } + } + }, + "additionalProperties": { + "conditional_route_to": { + "description": "", + "type": "object" + } + }, + "minProperties": 1 + } + } + } + }, + "minProperties": 1 + } + } + } + }, + "minProperties": 2 + } + }, + "minProperties": 1 + } + } +} \ No newline at end of file diff --git a/db/schema.rb b/db/schema.rb index 614c895c2..1ef990508 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -122,8 +122,6 @@ ActiveRecord::Schema.define(version: 2021_11_12_105348) do t.integer "rp_dontknow" t.datetime "discarded_at" t.string "tenancyother" - t.integer "override_net_income_validation" - t.string "net_income_known" t.string "gdpr_acceptance" t.string "gdpr_declined" t.string "property_owner_organisation" @@ -135,6 +133,8 @@ ActiveRecord::Schema.define(version: 2021_11_12_105348) do t.string "needs_type" t.string "sale_completion_date" t.string "purchaser_code" + t.integer "override_net_income_validation" + t.string "net_income_known" t.integer "reason" t.string "propcode" t.integer "majorrepairs" diff --git a/lib/tasks/form_definition.rake b/lib/tasks/form_definition.rake new file mode 100644 index 000000000..565728064 --- /dev/null +++ b/lib/tasks/form_definition.rake @@ -0,0 +1,50 @@ +require "json" +require "json-schema" + +def get_all_form_paths(directories) + form_paths = [] + directories.each do |directory| + Dir.glob("#{directory}/*.json").each do |form_path| + form_paths.push(form_path) + end + end + form_paths +end + +namespace :form_definition do + desc "Validate JSON against Generic Form Schema" + task :validate do + puts "#{Rails.root}" + path = "config/forms/schema/generic.json" + + file = File.open(path) + schema = JSON.parse(file.read) + metaschema = JSON::Validator.validator_for_name("draft4").metaschema + + puts path + + if JSON::Validator.validate(metaschema, schema) + puts "schema valid" + else + puts "schema not valid" + return + end + + directories = ["config/forms", "spec/fixtures/forms"] + # directories = ["config/forms"] + + get_all_form_paths(directories).each do |path| + puts path + file = File.open(path) + data = JSON.parse(file.read) + + puts JSON::Validator.fully_validate(schema, data, :strict => true) + + begin + JSON::Validator.validate!(schema, data) + rescue JSON::Schema::ValidationError => e + e.message + end + end + end + end \ No newline at end of file diff --git a/spec/fixtures/forms/test_form.json b/spec/fixtures/forms/test_form.json index 988d2176e..28feb44b9 100644 --- a/spec/fixtures/forms/test_form.json +++ b/spec/fixtures/forms/test_form.json @@ -1,542 +1,543 @@ { - "form_type": "lettings", - "sections": { - "household": { - "label": "About the household", - "subsections": { - "household_characteristics": { - "label": "Household characteristics", - "pages": { - "tenant_code": { - "questions": { - "tenant_code": { - "check_answer_label": "Tenant code", - "header": "What is the tenant code?", - "type": "text" - } - } - }, - "person_1_age": { - "questions": { - "age1": { - "check_answer_label": "Tenant's age", - "header": "What is the tenant's age?", - "type": "numeric", - "min": 0, - "max": 150, - "step": 1 - } - } - }, - "person_1_gender": { - "questions": { - "sex1": { - "check_answer_label": "Tenant's gender", - "header": "Which of these best describes the tenant's gender identity?", - "type": "radio", - "answer_options": { - "0": "Female", - "1": "Male", - "2": "Non-binary", - "3": "Prefer not to say" + "form_type": "lettings", + "sections": { + "household": { + "label": "About the household", + "subsections": { + "household_characteristics": { + "label": "Household characteristics", + "pages": { + "tenant_code": { + "questions": { + "tenant_code": { + "check_answer_label": "Tenant code", + "header": "What is the tenant code?", + "type": "text" } } - } - }, - "household_number_of_other_members": { - "questions": { - "other_hhmemb": { - "check_answer_label": "Number of Other Household Members", - "header": "How many other people are there in the household?", - "hint_text": "The maximum number of others is 1", - "type": "numeric", - "min": 0, - "max": 1, - "step": 1, - "conditional_for": { - "relat2": ">0", - "age2": ">0", - "sex2": ">0", - "ecstat2": ">0" - } - }, - "relat2": { - "check_answer_label": "Person 2's relationship to lead tenant", - "header": "What's person 2's relationship to lead tenant", - "type": "radio", - "answer_options": { - "0": "Other", - "1": "Prefer not to say" + }, + "person_1_age": { + "questions": { + "age1": { + "check_answer_label": "Tenant's age", + "header": "What is the tenant's age?", + "type": "numeric", + "min": 0, + "max": 150, + "step": 1 } - }, - "age2": { - "check_answer_label": "Person 2's age", - "header": "What's person 2's age", - "type": "numeric", - "min": 0, - "max": 150, - "step": 1 - }, - "sex2": { - "check_answer_label": "Person 2's gender", - "header": "Which of these best describes person 2's gender identity?", - "type": "radio", - "answer_options": { - "0": "Female", - "1": "Male", - "2": "Non-binary", - "3": "Prefer not to say" + } + }, + "person_1_gender": { + "questions": { + "sex1": { + "check_answer_label": "Tenant's gender", + "header": "Which of these best describes the tenant's gender identity?", + "type": "radio", + "answer_options": { + "0": "Female", + "1": "Male", + "2": "Non-binary", + "3": "Prefer not to say" + } } - }, - "ecstat2": { - "check_answer_label": "Person 2's Work", - "header": "Which of these best describes person 2's working situation?", - "type": "radio", - "answer_options": { - "0": "Other", - "1": "Prefer not to say" + } + }, + "household_number_of_other_members": { + "questions": { + "other_hhmemb": { + "check_answer_label": "Number of Other Household Members", + "header": "How many other people are there in the household?", + "hint_text": "The maximum number of others is 1", + "type": "numeric", + "min": 0, + "max": 1, + "step": 1, + "conditional_for": { + "relat2": ">0", + "age2": ">0", + "sex2": ">0", + "ecstat2": ">0" + } + }, + "relat2": { + "check_answer_label": "Person 2's relationship to lead tenant", + "header": "What's person 2's relationship to lead tenant", + "type": "radio", + "answer_options": { + "0": "Other", + "1": "Prefer not to say" + } + }, + "age2": { + "check_answer_label": "Person 2's age", + "header": "What's person 2's age", + "type": "numeric", + "min": 0, + "max": 150, + "step": 1 + }, + "sex2": { + "check_answer_label": "Person 2's gender", + "header": "Which of these best describes person 2's gender identity?", + "type": "radio", + "answer_options": { + "0": "Female", + "1": "Male", + "2": "Non-binary", + "3": "Prefer not to say" + } + }, + "ecstat2": { + "check_answer_label": "Person 2's Work", + "header": "Which of these best describes person 2's working situation?", + "type": "radio", + "answer_options": { + "0": "Other", + "1": "Prefer not to say" + } } } } } - } - }, - "household_needs": { - "label": "Household needs", - "pages": { - "armed_forces": { - "header": "Experience of the UK Armed Forces", - "questions": { - "armed_forces": { - "header": "Has the tenant ever served in the UK armed forces?", - "type": "radio", - "check_answer_label": "Armed Forces", - "answer_options": { - "0": "Yes - a regular", - "1": "Yes - a reserve", - "2": "No", - "3": "Prefer not to say" + }, + "household_needs": { + "label": "Household needs", + "pages": { + "armed_forces": { + "header": "Experience of the UK Armed Forces", + "questions": { + "armed_forces": { + "header": "Has the tenant ever served in the UK armed forces?", + "type": "radio", + "check_answer_label": "Armed Forces", + "answer_options": { + "0": "Yes - a regular", + "1": "Yes - a reserve", + "2": "No", + "3": "Prefer not to say" + }, + "conditional_for": { + "leftreg": ["Yes - a regular", "Yes - a reserve"], + "reservist": ["Yes - a regular", "Yes - a reserve"] + } }, - "conditional_for": { - "leftreg": ["Yes - a regular", "Yes - a reserve"], - "reservist": ["Yes - a regular", "Yes - a reserve"] - } - }, - "leftreg": { - "header": "Are they still serving?", - "type": "radio", - "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?", - "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" + "leftreg": { + "header": "Are they still serving?", + "type": "radio", + "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?", + "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" + } } } - } - }, - "medical_conditions": { - "questions": { - "illness": { - "header": "Does anyone in the household have any of the following that they expect to last for 12 months or more:", - "type": "radio", - "check_answer_label": "Physical, mental health or illness in the household", - "answer_options": { - "0": "Yes", - "1": "No", - "2": "Do not know", - "3": "Prefer not to say" + }, + "medical_conditions": { + "questions": { + "illness": { + "header": "Does anyone in the household have any of the following that they expect to last for 12 months or more:", + "type": "radio", + "check_answer_label": "Physical, mental health or illness in the household", + "answer_options": { + "0": "Yes", + "1": "No", + "2": "Do not know", + "3": "Prefer not to say" + } } } - } - }, - "accessibility_requirements": { - "questions": { - "accessibility_requirements": { - "header": "Are any of these affected by their condition or illness?", - "hint_text": "Select all that apply", - "type": "checkbox", - "check_answer_label": "Disability requirements", - "answer_options": { - "housingneeds_a": "Fully wheelchair accessible housing", - "housingneeds_b": "Wheelchair access to essential rooms", - "housingneeds_c": "Level access housing", - "divider_a": true, - "housingneeds_h": "Do not know" + }, + "accessibility_requirements": { + "questions": { + "accessibility_requirements": { + "header": "Are any of these affected by their condition or illness?", + "hint_text": "Select all that apply", + "type": "checkbox", + "check_answer_label": "Disability requirements", + "answer_options": { + "housingneeds_a": "Fully wheelchair accessible housing", + "housingneeds_b": "Wheelchair access to essential rooms", + "housingneeds_c": "Level access housing", + "divider_a": true, + "housingneeds_h": "Do not know" + } } } - } - }, - "condition_effects": { - "questions": { - "condition_effects": { - "header": "Are any of these affected by their condition or illness?", - "hint_text": "Select all that apply", - "type": "checkbox", - "check_answer_label": "Conditions or illnesses", - "answer_options": { - "illness_type_1": "Vision - such as blindness or partial sight", - "illness_type_2": "Hearing - such as deafness or partial hearing" + }, + "condition_effects": { + "questions": { + "condition_effects": { + "header": "Are any of these affected by their condition or illness?", + "hint_text": "Select all that apply", + "type": "checkbox", + "check_answer_label": "Conditions or illnesses", + "answer_options": { + "illness_type_1": "Vision - such as blindness or partial sight", + "illness_type_2": "Hearing - such as deafness or partial hearing" + } } } } } } } - } - }, - "tenancy_and_property": { - "label": "Tenancy and property information", - "subsections": { - "tenancy_information": { - "label": "Tenancy information", - "pages": { - "tenancy_code": { - "questions": { - "tenancy_code": { - "check_answer_label": "What is the tenancy code?", - "header": "What is the tenancy code?", - "type": "text" + }, + "tenancy_and_property": { + "label": "Tenancy and property information", + "subsections": { + "tenancy_information": { + "label": "Tenancy information", + "pages": { + "tenancy_code": { + "questions": { + "tenancy_code": { + "check_answer_label": "What is the tenancy code?", + "header": "What is the tenancy code?", + "type": "text" + } } } } - } - }, - "property_information": { - "label": "Property information", - "pages": { - "property_wheelchair_accessible": { - "questions": { - "wchair": { - "check_answer_label": "Is property built or adapted to wheelchair user standards?", - "header": "Is property built or adapted to wheelchair user standards?", - "type": "radio", - "answer_options": { - "0": "Yes", - "1": "No" + }, + "property_information": { + "label": "Property information", + "pages": { + "property_wheelchair_accessible": { + "questions": { + "wchair": { + "check_answer_label": "Is property built or adapted to wheelchair user standards?", + "header": "Is property built or adapted to wheelchair user standards?", + "type": "radio", + "answer_options": { + "0": "Yes", + "1": "No" + } } } } } - } - }, - "conditional_question": { - "label": "Conditional question", - "pages": { - "conditional_question": { - "questions": { - "preg_occ": { - "check_answer_label": "Has the condition been met?", - "header": "Has the condition been met?", - "type": "radio", - "answer_options": { - "0": "Yes", - "1": "No" + }, + "conditional_question": { + "label": "Conditional question", + "pages": { + "conditional_question": { + "questions": { + "preg_occ": { + "check_answer_label": "Has the condition been met?", + "header": "Has the condition been met?", + "type": "radio", + "answer_options": { + "0": "Yes", + "1": "No" + } } - } - }, - "conditional_route_to": { - "rent": { "preg_occ": "Yes", "sex1": "Female" }, - "conditional_question_yes_page": { "preg_occ": "Yes" }, - "conditional_question_no_page": { "preg_occ": "No" } + }, + "conditional_route_to": { + "rent": { "preg_occ": "Yes", "sex1": "Female" }, + "conditional_question_yes_page": { "preg_occ": "Yes" }, + "conditional_question_no_page": { "preg_occ": "No" } + }, + "default_next_page": "check_answers" }, - "default_next_page": "check_answers" - }, - "conditional_question_yes_page": { - "questions": { - "cbl": { - "check_answer_label": "Has the next condition been met?", - "header": "Has the next condition been met?", - "type": "radio", - "answer_options": { - "0": "Yes", - "1": "No" + "conditional_question_yes_page": { + "questions": { + "cbl": { + "check_answer_label": "Has the next condition been met?", + "header": "Has the next condition been met?", + "type": "radio", + "answer_options": { + "0": "Yes", + "1": "No" + } } - } + }, + "default_next_page": "check_answers" }, - "default_next_page": "check_answers" - }, - "conditional_question_no_page": { - "questions": { - "conditional_question_no_question": { - "check_answer_label": "Has the condition not been met?", - "header": "Has the next condition not been met?", - "type": "radio", - "answer_options": { - "0": "Yes", - "1": "No" + "conditional_question_no_page": { + "questions": { + "conditional_question_no_question": { + "check_answer_label": "Has the condition not been met?", + "header": "Has the next condition not been met?", + "type": "radio", + "answer_options": { + "0": "Yes", + "1": "No" + } } - } + }, + "default_next_page": "conditional_question_no_second_page" }, - "default_next_page": "conditional_question_no_second_page" - }, - "conditional_question_no_second_page": { - "questions": { - "conditional_question_no_second_question": { - "check_answer_label": "Has the condition not been met again?", - "header": "Has the next condition not been met again?", - "type": "radio", - "answer_options": { - "0": "Yes", - "1": "No" + "conditional_question_no_second_page": { + "questions": { + "conditional_question_no_second_question": { + "check_answer_label": "Has the condition not been met again?", + "header": "Has the next condition not been met again?", + "type": "radio", + "answer_options": { + "0": "Yes", + "1": "No" + } } } } } } } - } - }, - "rent_and_charges": { - "label": "Rent and charges", - "subsections": { - "income_and_benefits": { - "label": "Income and benefits", - "pages": { - "net_income": { - "questions": { - "earnings": { - "check_answer_label": "Income", - "header": "What is the tenant’s /and partner’s combined income after tax?", - "type": "numeric", - "min": 0, - "step": "1" + }, + "rent_and_charges": { + "label": "Rent and charges", + "subsections": { + "income_and_benefits": { + "label": "Income and benefits", + "pages": { + "net_income": { + "questions": { + "earnings": { + "check_answer_label": "Income", + "header": "What is the tenant’s /and partner’s combined income after tax?", + "type": "numeric", + "min": 0, + "step": "1" + }, + "incfreq": { + "check_answer_label": "Income Frequency", + "header": "How often do they receive this income?", + "type": "radio", + "answer_options": { + "0": "Weekly", + "1": "Monthly", + "2": "Yearly" + } + } }, - "incfreq": { - "check_answer_label": "Income Frequency", - "header": "How often do they receive this income?", - "type": "radio", - "answer_options": { - "0": "Weekly", - "1": "Monthly", - "2": "Yearly" + "soft_validations": { + "override_net_income_validation": { + "check_answer_label": "Net income confirmed?", + "type": "validation_override", + "answer_options": { + "override_net_income_validation": "Yes" + } } } }, - "soft_validations": { - "override_net_income_validation": { - "check_answer_label": "Net income confirmed?", - "type": "validation_override", - "answer_options": { - "override_net_income_validation": "Yes" + "net_income_uc_proportion": { + "questions": { + "benefits": { + "check_answer_label": "Benefits as a proportion of income", + "header": "How much of the tenant’s income is from Universal Credit, state pensions or benefits?", + "type": "radio", + "answer_options": { + "0": "All", + "1": "Some" + } } } - } - }, - "net_income_uc_proportion": { - "questions": { - "benefits": { - "check_answer_label": "Benefits as a proportion of income", - "header": "How much of the tenant’s income is from Universal Credit, state pensions or benefits?", - "type": "radio", - "answer_options": { - "0": "All", - "1": "Some" - } - } - } - }, - "housing_benefit": { - "questions": { - "hb": { - "check_answer_label": "Universal Credit & Housing Benefit", - "header": "Is the tenant likely to be in receipt of any of these housing-related benefits?", - "type": "radio", - "answer_options": { - "0": "Housing Benefit, but not Universal Credit", - "1": "Prefer not to say" + }, + "housing_benefit": { + "questions": { + "hb": { + "check_answer_label": "Universal Credit & Housing Benefit", + "header": "Is the tenant likely to be in receipt of any of these housing-related benefits?", + "type": "radio", + "answer_options": { + "0": "Housing Benefit, but not Universal Credit", + "1": "Prefer not to say" + } } } } } - } - }, - "rent": { - "label": "Rent", - "pages": { - "rent": { - "questions": { - "rent_frequency": { - "check_answer_label": "Rent Period", - "header": "Which period are rent and other charges due?", - "type": "radio", - "answer_options": { - "0": "Weekly for 52 weeks", - "1": "Fortnightly" + }, + "rent": { + "label": "Rent", + "pages": { + "rent": { + "questions": { + "rent_frequency": { + "check_answer_label": "Rent Period", + "header": "Which period are rent and other charges due?", + "type": "radio", + "answer_options": { + "0": "Weekly for 52 weeks", + "1": "Fortnightly" + } + }, + "brent": { + "check_answer_label": "Basic Rent", + "header": "What is the basic rent?", + "hint_text": "Eligible for housing benefit or Universal Credit", + "type": "numeric", + "min": 0, + "step": 1, + "fields-to-add": [ + "brent", + "scharge", + "pscharge", + "supcharg" + ], + "result-field": "tcharge" + }, + "scharge": { + "check_answer_label": "Service Charge", + "header": "What is the service charge?", + "hint_text": "Eligible for housing benefit or Universal Credit", + "type": "numeric", + "min": 0, + "step": 1, + "fields-to-add": [ + "brent", + "scharge", + "pscharge", + "supcharg" + ], + "result-field": "tcharge" + }, + "pscharge": { + "check_answer_label": "Personal Service Charge", + "header": "What is the personal service charge?", + "hint_text": "Not eligible for housing benefit or Universal Credit. For example, hot water excluding water rates.", + "type": "numeric", + "min": 0, + "step": 1, + "fields-to-add": [ + "brent", + "scharge", + "pscharge", + "supcharg" + ], + "result-field": "tcharge" + }, + "supcharg": { + "check_answer_label": "Support Charge", + "header": "What is the support charge?", + "hint_text": "This is to fund housing-related support services included in the tenancy agreement", + "type": "numeric", + "min": 0, + "step": 1, + "fields-to-add": [ + "brent", + "scharge", + "pscharge", + "supcharg" + ], + "result-field": "tcharge" + }, + "tcharge": { + "check_answer_label": "Total Charge", + "header": "Total charge?", + "hint_text": "This is the total of rent and all charges", + "type": "numeric", + "min": 0, + "step": 1, + "readonly": true } - }, - "brent": { - "check_answer_label": "Basic Rent", - "header": "What is the basic rent?", - "hint_text": "Eligible for housing benefit or Universal Credit", - "type": "numeric", - "min": 0, - "step": 1, - "fields-to-add": [ - "brent", - "scharge", - "pscharge", - "supcharg" - ], - "result-field": "tcharge" - }, - "scharge": { - "check_answer_label": "Service Charge", - "header": "What is the service charge?", - "hint_text": "Eligible for housing benefit or Universal Credit", - "type": "numeric", - "min": 0, - "step": 1, - "fields-to-add": [ - "brent", - "scharge", - "pscharge", - "supcharg" - ], - "result-field": "tcharge" - }, - "pscharge": { - "check_answer_label": "Personal Service Charge", - "header": "What is the personal service charge?", - "hint_text": "Not eligible for housing benefit or Universal Credit. For example, hot water excluding water rates.", - "type": "numeric", - "min": 0, - "step": 1, - "fields-to-add": [ - "brent", - "scharge", - "pscharge", - "supcharg" - ], - "result-field": "tcharge" - }, - "supcharg": { - "check_answer_label": "Support Charge", - "header": "What is the support charge?", - "hint_text": "This is to fund housing-related support services included in the tenancy agreement", - "type": "numeric", - "min": 0, - "step": 1, - "fields-to-add": [ - "brent", - "scharge", - "pscharge", - "supcharg" - ], - "result-field": "tcharge" - }, - "tcharge": { - "check_answer_label": "Total Charge", - "header": "Total charge?", - "hint_text": "This is the total of rent and all charges", - "type": "numeric", - "min": 0, - "step": 1, - "readonly": true } } } } } - } - }, - "local_authority": { - "label": "Local authority", - "subsections": { - "local_authority": { - "label": "Local authority", - "pages": { - "time_lived_in_la": { - "questions": { - "layear": { - "check_answer_label": "How long has the household continuously lived in the local authority area where the new letting is located?", - "header": "How long has the household continuously lived in the local authority area where the new letting is located?", - "type": "radio", - "answer_options": { - "0": "Just moved to local authority area", - "1": "Less than 1 year", - "2": "1 to 2 years", - "3": "2 to 3 years", - "4": "3 to 4 years", - "5": "4 to 5 years", - "6": "5 years or more", - "7": "Do not know" + }, + "local_authority": { + "label": "Local authority", + "subsections": { + "local_authority": { + "label": "Local authority", + "pages": { + "time_lived_in_la": { + "questions": { + "layear": { + "check_answer_label": "How long has the household continuously lived in the local authority area where the new letting is located?", + "header": "How long has the household continuously lived in the local authority area where the new letting is located?", + "type": "radio", + "answer_options": { + "0": "Just moved to local authority area", + "1": "Less than 1 year", + "2": "1 to 2 years", + "3": "2 to 3 years", + "4": "3 to 4 years", + "5": "4 to 5 years", + "6": "5 years or more", + "7": "Do not know" + } } } - } - }, - "time_on_la_waiting_list": { - "questions": { - "lawaitlist": { - "check_answer_label": "How long has the household been on the local authority waiting list where the new letting is located?", - "header": "How long has the household been on the local authority waiting list where the new letting is located?", - "type": "radio", - "answer_options": { - "0": "Just moved to local authority area", - "1": "Less than 1 year", - "2": "1 to 2 years", - "3": "2 to 3 years", - "4": "3 to 4 years", - "5": "4 to 5 years", - "6": "5 years or more", - "7": "Do not know" + }, + "time_on_la_waiting_list": { + "questions": { + "lawaitlist": { + "check_answer_label": "How long has the household been on the local authority waiting list where the new letting is located?", + "header": "How long has the household been on the local authority waiting list where the new letting is located?", + "type": "radio", + "answer_options": { + "0": "Just moved to local authority area", + "1": "Less than 1 year", + "2": "1 to 2 years", + "3": "2 to 3 years", + "4": "3 to 4 years", + "5": "4 to 5 years", + "6": "5 years or more", + "7": "Do not know" + } } } - } - }, - "property_postcode": { - "questions": { - "property_postcode": { - "check_answer_label": "Postcode of previous accomodation if the household has moved from settled accommodation", - "header": "Postcode for the previous accommodation", - "hint_text": "If the household has moved from settled accommodation immediately prior to being re-housed", - "type": "text", - "conditional_for": { "faake_key": "fake_condition" } - }, - "previous_postcode": { - "check_answer_label": "Postcode of previous accomodation if the household has moved from settled accommodation", - "header": "Postcode for the previous accommodation", - "hint_text": "If the household has moved from settled accommodation immediately prior to being re-housed", - "type": "text" + }, + "property_postcode": { + "questions": { + "property_postcode": { + "check_answer_label": "Postcode of previous accomodation if the household has moved from settled accommodation", + "header": "Postcode for the previous accommodation", + "hint_text": "If the household has moved from settled accommodation immediately prior to being re-housed", + "type": "text", + "conditional_for": { "faake_key": "fake_condition" } + }, + "previous_postcode": { + "check_answer_label": "Postcode of previous accomodation if the household has moved from settled accommodation", + "header": "Postcode for the previous accommodation", + "hint_text": "If the household has moved from settled accommodation immediately prior to being re-housed", + "type": "text" + } } - } - }, - "major_repairs_date": { - "questions": { - "mrcdate": { - "check_answer_label": "What was the major repairs completion date?", - "header": "What was the major repairs completion date?", - "hint_text": "For example, 27 3 2007", - "type": "date" + }, + "major_repairs_date": { + "questions": { + "mrcdate": { + "check_answer_label": "What was the major repairs completion date?", + "header": "What was the major repairs completion date?", + "hint_text": "For example, 27 3 2007", + "type": "date" + } } } } } } - } - }, - "submission": { - "label": "Submission", - "subsections": { - "declaration": { - "label": "Declaration", - "pages": { - "declaration": { - "questions": { - "declaration": { - "check_answer_label": "", - "header": "What is the tenant code?", - "type": "text" + }, + "submission": { + "label": "Submission", + "subsections": { + "declaration": { + "label": "Declaration", + "pages": { + "declaration": { + "questions": { + "declaration": { + "check_answer_label": "", + "header": "What is the tenant code?", + "type": "text" + } } } } @@ -544,5 +545,4 @@ } } } - } -} + } \ No newline at end of file diff --git a/spec/fixtures/forms/test_validator.json b/spec/fixtures/forms/test_validator.json new file mode 100644 index 000000000..2f8e770d3 --- /dev/null +++ b/spec/fixtures/forms/test_validator.json @@ -0,0 +1,48 @@ +{ + "form_type": "lettings", + "start_year": 2021, + "end_year": 2022, + "sections": { + "household": { + "label": "About the household", + "subsections": { + "household_characteristics": { + "label": "Household characteristics", + "ShouldThrowError": "Shouldn't be here but what you gonna do?", + "pages": { + "tenant_code": { + "header": "", + "description": "", + "ShouldThrowError": "Shouldn't be here but what you gonna do?", + "questions": { + "tenant_code": { + "check_answer_label": "Tenant code", + "header": "What is the tenant code?", + "description": "", + "type": "text" + } + }, + "conditional_route_to": {"test": "Yes"} + }, + "conditional_route_to": {"test": "Yes"}, + "person_1_age": { + "header": "", + "description": "", + "questions": { + "person_1_age": { + "check_answer_label": "Tenant's age", + "header": "What is the tenant's age?", + "hint_text": "", + "type": "numeric", + "min": 0, + "max": 120, + "step": 1 + } + } + } + } + } + } + } + } +}