Browse Source

Soft validations with controllers (#77)

* Working except for selenium checkbox issues

* Remove old debug message

* Make sure we only run the stimulus init once per page load

* Revert that

* Rubocop

* Use data targets rather than id lookup, so that coupling is explicit

* Use fetch

* Schema update

* No schema changes introduced here

* Add a request spec for the new controller

* Schema order did change

* map over forEach

* Update factory fields

* Int for booleans :(

* Fix spec fields

* Lazy create
pull/80/head
Daniel Baark 3 years ago committed by GitHub
parent
commit
3a753364eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      Gemfile.lock
  2. 4
      app/controllers/case_logs_controller.rb
  3. 20
      app/controllers/soft_validations_controller.rb
  4. 7
      app/javascript/controllers/conditional_question_controller.js
  5. 28
      app/javascript/controllers/soft_validations_controller.js
  6. 2
      app/validations/soft_validations.rb
  7. 17
      app/views/form/_validation_override_question.html.erb
  8. 8
      app/views/form/page.html.erb
  9. 1
      config/routes.rb
  10. 9
      db/migrate/20211105164644_change_net_income_overide.rb
  11. 4
      db/schema.rb
  12. 6
      spec/factories/case_log.rb
  13. 17
      spec/features/case_log_spec.rb
  14. 39
      spec/requests/soft_validations_controller_spec.rb

8
Gemfile.lock

@ -1,6 +1,6 @@
GIT GIT
remote: https://github.com/rspec/rspec-core.git remote: https://github.com/rspec/rspec-core.git
revision: d57c371ee92b16211b80ac7b0b025968438f5297 revision: 42a9fe3a2dc9d5e68811ee646f4b9b4349c18b24
branch: main branch: main
specs: specs:
rspec-core (3.11.0.pre) rspec-core (3.11.0.pre)
@ -40,7 +40,7 @@ GIT
GIT GIT
remote: https://github.com/rspec/rspec-support.git remote: https://github.com/rspec/rspec-support.git
revision: 2a17194b9fc868a4b4a41d7168103dca825c3004 revision: 9499cb622f9feef43a53f357809f9e656e7cb6de
branch: main branch: main
specs: specs:
rspec-support (3.11.0.pre) rspec-support (3.11.0.pre)
@ -182,7 +182,7 @@ GEM
rails (>= 6.0.0) rails (>= 6.0.0)
stimulus-rails stimulus-rails
turbo-rails turbo-rails
i18n (1.8.10) i18n (1.8.11)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
inherited_resources (1.13.0) inherited_resources (1.13.0)
actionpack (>= 5.2, < 6.2) actionpack (>= 5.2, < 6.2)
@ -309,7 +309,7 @@ GEM
rubocop-rails (= 2.12.2) rubocop-rails (= 2.12.2)
rubocop-rake (= 0.6.0) rubocop-rake (= 0.6.0)
rubocop-rspec (= 2.4.0) rubocop-rspec (= 2.4.0)
rubocop-performance (1.11.5) rubocop-performance (1.12.0)
rubocop (>= 1.7.0, < 2.0) rubocop (>= 1.7.0, < 2.0)
rubocop-ast (>= 0.4.0) rubocop-ast (>= 0.4.0)
rubocop-rails (2.12.2) rubocop-rails (2.12.2)

4
app/controllers/case_logs_controller.rb

@ -105,9 +105,9 @@ private
question_params = params["case_log"][question_key] question_params = params["case_log"][question_key]
next unless question_params next unless question_params
if question_info["type"] == "checkbox" if %w[checkbox validation_override].include?(question_info["type"])
question_info["answer_options"].keys.reject { |x| x.match(/divider/) }.each do |option| question_info["answer_options"].keys.reject { |x| x.match(/divider/) }.each do |option|
result[option] = question_params.include?(option) ? "Yes" : "No" result[option] = question_params.include?(option) ? 1 : 0
end end
else else
result[question_key] = question_params result[question_key] = question_params

20
app/controllers/soft_validations_controller.rb

@ -0,0 +1,20 @@
class SoftValidationsController < ApplicationController
def show
@case_log = CaseLog.find(params[:case_log_id])
page_key = request.env["PATH_INFO"].split("/")[-2]
form = FormHandler.instance.get_form("2021_2022")
page = form.all_pages[page_key]
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"]&.keys&.first
end
end

7
app/javascript/controllers/conditional_question_controller.js

@ -12,7 +12,6 @@ export default class extends Controller {
case "radio": case "radio":
this.displayConditionalRadio() this.displayConditionalRadio()
default: default:
console.log("Not yet implemented for " + this.element.type)
break; break;
} }
} }
@ -22,7 +21,7 @@ export default class extends Controller {
let selectedValue = this.element.value let selectedValue = this.element.value
let conditional_for = JSON.parse(this.element.dataset.info) let conditional_for = JSON.parse(this.element.dataset.info)
Object.entries(conditional_for).forEach(([targetQuestion, conditions]) => { Object.entries(conditional_for).map(([targetQuestion, conditions]) => {
let div = document.getElementById(targetQuestion + "_div") let div = document.getElementById(targetQuestion + "_div")
if(conditions.includes(selectedValue)) { if(conditions.includes(selectedValue)) {
div.style.display = "block" div.style.display = "block"
@ -32,7 +31,7 @@ export default class extends Controller {
if (buttons.length == 0){ if (buttons.length == 0){
buttons = document.getElementsByName(`case_log[${targetQuestion}][]`); buttons = document.getElementsByName(`case_log[${targetQuestion}][]`);
} }
Object.entries(buttons).forEach(([idx, button]) => { Object.entries(buttons).map(([idx, button]) => {
button.checked = false; button.checked = false;
}) })
} }
@ -44,7 +43,7 @@ export default class extends Controller {
let enteredValue = this.element.value let enteredValue = this.element.value
let conditional_for = JSON.parse(this.element.dataset.info) let conditional_for = JSON.parse(this.element.dataset.info)
Object.entries(conditional_for).forEach(([targetQuestion, condition]) => { Object.entries(conditional_for).map(([targetQuestion, condition]) => {
let div = document.getElementById(targetQuestion + "_div") let div = document.getElementById(targetQuestion + "_div")
if(eval((enteredValue + condition))) { if(eval((enteredValue + condition))) {
div.style.display = "block" div.style.display = "block"

28
app/javascript/controllers/soft_validations_controller.js

@ -0,0 +1,28 @@
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
})
}
}
)
}
}

2
app/validations/soft_validations.rb

@ -8,7 +8,7 @@ module SoftValidations
end end
def soft_errors_overridden? def soft_errors_overridden?
public_send(soft_errors.keys.first) if soft_errors.present? !public_send(soft_errors.keys.first).zero? if soft_errors.present?
end end
private private

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

@ -1,11 +1,14 @@
<div class="govuk-form-group govuk-form-group--error"> <div class="govuk-form-group govuk-form-group--error"
<%= f.govuk_check_boxes_fieldset @case_log.soft_errors.keys.first, data-controller="soft-validations"
legend: { text: @case_log.soft_errors.values.first.message, size: "l" }, data-soft-validations-target="override"
hint: { text: @case_log.soft_errors.values.first.hint_text } do %> style='display:none;'>
<%= f.govuk_check_box @case_log.soft_errors.keys.first, @case_log.soft_errors.keys.first, <%= f.govuk_check_boxes_fieldset page_info["soft_validations"]&.keys&.first,
label: { text: "Yes" }, legend: { text: "soft-validations-placeholder-message", size: "l" },
checked: f.object.send(@case_log.soft_errors.keys.first) hint: { text: "soft-validations-placeholder-hint-text" } do %>
<%= f.govuk_check_box page_info["soft_validations"]&.keys&.first, page_info["soft_validations"]&.keys&.first,
label: { text: "Yes" }
%> %>
<% end %> <% end %>
</div> </div>

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

@ -1,7 +1,5 @@
<meta http-equiv="Pragma" content="no-cache">
<% content_for :before_content do %> <% content_for :before_content do %>
<%= link_to 'Back', :back, class: "govuk-back-link" %> <%= link_to 'Back', 'javascript:history.back()', class: "govuk-back-link" %>
<% end %> <% end %>
<%= turbo_frame_tag "case_log_form", target: "_top" do %> <%= turbo_frame_tag "case_log_form", target: "_top" do %>
@ -19,8 +17,8 @@
</div> </div>
<% end %> <% end %>
<% if @case_log.soft_errors.present? && @case_log.soft_errors.keys.first == page_info["soft_validations"]&.keys&.first %> <% if page_info["soft_validations"]&.keys&.first %>
<%= render partial: "form/validation_override_question", locals: { f: f } %> <%= render partial: "form/validation_override_question", locals: { f: f, page_key: page_key, page_info: page_info } %>
<% end %> <% end %>
<%= f.hidden_field :page, value: page_key %> <%= f.hidden_field :page, value: page_key %>

1
config/routes.rb

@ -11,6 +11,7 @@ Rails.application.routes.draw do
resources :case_logs do resources :case_logs do
form.all_pages.keys.map do |page| form.all_pages.keys.map do |page|
get page.to_s, to: "case_logs##{page}" get page.to_s, to: "case_logs##{page}"
get "#{page}/soft_validations", to: "soft_validations#show"
end end
form.all_subsections.keys.map do |subsection| form.all_subsections.keys.map do |subsection|
get "#{subsection}/check_answers", to: "case_logs#check_answers" get "#{subsection}/check_answers", to: "case_logs#check_answers"

9
db/migrate/20211105164644_change_net_income_overide.rb

@ -0,0 +1,9 @@
class ChangeNetIncomeOveride < ActiveRecord::Migration[6.1]
def up
change_column :case_logs, :override_net_income_validation, "integer USING CAST(override_net_income_validation AS integer)"
end
def down
change_column :case_logs, :override_net_income_validation, "boolean USING override_net_income_validation::boolean"
end
end

4
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_11_03_090530) do ActiveRecord::Schema.define(version: 2021_11_05_164644) 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"
@ -133,7 +133,7 @@ ActiveRecord::Schema.define(version: 2021_11_03_090530) do
t.integer "rp_dontknow" t.integer "rp_dontknow"
t.datetime "discarded_at" t.datetime "discarded_at"
t.string "tenancyother" t.string "tenancyother"
t.boolean "override_net_income_validation" t.integer "override_net_income_validation"
t.string "net_income_known" t.string "net_income_known"
t.string "gdpr_acceptance" t.string "gdpr_acceptance"
t.string "gdpr_declined" t.string "gdpr_declined"

6
spec/factories/case_log.rb

@ -13,6 +13,12 @@ FactoryBot.define do
tenant_code { "BZ737" } tenant_code { "BZ737" }
property_postcode { "NW1 7TY" } property_postcode { "NW1 7TY" }
end end
trait :soft_validations_triggered do
status { 1 }
ecstat1 { "Full-time - 30 hours or more" }
earnings { 750 }
incfreq { "Weekly" }
end
created_at { Time.zone.now } created_at { Time.zone.now }
updated_at { Time.zone.now } updated_at { Time.zone.now }
end end

17
spec/features/case_log_spec.rb

@ -217,6 +217,17 @@ RSpec.describe "Test Features" do
click_link(text: "Back") click_link(text: "Back")
expect(page).to have_field("case-log-tenant-code-field") expect(page).to have_field("case-log-tenant-code-field")
end end
it "doesn't get stuck in infinite loops", js: true do
visit("/case_logs")
visit("/case_logs/#{id}/net_income")
fill_in("case-log-earnings-field", with: 740)
choose("case-log-incfreq-weekly-field", allow_label_click: true)
click_button("Save and continue")
click_link(text: "Back")
click_link(text: "Back")
expect(page).to have_current_path("/case_logs")
end
end end
end end
@ -231,7 +242,7 @@ RSpec.describe "Test Features" do
end end
end end
context "when changing an answer from the check answers page" do context "when changing an answer from the check answers page", js: true do
it "the back button routes correctly" do it "the back button routes correctly" do
visit("/case_logs/#{id}/household_characteristics/check_answers") visit("/case_logs/#{id}/household_characteristics/check_answers")
first("a", text: /Answer/).click first("a", text: /Answer/).click
@ -385,11 +396,11 @@ RSpec.describe "Test Features" do
describe "Soft Validation" do describe "Soft Validation" do
context "given a weekly net income that is above the expected amount for the given economic status but below the hard max" do context "given a weekly net income that is above the expected amount for the given economic status but below the hard max" do
let!(:case_log) { FactoryBot.create(:case_log, :in_progress, ecstat1: "Full-time - 30 hours or more") } let(:case_log) { FactoryBot.create(:case_log, :in_progress, ecstat1: "Full-time - 30 hours or more") }
let(:income_over_soft_limit) { 750 } let(:income_over_soft_limit) { 750 }
let(:income_under_soft_limit) { 700 } let(:income_under_soft_limit) { 700 }
it "prompts the user to confirm the value is correct" do it "prompts the user to confirm the value is correct", js: true do
visit("/case_logs/#{case_log.id}/net_income") visit("/case_logs/#{case_log.id}/net_income")
fill_in("case-log-earnings-field", with: income_over_soft_limit) fill_in("case-log-earnings-field", with: income_over_soft_limit)
choose("case-log-incfreq-weekly-field", allow_label_click: true) choose("case-log-incfreq-weekly-field", allow_label_click: true)

39
spec/requests/soft_validations_controller_spec.rb

@ -0,0 +1,39 @@
require "rails_helper"
RSpec.describe SoftValidationsController, type: :request do
let(:params) { { case_log_id: case_log.id } }
let(:url) { "/case_logs/#{case_log.id}/net_income/soft_validations" }
before do
get url, params: {}
end
describe "GET #show" do
context "Soft validation overide required" 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 "Soft validation overide not required" 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 with the soft validation fields" do
json_response = JSON.parse(response.body)
expect(json_response["show"]).to eq(false)
end
end
end
end
Loading…
Cancel
Save