Browse Source

Merge remote-tracking branch 'origin/main' into cldc-1304-export-fixes

pull/650/head
Stéphane Meny 3 years ago
parent
commit
449aec1559
No known key found for this signature in database
GPG Key ID: 9D0AFEA988527923
  1. 1
      .github/workflows/staging_pipeline.yml
  2. 2
      app/components/primary_navigation_component.rb
  3. 18
      app/controllers/schemes_controller.rb
  4. 29
      app/frontend/application.js
  5. 6
      app/frontend/controllers/accessible_autocomplete_controller.js
  6. 6
      app/frontend/controllers/application.js
  7. 10
      app/frontend/controllers/conditional_filter_controller.js
  8. 40
      app/frontend/controllers/conditional_question_controller.js
  9. 6
      app/frontend/controllers/filter_layout_controller.js
  10. 8
      app/frontend/controllers/govukfrontend_controller.js
  11. 26
      app/frontend/controllers/index.js
  12. 39
      app/frontend/controllers/numeric_question_controller.js
  13. 26
      app/frontend/modules/filter_toggle.js
  14. 21
      app/helpers/navigation_items_helper.rb
  15. 17
      app/models/form.rb
  16. 8
      app/models/form_handler.rb
  17. 20
      app/models/location.rb
  18. 5
      app/models/scheme.rb
  19. 4
      app/services/exports/case_log_export_service.rb
  20. 42
      app/services/imports/case_logs_field_import_service.rb
  21. 27
      app/services/imports/case_logs_import_service.rb
  22. 6
      app/views/organisations/schemes.html.erb
  23. 4
      app/views/schemes/_scheme_list.html.erb
  24. 6
      app/views/schemes/index.html.erb
  25. 41
      app/views/schemes/locations.html.erb
  26. 12
      app/views/schemes/show.html.erb
  27. 12
      babel.config.js
  28. 138
      config/forms/setup/log_setup.json
  29. 7
      config/initializers/feature_toggle.rb
  30. 8
      config/routes.rb
  31. 17
      db/migrate/20220614124115_create_locations.rb
  32. 18
      db/schema.rb
  33. 40
      db/seeds.rb
  34. 2
      lib/tasks/data_import_field.rake
  35. 7
      lib/tasks/lint.rake
  36. 8
      package.json
  37. 8
      spec/components/primary_navigation_component_spec.rb
  38. 3
      spec/factories/case_log.rb
  39. 13
      spec/factories/location.rb
  40. 2
      spec/factories/scheme.rb
  41. 18
      spec/features/form/tasklist_page_spec.rb
  42. 61
      spec/features/schemes_spec.rb
  43. 2
      spec/fixtures/exports/case_logs.xml
  44. 76
      spec/fixtures/forms/2022_2023.json
  45. 71
      spec/fixtures/forms/setup/log_setup.json
  46. 4
      spec/fixtures/softwire_imports/case_logs/166fc004-392e-47a8-acb8-1c018734882b.xml
  47. 2
      spec/fixtures/softwire_imports/case_logs/5ybz29dj-l33t-k1l0-hj86-n4k4ma77xkcd.xml
  48. 2
      spec/fixtures/softwire_imports/case_logs/893ufj2s-lq77-42m4-rty6-ej09gh585uy1.xml
  49. 85
      spec/helpers/navigation_items_helper_spec.rb
  50. 4
      spec/helpers/tab_nav_helper_spec.rb
  51. 4
      spec/helpers/tasklist_helper_spec.rb
  52. 14
      spec/lib/tasks/date_import_field_spec.rb
  53. 33
      spec/models/case_log_spec.rb
  54. 4
      spec/models/form_handler_spec.rb
  55. 2
      spec/models/organisation_spec.rb
  56. 3
      spec/models/rent_period_spec.rb
  57. 7
      spec/requests/case_logs_controller_spec.rb
  58. 26
      spec/requests/organisations_controller_spec.rb
  59. 235
      spec/requests/schemes_controller_spec.rb
  60. 122
      spec/services/imports/case_logs_field_import_service_spec.rb
  61. 5
      spec/services/imports/case_logs_import_service_spec.rb
  62. 34
      webpack.config.js
  63. 951
      yarn.lock

1
.github/workflows/staging_pipeline.yml

@ -8,6 +8,7 @@ on:
types: types:
- opened - opened
- ready_for_review - ready_for_review
- synchronize
workflow_dispatch: workflow_dispatch:
defaults: defaults:

2
app/components/primary_navigation_component.rb

@ -3,7 +3,7 @@ class PrimaryNavigationComponent < ViewComponent::Base
def initialize(items:) def initialize(items:)
@items = items @items = items
Rails.env.production? ? @items = @items.reject { |nav_item| nav_item.text.include?("Supported housing") } : @items FeatureToggle.supported_housing_schemes_enabled? ? @items : @items.reject! { |nav_item| nav_item.text.include?("Schemes") }
super super
end end

18
app/controllers/schemes_controller.rb

@ -3,10 +3,11 @@ class SchemesController < ApplicationController
include Modules::SearchFilter include Modules::SearchFilter
before_action :authenticate_user! before_action :authenticate_user!
before_action :find_resource, except: %i[index]
before_action :authenticate_scope! before_action :authenticate_scope!
def index def index
redirect_to supported_housing_organisation_path(current_user.organisation) unless current_user.support? redirect_to schemes_organisation_path(current_user.organisation) unless current_user.support?
all_schemes = Scheme.all all_schemes = Scheme.all
@pagy, @schemes = pagy(filtered_collection(all_schemes, search_term)) @pagy, @schemes = pagy(filtered_collection(all_schemes, search_term))
@ -16,7 +17,12 @@ class SchemesController < ApplicationController
def show def show
@scheme = Scheme.find_by(id: params[:id]) @scheme = Scheme.find_by(id: params[:id])
render_not_found and return unless (current_user.organisation == @scheme.organisation) || current_user.support? end
def locations
@scheme = Scheme.find_by(id: params[:id])
@pagy, @locations = pagy(@scheme.locations)
@total_count = @scheme.locations.size
end end
private private
@ -25,7 +31,15 @@ private
params["search"] params["search"]
end end
def find_resource
@scheme = Scheme.find_by(id: params[:id])
end
def authenticate_scope! def authenticate_scope!
head :unauthorized and return unless current_user.data_coordinator? || current_user.support? head :unauthorized and return unless current_user.data_coordinator? || current_user.support?
if %w[show locations].include?(action_name) && !((current_user.organisation == @scheme.organisation) || current_user.support?)
render_not_found and return
end
end end
end end

29
app/frontend/application.js

@ -1,22 +1,23 @@
// This file is automatically compiled by Webpack, along with any other files // This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in // present in this directory. You're encouraged to place your actual application
// a relevant structure within app/javascript and only use these pack files to reference // logic in a relevant structure within app/javascript and only use these pack
// that code so it'll be compiled. // files to reference that code so it'll be compiled.
// Polyfills for IE // Polyfills for IE
import "@webcomponents/webcomponentsjs" import '@webcomponents/webcomponentsjs'
import "core-js/stable" import 'core-js/stable'
import "regenerator-runtime/runtime" import 'regenerator-runtime/runtime'
import "@stimulus/polyfills" import '@stimulus/polyfills'
import "custom-event-polyfill" import 'custom-event-polyfill'
import "intersection-observer" import 'intersection-observer'
// //
import GOVUKFrontend from 'govuk-frontend'
import GOVUKPrototypeComponents from 'govuk-prototype-components'
import './styles/application.scss'
import './controllers'
require.context("govuk-frontend/govuk/assets") require.context('govuk-frontend/govuk/assets')
import GOVUKFrontend from "govuk-frontend"
import GOVUKPrototypeComponents from "govuk-prototype-components"
import "./styles/application.scss"
import "./controllers"
GOVUKFrontend.initAll() GOVUKFrontend.initAll()
GOVUKPrototypeComponents.initAll() GOVUKPrototypeComponents.initAll()

6
app/frontend/controllers/accessible_autocomplete_controller.js

@ -1,9 +1,9 @@
import { Controller } from "@hotwired/stimulus" import { Controller } from '@hotwired/stimulus'
import accessibleAutocomplete from "accessible-autocomplete" import accessibleAutocomplete from 'accessible-autocomplete'
import 'accessible-autocomplete/dist/accessible-autocomplete.min.css' import 'accessible-autocomplete/dist/accessible-autocomplete.min.css'
export default class extends Controller { export default class extends Controller {
connect() { connect () {
accessibleAutocomplete.enhanceSelectElement({ accessibleAutocomplete.enhanceSelectElement({
defaultValue: '', defaultValue: '',
selectElement: this.element selectElement: this.element

6
app/frontend/controllers/application.js

@ -1,10 +1,10 @@
import { Application } from "@hotwired/stimulus" import { Application } from '@hotwired/stimulus'
const application = Application.start() const application = Application.start()
// Configure Stimulus development experience // Configure Stimulus development experience
application.warnings = true application.warnings = true
application.debug = false application.debug = false
window.Stimulus = application window.Stimulus = application
export { application } export { application }

10
app/frontend/controllers/conditional_filter_controller.js

@ -1,13 +1,13 @@
import { Controller } from "@hotwired/stimulus" import { Controller } from '@hotwired/stimulus'
export default class extends Controller { export default class extends Controller {
initialize() { initialize () {
this.clearIfHidden() this.clearIfHidden()
} }
clearIfHidden() { clearIfHidden () {
if(this.element.style["display"] == "none") { if (this.element.style.display === 'none') {
this.element.value = "" this.element.value = ''
} }
} }
} }

40
app/frontend/controllers/conditional_question_controller.js

@ -1,36 +1,36 @@
import { Controller } from "@hotwired/stimulus" import { Controller } from '@hotwired/stimulus'
export default class extends Controller { export default class extends Controller {
initialize() { initialize () {
this.displayConditional() this.displayConditional()
} }
displayConditional() { displayConditional () {
if(this.element.checked) { if (this.element.checked) {
let selectedValue = this.element.value const selectedValue = this.element.value
let conditional_for = JSON.parse(this.element.dataset.info) const conditionalFor = JSON.parse(this.element.dataset.info)
Object.entries(conditional_for).map(([targetQuestion, conditions]) => {
if(conditions.map(String).includes(String(selectedValue))) { Object.entries(conditionalFor).forEach(([targetQuestion, conditions]) => {
if (!conditions.map(String).includes(String(selectedValue))) {
const textNumericInput = document.getElementById(`case-log-${targetQuestion.replaceAll('_', '-')}-field`)
if (textNumericInput == null) {
const dateInputs = [1, 2, 3].map((idx) => {
return document.getElementById(`case_log_${targetQuestion}_${idx}i`)
})
this.clearDateInputs(dateInputs)
} else { } else {
const textNumericInput = document.getElementById(`case-log-${targetQuestion.replaceAll("_","-")}-field`) this.clearTextNumericInput(textNumericInput)
if (textNumericInput == null) {
const dateInputs = [1,2,3].map((idx) => {
return document.getElementById(`case_log_${targetQuestion}_${idx}i`)
})
this.clearDateInputs(dateInputs)
} else {
this.clearTextNumericInput(textNumericInput)
} }
} }
}) })
} }
} }
clearTextNumericInput(input) { clearTextNumericInput (input) {
input.value = "" input.value = ''
} }
clearDateInputs(inputs) { clearDateInputs (inputs) {
inputs.forEach((input) => { input.value = "" }) inputs.forEach((input) => { input.value = '' })
} }
} }

6
app/frontend/controllers/filter_layout_controller.js

@ -1,8 +1,8 @@
import { Controller } from "@hotwired/stimulus"; import { Controller } from '@hotwired/stimulus'
import { FilterToggle } from "../modules/filter_toggle.js" import { FilterToggle } from '../modules/filter_toggle.js'
export default class extends Controller { export default class extends Controller {
connect() { connect () {
const filterToggle = new FilterToggle({ const filterToggle = new FilterToggle({
bigModeMediaQuery: '(min-width: 48.0625em)', bigModeMediaQuery: '(min-width: 48.0625em)',
closeButton: { closeButton: {

8
app/frontend/controllers/govukfrontend_controller.js

@ -1,9 +1,9 @@
import GOVUKFrontend from "govuk-frontend"; import GOVUKFrontend from 'govuk-frontend'
import GOVUKPrototypeComponents from "govuk-prototype-components" import GOVUKPrototypeComponents from 'govuk-prototype-components'
import { Controller } from "@hotwired/stimulus"; import { Controller } from '@hotwired/stimulus'
export default class extends Controller { export default class extends Controller {
connect() { connect () {
GOVUKFrontend.initAll() GOVUKFrontend.initAll()
GOVUKPrototypeComponents.initAll() GOVUKPrototypeComponents.initAll()
} }

26
app/frontend/controllers/index.js

@ -1,22 +1,22 @@
// Load all the controllers within this directory and all subdirectories. // Load all the controllers within this directory and all subdirectories.
// Controller files must be named *_controller.js. // Controller files must be named *_controller.js.
import { application } from "./application" import { application } from './application'
import AccessibleAutocompleteController from "./accessible_autocomplete_controller.js" import AccessibleAutocompleteController from './accessible_autocomplete_controller.js'
application.register("accessible-autocomplete", AccessibleAutocompleteController)
import ConditionalFilterController from "./conditional_filter_controller.js" import ConditionalFilterController from './conditional_filter_controller.js'
application.register("conditional-filter", ConditionalFilterController)
import ConditionalQuestionController from "./conditional_question_controller.js" import ConditionalQuestionController from './conditional_question_controller.js'
application.register("conditional-question", ConditionalQuestionController)
import GovukfrontendController from "./govukfrontend_controller.js" import GovukfrontendController from './govukfrontend_controller.js'
application.register("govukfrontend", GovukfrontendController)
import NumericQuestionController from "./numeric_question_controller.js" import NumericQuestionController from './numeric_question_controller.js'
application.register("numeric-question", NumericQuestionController)
import FilterLayoutController from "./filter_layout_controller.js" import FilterLayoutController from './filter_layout_controller.js'
application.register("filter-layout", FilterLayoutController) application.register('accessible-autocomplete', AccessibleAutocompleteController)
application.register('conditional-filter', ConditionalFilterController)
application.register('conditional-question', ConditionalQuestionController)
application.register('govukfrontend', GovukfrontendController)
application.register('numeric-question', NumericQuestionController)
application.register('filter-layout', FilterLayoutController)

39
app/frontend/controllers/numeric_question_controller.js

@ -1,27 +1,26 @@
import { Controller } from "@hotwired/stimulus" import { Controller } from '@hotwired/stimulus'
export default class extends Controller { export default class extends Controller {
connect() { connect () {
const affectedField = this.element.dataset.target; const affectedField = this.element.dataset.target
const targetQuestion = affectedField.split("case-log-")[1].split("-field")[0] const targetQuestion = affectedField.split('case-log-')[1].split('-field')[0]
const div = document.getElementById(targetQuestion + "_div"); const div = document.getElementById(targetQuestion + '_div')
div.style.display = "block"; div.style.display = 'block'
} }
calculateFields() { calculateFields () {
const affectedField = this.element.dataset.target; const affectedField = this.element.dataset.target
const fieldsToAdd = JSON.parse(this.element.dataset.calculated).map(x => `case-log-${x.replaceAll("_","-")}-field`); const fieldsToAdd = JSON.parse(this.element.dataset.calculated).map(x => `case-log-${x.replaceAll('_', '-')}-field`)
const valuesToAdd = fieldsToAdd.map(x => getFieldValue(x)).filter(x => x); const valuesToAdd = fieldsToAdd.map(x => getFieldValue(x)).filter(x => x)
const newValue = valuesToAdd.map(x => parseFloat(x)).reduce((a, b) => a + b, 0).toFixed(2); const newValue = valuesToAdd.map(x => parseFloat(x)).reduce((a, b) => a + b, 0).toFixed(2)
const elementToUpdate = document.getElementById(affectedField); const elementToUpdate = document.getElementById(affectedField)
elementToUpdate.value = newValue; elementToUpdate.value = newValue
} }
}
const getFieldValue = (field) => {
const elementFieldToAdd = document.getElementById(field)
if (elementFieldToAdd) {
return elementFieldToAdd.value
} }
let getFieldValue = (field) => { return document.getElementById(`${field}-error`).value
const elementFieldToAdd= document.getElementById(field)
if (elementFieldToAdd) {
return elementFieldToAdd.value
}
return document.getElementById(`${field}-error`).value
} }

26
app/frontend/modules/filter_toggle.js

@ -25,7 +25,7 @@ export class FilterToggle {
} }
enableSmallMode () { enableSmallMode () {
this.options.filter.container.setAttribute("tabindex", "-1") this.options.filter.container.setAttribute('tabindex', '-1')
this.hideMenu() this.hideMenu()
this.addMenuButton() this.addMenuButton()
this.addCloseButton() this.addCloseButton()
@ -33,8 +33,8 @@ export class FilterToggle {
addCloseButton () { addCloseButton () {
if (this.options.closeButton) { if (this.options.closeButton) {
this.closeButton = document.createElement("button") this.closeButton = document.createElement('button')
this.closeButton.classList.add("app-filter__close") this.closeButton.classList.add('app-filter__close')
this.closeButton.innerText = this.options.closeButton.text this.closeButton.innerText = this.options.closeButton.text
this.closeButton.type = 'button' this.closeButton.type = 'button'
this.closeButton.addEventListener('click', this.onCloseClick.bind(this)) this.closeButton.addEventListener('click', this.onCloseClick.bind(this))
@ -56,13 +56,13 @@ export class FilterToggle {
} }
addMenuButton () { addMenuButton () {
this.menuButton = document.createElement("button") this.menuButton = document.createElement('button')
this.menuButton.setAttribute("aria-expanded", "false") this.menuButton.setAttribute('aria-expanded', 'false')
this.menuButton.setAttribute("aria-has-popup", "true") this.menuButton.setAttribute('aria-has-popup', 'true')
this.menuButton.classList.add("govuk-button", this.options.toggleButton.classes, "app-filter-toggle__button") this.menuButton.classList.add('govuk-button', this.options.toggleButton.classes, 'app-filter-toggle__button')
this.menuButton.innerText = this.options.toggleButton.showText this.menuButton.innerText = this.options.toggleButton.showText
this.menuButton.type = "button" this.menuButton.type = 'button'
this.menuButton.addEventListener("click", this.onMenuButtonClick.bind(this)) this.menuButton.addEventListener('click', this.onMenuButtonClick.bind(this))
this.options.toggleButton.container.prepend(this.menuButton) this.options.toggleButton.container.prepend(this.menuButton)
} }
@ -76,18 +76,18 @@ export class FilterToggle {
hideMenu () { hideMenu () {
if (this.menuButton) { if (this.menuButton) {
this.menuButton.setAttribute("aria-expanded", "false") this.menuButton.setAttribute('aria-expanded', 'false')
this.menuButton.innerText = this.options.toggleButton.showText this.menuButton.innerText = this.options.toggleButton.showText
} }
this.options.filter.container.setAttribute("hidden", true) this.options.filter.container.setAttribute('hidden', true)
} }
showMenu () { showMenu () {
if (this.menuButton) { if (this.menuButton) {
this.menuButton.setAttribute("aria-expanded", "true") this.menuButton.setAttribute('aria-expanded', 'true')
this.menuButton.innerText = this.options.toggleButton.hideText this.menuButton.innerText = this.options.toggleButton.hideText
} }
this.options.filter.container.removeAttribute("hidden") this.options.filter.container.removeAttribute('hidden')
} }
onMenuButtonClick () { onMenuButtonClick () {

21
app/helpers/navigation_items_helper.rb

@ -7,12 +7,12 @@ module NavigationItemsHelper
NavigationItem.new("Organisations", organisations_path, organisations_current?(path)), NavigationItem.new("Organisations", organisations_path, organisations_current?(path)),
NavigationItem.new("Users", "/users", users_current?(path)), NavigationItem.new("Users", "/users", users_current?(path)),
NavigationItem.new("Logs", case_logs_path, logs_current?(path)), NavigationItem.new("Logs", case_logs_path, logs_current?(path)),
NavigationItem.new("Supported housing", "/supported-housing", supported_housing_current?(path)), NavigationItem.new("Schemes", "/schemes", supported_housing_schemes_current?(path)),
] ]
elsif current_user.data_coordinator? elsif current_user.data_coordinator?
[ [
NavigationItem.new("Logs", case_logs_path, logs_current?(path)), NavigationItem.new("Logs", case_logs_path, logs_current?(path)),
NavigationItem.new("Supported housing", "/supported-housing", subnav_supported_housing_path?(path)), NavigationItem.new("Schemes", "/schemes", subnav_supported_housing_schemes_path?(path)),
NavigationItem.new("Users", users_organisation_path(current_user.organisation), subnav_users_path?(path)), NavigationItem.new("Users", users_organisation_path(current_user.organisation), subnav_users_path?(path)),
NavigationItem.new("About your organisation", "/organisations/#{current_user.organisation.id}", subnav_details_path?(path)), NavigationItem.new("About your organisation", "/organisations/#{current_user.organisation.id}", subnav_details_path?(path)),
] ]
@ -28,12 +28,19 @@ module NavigationItemsHelper
def secondary_items(path, current_organisation_id) def secondary_items(path, current_organisation_id)
[ [
NavigationItem.new("Logs", "/organisations/#{current_organisation_id}/logs", subnav_logs_path?(path)), NavigationItem.new("Logs", "/organisations/#{current_organisation_id}/logs", subnav_logs_path?(path)),
NavigationItem.new("Supported housing", "/organisations/#{current_organisation_id}/supported-housing", subnav_supported_housing_path?(path)), NavigationItem.new("Schemes", "/organisations/#{current_organisation_id}/schemes", subnav_supported_housing_schemes_path?(path)),
NavigationItem.new("Users", "/organisations/#{current_organisation_id}/users", subnav_users_path?(path)), NavigationItem.new("Users", "/organisations/#{current_organisation_id}/users", subnav_users_path?(path)),
NavigationItem.new("About this organisation", "/organisations/#{current_organisation_id}", subnav_details_path?(path)), NavigationItem.new("About this organisation", "/organisations/#{current_organisation_id}", subnav_details_path?(path)),
] ]
end end
def scheme_items(path, current_scheme_id, title)
[
NavigationItem.new("Scheme", "/schemes/#{current_scheme_id}", !path.include?("locations")),
NavigationItem.new(title, "/schemes/#{current_scheme_id}/locations", path.include?("locations")),
]
end
private private
def logs_current?(path) def logs_current?(path)
@ -44,16 +51,16 @@ private
path == "/users" || path.include?("/users/") path == "/users" || path.include?("/users/")
end end
def supported_housing_current?(path) def supported_housing_schemes_current?(path)
path == "/supported-housing" || path.include?("/supported-housing/") path == "/schemes" || path.include?("/schemes/")
end end
def organisations_current?(path) def organisations_current?(path)
path == "/organisations" || path.include?("/organisations/") path == "/organisations" || path.include?("/organisations/")
end end
def subnav_supported_housing_path?(path) def subnav_supported_housing_schemes_path?(path)
path.include?("/organisations") && path.include?("/supported-housing") || path.include?("/supported-housing/") path.include?("/organisations") && path.include?("/schemes") || path.include?("/schemes/")
end end
def subnav_users_path?(path) def subnav_users_path?(path)

17
app/models/form.rb

@ -1,19 +1,24 @@
class Form class Form
attr_reader :form_definition, :sections, :subsections, :pages, :questions, attr_reader :form_definition, :sections, :subsections, :pages, :questions,
:start_date, :end_date, :type, :name :start_date, :end_date, :type, :name, :setup_definition,
:setup_sections, :form_sections
def initialize(form_path, name) def initialize(form_path, name, setup_path)
raise "No setup definition file exists for given path".freeze unless File.exist?(setup_path)
raise "No form definition file exists for given year".freeze unless File.exist?(form_path) raise "No form definition file exists for given year".freeze unless File.exist?(form_path)
@form_definition = JSON.parse(File.open(form_path).read)
@name = name @name = name
@start_date = Time.iso8601(form_definition["start_date"]) @setup_definition = JSON.parse(File.open(setup_path).read)
@end_date = Time.iso8601(form_definition["end_date"]) @setup_sections = setup_definition["sections"].map { |id, s| Form::Section.new(id, s, self) } || []
@form_definition = JSON.parse(File.open(form_path).read)
@form_sections = form_definition["sections"].map { |id, s| Form::Section.new(id, s, self) }
@type = form_definition["form_type"] @type = form_definition["form_type"]
@sections = form_definition["sections"].map { |id, s| Form::Section.new(id, s, self) } @sections = setup_sections + form_sections
@subsections = sections.flat_map(&:subsections) @subsections = sections.flat_map(&:subsections)
@pages = subsections.flat_map(&:pages) @pages = subsections.flat_map(&:pages)
@questions = pages.flat_map(&:questions) @questions = pages.flat_map(&:questions)
@start_date = Time.iso8601(form_definition["start_date"])
@end_date = Time.iso8601(form_definition["end_date"])
end end
def get_subsection(id) def get_subsection(id)

8
app/models/form_handler.rb

@ -21,12 +21,18 @@ private
directories.each do |directory| directories.each do |directory|
Dir.glob("#{directory}/*.json").each do |form_path| Dir.glob("#{directory}/*.json").each do |form_path|
form_name = File.basename(form_path, ".json") form_name = File.basename(form_path, ".json")
forms[form_name] = Form.new(form_path, form_name) forms[form_name] = Form.new(form_path, form_name, setup_path)
end end
end end
forms forms
end end
def setup_path
return "spec/fixtures/forms/setup/log_setup.json" if Rails.env.test?
"config/forms/setup/log_setup.json"
end
def directories def directories
Rails.env.test? ? ["spec/fixtures/forms"] : ["config/forms"] Rails.env.test? ? ["spec/fixtures/forms"] : ["config/forms"]
end end

20
app/models/location.rb

@ -0,0 +1,20 @@
class Location < ApplicationRecord
belongs_to :scheme
WHEELCHAIR_ADAPTATIONS = {
no: 0,
yes: 1,
}.freeze
enum wheelchair_adaptation: WHEELCHAIR_ADAPTATIONS
def display_attributes
[
{ name: "Location code ", value: location_code, suffix: false },
{ name: "Postcode", value: postcode, suffix: county },
{ name: "Type of unit", value: type_of_unit, suffix: false },
{ name: "Type of building", value: type_of_building, suffix: false },
{ name: "Wheelchair adaptation", value: wheelchair_adaptation, suffix: false },
]
end
end

5
app/models/scheme.rb

@ -1,5 +1,6 @@
class Scheme < ApplicationRecord class Scheme < ApplicationRecord
belongs_to :organisation belongs_to :organisation
has_many :locations
scope :search_by_code, ->(code) { where("code ILIKE ?", "%#{code}%") } scope :search_by_code, ->(code) { where("code ILIKE ?", "%#{code}%") }
scope :search_by_service_name, ->(name) { where("service_name ILIKE ?", "%#{name}%") } scope :search_by_service_name, ->(name) { where("service_name ILIKE ?", "%#{name}%") }
@ -66,8 +67,8 @@ class Scheme < ApplicationRecord
{ name: "Service code", value: code }, { name: "Service code", value: code },
{ name: "Name", value: service_name }, { name: "Name", value: service_name },
{ name: "Confidential information", value: sensitive_display }, { name: "Confidential information", value: sensitive_display },
{ name: "Managing agent", value: organisation.name }, { name: "Managed by", value: organisation.name },
{ name: "Type of service", value: scheme_type_display }, { name: "Type of scheme", value: scheme_type_display },
{ name: "Registered under Care Standards Act 2000", value: registered_under_care_act_display }, { name: "Registered under Care Standards Act 2000", value: registered_under_care_act_display },
{ name: "Total number of units", value: total_units }, { name: "Total number of units", value: total_units },
{ name: "Primary client group", value: primary_client_group_display }, { name: "Primary client group", value: primary_client_group_display },

4
app/services/exports/case_log_export_service.rb

@ -180,6 +180,10 @@ module Exports
attribute_hash["createddate"] = attribute_hash["created_at"] attribute_hash["createddate"] = attribute_hash["created_at"]
attribute_hash["uploaddate"] = attribute_hash["updated_at"] attribute_hash["uploaddate"] = attribute_hash["updated_at"]
attribute_hash["cbl"] = 2 if attribute_hash["cbl"]&.zero?
attribute_hash["cap"] = 2 if attribute_hash["cap"]&.zero?
attribute_hash["chr"] = 2 if attribute_hash["chr"]&.zero?
# Age refused # Age refused
(1..8).each do |index| (1..8).each do |index|
attribute_hash["age#{index}"] = -9 if attribute_hash["age#{index}_known"] == 1 attribute_hash["age#{index}"] = -9 if attribute_hash["age#{index}_known"] == 1

42
app/services/imports/case_logs_field_import_service.rb

@ -6,6 +6,8 @@ module Imports
import_from(folder, :update_tenant_code) import_from(folder, :update_tenant_code)
when "major_repairs" when "major_repairs"
import_from(folder, :update_major_repairs) import_from(folder, :update_major_repairs)
when "lettings_allocation"
import_from(folder, :update_lettings_allocation)
else else
raise "Updating #{field} is not supported by the field import service" raise "Updating #{field} is not supported by the field import service"
end end
@ -13,6 +15,36 @@ module Imports
private private
def update_lettings_allocation(xml_doc)
old_id = field_value(xml_doc, "meta", "document-id")
previous_status = field_value(xml_doc, "meta", "status")
record = CaseLog.find_by(old_id:)
if record.present? && previous_status.include?("submitted")
cbl = unsafe_string_as_integer(xml_doc, "Q15CBL")
chr = unsafe_string_as_integer(xml_doc, "Q15CHR")
cap = unsafe_string_as_integer(xml_doc, "Q15CAP")
if cbl == 2 && record.cbl == 1
record.update!(cbl: 0)
@logger.info("Case Log #{record.id}'s cbl value has been updated'")
end
if chr == 2 && record.chr == 1
record.update!(chr: 0)
@logger.info("Case Log #{record.id}'s chr value has been updated'")
end
if cap == 2 && record.cap == 1
record.update!(cap: 0)
@logger.info("Case Log #{record.id}'s cap value has been updated'")
end
if cbl == 2 && chr == 2 && cap == 2 && record.letting_allocation_unknown.nil?
record.update!(letting_allocation_unknown: 1)
@logger.info("Case Log #{record.id}'s letting_allocation_unknown value has been updated'")
end
else
@logger.warn("Could not find record matching legacy ID #{old_id}")
end
end
def update_major_repairs(xml_doc) def update_major_repairs(xml_doc)
old_id = field_value(xml_doc, "meta", "document-id") old_id = field_value(xml_doc, "meta", "document-id")
record = CaseLog.find_by(old_id:) record = CaseLog.find_by(old_id:)
@ -67,5 +99,15 @@ module Imports
str = field_value(xml_doc, "xmlns", attribute) str = field_value(xml_doc, "xmlns", attribute)
str.presence str.presence
end end
# Unsafe: A string that has more than just the integer value
def unsafe_string_as_integer(xml_doc, attribute)
str = string_or_nil(xml_doc, attribute)
if str.nil?
nil
else
str.to_i
end
end
end end
end end

27
app/services/imports/case_logs_import_service.rb

@ -123,9 +123,10 @@ module Imports
attributes["rp_hardship"] = unsafe_string_as_integer(xml_doc, "Q14b4").present? ? 1 : nil attributes["rp_hardship"] = unsafe_string_as_integer(xml_doc, "Q14b4").present? ? 1 : nil
attributes["rp_dontknow"] = unsafe_string_as_integer(xml_doc, "Q14b5").present? ? 1 : nil attributes["rp_dontknow"] = unsafe_string_as_integer(xml_doc, "Q14b5").present? ? 1 : nil
attributes["cbl"] = unsafe_string_as_integer(xml_doc, "Q15CBL").present? ? 1 : nil attributes["cbl"] = allocation_system(unsafe_string_as_integer(xml_doc, "Q15CBL"))
attributes["chr"] = unsafe_string_as_integer(xml_doc, "Q15CHR").present? ? 1 : nil attributes["chr"] = allocation_system(unsafe_string_as_integer(xml_doc, "Q15CHR"))
attributes["cap"] = unsafe_string_as_integer(xml_doc, "Q15CAP").present? ? 1 : nil attributes["cap"] = allocation_system(unsafe_string_as_integer(xml_doc, "Q15CAP"))
attributes["letting_allocation_unknown"] = allocation_system_unknown(attributes["cbl"], attributes["chr"], attributes["cap"])
attributes["referral"] = unsafe_string_as_integer(xml_doc, "Q16") attributes["referral"] = unsafe_string_as_integer(xml_doc, "Q16")
attributes["period"] = unsafe_string_as_integer(xml_doc, "Q17") attributes["period"] = unsafe_string_as_integer(xml_doc, "Q17")
@ -553,6 +554,26 @@ module Imports
end end
end end
def allocation_system(value)
case value
when 1
1
when 2
0
end
end
def allocation_system_unknown(cbl, chr, cap)
allocation_values = [cbl, chr, cap]
if allocation_values.all?(&:nil?)
nil
elsif allocation_values.all? { |att| att&.zero? }
1
else
0
end
end
def apply_date_consistency!(attributes) def apply_date_consistency!(attributes)
return if attributes["voiddate"].nil? || attributes["startdate"].nil? return if attributes["voiddate"].nil? || attributes["startdate"].nil?

6
app/views/organisations/schemes.html.erb

@ -1,9 +1,9 @@
<% item_label = format_label(@pagy.count, "scheme") %> <% item_label = format_label(@pagy.count, "scheme") %>
<% title = format_title(@searched, "Supported housing services", current_user, item_label, @pagy.count, @organisation.name) %> <% title = format_title(@searched, "Supported housing schemes", current_user, item_label, @pagy.count, @organisation.name) %>
<% content_for :title, title %> <% content_for :title, title %>
<%= render partial: "organisations/headings", locals: current_user.support? ? { main: @organisation.name, sub: nil } : { main: "Supported housing services", sub: current_user.organisation.name } %> <%= render partial: "organisations/headings", locals: current_user.support? ? { main: @organisation.name, sub: nil } : { main: "Schemes", sub: current_user.organisation.name } %>
<% if current_user.support? %> <% if current_user.support? %>
<%= render SubNavigationComponent.new( <%= render SubNavigationComponent.new(
@ -11,7 +11,7 @@
) %> ) %>
<% end %> <% end %>
<h2 class="govuk-visually-hidden">Supported housing services</h2> <h2 class="govuk-visually-hidden">Supported housing schemes</h2>
<%= render SearchComponent.new(current_user:, search_label: "Search by service name or code", value: @searched) %> <%= render SearchComponent.new(current_user:, search_label: "Search by service name or code", value: @searched) %>

4
app/views/schemes/_scheme_list.html.erb

@ -14,10 +14,10 @@
<% row.cell(header: true, text: "Code", html_attributes: { <% row.cell(header: true, text: "Code", html_attributes: {
scope: "col", scope: "col",
}) %> }) %>
<% row.cell(header: true, text: "Service", html_attributes: { <% row.cell(header: true, text: "Scheme", html_attributes: {
scope: "col", scope: "col",
}) %> }) %>
<% row.cell(header: true, text: "Managing agent", html_attributes: { <% row.cell(header: true, text: "Managed by", html_attributes: {
scope: "col", scope: "col",
}) %> }) %>
<% row.cell(header: true, text: "Created", html_attributes: { <% row.cell(header: true, text: "Created", html_attributes: {

6
app/views/schemes/index.html.erb

@ -1,11 +1,11 @@
<% item_label = format_label(@pagy.count, "scheme") %> <% item_label = format_label(@pagy.count, "scheme") %>
<% title = format_title(@searched, "Supported housing services", current_user, item_label, @pagy.count, nil) %> <% title = format_title(@searched, "Supported housing schemes", current_user, item_label, @pagy.count, nil) %>
<% content_for :title, title %> <% content_for :title, title %>
<%= render partial: "organisations/headings", locals: current_user.support? ? { main: "Supported housing services", sub: nil } : { main: "Supported housing services", sub: current_user.organisation.name } %> <%= render partial: "organisations/headings", locals: current_user.support? ? { main: "Supported housing schemes", sub: nil } : { main: "Supported housing schemes", sub: current_user.organisation.name } %>
<h2 class="govuk-visually-hidden">Supported housing services</h2> <h2 class="govuk-visually-hidden">Supported housing schemes</h2>
<%= render SearchComponent.new(current_user:, search_label: "Search by service name or code", value: @searched) %> <%= render SearchComponent.new(current_user:, search_label: "Search by service name or code", value: @searched) %>

41
app/views/schemes/locations.html.erb

@ -0,0 +1,41 @@
<% title = @scheme.service_name %>
<% content_for :title, title %>
<%= govuk_back_link(href: request.referer.to_s) %>
<%= render partial: "organisations/headings", locals: { main: @scheme.service_name, sub: nil } %>
<%= render SubNavigationComponent.new(items: scheme_items(request.path, @scheme.id, @scheme.locations.count.eql?(1) ? "1 location" : "#{@scheme.locations.count} locations")) %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-three-quarters">
<% @locations.each do |location| %>
<section class="x-govuk-summary-card govuk-!-margin-bottom-6">
<header class="x-govuk-summary-card__header">
<h2 class="x-govuk-summary-card__title">
<%= "#{location.address_line1}, #{location.address_line2}" %>
</h2>
</header>
<div class="x-govuk-summary-card__body">
<dl class="govuk-summary-list">
<% location.display_attributes.each do |attribute| %>
<div class="govuk-summary-list__row">
<dt class="govuk-summary-list__key">
<%= attribute[:name] %>
</dt>
<dd class="govuk-summary-list__value app-!-font-tabular">
<span class="govuk-!-margin-right-4"><%= attribute[:value] %></span>
<% if attribute[:suffix] %>
<span class="govuk-!-font-weight-regular app-!-colour-muted"><%= attribute[:suffix] %></span>
<% end %>
</dd>
</div>
<% end %>
</dl>
</div>
</section>
<% end %>
</div>
</div>
<%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "locations" } %>

12
app/views/schemes/show.html.erb

@ -1,17 +1,21 @@
<% title = @scheme.service_name %> <% title = @scheme.service_name %>
<% content_for :title, title %> <% content_for :title, title %>
<%= govuk_back_link(href: request.referer.to_s) %>
<%= render partial: "organisations/headings", locals: { main: @scheme.service_name, sub: nil } %> <%= render partial: "organisations/headings", locals: { main: @scheme.service_name, sub: nil } %>
<%= render SubNavigationComponent.new(items: scheme_items(request.path, @scheme.id, @scheme.locations.count.eql?(1) ? "1 location" : "#{@scheme.locations.count} locations")) %>
<div class="govuk-grid-row"> <div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds"> <div class="govuk-grid-column-two-thirds">
<%= govuk_summary_list do |summary_list| %> <%= govuk_summary_list do |summary_list| %>
<% @scheme.display_attributes.each do |attr| %> <% @scheme.display_attributes.each do |attr| %>
<%= summary_list.row do |row| %> <%= summary_list.row do |row| %>
<% row.key { attr[:name].to_s.humanize } %> <% row.key { attr[:name].eql?("Registered under Care Standards Act 2000") ? "Registered under Care Standards Act 2000" : attr[:name].to_s.humanize } %>
<% row.value { details_html(attr) } %> <% row.value { details_html(attr) } %>
<% end %>
<% end %> <% end %>
<% end %>
<% end %> <% end %>
</div> </div>
</div> </div>

12
babel.config.js

@ -1,9 +1,9 @@
module.exports = function(api) { module.exports = function (api) {
var validEnv = ['development', 'test', 'production'] const validEnv = ['development', 'test', 'production']
var currentEnv = api.env() const currentEnv = api.env()
var isDevelopmentEnv = api.env('development') const isDevelopmentEnv = api.env('development')
var isProductionEnv = api.env('production') const isProductionEnv = api.env('production')
var isTestEnv = api.env('test') const isTestEnv = api.env('test')
if (!validEnv.includes(currentEnv)) { if (!validEnv.includes(currentEnv)) {
throw new Error( throw new Error(

138
config/forms/setup/log_setup.json

@ -0,0 +1,138 @@
{
"form_type": "setup",
"sections": {
"setup": {
"label": "Before you start",
"subsections": {
"setup": {
"label": "Set up this lettings log",
"pages": {
"needs_type": {
"header": "",
"description": "",
"questions": {
"needstype": {
"check_answer_label": "Needs type",
"header": "What is the needs type?",
"hint_text": "",
"type": "radio",
"answer_options": {
"1": {
"value": "General needs"
},
"2": {
"value": "Supported housing"
}
}
}
},
"derived": true,
"depends_on": [
{
"supported_housing_schemes_enabled?" : true
}
]
},
"renewal": {
"header": "",
"description": "",
"questions": {
"renewal": {
"check_answer_label": "Property renewal",
"header": "Is this letting a renewal?",
"hint_text": "A renewal is a letting to the same tenant in the same property.",
"type": "radio",
"answer_options": {
"1": {
"value": "Yes"
},
"0": {
"value": "No"
}
}
}
}
},
"tenancy_start_date": {
"header": "",
"description": "",
"questions": {
"startdate": {
"check_answer_label": "Tenancy start date",
"header": "What is the tenancy start date?",
"type": "date"
}
}
},
"rent_type": {
"header": "",
"description": "",
"questions": {
"rent_type": {
"check_answer_label": "Rent type",
"header": "What is the rent type?",
"hint_text": "",
"type": "radio",
"answer_options": {
"1": {
"value": "Affordable Rent"
},
"2": {
"value": "London Affordable Rent"
},
"4": {
"value": "London Living Rent"
},
"3": {
"value": "Rent to Buy"
},
"0": {
"value": "Social Rent"
},
"5": {
"value": "Other intermediate rent product"
}
},
"conditional_for": {
"irproduct_other": [5]
}
},
"irproduct_other": {
"check_answer_label": "Product name",
"header": "Name of rent product",
"type": "text"
}
}
},
"tenant_code": {
"header": "",
"description": "",
"questions": {
"tenant_code": {
"check_answer_label": "Tenant code",
"header": "What is the tenant code?",
"hint_text": "This is how you usually refer to this tenancy on your own systems.",
"type": "text",
"width": 10
}
}
},
"property_reference": {
"header": "",
"description": "",
"questions": {
"propcode": {
"check_answer_label": "Property reference",
"header": "What is the property reference?",
"hint_text": "This is how you usually refer to this property on your own systems.",
"type": "text",
"width": 10
}
}
}
}
}
}
}
}
}

7
config/initializers/feature_toggle.rb

@ -0,0 +1,7 @@
class FeatureToggle
def self.supported_housing_schemes_enabled?
return true unless Rails.env.production?
false
end
end

8
config/routes.rb

@ -35,7 +35,11 @@ Rails.application.routes.draw do
get "edit/password", to: "users#edit_password" get "edit/password", to: "users#edit_password"
end end
resources :schemes, path: "/supported-housing", only: %i[index show] resources :schemes, only: %i[index show] do
member do
get "locations", to: "schemes#locations"
end
end
resources :users do resources :users do
member do member do
@ -50,7 +54,7 @@ Rails.application.routes.draw do
get "users", to: "organisations#users" get "users", to: "organisations#users"
get "users/invite", to: "users/account#new" get "users/invite", to: "users/account#new"
get "logs", to: "organisations#logs" get "logs", to: "organisations#logs"
get "supported-housing", to: "organisations#schemes" get "schemes", to: "organisations#schemes"
end end
end end

17
db/migrate/20220614124115_create_locations.rb

@ -0,0 +1,17 @@
class CreateLocations < ActiveRecord::Migration[7.0]
def change
create_table :locations do |t|
t.string :location_code
t.string :postcode
t.string :type_of_unit
t.string :type_of_building
t.integer :wheelchair_adaptation
t.references :scheme, null: false, foreign_key: true
t.string :address_line1
t.string :address_line2
t.string :county
t.timestamps
end
end
end

18
db/schema.rb

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2022_06_13_123730) do ActiveRecord::Schema[7.0].define(version: 2022_06_14_124115) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -232,6 +232,21 @@ ActiveRecord::Schema[7.0].define(version: 2022_06_13_123730) do
t.index ["start_year", "lettype", "beds", "la"], name: "index_la_rent_ranges_on_start_year_and_lettype_and_beds_and_la", unique: true t.index ["start_year", "lettype", "beds", "la"], name: "index_la_rent_ranges_on_start_year_and_lettype_and_beds_and_la", unique: true
end end
create_table "locations", force: :cascade do |t|
t.string "location_code"
t.string "postcode"
t.string "type_of_unit"
t.string "type_of_building"
t.integer "wheelchair_adaptation"
t.bigint "scheme_id", null: false
t.string "address_line1"
t.string "address_line2"
t.string "county"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["scheme_id"], name: "index_locations_on_scheme_id"
end
create_table "logs_exports", force: :cascade do |t| create_table "logs_exports", force: :cascade do |t|
t.datetime "created_at", default: -> { "CURRENT_TIMESTAMP" } t.datetime "created_at", default: -> { "CURRENT_TIMESTAMP" }
t.datetime "started_at", null: false t.datetime "started_at", null: false
@ -349,5 +364,6 @@ ActiveRecord::Schema[7.0].define(version: 2022_06_13_123730) do
t.index ["item_type", "item_id"], name: "index_versions_on_item_type_and_item_id" t.index ["item_type", "item_id"], name: "index_versions_on_item_type_and_item_id"
end end
add_foreign_key "locations", "schemes"
add_foreign_key "schemes", "organisations" add_foreign_key "schemes", "organisations"
end end

40
db/seeds.rb

@ -70,7 +70,7 @@ unless Rails.env.test?
end end
if Rails.env.development? && Scheme.count.zero? if Rails.env.development? && Scheme.count.zero?
Scheme.create!( scheme1 = Scheme.create!(
code: "S878", code: "S878",
service_name: "Beulahside Care", service_name: "Beulahside Care",
sensitive: 0, sensitive: 0,
@ -85,7 +85,7 @@ unless Rails.env.test?
created_at: Time.zone.now, created_at: Time.zone.now,
) )
Scheme.create!( scheme2 = Scheme.create!(
code: "S312", code: "S312",
service_name: "Abdullahview Point", service_name: "Abdullahview Point",
sensitive: 0, sensitive: 0,
@ -114,6 +114,42 @@ unless Rails.env.test?
organisation: dummy_org, organisation: dummy_org,
created_at: Time.zone.now, created_at: Time.zone.now,
) )
Location.create!(
scheme: scheme1,
location_code: "S254-CU193AA",
postcode: "CU19 3AA",
address_line1: "Rectory Road",
address_line2: "North Chaim",
type_of_unit: "Self-contained flat or bedsit",
type_of_building: "Purpose-built",
county: "Mid Sussex",
wheelchair_adaptation: 0,
)
Location.create!(
scheme: scheme1,
location_code: "S254-DM250DC",
postcode: "DM25 0DC",
address_line1: "Smithy Lane",
address_line2: "North Kellieworth",
type_of_unit: "Self-contained flat or bedsit with common facilities",
type_of_building: "Converted from previous residential or non-residential property",
county: "Fife",
wheelchair_adaptation: 1,
)
Location.create!(
scheme: scheme2,
location_code: "S254-YX130WP",
postcode: "YX13 0WP",
address_line1: "Smithy Lane",
address_line2: "East Darwin",
type_of_unit: "Shared house or hostel",
type_of_building: "Converted from previous residential or non-residential property",
county: "Rochford",
wheelchair_adaptation: 1,
)
end end
pp "Seeded 3 dummy schemes" pp "Seeded 3 dummy schemes"

2
lib/tasks/data_import_field.rake

@ -9,7 +9,7 @@ namespace :core do
# We only allow a reduced list of known fields to be updatable # We only allow a reduced list of known fields to be updatable
case field case field
when "tenant_code", "major_repairs" when "tenant_code", "major_repairs", "lettings_allocation"
Imports::CaseLogsFieldImportService.new(storage_service).update_field(field, path) Imports::CaseLogsFieldImportService.new(storage_service).update_field(field, path)
else else
raise "Field #{field} cannot be updated by data_import_field" raise "Field #{field} cannot be updated by data_import_field"

7
lib/tasks/lint.rake

@ -8,10 +8,15 @@ task erblint: :environment do
sh "bundle exec erblint --lint-all" sh "bundle exec erblint --lint-all"
end end
desc "Run Standard"
task standard: :environment do
sh "yarn standard"
end
desc "Run Stylelint" desc "Run Stylelint"
task stylelint: :environment do task stylelint: :environment do
sh "yarn stylelint app/frontend/styles" sh "yarn stylelint app/frontend/styles"
end end
desc "Run all the linters" desc "Run all the linters"
task lint: %i[rubocop erblint stylelint] task lint: %i[rubocop erblint standard stylelint]

8
package.json

@ -20,7 +20,7 @@
"css-loader": "^6.7.1", "css-loader": "^6.7.1",
"custom-event-polyfill": "^1.0.7", "custom-event-polyfill": "^1.0.7",
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
"govuk-frontend": "^4.0.1", "govuk-frontend": "4.0.1",
"govuk-prototype-components": "^0.1.5", "govuk-prototype-components": "^0.1.5",
"html5shiv": "^3.7.3", "html5shiv": "^3.7.3",
"intersection-observer": "^0.12.0", "intersection-observer": "^0.12.0",
@ -36,6 +36,7 @@
"version": "0.1.0", "version": "0.1.0",
"devDependencies": { "devDependencies": {
"are-you-es5": "^2.1.2", "are-you-es5": "^2.1.2",
"standard": "^17.0.0",
"stylelint": "^14.7.1", "stylelint": "^14.7.1",
"stylelint-config-gds": "^0.2.0" "stylelint-config-gds": "^0.2.0"
}, },
@ -51,6 +52,11 @@
"last 1 safari version" "last 1 safari version"
] ]
}, },
"standard": {
"ignore": [
"app/frontend/vendor/*.js"
]
},
"stylelint": { "stylelint": {
"extends": "stylelint-config-gds/scss" "extends": "stylelint-config-gds/scss"
}, },

8
spec/components/primary_navigation_component_spec.rb

@ -6,7 +6,7 @@ RSpec.describe PrimaryNavigationComponent, type: :component do
NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true), NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true),
NavigationItemsHelper::NavigationItem.new("Users", "/users", false), NavigationItemsHelper::NavigationItem.new("Users", "/users", false),
NavigationItemsHelper::NavigationItem.new("Logs ", "/logs", false), NavigationItemsHelper::NavigationItem.new("Logs ", "/logs", false),
NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", false), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false),
] ]
end end
@ -36,7 +36,7 @@ RSpec.describe PrimaryNavigationComponent, type: :component do
expect(result.text).to include("Organisations") expect(result.text).to include("Organisations")
expect(result.text).to include("Users") expect(result.text).to include("Users")
expect(result.text).to include("Logs") expect(result.text).to include("Logs")
expect(result.text).to include("Supported housing") expect(result.text).to include("Schemes")
end end
end end
@ -45,9 +45,9 @@ RSpec.describe PrimaryNavigationComponent, type: :component do
allow(Rails.env).to receive(:production?).and_return(true) allow(Rails.env).to receive(:production?).and_return(true)
end end
it "doesn't render supported housing" do it "doesn't render schemes" do
result = render_inline(described_class.new(items:)) result = render_inline(described_class.new(items:))
expect(result.text).not_to include("Supported housing") expect(result.text).not_to include("Schemes")
end end
end end
end end

3
spec/factories/case_log.rb

@ -16,6 +16,9 @@ FactoryBot.define do
ppostcode_full { Faker::Address.postcode } ppostcode_full { Faker::Address.postcode }
age1 { 17 } age1 { 17 }
age2 { 19 } age2 { 19 }
renewal { 1 }
rent_type { 1 }
startdate { Time.zone.local(2021, 5, 1) }
end end
trait :soft_validations_triggered do trait :soft_validations_triggered do
status { 1 } status { 1 }

13
spec/factories/location.rb

@ -0,0 +1,13 @@
FactoryBot.define do
factory :location do
location_code { Faker::Name.initials(number: 10) }
postcode { Faker::Address.postcode }
address_line1 { Faker::Address.street_name }
address_line2 { Faker::Address.city }
type_of_unit { Faker::Lorem.word }
type_of_building { Faker::Lorem.word }
wheelchair_adaptation { 0 }
county { Faker::Address.state }
scheme
end
end

2
spec/factories/scheme.rb

@ -1,7 +1,7 @@
FactoryBot.define do FactoryBot.define do
factory :scheme do factory :scheme do
code { Faker::Name.initials(number: 4) } code { Faker::Name.initials(number: 4) }
service_name { Faker::Name.name_with_middle } service_name { Faker::Name.name }
sensitive { Faker::Number.within(range: 0..1) } sensitive { Faker::Number.within(range: 0..1) }
registered_under_care_act { Faker::Number.within(range: 0..1) } registered_under_care_act { Faker::Number.within(range: 0..1) }
support_type { Faker::Number.within(range: 0..6) } support_type { Faker::Number.within(range: 0..6) }

18
spec/features/form/tasklist_page_spec.rb

@ -27,6 +27,15 @@ RSpec.describe "Task List" do
managing_organisation: user.organisation, managing_organisation: user.organisation,
) )
end end
let(:setup_completed_log) do
FactoryBot.create(
:case_log,
:about_completed,
owning_organisation: user.organisation,
managing_organisation: user.organisation,
startdate: Time.zone.local(2021, 5, 1),
)
end
let(:id) { case_log.id } let(:id) { case_log.id }
let(:status) { case_log.status } let(:status) { case_log.status }
@ -40,14 +49,13 @@ RSpec.describe "Task List" do
end end
it "shows number of completed sections if one section is completed" do it "shows number of completed sections if one section is completed" do
answer_all_questions_in_income_subsection(empty_case_log) visit("/logs/#{setup_completed_log.id}")
visit("/logs/#{empty_case_log.id}") expect(page).to have_content("1 of 9 sections completed.")
expect(page).to have_content("1 of 8 sections completed.")
end end
it "show skip link for next incomplete section" do it "show skip link for next incomplete section" do
answer_all_questions_in_income_subsection(empty_case_log) answer_all_questions_in_income_subsection(setup_completed_log)
visit("/logs/#{empty_case_log.id}") visit("/logs/#{setup_completed_log.id}")
expect(page).to have_link("Skip to next incomplete section", href: /#household-characteristics/) expect(page).to have_link("Skip to next incomplete section", href: /#household-characteristics/)
end end

61
spec/features/schemes_spec.rb

@ -1,6 +1,6 @@
require "rails_helper" require "rails_helper"
RSpec.describe "Supported housing scheme Features" do RSpec.describe "Schemes scheme Features" do
context "when viewing list of schemes" do context "when viewing list of schemes" do
context "when I am signed as a support user and there are schemes in the database" do context "when I am signed as a support user and there are schemes in the database" do
let(:user) { FactoryBot.create(:user, :support, last_sign_in_at: Time.zone.now) } let(:user) { FactoryBot.create(:user, :support, last_sign_in_at: Time.zone.now) }
@ -25,13 +25,13 @@ RSpec.describe "Supported housing scheme Features" do
click_button("Submit") click_button("Submit")
end end
it "displays the link to the supported housing" do it "displays the link to the schemes" do
expect(page).to have_link("Supported housing", href: "/supported-housing") expect(page).to have_link("Schemes", href: "/schemes")
end end
context "when I click Supported housing" do context "when I click schemes" do
before do before do
click_link "Supported housing", href: "/supported-housing" click_link "Schemes", href: "/schemes"
end end
it "shows list of schemes" do it "shows list of schemes" do
@ -99,9 +99,9 @@ RSpec.describe "Supported housing scheme Features" do
click_button("Submit") click_button("Submit")
end end
context "when I visit supported housing page" do context "when I visit schemes page" do
before do before do
visit("supported-housing") visit("schemes")
end end
it "shows list of links to schemes" do it "shows list of links to schemes" do
@ -112,8 +112,10 @@ RSpec.describe "Supported housing scheme Features" do
end end
context "when I click to see individual scheme" do context "when I click to see individual scheme" do
let(:scheme) { schemes.first }
before do before do
click_link(schemes.first.service_name) click_link(scheme.service_name)
end end
it "shows me details about the selected scheme" do it "shows me details about the selected scheme" do
@ -128,6 +130,49 @@ RSpec.describe "Supported housing scheme Features" do
expect(page).to have_content(schemes.first.support_type_display) expect(page).to have_content(schemes.first.support_type_display)
expect(page).to have_content(schemes.first.intended_stay_display) expect(page).to have_content(schemes.first.intended_stay_display)
end end
context "when I click to go back" do
before do
visit("schemes")
click_link(scheme.service_name)
end
it "shows list of links to schemes" do
click_on("Back")
schemes.each do |scheme|
expect(page).to have_link(scheme.service_name)
expect(page).to have_content(scheme.primary_client_group_display)
end
end
end
context "when there are locations that belong to the selected scheme" do
let!(:schemes) { FactoryBot.create_list(:scheme, 5) }
let(:scheme) { schemes.first }
let!(:locations) { FactoryBot.create_list(:location, 3, scheme:) }
before do
visit("schemes")
click_link(scheme.service_name)
end
it "shows service and locations tab" do
expect(page).to have_link("Scheme")
expect(page).to have_link("#{scheme.locations.count} locations")
end
context "when I click locations link" do
before do
click_link("#{scheme.locations.count} locations")
end
it "shows details of those locations" do
locations.each do |location|
expect(page).to have_content(location.location_code)
end
end
end
end
end end
end end
end end

2
spec/fixtures/exports/case_logs.xml vendored

@ -56,7 +56,7 @@
<reasonpref>1</reasonpref> <reasonpref>1</reasonpref>
<cbl>1</cbl> <cbl>1</cbl>
<chr>1</chr> <chr>1</chr>
<cap>0</cap> <cap>2</cap>
<reasonother/> <reasonother/>
<housingneeds_a>1</housingneeds_a> <housingneeds_a>1</housingneeds_a>
<housingneeds_b>0</housingneeds_b> <housingneeds_b>0</housingneeds_b>

76
spec/fixtures/forms/2022_2023.json vendored

@ -3,82 +3,6 @@
"start_date": "2022-04-01T00:00:00.000+01:00", "start_date": "2022-04-01T00:00:00.000+01:00",
"end_date": "2023-07-01T00:00:00.000+01:00", "end_date": "2023-07-01T00:00:00.000+01:00",
"sections": { "sections": {
"setup": {
"label": "Before you start",
"subsections": {
"setup": {
"label": "Set up this lettings log",
"pages": {
"renewal": {
"header": "",
"description": "",
"questions": {
"renewal": {
"check_answer_label": "Property renewal",
"header": "Is this a renewal to the same tenant in the same property?",
"hint_text": "",
"type": "radio",
"answer_options": {
"1": "Yes",
"0": "No"
}
}
}
},
"startdate": {
"header": "",
"description": "",
"questions": {
"startdate": {
"check_answer_label": "Tenancy start date",
"header": "What is the tenancy start date?",
"hint_text": "For example, 27 3 2007",
"type": "date"
}
}
},
"about_this_letting": {
"header": "Tell us about this letting",
"description": "",
"questions": {
"rent_type": {
"check_answer_label": "Rent type",
"header": "What is the rent type?",
"hint_text": "",
"type": "radio",
"answer_options": {
"0": "Social rent",
"1": "Affordable rent",
"2": "London Affordable rent",
"3": "Rent to buy",
"4": "London living rent",
"5": "Other intermediate rent product"
},
"conditional_for": {
"intermediate_rent_product_name": [5]
}
},
"intermediate_rent_product_name": {
"check_answer_label": "Product name",
"header": "What is intermediate rent product name?",
"type": "text"
},
"needstype": {
"check_answer_label": "Needs type",
"header": "What is the needs type?",
"hint_text": "",
"type": "radio",
"answer_options": {
"0": "Supported housing",
"1": "General needs"
}
}
}
}
}
}
}
},
"household": { "household": {
"label": "About the household", "label": "About the household",
"subsections": { "subsections": {

71
spec/fixtures/forms/setup/log_setup.json vendored

@ -0,0 +1,71 @@
{
"form_type": "setup",
"sections": {
"setup": {
"label": "Before you start",
"subsections": {
"setup": {
"label": "Set up this lettings log",
"pages": {
"renewal": {
"header": "",
"description": "",
"questions": {
"renewal": {
"check_answer_label": "Property renewal",
"header": "Is this a renewal to the same tenant in the same property?",
"hint_text": "",
"type": "radio",
"answer_options": {
"1": "Yes",
"0": "No"
}
}
}
},
"startdate": {
"header": "",
"description": "",
"questions": {
"startdate": {
"check_answer_label": "Tenancy start date",
"header": "What is the tenancy start date?",
"hint_text": "For example, 27 3 2007",
"type": "date"
}
}
},
"about_this_letting": {
"header": "Tell us about this letting",
"description": "",
"questions": {
"rent_type": {
"check_answer_label": "Rent type",
"header": "What is the rent type?",
"hint_text": "",
"type": "radio",
"answer_options": {
"0": "Social rent",
"1": "Affordable rent",
"2": "London Affordable rent",
"3": "Rent to buy",
"4": "London living rent",
"5": "Other intermediate rent product"
},
"conditional_for": {
"intermediate_rent_product_name": [5]
}
},
"intermediate_rent_product_name": {
"check_answer_label": "Product name",
"header": "What is intermediate rent product name?",
"type": "text"
}
}
}
}
}
}
}
}
}

4
spec/fixtures/softwire_imports/case_logs/166fc004-392e-47a8-acb8-1c018734882b.xml vendored

@ -2,7 +2,7 @@
<meta:metadata xmlns:es="http://www.ecmascript.org/" xmlns:xqx="http://www.w3.org/2005/XQueryX" xmlns:XSLT="http://www.w3.org/1999/XSL/Transform/compile"> <meta:metadata xmlns:es="http://www.ecmascript.org/" xmlns:xqx="http://www.w3.org/2005/XQueryX" xmlns:XSLT="http://www.w3.org/1999/XSL/Transform/compile">
<meta:form-name>2021-CORE-IR-GN</meta:form-name> <meta:form-name>2021-CORE-IR-GN</meta:form-name>
<meta:document-id>166fc004-392e-47a8-acb8-1c018734882b</meta:document-id> <meta:document-id>166fc004-392e-47a8-acb8-1c018734882b</meta:document-id>
<meta:owner-user-id>e29c492473446dca4d50224f2bb7cf965a261d6f</meta:owner-user-id> <meta:owner-user-id>c3061a2e6ea0b702e6f6210d5c52d2a92612d2aa</meta:owner-user-id>
<meta:owner-institution-id>7c5bd5fb549c09a2c55d7cb90d7ba84927e64618</meta:owner-institution-id> <meta:owner-institution-id>7c5bd5fb549c09a2c55d7cb90d7ba84927e64618</meta:owner-institution-id>
<meta:managing-institution-id>7c5bd5fb549c09a2c55d7cb90d7ba84927e64618</meta:managing-institution-id> <meta:managing-institution-id>7c5bd5fb549c09a2c55d7cb90d7ba84927e64618</meta:managing-institution-id>
<meta:created-date>2022-04-12T14:10:59.953121Z</meta:created-date> <meta:created-date>2022-04-12T14:10:59.953121Z</meta:created-date>
@ -133,7 +133,7 @@
<Group> <Group>
<Q15CBL>2 No</Q15CBL> <Q15CBL>2 No</Q15CBL>
<Q15CHR>2 No</Q15CHR> <Q15CHR>2 No</Q15CHR>
<Q15CAP>2 No</Q15CAP> <Q15CAP>1 Yes</Q15CAP>
</Group> </Group>
<Group> <Group>
<Q16>10 Other social landlord</Q16> <Q16>10 Other social landlord</Q16>

2
spec/fixtures/softwire_imports/case_logs/5ybz29dj-l33t-k1l0-hj86-n4k4ma77xkcd.xml vendored

@ -131,7 +131,7 @@
<Q14b5/> <Q14b5/>
</Group> </Group>
<Group> <Group>
<Q15CBL>2 No</Q15CBL> <Q15CBL>1 Yes</Q15CBL>
<Q15CHR>2 No</Q15CHR> <Q15CHR>2 No</Q15CHR>
<Q15CAP>2 No</Q15CAP> <Q15CAP>2 No</Q15CAP>
</Group> </Group>

2
spec/fixtures/softwire_imports/case_logs/893ufj2s-lq77-42m4-rty6-ej09gh585uy1.xml vendored

@ -2,7 +2,7 @@
<meta:metadata xmlns:es="http://www.ecmascript.org/" xmlns:xqx="http://www.w3.org/2005/XQueryX" xmlns:XSLT="http://www.w3.org/1999/XSL/Transform/compile"> <meta:metadata xmlns:es="http://www.ecmascript.org/" xmlns:xqx="http://www.w3.org/2005/XQueryX" xmlns:XSLT="http://www.w3.org/1999/XSL/Transform/compile">
<meta:form-name>2021-CORE-IR-GN</meta:form-name> <meta:form-name>2021-CORE-IR-GN</meta:form-name>
<meta:document-id>893ufj2s-lq77-42m4-rty6-ej09gh585uy1</meta:document-id> <meta:document-id>893ufj2s-lq77-42m4-rty6-ej09gh585uy1</meta:document-id>
<meta:owner-user-id>e29c492473446dca4d50224f2bb7cf965a261d6f</meta:owner-user-id> <meta:owner-user-id>c3061a2e6ea0b702e6f6210d5c52d2a92612d2aa</meta:owner-user-id>
<meta:owner-institution-id>7c5bd5fb549c09a2c55d7cb90d7ba84927e64618</meta:owner-institution-id> <meta:owner-institution-id>7c5bd5fb549c09a2c55d7cb90d7ba84927e64618</meta:owner-institution-id>
<meta:managing-institution-id>7c5bd5fb549c09a2c55d7cb90d7ba84927e64618</meta:managing-institution-id> <meta:managing-institution-id>7c5bd5fb549c09a2c55d7cb90d7ba84927e64618</meta:managing-institution-id>
<meta:created-date>2022-04-11T13:46:23.953121Z</meta:created-date> <meta:created-date>2022-04-11T13:46:23.953121Z</meta:created-date>

85
spec/helpers/navigation_items_helper_spec.rb

@ -12,7 +12,7 @@ RSpec.describe NavigationItemsHelper do
let(:expected_navigation_items) do let(:expected_navigation_items) do
[ [
NavigationItemsHelper::NavigationItem.new("Logs", "/logs", true), NavigationItemsHelper::NavigationItem.new("Logs", "/logs", true),
NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", false), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false),
NavigationItemsHelper::NavigationItem.new("Users", users_path, false), NavigationItemsHelper::NavigationItem.new("Users", users_path, false),
NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false), NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false),
] ]
@ -27,7 +27,7 @@ RSpec.describe NavigationItemsHelper do
let(:expected_navigation_items) do let(:expected_navigation_items) do
[ [
NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false),
NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", false), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false),
NavigationItemsHelper::NavigationItem.new("Users", users_path, true), NavigationItemsHelper::NavigationItem.new("Users", users_path, true),
NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false), NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false),
] ]
@ -42,7 +42,7 @@ RSpec.describe NavigationItemsHelper do
let(:expected_navigation_items) do let(:expected_navigation_items) do
[ [
NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false),
NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", false), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false),
NavigationItemsHelper::NavigationItem.new("Users", users_path, false), NavigationItemsHelper::NavigationItem.new("Users", users_path, false),
NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, true), NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, true),
] ]
@ -57,7 +57,7 @@ RSpec.describe NavigationItemsHelper do
let(:expected_navigation_items) do let(:expected_navigation_items) do
[ [
NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false),
NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", false), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false),
NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false), NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false),
NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false), NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false),
] ]
@ -72,7 +72,7 @@ RSpec.describe NavigationItemsHelper do
let(:expected_navigation_items) do let(:expected_navigation_items) do
[ [
NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false),
NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", false), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false),
NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", true), NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", true),
NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false), NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false),
] ]
@ -87,14 +87,14 @@ RSpec.describe NavigationItemsHelper do
let(:expected_navigation_items) do let(:expected_navigation_items) do
[ [
NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false),
NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", true), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", true),
NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false), NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false),
NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false), NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false),
] ]
end end
it "returns navigation items with supported housing item set as current" do it "returns navigation items with Schemes item set as current" do
expect(primary_items("/supported-housing/1", current_user)).to eq(expected_navigation_items) expect(primary_items("/schemes/1", current_user)).to eq(expected_navigation_items)
end end
end end
end end
@ -108,7 +108,7 @@ RSpec.describe NavigationItemsHelper do
NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false),
NavigationItemsHelper::NavigationItem.new("Users", "/users", false), NavigationItemsHelper::NavigationItem.new("Users", "/users", false),
NavigationItemsHelper::NavigationItem.new("Logs", "/logs", true), NavigationItemsHelper::NavigationItem.new("Logs", "/logs", true),
NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", false), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false),
] ]
end end
@ -123,7 +123,7 @@ RSpec.describe NavigationItemsHelper do
NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false),
NavigationItemsHelper::NavigationItem.new("Users", "/users", true), NavigationItemsHelper::NavigationItem.new("Users", "/users", true),
NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false),
NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", false), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false),
] ]
end end
@ -138,7 +138,7 @@ RSpec.describe NavigationItemsHelper do
NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false),
NavigationItemsHelper::NavigationItem.new("Users", "/users", false), NavigationItemsHelper::NavigationItem.new("Users", "/users", false),
NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false),
NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", false), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false),
] ]
end end
@ -147,18 +147,18 @@ RSpec.describe NavigationItemsHelper do
end end
end end
context "when the user is on the supported housing page" do context "when the user is on the Schemes page" do
let(:expected_navigation_items) do let(:expected_navigation_items) do
[ [
NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false),
NavigationItemsHelper::NavigationItem.new("Users", "/users", false), NavigationItemsHelper::NavigationItem.new("Users", "/users", false),
NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false),
NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", true), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", true),
] ]
end end
it "returns navigation items with the users item set as current" do it "returns navigation items with the users item set as current" do
expect(primary_items("/supported-housing", current_user)).to eq(expected_navigation_items) expect(primary_items("/schemes", current_user)).to eq(expected_navigation_items)
end end
end end
@ -168,7 +168,7 @@ RSpec.describe NavigationItemsHelper do
NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false),
NavigationItemsHelper::NavigationItem.new("Users", "/users", true), NavigationItemsHelper::NavigationItem.new("Users", "/users", true),
NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false),
NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", false), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false),
] ]
end end
@ -183,12 +183,43 @@ RSpec.describe NavigationItemsHelper do
NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false),
NavigationItemsHelper::NavigationItem.new("Users", "/users", false), NavigationItemsHelper::NavigationItem.new("Users", "/users", false),
NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false),
NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", true), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", true),
] ]
end end
it "returns navigation items with supported housing item set as current" do let(:expected_scheme_items) do
expect(primary_items("/supported-housing/1", current_user)).to eq(expected_navigation_items) [
NavigationItemsHelper::NavigationItem.new("Scheme", "/schemes/1", true),
NavigationItemsHelper::NavigationItem.new("1 location", "/schemes/1/locations", false),
]
end
it "returns navigation items with Schemes item set as current" do
expect(primary_items("/schemes/1", current_user)).to eq(expected_navigation_items)
expect(scheme_items("/schemes/1", 1, "1 location")).to eq(expected_scheme_items)
end
end
context "when the user is on the scheme locations page" do
let(:expected_navigation_items) do
[
NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false),
NavigationItemsHelper::NavigationItem.new("Users", "/users", false),
NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false),
NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", true),
]
end
let(:expected_scheme_items) do
[
NavigationItemsHelper::NavigationItem.new("Scheme", "/schemes/1", false),
NavigationItemsHelper::NavigationItem.new("1 location", "/schemes/1/locations", true),
]
end
it "returns navigation items with Schemes item set as current" do
expect(primary_items("/schemes/1/locations", current_user)).to eq(expected_navigation_items)
expect(scheme_items("/schemes/1/locations", 1, "1 location")).to eq(expected_scheme_items)
end end
end end
@ -200,14 +231,14 @@ RSpec.describe NavigationItemsHelper do
NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true), NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true),
NavigationItemsHelper::NavigationItem.new("Users", "/users", false), NavigationItemsHelper::NavigationItem.new("Users", "/users", false),
NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false),
NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", false), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false),
] ]
end end
let(:expected_secondary_navigation_items) do let(:expected_secondary_navigation_items) do
[ [
NavigationItemsHelper::NavigationItem.new("Logs", "/organisations/#{current_user.organisation.id}/logs", true), NavigationItemsHelper::NavigationItem.new("Logs", "/organisations/#{current_user.organisation.id}/logs", true),
NavigationItemsHelper::NavigationItem.new("Supported housing", "/organisations/#{current_user.organisation.id}/supported-housing", false), NavigationItemsHelper::NavigationItem.new("Schemes", "/organisations/#{current_user.organisation.id}/schemes", false),
NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false), NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false),
NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", false), NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", false),
] ]
@ -226,14 +257,14 @@ RSpec.describe NavigationItemsHelper do
NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true), NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true),
NavigationItemsHelper::NavigationItem.new("Users", "/users", false), NavigationItemsHelper::NavigationItem.new("Users", "/users", false),
NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false),
NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", false), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false),
] ]
end end
let(:expected_secondary_navigation_items) do let(:expected_secondary_navigation_items) do
[ [
NavigationItemsHelper::NavigationItem.new("Logs", "/organisations/#{current_user.organisation.id}/logs", false), NavigationItemsHelper::NavigationItem.new("Logs", "/organisations/#{current_user.organisation.id}/logs", false),
NavigationItemsHelper::NavigationItem.new("Supported housing", "/organisations/#{current_user.organisation.id}/supported-housing", false), NavigationItemsHelper::NavigationItem.new("Schemes", "/organisations/#{current_user.organisation.id}/schemes", false),
NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", true), NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", true),
NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", false), NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", false),
] ]
@ -246,20 +277,20 @@ RSpec.describe NavigationItemsHelper do
end end
context "when the user is on organisation schemes page" do context "when the user is on organisation schemes page" do
let(:required_sub_path) { "supported-housing" } let(:required_sub_path) { "schemes" }
let(:expected_navigation_items) do let(:expected_navigation_items) do
[ [
NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true), NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true),
NavigationItemsHelper::NavigationItem.new("Users", "/users", false), NavigationItemsHelper::NavigationItem.new("Users", "/users", false),
NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false),
NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", false), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false),
] ]
end end
let(:expected_secondary_navigation_items) do let(:expected_secondary_navigation_items) do
[ [
NavigationItemsHelper::NavigationItem.new("Logs", "/organisations/#{current_user.organisation.id}/logs", false), NavigationItemsHelper::NavigationItem.new("Logs", "/organisations/#{current_user.organisation.id}/logs", false),
NavigationItemsHelper::NavigationItem.new("Supported housing", "/organisations/#{current_user.organisation.id}/supported-housing", true), NavigationItemsHelper::NavigationItem.new("Schemes", "/organisations/#{current_user.organisation.id}/schemes", true),
NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false), NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false),
NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", false), NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", false),
] ]
@ -278,14 +309,14 @@ RSpec.describe NavigationItemsHelper do
NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true), NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true),
NavigationItemsHelper::NavigationItem.new("Users", "/users", false), NavigationItemsHelper::NavigationItem.new("Users", "/users", false),
NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false),
NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", false), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false),
] ]
end end
let(:expected_secondary_navigation_items) do let(:expected_secondary_navigation_items) do
[ [
NavigationItemsHelper::NavigationItem.new("Logs", "/organisations/#{current_user.organisation.id}/logs", false), NavigationItemsHelper::NavigationItem.new("Logs", "/organisations/#{current_user.organisation.id}/logs", false),
NavigationItemsHelper::NavigationItem.new("Supported housing", "/organisations/#{current_user.organisation.id}/supported-housing", false), NavigationItemsHelper::NavigationItem.new("Schemes", "/organisations/#{current_user.organisation.id}/schemes", false),
NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false), NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false),
NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", true), NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", true),
] ]

4
spec/helpers/tab_nav_helper_spec.rb

@ -3,7 +3,7 @@ require "rails_helper"
RSpec.describe TabNavHelper do RSpec.describe TabNavHelper do
let(:organisation) { FactoryBot.create(:organisation) } let(:organisation) { FactoryBot.create(:organisation) }
let(:user) { FactoryBot.build(:user, organisation:) } let(:user) { FactoryBot.build(:user, organisation:) }
let(:scheme) { FactoryBot.build(:scheme) } let(:scheme) { FactoryBot.build(:scheme, service_name: "Some name") }
describe "#user_cell" do describe "#user_cell" do
it "returns user link and email separated by a newline character" do it "returns user link and email separated by a newline character" do
@ -21,7 +21,7 @@ RSpec.describe TabNavHelper do
describe "#scheme_cell" do describe "#scheme_cell" do
it "returns the scheme link service name and primary user group separated by a newline character" do it "returns the scheme link service name and primary user group separated by a newline character" do
expected_html = "<a class=\"govuk-link\" href=\"/supported-housing\">#{scheme.service_name}</a>\n<span class=\"govuk-visually-hidden\">Scheme </span><span class=\"govuk-!-font-weight-regular app-!-colour-muted\">#{scheme.primary_client_group_display}</span>" expected_html = "<a class=\"govuk-link\" href=\"/schemes\">#{scheme.service_name}</a>\n<span class=\"govuk-visually-hidden\">Scheme </span><span class=\"govuk-!-font-weight-regular app-!-colour-muted\">#{scheme.primary_client_group_display}</span>"
expect(scheme_cell(scheme)).to match(expected_html) expect(scheme_cell(scheme)).to match(expected_html)
end end
end end

4
spec/helpers/tasklist_helper_spec.rb

@ -17,7 +17,7 @@ RSpec.describe TasklistHelper do
describe "get sections count" do describe "get sections count" do
it "returns the total of sections if no status is given" do it "returns the total of sections if no status is given" do
expect(get_subsections_count(empty_case_log)).to eq(8) expect(get_subsections_count(empty_case_log)).to eq(9)
end end
it "returns 0 sections for completed sections if no sections are completed" do it "returns 0 sections for completed sections if no sections are completed" do
@ -25,7 +25,7 @@ RSpec.describe TasklistHelper do
end end
it "returns the number of not started sections" do it "returns the number of not started sections" do
expect(get_subsections_count(empty_case_log, :not_started)).to eq(7) expect(get_subsections_count(empty_case_log, :not_started)).to eq(8)
end end
it "returns the number of sections in progress" do it "returns the number of sections in progress" do

14
spec/lib/tasks/date_import_field_spec.rb

@ -42,6 +42,20 @@ describe "rake core:data_import_field", type: :task do
end end
end end
context "and we update the lettings_allocation fields" do
let(:field) { "lettings_allocation" }
it "properly configures the storage service" do
expect(StorageService).to receive(:new).with(paas_config_service, instance_name)
task.invoke(field, fixture_path)
end
it "calls the expected update method with parameters" do
expect(import_service).to receive(:update_field).with(field, fixture_path)
task.invoke(field, fixture_path)
end
end
context "and we update the major repairs fields" do context "and we update the major repairs fields" do
let(:field) { "major_repairs" } let(:field) { "major_repairs" }

33
spec/models/case_log_spec.rb

@ -211,10 +211,17 @@ RSpec.describe CaseLog do
}) })
end end
it "derives that all forms are general needs" do context "when a case log is created in production" do
record_from_db = ActiveRecord::Base.connection.execute("select needstype from case_logs where id=#{case_log.id}").to_a[0] before do
expect(record_from_db["needstype"]).to eq(1) allow(Rails.env).to receive(:production?).and_return(true)
expect(case_log["needstype"]).to eq(1) end
it "derives that all forms are general needs" do
case_log = FactoryBot.create(:case_log)
record_from_db = ActiveRecord::Base.connection.execute("select needstype from case_logs where id=#{case_log.id}").to_a[0]
expect(record_from_db["needstype"]).to eq(1)
expect(case_log["needstype"]).to eq(1)
end
end end
it "correctly derives and saves partial and full major repairs date" do it "correctly derives and saves partial and full major repairs date" do
@ -2097,4 +2104,22 @@ RSpec.describe CaseLog do
end end
end end
end end
describe "supported_housing_schemes_enabled?" do
it "returns true for the case log if the environment is not production" do
case_log = FactoryBot.create(:case_log)
expect(case_log.supported_housing_schemes_enabled?).to eq(true)
end
context "when in the production environment" do
before do
allow(Rails.env).to receive(:production?).and_return(true)
end
it "returns false for a case log" do
case_log = FactoryBot.create(:case_log)
expect(case_log.supported_housing_schemes_enabled?).to eq(false)
end
end
end
end end

4
spec/models/form_handler_spec.rb

@ -17,7 +17,7 @@ RSpec.describe FormHandler do
form_handler = described_class.instance form_handler = described_class.instance
form = form_handler.get_form(test_form_name) form = form_handler.get_form(test_form_name)
expect(form).to be_a(Form) expect(form).to be_a(Form)
expect(form.pages.count).to eq(34) expect(form.pages.count).to eq(37)
end end
end end
@ -32,7 +32,7 @@ RSpec.describe FormHandler do
it "loads the form once at boot time" do it "loads the form once at boot time" do
form_handler = described_class.instance form_handler = described_class.instance
expect(Form).not_to receive(:new).with(:any, test_form_name) expect(Form).not_to receive(:new).with(:any, test_form_name, :any)
expect(form_handler.get_form(test_form_name)).to be_a(Form) expect(form_handler.get_form(test_form_name)).to be_a(Form)
end end
end end

2
spec/models/organisation_spec.rb

@ -87,7 +87,7 @@ RSpec.describe Organisation, type: :model do
end end
it "has case logs" do it "has case logs" do
expect(organisation.case_logs.to_a).to eq([owned_case_log, managed_case_log]) expect(organisation.case_logs.to_a).to match_array([owned_case_log, managed_case_log])
end end
it "has case log status helper methods" do it "has case log status helper methods" do

3
spec/models/rent_period_spec.rb

@ -2,7 +2,8 @@ require "rails_helper"
RSpec.describe RentPeriod, type: :model do RSpec.describe RentPeriod, type: :model do
describe "rent period mapping" do describe "rent period mapping" do
let(:form) { Form.new("spec/fixtures/forms/2021_2022.json", "2021_2022") } let(:setup_path) { "spec/fixtures/forms/setup/log_setup.json" }
let(:form) { Form.new("spec/fixtures/forms/2021_2022.json", "2021_2022", setup_path) }
before do before do
allow(FormHandler.instance).to receive(:current_form).and_return(form) allow(FormHandler.instance).to receive(:current_form).and_return(form)

7
spec/requests/case_logs_controller_spec.rb

@ -288,7 +288,8 @@ RSpec.describe CaseLogsController, type: :request do
mrcdate: Time.zone.local(2022, 2, 1), mrcdate: Time.zone.local(2022, 2, 1),
startdate: Time.zone.local(2022, 12, 1), startdate: Time.zone.local(2022, 12, 1),
tenancy: 6, tenancy: 6,
managing_organisation: organisation) managing_organisation: organisation,
tenant_code: nil)
end end
it "shows case logs for multiple selected statuses and years" do it "shows case logs for multiple selected statuses and years" do
@ -622,7 +623,7 @@ RSpec.describe CaseLogsController, type: :request do
end end
it "displays a section status for a case log" do it "displays a section status for a case log" do
assert_select ".govuk-tag", text: /Not started/, count: 6 assert_select ".govuk-tag", text: /Not started/, count: 7
assert_select ".govuk-tag", text: /In progress/, count: 1 assert_select ".govuk-tag", text: /In progress/, count: 1
assert_select ".govuk-tag", text: /Completed/, count: 0 assert_select ".govuk-tag", text: /Completed/, count: 0
assert_select ".govuk-tag", text: /Cannot start yet/, count: 1 assert_select ".govuk-tag", text: /Cannot start yet/, count: 1
@ -645,7 +646,7 @@ RSpec.describe CaseLogsController, type: :request do
end end
it "displays a section status for a case log" do it "displays a section status for a case log" do
assert_select ".govuk-tag", text: /Not started/, count: 6 assert_select ".govuk-tag", text: /Not started/, count: 7
assert_select ".govuk-tag", text: /Completed/, count: 1 assert_select ".govuk-tag", text: /Completed/, count: 1
assert_select ".govuk-tag", text: /Cannot start yet/, count: 1 assert_select ".govuk-tag", text: /Cannot start yet/, count: 1
end end

26
spec/requests/organisations_controller_spec.rb

@ -31,8 +31,8 @@ RSpec.describe OrganisationsController, type: :request do
expect(response).to redirect_to("/account/sign-in") expect(response).to redirect_to("/account/sign-in")
end end
it "does not let you see supported housing list" do it "does not let you see schemes list" do
get "/organisations/#{organisation.id}/supported-housing", headers: headers, params: {} get "/organisations/#{organisation.id}/schemes", headers: headers, params: {}
expect(response).to redirect_to("/account/sign-in") expect(response).to redirect_to("/account/sign-in")
end end
end end
@ -48,11 +48,11 @@ RSpec.describe OrganisationsController, type: :request do
before do before do
allow(user).to receive(:need_two_factor_authentication?).and_return(false) allow(user).to receive(:need_two_factor_authentication?).and_return(false)
sign_in user sign_in user
get "/organisations/#{organisation.id}/supported-housing", headers:, params: {} get "/organisations/#{organisation.id}/schemes", headers:, params: {}
end end
it "has page heading" do it "has page heading" do
expect(page).to have_content("Supported housing services") expect(page).to have_content("Schemes")
end end
it "shows a search bar" do it "shows a search bar" do
@ -60,7 +60,7 @@ RSpec.describe OrganisationsController, type: :request do
end end
it "has hidden accebility field with description" do it "has hidden accebility field with description" do
expected_field = "<h2 class=\"govuk-visually-hidden\">Supported housing services</h2>" expected_field = "<h2 class=\"govuk-visually-hidden\">Supported housing schemes</h2>"
expect(CGI.unescape_html(response.body)).to include(expected_field) expect(CGI.unescape_html(response.body)).to include(expected_field)
end end
@ -77,7 +77,7 @@ RSpec.describe OrganisationsController, type: :request do
before do before do
allow(user).to receive(:need_two_factor_authentication?).and_return(false) allow(user).to receive(:need_two_factor_authentication?).and_return(false)
get "/organisations/#{organisation.id}/supported-housing?search=#{search_param}" get "/organisations/#{organisation.id}/schemes?search=#{search_param}"
end end
it "returns matching results" do it "returns matching results" do
@ -104,19 +104,19 @@ RSpec.describe OrganisationsController, type: :request do
before do before do
sign_in user sign_in user
get "/organisations/#{organisation.id}/supported-housing", headers:, params: {} get "/organisations/#{organisation.id}/schemes", headers:, params: {}
end end
it "has page heading" do it "has page heading" do
expect(page).to have_content("Supported housing services") expect(page).to have_content("Schemes")
end end
it "shows a search bar" do it "shows a search bar" do
expect(page).to have_field("search", type: "search") expect(page).to have_field("search", type: "search")
end end
it "has hidden accebility field with description" do it "has hidden accessibility field with description" do
expected_field = "<h2 class=\"govuk-visually-hidden\">Supported housing services</h2>" expected_field = "<h2 class=\"govuk-visually-hidden\">Supported housing schemes</h2>"
expect(CGI.unescape_html(response.body)).to include(expected_field) expect(CGI.unescape_html(response.body)).to include(expected_field)
end end
@ -131,7 +131,7 @@ RSpec.describe OrganisationsController, type: :request do
let!(:unauthorised_organisation) { FactoryBot.create(:organisation) } let!(:unauthorised_organisation) { FactoryBot.create(:organisation) }
before do before do
get "/organisations/#{unauthorised_organisation.id}/supported-housing", headers:, params: {} get "/organisations/#{unauthorised_organisation.id}/schemes", headers:, params: {}
end end
it "returns not found 404 from org details route" do it "returns not found 404 from org details route" do
@ -144,7 +144,7 @@ RSpec.describe OrganisationsController, type: :request do
let(:search_param) { "CODE321" } let(:search_param) { "CODE321" }
before do before do
get "/organisations/#{organisation.id}/supported-housing?search=#{search_param}" get "/organisations/#{organisation.id}/schemes?search=#{search_param}"
end end
it "returns matching results" do it "returns matching results" do
@ -159,7 +159,7 @@ RSpec.describe OrganisationsController, type: :request do
end end
it "has search in the title" do it "has search in the title" do
expect(page).to have_title("Supported housing services (1 scheme matching ‘#{search_param}’) - Submit social housing lettings and sales data (CORE) - GOV.UK") expect(page).to have_title("Supported housing schemes (1 scheme matching ‘#{search_param}’) - Submit social housing lettings and sales data (CORE) - GOV.UK")
end end
end end
end end

235
spec/requests/schemes_controller_spec.rb

@ -10,7 +10,7 @@ RSpec.describe SchemesController, type: :request do
describe "#index" do describe "#index" do
context "when not signed in" do context "when not signed in" do
it "redirects to the sign in page" do it "redirects to the sign in page" do
get "/supported-housing" get "/schemes"
expect(response).to redirect_to("/account/sign-in") expect(response).to redirect_to("/account/sign-in")
end end
end end
@ -20,7 +20,7 @@ RSpec.describe SchemesController, type: :request do
before do before do
sign_in user sign_in user
get "/supported-housing" get "/schemes"
end end
it "returns 401 unauthorized" do it "returns 401 unauthorized" do
@ -34,12 +34,12 @@ RSpec.describe SchemesController, type: :request do
before do before do
sign_in user sign_in user
get "/supported-housing" get "/schemes"
end end
it "redirects to the organisation schemes path" do it "redirects to the organisation schemes path" do
follow_redirect! follow_redirect!
expect(path).to match("/organisations/#{user.organisation.id}/supported-housing") expect(path).to match("/organisations/#{user.organisation.id}/schemes")
end end
end end
@ -47,11 +47,11 @@ RSpec.describe SchemesController, type: :request do
before do before do
allow(user).to receive(:need_two_factor_authentication?).and_return(false) allow(user).to receive(:need_two_factor_authentication?).and_return(false)
sign_in user sign_in user
get "/supported-housing" get "/schemes"
end end
it "has page heading" do it "has page heading" do
expect(page).to have_content("Supported housing services") expect(page).to have_content("Schemes")
end end
it "shows all schemes" do it "shows all schemes" do
@ -65,7 +65,7 @@ RSpec.describe SchemesController, type: :request do
end end
it "has correct title" do it "has correct title" do
expect(page).to have_title("Supported housing services - Submit social housing lettings and sales data (CORE) - GOV.UK") expect(page).to have_title("Supported housing schemes - Submit social housing lettings and sales data (CORE) - GOV.UK")
end end
it "shows the total organisations count" do it "shows the total organisations count" do
@ -73,7 +73,7 @@ RSpec.describe SchemesController, type: :request do
end end
it "has hidden accebility field with description" do it "has hidden accebility field with description" do
expected_field = "<h2 class=\"govuk-visually-hidden\">Supported housing services</h2>" expected_field = "<h2 class=\"govuk-visually-hidden\">Supported housing schemes</h2>"
expect(CGI.unescape_html(response.body)).to include(expected_field) expect(CGI.unescape_html(response.body)).to include(expected_field)
end end
@ -86,7 +86,7 @@ RSpec.describe SchemesController, type: :request do
context "when on the first page" do context "when on the first page" do
before do before do
get "/supported-housing" get "/schemes"
end end
it "shows the total schemes count" do it "shows the total schemes count" do
@ -98,7 +98,7 @@ RSpec.describe SchemesController, type: :request do
end end
it "has correct page 1 of 2 title" do it "has correct page 1 of 2 title" do
expect(page).to have_title("Supported housing services (page 1 of 2) - Submit social housing lettings and sales data (CORE) - GOV.UK") expect(page).to have_title("Supported housing schemes (page 1 of 2) - Submit social housing lettings and sales data (CORE) - GOV.UK")
end end
it "has pagination links" do it "has pagination links" do
@ -111,7 +111,7 @@ RSpec.describe SchemesController, type: :request do
context "when on the second page" do context "when on the second page" do
before do before do
get "/supported-housing?page=2" get "/schemes?page=2"
end end
it "shows the total schemes count" do it "shows the total schemes count" do
@ -130,7 +130,7 @@ RSpec.describe SchemesController, type: :request do
end end
it "has correct page 1 of 2 title" do it "has correct page 1 of 2 title" do
expect(page).to have_title("Supported housing services (page 2 of 2) - Submit social housing lettings and sales data (CORE) - GOV.UK") expect(page).to have_title("Supported housing schemes (page 2 of 2) - Submit social housing lettings and sales data (CORE) - GOV.UK")
end end
end end
end end
@ -140,7 +140,7 @@ RSpec.describe SchemesController, type: :request do
let(:search_param) { "CODE321" } let(:search_param) { "CODE321" }
before do before do
get "/supported-housing?search=#{search_param}" get "/schemes?search=#{search_param}"
end end
it "returns matching results" do it "returns matching results" do
@ -155,7 +155,7 @@ RSpec.describe SchemesController, type: :request do
end end
it "has search in the title" do it "has search in the title" do
expect(page).to have_title("Supported housing services (1 scheme matching ‘#{search_param}’) - Submit social housing lettings and sales data (CORE) - GOV.UK") expect(page).to have_title("Supported housing schemes (1 scheme matching ‘#{search_param}’) - Submit social housing lettings and sales data (CORE) - GOV.UK")
end end
end end
end end
@ -166,7 +166,7 @@ RSpec.describe SchemesController, type: :request do
context "when not signed in" do context "when not signed in" do
it "redirects to the sign in page" do it "redirects to the sign in page" do
get "/supported-housing/#{specific_scheme.id}" get "/schemes/#{specific_scheme.id}"
expect(response).to redirect_to("/account/sign-in") expect(response).to redirect_to("/account/sign-in")
end end
end end
@ -176,7 +176,7 @@ RSpec.describe SchemesController, type: :request do
before do before do
sign_in user sign_in user
get "/supported-housing/#{specific_scheme.id}" get "/schemes/#{specific_scheme.id}"
end end
it "returns 401 unauthorized" do it "returns 401 unauthorized" do
@ -194,7 +194,7 @@ RSpec.describe SchemesController, type: :request do
end end
it "has page heading" do it "has page heading" do
get "/supported-housing/#{specific_scheme.id}" get "/schemes/#{specific_scheme.id}"
expect(page).to have_content(specific_scheme.code) expect(page).to have_content(specific_scheme.code)
expect(page).to have_content(specific_scheme.service_name) expect(page).to have_content(specific_scheme.service_name)
expect(page).to have_content(specific_scheme.organisation.name) expect(page).to have_content(specific_scheme.organisation.name)
@ -211,11 +211,11 @@ RSpec.describe SchemesController, type: :request do
expect(page).to have_content(specific_scheme.intended_stay_display) expect(page).to have_content(specific_scheme.intended_stay_display)
end end
context "when coordinator attempts to see scheme belogning to a different organisation" do context "when coordinator attempts to see scheme belonging to a different organisation" do
let!(:specific_scheme) { FactoryBot.create(:scheme) } let!(:specific_scheme) { FactoryBot.create(:scheme) }
it "returns 404 not found" do it "returns 404 not found" do
get "/supported-housing/#{specific_scheme.id}" get "/schemes/#{specific_scheme.id}"
expect(response).to have_http_status(:not_found) expect(response).to have_http_status(:not_found)
end end
end end
@ -225,7 +225,7 @@ RSpec.describe SchemesController, type: :request do
before do before do
allow(user).to receive(:need_two_factor_authentication?).and_return(false) allow(user).to receive(:need_two_factor_authentication?).and_return(false)
sign_in user sign_in user
get "/supported-housing/#{specific_scheme.id}" get "/schemes/#{specific_scheme.id}"
end end
it "has page heading" do it "has page heading" do
@ -246,4 +246,199 @@ RSpec.describe SchemesController, type: :request do
end end
end end
end end
describe "#locations" do
let(:specific_scheme) { schemes.first }
context "when not signed in" do
it "redirects to the sign in page" do
get "/schemes/#{specific_scheme.id}/locations"
expect(response).to redirect_to("/account/sign-in")
end
end
context "when signed in as a data provider user" do
let(:user) { FactoryBot.create(:user) }
before do
sign_in user
get "/schemes/#{specific_scheme.id}/locations"
end
it "returns 401 unauthorized" do
request
expect(response).to have_http_status(:unauthorized)
end
end
context "when signed in as a data coordinator user" do
let(:user) { FactoryBot.create(:user, :data_coordinator) }
let!(:scheme) { FactoryBot.create(:scheme, organisation: user.organisation) }
let!(:locations) { FactoryBot.create_list(:location, 3, scheme:) }
before do
sign_in user
get "/schemes/#{scheme.id}/locations"
end
context "when coordinator attempts to see scheme belonging to a different organisation" do
let!(:specific_scheme) { FactoryBot.create(:scheme) }
before do
FactoryBot.create(:location, scheme: specific_scheme)
end
it "returns 404 not found" do
get "/schemes/#{specific_scheme.id}/locations"
expect(response).to have_http_status(:not_found)
end
end
it "shows scheme" do
locations.each do |location|
expect(page).to have_content(location.location_code)
expect(page).to have_content(location.postcode)
expect(page).to have_content(location.county)
expect(page).to have_content(location.type_of_unit)
expect(page).to have_content(location.type_of_building)
expect(page).to have_content(location.wheelchair_adaptation)
expect(page).to have_content(location.address_line1)
expect(page).to have_content(location.address_line2)
end
end
it "has page heading" do
expect(page).to have_content(scheme.service_name)
end
it "has correct title" do
expect(page).to have_title("#{scheme.service_name} - Submit social housing lettings and sales data (CORE) - GOV.UK")
end
context "when paginating over 20 results" do
let!(:locations) { FactoryBot.create_list(:location, 25, scheme:) }
context "when on the first page" do
before do
get "/schemes/#{scheme.id}/locations"
end
it "shows which schemes are being shown on the current page" do
expect(CGI.unescape_html(response.body)).to match("Showing <b>1</b> to <b>20</b> of <b>#{locations.count}</b> locations")
end
it "has correct page 1 of 2 title" do
expect(page).to have_title("#{scheme.service_name} (page 1 of 2) - Submit social housing lettings and sales data (CORE) - GOV.UK")
end
it "has pagination links" do
expect(page).to have_content("Previous")
expect(page).not_to have_link("Previous")
expect(page).to have_content("Next")
expect(page).to have_link("Next")
end
end
context "when on the second page" do
before do
get "/schemes/#{scheme.id}/locations?page=2"
end
it "shows which schemes are being shown on the current page" do
expect(CGI.unescape_html(response.body)).to match("Showing <b>21</b> to <b>25</b> of <b>#{locations.count}</b> locations")
end
it "has correct page 1 of 2 title" do
expect(page).to have_title("#{scheme.service_name} (page 2 of 2) - Submit social housing lettings and sales data (CORE) - GOV.UK")
end
it "has pagination links" do
expect(page).to have_content("Previous")
expect(page).to have_link("Previous")
expect(page).to have_content("Next")
expect(page).not_to have_link("Next")
end
end
end
end
context "when signed in as a support user" do
let(:user) { FactoryBot.create(:user, :support) }
let!(:scheme) { FactoryBot.create(:scheme) }
let!(:locations) { FactoryBot.create_list(:location, 3, scheme:) }
before do
allow(user).to receive(:need_two_factor_authentication?).and_return(false)
sign_in user
get "/schemes/#{scheme.id}/locations"
end
it "shows scheme" do
locations.each do |location|
expect(page).to have_content(location.location_code)
expect(page).to have_content(location.postcode)
expect(page).to have_content(location.county)
expect(page).to have_content(location.type_of_unit)
expect(page).to have_content(location.type_of_building)
expect(page).to have_content(location.wheelchair_adaptation)
expect(page).to have_content(location.address_line1)
expect(page).to have_content(location.address_line2)
end
end
it "has page heading" do
expect(page).to have_content(scheme.service_name)
end
it "has correct title" do
expect(page).to have_title("#{scheme.service_name} - Submit social housing lettings and sales data (CORE) - GOV.UK")
end
context "when paginating over 20 results" do
let!(:locations) { FactoryBot.create_list(:location, 25, scheme:) }
context "when on the first page" do
before do
get "/schemes/#{scheme.id}/locations"
end
it "shows which schemes are being shown on the current page" do
expect(CGI.unescape_html(response.body)).to match("Showing <b>1</b> to <b>20</b> of <b>#{locations.count}</b> locations")
end
it "has correct page 1 of 2 title" do
expect(page).to have_title("#{scheme.service_name} (page 1 of 2) - Submit social housing lettings and sales data (CORE) - GOV.UK")
end
it "has pagination links" do
expect(page).to have_content("Previous")
expect(page).not_to have_link("Previous")
expect(page).to have_content("Next")
expect(page).to have_link("Next")
end
end
context "when on the second page" do
before do
get "/schemes/#{scheme.id}/locations?page=2"
end
it "shows which schemes are being shown on the current page" do
expect(CGI.unescape_html(response.body)).to match("Showing <b>21</b> to <b>25</b> of <b>#{locations.count}</b> locations")
end
it "has correct page 1 of 2 title" do
expect(page).to have_title("#{scheme.service_name} (page 2 of 2) - Submit social housing lettings and sales data (CORE) - GOV.UK")
end
it "has pagination links" do
expect(page).to have_content("Previous")
expect(page).to have_link("Previous")
expect(page).to have_content("Next")
expect(page).not_to have_link("Next")
end
end
end
end
end
end end

122
spec/services/imports/case_logs_field_import_service_spec.rb

@ -6,7 +6,8 @@ RSpec.describe Imports::CaseLogsFieldImportService do
let(:storage_service) { instance_double(StorageService) } let(:storage_service) { instance_double(StorageService) }
let(:logger) { instance_double(ActiveSupport::Logger) } let(:logger) { instance_double(ActiveSupport::Logger) }
let(:real_2021_2022_form) { Form.new("config/forms/2021_2022.json", "2021_2022") } let(:real_setup_path) { "config/forms/setup/log_setup.json" }
let(:real_2021_2022_form) { Form.new("config/forms/2021_2022.json", "2021_2022", real_setup_path) }
let(:fixture_directory) { "spec/fixtures/softwire_imports/case_logs" } let(:fixture_directory) { "spec/fixtures/softwire_imports/case_logs" }
let(:case_log_id) { "0ead17cb-1668-442d-898c-0d52879ff592" } let(:case_log_id) { "0ead17cb-1668-442d-898c-0d52879ff592" }
@ -74,6 +75,125 @@ RSpec.describe Imports::CaseLogsFieldImportService do
end end
end end
context "when updating letings allocation values" do
let(:field) { "lettings_allocation" }
let(:case_log) { CaseLog.find_by(old_id: case_log_id) }
before do
allow(logger).to receive(:warn)
Imports::CaseLogsImportService.new(storage_service, logger).create_logs(fixture_directory)
case_log_file.rewind
end
context "when cbl" do
let(:case_log_id) { "166fc004-392e-47a8-acb8-1c018734882b" }
context "when it was incorrectly set" do
before do
case_log.update!(cbl: 1)
end
it "updates the value" do
expect(logger).to receive(:info).with(/Case Log \d+'s cbl value has been updated/)
expect { import_service.send(:update_field, field, remote_folder) }
.to(change { case_log.reload.cbl }.from(1).to(0))
end
end
context "when it was correctly set" do
before do
case_log.update!(cbl: 0)
end
it "does not update the value" do
expect { import_service.send(:update_field, field, remote_folder) }
.not_to(change { case_log.reload.cbl })
end
end
end
context "when chr" do
let(:case_log_id) { "166fc004-392e-47a8-acb8-1c018734882b" }
context "when it was incorrectly set" do
before do
case_log.update!(chr: 1)
end
it "updates the value" do
expect(logger).to receive(:info).with(/Case Log \d+'s chr value has been updated/)
expect { import_service.send(:update_field, field, remote_folder) }
.to(change { case_log.reload.chr }.from(1).to(0))
end
end
context "when it was correctly set" do
before do
case_log.update!(chr: 0)
end
it "does not update the value" do
expect { import_service.send(:update_field, field, remote_folder) }
.not_to(change { case_log.reload.chr })
end
end
end
context "when cap" do
let(:case_log_id) { "0ead17cb-1668-442d-898c-0d52879ff592" }
context "when it was incorrectly set" do
before do
case_log.update!(cap: 1)
end
it "updates the value" do
expect(logger).to receive(:info).with(/Case Log \d+'s cap value has been updated/)
expect { import_service.send(:update_field, field, remote_folder) }
.to(change { case_log.reload.cap }.from(1).to(0))
end
end
context "when it was correctly set" do
before do
case_log.update!(cap: 0)
end
it "does not update the value" do
expect { import_service.send(:update_field, field, remote_folder) }
.not_to(change { case_log.reload.cap })
end
end
end
context "when allocation type is none of cap, chr, cbl" do
let(:case_log_id) { "893ufj2s-lq77-42m4-rty6-ej09gh585uy1" }
context "when it did not have a value set for letting_allocation_unknown" do
before do
case_log.update!(letting_allocation_unknown: nil)
end
it "updates the value" do
expect(logger).to receive(:info).with(/Case Log \d+'s letting_allocation_unknown value has been updated/)
expect { import_service.send(:update_field, field, remote_folder) }
.to(change { case_log.reload.letting_allocation_unknown }.from(nil).to(1))
end
end
context "when it had a value set for letting_allocation_unknown" do
before do
case_log.update!(letting_allocation_unknown: 1)
end
it "updates the value" do
expect { import_service.send(:update_field, field, remote_folder) }
.not_to(change { case_log.reload.letting_allocation_unknown })
end
end
end
end
context "when updating major repairs" do context "when updating major repairs" do
let(:field) { "major_repairs" } let(:field) { "major_repairs" }

5
spec/services/imports/case_logs_import_service_spec.rb

@ -6,8 +6,9 @@ RSpec.describe Imports::CaseLogsImportService do
let(:storage_service) { instance_double(StorageService) } let(:storage_service) { instance_double(StorageService) }
let(:logger) { instance_double(ActiveSupport::Logger) } let(:logger) { instance_double(ActiveSupport::Logger) }
let(:real_2021_2022_form) { Form.new("config/forms/2021_2022.json", "2021_2022") } let(:real_setup_path) { "config/forms/setup/log_setup.json" }
let(:real_2022_2023_form) { Form.new("config/forms/2022_2023.json", "2022_2023") } let(:real_2021_2022_form) { Form.new("config/forms/2021_2022.json", "2021_2022", real_setup_path) }
let(:real_2022_2023_form) { Form.new("config/forms/2022_2023.json", "2022_2023", real_setup_path) }
let(:fixture_directory) { "spec/fixtures/softwire_imports/case_logs" } let(:fixture_directory) { "spec/fixtures/softwire_imports/case_logs" }
def open_file(directory, filename) def open_file(directory, filename)

34
webpack.config.js

@ -1,18 +1,18 @@
const path = require("path") const path = require('node:path')
const webpack = require("webpack") const webpack = require('webpack')
const MiniCssExtractPlugin = require('mini-css-extract-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const RemoveEmptyScriptsPlugin = require('webpack-remove-empty-scripts') const RemoveEmptyScriptsPlugin = require('webpack-remove-empty-scripts')
const CopyPlugin = require("copy-webpack-plugin"); const CopyPlugin = require('copy-webpack-plugin')
const mode = process.env.NODE_ENV === 'development' ? 'development' : 'production' const mode = process.env.NODE_ENV === 'development' ? 'development' : 'production'
module.exports = { module.exports = {
mode, mode,
devtool: "source-map", devtool: 'source-map',
entry: { entry: {
application: [ application: [
"./app/frontend/application.js", './app/frontend/application.js'
] ]
}, },
module: { module: {
@ -24,9 +24,9 @@ module.exports = {
path.resolve(__dirname, 'node_modules/@stimulus/polyfills'), path.resolve(__dirname, 'node_modules/@stimulus/polyfills'),
path.resolve(__dirname, 'node_modules/@rails/actioncable'), path.resolve(__dirname, 'node_modules/@rails/actioncable'),
path.resolve(__dirname, 'node_modules/chartjs'), path.resolve(__dirname, 'node_modules/chartjs'),
path.resolve(__dirname, 'app/frontend'), path.resolve(__dirname, 'app/frontend')
], ],
use: ['babel-loader'], use: ['babel-loader']
}, },
{ {
test: /\.(png|jpe?g|gif|eot|woff|woff2|ttf|svg|ico)$/i, test: /\.(png|jpe?g|gif|eot|woff|woff2|ttf|svg|ico)$/i,
@ -34,9 +34,9 @@ module.exports = {
}, },
{ {
test: /\.(scss|css)/i, test: /\.(scss|css)/i,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'], use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
} }
], ]
}, },
resolve: { resolve: {
alias: { alias: {
@ -46,11 +46,11 @@ module.exports = {
modules: ['node_modules', 'node_modules/govuk-frontend/govuk'] modules: ['node_modules', 'node_modules/govuk-frontend/govuk']
}, },
output: { output: {
filename: "[name].js", filename: '[name].js',
// we must set publicPath to an empty value to override the default of // we must set publicPath to an empty value to override the default of
// auto which doesn't work in IE11 // auto which doesn't work in IE11
publicPath: '', publicPath: '',
path: path.resolve(__dirname, "app/assets/builds"), path: path.resolve(__dirname, 'app/assets/builds')
}, },
plugins: [ plugins: [
new RemoveEmptyScriptsPlugin(), new RemoveEmptyScriptsPlugin(),
@ -58,12 +58,12 @@ module.exports = {
new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 }), new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 }),
new CopyPlugin({ new CopyPlugin({
patterns: [ patterns: [
{ from: "node_modules/govuk-frontend/govuk/assets/images", to: "images" }, { from: 'node_modules/govuk-frontend/govuk/assets/images', to: 'images' },
{ from: "node_modules/govuk-frontend/govuk/assets/fonts", to: "fonts" }, { from: 'node_modules/govuk-frontend/govuk/assets/fonts', to: 'fonts' },
{ from: "node_modules/html5shiv/dist/html5shiv.min.js", to: "vendor" }, { from: 'node_modules/html5shiv/dist/html5shiv.min.js', to: 'vendor' },
{ from: "app/frontend/vendor/outerHTML.js", to: "vendor" }, { from: 'app/frontend/vendor/outerHTML.js', to: 'vendor' },
{ from: "app/frontend/vendor/polyfill-output-value.js", to: "vendor" } { from: 'app/frontend/vendor/polyfill-output-value.js', to: 'vendor' }
], ]
}) })
] ]
} }

951
yarn.lock

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save