Browse Source

Merge branch 'main' into CLDC-344/Implement-a-few-validations

pull/39/head
Matthew Phelan 3 years ago
parent
commit
e2268684c0
  1. 10
      Gemfile.lock
  2. 135
      README.md
  3. 29
      app/controllers/case_logs_controller.rb
  4. 6
      app/helpers/conditional_questions_helper.rb
  5. 12
      app/helpers/numeric_questions_helper.rb
  6. 2
      app/javascript/controllers/conditional_question_controller.js
  7. 12
      app/javascript/controllers/numeric_question_controller.js
  8. 10
      app/models/form.rb
  9. 4
      app/views/form/_numeric_question.html.erb
  10. 13
      app/views/form/page.html.erb
  11. 27
      config/forms/2021_2022.json
  12. 3
      config/routes.rb
  13. 58
      spec/features/case_log_spec.rb
  14. 21
      spec/helpers/numeric_questions_helper_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)

135
README.md

@ -1,116 +1,116 @@
[![CI/CD Pipeline](https://github.com/communitiesuk/mhclg-data-collection-beta/actions/workflows/pipeline.yml/badge.svg?branch=main&event=push)](https://github.com/communitiesuk/mhclg-data-collection-beta/actions/workflows/pipeline.yml) [![CI/CD Pipeline](https://github.com/communitiesuk/mhclg-data-collection-beta/actions/workflows/pipeline.yml/badge.svg?branch=main&event=push)](https://github.com/communitiesuk/mhclg-data-collection-beta/actions/workflows/pipeline.yml)
# Data Collection App # Data Collection App
This is the codebase for the Ruby/Rails app that will handle the submission of Lettings and Sales of Social Housing in England data.
This is the codebase for the Ruby on Rails app that will handle the submission of Lettings and Sales of Social Housing in England data.
## Required Setup ## Required Setup
Pre-requisites Pre-requisites:
- Ruby - Ruby
- Rails - Rails
- Postgres - Postgres
### Quick start
### Setup Quickstart 1. Copy the `.env.example` to `.env` and replace the database credentials with your local postgres user credentials.
Copy the `.env.example` to `.env` and replace the database credentials with your local postgres user credentials. 2. Install the dependencies:\
`bundle install`
Install the dependencies 3. Create the database:\
`bundle install` `rake db:create`
Create the database 4. Run the database migrations:\
`rake db:create` `rake db:migrate`
Run the database migrations 5. Install the frontend depenencies:\
`rake db:migrate` `yarn install`
Start the rails server 6. Start the Rails server:\
``` `bundle exec rails s`
rails s
```
This starts the rails server on localhost:3000
or using Docker The Rails server will start on <http://localhost:3000>.
``` ### Using Docker
```sh
docker-compose build docker-compose build
docker-compose run --rm app rails db:create docker-compose run --rm app rails db:create
docker-compose up docker-compose up
``` ```
This exposes the rails server on localhost:8080. The Rails server will start on <http://localhost:8080>.
Note docker-compose runs the production docker image (RAILS_ENV=production) as the Dockerfile doesn't include development gems to keep the image size down. Note `docker-compose` runs the production docker image (`RAILS_ENV=production`) as the Dockerfile doesn’t include development gems to keep the image size down.
## Infrastructure
### Infrastructure This application is running on [GOV.UK PaaS](https://www.cloud.service.gov.uk/). To deploy you need to:
This application is running on [Gov PaaS](https://www.cloud.service.gov.uk/). To deploy you need to: 1. Contact your organisation manager to get an account in `dluhc-core` organization and in the relevant spaces (sandbox/production).
- Contact your organisation manager to get an account in `dluhc-core` organization and in the relevant spaces (sandbox/production). 2. [Install the Cloud Foundry CLI](https://docs.cloudfoundry.org/cf-cli/install-go-cli.html)
- Install the cloudfoundry cli https://docs.cloudfoundry.org/cf-cli/install-go-cli.html
- Login <br/> 3. Login:\
`cf login -a api.london.cloud.service.gov.uk -u <your_username>` `cf login -a api.london.cloud.service.gov.uk -u <your_username>`
- Set your deployment target (sandbox/production) <br/> 4. Set your deployment target (sandbox/production):\
`cf target -o dluhc-core -s <deploy_environment>` `cf target -o dluhc-core -s <deploy_environment>`
- Deploy <br/> 5. Deploy:\
`cf push dluhc-core --strategy rolling`. This will use the [manifest file](manifest.yml) `cf push dluhc-core --strategy rolling`. This will use the [manifest file](manifest.yml)
Once the app is deployed: Once the app is deployed:
- Get a rails console <br/> 1. Get a Rails console:\
`cf ssh dluhc-core -t -c "/tmp/lifecycle/launcher /home/vcap/app 'rails console' ''"` `cf ssh dluhc-core -t -c "/tmp/lifecycle/launcher /home/vcap/app 'rails console' ''"`
- Check logs <br /> 2. Check logs:\
`cf logs dluhc-core --recent` `cf logs dluhc-core --recent`
## CI/CD
### CI/CD When a commit is made to `main` the following GitHub action jobs are triggered:
When a commit is made to `main` the following Github action jobs are triggered: 1. **Test**: RSpec runs our test suite
2. **Deploy**: If the Test stage passes, this job will deploy the app to our GOV.UK PaaS account using the Cloud Foundry CLI
1. Test - RSpec runs our test suite When a pull request is opened to `main` only the Test stage runs.
2. Deploy - If the Test stage passes, this deploys the app to our Gov PaaS account using the cloudfoundry cli
When a pull request is opened to `main` only the test stage runs. ## Single log submission
The form for this is driven by a JSON file in `/config/forms/{start_year}_{end_year}.json`
### Single log submission
The form for this is driven by a json file in `/config/forms/{start_year}_{end_year}.json`
The JSON should follow the structure: The JSON should follow the structure:
``` ```jsonc
{ {
form_type: [lettings/sales] "form_type": "lettings" / "sales",
start_year: yyyy "start_year": Integer, // i.e. 2020
end_year: yyyy "end_year": Integer, // i.e. 2021
sections: { "sections": {
snake case section name string: { "[snake_case_section_name_string]": {
label: string, "label": String,
subsections: { "subsections": {
snake case subsection name string: { "[snake_case_subsection_name_string]": {
label: string, "label": String,
pages: { "pages": {
snake case page name string: { "[snake_case_page_name_string]": {
header: string, "header": String,
description: string, "description": String,
questions: { "questions": {
snake case question name string: { "[snake_case_question_name_string]": {
header: string, "header": String,
hint_text: string, "hint_text": String,
type: [text / numeric / radio / checkbox / date ], "type": "text" / "numeric" / "radio" / "checkbox" / "date",
min: integer, (numeric only), "min": Integer, // numeric only
max: integer, (numeric only), "max": Integer, // numeric only
step: integer (numeric only), "step": Integer, // numeric only
answer_options: { (checkbox and radio only) "answer_options": { // checkbox and radio only
"0": string, "0": String,
"1": string "1": String
} }
} }
} }
@ -132,19 +132,18 @@ Assumptions made by the format:
- The ActiveRecord case log model has a field for each question name (must match) - The ActiveRecord case log model has a field for each question name (must match)
- Text not required by a page/question such as a header or hint text should be passed as an empty string - Text not required by a page/question such as a header or hint text should be passed as an empty string
## Useful documentation (external dependencies)
### Useful documentation (external dependencies) ### GOV.UK Design System Form Builder for Rails
##### DfE Form Builder Gem
- [Examples](https://govuk-form-builder.netlify.app/) - [Examples](https://govuk-form-builder.netlify.app/)
- [Technical Docs](https://www.rubydoc.info/gems/govuk_design_system_formbuilder/) - [Technical docs](https://www.rubydoc.info/gems/govuk_design_system_formbuilder/)
- [GitHub repository](https://github.com/DFE-Digital/govuk-formbuilder) - [GitHub repository](https://github.com/DFE-Digital/govuk-formbuilder)
##### Alpha Gov UK frontend gem ### GOV.UK Frontend
- [GitHub repository](https://github.com/alphagov/govuk-frontend) - [GitHub repository](https://github.com/alphagov/govuk-frontend)
##### Hotwire (Turbo/Stimulus) ### Hotwire (Turbo/Stimulus)
- [Docs](https://turbo.hotwired.dev/) - [Docs](https://turbo.hotwired.dev/)

29
app/controllers/case_logs_controller.rb

@ -20,29 +20,18 @@ 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[:case_log][:previous_page]
previous_page = params[: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) }
if @case_log.update(answers_for_page)
@case_log_temp = CaseLog.new(answers_for_page) redirect_path = form.next_page_redirect_path(previous_page)
if @case_log_temp.valid?
@case_log.update!(answers_for_page)
next_page = form.next_page(previous_page)
redirect_path = if next_page == :check_answers
subsection = form.subsection_for_page(previous_page)
"case_log_#{subsection}_check_answers_path"
else
"case_log_#{next_page}_path"
end
redirect_to(send(redirect_path, @case_log)) redirect_to(send(redirect_path, @case_log))
else else
@errors = @case_log_temp.errors.full_messages page_info = form.all_pages[previous_page]
redirect_to(send("case_log_#{previous_page}_path", @case_log, errors: @errors)) render "form/page", locals: { form: form, page_key: previous_page, page_info: page_info }, status: :unprocessable_entity
end end
end end
@ -59,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 |errors = {}| define_method(page_key) do |errors = {}|
@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, errors: errors } 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

12
app/helpers/numeric_questions_helper.rb

@ -0,0 +1,12 @@
module NumericQuestionsHelper
def numeric_question_html_attributes(question)
return {} if question["fields-to-add"].blank? || question["result-field"].blank?
{
"data-controller": "numeric-question",
"data-action": "numeric-question#calculateFields",
"data-target": "#{question['result-field'].to_s.dasherize}-field",
"data-calculated": question["fields-to-add"].to_json,
}
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;
}) })

12
app/javascript/controllers/numeric_question_controller.js

@ -0,0 +1,12 @@
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
calculateFields() {
const affectedField = this.element.dataset.target;
const fieldsToAdd = JSON.parse(this.element.dataset.calculated).map(x => `${x.replaceAll("_","-")}-field`);
const valuesToAdd = fieldsToAdd.map(x => document.getElementById(x).value).filter(x => x);
const newValue = valuesToAdd.map(x => parseInt(x)).reduce((a, b) => a + b, 0);
const elementToUpdate = document.getElementById(affectedField);
elementToUpdate.value = newValue;
}
}

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)

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

@ -1,5 +1,7 @@
<%= f.govuk_number_field question_key, <%= f.govuk_number_field question_key,
hint: { text: question["hint_text"] }, hint: { text: question["hint_text"] },
label: { text: question["header"].html_safe, size: "l"}, label: { text: question["header"].html_safe, size: "l"},
min: question["min"], max: question["max"], step: question["step"], width: 20 min: question["min"], max: question["max"], step: question["step"],
width: 20, :readonly => question["readonly"],
**numeric_question_html_attributes(question)
%> %>

13
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 %>
@ -12,20 +12,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| %>
<% if params[:errors].present? %> <%= f.govuk_error_summary %>
<%= params[:errors] %>
<%= f.govuk_error_summary 'Uh-oh, spaghettios' %>
<% end %>
<% 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 %>

27
config/forms/2021_2022.json

@ -287,6 +287,7 @@
"header": "Has the tenant ever served in the UK armed forces?", "header": "Has the tenant ever served in the UK armed forces?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"check_answer_label": "Armed Forces",
"answer_options": { "answer_options": {
"0": "Yes - a regular", "0": "Yes - a regular",
"1": "Yes - a reserve", "1": "Yes - a reserve",
@ -302,6 +303,7 @@
"header": "Are they still serving?", "header": "Are they still serving?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"check_answer_label": "When did they leave the Armed Forces?",
"answer_options": { "answer_options": {
"0": "Yes", "0": "Yes",
"1": "No - they left up to 5 years ago", "1": "No - they left up to 5 years ago",
@ -313,6 +315,7 @@
"header": "Were they seriously injured or ill as a result of their service?", "header": "Were they seriously injured or ill as a result of their service?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"check_answer_label": "Has anyone in the household been seriously injured or ill as a result of their service in the armed forces?",
"answer_options": { "answer_options": {
"0": "Yes", "0": "Yes",
"1": "No", "1": "No",
@ -323,6 +326,7 @@
"header": "Was the tenant the spouse or civil partner of someone who served in the UK armed forces?", "header": "Was the tenant the spouse or civil partner of someone who served in the UK armed forces?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"check_answer_label": "Was the tenant the spouse or civil partner of someone who served in the UK armed forces?",
"answer_options": { "answer_options": {
"0": "Yes - was the spouse or civil partner of a UK Armed Forces member and have separated within the last 2 years", "0": "Yes - was the spouse or civil partner of a UK Armed Forces member and have separated within the last 2 years",
"1": "Yes - was the spouse or civil partner of a UK Armed Forces member who died within the last 2 years", "1": "Yes - was the spouse or civil partner of a UK Armed Forces member who died within the last 2 years",
@ -340,6 +344,7 @@
"header": "Does anyone in the household have any of the following that they expect to last for 12 months or more:<ul><li>Physical Condition</li><li>Mental Health Condition</li><li>Other Illness</li></ul>", "header": "Does anyone in the household have any of the following that they expect to last for 12 months or more:<ul><li>Physical Condition</li><li>Mental Health Condition</li><li>Other Illness</li></ul>",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"check_answer_label": "Physical, mental health or illness in the household",
"answer_options": { "answer_options": {
"0": "Yes", "0": "Yes",
"1": "No", "1": "No",
@ -357,6 +362,7 @@
"header": "Is anyone in the household pregnant?", "header": "Is anyone in the household pregnant?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"check_answer_label": "Pregnancy in the household",
"answer_options": { "answer_options": {
"0": "Yes", "0": "Yes",
"1": "No", "1": "No",
@ -373,6 +379,7 @@
"header": "Are any of these affected by their condition or illness?", "header": "Are any of these affected by their condition or illness?",
"hint_text": "Select all that apply", "hint_text": "Select all that apply",
"type": "checkbox", "type": "checkbox",
"check_answer_label": "Disability requirements",
"answer_options": { "answer_options": {
"0": "Fully wheelchair accessible housing", "0": "Fully wheelchair accessible housing",
"1": "Wheelchair access to essential rooms", "1": "Wheelchair access to essential rooms",
@ -395,6 +402,7 @@
"header": "Are any of these affected by their condition or illness?", "header": "Are any of these affected by their condition or illness?",
"hint_text": "Select all that apply", "hint_text": "Select all that apply",
"type": "checkbox", "type": "checkbox",
"check_answer_label": "Conditions or illnesses",
"answer_options": { "answer_options": {
"0": "Vision - such as blindness or partial sight", "0": "Vision - such as blindness or partial sight",
"1": "Hearing - such as deafness or partial hearing", "1": "Hearing - such as deafness or partial hearing",
@ -1136,7 +1144,9 @@
"hint_text": "Eligible for housing benefit or Universal Credit", "hint_text": "Eligible for housing benefit or Universal Credit",
"type": "numeric", "type": "numeric",
"min": 0, "min": 0,
"step": 1 "step": 1,
"fields-to-add": ["basic_rent", "service_charge", "personal_service_charge", "support_charge"],
"result-field": "total_charge"
}, },
"service_charge": { "service_charge": {
"check_answer_label": "Service Charge", "check_answer_label": "Service Charge",
@ -1144,7 +1154,9 @@
"hint_text": "Eligible for housing benefit or Universal Credit", "hint_text": "Eligible for housing benefit or Universal Credit",
"type": "numeric", "type": "numeric",
"min": 0, "min": 0,
"step": 1 "step": 1,
"fields-to-add": ["basic_rent", "service_charge", "personal_service_charge", "support_charge"],
"result-field": "total_charge"
}, },
"personal_service_charge": { "personal_service_charge": {
"check_answer_label": "Personal Service Charge", "check_answer_label": "Personal Service Charge",
@ -1152,7 +1164,9 @@
"hint_text": "Not eligible for housing benefit or Universal Credit. For example, hot water excluding water rates.", "hint_text": "Not eligible for housing benefit or Universal Credit. For example, hot water excluding water rates.",
"type": "numeric", "type": "numeric",
"min": 0, "min": 0,
"step": 1 "step": 1,
"fields-to-add": ["basic_rent", "service_charge", "personal_service_charge", "support_charge"],
"result-field": "total_charge"
}, },
"support_charge": { "support_charge": {
"check_answer_label": "Support Charge", "check_answer_label": "Support Charge",
@ -1160,7 +1174,9 @@
"hint_text": "This is to fund housing-related support services included in the tenancy agreement", "hint_text": "This is to fund housing-related support services included in the tenancy agreement",
"type": "numeric", "type": "numeric",
"min": 0, "min": 0,
"step": 1 "step": 1,
"fields-to-add": ["basic_rent", "service_charge", "personal_service_charge", "support_charge"],
"result-field": "total_charge"
}, },
"total_charge": { "total_charge": {
"check_answer_label": "Total Charge", "check_answer_label": "Total Charge",
@ -1168,7 +1184,8 @@
"hint_text": "This is the total of rent and all charges", "hint_text": "This is the total of rent and all charges",
"type": "numeric", "type": "numeric",
"min": 0, "min": 0,
"step": 1 "step": 1,
"readonly": true
}, },
"outstanding_amount": { "outstanding_amount": {
"check_answer_label": "After housing benefit and/or housing element of UC payment is received, will there be an outstanding amount for basic rent and/or benefit eligible charges?", "check_answer_label": "After housing benefit and/or housing element of UC payment is received, will there be an outstanding amount for basic rent and/or benefit eligible charges?",

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

58
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,17 +103,33 @@ 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)
}.from(original_value).to(answer) }.from(original_value).to(answer)
end end
end end
it "updates total value of the rent", js: true do
visit("/case_logs/#{id}/rent")
fill_in("basic_rent", with: 3)
expect(page).to have_field("total-charge-field", with: "3")
fill_in("service_charge", with: 2)
expect(page).to have_field("total-charge-field", with: "5")
fill_in("personal_service_charge", with: 1)
expect(page).to have_field("total-charge-field", with: "6")
fill_in("support_charge", with: 4)
expect(page).to have_field("total-charge-field", with: "10")
end
end end
describe "Back link directs correctly" do describe "Back link directs correctly" do
@ -126,7 +142,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 +166,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 +191,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 +245,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

21
spec/helpers/numeric_questions_helper_spec.rb

@ -0,0 +1,21 @@
require "rails_helper"
RSpec.describe NumericQuestionsHelper do
let(:form) { Form.new(2021, 2022) }
let(:questions) { form.questions_for_page("rent") }
describe "html attributes" do
it "returns empty hash if fields-to-add or result-field are empty " do
expect(numeric_question_html_attributes(questions["total_charge"])).to eq({})
end
it "returns html attributes if fields-to-add or result-field are not empty " do
expect(numeric_question_html_attributes(questions["basic_rent"])).to eq({
"data-controller": "numeric-question",
"data-action": "numeric-question#calculateFields",
"data-target": "#{questions['basic_rent']['result-field'].to_s.dasherize}-field",
"data-calculated": questions["basic_rent"]["fields-to-add"].to_json,
})
end
end
end
Loading…
Cancel
Save