Browse Source

CLDC-827: Add custom guidance partials (#192)

* Add custom guidance partials

* Add a test for the partial being rendered

* Add some unit tests for this

* Rubocop

* Don't introduce heisenspecs

* Use component

* Add comment

* Update check values

* Make net_income_known enum
pull/197/head
baarkerlounger 3 years ago committed by GitHub
parent
commit
8134c2db7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      app/controllers/application_controller.rb
  2. 11
      app/models/case_log.rb
  3. 7
      app/models/constants/case_log.rb
  4. 4
      app/models/form/question.rb
  5. 2
      app/views/form/_checkbox_question.html.erb
  6. 2
      app/views/form/_date_question.html.erb
  7. 2
      app/views/form/_numeric_question.html.erb
  8. 2
      app/views/form/_radio_question.html.erb
  9. 2
      app/views/form/_select_question.html.erb
  10. 2
      app/views/form/_text_question.html.erb
  11. 2
      app/views/form/_textarea_question.html.erb
  12. 16
      app/views/form/guidance/_what_counts_as_income.html.erb
  13. 13
      config/forms/2021_2022.json
  14. 1
      config/initializers/default_form_builder.rb
  15. 15
      db/migrate/20220107103143_convert_net_income_known_to_enum.rb
  16. 6
      db/schema.rb
  17. 2
      spec/factories/case_log.rb
  18. 2
      spec/fixtures/complete_case_log.json
  19. 1
      spec/fixtures/forms/2021_2022.json
  20. 8
      spec/models/case_log_spec.rb
  21. 7
      spec/requests/form_controller_spec.rb
  22. 81
      spec/views/form/page_view_spec.rb

2
app/controllers/application_controller.rb

@ -1,6 +1,4 @@
class ApplicationController < ActionController::Base class ApplicationController < ActionController::Base
default_form_builder GOVUKDesignSystemFormBuilder::FormBuilder
def render_not_found def render_not_found
render "errors/not_found", status: :not_found render "errors/not_found", status: :not_found
end end

11
app/models/case_log.rb

@ -130,6 +130,7 @@ class CaseLog < ApplicationRecord
enum lettype: LET_TYPE, _suffix: true enum lettype: LET_TYPE, _suffix: true
enum postcode_known: POLAR, _suffix: true enum postcode_known: POLAR, _suffix: true
enum la_known: POLAR, _suffix: true enum la_known: POLAR, _suffix: true
enum net_income_known: NET_INCOME_KNOWN, _suffix: true
AUTOGENERATED_FIELDS = %w[id status created_at updated_at discarded_at renttype lettype is_la_inferred totchild totelder totadult].freeze AUTOGENERATED_FIELDS = %w[id status created_at updated_at discarded_at renttype lettype is_la_inferred totchild totelder totadult].freeze
OPTIONAL_FIELDS = %w[postcode_known OPTIONAL_FIELDS = %w[postcode_known
@ -214,7 +215,7 @@ private
self.month = startdate.month self.month = startdate.month
self.year = startdate.year self.year = startdate.year
end end
self.incref = 1 if net_income_known == "Prefer not to say" self.incref = 1 if net_income_known == "Tenant prefers not to say"
self.hhmemb = other_hhmemb + 1 if other_hhmemb.present? self.hhmemb = other_hhmemb + 1 if other_hhmemb.present?
self.renttype = RENT_TYPE_MAPPING[rent_type] self.renttype = RENT_TYPE_MAPPING[rent_type]
self.lettype = "#{renttype} #{needstype} #{owning_organisation['Org type']}" if renttype.present? && needstype.present? && owning_organisation["Org type"].present? self.lettype = "#{renttype} #{needstype} #{owning_organisation['Org type']}" if renttype.present? && needstype.present? && owning_organisation["Org type"].present?
@ -316,9 +317,11 @@ private
dynamically_not_required << "tenancyother" dynamically_not_required << "tenancyother"
end end
unless net_income_known == "Yes" if net_income_known == "Tenant prefers not to say"
dynamically_not_required << "earnings" dynamically_not_required << "earnings"
dynamically_not_required << "incfreq" dynamically_not_required << "incfreq"
else
dynamically_not_required << "incref"
end end
start_range = (other_hhmemb || 0) + 2 start_range = (other_hhmemb || 0) + 2
@ -329,10 +332,6 @@ private
dynamically_not_required << "ecstat#{n}" dynamically_not_required << "ecstat#{n}"
end end
if net_income_known != "Prefer not to say"
dynamically_not_required << "incref"
end
required.delete_if { |key, _value| dynamically_not_required.include?(key) } required.delete_if { |key, _value| dynamically_not_required.include?(key) }
end end
end end

7
app/models/constants/case_log.rb

@ -1070,4 +1070,11 @@ module Constants::CaseLog
"Non-binary" => "X", "Non-binary" => "X",
"Prefer not to say" => "R", "Prefer not to say" => "R",
}.freeze }.freeze
NET_INCOME_KNOWN = {
"Yes – the household has a weekly income" => 0,
"Yes – the household has a monthly income" => 1,
"Yes – the household has a yearly income" => 2,
"Tenant prefers not to say" => 3,
}.freeze
end end

4
app/models/form/question.rb

@ -2,12 +2,14 @@ class Form::Question
attr_accessor :id, :header, :hint_text, :description, :questions, attr_accessor :id, :header, :hint_text, :description, :questions,
:type, :min, :max, :step, :width, :fields_to_add, :result_field, :type, :min, :max, :step, :width, :fields_to_add, :result_field,
:conditional_for, :readonly, :answer_options, :page, :check_answer_label, :conditional_for, :readonly, :answer_options, :page, :check_answer_label,
:inferred_answers, :hidden_in_check_answers, :inferred_check_answers_value :inferred_answers, :hidden_in_check_answers, :inferred_check_answers_value,
:guidance_partial
def initialize(id, hsh, page) def initialize(id, hsh, page)
@id = id @id = id
@check_answer_label = hsh["check_answer_label"] @check_answer_label = hsh["check_answer_label"]
@header = hsh["header"] @header = hsh["header"]
@guidance_partial = hsh["guidance_partial"]
@hint_text = hsh["hint_text"] @hint_text = hsh["hint_text"]
@type = hsh["type"] @type = hsh["type"]
@min = hsh["min"] @min = hsh["min"]

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

@ -1,3 +1,5 @@
<%= 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 && !page_header.present? ? { text: caption.html_safe, size: "l" } : nil,
legend: { text: question.header.html_safe, size: !page_header.present? ? "l" : "m", tag: !page_header.present? ? "h1" : "h2" }, legend: { text: question.header.html_safe, size: !page_header.present? ? "l" : "m", tag: !page_header.present? ? "h1" : "h2" },

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

@ -1,3 +1,5 @@
<%= 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 && !page_header.present? ? { text: caption.html_safe, size: "l" } : nil,
legend: { text: question.header.html_safe, size: !page_header.present? ? "l" : "m", tag: !page_header.present? ? "h1" : "h2" }, legend: { text: question.header.html_safe, size: !page_header.present? ? "l" : "m", tag: !page_header.present? ? "h1" : "h2" },

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

@ -1,3 +1,5 @@
<%= 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 && !page_header.present? ? { text: caption.html_safe, size: "l" } : nil,
label: { text: question.header.html_safe, size: !page_header.present? ? "l" : "m", tag: !page_header.present? ? "h1" : "h2" }, label: { text: question.header.html_safe, size: !page_header.present? ? "l" : "m", tag: !page_header.present? ? "h1" : "h2" },

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

@ -1,3 +1,5 @@
<%= 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 && !page_header.present? ? { text: caption.html_safe, size: "l" } : nil,
legend: { text: question.header.html_safe, size: !page_header.present? ? "l" : "m", tag: !page_header.present? ? "h1" : "h2" }, legend: { text: question.header.html_safe, size: !page_header.present? ? "l" : "m", tag: !page_header.present? ? "h1" : "h2" },

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

@ -1,3 +1,5 @@
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %>
<% selected = CaseLog::UK_LA[@case_log.public_send(question.id)] || "" %> <% selected = CaseLog::UK_LA[@case_log.public_send(question.id)] || "" %>
<%= answers = question.answer_options.map { |key, value| OpenStruct.new(id: key, name: value) } <%= answers = question.answer_options.map { |key, value| OpenStruct.new(id: key, name: value) }
f.govuk_collection_select question.id.to_sym, f.govuk_collection_select question.id.to_sym,

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

@ -1,3 +1,5 @@
<%= 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 && !page_header.present? ? { text: caption.html_safe, size: "l" } : nil,
label: { text: question.header.html_safe, size: !page_header.present? ? "l" : "m", tag: !page_header.present? ? "h1" : "h2" }, label: { text: question.header.html_safe, size: !page_header.present? ? "l" : "m", tag: !page_header.present? ? "h1" : "h2" },

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

@ -1,3 +1,5 @@
<%= 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 && !page_header.present? ? { text: caption.html_safe, size: "l" } : nil,
label: { text: question.header.html_safe, size: !page_header.present? ? "l" : "m", tag: !page_header.present? ? "h1" : "h2" }, label: { text: question.header.html_safe, size: !page_header.present? ? "l" : "m", tag: !page_header.present? ? "h1" : "h2" },

16
app/views/form/guidance/_what_counts_as_income.html.erb

@ -0,0 +1,16 @@
<%= govuk_details(summary_text: 'What counts as income?') do %>
<p class="govuk-body">You should include any income from:</p>
<ul class="govuk-list govuk-list--bullet">
<li>employment</li>
<li>pensions</li>
<li>Universal Credit</li>
</ul>
<p class="govuk-body">Don’t include:</p>
<ul class="govuk-list govuk-list--bullet">
<li>National Insurance (NI) contributions and tax</li>
<li>housing benefit</li>
<li>child benefit</li>
<li>council tax support</li>
</ul>
<% end %>

13
config/forms/2021_2022.json

@ -1805,18 +1805,21 @@
"depends_on": { "about_this_log": "completed" }, "depends_on": { "about_this_log": "completed" },
"pages": { "pages": {
"net_income": { "net_income": {
"header": "", "header": "Household’s combined income",
"description": "", "description": "",
"questions": { "questions": {
"net_income_known": { "net_income_known": {
"check_answer_label": "Income known", "check_answer_label": "Income known",
"header": "Do you know the tenant and their partner’s net income?", "header": "Do you know the household’s combined income after tax?",
"guidance_partial": "what_counts_as_income",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"answer_options": { "answer_options": {
"0": "Yes", "0": "Yes – the household has a weekly income",
"1": "No", "1": "Yes – the household has a monthly income",
"2": "Tenant prefers not to say" "2": "Yes – the household has a yearly income",
"divider_a": true,
"3": "Tenant prefers not to say"
}, },
"conditional_for": { "conditional_for": {
"earnings": ["Yes"], "earnings": ["Yes"],

1
config/initializers/default_form_builder.rb

@ -0,0 +1 @@
Rails.application.config.action_view.default_form_builder = GOVUKDesignSystemFormBuilder::FormBuilder

15
db/migrate/20220107103143_convert_net_income_known_to_enum.rb

@ -0,0 +1,15 @@
class ConvertNetIncomeKnownToEnum < ActiveRecord::Migration[7.0]
def up
change_table :case_logs, bulk: true do |t|
t.remove :net_income_known
t.column :net_income_known, :integer
end
end
def down
change_table :case_logs, bulk: true do |t|
t.remove :net_income_known
t.column :net_income_known, :string
end
end
end

6
db/schema.rb

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2021_12_14_163230) do ActiveRecord::Schema.define(version: 2022_01_07_103143) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -127,7 +127,6 @@ ActiveRecord::Schema.define(version: 2021_12_14_163230) do
t.datetime "discarded_at" t.datetime "discarded_at"
t.string "tenancyother" t.string "tenancyother"
t.integer "override_net_income_validation" t.integer "override_net_income_validation"
t.string "net_income_known"
t.string "gdpr_acceptance" t.string "gdpr_acceptance"
t.string "gdpr_declined" t.string "gdpr_declined"
t.string "property_owner_organisation" t.string "property_owner_organisation"
@ -163,9 +162,9 @@ ActiveRecord::Schema.define(version: 2021_12_14_163230) do
t.string "why_dont_you_know_la" t.string "why_dont_you_know_la"
t.integer "unitletas" t.integer "unitletas"
t.integer "builtype" t.integer "builtype"
t.datetime "property_void_date"
t.bigint "owning_organisation_id" t.bigint "owning_organisation_id"
t.bigint "managing_organisation_id" t.bigint "managing_organisation_id"
t.datetime "property_void_date"
t.integer "renttype" t.integer "renttype"
t.integer "needstype" t.integer "needstype"
t.integer "lettype" t.integer "lettype"
@ -178,6 +177,7 @@ ActiveRecord::Schema.define(version: 2021_12_14_163230) do
t.integer "totchild" t.integer "totchild"
t.integer "totelder" t.integer "totelder"
t.integer "totadult" t.integer "totadult"
t.integer "net_income_known"
t.index ["discarded_at"], name: "index_case_logs_on_discarded_at" t.index ["discarded_at"], name: "index_case_logs_on_discarded_at"
t.index ["managing_organisation_id"], name: "index_case_logs_on_managing_organisation_id" t.index ["managing_organisation_id"], name: "index_case_logs_on_managing_organisation_id"
t.index ["owning_organisation_id"], name: "index_case_logs_on_owning_organisation_id" t.index ["owning_organisation_id"], name: "index_case_logs_on_owning_organisation_id"

2
spec/factories/case_log.rb

@ -113,7 +113,7 @@ FactoryBot.define do
discarded_at { nil } discarded_at { nil }
tenancyother { nil } tenancyother { nil }
override_net_income_validation { nil } override_net_income_validation { nil }
net_income_known { "Yes" } net_income_known { "Yes – the household has a weekly income" }
gdpr_acceptance { "Yes" } gdpr_acceptance { "Yes" }
gdpr_declined { "No" } gdpr_declined { "No" }
property_owner_organisation { "Test" } property_owner_organisation { "Test" }

2
spec/fixtures/complete_case_log.json vendored

@ -74,7 +74,7 @@
"mrcyear": 2020, "mrcyear": 2020,
"offered": 2, "offered": 2,
"wchair": "Yes", "wchair": "Yes",
"net_income_known": "Yes", "net_income_known": "Yes – the household has a weekly income",
"earnings": 0, "earnings": 0,
"incfreq": null, "incfreq": null,
"benefits": "Some", "benefits": "Some",

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

@ -368,6 +368,7 @@
"earnings": { "earnings": {
"check_answer_label": "Income", "check_answer_label": "Income",
"header": "What is the tenant’s /and partner’s combined income after tax?", "header": "What is the tenant’s /and partner’s combined income after tax?",
"guidance_partial": "what_counts_as_income",
"type": "numeric", "type": "numeric",
"min": 0, "min": 0,
"step": 1, "step": 1,

8
spec/models/case_log_spec.rb

@ -1030,11 +1030,9 @@ RSpec.describe Form, type: :model do
owning_organisation: organisation, owning_organisation: organisation,
property_postcode: "M1 1AE", property_postcode: "M1 1AE",
previous_postcode: "M2 2AE", previous_postcode: "M2 2AE",
# rubocop:disable Style/DateTime startdate: Time.gm(2021, 10, 10),
startdate: DateTime.new(2021, 10, 10), mrcdate: Time.gm(2021, 5, 4),
mrcdate: DateTime.new(2021, 5, 4), net_income_known: "Tenant prefers not to say",
# rubocop:enable Style/DateTime
net_income_known: "Prefer not to say",
other_hhmemb: 6, other_hhmemb: 6,
rent_type: "London living rent", rent_type: "London living rent",
needstype: "General needs", needstype: "General needs",

7
spec/requests/form_controller_spec.rb

@ -66,6 +66,13 @@ RSpec.describe FormController, type: :request do
expect(response).to have_http_status(:not_found) expect(response).to have_http_status(:not_found)
end end
end end
context "a form page that has custom guidance" do
it "displays the correct partial" do
get "/logs/#{case_log.id}/net-income", headers: headers, params: {}
expect(response.body).to match("What counts as income?")
end
end
end end
context "check answers pages" do context "check answers pages" do

81
spec/views/form/page_view_spec.rb

@ -0,0 +1,81 @@
require "rails_helper"
require_relative "../../request_helper"
RSpec.describe "form/page" do
before do
RequestHelper.stub_http_requests
end
let(:case_log) { FactoryBot.create(:case_log, :in_progress) }
let(:form) { case_log.form }
let(:subsection) { form.get_subsection("income_and_benefits") }
let(:page) { form.get_page("net_income") }
let(:question) { page.questions.find { |q| q.id == "earnings" } }
let(:initial_attribs) { { type: "numeric", answer_options: nil } }
def assign_attributes(object, attrs)
attrs.each_pair do |attr, value|
object.public_send("#{attr}=", value)
end
end
context "given a question with extra guidance" do
let(:expected_guidance) { /What counts as income?/ }
before do
assign(:case_log, case_log)
assign(:page, page)
assign(:subsection, subsection)
assign_attributes(question, attribs)
render
end
after do
# Revert any changes we've made to avoid affecting other specs as the form,
# subsection, page, question objects being acted on are in memory
assign_attributes(question, initial_attribs)
end
context "with radio type" do
let(:attribs) { { type: "radio", answer_options: { "1": "A", "2": "B" } } }
it "renders the guidance partial for radio questions" do
expect(rendered).to match(expected_guidance)
end
end
context "with text type" do
let(:attribs) { { type: "text", answer_options: nil } }
it "renders the guidance partial for text questions" do
expect(rendered).to match(expected_guidance)
end
end
context "with numeric type" do
let(:attribs) { { type: "numeric", answer_options: nil } }
it "renders the guidance partial for numeric questions" do
expect(rendered).to match(expected_guidance)
end
end
context "with select type" do
let(:attribs) { { type: "select", answer_options: { "1": "A", "2": "B" } } }
it "renders the guidance partial for select questions" do
expect(rendered).to match(expected_guidance)
end
end
context "with checkbox type" do
let(:attribs) { { type: "checkbox", answer_options: { "1": "A", "2": "B" } } }
it "renders the guidance partial for checkbox questions" do
expect(rendered).to match(expected_guidance)
end
end
context "with date type" do
let(:attribs) { { type: "date", answer_options: nil } }
it "renders the guidance partial for date questions" do
expect(rendered).to match(expected_guidance)
end
end
end
end
Loading…
Cancel
Save