Browse Source

WIP: CLDC:680 - Refactor feature specs into smaller units (#109)

* Refactor feature specs into smaller units

* DRY spec helpers

* Bump Turbo

* Use database ID assignment everywhere we need ID to reduce flakiness

* Update gemfile.lock

* Email is unique

* Selenium deprecation

* Don't need let bang

* Remove the uneeded extra options

* Remove flaky test mitigations that didn't work

* Use lockstep to remove test flakiness

* Dry up condition evaluation

* Spec admin user edit form

* Test income refused derivation

* Set valid incref

* Test Case Log deletion failures

* Test other household member age validations

* Test all reasonable preference validation paths
pull/111/head
Daniel Baark 3 years ago committed by GitHub
parent
commit
7b17b38547
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      Gemfile
  2. 28
      Gemfile.lock
  3. 14
      app/javascript/controllers/soft_validations_controller.js
  4. 1
      app/models/case_log.rb
  5. 6
      app/models/form/question.rb
  6. 1
      app/views/layouts/application.html.erb
  7. 12
      spec/controllers/admin/admin_users_controller_spec.rb
  8. 2
      spec/factories/admin_user.rb
  9. 3
      spec/factories/case_log.rb
  10. 2
      spec/factories/user.rb
  11. 508
      spec/features/case_log_spec.rb
  12. 123
      spec/features/form/check_answers_page_spec.rb
  13. 30
      spec/features/form/conditional_questions_spec.rb
  14. 80
      spec/features/form/form_navigation_spec.rb
  15. 18
      spec/features/form/helpers.rb
  16. 38
      spec/features/form/page_routing_spec.rb
  17. 89
      spec/features/form/saving_data_spec.rb
  18. 53
      spec/features/form/tasklist_page_spec.rb
  19. 139
      spec/features/form/validations_spec.rb
  20. 10
      spec/features/user_spec.rb
  21. 4
      spec/helpers/tasklist_helper_spec.rb
  22. 20
      spec/models/case_log_spec.rb
  23. 59
      spec/rails_helper.rb
  24. 51
      spec/requests/case_log_controller_spec.rb
  25. 4
      spec/views/case_log_index_view_spec.rb

4
Gemfile

@ -13,8 +13,6 @@ gem "pg", "~> 1.1"
gem "puma", "~> 5.0"
# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
gem "webpacker", "~> 5.0"
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem "jbuilder", "~> 2.7"
# Reduces boot times through caching; required in config/boot.rb
gem "bootsnap", ">= 1.4.4", require: false
# Gov.UK frontend components
@ -61,7 +59,7 @@ end
group :test do
gem "capybara", require: false
gem "database_cleaner-active_record", require: false
gem "capybara-lockstep"
gem "factory_bot_rails"
gem "selenium-webdriver", require: false
gem "simplecov", require: false

28
Gemfile.lock

@ -26,7 +26,7 @@ GIT
GIT
remote: https://github.com/rspec/rspec-rails.git
revision: d3e7b85877fcbcec63f8a76434d8750e7f3b7aef
revision: 1fe6c2e8a56f46ae4c2f13ffa0733cce3b717c2d
branch: main
specs:
rspec-rails (5.1.0.pre)
@ -124,7 +124,7 @@ GEM
ast (2.4.2)
bcrypt (3.1.16)
bindex (0.8.1)
bootsnap (1.9.1)
bootsnap (1.9.3)
msgpack (~> 1.0)
builder (3.2.4)
byebug (11.1.3)
@ -137,15 +137,16 @@ GEM
rack-test (>= 0.6.3)
regexp_parser (>= 1.5, < 3.0)
xpath (~> 3.2)
capybara-lockstep (1.1.0)
activesupport (>= 3.2)
capybara (>= 2.0)
ruby2_keywords
selenium-webdriver (>= 3)
chartkick (4.1.2)
childprocess (4.1.0)
coderay (1.1.3)
concurrent-ruby (1.1.9)
crass (1.0.6)
database_cleaner-active_record (2.0.1)
activerecord (>= 5.a)
database_cleaner-core (~> 2.0.0)
database_cleaner-core (2.0.1)
deep_merge (1.2.1)
devise (4.8.0)
bcrypt (~> 3.0)
@ -171,7 +172,7 @@ GEM
formtastic (4.0.0)
actionpack (>= 5.2.0)
formtastic_i18n (0.7.0)
globalid (0.5.2)
globalid (0.6.0)
activesupport (>= 5.0)
govuk-components (2.1.4)
activemodel (>= 6.0)
@ -197,8 +198,6 @@ GEM
railties (>= 5.2, < 6.2)
responders (>= 2, < 4)
iniparse (1.5.0)
jbuilder (2.11.3)
activesupport (>= 5.0.0)
jquery-rails (4.4.0)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
@ -345,7 +344,7 @@ GEM
sass (~> 3.5, >= 3.5.5)
scss_lint-govuk (0.2.0)
scss_lint
selenium-webdriver (4.0.3)
selenium-webdriver (4.1.0)
childprocess (>= 0.5, < 5.0)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2)
@ -359,14 +358,14 @@ GEM
sprockets (4.0.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.4.0)
sprockets-rails (3.4.1)
actionpack (>= 5.2)
activesupport (>= 5.2)
sprockets (>= 3.0.0)
stimulus-rails (0.7.2)
stimulus-rails (0.7.3)
rails (>= 6.0.0)
thor (1.1.0)
turbo-rails (0.8.3)
turbo-rails (0.9.0)
rails (>= 6.0.0)
tzinfo (2.0.4)
concurrent-ruby (~> 1.0)
@ -403,8 +402,8 @@ DEPENDENCIES
bootsnap (>= 1.4.4)
byebug
capybara
capybara-lockstep
chartkick
database_cleaner-active_record
devise
discard
dotenv-rails
@ -412,7 +411,6 @@ DEPENDENCIES
govuk-components
govuk_design_system_formbuilder
hotwire-rails
jbuilder (~> 2.7)
json-schema
listen (~> 3.3)
overcommit (>= 0.37.0)

14
app/javascript/controllers/soft_validations_controller.js

@ -5,13 +5,8 @@ export default class extends Controller {
initialize() {
let url = window.location.href + "/soft_validations"
this.fetch_retry(url, { headers: { accept: "application/json" } }, 5)
}
fetch_retry(url, options, n) {
let self = this
let div = this.overrideTarget
fetch(url, options)
fetch(url, { headers: { accept: "application/json" } })
.then(response => response.json())
.then((response) => {
if(response["show"]){
@ -27,10 +22,7 @@ export default class extends Controller {
button.checked = false
})
}
})
.catch(function(error) {
if (n === 1) throw error
return self.fetch_retry(url, options, n - 1)
})
}
)
}
}

1
app/models/case_log.rb

@ -113,6 +113,7 @@ class CaseLog < ApplicationRecord
enum first_time_property_let_as_social_housing: DbEnums.polar, _suffix: true
enum unitletas: DbEnums.unitletas, _suffix: true
enum builtype: DbEnums.builtype, _suffix: true
enum incref: DbEnums.polar, _suffix: true
AUTOGENERATED_FIELDS = %w[id status created_at updated_at discarded_at].freeze
OPTIONAL_FIELDS = %w[do_you_know_the_postcode

6
app/models/form/question.rb

@ -67,11 +67,7 @@ private
operator = condition[:cond][/[<>=]+/].to_sym
operand = condition[:cond][/\d+/].to_i
case_log[condition[:from]].present? && case_log[condition[:from]].send(operator, operand)
when "text"
case_log[condition[:from]].present? && condition[:cond].include?(case_log[condition[:from]])
when "radio"
case_log[condition[:from]].present? && condition[:cond].include?(case_log[condition[:from]])
when "select"
when "text", "radio", "select"
case_log[condition[:from]].present? && condition[:cond].include?(case_log[condition[:from]])
else
raise "Not implemented yet"

1
app/views/layouts/application.html.erb

@ -15,6 +15,7 @@
<%= favicon_link_tag asset_pack_path('media/images/govuk-apple-touch-icon-180x180.png'), rel: 'apple-touch-icon', type: 'image/png', size: '180x180' %>
<%= stylesheet_pack_tag 'application', media: 'all' %>
<%= javascript_pack_tag 'application', defer: true %>
<%= capybara_lockstep if defined?(Capybara::Lockstep) %>
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>

12
spec/controllers/admin/admin_users_controller_spec.rb

@ -27,4 +27,16 @@ describe Admin::AdminUsersController, type: :controller do
expect { post :create, session: valid_session, params: params }.to change(AdminUser, :count).by(1)
end
end
describe "Update admin users" do
before do
get :edit, session: valid_session, params: { id: AdminUser.first.id }
end
it "creates a new admin users" do
expect(page).to have_field("admin_user_email")
expect(page).to have_field("admin_user_password")
expect(page).to have_field("admin_user_password_confirmation")
end
end
end

2
spec/factories/admin_user.rb

@ -1,6 +1,6 @@
FactoryBot.define do
factory :admin_user do
email { "admin@example.com" }
sequence(:email) { |i| "admin#{i}@example.com" }
password { "pAssword1" }
created_at { Time.zone.now }
updated_at { Time.zone.now }

3
spec/factories/case_log.rb

@ -1,6 +1,5 @@
FactoryBot.define do
factory :case_log do
sequence(:id) { |i| i }
trait :in_progress do
status { 1 }
tenant_code { "TH356" }
@ -130,7 +129,7 @@ FactoryBot.define do
mrcday { 5 }
mrcmonth { 5 }
mrcyear { 2020 }
incref { 554_355 }
incref { 0 }
sale_completion_date { nil }
startdate { Time.zone.now }
armedforces { 1 }

2
spec/factories/user.rb

@ -1,6 +1,6 @@
FactoryBot.define do
factory :user do
email { "test@example.com" }
sequence(:email) { |i| "test#{i}@example.com" }
password { "pAssword1" }
created_at { Time.zone.now }
updated_at { Time.zone.now }

508
spec/features/case_log_spec.rb

@ -1,508 +0,0 @@
require "rails_helper"
RSpec.describe "Form Features" do
let!(:case_log) { FactoryBot.create(:case_log, :in_progress) }
let!(:empty_case_log) { FactoryBot.create(:case_log) }
let(:id) { case_log.id }
let(:status) { case_log.status }
before do
allow_any_instance_of(CaseLogsController).to receive(:authenticate_user!).and_return(true)
end
question_answers = {
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" },
other_hhmemb: { type: "numeric", answer: 2, path: "household_number_of_other_members" },
}
def fill_in_number_question(case_log_id, question, value, path)
visit("/case_logs/#{case_log_id}/#{path}")
fill_in("case-log-#{question.to_s.dasherize}-field", with: value)
click_button("Save and continue")
end
def answer_all_questions_in_income_subsection
visit("/case_logs/#{empty_case_log.id}/net_income")
fill_in("case-log-earnings-field", with: 18_000)
choose("case-log-incfreq-yearly-field")
click_button("Save and continue")
choose("case-log-benefits-all-field")
click_button("Save and continue")
choose("case-log-hb-prefer-not-to-say-field")
click_button("Save and continue")
end
describe "Create new log" do
it "redirects to the task list for the new log" do
visit("/case_logs")
click_link("Create new log")
id = CaseLog.order(created_at: :desc).first.id
expect(page).to have_content("Tasklist for log #{id}")
end
end
describe "Viewing a log" do
context "tasklist page" do
it "displays a tasklist header" do
visit("/case_logs/#{id}")
expect(page).to have_content("Tasklist for log #{id}")
expect(page).to have_content("This submission is #{status.humanize.downcase}")
end
it "displays a section status" do
visit("/case_logs/#{empty_case_log.id}")
assert_selector ".govuk-tag", text: /Not started/, count: 8
assert_selector ".govuk-tag", text: /Completed/, count: 0
assert_selector ".govuk-tag", text: /Cannot start yet/, count: 1
end
it "shows the correct status if one section is completed" do
answer_all_questions_in_income_subsection
visit("/case_logs/#{empty_case_log.id}")
assert_selector ".govuk-tag", text: /Not started/, count: 7
assert_selector ".govuk-tag", text: /Completed/, count: 1
assert_selector ".govuk-tag", text: /Cannot start yet/, count: 1
end
it "skips to the first section if no answers are completed" do
visit("/case_logs/#{empty_case_log.id}")
expect(page).to have_link("Skip to next incomplete section", href: /#household_characteristics/)
end
it "shows the number of completed sections if no sections are completed" do
visit("/case_logs/#{empty_case_log.id}")
expect(page).to have_content("You've completed 0 of 9 sections.")
end
it "shows the number of completed sections if one section is completed" do
answer_all_questions_in_income_subsection
visit("/case_logs/#{empty_case_log.id}")
expect(page).to have_content("You've completed 1 of 9 sections.")
end
end
describe "form questions" do
let(:case_log_with_checkbox_questions_answered) do
FactoryBot.create(
:case_log, :in_progress,
housingneeds_a: "Yes",
housingneeds_c: "Yes"
)
end
it "can be accessed by url" do
visit("/case_logs/#{id}/person_1_age")
expect(page).to have_field("case-log-age1-field")
end
it "updates model attributes correctly for each question" do
question_answers.each do |question, hsh|
type = hsh[:type]
answer = hsh[:answer]
path = hsh[:path]
original_value = case_log.send(question)
visit("/case_logs/#{id}/#{path}")
case type
when "text"
fill_in("case-log-#{question.to_s.dasherize}-field", with: answer)
when "radio"
choose("case-log-#{question.to_s.dasherize}-#{answer.parameterize}-field")
else
fill_in("case-log-#{question.to_s.dasherize}-field", with: answer)
end
expect { click_button("Save and continue") }.to change {
case_log.reload.send(question.to_s)
}.from(original_value).to(answer)
end
end
it "updates total value of the rent", js: true do
visit("/case_logs/#{id}/rent")
fill_in("case-log-brent-field", with: 3)
expect(page).to have_field("case-log-tcharge-field", with: "3")
fill_in("case-log-scharge-field", with: 2)
expect(page).to have_field("case-log-tcharge-field", with: "5")
fill_in("case-log-pscharge-field", with: 1)
expect(page).to have_field("case-log-tcharge-field", with: "6")
fill_in("case-log-supcharg-field", with: 4)
expect(page).to have_field("case-log-tcharge-field", with: "10")
end
it "displays number answers in inputs if they are already saved" do
visit("/case_logs/#{id}/property_postcode")
expect(page).to have_field("case-log-property-postcode-field", with: "P0 5ST")
end
it "displays text answers in inputs if they are already saved" do
visit("/case_logs/#{id}/person_1_age")
expect(page).to have_field("case-log-age1-field", with: "17")
end
it "displays checkbox answers in inputs if they are already saved" do
visit("/case_logs/#{case_log_with_checkbox_questions_answered.id}/accessibility_requirements")
# Something about our styling makes the selenium webdriver think the actual radio buttons are not visible so we pass false here
expect(page).to have_checked_field(
"case-log-accessibility-requirements-housingneeds-a-field",
visible: false,
)
expect(page).to have_unchecked_field(
"case-log-accessibility-requirements-housingneeds-b-field",
visible: false,
)
expect(page).to have_checked_field(
"case-log-accessibility-requirements-housingneeds-c-field",
visible: false,
)
end
end
describe "Back link directs correctly", js: true do
it "go back to tasklist page from tenant code" do
visit("/case_logs/#{id}")
visit("/case_logs/#{id}/tenant_code")
click_link(text: "Back")
expect(page).to have_content("Tasklist for log #{id}")
end
it "go back to tenant code page from tenant age page", js: true do
visit("/case_logs/#{id}/tenant_code")
click_button("Save and continue")
visit("/case_logs/#{id}/person_1_age")
click_link(text: "Back")
expect(page).to have_field("case-log-tenant-code-field")
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
describe "Form flow is correct" do
context "given an ordered list of pages" do
it "leads to the next one in the correct order" do
pages = question_answers.map { |_key, val| val[:path] }
pages[0..-2].each_with_index do |val, index|
visit("/case_logs/#{id}/#{val}")
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/#{pages[index + 1]}")
end
end
context "when changing an answer from the check answers page", js: true do
it "the back button routes correctly" do
visit("/case_logs/#{id}/household_characteristics/check_answers")
first("a", text: /Answer/).click
click_link("Back")
expect(page).to have_current_path("/case_logs/#{id}/household_characteristics/check_answers")
end
end
end
end
describe "check answers page" do
let(:subsection) { "household_characteristics" }
let(:conditional_subsection) { "conditional_question" }
context "when the user needs to check their answers for a subsection" do
it "can be visited by URL" do
visit("case_logs/#{id}/#{subsection}/check_answers")
expect(page).to have_content("Check the answers you gave for #{subsection.tr('_', ' ')}")
end
let(:last_question_for_subsection) { "household_number_of_other_members" }
it "redirects to the check answers page when answering the last question and clicking save and continue" do
fill_in_number_question(id, "other_hhmemb", 0, last_question_for_subsection)
expect(page).to have_current_path("/case_logs/#{id}/#{subsection}/check_answers")
end
it "has question headings based on the subsection" do
visit("case_logs/#{id}/#{subsection}/check_answers")
question_labels = ["Tenant code", "Tenant's age", "Tenant's gender", "Number of Other Household Members"]
question_labels.each do |label|
expect(page).to have_content(label)
end
end
it "should display answers given by the user for the question in the subsection" do
fill_in_number_question(empty_case_log.id, "age1", 28, "person_1_age")
choose("case-log-sex1-non-binary-field")
click_button("Save and continue")
visit("/case_logs/#{empty_case_log.id}/#{subsection}/check_answers")
expect(page).to have_content("28")
expect(page).to have_content("Non-binary")
end
it "should have an answer link for questions missing an answer" do
visit("case_logs/#{empty_case_log.id}/#{subsection}/check_answers")
assert_selector "a", text: /Answer\z/, count: 4
assert_selector "a", text: "Change", count: 0
expect(page).to have_link("Answer", href: "/case_logs/#{empty_case_log.id}/person_1_age")
end
it "should have a change link for answered questions" do
fill_in_number_question(empty_case_log.id, "age1", 28, "person_1_age")
visit("/case_logs/#{empty_case_log.id}/#{subsection}/check_answers")
assert_selector "a", text: /Answer\z/, count: 3
assert_selector "a", text: "Change", count: 1
expect(page).to have_link("Change", href: "/case_logs/#{empty_case_log.id}/person_1_age")
end
it "should have a change link for answered questions" do
visit("/case_logs/#{empty_case_log.id}/household_needs/check_answers")
assert_selector "a", text: /Answer\z/, count: 4
assert_selector "a", text: "Change", count: 0
visit("/case_logs/#{empty_case_log.id}/accessibility_requirements")
check("case-log-accessibility-requirements-housingneeds-c-field")
click_button("Save and continue")
visit("/case_logs/#{empty_case_log.id}/household_needs/check_answers")
assert_selector "a", text: /Answer\z/, count: 3
assert_selector "a", text: "Change", count: 1
expect(page).to have_link("Change", href: "/case_logs/#{empty_case_log.id}/accessibility_requirements")
end
it "should have a link pointing to the first question if no questions are answered" do
visit("/case_logs/#{empty_case_log.id}/#{subsection}/check_answers")
expect(page).to have_content("You answered 0 of 4 questions")
expect(page).to have_link("Answer the missing questions", href: "/case_logs/#{empty_case_log.id}/tenant_code")
end
it "should have a link pointing to the next empty question if some questions are answered" do
fill_in_number_question(empty_case_log.id, "earnings", 18_000, "net_income")
visit("/case_logs/#{empty_case_log.id}/income_and_benefits/check_answers")
expect(page).to have_content("You answered 1 of 4 questions")
expect(page).to have_link("Answer the missing questions", href: "/case_logs/#{empty_case_log.id}/net_income")
end
it "should not display the missing answer questions link if all questions are answered" do
answer_all_questions_in_income_subsection
expect(page).to have_content("You answered all the questions")
assert_selector "a", text: "Answer the missing questions", count: 0
end
it "does not display conditional questions that were not visited" do
visit("case_logs/#{id}/#{conditional_subsection}/check_answers")
question_labels = ["Has the condition been met?"]
question_labels.each do |label|
expect(page).to have_content(label)
end
excluded_question_labels = ["Has the next condition been met?", "Has the condition not been met?"]
excluded_question_labels.each do |label|
expect(page).not_to have_content(label)
end
end
it "displays conditional question that were visited" do
visit("/case_logs/#{id}/conditional_question")
choose("case-log-preg-occ-no-field")
click_button("Save and continue")
visit("/case_logs/#{id}/#{conditional_subsection}/check_answers")
question_labels = ["Has the condition been met?", "Has the condition not been met?"]
question_labels.each do |label|
expect(page).to have_content(label)
end
excluded_question_labels = ["Has the next condition been met?"]
excluded_question_labels.each do |label|
expect(page).not_to have_content(label)
end
end
end
end
describe "Conditional questions" do
context "given a page where some questions are only conditionally shown, depending on how you answer the first question" do
it "initially hides conditional questions" do
visit("/case_logs/#{id}/armed_forces")
expect(page).not_to have_selector("#armed_forces_injured_div")
end
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")
# Something about our styling makes the selenium webdriver think the actual radio buttons are not visible so we allow label click here
choose("case-log-armedforces-a-current-or-former-regular-in-the-uk-armed-forces-exc-national-service-field", allow_label_click: true)
expect(page).to have_selector("#reservist_div")
choose("case-log-reservist-no-field", allow_label_click: true)
expect(page).to have_checked_field("case-log-reservist-no-field", visible: false)
choose("case-log-armedforces-no-field", allow_label_click: true)
expect(page).not_to have_selector("#reservist_div")
choose("case-log-armedforces-a-current-or-former-regular-in-the-uk-armed-forces-exc-national-service-field", allow_label_click: true)
expect(page).to have_unchecked_field("case-log-reservist-no-field", visible: false)
end
end
end
describe "Question validation" do
context "given an invalid tenant age" do
it " of less than 0 it shows validation" do
visit("/case_logs/#{id}/person_1_age")
fill_in_number_question(empty_case_log.id, "age1", -5, "person_1_age")
expect(page).to have_selector("#error-summary-title")
expect(page).to have_selector("#case-log-age1-error")
expect(page).to have_selector("#case-log-age1-field-error")
end
it " of greater than 120 it shows validation" do
visit("/case_logs/#{id}/person_1_age")
fill_in_number_question(empty_case_log.id, "age1", 121, "person_1_age")
expect(page).to have_selector("#error-summary-title")
expect(page).to have_selector("#case-log-age1-error")
expect(page).to have_selector("#case-log-age1-field-error")
end
end
end
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
let(:case_log) { FactoryBot.create(:case_log, :in_progress, ecstat1: "Full-time - 30 hours or more") }
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
visit("/case_logs/#{case_log.id}/net_income")
fill_in("case-log-earnings-field", with: income_over_soft_limit)
choose("case-log-incfreq-weekly-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)
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{case_log.id}/net_income_uc_proportion")
end
it "does not require confirming the value if the value is amended" do
visit("/case_logs/#{case_log.id}/net_income")
fill_in("case-log-earnings-field", with: income_over_soft_limit)
choose("case-log-incfreq-weekly-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")
expect(page).to have_current_path("/case_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("/case_logs/#{case_log.id}/net_income")
fill_in("case-log-earnings-field", with: income_over_soft_limit)
choose("case-log-incfreq-weekly-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("/case_logs/#{case_log.id}/net_income")
fill_in("case-log-earnings-field", with: income_over_soft_limit)
choose("case-log-incfreq-weekly-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?")
end
end
end
describe "conditional page routing", js: true do
before do
allow_any_instance_of(CaseLogValidator).to receive(:validate_pregnancy).and_return(true)
end
it "can route the user to a different page based on their answer on the current page" do
visit("case_logs/#{id}/conditional_question")
# using a question name that is already in the db to avoid
# having to add a new column to the db for this test
choose("case-log-preg-occ-yes-field", allow_label_click: true)
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/conditional_question_yes_page")
click_link(text: "Back")
expect(page).to have_current_path("/case_logs/#{id}/conditional_question")
choose("case-log-preg-occ-no-field", allow_label_click: true)
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/conditional_question_no_page")
end
it "can route based on multiple conditions" do
visit("/case_logs/#{id}/person_1_gender")
choose("case-log-sex1-female-field", allow_label_click: true)
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/household_number_of_other_members")
visit("/case_logs/#{id}/conditional_question")
choose("case-log-preg-occ-no-field", allow_label_click: true)
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/conditional_question_no_page")
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/conditional_question/check_answers")
end
end
describe "date validation", js: true do
def fill_in_date(case_log_id, question, day, month, year, path)
visit("/case_logs/#{case_log_id}/#{path}")
fill_in("#{question}_1i", with: year)
fill_in("#{question}_2i", with: month)
fill_in("#{question}_3i", with: day)
end
it "does not allow out of range dates to be submitted" do
fill_in_date(id, "case_log_mrcdate", 3100, 12, 2000, "property_major_repairs")
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/property_major_repairs")
fill_in_date(id, "case_log_mrcdate", 12, 1, 20_000, "property_major_repairs")
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/property_major_repairs")
fill_in_date(id, "case_log_mrcdate", 13, 100, 2020, "property_major_repairs")
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/property_major_repairs")
fill_in_date(id, "case_log_mrcdate", 21, 11, 2020, "property_major_repairs")
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/local_authority/check_answers")
end
it "does not allow non numeric inputs to be submitted" do
fill_in_date(id, "case_log_mrcdate", "abc", "de", "ff", "property_major_repairs")
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/property_major_repairs")
end
it "does not allow partial inputs to be submitted" do
fill_in_date(id, "case_log_mrcdate", 21, 12, nil, "property_major_repairs")
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/property_major_repairs")
fill_in_date(id, "case_log_mrcdate", 12, nil, 2000, "property_major_repairs")
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/property_major_repairs")
fill_in_date(id, "case_log_mrcdate", nil, 10, 2020, "property_major_repairs")
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/property_major_repairs")
end
it "allows valid inputs to be submitted" do
fill_in_date(id, "case_log_mrcdate", 21, 11, 2020, "property_major_repairs")
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/local_authority/check_answers")
end
end
end

123
spec/features/form/check_answers_page_spec.rb

@ -0,0 +1,123 @@
require "rails_helper"
require_relative "helpers"
RSpec.describe "Form Check Answers Page" do
include Helpers
let(:case_log) { FactoryBot.create(:case_log, :in_progress) }
let(:empty_case_log) { FactoryBot.create(:case_log) }
let(:id) { case_log.id }
before do
allow_any_instance_of(CaseLogsController).to receive(:authenticate_user!).and_return(true)
end
let(:subsection) { "household_characteristics" }
let(:conditional_subsection) { "conditional_question" }
context "when the user needs to check their answers for a subsection" do
it "can be visited by URL" do
visit("case_logs/#{id}/#{subsection}/check_answers")
expect(page).to have_content("Check the answers you gave for #{subsection.tr('_', ' ')}")
end
let(:last_question_for_subsection) { "household_number_of_other_members" }
it "redirects to the check answers page when answering the last question and clicking save and continue" do
fill_in_number_question(id, "other_hhmemb", 0, last_question_for_subsection)
expect(page).to have_current_path("/case_logs/#{id}/#{subsection}/check_answers")
end
it "has question headings based on the subsection" do
visit("case_logs/#{id}/#{subsection}/check_answers")
question_labels = ["Tenant code", "Tenant's age", "Tenant's gender", "Number of Other Household Members"]
question_labels.each do |label|
expect(page).to have_content(label)
end
end
it "should display answers given by the user for the question in the subsection" do
fill_in_number_question(empty_case_log.id, "age1", 28, "person_1_age")
choose("case-log-sex1-non-binary-field")
click_button("Save and continue")
visit("/case_logs/#{empty_case_log.id}/#{subsection}/check_answers")
expect(page).to have_content("28")
expect(page).to have_content("Non-binary")
end
it "should have an answer link for questions missing an answer" do
visit("case_logs/#{empty_case_log.id}/#{subsection}/check_answers")
assert_selector "a", text: /Answer\z/, count: 4
assert_selector "a", text: "Change", count: 0
expect(page).to have_link("Answer", href: "/case_logs/#{empty_case_log.id}/person_1_age")
end
it "should have a change link for answered questions" do
fill_in_number_question(empty_case_log.id, "age1", 28, "person_1_age")
visit("/case_logs/#{empty_case_log.id}/#{subsection}/check_answers")
assert_selector "a", text: /Answer\z/, count: 3
assert_selector "a", text: "Change", count: 1
expect(page).to have_link("Change", href: "/case_logs/#{empty_case_log.id}/person_1_age")
end
it "should have a change link for answered questions" do
visit("/case_logs/#{empty_case_log.id}/household_needs/check_answers")
assert_selector "a", text: /Answer\z/, count: 4
assert_selector "a", text: "Change", count: 0
visit("/case_logs/#{empty_case_log.id}/accessibility_requirements")
check("case-log-accessibility-requirements-housingneeds-c-field")
click_button("Save and continue")
visit("/case_logs/#{empty_case_log.id}/household_needs/check_answers")
assert_selector "a", text: /Answer\z/, count: 3
assert_selector "a", text: "Change", count: 1
expect(page).to have_link("Change", href: "/case_logs/#{empty_case_log.id}/accessibility_requirements")
end
it "should have a link pointing to the first question if no questions are answered" do
visit("/case_logs/#{empty_case_log.id}/#{subsection}/check_answers")
expect(page).to have_content("You answered 0 of 4 questions")
expect(page).to have_link("Answer the missing questions", href: "/case_logs/#{empty_case_log.id}/tenant_code")
end
it "should have a link pointing to the next empty question if some questions are answered" do
fill_in_number_question(empty_case_log.id, "earnings", 18_000, "net_income")
visit("/case_logs/#{empty_case_log.id}/income_and_benefits/check_answers")
expect(page).to have_content("You answered 1 of 4 questions")
expect(page).to have_link("Answer the missing questions", href: "/case_logs/#{empty_case_log.id}/net_income")
end
it "should not display the missing answer questions link if all questions are answered" do
answer_all_questions_in_income_subsection(empty_case_log)
expect(page).to have_content("You answered all the questions")
assert_selector "a", text: "Answer the missing questions", count: 0
end
it "does not display conditional questions that were not visited" do
visit("case_logs/#{id}/#{conditional_subsection}/check_answers")
question_labels = ["Has the condition been met?"]
question_labels.each do |label|
expect(page).to have_content(label)
end
excluded_question_labels = ["Has the next condition been met?", "Has the condition not been met?"]
excluded_question_labels.each do |label|
expect(page).not_to have_content(label)
end
end
it "displays conditional question that were visited" do
visit("/case_logs/#{id}/conditional_question")
choose("case-log-preg-occ-no-field")
click_button("Save and continue")
visit("/case_logs/#{id}/#{conditional_subsection}/check_answers")
question_labels = ["Has the condition been met?", "Has the condition not been met?"]
question_labels.each do |label|
expect(page).to have_content(label)
end
excluded_question_labels = ["Has the next condition been met?"]
excluded_question_labels.each do |label|
expect(page).not_to have_content(label)
end
end
end
end

30
spec/features/form/conditional_questions_spec.rb

@ -0,0 +1,30 @@
require "rails_helper"
RSpec.describe "Form Conditional Questions" do
let(:case_log) { FactoryBot.create(:case_log, :in_progress) }
let(:id) { case_log.id }
before do
allow_any_instance_of(CaseLogsController).to receive(:authenticate_user!).and_return(true)
end
context "given a page where some questions are only conditionally shown, depending on how you answer the first question" do
it "initially hides conditional questions" do
visit("/case_logs/#{id}/armed_forces")
expect(page).not_to have_selector("#armed_forces_injured_div")
end
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")
# Something about our styling makes the selenium webdriver think the actual radio buttons are not visible so we allow label click here
choose("case-log-armedforces-a-current-or-former-regular-in-the-uk-armed-forces-exc-national-service-field", allow_label_click: true)
expect(page).to have_selector("#reservist_div")
choose("case-log-reservist-no-field", allow_label_click: true)
expect(page).to have_checked_field("case-log-reservist-no-field", visible: false)
choose("case-log-armedforces-no-field", allow_label_click: true)
expect(page).not_to have_selector("#reservist_div")
choose("case-log-armedforces-a-current-or-former-regular-in-the-uk-armed-forces-exc-national-service-field", allow_label_click: true)
expect(page).to have_unchecked_field("case-log-reservist-no-field", visible: false)
end
end
end

80
spec/features/form/form_navigation_spec.rb

@ -0,0 +1,80 @@
require "rails_helper"
RSpec.describe "Form Navigation" do
let(:case_log) { FactoryBot.create(:case_log, :in_progress) }
let(:id) { case_log.id }
let(:question_answers) 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" },
other_hhmemb: { type: "numeric", answer: 2, path: "household_number_of_other_members" },
}
end
before do
allow_any_instance_of(CaseLogsController).to receive(:authenticate_user!).and_return(true)
end
describe "Create new log" do
it "redirects to the task list for the new log" do
visit("/case_logs")
click_link("Create new log")
id = CaseLog.order(created_at: :desc).first.id
expect(page).to have_content("Tasklist for log #{id}")
end
end
describe "Viewing a log" do
it "questions can be accessed by url" do
visit("/case_logs/#{id}/person_1_age")
expect(page).to have_field("case-log-age1-field")
end
it "a question page leads to the next question defined in the form definition" do
pages = question_answers.map { |_key, val| val[:path] }
pages[0..-2].each_with_index do |val, index|
visit("/case_logs/#{id}/#{val}")
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/#{pages[index + 1]}")
end
end
describe "Back link directs correctly", js: true do
it "go back to tasklist page from tenant code" do
visit("/case_logs/#{id}")
visit("/case_logs/#{id}/tenant_code")
click_link(text: "Back")
expect(page).to have_content("Tasklist for log #{id}")
end
it "go back to tenant code page from tenant age page", js: true do
visit("/case_logs/#{id}/tenant_code")
click_button("Save and continue")
visit("/case_logs/#{id}/person_1_age")
click_link(text: "Back")
expect(page).to have_field("case-log-tenant-code-field")
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
context "when changing an answer from the check answers page", js: true do
it "the back button routes correctly" do
visit("/case_logs/#{id}/household_characteristics/check_answers")
first("a", text: /Answer/).click
click_link("Back")
expect(page).to have_current_path("/case_logs/#{id}/household_characteristics/check_answers")
end
end
end
end
end

18
spec/features/form/helpers.rb

@ -0,0 +1,18 @@
module Helpers
def fill_in_number_question(case_log_id, question, value, path)
visit("/case_logs/#{case_log_id}/#{path}")
fill_in("case-log-#{question.to_s.dasherize}-field", with: value)
click_button("Save and continue")
end
def answer_all_questions_in_income_subsection(case_log)
visit("/case_logs/#{case_log.id}/net_income")
fill_in("case-log-earnings-field", with: 18_000)
choose("case-log-incfreq-yearly-field")
click_button("Save and continue")
choose("case-log-benefits-all-field")
click_button("Save and continue")
choose("case-log-hb-prefer-not-to-say-field")
click_button("Save and continue")
end
end

38
spec/features/form/page_routing_spec.rb

@ -0,0 +1,38 @@
require "rails_helper"
RSpec.describe "Form Page Routing" do
let(:case_log) { FactoryBot.create(:case_log, :in_progress) }
let(:id) { case_log.id }
before do
allow_any_instance_of(CaseLogsController).to receive(:authenticate_user!).and_return(true)
allow_any_instance_of(CaseLogValidator).to receive(:validate_pregnancy).and_return(true)
end
it "can route the user to a different page based on their answer on the current page", js: true do
visit("case_logs/#{id}/conditional_question")
# using a question name that is already in the db to avoid
# having to add a new column to the db for this test
choose("case-log-preg-occ-yes-field", allow_label_click: true)
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/conditional_question_yes_page")
click_link(text: "Back")
expect(page).to have_current_path("/case_logs/#{id}/conditional_question")
choose("case-log-preg-occ-no-field", allow_label_click: true)
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/conditional_question_no_page")
end
it "can route based on multiple conditions", js: true do
visit("/case_logs/#{id}/person_1_gender")
choose("case-log-sex1-female-field", allow_label_click: true)
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/household_number_of_other_members")
visit("/case_logs/#{id}/conditional_question")
choose("case-log-preg-occ-no-field", allow_label_click: true)
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/conditional_question_no_page")
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/conditional_question/check_answers")
end
end

89
spec/features/form/saving_data_spec.rb

@ -0,0 +1,89 @@
require "rails_helper"
RSpec.describe "Form Saving Data" do
let(:case_log) { FactoryBot.create(:case_log, :in_progress) }
let(:id) { case_log.id }
let(:case_log_with_checkbox_questions_answered) do
FactoryBot.create(
:case_log, :in_progress,
housingneeds_a: "Yes",
housingneeds_c: "Yes"
)
end
let(:question_answers) 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" },
other_hhmemb: { type: "numeric", answer: 2, path: "household_number_of_other_members" },
}
end
before do
allow_any_instance_of(CaseLogsController).to receive(:authenticate_user!).and_return(true)
end
it "updates model attributes correctly for each question" do
question_answers.each do |question, hsh|
type = hsh[:type]
answer = hsh[:answer]
path = hsh[:path]
original_value = case_log.send(question)
visit("/case_logs/#{id}/#{path}")
case type
when "text"
fill_in("case-log-#{question.to_s.dasherize}-field", with: answer)
when "radio"
choose("case-log-#{question.to_s.dasherize}-#{answer.parameterize}-field")
else
fill_in("case-log-#{question.to_s.dasherize}-field", with: answer)
end
expect { click_button("Save and continue") }.to change {
case_log.reload.send(question.to_s)
}.from(original_value).to(answer)
end
end
it "updates total value of the rent", js: true do
visit("/case_logs/#{id}/rent")
fill_in("case-log-brent-field", with: 3)
expect(page).to have_field("case-log-tcharge-field", with: "3")
fill_in("case-log-scharge-field", with: 2)
expect(page).to have_field("case-log-tcharge-field", with: "5")
fill_in("case-log-pscharge-field", with: 1)
expect(page).to have_field("case-log-tcharge-field", with: "6")
fill_in("case-log-supcharg-field", with: 4)
expect(page).to have_field("case-log-tcharge-field", with: "10")
end
it "displays number answers in inputs if they are already saved" do
visit("/case_logs/#{id}/property_postcode")
expect(page).to have_field("case-log-property-postcode-field", with: "P0 5ST")
end
it "displays text answers in inputs if they are already saved" do
visit("/case_logs/#{id}/person_1_age")
expect(page).to have_field("case-log-age1-field", with: "17")
end
it "displays checkbox answers in inputs if they are already saved" do
visit("/case_logs/#{case_log_with_checkbox_questions_answered.id}/accessibility_requirements")
# Something about our styling makes the selenium webdriver think the actual radio buttons are not visible so we pass false here
expect(page).to have_checked_field(
"case-log-accessibility-requirements-housingneeds-a-field",
visible: false,
)
expect(page).to have_unchecked_field(
"case-log-accessibility-requirements-housingneeds-b-field",
visible: false,
)
expect(page).to have_checked_field(
"case-log-accessibility-requirements-housingneeds-c-field",
visible: false,
)
end
end

53
spec/features/form/tasklist_page_spec.rb

@ -0,0 +1,53 @@
require "rails_helper"
require_relative "helpers"
RSpec.describe "Task List" do
include Helpers
let(:case_log) { FactoryBot.create(:case_log, :in_progress) }
let(:empty_case_log) { FactoryBot.create(:case_log) }
let(:id) { case_log.id }
let(:status) { case_log.status }
before do
allow_any_instance_of(CaseLogsController).to receive(:authenticate_user!).and_return(true)
end
it "displays a tasklist header" do
visit("/case_logs/#{id}")
expect(page).to have_content("Tasklist for log #{id}")
expect(page).to have_content("This submission is #{status.humanize.downcase}")
end
it "displays a section status" do
visit("/case_logs/#{empty_case_log.id}")
assert_selector ".govuk-tag", text: /Not started/, count: 8
assert_selector ".govuk-tag", text: /Completed/, count: 0
assert_selector ".govuk-tag", text: /Cannot start yet/, count: 1
end
it "shows the correct status if one section is completed" do
answer_all_questions_in_income_subsection(empty_case_log)
visit("/case_logs/#{empty_case_log.id}")
assert_selector ".govuk-tag", text: /Not started/, count: 7
assert_selector ".govuk-tag", text: /Completed/, count: 1
assert_selector ".govuk-tag", text: /Cannot start yet/, count: 1
end
it "skips to the first section if no answers are completed" do
visit("/case_logs/#{empty_case_log.id}")
expect(page).to have_link("Skip to next incomplete section", href: /#household_characteristics/)
end
it "shows the number of completed sections if no sections are completed" do
visit("/case_logs/#{empty_case_log.id}")
expect(page).to have_content("You've completed 0 of 9 sections.")
end
it "shows the number of completed sections if one section is completed" do
answer_all_questions_in_income_subsection(empty_case_log)
visit("/case_logs/#{empty_case_log.id}")
expect(page).to have_content("You've completed 1 of 9 sections.")
end
end

139
spec/features/form/validations_spec.rb

@ -0,0 +1,139 @@
require "rails_helper"
require_relative "helpers"
RSpec.describe "validations" do
include Helpers
let(:case_log) { FactoryBot.create(:case_log, :in_progress) }
let(:empty_case_log) { FactoryBot.create(:case_log) }
let(:id) { case_log.id }
before do
allow_any_instance_of(CaseLogsController).to receive(:authenticate_user!).and_return(true)
end
describe "Question validation" do
context "given an invalid tenant age" do
it " of less than 0 it shows validation" do
visit("/case_logs/#{id}/person_1_age")
fill_in_number_question(empty_case_log.id, "age1", -5, "person_1_age")
expect(page).to have_selector("#error-summary-title")
expect(page).to have_selector("#case-log-age1-error")
expect(page).to have_selector("#case-log-age1-field-error")
end
it " of greater than 120 it shows validation" do
visit("/case_logs/#{id}/person_1_age")
fill_in_number_question(empty_case_log.id, "age1", 121, "person_1_age")
expect(page).to have_selector("#error-summary-title")
expect(page).to have_selector("#case-log-age1-error")
expect(page).to have_selector("#case-log-age1-field-error")
end
end
end
describe "date validation", js: true do
def fill_in_date(case_log_id, question, day, month, year, path)
visit("/case_logs/#{case_log_id}/#{path}")
fill_in("#{question}_1i", with: year)
fill_in("#{question}_2i", with: month)
fill_in("#{question}_3i", with: day)
end
it "does not allow out of range dates to be submitted" do
fill_in_date(id, "case_log_mrcdate", 3100, 12, 2000, "property_major_repairs")
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/property_major_repairs")
fill_in_date(id, "case_log_mrcdate", 12, 1, 20_000, "property_major_repairs")
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/property_major_repairs")
fill_in_date(id, "case_log_mrcdate", 13, 100, 2020, "property_major_repairs")
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/property_major_repairs")
fill_in_date(id, "case_log_mrcdate", 21, 11, 2020, "property_major_repairs")
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/local_authority/check_answers")
end
it "does not allow non numeric inputs to be submitted" do
fill_in_date(id, "case_log_mrcdate", "abc", "de", "ff", "property_major_repairs")
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/property_major_repairs")
end
it "does not allow partial inputs to be submitted" do
fill_in_date(id, "case_log_mrcdate", 21, 12, nil, "property_major_repairs")
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/property_major_repairs")
fill_in_date(id, "case_log_mrcdate", 12, nil, 2000, "property_major_repairs")
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/property_major_repairs")
fill_in_date(id, "case_log_mrcdate", nil, 10, 2020, "property_major_repairs")
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/property_major_repairs")
end
it "allows valid inputs to be submitted" do
fill_in_date(id, "case_log_mrcdate", 21, 11, 2020, "property_major_repairs")
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{id}/local_authority/check_answers")
end
end
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
let(:case_log) { FactoryBot.create(:case_log, :in_progress, ecstat1: "Full-time - 30 hours or more") }
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
visit("/case_logs/#{case_log.id}/net_income")
fill_in("case-log-earnings-field", with: income_over_soft_limit)
choose("case-log-incfreq-weekly-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)
click_button("Save and continue")
expect(page).to have_current_path("/case_logs/#{case_log.id}/net_income_uc_proportion")
end
it "does not require confirming the value if the value is amended" do
visit("/case_logs/#{case_log.id}/net_income")
fill_in("case-log-earnings-field", with: income_over_soft_limit)
choose("case-log-incfreq-weekly-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")
expect(page).to have_current_path("/case_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("/case_logs/#{case_log.id}/net_income")
fill_in("case-log-earnings-field", with: income_over_soft_limit)
choose("case-log-incfreq-weekly-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("/case_logs/#{case_log.id}/net_income")
fill_in("case-log-earnings-field", with: income_over_soft_limit)
choose("case-log-incfreq-weekly-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?")
end
end
end
end

10
spec/features/user_spec.rb

@ -9,7 +9,7 @@ RSpec.describe "User Features" do
it " is redirected to case logs after signing in" do
visit("/case_logs")
fill_in("user_email", with: "test@example.com")
fill_in("user_email", with: user.email)
fill_in("user_password", with: "pAssword1")
click_button("Sign in")
expect(page).to have_current_path("/case_logs")
@ -25,16 +25,16 @@ RSpec.describe "User Features" do
it " is redirected to check your email page after submitting an email on the reset password page" do
visit("/users/password/new")
fill_in("user_email", with: "test@example.com")
fill_in("user_email", with: user.email)
click_button("Send email")
expect(page).to have_content("Check your email")
end
it " is shown their email on the password reset confirmation page" do
visit("/users/password/new")
fill_in("user_email", with: "test@example.com")
fill_in("user_email", with: user.email)
click_button("Send email")
expect(page).to have_content("test@example.com")
expect(page).to have_content(user.email)
end
it " is shown the reset password confirmation page even if their email doesn't exist in the system" do
@ -46,7 +46,7 @@ RSpec.describe "User Features" do
it " is sent a reset password email" do
visit("/users/password/new")
fill_in("user_email", with: "test@example.com")
fill_in("user_email", with: user.email)
expect { click_button("Send email") }.to change { ActionMailer::Base.deliveries.count }.by(1)
end
end

4
spec/helpers/tasklist_helper_spec.rb

@ -1,8 +1,8 @@
require "rails_helper"
RSpec.describe TasklistHelper do
let(:empty_case_log) { FactoryBot.build(:case_log) }
let(:case_log) { FactoryBot.build(:case_log, :in_progress) }
let(:empty_case_log) { FactoryBot.create(:case_log) }
let(:case_log) { FactoryBot.create(:case_log, :in_progress) }
form_handler = FormHandler.instance
let(:form) { form_handler.get_form("test_form") }

20
spec/models/case_log_spec.rb

@ -4,14 +4,17 @@ RSpec.describe Form, type: :model do
describe "#new" do
it "validates age is a number" do
expect { CaseLog.create!(age1: "random") }.to raise_error(ActiveRecord::RecordInvalid)
expect { CaseLog.create!(age3: "random") }.to raise_error(ActiveRecord::RecordInvalid)
end
it "validates age is under 120" do
expect { CaseLog.create!(age1: 121) }.to raise_error(ActiveRecord::RecordInvalid)
expect { CaseLog.create!(age3: 121) }.to raise_error(ActiveRecord::RecordInvalid)
end
it "validates age is over 0" do
expect { CaseLog.create!(age1: 0) }.to raise_error(ActiveRecord::RecordInvalid)
expect { CaseLog.create!(age3: 0) }.to raise_error(ActiveRecord::RecordInvalid)
end
it "validates number of relets is a number" do
@ -54,6 +57,15 @@ RSpec.describe Form, type: :model do
CaseLog.create!(reasonpref: "No", rp_homeless: "No")
}.not_to raise_error
end
it "validates that no reason has been provided" do
expect {
CaseLog.create!(
reasonpref: "No",
rp_medwel: "Yes"
)
}.to raise_error(ActiveRecord::RecordInvalid)
end
end
context "reason for leaving last settled home validation" do
@ -424,6 +436,14 @@ RSpec.describe Form, type: :model do
end
end
describe "incref" do
let(:case_log) { FactoryBot.build(:case_log, net_income_known: "Prefer not to say") }
it "sets income refused to Yes" do
expect(case_log.incref).to eq(1)
end
end
describe "weekly_net_income" do
let(:net_income) { 5000 }
let(:case_log) { FactoryBot.build(:case_log, earnings: net_income) }

59
spec/rails_helper.rb

@ -6,10 +6,19 @@ require File.expand_path("../config/environment", __dir__)
abort("The Rails environment is running in production mode!") if Rails.env.production?
require "rspec/rails"
require "capybara/rspec"
require "database_cleaner/active_record"
require "selenium-webdriver"
# Comment to run `js: true specs` with visible browser interaction
Capybara.javascript_driver = :selenium_headless
Capybara.register_driver :headless do |app|
options = Selenium::WebDriver::Firefox::Options.new
options.add_argument("--headless")
Capybara::Selenium::Driver.new(app,
browser: :firefox,
capabilities: options
)
end
Capybara.javascript_driver = :headless
# Add additional requires below this line. Rails is not loaded until this point!
@ -43,49 +52,7 @@ RSpec.configure do |config|
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
# config.use_transactional_fixtures = true
config.before(:suite) do
if config.use_transactional_fixtures?
raise(<<-MSG)
Delete line `config.use_transactional_fixtures = true` from rails_helper.rb
(or set it to false) to prevent uncommitted transactions being used in
JavaScript-dependent specs.
During testing, the app-under-test that the browser driver connects to
uses a different database connection to the database connection used by
the spec. The app's database connection would not be able to access
uncommitted transaction data setup over the spec's database connection.
MSG
end
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, type: :feature) do
# :rack_test driver's Rack app under test shares database connection
# with the specs, so continue to use transaction strategy for speed.
driver_shares_db_connection_with_specs = Capybara.current_driver == :rack_test
unless driver_shares_db_connection_with_specs
# Driver is probably for an external browser with an app
# under test that does *not* share a database connection with the
# specs, so use truncation strategy.
DatabaseCleaner.strategy = :truncation
end
end
config.before(:each) do
DatabaseCleaner.start
end
config.append_after(:each) do
DatabaseCleaner.clean
end
config.use_transactional_fixtures = true
# You can uncomment this line to turn off ActiveRecord support entirely.
# config.use_active_record = false

51
spec/requests/case_log_controller_spec.rb

@ -231,34 +231,47 @@ RSpec.describe CaseLogsController, type: :request do
end
let(:id) { case_log.id }
before do
delete "/case_logs/#{id}", headers: headers
end
context "expected deletion" do
before do
delete "/case_logs/#{id}", headers: headers
end
it "returns http success" do
expect(response).to have_http_status(:success)
end
it "returns http success" do
expect(response).to have_http_status(:success)
end
it "soft deletes the case log" do
expect { CaseLog.find(id) }.to raise_error(ActiveRecord::RecordNotFound)
expect(CaseLog.with_discarded.find(id)).to be_a(CaseLog)
end
it "soft deletes the case log" do
expect { CaseLog.find(id) }.to raise_error(ActiveRecord::RecordNotFound)
expect(CaseLog.with_discarded.find(id)).to be_a(CaseLog)
end
context "invalid case log id" do
let(:id) { (CaseLog.order(:id).last&.id || 0) + 1 }
context "invalid case log id" do
let(:id) { (CaseLog.order(:id).last&.id || 0) + 1 }
it "returns 404" do
expect(response).to have_http_status(:not_found)
it "returns 404" do
expect(response).to have_http_status(:not_found)
end
end
context "request with invalid credentials" do
let(:basic_credentials) do
ActionController::HttpAuthentication::Basic.encode_credentials(api_username, "Oops")
end
it "returns 401" do
expect(response).to have_http_status(:unauthorized)
end
end
end
context "request with invalid credentials" do
let(:basic_credentials) do
ActionController::HttpAuthentication::Basic.encode_credentials(api_username, "Oops")
context "deletion fails" do
before do
allow_any_instance_of(CaseLog).to receive(:discard).and_return(false)
delete "/case_logs/#{id}", headers: headers
end
it "returns 401" do
expect(response).to have_http_status(:unauthorized)
it "returns an unprocessable entity 422" do
expect(response).to have_http_status(:unprocessable_entity)
end
end
end

4
spec/views/case_log_index_view_spec.rb

@ -1,7 +1,7 @@
require "rails_helper"
RSpec.describe "case_logs/index" do
let(:in_progress_log) { FactoryBot.build(:case_log, :in_progress) }
let(:completed_log) { FactoryBot.build(:case_log, :completed) }
let(:in_progress_log) { FactoryBot.create(:case_log, :in_progress) }
let(:completed_log) { FactoryBot.create(:case_log, :completed) }
context "given an in progress log list" do
it "renders a table for in progress logs only" do

Loading…
Cancel
Save