Browse Source

CLDC-344: Validations (#36)

* Form with model so that we can validate (hypothetically)

* Put turbo frame back

* Turbo can update form errors if submitting returns 422

* Refactor and fix specs

* String interpolation over concatenation
pull/37/head
Daniel Baark 3 years ago committed by GitHub
parent
commit
5e201d3553
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      Gemfile.lock
  2. 27
      app/controllers/case_logs_controller.rb
  3. 6
      app/helpers/conditional_questions_helper.rb
  4. 2
      app/javascript/controllers/conditional_question_controller.js
  5. 2
      app/models/case_log.rb
  6. 10
      app/models/form.rb
  7. 9
      app/views/form/page.html.erb
  8. 3
      config/routes.rb
  9. 42
      spec/features/case_log_spec.rb

10
Gemfile.lock

@ -26,7 +26,7 @@ GIT
GIT GIT
remote: https://github.com/rspec/rspec-rails.git remote: https://github.com/rspec/rspec-rails.git
revision: 211d7d990e9762e229d8a86249b88c2a7604e8b0 revision: fdcd1df0b13f9b6547336b4d37dffb66f70f7228
branch: main branch: main
specs: specs:
rspec-rails (5.1.0.pre) rspec-rails (5.1.0.pre)
@ -143,7 +143,7 @@ GEM
ffi (1.15.4) ffi (1.15.4)
globalid (0.5.2) globalid (0.5.2)
activesupport (>= 5.0) activesupport (>= 5.0)
govuk-components (2.1.1) govuk-components (2.1.2)
activemodel (>= 6.0) activemodel (>= 6.0)
railties (>= 6.0) railties (>= 6.0)
view_component (~> 2.39.0) view_component (~> 2.39.0)
@ -175,9 +175,9 @@ GEM
minitest (5.14.4) minitest (5.14.4)
msgpack (1.4.2) msgpack (1.4.2)
nio4r (2.5.8) nio4r (2.5.8)
nokogiri (1.12.4-x86_64-darwin) nokogiri (1.12.5-x86_64-darwin)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.12.4-x86_64-linux) nokogiri (1.12.5-x86_64-linux)
racc (~> 1.4) racc (~> 1.4)
overcommit (0.58.0) overcommit (0.58.0)
childprocess (>= 0.6.3, < 5) childprocess (>= 0.6.3, < 5)
@ -294,7 +294,7 @@ GEM
actionpack (>= 4.0) actionpack (>= 4.0)
activesupport (>= 4.0) activesupport (>= 4.0)
sprockets (>= 3.0.0) sprockets (>= 3.0.0)
stimulus-rails (0.6.0) stimulus-rails (0.6.1)
rails (>= 6.0.0) rails (>= 6.0.0)
thor (1.1.0) thor (1.1.0)
turbo-rails (0.8.1) turbo-rails (0.8.1)

27
app/controllers/case_logs_controller.rb

@ -20,22 +20,19 @@ class CaseLogsController < ApplicationController
render :edit render :edit
end end
def next_page def submit_form
form = Form.new(2021, 2022) form = Form.new(2021, 2022)
@case_log = CaseLog.find(params[:case_log_id]) @case_log = CaseLog.find(params[:id])
previous_page = params[:previous_page] previous_page = params[:case_log][:previous_page]
questions_for_page = form.questions_for_page(previous_page).keys questions_for_page = form.questions_for_page(previous_page).keys
answers_for_page = page_params(questions_for_page).select { |k, _v| questions_for_page.include?(k) } answers_for_page = page_params(questions_for_page).select { |k, _v| questions_for_page.include?(k) }
@case_log.update!(answers_for_page) if @case_log.update(answers_for_page)
next_page = form.next_page(previous_page) redirect_path = form.next_page_redirect_path(previous_page)
redirect_path = if next_page == :check_answers redirect_to(send(redirect_path, @case_log))
subsection = form.subsection_for_page(previous_page) else
"case_log_#{subsection}_check_answers_path" page_info = form.all_pages[previous_page]
else render "form/page", locals: { form: form, page_key: previous_page, page_info: page_info }, status: :unprocessable_entity
"case_log_#{next_page}_path" end
end
redirect_to(send(redirect_path, @case_log))
end end
def check_answers def check_answers
@ -51,13 +48,13 @@ class CaseLogsController < ApplicationController
form.all_pages.map do |page_key, page_info| form.all_pages.map do |page_key, page_info|
define_method(page_key) do define_method(page_key) do
@case_log = CaseLog.find(params[:case_log_id]) @case_log = CaseLog.find(params[:case_log_id])
render "form/page", locals: { case_log_id: @case_log.id, form: form, page_key: page_key, page_info: page_info } render "form/page", locals: { form: form, page_key: page_key, page_info: page_info }
end end
end end
private private
def page_params(questions_for_page) def page_params(questions_for_page)
params.permit(questions_for_page) params.require(:case_log).permit(questions_for_page)
end end
end end

6
app/helpers/conditional_questions_helper.rb

@ -6,10 +6,6 @@ module ConditionalQuestionsHelper
end end
def display_question_key_div(page_info, question_key) def display_question_key_div(page_info, question_key)
if conditional_questions_for_page(page_info).include?(question_key) "style='display:none;'".html_safe if conditional_questions_for_page(page_info).include?(question_key)
"<div id=#{question_key}_div style='display:none;'>".html_safe
else
"<div id=#{question_key}_div>".html_safe
end
end end
end end

2
app/javascript/controllers/conditional_question_controller.js

@ -16,7 +16,7 @@ export default class extends Controller {
div.style.display = "block" div.style.display = "block"
} else { } else {
div.style.display = "none" div.style.display = "none"
let buttons = document.getElementsByName(key) let buttons = document.getElementsByName(`case_log[${key}]`)
Object.entries(buttons).forEach(([idx, button]) => { Object.entries(buttons).forEach(([idx, button]) => {
button.checked = false; button.checked = false;
}) })

2
app/models/case_log.rb

@ -1,3 +1,5 @@
class CaseLog < ApplicationRecord class CaseLog < ApplicationRecord
enum status: { "in progress" => 0, "submitted" => 1 } enum status: { "in progress" => 0, "submitted" => 1 }
# validates :tenant_age, presence: true
end end

10
app/models/form.rb

@ -53,6 +53,16 @@ class Form
pages_for_subsection(subsection).keys[previous_page_idx + 1] || :check_answers pages_for_subsection(subsection).keys[previous_page_idx + 1] || :check_answers
end end
def next_page_redirect_path(previous_page)
next_page = next_page(previous_page)
if next_page == :check_answers
subsection = subsection_for_page(previous_page)
"case_log_#{subsection}_check_answers_path"
else
"case_log_#{next_page}_path"
end
end
def previous_page(current_page) def previous_page(current_page)
subsection = subsection_for_page(current_page) subsection = subsection_for_page(current_page)
current_page_idx = pages_for_subsection(subsection).keys.index(current_page) current_page_idx = pages_for_subsection(subsection).keys.index(current_page)

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

@ -1,6 +1,6 @@
<% previous_page = form.previous_page(page_key) %> <% previous_page = form.previous_page(page_key) %>
<% content_for :before_content do %> <% content_for :before_content do %>
<%= govuk_back_link href: "/case_logs/#{case_log_id}/#{previous_page}" do %> <%= govuk_back_link href: "/case_logs/#{@case_log.id}/#{previous_page}" do %>
Back Back
<% end %> <% end %>
<% end %> <% end %>
@ -11,16 +11,15 @@
<%= page_info["header"] %> <%= page_info["header"] %>
</h1> </h1>
<% end %> <% end %>
<%= form_with action: '/case_logs', method: "next_page", builder: GOVUKDesignSystemFormBuilder::FormBuilder do |f| %> <%= form_with model: @case_log, method: "submit_form", builder: GOVUKDesignSystemFormBuilder::FormBuilder do |f| %>
<%= f.govuk_error_summary %>
<% page_info["questions"].map do |question_key, question| %> <% page_info["questions"].map do |question_key, question| %>
<%= display_question_key_div(page_info, question_key)%> <div id=<%= question_key + "_div " %><%= display_question_key_div(page_info, question_key) %> >
<%= render partial: "form/#{question["type"]}_question", locals: { question_key: question_key, question: question, f: f } %> <%= render partial: "form/#{question["type"]}_question", locals: { question_key: question_key, question: question, f: f } %>
</div> </div>
<% end %> <% end %>
<%= f.hidden_field :previous_page, value: page_key %> <%= f.hidden_field :previous_page, value: page_key %>
<%= f.hidden_field :case_log_id, value: case_log_id %>
<%= f.govuk_submit "Save and continue" %> <%= f.govuk_submit "Save and continue" %>
<% end %> <% end %>
<% end %> <% end %>

3
config/routes.rb

@ -3,11 +3,12 @@ Rails.application.routes.draw do
get "about", to: "about#index" get "about", to: "about#index"
get "/", to: "test#index" get "/", to: "test#index"
post '/case_logs/:id', to: "case_logs#submit_form"
form = Form.new(2021, 2022) form = Form.new(2021, 2022)
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}"
post page.to_s, to: "case_logs#next_page"
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"
end end

42
spec/features/case_log_spec.rb

@ -17,12 +17,12 @@ RSpec.describe "Test Features" do
def answer_all_questions_in_income_subsection def answer_all_questions_in_income_subsection
visit("/case_logs/#{empty_case_log.id}/net_income") visit("/case_logs/#{empty_case_log.id}/net_income")
fill_in("net_income", with: 18_000) fill_in("case-log-net-income-field", with: 18_000)
choose("net-income-frequency-yearly-field") choose("case-log-net-income-frequency-yearly-field")
click_button("Save and continue") click_button("Save and continue")
choose("net-income-uc-proportion-all-field") choose("case-log-net-income-uc-proportion-all-field")
click_button("Save and continue") click_button("Save and continue")
choose("housing-benefit-housing-benefit-but-not-universal-credit-field") choose("case-log-housing-benefit-housing-benefit-but-not-universal-credit-field")
click_button("Save and continue") click_button("Save and continue")
end end
@ -80,19 +80,19 @@ RSpec.describe "Test Features" do
it "displays the household questions when you click into that section" do it "displays the household questions when you click into that section" do
visit("/case_logs/#{id}") visit("/case_logs/#{id}")
click_link("Household characteristics") click_link("Household characteristics")
expect(page).to have_field("tenant-code-field") expect(page).to have_field("case-log-tenant-code-field")
click_button("Save and continue") click_button("Save and continue")
expect(page).to have_field("tenant-age-field") expect(page).to have_field("case-log-tenant-age-field")
click_button("Save and continue") click_button("Save and continue")
expect(page).to have_field("tenant-gender-male-field") expect(page).to have_field("case-log-tenant-gender-male-field")
visit page.driver.request.env["HTTP_REFERER"] visit page.driver.request.env["HTTP_REFERER"]
expect(page).to have_field("tenant-age-field") expect(page).to have_field("case-log-tenant-age-field")
end end
describe "form questions" do describe "form questions" do
it "can be accessed by url" do it "can be accessed by url" do
visit("/case_logs/#{id}/tenant_age") visit("/case_logs/#{id}/tenant_age")
expect(page).to have_field("tenant-age-field") expect(page).to have_field("case-log-tenant-age-field")
end end
it "updates model attributes correctly for each question" do it "updates model attributes correctly for each question" do
@ -103,11 +103,11 @@ RSpec.describe "Test Features" do
visit("/case_logs/#{id}/#{question}") visit("/case_logs/#{id}/#{question}")
case type case type
when "text" when "text"
fill_in(question.to_s, with: answer) fill_in("case-log-#{question.to_s.dasherize}-field", with: answer)
when "radio" when "radio"
choose("#{question.to_s.dasherize}-#{answer.parameterize}-field") choose("case-log-#{question.to_s.dasherize}-#{answer.parameterize}-field")
else else
fill_in(question.to_s, with: answer) fill_in("case-log-#{question.to_s.dasherize}-field", with: answer)
end end
expect { click_button("Save and continue") }.to change { expect { click_button("Save and continue") }.to change {
case_log.reload.send(question.to_s) case_log.reload.send(question.to_s)
@ -126,7 +126,7 @@ RSpec.describe "Test Features" do
it "go back to tenant code page from tenant age page" do it "go back to tenant code page from tenant age page" do
visit("/case_logs/#{id}/tenant_age") visit("/case_logs/#{id}/tenant_age")
click_link(text: "Back") click_link(text: "Back")
expect(page).to have_field("tenant-code-field") expect(page).to have_field("case-log-tenant-code-field")
end end
end end
end end
@ -150,7 +150,7 @@ RSpec.describe "Test Features" do
context "when the user needs to check their answers for a subsection" do context "when the user needs to check their answers for a subsection" do
def fill_in_number_question(case_log_id, question, value) def fill_in_number_question(case_log_id, question, value)
visit("/case_logs/#{case_log_id}/#{question}") visit("/case_logs/#{case_log_id}/#{question}")
fill_in(question.to_s, with: value) fill_in("case-log-#{question.to_s.dasherize}-field", with: value)
click_button("Save and continue") click_button("Save and continue")
end end
@ -175,7 +175,7 @@ RSpec.describe "Test Features" do
it "should display answers given by the user for the question in the subsection" do it "should display answers given by the user for the question in the subsection" do
fill_in_number_question(empty_case_log.id, "tenant_age", 28) fill_in_number_question(empty_case_log.id, "tenant_age", 28)
choose("tenant-gender-non-binary-field") choose("case-log-tenant-gender-non-binary-field")
click_button("Save and continue") click_button("Save and continue")
visit("/case_logs/#{empty_case_log.id}/#{subsection}/check_answers") visit("/case_logs/#{empty_case_log.id}/#{subsection}/check_answers")
expect(page).to have_content("28") expect(page).to have_content("28")
@ -229,14 +229,14 @@ RSpec.describe "Test Features" do
it "shows conditional questions if the required answer is selected and hides it again when a different answer option is selected", js: true do it "shows conditional questions if the required answer is selected and hides it again when a different answer option is selected", js: true do
visit("/case_logs/#{id}/armed_forces") visit("/case_logs/#{id}/armed_forces")
# Something about our styling makes the selenium webdriver think the actual radio buttons are not visible so we allow label click here # Something about our styling makes the selenium webdriver think the actual radio buttons are not visible so we allow label click here
choose("armed-forces-yes-a-regular-field", allow_label_click: true) choose("case-log-armed-forces-yes-a-regular-field", allow_label_click: true)
expect(page).to have_selector("#armed_forces_injured_div") expect(page).to have_selector("#armed_forces_injured_div")
choose("armed-forces-injured-no-field", allow_label_click: true) choose("case-log-armed-forces-injured-no-field", allow_label_click: true)
expect(find_field("armed-forces-injured-no-field", visible: false).checked?).to be_truthy expect(find_field("case-log-armed-forces-injured-no-field", visible: false).checked?).to be_truthy
choose("armed-forces-no-field", allow_label_click: true) choose("case-log-armed-forces-no-field", allow_label_click: true)
expect(page).not_to have_selector("#armed_forces_injured_div") expect(page).not_to have_selector("#armed_forces_injured_div")
choose("armed-forces-yes-a-regular-field", allow_label_click: true) choose("case-log-armed-forces-yes-a-regular-field", allow_label_click: true)
expect(find_field("armed-forces-injured-no-field", visible: false).checked?).to be_falsey expect(find_field("case-log-armed-forces-injured-no-field", visible: false).checked?).to be_falsey
end end
end end
end end

Loading…
Cancel
Save