Browse Source

CLDC-3014 Add schemes and locations csv download functionality (#2083)

* feat: add schemes and locations download links and pages

* feat: update current path helper

* feat: update tests for different user visibility levels

* feat: update search caption tests

* refactor: lint tests

* refactor: lint tests

* git: revert unintentional inclusion

* feat: update tests

* refactor: lint

* feat: DRY up routing

* refactor: lint

* feat: add csv confirmation view

* feat: add scheme csv service

* feat: rename

* feat: update csv service

* feat: update csv service

* feat: update controller and rename view

* feat: update view

* refactor: lint

* feat: show correct headers in csv

* feat: add locations and combined csv behaviour

* feat: remove redundant user instance variable

* feat: add scheme csv service spec

* feat: add scheme email csv job tests

* feat: update filters in spec

* refactor: move scheme_email_csv_job_spec.rb

* feat: update spec

* refactor: remove blank line

* feat: add nowrap to all download links

* feat: update org schemes controller with org schemes (and rename for clarity)

* feat: update link indentation and spec

* feat: only include location LA name, and rename to location_local_authority

* feat: update seed locations with westminster local authorities to avoid similar confusion to some that arose in PO review

* feat: display multiple active periods on a single line

* feat: display multiple active periods on a single line

* feat: update line spacing in search captions

* feat: replace 2/3 with full column in download page

* feat: move scheme alphabeticising into manager

* feat: update tests now search/filterless copy has changed

* refactor: lint

* refactor: lint

* refactor: lint

* feat: add filter alphabeticising test

* feat: correct spacing
pull/2090/head
natdeanlewissoftwire 1 year ago committed by GitHub
parent
commit
9b19b1eedc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      app/components/search_result_caption_component.html.erb
  2. 22
      app/controllers/organisations_controller.rb
  3. 24
      app/controllers/schemes_controller.rb
  4. 4
      app/frontend/styles/_search.scss
  5. 2
      app/helpers/navigation_items_helper.rb
  6. 22
      app/helpers/schemes_helper.rb
  7. 29
      app/jobs/scheme_email_csv_job.rb
  8. 8
      app/models/user.rb
  9. 108
      app/services/csv/scheme_csv_service.rb
  10. 2
      app/services/filter_manager.rb
  11. 6
      app/views/logs/_log_list.html.erb
  12. 2
      app/views/logs/download_csv.html.erb
  13. 6
      app/views/organisations/schemes.html.erb
  14. 9
      app/views/schemes/_scheme_list.html.erb
  15. 15
      app/views/schemes/csv_confirmation.html.erb
  16. 16
      app/views/schemes/download_csv.html.erb
  17. 2
      app/views/schemes/index.html.erb
  18. 2
      app/views/users/_user_list.html.erb
  19. 9
      config/routes.rb
  20. 9
      db/seeds.rb
  21. 12
      spec/components/search_result_caption_component_spec.rb
  22. 4
      spec/features/organisation_spec.rb
  23. 4
      spec/features/schemes_spec.rb
  24. 2
      spec/fixtures/files/locations_csv_export.csv
  25. 2
      spec/fixtures/files/schemes_and_locations_csv_export.csv
  26. 2
      spec/fixtures/files/schemes_csv_export.csv
  27. 94
      spec/jobs/scheme_email_csv_job_spec.rb
  28. 4
      spec/requests/lettings_logs_controller_spec.rb
  29. 12
      spec/requests/organisation_relationships_controller_spec.rb
  30. 126
      spec/requests/organisations_controller_spec.rb
  31. 4
      spec/requests/sales_logs_controller_spec.rb
  32. 67
      spec/requests/schemes_controller_spec.rb
  33. 2
      spec/requests/users_controller_spec.rb
  34. 147
      spec/services/csv/scheme_csv_service_spec.rb
  35. 17
      spec/services/filter_manager_spec.rb

6
app/components/search_result_caption_component.html.erb

@ -1,4 +1,4 @@
<span class="govuk-!-margin-right-4">
<span>
<% if searched.present? && filters_count&.positive? %>
<strong><%= count %></strong> <%= item_label.pluralize(count) %> matching search and filters<br>
<% elsif searched.present? %>
@ -6,6 +6,8 @@
<% elsif filters_count&.positive? %>
<strong><%= count %></strong> <%= item_label.pluralize(count) %> matching filters<br>
<% else %>
<strong><%= count %></strong> matching <%= item %>
<span class="govuk-!-margin-right-4">
<strong><%= count %></strong> total <%= item %>
</span>
<% end %>
</span>

22
app/controllers/organisations_controller.rb

@ -7,9 +7,9 @@ class OrganisationsController < ApplicationController
before_action :find_resource, except: %i[index new create]
before_action :authenticate_scope!, except: [:index]
before_action :session_filters, if: -> { current_user.support? || current_user.organisation.has_managing_agents? }, only: %i[lettings_logs sales_logs email_lettings_csv download_lettings_csv email_sales_csv download_sales_csv]
before_action :session_filters, only: %i[users schemes]
before_action :session_filters, only: %i[users schemes email_schemes_csv download_schemes_csv]
before_action -> { filter_manager.serialize_filters_to_session }, if: -> { current_user.support? || current_user.organisation.has_managing_agents? }, only: %i[lettings_logs sales_logs email_lettings_csv download_lettings_csv email_sales_csv download_sales_csv]
before_action -> { filter_manager.serialize_filters_to_session }, only: %i[users schemes]
before_action -> { filter_manager.serialize_filters_to_session }, only: %i[users schemes email_schemes_csv download_schemes_csv]
def index
redirect_to organisation_path(current_user.organisation) unless current_user.support?
@ -21,14 +21,26 @@ class OrganisationsController < ApplicationController
end
def schemes
all_schemes = Scheme.where(owning_organisation: [@organisation] + @organisation.parent_organisations)
organisation_schemes = Scheme.where(owning_organisation: [@organisation] + @organisation.parent_organisations)
@pagy, @schemes = pagy(filter_manager.filtered_schemes(all_schemes, search_term, session_filters).order_by_service_name)
@pagy, @schemes = pagy(filter_manager.filtered_schemes(organisation_schemes, search_term, session_filters))
@searched = search_term.presence
@total_count = all_schemes.size
@total_count = organisation_schemes.size
@filter_type = "schemes"
end
def download_schemes_csv
organisation_schemes = Scheme.where(owning_organisation: [@organisation] + @organisation.parent_organisations)
unpaginated_filtered_schemes = filter_manager.filtered_schemes(organisation_schemes, search_term, session_filters)
render "schemes/download_csv", locals: { search_term:, post_path: email_csv_schemes_path, download_type: params[:download_type], schemes: unpaginated_filtered_schemes }
end
def email_schemes_csv
SchemeEmailCsvJob.perform_later(current_user, search_term, session_filters, false, @organisation, params[:download_type])
redirect_to schemes_csv_confirmation_organisation_path
end
def show
redirect_to details_organisation_path(@organisation)
end

24
app/controllers/schemes_controller.rb

@ -3,11 +3,11 @@ class SchemesController < ApplicationController
include Modules::SearchFilter
before_action :authenticate_user!
before_action :find_resource, except: %i[index create new changes]
before_action :find_resource, except: %i[index create new changes email_csv download_csv csv_confirmation]
before_action :redirect_if_scheme_confirmed, only: %i[primary_client_group confirm_secondary_client_group secondary_client_group support details]
before_action :authorize_user
before_action :session_filters, if: :current_user, only: %i[index]
before_action -> { filter_manager.serialize_filters_to_session }, if: :current_user, only: %i[index]
before_action :authorize_user, except: %i[email_csv download_csv csv_confirmation]
before_action :session_filters, if: :current_user, only: %i[index email_csv download_csv]
before_action -> { filter_manager.serialize_filters_to_session }, if: :current_user, only: %i[index email_csv download_csv]
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found
@ -15,7 +15,7 @@ class SchemesController < ApplicationController
redirect_to schemes_organisation_path(current_user.organisation) unless current_user.support?
all_schemes = Scheme.all
@pagy, @schemes = pagy(filter_manager.filtered_schemes(all_schemes, search_term, session_filters).order_by_service_name)
@pagy, @schemes = pagy(filter_manager.filtered_schemes(all_schemes, search_term, session_filters))
@searched = search_term.presence
@total_count = all_schemes.size
@filter_type = "schemes"
@ -205,6 +205,20 @@ class SchemesController < ApplicationController
render "schemes/changes"
end
def download_csv
unpaginated_filtered_schemes = filter_manager.filtered_schemes(current_user.schemes, search_term, session_filters)
render "download_csv", locals: { search_term:, post_path: email_csv_schemes_path, download_type: params[:download_type], schemes: unpaginated_filtered_schemes }
end
def email_csv
all_orgs = params["organisation_select"] == "all"
SchemeEmailCsvJob.perform_later(current_user, search_term, session_filters, all_orgs, nil, params[:download_type])
redirect_to csv_confirmation_schemes_path
end
def csv_confirmation; end
private
def authorize_user

4
app/frontend/styles/_search.scss

@ -22,3 +22,7 @@
margin-bottom: 2px;
width: auto;
}
.app-search__caption {
line-height: govuk-spacing(7);
}

2
app/helpers/navigation_items_helper.rb

@ -57,7 +57,7 @@ private
end
def supported_housing_schemes_current?(path)
path == schemes_path || path.include?("/schemes/")
path.starts_with?(schemes_path)
end
def non_support_supported_housing_schemes_current?(path)

22
app/helpers/schemes_helper.rb

@ -54,6 +54,28 @@ module SchemesHelper
end
end
def selected_schemes_and_locations_text(download_type, schemes)
scheme_count = schemes.count
case download_type
when "schemes"
"You've selected #{pluralize(scheme_count, 'scheme')}."
when "locations"
location_count = schemes.map(&:locations).flatten.count
"You've selected #{pluralize(location_count, 'location')} from #{pluralize(scheme_count, 'scheme')}."
when "combined"
location_count = schemes.map(&:locations).flatten.count
"You've selected #{pluralize(scheme_count, 'scheme')} with #{pluralize(location_count, 'location')}. The CSV will have one location per row with scheme details listed for each location."
end
end
def primary_schemes_csv_download_url(search, download_type)
csv_download_schemes_path(search:, download_type:)
end
def secondary_schemes_csv_download_url(organisation, search, download_type)
schemes_csv_download_organisation_path(organisation, search:, download_type:)
end
private
ActivePeriod = Struct.new(:from, :to)

29
app/jobs/scheme_email_csv_job.rb

@ -0,0 +1,29 @@
class SchemeEmailCsvJob < ApplicationJob
queue_as :default
BYTE_ORDER_MARK = "\uFEFF".freeze # Required to ensure Excel always reads CSV as UTF-8
EXPIRATION_TIME = 24.hours.to_i
def perform(user, search_term = nil, filters = {}, all_orgs = false, organisation = nil, download_type = "combined") # rubocop:disable Style/OptionalBooleanParameter - sidekiq can't serialise named params
unfiltered_schemes = organisation.present? && user.support? ? Scheme.where(owning_organisation_id: organisation.id) : user.schemes
filtered_schemes = FilterManager.filter_schemes(unfiltered_schemes, search_term, filters, all_orgs, user)
csv_string = Csv::SchemeCsvService.new(download_type:).prepare_csv(filtered_schemes)
case download_type
when "schemes"
filename = "#{['schemes', organisation&.name, Time.zone.now].compact.join('-')}.csv"
when "locations"
filename = "#{['locations', organisation&.name, Time.zone.now].compact.join('-')}.csv"
when "combined"
filename = "#{['schemes-and-locations', organisation&.name, Time.zone.now].compact.join('-')}.csv"
end
storage_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["CSV_DOWNLOAD_PAAS_INSTANCE"])
storage_service.write_file(filename, BYTE_ORDER_MARK + csv_string)
url = storage_service.get_presigned_url(filename, EXPIRATION_TIME)
CsvDownloadMailer.new.send_csv_download_mail(user, url, EXPIRATION_TIME)
end
end

8
app/models/user.rb

@ -107,6 +107,14 @@ class User < ApplicationRecord
SalesLog.filter_by_managing_organisation(organisation.absorbed_organisations + [organisation])
end
def schemes
if support?
Scheme.all
else
Scheme.filter_by_owning_organisation(organisation.absorbed_organisations + [organisation])
end
end
def is_key_contact?
is_key_contact
end

108
app/services/csv/scheme_csv_service.rb

@ -0,0 +1,108 @@
module Csv
class SchemeCsvService
include SchemesHelper
include LocationsHelper
def initialize(download_type:)
@download_type = download_type
end
def prepare_csv(schemes)
CSV.generate(headers: true) do |csv|
csv << attributes
schemes.find_each do |scheme|
if @download_type == "schemes"
csv << scheme_attributes.map { |attribute| scheme_value(attribute, scheme) }
else
scheme.locations.each do |location|
case @download_type
when "locations"
csv << [scheme.id_to_display] + location_attributes.map { |attribute| location_value(attribute, location) }
when "combined"
csv << scheme_attributes.map { |attribute| scheme_value(attribute, scheme) } + location_attributes.map { |attribute| location_value(attribute, location) }
end
end
end
end
end
end
private
SCHEME_FIELD_FROM_ATTRIBUTE = {
"scheme_code" => "id_to_display",
"scheme_service_name" => "service_name",
"scheme_status" => "status",
"scheme_sensitive" => "sensitive",
"scheme_registered_under_care_act" => "registered_under_care_act",
"scheme_support_services_provided_by" => "arrangement_type",
"scheme_primary_client_group" => "primary_client_group",
"scheme_has_other_client_group" => "has_other_client_group",
"scheme_secondary_client_group" => "secondary_client_group",
"scheme_support_type" => "support_type",
"scheme_intended_stay" => "intended_stay",
"scheme_created_at" => "created_at",
}.freeze
LOCATION_FIELD_FROM_ATTRIBUTE = {
"location_code" => "id",
"location_postcode" => "postcode",
"location_name" => "name",
"location_status" => "status",
"location_local_authority" => "location_admin_district",
"location_units" => "units",
"location_type_of_unit" => "type_of_unit",
"location_mobility_type" => "mobility_type",
}.freeze
CUSTOM_CALL_CHAINS = {
scheme_owning_organisation_name: %i[owning_organisation name],
}.freeze
SYSTEM_DATE_FIELDS = %w[
created_at
].freeze
def scheme_value(attribute, scheme)
attribute = SCHEME_FIELD_FROM_ATTRIBUTE.fetch(attribute, attribute)
if attribute == "scheme_active_dates"
scheme_availability(scheme).gsub("\n", ", ").to_s
elsif CUSTOM_CALL_CHAINS.key? attribute.to_sym
call_chain = CUSTOM_CALL_CHAINS[attribute.to_sym]
call_chain.reduce(scheme) { |object, next_call| object&.public_send(next_call) }
elsif SYSTEM_DATE_FIELDS.include? attribute
scheme.public_send(attribute)&.iso8601
else
scheme.public_send(attribute)
end
end
def location_value(attribute, location)
attribute = LOCATION_FIELD_FROM_ATTRIBUTE.fetch(attribute, attribute)
if attribute == "location_active_dates"
location_availability(location).gsub("\n", ", ").to_s
else
location.public_send(attribute)
end
end
def scheme_attributes
%w[scheme_code scheme_service_name scheme_status scheme_sensitive scheme_type scheme_registered_under_care_act scheme_owning_organisation_name scheme_support_services_provided_by scheme_primary_client_group scheme_has_other_client_group scheme_secondary_client_group scheme_support_type scheme_intended_stay scheme_created_at scheme_active_dates]
end
def location_attributes
%w[location_code location_postcode location_name location_status location_local_authority location_units location_type_of_unit location_mobility_type location_active_dates]
end
def attributes
case @download_type
when "schemes"
scheme_attributes
when "locations"
%w[scheme_code] + location_attributes
when "combined"
scheme_attributes + location_attributes
end
end
end
end

2
app/services/filter_manager.rb

@ -59,7 +59,7 @@ class FilterManager
schemes = schemes.public_send("filter_by_#{category}", values, user)
end
schemes
schemes.order_by_service_name
end
def self.filter_locations(locations, search_term, filters, user)

6
app/views/logs/_log_list.html.erb

@ -1,11 +1,11 @@
<h2 class="govuk-body">
<div class="govuk-grid-row">
<div class="govuk-grid-row app-search__caption">
<div class="govuk-grid-column-three-quarters">
<%= render(SearchResultCaptionComponent.new(searched:, count: pagy.count, item_label:, total_count:, item: "logs", filters_count: applied_filters_count(@filter_type))) %>
<% if logs&.any? %>
<%= govuk_link_to "Download (CSV)", csv_download_url, type: "text/csv", class: "govuk-!-margin-right-4" %>
<%= govuk_link_to "Download (CSV)", csv_download_url, type: "text/csv", class: "govuk-!-margin-right-4", style: "white-space: nowrap" %>
<% if @current_user.support? %>
<%= govuk_link_to "Download (CSV, codes only)", csv_codes_only_download_url, type: "text/csv", class: "govuk-!-margin-right-4" %>
<%= govuk_link_to "Download (CSV, codes only)", csv_codes_only_download_url, type: "text/csv", class: "govuk-!-margin-right-4", style: "white-space: nowrap" %>
<% end %>
<% end %>
</div>

2
app/views/logs/download_csv.html.erb

@ -5,7 +5,7 @@
<% end %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<div class="govuk-grid-column-full">
<h1 class="govuk-heading-l">Download CSV</h1>
<p class="govuk-body">We'll send a secure download link to your email address <strong><%= @current_user.email %></strong>.</p>

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

@ -27,7 +27,11 @@
<hr class="govuk-section-break govuk-section-break--visible govuk-section-break--m">
<%= render partial: "schemes/scheme_list", locals: { schemes: @schemes, title:, pagy: @pagy, searched: @searched, item_label:, total_count: @total_count } %>
<% if current_user.support? %>
<%= render partial: "schemes/scheme_list", locals: { schemes: @schemes, title:, pagy: @pagy, searched: @searched, item_label:, total_count: @total_count, schemes_csv_download_url: secondary_schemes_csv_download_url(@organisation, @searched, "schemes"), locations_csv_download_url: secondary_schemes_csv_download_url(@organisation, @searched, "locations"), combined_csv_download_url: secondary_schemes_csv_download_url(@organisation, @searched, "combined") } %>
<% else %>
<%= render partial: "schemes/scheme_list", locals: { schemes: @schemes, title:, pagy: @pagy, searched: @searched, item_label:, total_count: @total_count, schemes_csv_download_url: primary_schemes_csv_download_url(@searched, "schemes"), locations_csv_download_url: primary_schemes_csv_download_url(@searched, "locations"), combined_csv_download_url: primary_schemes_csv_download_url(@searched, "combined") } %>
<% end %>
<%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "schemes" } %>
</div>

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

@ -1,8 +1,15 @@
<section class="app-table-group" tabindex="0" aria-labelledby="<%= title.dasherize %>">
<%= govuk_table do |table| %>
<%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %>
<%= render(SearchResultCaptionComponent.new(searched:, count: pagy.count, item_label:, total_count:, item: "schemes", filters_count: applied_filters_count(@filter_type))) %>
<span class="app-search__caption">
<%= render(SearchResultCaptionComponent.new(searched:, count: pagy.count, item_label:, total_count:, item: "schemes", filters_count: applied_filters_count(@filter_type))) %>
<% if @schemes&.any? %>
<%= govuk_link_to "Download schemes (CSV)", schemes_csv_download_url, type: "text/csv", class: "govuk-!-margin-right-4", style: "white-space: nowrap" %>
<%= govuk_link_to "Download locations (CSV)", locations_csv_download_url, type: "text/csv", class: "govuk-!-margin-right-4", style: "white-space: nowrap" %>
<%= govuk_link_to "Download schemes and locations (CSV)", combined_csv_download_url, type: "text/csv", class: "govuk-!-margin-right-4", style: "white-space: nowrap" %>
<% end %>
</span>
<% end %>
<%= table.head do |head| %>
<%= head.row do |row| %>
<% row.cell(header: true, text: "Scheme", html_attributes: { scope: "col", class: "govuk-!-width-one-quarter" }) %>

15
app/views/schemes/csv_confirmation.html.erb

@ -0,0 +1,15 @@
<% content_for :title, "We’re sending you an email" %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<%= govuk_panel(title_text: "We’re sending you an email") %>
<p class="govuk-body">It should arrive in a few minutes, but it could take longer.</p>
<h2 class="govuk-heading-m">What happens next</h2>
<p class="govuk-body">Open your email inbox and click the link to download your CSV file.</p>
<p class="govuk-body">
<%= govuk_link_to "Return to schemes", schemes_path %>
</p>
</div>
</div>

16
app/views/schemes/download_csv.html.erb

@ -0,0 +1,16 @@
<% content_for :title, "Download CSV" %>
<% content_for :before_content do %>
<%= govuk_back_link(href: :back) %>
<% end %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-full">
<h1 class="govuk-heading-l">Download CSV</h1>
<p class="govuk-body">We'll send a secure download link to your email address <strong><%= @current_user.email %></strong>.</p>
<p class="govuk-body"><%= selected_schemes_and_locations_text(download_type, schemes) %></p>
<%= govuk_button_to "Send email", post_path, method: :post, params: { search: search_term, download_type: } %>
</div>
</div>

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

@ -15,7 +15,7 @@
<hr class="govuk-section-break govuk-section-break--visible govuk-section-break--m">
<%= render partial: "schemes/scheme_list", locals: { schemes: @schemes, title:, pagy: @pagy, searched: @searched, item_label:, total_count: @total_count } %>
<%= render partial: "schemes/scheme_list", locals: { schemes: @schemes, title:, pagy: @pagy, searched: @searched, item_label:, total_count: @total_count, schemes_csv_download_url: primary_schemes_csv_download_url(@searched, "schemes"), locations_csv_download_url: primary_schemes_csv_download_url(@searched, "locations"), combined_csv_download_url: primary_schemes_csv_download_url(@searched, "combined") } %>
<%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "schemes" } %>
</div>

2
app/views/users/_user_list.html.erb

@ -4,7 +4,7 @@
<%= render(SearchResultCaptionComponent.new(searched:, count: pagy.count, item_label:, total_count:, item: "users", filters_count: applied_filters_count(@filter_type))) %>
<% if current_user.support? %>
<% query = searched.present? ? "?search=#{searched}" : nil %>
<%= govuk_link_to "Download (CSV)", "#{request.path}.csv#{query}", type: "text/csv" %>
<%= govuk_link_to "Download (CSV)", "#{request.path}.csv#{query}", type: "text/csv", style: "white-space: nowrap" %>
<% end %>
<% end %>
<%= table.head do |head| %>

9
config/routes.rb

@ -79,6 +79,12 @@ Rails.application.routes.draw do
patch "deactivate", to: "schemes#deactivate"
patch "reactivate", to: "schemes#reactivate"
collection do
get "csv-download", to: "schemes#download_csv"
post "email-csv", to: "schemes#email_csv"
get "csv-confirmation", to: "schemes#csv_confirmation"
end
resources :locations do
post "locations", to: "locations#create"
get "new-deactivation", to: "locations#new_deactivation"
@ -148,6 +154,9 @@ Rails.application.routes.draw do
post "sales-logs/email-csv", to: "organisations#email_sales_csv"
get "sales-logs/csv-confirmation", to: "sales_logs#csv_confirmation"
get "schemes", to: "organisations#schemes"
get "schemes/csv-download", to: "organisations#download_schemes_csv"
post "schemes/email-csv", to: "organisations#email_schemes_csv"
get "schemes/csv-confirmation", to: "schemes#csv_confirmation"
get "stock-owners", to: "organisation_relationships#stock_owners"
get "stock-owners/add", to: "organisation_relationships#add_stock_owner"
get "stock-owners/remove", to: "organisation_relationships#remove_stock_owner"

9
db/seeds.rb

@ -301,7 +301,8 @@ unless Rails.env.test?
Location.create!(
scheme: scheme1,
location_code: "S254-CU193AA",
location_code: "E09000033",
location_admin_district: "Westminster",
postcode: "CU193AA",
name: "Rectory Road",
type_of_unit: 4,
@ -311,7 +312,8 @@ unless Rails.env.test?
Location.create!(
scheme: scheme1,
location_code: "S254-DM250DC",
location_code: "E09000033",
location_admin_district: "Westminster",
postcode: "DM250DC",
name: "Smithy Lane",
type_of_unit: 1,
@ -321,7 +323,8 @@ unless Rails.env.test?
Location.create!(
scheme: scheme2,
location_code: "S254-YX130WP",
location_code: "E09000033",
location_admin_district: "Westminster",
postcode: "YX130WP",
name: "Smithy Lane",
type_of_unit: 2,

12
spec/components/search_result_caption_component_spec.rb

@ -11,7 +11,7 @@ RSpec.describe SearchResultCaptionComponent, type: :component do
context "when search and filter results are found" do
it "renders table caption including the search results and total" do
expect(result.to_html).to eq("<span class=\"govuk-!-margin-right-4\">\n <strong>2</strong> users matching search and filters<br>\n</span>\n")
expect(result.to_html).to eq("<span>\n <strong>2</strong> users matching search and filters<br>\n</span>\n")
end
end
@ -19,7 +19,7 @@ RSpec.describe SearchResultCaptionComponent, type: :component do
let(:filters_count) { nil }
it "renders table caption including the search results and total" do
expect(result.to_html).to eq("<span class=\"govuk-!-margin-right-4\">\n <strong>2</strong> users matching search<br>\n</span>\n")
expect(result.to_html).to eq("<span>\n <strong>2</strong> users matching search<br>\n</span>\n")
end
end
@ -27,7 +27,7 @@ RSpec.describe SearchResultCaptionComponent, type: :component do
let(:searched) { nil }
it "renders table caption including the search results and total" do
expect(result.to_html).to eq("<span class=\"govuk-!-margin-right-4\">\n <strong>2</strong> users matching filters<br>\n</span>\n")
expect(result.to_html).to eq("<span>\n <strong>2</strong> users matching filters<br>\n</span>\n")
end
end
@ -36,7 +36,7 @@ RSpec.describe SearchResultCaptionComponent, type: :component do
let(:filters_count) { nil }
it "renders table caption with total count only" do
expect(result.to_html).to eq("<span class=\"govuk-!-margin-right-4\">\n <strong>#{count}</strong> matching #{item}\n</span>\n")
expect(result.to_html).to eq("<span>\n <span class=\"govuk-!-margin-right-4\">\n <strong>2</strong> total schemes\n </span>\n</span>\n")
end
end
@ -44,7 +44,7 @@ RSpec.describe SearchResultCaptionComponent, type: :component do
let(:count) { 0 }
it "renders table caption with total count only" do
expect(result.to_html).to eq("<span class=\"govuk-!-margin-right-4\">\n <strong>0</strong> users matching search and filters<br>\n</span>\n")
expect(result.to_html).to eq("<span>\n <strong>0</strong> users matching search and filters<br>\n</span>\n")
end
end
@ -52,7 +52,7 @@ RSpec.describe SearchResultCaptionComponent, type: :component do
let(:count) { 1 }
it "renders table caption with total count only" do
expect(result.to_html).to eq("<span class=\"govuk-!-margin-right-4\">\n <strong>1</strong> user matching search and filters<br>\n</span>\n")
expect(result.to_html).to eq("<span>\n <strong>1</strong> user matching search and filters<br>\n</span>\n")
end
end
end

4
spec/features/organisation_spec.rb

@ -189,7 +189,7 @@ RSpec.describe "User Features" do
end
it "has correct page details" do
expect(page).to have_content("#{number_of_lettings_logs} matching logs")
expect(page).to have_content("#{number_of_lettings_logs} total logs")
organisation.lettings_logs.map(&:id).each do |lettings_log_id|
expect(page).to have_link lettings_log_id.to_s, href: "/lettings-logs/#{lettings_log_id}"
end
@ -237,7 +237,7 @@ RSpec.describe "User Features" do
end
it "can filter sales logs" do
expect(page).to have_content("#{number_of_sales_logs} matching logs")
expect(page).to have_content("#{number_of_sales_logs} total logs")
organisation.sales_logs.map(&:id).each do |sales_log_id|
expect(page).to have_link sales_log_id.to_s, href: "/sales-logs/#{sales_log_id}"
end

4
spec/features/schemes_spec.rb

@ -573,7 +573,7 @@ RSpec.describe "Schemes scheme Features" do
it "displays information about a single location" do
expect(page).to have_content "Locations"
expect(page).to have_content "#{scheme.locations.count} matching location"
expect(page).to have_content "#{scheme.locations.count} total location"
end
it "displays information about the first created location" do
@ -586,7 +586,7 @@ RSpec.describe "Schemes scheme Features" do
fill_in_and_save_second_location
click_button "Save and return to locations"
expect(page).to have_content "Locations"
expect(page).to have_content "#{scheme.locations.count} matching location"
expect(page).to have_content "#{scheme.locations.count} total location"
end
it "displays information about newly created location" do

2
spec/fixtures/files/locations_csv_export.csv vendored

@ -0,0 +1,2 @@
scheme_code,location_code,location_postcode,location_name,location_status,location_local_authority,location_units,location_type_of_unit,location_mobility_type,location_active_dates
,,SW1A 2AA,Downing Street,deactivating_soon,Westminster,20,Self-contained house,Fitted with equipment and adaptations,"Active from 1 April 2022 to 25 December 2023, Deactivated on 26 December 2023"
1 scheme_code location_code location_postcode location_name location_status location_local_authority location_units location_type_of_unit location_mobility_type location_active_dates
2 SW1A 2AA Downing Street deactivating_soon Westminster 20 Self-contained house Fitted with equipment and adaptations Active from 1 April 2022 to 25 December 2023, Deactivated on 26 December 2023

2
spec/fixtures/files/schemes_and_locations_csv_export.csv vendored

@ -0,0 +1,2 @@
scheme_code,scheme_service_name,scheme_status,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_support_services_provided_by,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,scheme_active_dates,location_code,location_postcode,location_name,location_status,location_local_authority,location_units,location_type_of_unit,location_mobility_type,location_active_dates
,Test name,active,Yes,Housing for older people,No,DLUHC,The same organisation that owns the housing stock,People with alcohol problems,Yes,Older people with support needs,High level,Medium stay,2021-04-01T00:00:00+01:00,"Active from 1 April 2020 to 31 March 2022, Deactivated on 1 April 2022, Active from 1 April 2023",,SW1A 2AA,Downing Street,deactivating_soon,Westminster,20,Self-contained house,Fitted with equipment and adaptations,"Active from 1 April 2022 to 25 December 2023, Deactivated on 26 December 2023"
1 scheme_code scheme_service_name scheme_status scheme_sensitive scheme_type scheme_registered_under_care_act scheme_owning_organisation_name scheme_support_services_provided_by scheme_primary_client_group scheme_has_other_client_group scheme_secondary_client_group scheme_support_type scheme_intended_stay scheme_created_at scheme_active_dates location_code location_postcode location_name location_status location_local_authority location_units location_type_of_unit location_mobility_type location_active_dates
2 Test name active Yes Housing for older people No DLUHC The same organisation that owns the housing stock People with alcohol problems Yes Older people with support needs High level Medium stay 2021-04-01T00:00:00+01:00 Active from 1 April 2020 to 31 March 2022, Deactivated on 1 April 2022, Active from 1 April 2023 SW1A 2AA Downing Street deactivating_soon Westminster 20 Self-contained house Fitted with equipment and adaptations Active from 1 April 2022 to 25 December 2023, Deactivated on 26 December 2023

2
spec/fixtures/files/schemes_csv_export.csv vendored

@ -0,0 +1,2 @@
scheme_code,scheme_service_name,scheme_status,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_support_services_provided_by,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,scheme_active_dates
,Test name,active,Yes,Housing for older people,No,DLUHC,The same organisation that owns the housing stock,People with alcohol problems,Yes,Older people with support needs,High level,Medium stay,2021-04-01T00:00:00+01:00,"Active from 1 April 2020 to 31 March 2022, Deactivated on 1 April 2022, Active from 1 April 2023"
1 scheme_code scheme_service_name scheme_status scheme_sensitive scheme_type scheme_registered_under_care_act scheme_owning_organisation_name scheme_support_services_provided_by scheme_primary_client_group scheme_has_other_client_group scheme_secondary_client_group scheme_support_type scheme_intended_stay scheme_created_at scheme_active_dates
2 Test name active Yes Housing for older people No DLUHC The same organisation that owns the housing stock People with alcohol problems Yes Older people with support needs High level Medium stay 2021-04-01T00:00:00+01:00 Active from 1 April 2020 to 31 March 2022, Deactivated on 1 April 2022, Active from 1 April 2023

94
spec/jobs/scheme_email_csv_job_spec.rb

@ -0,0 +1,94 @@
require "rails_helper"
describe SchemeEmailCsvJob do
include Helpers
test_url = :test_url
let(:job) { described_class.new }
let(:user) { FactoryBot.create(:user) }
let(:storage_service) { instance_double(Storage::S3Service) }
let(:mailer) { instance_double(CsvDownloadMailer) }
let(:scheme_csv_service) { instance_double(Csv::SchemeCsvService) }
let(:search_term) { "meaning" }
let(:filters) { { "owning_organisation" => organisation.id, "status" => %w[active] } }
let(:all_orgs) { false }
let(:organisation) { build(:organisation) }
let(:download_type) { "combined" }
let(:schemes) { build_list(:scheme, 5, owning_organisation: organisation) }
let(:locations) { build_list(:location, 5, scheme: schemes.first) }
before do
allow(Storage::S3Service).to receive(:new).and_return(storage_service)
allow(storage_service).to receive(:write_file)
allow(storage_service).to receive(:get_presigned_url).and_return(test_url)
allow(Csv::SchemeCsvService).to receive(:new).and_return(scheme_csv_service)
allow(scheme_csv_service).to receive(:prepare_csv).and_return("")
allow(CsvDownloadMailer).to receive(:new).and_return(mailer)
allow(mailer).to receive(:send_csv_download_mail)
end
context "when exporting" do
before do
allow(FilterManager).to receive(:filter_schemes).and_return(schemes)
end
context "when download type schemes" do
let(:download_type) { "schemes" }
it "uses an appropriate filename in S3" do
expect(storage_service).to receive(:write_file).with(/schemes-.*\.csv/, anything)
job.perform(user)
end
end
context "when download type locations" do
let(:download_type) { "locations" }
it "uses an appropriate filename in S3" do
expect(storage_service).to receive(:write_file).with(/locations-.*\.csv/, anything)
job.perform(user)
end
end
context "when download type combined" do
let(:download_type) { "combined" }
it "uses an appropriate filename in S3" do
expect(storage_service).to receive(:write_file).with(/schemes-and-locations.*\.csv/, anything)
job.perform(user)
end
end
it "includes the organisation name in the filename when one is provided" do
expect(storage_service).to receive(:write_file).with(/schemes-and-locations-#{organisation.name}-.*\.csv/, anything)
job.perform(user, nil, {}, nil, organisation, "combined")
end
it "calls the filter manager with the arguments provided" do
expect(FilterManager).to receive(:filter_schemes).with(a_kind_of(ActiveRecord::Relation), search_term, filters, all_orgs, user)
job.perform(user, search_term, filters, all_orgs, organisation, "combined")
end
it "creates a SchemeCsvService with the correct download type" do
expect(Csv::SchemeCsvService).to receive(:new).with(download_type: "schemes")
job.perform(user, nil, {}, nil, nil, "schemes")
expect(Csv::SchemeCsvService).to receive(:new).with(download_type: "locations")
job.perform(user, nil, {}, nil, nil, "locations")
expect(Csv::SchemeCsvService).to receive(:new).with(download_type: "combined")
job.perform(user, nil, {}, nil, nil, "combined")
end
it "passes the schemes returned by the filter manager to the csv service" do
expect(scheme_csv_service).to receive(:prepare_csv).with(schemes)
job.perform(user, nil, {}, nil, nil, "combined")
end
end
it "sends an E-mail with the presigned URL and duration" do
expect(mailer).to receive(:send_csv_download_mail).with(user, test_url, instance_of(Integer))
job.perform(user)
end
end

4
spec/requests/lettings_logs_controller_spec.rb

@ -788,7 +788,7 @@ RSpec.describe LettingsLogsController, type: :request do
end
it "shows the total log count" do
expect(CGI.unescape_html(response.body)).to match("<strong>1</strong> matching logs")
expect(CGI.unescape_html(response.body)).to match("<strong>1</strong> total logs")
end
it "does not show the pagination links" do
@ -882,7 +882,7 @@ RSpec.describe LettingsLogsController, type: :request do
end
it "shows the total log count" do
expect(CGI.unescape_html(response.body)).to match("<strong>26</strong> matching logs")
expect(CGI.unescape_html(response.body)).to match("<strong>26</strong> total logs")
end
it "has pagination links" do

12
spec/requests/organisation_relationships_controller_spec.rb

@ -47,7 +47,7 @@ RSpec.describe OrganisationRelationshipsController, type: :request do
end
it "shows the pagination count" do
expect(page).to have_content("1 matching stock owners")
expect(page).to have_content("1 total stock owners")
end
context "when adding a stock owner" do
@ -113,7 +113,7 @@ RSpec.describe OrganisationRelationshipsController, type: :request do
end
it "shows the pagination count" do
expect(page).to have_content("1 matching agents")
expect(page).to have_content("1 total agents")
end
end
@ -285,7 +285,7 @@ RSpec.describe OrganisationRelationshipsController, type: :request do
end
it "shows the pagination count" do
expect(page).to have_content("1 matching stock owners")
expect(page).to have_content("1 total stock owners")
end
end
@ -421,7 +421,7 @@ RSpec.describe OrganisationRelationshipsController, type: :request do
end
it "shows the pagination count" do
expect(page).to have_content("1 matching agents")
expect(page).to have_content("1 total agents")
end
end
@ -587,7 +587,7 @@ RSpec.describe OrganisationRelationshipsController, type: :request do
end
it "shows the pagination count" do
expect(page).to have_content("1 matching stock owners")
expect(page).to have_content("1 total stock owners")
end
context "when adding a stock owner" do
@ -637,7 +637,7 @@ RSpec.describe OrganisationRelationshipsController, type: :request do
end
it "shows the pagination count" do
expect(page).to have_content("1 matching agents")
expect(page).to have_content("1 total agents")
end
it "shows remove link(s)" do

126
spec/requests/organisations_controller_spec.rb

@ -59,6 +59,66 @@ RSpec.describe OrganisationsController, type: :request do
expect(page).to have_field("search", type: "search")
end
describe "scheme and location csv downloads" do
let!(:specific_organisation) { create(:organisation) }
let!(:specific_org_scheme) { create(:scheme, owning_organisation: specific_organisation) }
before do
create_list(:scheme, 5, owning_organisation: specific_organisation)
create_list(:location, 3, scheme: specific_org_scheme)
get "/organisations/#{specific_organisation.id}/schemes", headers:, params: {}
end
it "shows scheme and location download links" do
expect(page).to have_link("Download schemes (CSV)", href: schemes_csv_download_organisation_path(specific_organisation, download_type: "schemes"))
expect(page).to have_link("Download locations (CSV)", href: schemes_csv_download_organisation_path(specific_organisation, download_type: "locations"))
expect(page).to have_link("Download schemes and locations (CSV)", href: schemes_csv_download_organisation_path(specific_organisation, download_type: "combined"))
end
context "when there are no schemes for this organisation" do
before do
specific_organisation.owned_schemes.destroy_all
get "/organisations/#{specific_organisation.id}/schemes", headers:, params: {}
end
it "does not display CSV download links" do
expect(page).not_to have_link("Download schemes (CSV)")
expect(page).not_to have_link("Download locations (CSV)")
expect(page).not_to have_link("Download schemes and locations (CSV)")
end
end
context "when downloading scheme data" do
before do
get schemes_csv_download_organisation_path(specific_organisation, download_type: "schemes")
end
it "redirects to the correct download page" do
expect(page).to have_content("You've selected 6 schemes.")
end
end
context "when downloading location data" do
before do
get schemes_csv_download_organisation_path(specific_organisation, download_type: "locations")
end
it "redirects to the correct download page" do
expect(page).to have_content("You've selected 3 locations from 6 schemes.")
end
end
context "when downloading scheme and location data" do
before do
get schemes_csv_download_organisation_path(specific_organisation, download_type: "combined")
end
it "redirects to the correct download page" do
expect(page).to have_content("You've selected 6 schemes with 3 locations.")
end
end
end
it "has hidden accessibility field with description" do
expected_field = "<h2 class=\"govuk-visually-hidden\">Supported housing schemes</h2>"
expect(CGI.unescape_html(response.body)).to include(expected_field)
@ -116,6 +176,62 @@ RSpec.describe OrganisationsController, type: :request do
expect(page).to have_field("search", type: "search")
end
describe "scheme and location csv downloads" do
before do
create_list(:scheme, 5, owning_organisation: user.organisation)
create_list(:location, 3, scheme: same_org_scheme)
end
it "shows scheme and location download links" do
expect(page).to have_link("Download schemes (CSV)", href: csv_download_schemes_path(download_type: "schemes"))
expect(page).to have_link("Download locations (CSV)", href: csv_download_schemes_path(download_type: "locations"))
expect(page).to have_link("Download schemes and locations (CSV)", href: csv_download_schemes_path(download_type: "combined"))
end
context "when there are no schemes for this organisation" do
before do
user.organisation.owned_schemes.destroy_all
get "/organisations/#{organisation.id}/schemes", headers:, params: {}
end
it "does not display CSV download links" do
expect(page).not_to have_link("Download schemes (CSV)")
expect(page).not_to have_link("Download locations (CSV)")
expect(page).not_to have_link("Download schemes and locations (CSV)")
end
end
context "when downloading scheme data" do
before do
get csv_download_schemes_path(download_type: "schemes")
end
it "redirects to the correct download page" do
expect(page).to have_content("You've selected 6 schemes.")
end
end
context "when downloading location data" do
before do
get csv_download_schemes_path(download_type: "locations")
end
it "redirects to the correct download page" do
expect(page).to have_content("You've selected 3 locations from 6 schemes.")
end
end
context "when downloading scheme and location data" do
before do
get csv_download_schemes_path(download_type: "combined")
end
it "redirects to the correct download page" do
expect(page).to have_content("You've selected 6 schemes with 3 locations.")
end
end
end
it "shows only schemes belonging to the same organisation" do
expect(page).to have_content(same_org_scheme.id_to_display)
schemes.each do |scheme|
@ -415,7 +531,7 @@ RSpec.describe OrganisationsController, type: :request do
end
it "shows the pagination count" do
expect(page).to have_content("#{user.organisation.users.count} matching users")
expect(page).to have_content("#{user.organisation.users.count} total users")
end
end
@ -799,7 +915,7 @@ RSpec.describe OrganisationsController, type: :request do
total_number_of_orgs = Organisation.all.count
expect(page).to have_link organisation.name, href: "organisations/#{organisation.id}/lettings-logs"
expect(page).to have_link unauthorised_organisation.name, href: "organisations/#{unauthorised_organisation.id}/lettings-logs"
expect(page).to have_content("#{total_number_of_orgs} matching organisations")
expect(page).to have_content("#{total_number_of_orgs} total organisations")
end
it "shows a search bar" do
@ -828,7 +944,7 @@ RSpec.describe OrganisationsController, type: :request do
end
it "only shows logs for that organisation" do
expect(page).to have_content("#{total_number_of_org1_logs} matching logs")
expect(page).to have_content("#{total_number_of_org1_logs} total logs")
organisation.lettings_logs.visible.map(&:id).each do |lettings_log_id|
expect(page).to have_link lettings_log_id.to_s, href: "/lettings-logs/#{lettings_log_id}"
@ -982,7 +1098,7 @@ RSpec.describe OrganisationsController, type: :request do
end
it "only shows logs for that organisation" do
expect(page).to have_content("#{number_of_org1_sales_logs} matching logs")
expect(page).to have_content("#{number_of_org1_sales_logs} total logs")
organisation.sales_logs.map(&:id).each do |sales_log_id|
expect(page).to have_link sales_log_id.to_s, href: "/sales-logs/#{sales_log_id}"
end
@ -1243,7 +1359,7 @@ RSpec.describe OrganisationsController, type: :request do
end
it "shows the total organisations count" do
expect(CGI.unescape_html(response.body)).to match("<strong>#{total_organisations_count}</strong> matching organisations")
expect(CGI.unescape_html(response.body)).to match("<strong>#{total_organisations_count}</strong> total organisations")
end
it "has pagination links" do

4
spec/requests/sales_logs_controller_spec.rb

@ -703,7 +703,7 @@ RSpec.describe SalesLogsController, type: :request do
end
it "shows the total log count" do
expect(CGI.unescape_html(response.body)).to match("<strong>1</strong> matching logs")
expect(CGI.unescape_html(response.body)).to match("<strong>1</strong> total logs")
end
it "does not show the pagination links" do
@ -756,7 +756,7 @@ RSpec.describe SalesLogsController, type: :request do
end
it "shows the total log count" do
expect(CGI.unescape_html(response.body)).to match("<strong>26</strong> matching logs")
expect(CGI.unescape_html(response.body)).to match("<strong>26</strong> total logs")
end
it "has pagination links" do

67
spec/requests/schemes_controller_spec.rb

@ -191,6 +191,67 @@ RSpec.describe SchemesController, type: :request do
expect(page).to have_content("Schemes")
end
describe "scheme and location csv downloads" do
let!(:same_org_scheme) { create(:scheme, owning_organisation: user.organisation) }
let!(:specific_organisation) { create(:organisation) }
let!(:specific_org_scheme) { create(:scheme, owning_organisation: specific_organisation) }
before do
create(:location, scheme: same_org_scheme)
create_list(:scheme, 5, owning_organisation: specific_organisation)
create_list(:location, 3, scheme: specific_org_scheme)
end
it "shows scheme and location download links" do
expect(page).to have_link("Download schemes (CSV)", href: csv_download_schemes_path(download_type: "schemes"))
expect(page).to have_link("Download locations (CSV)", href: csv_download_schemes_path(download_type: "locations"))
expect(page).to have_link("Download schemes and locations (CSV)", href: csv_download_schemes_path(download_type: "combined"))
end
context "when there are no schemes for any organisation" do
before do
Scheme.destroy_all
get "/schemes"
end
it "does not display CSV download links" do
expect(page).not_to have_link("Download schemes (CSV)")
expect(page).not_to have_link("Download locations (CSV)")
expect(page).not_to have_link("Download schemes and locations (CSV)")
end
end
context "when downloading scheme data" do
before do
get csv_download_schemes_path(download_type: "schemes")
end
it "redirects to the correct download page" do
expect(page).to have_content("You've selected 12 schemes.")
end
end
context "when downloading location data" do
before do
get csv_download_schemes_path(download_type: "locations")
end
it "redirects to the correct download page" do
expect(page).to have_content("You've selected 9 locations from 12 schemes.")
end
end
context "when downloading scheme and location data" do
before do
get csv_download_schemes_path(download_type: "combined")
end
it "redirects to the correct download page" do
expect(page).to have_content("You've selected 12 schemes with 9 locations.")
end
end
end
it "shows all schemes" do
schemes.each do |scheme|
expect(page).to have_content(scheme.id_to_display)
@ -236,7 +297,7 @@ RSpec.describe SchemesController, type: :request do
end
it "shows the total organisations count" do
expect(CGI.unescape_html(response.body)).to match("<strong>#{schemes.count}</strong> matching schemes")
expect(CGI.unescape_html(response.body)).to match("<strong>#{schemes.count}</strong> total schemes")
end
context "when paginating over 20 results" do
@ -252,7 +313,7 @@ RSpec.describe SchemesController, type: :request do
end
it "shows the total schemes count" do
expect(CGI.unescape_html(response.body)).to match("<strong>#{total_schemes_count}</strong> matching schemes")
expect(CGI.unescape_html(response.body)).to match("<strong>#{total_schemes_count}</strong> total schemes")
end
it "shows which schemes are being shown on the current page" do
@ -277,7 +338,7 @@ RSpec.describe SchemesController, type: :request do
end
it "shows the total schemes count" do
expect(CGI.unescape_html(response.body)).to match("<strong>#{total_schemes_count}</strong> matching schemes")
expect(CGI.unescape_html(response.body)).to match("<strong>#{total_schemes_count}</strong> total schemes")
end
it "has pagination links" do

2
spec/requests/users_controller_spec.rb

@ -1194,7 +1194,7 @@ RSpec.describe UsersController, type: :request do
end
it "shows the pagination count" do
expect(page).to have_content("4 matching users")
expect(page).to have_content("4 total users")
end
it "shows the download csv link" do

147
spec/services/csv/scheme_csv_service_spec.rb

@ -0,0 +1,147 @@
require "rails_helper"
RSpec.describe Csv::SchemeCsvService do
let(:organisation) { create(:organisation) }
let(:fixed_time) { Time.zone.local(2023, 6, 26) }
let(:scheme) { create(:scheme, :export, owning_organisation: organisation, service_name: "Test name") }
let(:location) { create(:location, :export, scheme:) }
let(:service) { described_class.new(download_type:) }
let(:download_type) { "combined" }
let(:csv) { CSV.parse(service.prepare_csv(Scheme.where(id: schemes.map(&:id)))) }
let(:schemes) { [scheme] }
let(:headers) { csv.first }
before do
Timecop.freeze(fixed_time)
create(:scheme_deactivation_period, scheme:, deactivation_date: scheme.created_at + 1.year, reactivation_date: scheme.created_at + 2.years)
create(:location_deactivation_period, location:, deactivation_date: location.created_at + 6.months)
end
after do
Timecop.return
end
it "returns a string" do
result = service.prepare_csv(Scheme.all)
expect(result).to be_a String
end
it "returns a csv with headers" do
expect(csv.first.first).to eq "scheme_code"
end
it "returns the correctly formatted scheme code" do
expect(csv.second.first.first).to eq "S"
end
context "when download type is schemes" do
let(:download_type) { "schemes" }
let(:scheme_attributes) { %w[scheme_code scheme_service_name scheme_status scheme_sensitive scheme_type scheme_registered_under_care_act scheme_owning_organisation_name scheme_support_services_provided_by scheme_primary_client_group scheme_has_other_client_group scheme_secondary_client_group scheme_support_type scheme_intended_stay scheme_created_at scheme_active_dates] }
it "has the correct headers" do
expect(headers).to eq(scheme_attributes)
end
it "exports the CSV with all values correct" do
expected_content = CSV.read("spec/fixtures/files/schemes_csv_export.csv")
values_to_delete = %w[scheme_code]
values_to_delete.each do |attribute|
index = csv.first.index(attribute)
csv.second[index] = nil
end
expect(csv).to eq expected_content
end
context "when there are many schemes and locations" do
let(:schemes) { create_list(:scheme, scheme_count) }
let(:scheme_count) { 5 }
let(:locations_per_scheme) { 2 }
before do
schemes.each do |scheme|
create_list(:location, locations_per_scheme, scheme:)
end
end
it "creates a CSV with the correct number of schemes" do
expected_row_count_with_headers = scheme_count + 1
expect(csv.size).to be expected_row_count_with_headers
end
end
end
context "when download type is locations" do
let(:download_type) { "locations" }
let(:location_attributes) { %w[scheme_code location_code location_postcode location_name location_status location_local_authority location_units location_type_of_unit location_mobility_type location_active_dates] }
it "has the correct headers" do
expect(headers).to eq(location_attributes)
end
it "exports the CSV with all values correct" do
expected_content = CSV.read("spec/fixtures/files/locations_csv_export.csv")
values_to_delete = %w[scheme_code location_code]
values_to_delete.each do |attribute|
index = csv.first.index(attribute)
csv.second[index] = nil
end
expect(csv).to eq expected_content
end
context "when there are many schemes and locations" do
let(:schemes) { create_list(:scheme, scheme_count) }
let(:scheme_count) { 5 }
let(:locations_per_scheme) { 2 }
before do
schemes.each do |scheme|
create_list(:location, locations_per_scheme, scheme:)
end
end
it "creates a CSV with the correct number of locations" do
expected_row_count_with_headers = locations_per_scheme * scheme_count + 1
expect(csv.size).to be expected_row_count_with_headers
end
end
end
context "when download type is combined" do
let(:combined_attributes) { %w[scheme_code scheme_service_name scheme_status scheme_sensitive scheme_type scheme_registered_under_care_act scheme_owning_organisation_name scheme_support_services_provided_by scheme_primary_client_group scheme_has_other_client_group scheme_secondary_client_group scheme_support_type scheme_intended_stay scheme_created_at scheme_active_dates location_code location_postcode location_name location_status location_local_authority location_units location_type_of_unit location_mobility_type location_active_dates] }
before do
scheme
end
it "has the correct headers" do
expect(headers).to eq(combined_attributes)
end
it "exports the CSV with all values correct" do
expected_content = CSV.read("spec/fixtures/files/schemes_and_locations_csv_export.csv")
values_to_delete = %w[scheme_code location_code]
values_to_delete.each do |attribute|
index = csv.first.index(attribute)
csv.second[index] = nil
end
expect(csv).to eq expected_content
end
context "when there are many schemes and locations" do
let(:schemes) { create_list(:scheme, scheme_count) }
let(:scheme_count) { 5 }
let(:locations_per_scheme) { 2 }
before do
schemes.each do |scheme|
create_list(:location, locations_per_scheme, scheme:)
end
end
it "creates a CSV with the correct number of locations" do
expected_row_count_with_headers = locations_per_scheme * scheme_count + 1
expect(csv.size).to be expected_row_count_with_headers
end
end
end
end

17
spec/services/filter_manager_spec.rb

@ -77,4 +77,21 @@ describe FilterManager do
end
end
end
describe "filter_schemes" do
let(:schemes) { create_list(:scheme, 5) }
let(:alphabetical_order_schemes) { [schemes[4], schemes[2], schemes[0], schemes[1], schemes[3]] }
before do
schemes[4].update!(service_name: "a")
schemes[2].update!(service_name: "bB")
schemes[0].update!(service_name: "C")
schemes[1].update!(service_name: "Dd")
schemes[3].update!(service_name: "e")
end
it "returns schemes in alphabetical order by service name" do
expect(described_class.filter_schemes(Scheme.all, nil, {}, nil, nil)).to eq(alphabetical_order_schemes)
end
end
end

Loading…
Cancel
Save