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:
- Physical Condition
- Mental Health Condition
- Other Illness
",
- "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:- Physical Condition
- Mental Health Condition
- Other Illness
",
+ "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
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}