Browse Source

CLDC-788 interruption screen net income soft validation (#372)

* Set up failing test for new soft validation design

* remove unneeded js controller

* remove unneeded controller and spec

* rename net income validation override column

* spec form changes (to allow tests to run)

* validation spec changes

* replace all instances of override_net_income_validatiom

* passing first test

* Add to form and import styling

* remove unneeded code from interruption screen view

* Update test & code - failing last new expectation

* Display 2 previous question answers in the informative text

* extract displaying of the informative text into a helper

* Add tests for helper

* Remove some of the previous soft validations set up

* fix some failing specs

* Fix more tests

* lint

* Reset earnings question prefix and suffix to initial values instead of nil

* make it route backwards on a no

* test and linter fixes

* refactor next_page

* stryling

* more changes

* fix specs

* update real form

* design tweaks

* delete commented out tests

* content changes

* content changes

* changes to informative/hint text

* More robust styles for interuption panel, use smart quotes

* Rubocop

Co-authored-by: Kat <katrina@madetech.com>
Co-authored-by: Paul Robert Lloyd <me+git@paulrobertlloyd.com>
Co-authored-by: baarkerlounger <db@slothlife.xyz>
pull/374/head
Dushan 3 years ago committed by GitHub
parent
commit
484bb499e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      app/controllers/form_controller.rb
  2. 21
      app/controllers/soft_validations_controller.rb
  3. 16
      app/helpers/interuption_screen_helper.rb
  4. 4
      app/models/case_log.rb
  5. 4
      app/models/form.rb
  6. 15
      app/models/form/page.rb
  7. 2
      app/models/form/question.rb
  8. 32
      app/models/validations/soft_validations.rb
  9. 23
      app/views/form/_interruption_screen_question.html.erb
  10. 15
      app/views/form/_validation_override_question.html.erb
  11. 12
      app/views/form/page.html.erb
  12. 28
      app/webpacker/controllers/soft_validations_controller.js
  13. 31
      app/webpacker/styles/_button.scss
  14. 28
      app/webpacker/styles/_panel.scss
  15. 2
      app/webpacker/styles/application.scss
  16. 34
      config/forms/2021_2022.json
  17. 10
      config/locales/en.yml
  18. 1
      config/routes.rb
  19. 5
      db/migrate/20220228112732_rename_net_income_validation_override_column.rb
  20. 2
      db/schema.rb
  21. 2
      docs/api/DLUHC-CORE-Data.v1.json
  22. 2
      spec/factories/case_log.rb
  23. 4
      spec/features/form/check_answers_page_spec.rb
  24. 1
      spec/features/form/form_navigation_spec.rb
  25. 2
      spec/features/form/page_routing_spec.rb
  26. 39
      spec/features/form/validations_spec.rb
  27. 2
      spec/fixtures/complete_case_log.json
  28. 2
      spec/fixtures/exports/case_logs.xml
  29. 75
      spec/fixtures/forms/2021_2022.json
  30. 3
      spec/helpers/check_answers_helper_spec.rb
  31. 65
      spec/helpers/interuption_screen_helper_spec.rb
  32. 32
      spec/models/case_log_spec.rb
  33. 14
      spec/models/form/page_spec.rb
  34. 10
      spec/models/form/subsection_spec.rb
  35. 2
      spec/models/form_handler_spec.rb
  36. 5
      spec/models/form_spec.rb
  37. 2
      spec/requests/form_controller_spec.rb
  38. 54
      spec/requests/soft_validations_controller_spec.rb
  39. 2
      spec/views/form/page_view_spec.rb

4
app/controllers/form_controller.rb

@ -7,7 +7,7 @@ class FormController < ApplicationController
if @case_log
@page = @case_log.form.get_page(params[:case_log][:page])
responses_for_page = responses_for_page(@page)
if @case_log.update(responses_for_page) && @case_log.has_no_unresolved_soft_errors?
if @case_log.update(responses_for_page)
if @case_log.form.is_last_question?(@page, @case_log.form.subsection_for_page(@page), @case_log)
redirect_to(case_logs_path)
else
@ -54,7 +54,7 @@ class FormController < ApplicationController
private
def responses_for_page(page)
page.expected_responses.each_with_object({}) do |question, result|
page.questions.each_with_object({}) do |question, result|
question_params = params["case_log"][question.id]
if question.type == "date"
day = params["case_log"]["#{question.id}(3i)"]

21
app/controllers/soft_validations_controller.rb

@ -1,21 +0,0 @@
class SoftValidationsController < ApplicationController
before_action :authenticate_user!
def show
@case_log = CaseLog.find(params[:case_log_id])
page_id = request.env["PATH_INFO"].split("/")[-2]
page = @case_log.form.get_page(page_id)
if page_requires_soft_validation_override?(page)
errors = @case_log.soft_errors.values.first
render json: { show: true, label: errors.message, hint: errors.hint_text }
else
render json: { show: false }
end
end
private
def page_requires_soft_validation_override?(page)
@case_log.soft_errors.present? && @case_log.soft_errors.keys.first == page.soft_validations&.first&.id
end
end

16
app/helpers/interuption_screen_helper.rb

@ -0,0 +1,16 @@
module InteruptionScreenHelper
def display_informative_text(informative_text, case_log)
translation_questions = informative_text["argument"].map { |x| case_log.form.get_question(x) }
begin
case translation_questions.count
when 2
translation = I18n.t(informative_text["translation"], informative_text["argument"][0].to_sym => translation_questions[0].answer_label(case_log), informative_text["argument"][1].to_sym => translation_questions[1].answer_label(case_log))
when 1
translation = I18n.t(informative_text["translation"], informative_text["argument"][0].to_sym => translation_questions[0].answer_label(case_log))
end
rescue StandardError
return ""
end
translation.to_s.html_safe
end
end

4
app/models/case_log.rb

@ -111,6 +111,10 @@ class CaseLog < ApplicationRecord
incfreq == 2
end
def net_income_soft_validation_triggered?
net_income_in_soft_min_range? || net_income_in_soft_max_range?
end
def given_reasonable_preference?
reasonpref == 1
end

4
app/models/form.rb

@ -35,7 +35,9 @@ class Form
def next_page(page, case_log)
page_ids = subsection_for_page(page).pages.map(&:id)
page_index = page_ids.index(page.id)
nxt_page = get_page(page_ids[page_index + 1])
page_id = page.id.include?("value_check") && case_log[page.id] == 1 ? page_ids[page_index - 1] : page_ids[page_index + 1]
nxt_page = get_page(page_id)
return :check_answers if nxt_page.nil?
return nxt_page.id if nxt_page.routed_to?(case_log)

15
app/models/form/page.rb

@ -1,6 +1,6 @@
class Form::Page
attr_accessor :id, :header, :description, :questions, :soft_validations,
:depends_on, :subsection, :hide_subsection_label
attr_accessor :id, :header, :description, :questions,
:depends_on, :title_text, :informative_text, :subsection, :hide_subsection_label
def initialize(id, hsh, subsection)
@id = id
@ -8,19 +8,12 @@ class Form::Page
@description = hsh["description"]
@questions = hsh["questions"].map { |q_id, q| Form::Question.new(q_id, q, self) }
@depends_on = hsh["depends_on"]
@title_text = hsh["title_text"]
@informative_text = hsh["informative_text"]
@hide_subsection_label = hsh["hide_subsection_label"]
@soft_validations = hsh["soft_validations"]&.map { |sv_id, s| Form::Question.new(sv_id, s, self) }
@subsection = subsection
end
def expected_responses
questions + (soft_validations || [])
end
def has_soft_validations?
soft_validations.present?
end
def routed_to?(case_log)
return true unless depends_on || subsection.depends_on

2
app/models/form/question.rb

@ -229,6 +229,7 @@ private
household_charge: [0],
is_carehome: [1],
rent_shortfall: [0],
net_income_value_check: [0],
}.freeze
RADIO_NO_VALUE = {
@ -253,6 +254,7 @@ private
household_charge: [1],
is_carehome: [0],
rent_shortfall: [1],
net_income_value_check: [1],
}.freeze
RADIO_DONT_KNOW_VALUE = {

32
app/models/validations/soft_validations.rb

@ -13,38 +13,6 @@ module Validations::SoftValidations
10 => OpenStruct.new(soft_min: 47, soft_max: 730, hard_min: 10, hard_max: 1300),
}.freeze
def has_no_unresolved_soft_errors?
soft_errors.empty? || soft_errors_overridden?
end
def soft_errors
{}.merge(net_income_validations)
end
def soft_errors_overridden?
public_send(soft_errors.keys.first) == 1 if soft_errors.present?
end
private
def net_income_validations
net_income_errors = {}
if net_income_in_soft_min_range?
net_income_errors["override_net_income_validation"] = OpenStruct.new(
message: I18n.t("soft_validations.net_income.in_soft_min_range.message"),
hint_text: I18n.t("soft_validations.net_income.hint_text", ecstat1:),
)
elsif net_income_in_soft_max_range?
net_income_errors["override_net_income_validation"] = OpenStruct.new(
message: I18n.t("soft_validations.net_income.in_soft_max_range.message"),
hint_text: I18n.t("soft_validations.net_income.hint_text", ecstat1:),
)
else
update_column(:override_net_income_validation, nil)
end
net_income_errors
end
def net_income_in_soft_max_range?
return unless weekly_net_income && ecstat1

23
app/views/form/_interruption_screen_question.html.erb

@ -0,0 +1,23 @@
<%= govuk_panel(
title_text: title_text,
classes: 'app-panel--interruption',
) do %>
<%= display_informative_text(informative_text, case_log) %>
<%= f.govuk_radio_buttons_fieldset question.id.to_sym,
legend: { text: question.header },
hint: { text: question.hint_text&.html_safe } do %>
<% question.answer_options.map do |key, options| %>
<% if key.starts_with?("divider") %>
<%= f.govuk_radio_divider %>
<% else %>
<%= f.govuk_radio_button question.id,
key,
label: { text: options['value'] },
hint: { text: options['hint'] },
**stimulus_html_attributes(question)
%>
<% end %>
<% end %>
<% end %>
<%= f.govuk_submit "Save and continue", accesskey: "s", class: "app-button--inverse govuk-!-margin-bottom-0" %>
<% end %>

15
app/views/form/_validation_override_question.html.erb

@ -1,15 +0,0 @@
<div class="govuk-form-group govuk-form-group--error"
data-controller="soft-validations"
data-soft-validations-target="override"
style='display:none;'>
<%= f.govuk_check_boxes_fieldset page.soft_validations&.first&.id.to_sym,
legend: { text: "soft-validations-placeholder-message", size: "l" },
hint: { text: "soft-validations-placeholder-hint-text" } do %>
<%= f.govuk_check_box page.soft_validations&.first&.id, page.soft_validations&.first&.id,
label: { text: "Yes" },
checked: @case_log[page.soft_validations&.first&.id] == "Yes"
%>
<% end %>
</div>

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

@ -16,7 +16,7 @@
<%= turbo_frame_tag "case_log_form", target: "_top" do %>
<%= form_with model: @case_log, url: form_case_log_path(@case_log), method: "post" do |f| %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds-from-desktop">
<div class=<%= @page.questions[0].type == "interruption_screen" ? "govuk-grid-column-full-from-desktop" : "govuk-grid-column-two-thirds-from-desktop"%>>
<% remove_other_page_errors(@case_log, @page) %>
<%= f.govuk_error_summary %>
@ -38,19 +38,21 @@
<% if question.read_only? %>
<hr class="govuk-section-break govuk-section-break--visible govuk-section-break--m">
<% end %>
<% if question.type == "interruption_screen" %>
<%= render partial: "form/#{question.type}_question", locals: { question: question, caption_text: @subsection.label, page_header: @page.header, case_log: @case_log, title_text: @page.title_text, informative_text: @page.informative_text, form: @form, f: f, conditional: false } %>
<% else %>
<%= render partial: "form/#{question.type}_question", locals: { question: question, caption_text: @subsection.label, page_header: @page.header, case_log: @case_log, f: f, conditional: false } %>
</div>
<% end %>
<% if @page.has_soft_validations? %>
<%= render partial: "form/validation_override_question", locals: { f: f, page: @page } %>
</div>
<% end %>
<%= f.hidden_field :page, value: @page.id %>
<% if @case_log.form.is_last_question?(@page, @subsection, @case_log) %>
<%= f.govuk_submit "Submit lettings log", accesskey: "s" %>
<%else %>
<% if !@page.id.include?("value_check") %>
<%= f.govuk_submit "Save and continue", accesskey: "s" %>
<% end %>
<%end %>
</div>
</div>

28
app/webpacker/controllers/soft_validations_controller.js

@ -1,28 +0,0 @@
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = [ "override" ]
initialize() {
let url = window.location.href + "/soft-validations"
let div = this.overrideTarget
fetch(url, { headers: { accept: "application/json" } })
.then(response => response.json())
.then((response) => {
if(response["show"]){
div.style.display = "block"
let innerHTML = div.innerHTML
innerHTML = innerHTML.replace("soft-validations-placeholder-message", response["label"])
innerHTML = innerHTML.replace("soft-validations-placeholder-hint-text", response["hint"])
div.innerHTML = innerHTML
} else {
div.style.display = "none"
let buttons = document.getElementsByName(`case_log[override_net_income_validation][]`)
Object.entries(buttons).map(([idx, button]) => {
button.checked = false
})
}
}
)
}
}

31
app/webpacker/styles/_button.scss

@ -0,0 +1,31 @@
$app-button-shadow-size: $govuk-border-width-form-element;
$app-button-inverse-background-colour: govuk-colour("white");
$app-button-inverse-foreground-colour: $govuk-brand-colour;
$app-button-inverse-shadow-colour: govuk-shade($app-button-inverse-foreground-colour, 30%);
$app-button-inverse-hover-background-colour: govuk-tint($app-button-inverse-foreground-colour, 90%);
.app-button--inverse,
.app-button--inverse:link,
.app-button--inverse:visited {
color: $app-button-inverse-foreground-colour;
background-color: $app-button-inverse-background-colour;
box-shadow: 0 $app-button-shadow-size 0 $app-button-inverse-shadow-colour;
}
.app-button--inverse:hover {
color: $app-button-inverse-foreground-colour;
background-color: $app-button-inverse-hover-background-colour;
}
.app-button--inverse:focus:not(:hover) {
color: $govuk-focus-text-colour;
background-color: $govuk-focus-colour;
}
.app-button--inverse:active,
.app-button--inverse:focus {
border-color: $govuk-focus-colour;
color: $app-button-inverse-foreground-colour;
background-color: $app-button-inverse-hover-background-colour;
box-shadow: inset 0 0 0 2px $govuk-focus-colour;
}

28
app/webpacker/styles/_panel.scss

@ -0,0 +1,28 @@
.app-panel--interruption {
background-color: govuk-colour("blue");
color: govuk-colour("white");
text-align: left;
p,
.govuk-body,
.govuk-label,
.govuk-fieldset__legend,
.govuk-hint {
color: govuk-colour("white");
}
a:not(:focus) {
color: inherit;
}
*:last-child {
margin-bottom: 0;
}
.govuk-radios__label::before,
& ::after {
color: govuk-colour("black");
border-color: govuk-colour("black");
background-color: govuk-colour("white");
}
}

2
app/webpacker/styles/application.scss

@ -13,6 +13,7 @@ $govuk-global-styles: true;
@import "~govuk-frontend/govuk/all";
@import "accessible-autocomplete";
@import "button";
@import "figure";
@import "input";
@import "related-navigation";
@ -21,6 +22,7 @@ $govuk-global-styles: true;
@import "table-group";
@import "task-list";
@import "template";
@import "panel";
// Turbo
.turbo-progress-bar {

34
config/forms/2021_2022.json

@ -1575,15 +1575,6 @@
}
}
}
},
"soft_validations": {
"override_net_income_validation": {
"check_answer_label": "Net income confirmed?",
"type": "validation_override",
"answer_options": {
"override_net_income_validation": "Yes"
}
}
}
},
"person_2_known": {
@ -4583,13 +4574,28 @@
},
"hidden_in_check_answers": true
}
}
},
"net_income_value_check": {
"depends_on": [{ "net_income_soft_validation_triggered?": true }],
"title_text": "Net income is outside the expected range based on the main tenant’s working situation",
"informative_text": {
"translation": "soft_validations.net_income.hint_text",
"argument": ["ecstat1", "earnings"]
},
"soft_validations": {
"override_net_income_validation": {
"check_answer_label": "Net income confirmed?",
"type": "validation_override",
"questions": {
"net_income_value_check": {
"check_answer_label": "Net income soft validation",
"hidden_in_check_answers": true,
"header": "Are you sure this is correct?",
"type": "interruption_screen",
"answer_options": {
"override_net_income_validation": "Yes"
"0": {
"value":"Yes"
},
"1": {
"value":"No"
}
}
}
}

10
config/locales/en.yml

@ -90,8 +90,8 @@ en:
reasonpref:
not_homeless: "Answer cannot be ‘homeless or about to lose their home’ as you already told us the tenant was not homeless immediately prior to this letting"
reasonable_preference_reason:
reason_required: "If reasonable preference is \"Yes\", a reason must be given"
reason_not_required: "If reasonable preference is \"No\", no reasons should be given"
reason_required: 'If reasonable preference is "Yes", a reason must be given'
reason_not_required: 'If reasonable preference is "No", no reasons should be given'
underoccupation_benefitcap:
dont_know_required: "must be don’t know if tenant’s main reason for leaving is don’t know"
reservist:
@ -131,7 +131,6 @@ en:
not_homeless: "Answer cannot be ‘no’ as you already told us the tenant was homeless or about to lose their home"
previous_la_known: "Enter a local authority"
tenancy:
length:
fixed_term_not_required: "You must only answer the fixed term tenancy length question if the tenancy type is fixed term"
@ -145,7 +144,7 @@ en:
soft_validations:
net_income:
hint_text: "This is based on the tenant’s work situation: %{ecstat1}"
hint_text: "<p>You told us the main tenant’s working situation is: <strong>%{ecstat1}</strong></p><p>The household income you have entered is <strong>%{earnings}</strong></p>"
in_soft_min_range:
message: "Net income is lower than expected based on the main tenant’s working situation. Are you sure this is correct?"
in_soft_max_range:
@ -160,3 +159,6 @@ en:
code_has_been_sent: "Your security code has been sent."
code_required: "Security code is required"
code_incorrect: "Security code is incorrect"
test:
one_argument: "This is based on the tenant’s work situation: %{ecstat1}"

1
config/routes.rb

@ -60,7 +60,6 @@ Rails.application.routes.draw do
FormHandler.instance.forms.each do |_key, form|
form.pages.map do |page|
get page.id.to_s.dasherize, to: "form##{page.id}"
get "#{page.id.to_s.dasherize}/soft-validations", to: "soft_validations#show" if page.has_soft_validations?
end
form.subsections.map do |subsection|

5
db/migrate/20220228112732_rename_net_income_validation_override_column.rb

@ -0,0 +1,5 @@
class RenameNetIncomeValidationOverrideColumn < ActiveRecord::Migration[7.0]
def change
rename_column :case_logs, :override_net_income_validation, :net_income_value_check
end
end

2
db/schema.rb

@ -124,7 +124,7 @@ ActiveRecord::Schema[7.0].define(version: 202202071123100) do
t.integer "rp_hardship"
t.integer "rp_dontknow"
t.string "tenancyother"
t.integer "override_net_income_validation"
t.integer "net_income_value_check"
t.string "property_owner_organisation"
t.string "property_manager_organisation"
t.string "sale_or_letting"

2
docs/api/DLUHC-CORE-Data.v1.json

@ -386,7 +386,7 @@
"rp_hardship": 0,
"rp_dontknow": 0,
"discarded_at": "05/05/2020",
"override_net_income_validation": "",
"net_income_value_check": "",
"property_owner_organisation": "",
"property_manager_organisation": "",
"rent_type": 0,

2
spec/factories/case_log.rb

@ -105,7 +105,7 @@ FactoryBot.define do
rp_hardship { 0 }
rp_dontknow { 0 }
tenancyother { nil }
override_net_income_validation { nil }
net_income_value_check { nil }
net_income_known { 1 }
property_owner_organisation { "Test" }
property_manager_organisation { "Test" }

4
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")
assert_selector "a", text: /Answer (?!the missing questions)/, count: 4
assert_selector "a", text: /Answer (?!the missing questions)/, count: 5
assert_selector "a", text: "Change", count: 0
expect(page).to have_link("Answer", href: "/logs/#{empty_case_log.id}/person-1-age")
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: 3
assert_selector "a", text: /Answer (?!the missing questions)/, count: 4
assert_selector "a", text: "Change", count: 1
expect(page).to have_link("Change", href: "/logs/#{empty_case_log.id}/person-1-age")
end

1
spec/features/form/form_navigation_spec.rb

@ -18,6 +18,7 @@ RSpec.describe "Form Navigation" do
tenant_code: { type: "text", answer: "BZ737", path: "tenant-code" },
age1: { type: "numeric", answer: 25, path: "person-1-age" },
sex1: { type: "radio", answer: "Female", path: "person-1-gender" },
ecstat1: { type: "radio", answer: 3, path: "person-1-working-situation" },
other_hhmemb: { type: "numeric", answer: 2, path: "household-number-of-other-members" },
}
end

2
spec/features/form/page_routing_spec.rb

@ -38,7 +38,7 @@ RSpec.describe "Form Page Routing" do
visit("/logs/#{id}/person-1-gender")
choose("case-log-sex1-f-field", allow_label_click: true)
click_button("Save and continue")
expect(page).to have_current_path("/logs/#{id}/household-number-of-other-members")
expect(page).to have_current_path("/logs/#{id}/person-1-working-situation")
visit("/logs/#{id}/conditional-question")
choose("case-log-preg-occ-1-field", allow_label_click: true)
click_button("Save and continue")

39
spec/features/form/validations_spec.rb

@ -124,49 +124,28 @@ RSpec.describe "validations" do
let(:income_over_soft_limit) { 750 }
let(:income_under_soft_limit) { 700 }
it "prompts the user to confirm the value is correct", js: true do
it "prompts the user to confirm the value is correct with an interruption screen" do
visit("/logs/#{case_log.id}/net-income")
fill_in("case-log-earnings-field", with: income_over_soft_limit)
choose("case-log-incfreq-0-field", allow_label_click: true)
click_button("Save and continue")
expect(page).to have_content("Are you sure this is correct?")
check("case-log-override-net-income-validation-override-net-income-validation-field", allow_label_click: true)
expect(page).to have_current_path("/logs/#{case_log.id}/net-income-value-check")
expect(page).to have_content("Net income is outside the expected range based on the main tenant’s working situation")
expect(page).to have_content("You told us the main tenant’s working situation is: Full-time – 30 hours or more")
expect(page).to have_content("The household income you have entered is £750.00 every week")
choose("case-log-net-income-value-check-0-field", allow_label_click: true)
click_button("Save and continue")
expect(page).to have_current_path("/logs/#{case_log.id}/net-income-uc-proportion")
end
it "does not require confirming the value if the value is amended" do
it "returns the user to the previous question if they do not confirm the value as correct on the interruption screen" do
visit("/logs/#{case_log.id}/net-income")
fill_in("case-log-earnings-field", with: income_over_soft_limit)
choose("case-log-incfreq-0-field", allow_label_click: true)
click_button("Save and continue")
fill_in("case-log-earnings-field", with: income_under_soft_limit)
choose("case-log-net-income-value-check-1-field", allow_label_click: true)
click_button("Save and continue")
expect(page).to have_current_path("/logs/#{case_log.id}/net-income-uc-proportion")
case_log.reload
expect(case_log.override_net_income_validation).to be_nil
end
it "clears the confirmation question if the amount was amended and the page is returned to using the back button", js: true do
visit("/logs/#{case_log.id}/net-income")
fill_in("case-log-earnings-field", with: income_over_soft_limit)
choose("case-log-incfreq-0-field", allow_label_click: true)
click_button("Save and continue")
fill_in("case-log-earnings-field", with: income_under_soft_limit)
click_button("Save and continue")
click_link(text: "Back")
expect(page).to have_no_content("Are you sure this is correct?")
end
it "does not clear the confirmation question if the page is returned to using the back button and the amount is still over the soft limit", js: true do
visit("/logs/#{case_log.id}/net-income")
fill_in("case-log-earnings-field", with: income_over_soft_limit)
choose("case-log-incfreq-0-field", allow_label_click: true)
click_button("Save and continue")
check("case-log-override-net-income-validation-override-net-income-validation-field", allow_label_click: true)
click_button("Save and continue")
click_link(text: "Back")
expect(page).to have_content("Are you sure this is correct?")
expect(page).to have_current_path("/logs/#{case_log.id}/net-income")
end
end
end

2
spec/fixtures/complete_case_log.json vendored

@ -118,7 +118,7 @@
"rp_hardship": 0,
"rp_dontknow": 0,
"discarded_at": "05/05/2020",
"override_net_income_validation": "",
"net_income_value_check": 0,
"property_owner_organisation": "",
"property_manager_organisation": "",
"rent_type": 0,

2
spec/fixtures/exports/case_logs.xml vendored

@ -86,7 +86,7 @@
<rp_hardship>0</rp_hardship>
<rp_dontknow>0</rp_dontknow>
<tenancyother/>
<override_net_income_validation/>
<net_income_value_check/>
<property_owner_organisation>Test</property_owner_organisation>
<property_manager_organisation>Test</property_manager_organisation>
<sale_or_letting/>

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

@ -64,6 +64,56 @@
}
}
},
"person_1_working_situation": {
"header": "",
"description": "",
"questions": {
"ecstat1": {
"check_answer_label": "Lead tenant’s working situation",
"header": "Which of these best describes the lead tenant’s socks?",
"hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.",
"type": "radio",
"answer_options": {
"0": {
"value": "Part-time – Less than 30 hours"
},
"1": {
"value": "Full-time – 30 hours or more"
},
"2": {
"value": "Full-time student"
},
"3": {
"value": "In government training into work, such as New Deal"
},
"4": {
"value": "Jobseeker"
},
"5": {
"value": "Not seeking work"
},
"6": {
"value": "Unable to work because of long term sick or disability"
},
"7": {
"value": "Retired"
},
"8": {
"value": "Child under 16"
},
"9": {
"value": "Other"
},
"divider": {
"value": true
},
"10": {
"value": "Tenant prefers not to say"
}
}
}
}
},
"household_number_of_other_members": {
"questions": {
"other_hhmemb": {
@ -485,13 +535,28 @@
}
}
}
}
},
"net_income_value_check": {
"depends_on": [{ "net_income_soft_validation_triggered?": true }],
"title_text": "Net income is outside the expected range based on the main tenant’s working situation",
"informative_text": {
"translation": "soft_validations.net_income.hint_text",
"argument": ["ecstat1", "earnings"]
},
"soft_validations": {
"override_net_income_validation": {
"check_answer_label": "Net income confirmed?",
"type": "validation_override",
"questions": {
"net_income_value_check": {
"check_answer_label": "Net income soft validation",
"hidden_in_check_answers": true,
"header": "Are you sure this is correct?",
"type": "interruption_screen",
"answer_options": {
"override_net_income_validation": "Yes"
"0": {
"value":"Yes"
},
"1": {
"value":"No"
}
}
}
}

3
spec/helpers/check_answers_helper_spec.rb

@ -9,7 +9,7 @@ RSpec.describe CheckAnswersHelper do
context "when a section hasn't been completed yet" do
it "returns that you have unanswered questions" do
expect(display_answered_questions_summary(subsection, case_log))
.to match(/You have answered 2 of 5 questions./)
.to match(/You have answered 2 of 6 questions./)
end
end
@ -18,6 +18,7 @@ RSpec.describe CheckAnswersHelper do
case_log.sex1 = "F"
case_log.other_hhmemb = 0
case_log.propcode = "123"
case_log.ecstat1 = 200
expect(display_answered_questions_summary(subsection, case_log))
.to match(/You answered all the questions./)
expect(display_answered_questions_summary(subsection, case_log))

65
spec/helpers/interuption_screen_helper_spec.rb

@ -0,0 +1,65 @@
require "rails_helper"
RSpec.describe InteruptionScreenHelper do
form_handler = FormHandler.instance
let(:form) { form_handler.get_form("test_form") }
let(:subsection) { form.get_subsection("household_characteristics") }
let(:user) { FactoryBot.create(:user) }
let(:case_log) do
FactoryBot.create(
:case_log,
:in_progress,
ecstat1: 1,
earnings: 750,
incfreq: 0,
owning_organisation: user.organisation,
managing_organisation: user.organisation,
)
end
describe "display_informative_text" do
context "when 2 out of 2 arguments are given" do
it "returns correct informative text" do
informative_text = {
"translation" => "soft_validations.net_income.hint_text",
"argument" => %w[ecstat1 earnings],
}
expect(display_informative_text(informative_text, case_log))
.to eq("<p>You told us the main tenant’s working situation is: <strong>Full-time – 30 hours or more</strong></p><p>The household income you have entered is <strong>£750.00 every week</strong></p>")
end
end
context "when 1 out of 1 arguments is given" do
it "returns correct informative text" do
informative_text = {
"translation" => "test.one_argument",
"argument" => %w[ecstat1],
}
expect(display_informative_text(informative_text, case_log))
.to eq("This is based on the tenant’s work situation: Full-time – 30 hours or more")
end
end
end
context "when 2 out of 1 arguments are given" do
it "returns correct informative text" do
informative_text = {
"translation" => "test.one_argument",
"argument" => %w[ecstat1 earnings],
}
expect(display_informative_text(informative_text, case_log))
.to eq("This is based on the tenant’s work situation: Full-time – 30 hours or more")
end
end
context "when 1 out of 2 arguments are given" do
it "returns an empty string" do
informative_text = {
"translation" => "soft_validations.net_income.hint_text",
"argument" => %w[ecstat1],
}
expect(display_informative_text(informative_text, case_log))
.to eq("")
end
end
end

32
spec/models/case_log_spec.rb

@ -42,38 +42,6 @@ RSpec.describe CaseLog do
.to include(CaseLogValidator)
end
end
context "when soft validations exist" do
context "with an income in upper soft range" do
let(:case_log) do
FactoryBot.create(:case_log,
ecstat1: 1,
earnings: 750,
incfreq: 0)
end
it "updates soft errors" do
expect(case_log.has_no_unresolved_soft_errors?).to be false
expect(case_log.soft_errors["override_net_income_validation"].message)
.to match(I18n.t("soft_validations.net_income.in_soft_max_range.message"))
end
end
context "with an income in lower soft validation range" do
let(:case_log) do
FactoryBot.create(:case_log,
ecstat1: 1,
earnings: 120,
incfreq: 0)
end
it "updates soft errors" do
expect(case_log.has_no_unresolved_soft_errors?).to be false
expect(case_log.soft_errors["override_net_income_validation"].message)
.to match(I18n.t("soft_validations.net_income.in_soft_min_range.message"))
end
end
end
end
describe "#update" do

14
spec/models/form/page_spec.rb

@ -31,20 +31,6 @@ RSpec.describe Form::Page, type: :model do
expect(page.questions.map(&:id)).to eq(expected_questions)
end
it "has soft validations" do
expected_soft_validations = %w[override_net_income_validation]
expect(page.soft_validations.map(&:id)).to eq(expected_soft_validations)
end
it "has a soft_validation helper" do
expect(page.has_soft_validations?).to be true
end
it "has expected form responses" do
expected_responses = %w[earnings incfreq override_net_income_validation]
expect(page.expected_responses.map(&:id)).to eq(expected_responses)
end
context "with a page having conditional questions" do
let(:page_id) { "housing_benefit" }

10
spec/models/form/subsection_spec.rb

@ -25,12 +25,12 @@ RSpec.describe Form::Subsection, type: :model do
end
it "has pages" do
expected_pages = %w[tenant_code person_1_age person_1_gender household_number_of_other_members propcode]
expected_pages = %w[tenant_code person_1_age person_1_gender person_1_working_situation household_number_of_other_members propcode]
expect(sub_section.pages.map(&:id)).to eq(expected_pages)
end
it "has questions" do
expected_questions = %w[tenant_code age1 sex1 other_hhmemb relat2 age2 sex2 ecstat2 propcode]
expected_questions = %w[tenant_code age1 sex1 ecstat1 other_hhmemb relat2 age2 sex2 ecstat2 propcode]
expect(sub_section.questions.map(&:id)).to eq(expected_questions)
end
@ -58,9 +58,9 @@ RSpec.describe Form::Subsection, type: :model do
end
it "has question helpers for the number of applicable questions" do
expected_questions = %w[tenant_code age1 sex1 other_hhmemb propcode]
expected_questions = %w[tenant_code age1 sex1 ecstat1 other_hhmemb propcode]
expect(sub_section.applicable_questions(case_log).map(&:id)).to eq(expected_questions)
expect(sub_section.applicable_questions_count(case_log)).to eq(5)
expect(sub_section.applicable_questions_count(case_log)).to eq(6)
end
it "has question helpers for the number of answered questions" do
@ -79,7 +79,7 @@ RSpec.describe Form::Subsection, type: :model do
end
it "has a question helpers for the unanswered questions" do
expected_questions = %w[sex1 other_hhmemb propcode]
expected_questions = %w[sex1 ecstat1 other_hhmemb propcode]
expect(sub_section.unanswered_questions(case_log).map(&:id)).to eq(expected_questions)
end
end

2
spec/models/form_handler_spec.rb

@ -17,7 +17,7 @@ RSpec.describe FormHandler do
form_handler = described_class.instance
form = form_handler.get_form(test_form_name)
expect(form).to be_a(Form)
expect(form.pages.count).to eq(29)
expect(form.pages.count).to eq(31)
end
end

5
spec/models/form_spec.rb

@ -89,6 +89,7 @@ RSpec.describe Form, type: :model do
case_log.tenant_code = "123"
case_log.age1 = 35
case_log.sex1 = "Male"
case_log.ecstat1 = 0
case_log.other_hhmemb = 0
end
@ -131,7 +132,7 @@ RSpec.describe Form, type: :model do
describe "invalidated_page_questions" do
context "when dependencies are not met" do
let(:expected_invalid) { %w[la_known cbl conditional_question_no_second_question dependent_question layear declaration] }
let(:expected_invalid) { %w[la_known cbl conditional_question_no_second_question net_income_value_check dependent_question layear declaration] }
it "returns an array of question keys whose pages conditions are not met" do
expect(form.invalidated_page_questions(case_log).map(&:id).uniq).to eq(expected_invalid)
@ -139,7 +140,7 @@ RSpec.describe Form, type: :model do
end
context "with two pages having the same question and only one has dependencies met" do
let(:expected_invalid) { %w[la_known cbl conditional_question_no_second_question dependent_question layear declaration] }
let(:expected_invalid) { %w[la_known cbl conditional_question_no_second_question net_income_value_check dependent_question layear declaration] }
it "returns an array of question keys whose pages conditions are not met" do
case_log["preg_occ"] = "No"

2
spec/requests/form_controller_spec.rb

@ -240,7 +240,7 @@ RSpec.describe FormController, type: :request do
let(:page) { case_log.form.get_page("accessibility_requirements") }
it "updates both question fields" do
allow(page).to receive(:expected_responses).and_return(questions_for_page)
allow(page).to receive(:questions).and_return(questions_for_page)
post "/logs/#{case_log.id}/form", params: case_log_form_params
case_log.reload

54
spec/requests/soft_validations_controller_spec.rb

@ -1,54 +0,0 @@
require "rails_helper"
RSpec.describe SoftValidationsController, type: :request do
let(:params) { { case_log_id: case_log.id } }
let(:url) { "/logs/#{case_log.id}/net-income/soft-validations" }
let(:user) { FactoryBot.create(:user) }
context "when a user is not signed in" do
let(:case_log) { FactoryBot.create(:case_log, :in_progress) }
describe "GET #show" do
it "redirects to the sign in page" do
get url, headers: headers, params: {}
expect(response).to redirect_to("/users/sign-in")
end
end
end
context "when a user is signed in" do
before do
sign_in user
get url, params: {}
end
describe "GET #show" do
context "when a soft validation is triggered" do
let(:case_log) { FactoryBot.create(:case_log, :soft_validations_triggered) }
it "returns a success response" do
expect(response).to be_successful
end
it "returns a json with the soft validation fields" do
json_response = JSON.parse(response.body)
expect(json_response["show"]).to eq(true)
expect(json_response["label"]).to match(/Are you sure this is correct?/)
end
end
context "when no soft validation is triggered" do
let(:case_log) { FactoryBot.create(:case_log, :in_progress) }
it "returns a success response" do
expect(response).to be_successful
end
it "returns a json without the soft validation fields" do
json_response = JSON.parse(response.body)
expect(json_response["show"]).to eq(false)
end
end
end
end
end

2
spec/views/form/page_view_spec.rb

@ -7,7 +7,7 @@ RSpec.describe "form/page" do
let(:page) { form.get_page("net_income") }
let(:question) { page.questions.find { |q| q.id == "earnings" } }
let(:initial_page_attributes) { { description: nil, hide_subsection_label: nil } }
let(:initial_question_attributes) { { type: "numeric", answer_options: nil, prefix: nil, suffix: nil } }
let(:initial_question_attributes) { { type: "numeric", answer_options: nil, prefix: "£", suffix: " every week" } }
let(:page_attributes) { {} }
let(:question_attributes) { {} }

Loading…
Cancel
Save