diff --git a/app/components/primary_navigation_component.rb b/app/components/primary_navigation_component.rb
index 0389d30b3..e766a83ce 100644
--- a/app/components/primary_navigation_component.rb
+++ b/app/components/primary_navigation_component.rb
@@ -3,6 +3,7 @@ class PrimaryNavigationComponent < ViewComponent::Base
def initialize(items:)
@items = items
+ Rails.env.production? ? @items = @items.reject { |nav_item| nav_item.text.include?("Supported housing") } : @items
super
end
diff --git a/app/components/search_component.rb b/app/components/search_component.rb
index eefdc5309..b08c1039a 100644
--- a/app/components/search_component.rb
+++ b/app/components/search_component.rb
@@ -13,6 +13,8 @@ class SearchComponent < ViewComponent::Base
request.path
elsif request.path.include?("organisations") && request.path.include?("logs")
request.path
+ elsif request.path.include?("organisations") && request.path.include?("supported-housing")
+ request.path
elsif request.path.include?("users")
user_path(current_user)
elsif request.path.include?("organisations")
diff --git a/app/controllers/organisations_controller.rb b/app/controllers/organisations_controller.rb
index d57f9b215..7a92e5811 100644
--- a/app/controllers/organisations_controller.rb
+++ b/app/controllers/organisations_controller.rb
@@ -16,6 +16,14 @@ class OrganisationsController < ApplicationController
@total_count = all_organisations.size
end
+ def schemes
+ all_schemes = Scheme.where(organisation: @organisation)
+
+ @pagy, @schemes = pagy(filtered_collection(all_schemes, search_term))
+ @searched = search_term.presence
+ @total_count = all_schemes.size
+ end
+
def show
redirect_to details_organisation_path(@organisation)
end
diff --git a/app/controllers/schemes_controller.rb b/app/controllers/schemes_controller.rb
new file mode 100644
index 000000000..83bff5542
--- /dev/null
+++ b/app/controllers/schemes_controller.rb
@@ -0,0 +1,26 @@
+class SchemesController < ApplicationController
+ include Pagy::Backend
+ include Modules::SearchFilter
+
+ before_action :authenticate_user!
+ before_action :authenticate_scope!
+
+ def index
+ redirect_to supported_housing_organisation_path(current_user.organisation) unless current_user.support?
+ all_schemes = Scheme.all
+
+ @pagy, @schemes = pagy(filtered_collection(all_schemes, search_term))
+ @searched = search_term.presence
+ @total_count = all_schemes.size
+ end
+
+private
+
+ def search_term
+ params["search"]
+ end
+
+ def authenticate_scope!
+ head :unauthorized and return unless current_user.data_coordinator? || current_user.support?
+ end
+end
diff --git a/app/helpers/navigation_items_helper.rb b/app/helpers/navigation_items_helper.rb
index 1ebf17d11..c0b55c674 100644
--- a/app/helpers/navigation_items_helper.rb
+++ b/app/helpers/navigation_items_helper.rb
@@ -7,6 +7,14 @@ module NavigationItemsHelper
NavigationItem.new("Organisations", organisations_path, organisations_current?(path)),
NavigationItem.new("Users", "/users", users_current?(path)),
NavigationItem.new("Logs", case_logs_path, logs_current?(path)),
+ NavigationItem.new("Supported housing", "/supported-housing", supported_housing_current?(path)),
+ ]
+ elsif current_user.data_coordinator?
+ [
+ NavigationItem.new("Logs", case_logs_path, logs_current?(path)),
+ NavigationItem.new("Supported housing", "/supported-housing", subnav_supported_housing_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)),
]
else
[
@@ -20,6 +28,7 @@ module NavigationItemsHelper
def secondary_items(path, current_organisation_id)
[
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("Users", "/organisations/#{current_organisation_id}/users", subnav_users_path?(path)),
NavigationItem.new("About this organisation", "/organisations/#{current_organisation_id}", subnav_details_path?(path)),
]
@@ -35,8 +44,16 @@ private
path == "/users"
end
+ def supported_housing_current?(path)
+ path == "/supported-housing"
+ end
+
def organisations_current?(path)
- path == "/organisations" || subnav_users_path?(path) || subnav_logs_path?(path) || subnav_details_path?(path)
+ path == "/organisations" || subnav_users_path?(path) || subnav_logs_path?(path) || subnav_details_path?(path) || subnav_supported_housing_path?(path)
+ end
+
+ def subnav_supported_housing_path?(path)
+ path.include?("/organisations") && path.include?("/supported-housing")
end
def subnav_users_path?(path)
diff --git a/app/models/organisation.rb b/app/models/organisation.rb
index 6f04c037a..0319befa4 100644
--- a/app/models/organisation.rb
+++ b/app/models/organisation.rb
@@ -4,6 +4,7 @@ class Organisation < ApplicationRecord
has_many :managed_case_logs, class_name: "CaseLog", foreign_key: "managing_organisation_id"
has_many :data_protection_confirmations
has_many :organisation_rent_periods
+ has_many :schemes
scope :search_by_name, ->(name) { where("name ILIKE ?", "%#{name}%") }
scope :search_by, ->(param) { search_by_name(param) }
diff --git a/app/models/scheme.rb b/app/models/scheme.rb
new file mode 100644
index 000000000..7f6ec337e
--- /dev/null
+++ b/app/models/scheme.rb
@@ -0,0 +1,7 @@
+class Scheme < ApplicationRecord
+ belongs_to :organisation
+
+ scope :search_by_code, ->(code) { where("code ILIKE ?", "%#{code}%") }
+ scope :search_by_service_name, ->(name) { where("service_name ILIKE ?", "%#{name}%") }
+ scope :search_by, ->(param) { search_by_code(param).or(search_by_service_name(param)) }
+end
diff --git a/app/views/organisations/schemes.html.erb b/app/views/organisations/schemes.html.erb
new file mode 100644
index 000000000..b28d9c306
--- /dev/null
+++ b/app/views/organisations/schemes.html.erb
@@ -0,0 +1,22 @@
+<% item_label = format_label(@pagy.count, "scheme") %>
+<% title = format_title(@searched, "Supported housing services", current_user, item_label, @pagy.count, @organisation.name) %>
+
+<% 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 } %>
+
+<% if current_user.support? %>
+ <%= render SubNavigationComponent.new(
+ items: secondary_items(request.path, @organisation.id),
+ ) %>
+<% end %>
+
+
Supported housing services
+
+<%= render SearchComponent.new(current_user:, search_label: "Search by service name or code", value: @searched) %>
+
+
+
+<%= render partial: "schemes/scheme_list", locals: { schemes: @schemes, title:, pagy: @pagy, searched: @searched, item_label:, total_count: @total_count } %>
+
+<%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "schemes" } %>
diff --git a/app/views/schemes/_scheme_list.html.erb b/app/views/schemes/_scheme_list.html.erb
new file mode 100644
index 000000000..002a215b4
--- /dev/null
+++ b/app/views/schemes/_scheme_list.html.erb
@@ -0,0 +1,39 @@
+
+ <%= govuk_table do |table| %>
+ <%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %>
+
+ <% if searched.present? %>
+ <%= pagy.count %> <%= item_label %> found matching ‘<%= searched %>’ of <%= total_count %> total schemes. <%= govuk_link_to("Clear search", request.path) %>
+ <% else %>
+ <%= pagy.count %> total schemes.
+ <% end %>
+
+ <% end %>
+ <%= table.head do |head| %>
+ <%= head.row do |row| %>
+ <% row.cell(header: true, text: "Code", html_attributes: {
+ scope: "col",
+ }) %>
+ <% row.cell(header: true, text: "Service", html_attributes: {
+ scope: "col",
+ }) %>
+ <% row.cell(header: true, text: "Managing agent", html_attributes: {
+ scope: "col",
+ }) %>
+ <% row.cell(header: true, text: "Created", html_attributes: {
+ scope: "col",
+ }) %>
+ <% end %>
+ <% end %>
+ <% @schemes.each do |scheme| %>
+ <%= table.body do |body| %>
+ <%= body.row do |row| %>
+ <% row.cell(text: scheme.code) %>
+ <% row.cell(text: scheme.service_name) %>
+ <% row.cell(text: scheme.organisation.name) %>
+ <% row.cell(text: scheme.created_at.to_formatted_s(:govuk_date)) %>
+ <% end %>
+ <% end %>
+ <% end %>
+ <% end %>
+
diff --git a/app/views/schemes/index.html.erb b/app/views/schemes/index.html.erb
new file mode 100644
index 000000000..b96e7bdc3
--- /dev/null
+++ b/app/views/schemes/index.html.erb
@@ -0,0 +1,16 @@
+<% item_label = format_label(@pagy.count, "scheme") %>
+<% title = format_title(@searched, "Supported housing services", current_user, item_label, @pagy.count, nil) %>
+
+<% 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 } %>
+
+Supported housing services
+
+<%= render SearchComponent.new(current_user:, search_label: "Search by service name or code", value: @searched) %>
+
+
+
+<%= render partial: "schemes/scheme_list", locals: { schemes: @schemes, title:, pagy: @pagy, searched: @searched, item_label:, total_count: @total_count } %>
+
+<%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "schemes" } %>
diff --git a/config/routes.rb b/config/routes.rb
index 4f7bc91e5..c7b3df36e 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -35,6 +35,8 @@ Rails.application.routes.draw do
get "edit/password", to: "users#edit_password"
end
+ resources :schemes, path: "/supported-housing", only: [:index]
+
resources :users do
member do
get "deactivate", to: "users#deactivate"
@@ -48,6 +50,7 @@ Rails.application.routes.draw do
get "users", to: "organisations#users"
get "users/invite", to: "users/account#new"
get "logs", to: "organisations#logs"
+ get "supported-housing", to: "organisations#schemes"
end
end
diff --git a/db/migrate/20220608144156_create_schemes.rb b/db/migrate/20220608144156_create_schemes.rb
new file mode 100644
index 000000000..2f96bc29f
--- /dev/null
+++ b/db/migrate/20220608144156_create_schemes.rb
@@ -0,0 +1,11 @@
+class CreateSchemes < ActiveRecord::Migration[7.0]
+ def change
+ create_table :schemes do |t|
+ t.string :code
+ t.string :service_name
+ t.references :organisation, null: false, foreign_key: true
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 4cca6211e..cd132897d 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.0].define(version: 2022_06_06_082639) do
+ActiveRecord::Schema[7.0].define(version: 2022_06_08_144156) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -192,9 +192,9 @@ ActiveRecord::Schema[7.0].define(version: 2022_06_06_082639) do
t.integer "joint"
t.bigint "created_by_id"
t.integer "illness_type_0"
+ t.integer "retirement_value_check"
t.integer "tshortfall_known"
t.integer "shelteredaccom"
- t.integer "retirement_value_check"
t.integer "pregnancy_value_check"
t.index ["created_by_id"], name: "index_case_logs_on_created_by_id"
t.index ["managing_organisation_id"], name: "index_case_logs_on_managing_organisation_id"
@@ -232,7 +232,7 @@ ActiveRecord::Schema[7.0].define(version: 2022_06_06_082639) do
create_table "logs_exports", force: :cascade do |t|
t.datetime "created_at", default: -> { "CURRENT_TIMESTAMP" }
- t.datetime "started_at", precision: nil, null: false
+ t.datetime "started_at", null: false
t.integer "base_number", default: 1, null: false
t.integer "increment_number", default: 1, null: false
t.boolean "empty_export", default: false, null: false
@@ -277,6 +277,15 @@ ActiveRecord::Schema[7.0].define(version: 2022_06_06_082639) do
t.index ["old_visible_id"], name: "index_organisations_on_old_visible_id", unique: true
end
+ create_table "schemes", force: :cascade do |t|
+ t.string "code"
+ t.string "service_name"
+ t.bigint "organisation_id", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["organisation_id"], name: "index_schemes_on_organisation_id"
+ end
+
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
@@ -330,4 +339,5 @@ ActiveRecord::Schema[7.0].define(version: 2022_06_06_082639) do
t.index ["item_type", "item_id"], name: "index_versions_on_item_type_and_item_id"
end
+ add_foreign_key "schemes", "organisations"
end
diff --git a/db/seeds.rb b/db/seeds.rb
index 54ffb843a..356e020b4 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -54,6 +54,45 @@ unless Rails.env.test?
pp "Seeded 3 dummy users"
end
+ if Rails.env.development?
+ dummy_org = Organisation.find_or_create_by!(
+ name: "FooBar LTD",
+ address_line1: "Higher Kingston",
+ address_line2: "Yeovil",
+ postcode: "BA21 4AT",
+ holds_own_stock: false,
+ other_stock_owners: "None",
+ managing_agents: "None",
+ provider_type: "LA",
+ )
+
+ pp "Seeded dummy FooBar LTD organisation"
+ end
+
+ if Rails.env.development? && Scheme.count.zero?
+ Scheme.create!(
+ code: "S878",
+ service_name: "Beulahside Care",
+ organisation: org,
+ created_at: Time.zone.now,
+ )
+
+ Scheme.create!(
+ code: "S312",
+ service_name: "Abdullahview Point",
+ organisation: org,
+ created_at: Time.zone.now,
+ )
+
+ Scheme.create!(
+ code: "7XYZ",
+ service_name: "Caspermouth Center",
+ organisation: dummy_org,
+ created_at: Time.zone.now,
+ )
+ end
+
+ pp "Seeded 3 dummy schemes"
if LaRentRange.count.zero?
Dir.glob("config/rent_range_data/*.csv").each do |path|
start_year = File.basename(path, ".csv")
diff --git a/spec/components/primary_navigation_component_spec.rb b/spec/components/primary_navigation_component_spec.rb
index 0de1c58d1..9a3c91d09 100644
--- a/spec/components/primary_navigation_component_spec.rb
+++ b/spec/components/primary_navigation_component_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe PrimaryNavigationComponent, type: :component do
NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true),
NavigationItemsHelper::NavigationItem.new("Users", "/users", false),
NavigationItemsHelper::NavigationItem.new("Logs ", "/logs", false),
+ NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", false),
]
end
@@ -24,6 +25,7 @@ RSpec.describe PrimaryNavigationComponent, type: :component do
expect(navigation_panel).to be_highlighted_item(items[0], "/something-else")
expect(navigation_panel).not_to be_highlighted_item(items[1], "/something-else")
expect(navigation_panel).not_to be_highlighted_item(items[2], "/something-else")
+ expect(navigation_panel).not_to be_highlighted_item(items[3], "/something-else")
end
end
@@ -34,6 +36,18 @@ RSpec.describe PrimaryNavigationComponent, type: :component do
expect(result.text).to include("Organisations")
expect(result.text).to include("Users")
expect(result.text).to include("Logs")
+ expect(result.text).to include("Supported housing")
+ end
+ end
+
+ context "when production environment" do
+ before do
+ allow(Rails.env).to receive(:production?).and_return(true)
+ end
+
+ it "doesn't render supported housing" do
+ result = render_inline(described_class.new(items:))
+ expect(result.text).not_to include("Supported housing")
end
end
end
diff --git a/spec/factories/scheme.rb b/spec/factories/scheme.rb
new file mode 100644
index 000000000..8d15032fe
--- /dev/null
+++ b/spec/factories/scheme.rb
@@ -0,0 +1,8 @@
+FactoryBot.define do
+ factory :scheme do
+ code { Faker::Name.initials(number: 4) }
+ service_name { Faker::Name.name_with_middle }
+ organisation
+ created_at { Time.zone.now }
+ end
+end
diff --git a/spec/features/schemes_spec.rb b/spec/features/schemes_spec.rb
new file mode 100644
index 000000000..b1fe131fe
--- /dev/null
+++ b/spec/features/schemes_spec.rb
@@ -0,0 +1,78 @@
+require "rails_helper"
+
+RSpec.describe "Supported housing scheme Features" do
+ context "when viewing list of schemes" do
+ context "when I am signed as a support user in there are schemes in the database" do
+ let(:user) { FactoryBot.create(:user, :support, last_sign_in_at: Time.zone.now) }
+ let!(:schemes) { FactoryBot.create_list(:scheme, 5) }
+ let!(:scheme_to_search) { FactoryBot.create(:scheme) }
+ let(:notify_client) { instance_double(Notifications::Client) }
+ let(:confirmation_token) { "MCDH5y6Km-U7CFPgAMVS" }
+ let(:devise_notify_mailer) { DeviseNotifyMailer.new }
+ let(:otp) { "999111" }
+
+ before do
+ allow(DeviseNotifyMailer).to receive(:new).and_return(devise_notify_mailer)
+ allow(devise_notify_mailer).to receive(:notify_client).and_return(notify_client)
+ allow(Devise).to receive(:friendly_token).and_return(confirmation_token)
+ allow(notify_client).to receive(:send_email).and_return(true)
+ allow(SecureRandom).to receive(:random_number).and_return(otp)
+ visit("/logs")
+ fill_in("user[email]", with: user.email)
+ fill_in("user[password]", with: user.password)
+ click_button("Sign in")
+ fill_in("code", with: otp)
+ click_button("Submit")
+ end
+
+ it "displays the link to the supported housing" do
+ expect(page).to have_link("Supported housing", href: "/supported-housing")
+ end
+
+ context "when I click Supported housing" do
+ before do
+ click_link "Supported housing", href: "/supported-housing"
+ end
+
+ it "shows list of schemes" do
+ schemes.each do |scheme|
+ expect(page).to have_content(scheme.code)
+ end
+ end
+
+ context "when I search for a specific scheme" do
+ it "there is a search bar with a message and search button for schemes" do
+ expect(page).to have_field("search")
+ expect(page).to have_content("Search by service name or code")
+ expect(page).to have_button("Search")
+ end
+
+ context "when I fill in search information and press the search button" do
+ before do
+ fill_in("search", with: scheme_to_search.code)
+ click_button("Search")
+ end
+
+ it "displays scheme matching the scheme code" do
+ expect(page).to have_content(scheme_to_search.code)
+ end
+
+ context "when I want to clear results" do
+ it "there is link to clear the search results" do
+ expect(page).to have_link("Clear search")
+ end
+
+ it "displays all schemes after I clear the search results" do
+ click_link("Clear search")
+ expect(page).to have_content(scheme_to_search.code)
+ schemes.each do |scheme|
+ expect(page).to have_content(scheme.code)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/helpers/navigation_items_helper_spec.rb b/spec/helpers/navigation_items_helper_spec.rb
index 1bcf23f1a..8aa02eb82 100644
--- a/spec/helpers/navigation_items_helper_spec.rb
+++ b/spec/helpers/navigation_items_helper_spec.rb
@@ -8,10 +8,26 @@ RSpec.describe NavigationItemsHelper do
describe "#primary items" do
context "when the user is a data coordinator" do
+ context "when the user is on the logs page" do
+ let(:expected_navigation_items) do
+ [
+ NavigationItemsHelper::NavigationItem.new("Logs", "/logs", true),
+ NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", false),
+ NavigationItemsHelper::NavigationItem.new("Users", users_path, false),
+ NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false),
+ ]
+ end
+
+ it "returns navigation items with the users item set as current" do
+ expect(primary_items("/logs", current_user)).to eq(expected_navigation_items)
+ end
+ end
+
context "when the user is on the users page" do
let(:expected_navigation_items) do
[
NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false),
+ NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", false),
NavigationItemsHelper::NavigationItem.new("Users", users_path, true),
NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false),
]
@@ -26,6 +42,7 @@ RSpec.describe NavigationItemsHelper do
let(:expected_navigation_items) do
[
NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false),
+ NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", false),
NavigationItemsHelper::NavigationItem.new("Users", users_path, false),
NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, true),
]
@@ -40,6 +57,7 @@ RSpec.describe NavigationItemsHelper do
let(:expected_navigation_items) do
[
NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false),
+ NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", false),
NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false),
NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false),
]
@@ -54,12 +72,28 @@ RSpec.describe NavigationItemsHelper do
context "when the user is a support user" do
let(:current_user) { FactoryBot.create(:user, :support) }
+ context "when the user is on the logs page" do
+ let(:expected_navigation_items) do
+ [
+ NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false),
+ NavigationItemsHelper::NavigationItem.new("Users", "/users", false),
+ NavigationItemsHelper::NavigationItem.new("Logs", "/logs", true),
+ NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", false),
+ ]
+ end
+
+ it "returns navigation items with the users item set as current" do
+ expect(primary_items("/logs", current_user)).to eq(expected_navigation_items)
+ end
+ end
+
context "when the user is on the users page" do
let(:expected_navigation_items) do
[
NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false),
NavigationItemsHelper::NavigationItem.new("Users", "/users", true),
NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false),
+ NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", false),
]
end
@@ -74,6 +108,7 @@ RSpec.describe NavigationItemsHelper do
NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false),
NavigationItemsHelper::NavigationItem.new("Users", "/users", false),
NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false),
+ NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", false),
]
end
@@ -82,6 +117,21 @@ RSpec.describe NavigationItemsHelper do
end
end
+ context "when the user is on the supported housing 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("Supported housing", "/supported-housing", true),
+ ]
+ end
+
+ it "returns navigation items with the users item set as current" do
+ expect(primary_items("/supported-housing", current_user)).to eq(expected_navigation_items)
+ end
+ end
+
context "when the user is on the specific organisation's page" do
context "when the user is on organisation logs page" do
let(:required_sub_path) { "logs" }
@@ -90,12 +140,14 @@ RSpec.describe NavigationItemsHelper do
NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true),
NavigationItemsHelper::NavigationItem.new("Users", "/users", false),
NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false),
+ NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", false),
]
end
let(:expected_secondary_navigation_items) do
[
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("Users", "/organisations/#{current_user.organisation.id}/users", false),
NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", false),
]
@@ -114,12 +166,14 @@ RSpec.describe NavigationItemsHelper do
NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true),
NavigationItemsHelper::NavigationItem.new("Users", "/users", false),
NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false),
+ NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", false),
]
end
let(:expected_secondary_navigation_items) do
[
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("Users", "/organisations/#{current_user.organisation.id}/users", true),
NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", false),
]
@@ -131,6 +185,32 @@ RSpec.describe NavigationItemsHelper do
end
end
+ context "when the user is on organisation schemes page" do
+ let(:required_sub_path) { "supported-housing" }
+ let(:expected_navigation_items) do
+ [
+ NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true),
+ NavigationItemsHelper::NavigationItem.new("Users", "/users", false),
+ NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false),
+ NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", false),
+ ]
+ end
+
+ let(:expected_secondary_navigation_items) do
+ [
+ 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("Users", "/organisations/#{current_user.organisation.id}/users", false),
+ NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", false),
+ ]
+ end
+
+ it "returns navigation items with the schemes item set as current" do
+ expect(primary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user)).to eq(expected_navigation_items)
+ expect(secondary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user.organisation.id)).to eq(expected_secondary_navigation_items)
+ end
+ end
+
context "when the user is on organisation details page" do
let(:required_sub_path) { "details" }
let(:expected_navigation_items) do
@@ -138,12 +218,14 @@ RSpec.describe NavigationItemsHelper do
NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true),
NavigationItemsHelper::NavigationItem.new("Users", "/users", false),
NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false),
+ NavigationItemsHelper::NavigationItem.new("Supported housing", "/supported-housing", false),
]
end
let(:expected_secondary_navigation_items) do
[
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("Users", "/organisations/#{current_user.organisation.id}/users", false),
NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", true),
]
diff --git a/spec/models/organisation_spec.rb b/spec/models/organisation_spec.rb
index a524e1e85..17b8ec200 100644
--- a/spec/models/organisation_spec.rb
+++ b/spec/models/organisation_spec.rb
@@ -4,6 +4,7 @@ RSpec.describe Organisation, type: :model do
describe "#new" do
let(:user) { FactoryBot.create(:user) }
let!(:organisation) { user.organisation }
+ let!(:scheme) { FactoryBot.create(:scheme, organisation:) }
it "has expected fields" do
expect(organisation.attribute_names).to include("name", "phone", "provider_type")
@@ -13,6 +14,10 @@ RSpec.describe Organisation, type: :model do
expect(organisation.users.first).to eq(user)
end
+ it "has schemes" do
+ expect(organisation.schemes.first).to eq(scheme)
+ end
+
it "validates provider_type presence" do
expect { FactoryBot.create(:organisation, provider_type: nil) }
.to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Provider type can’t be blank")
diff --git a/spec/models/scheme_spec.rb b/spec/models/scheme_spec.rb
new file mode 100644
index 000000000..bbeec6746
--- /dev/null
+++ b/spec/models/scheme_spec.rb
@@ -0,0 +1,49 @@
+require "rails_helper"
+
+RSpec.describe Scheme, type: :model do
+ describe "#new" do
+ let(:scheme) { FactoryBot.create(:scheme) }
+
+ it "belongs to an organisation" do
+ expect(scheme.organisation).to be_a(Organisation)
+ end
+
+ describe "scopes" do
+ let!(:scheme_1) { FactoryBot.create(:scheme) }
+ let!(:scheme_2) { FactoryBot.create(:scheme) }
+
+ context "when searching by code" do
+ it "returns case insensitive matching records" do
+ expect(described_class.search_by_code(scheme_1.code.upcase).count).to eq(1)
+ expect(described_class.search_by_code(scheme_1.code.downcase).count).to eq(1)
+ expect(described_class.search_by_code(scheme_1.code.downcase).first.code).to eq(scheme_1.code)
+ expect(described_class.search_by_code(scheme_2.code.upcase).count).to eq(1)
+ expect(described_class.search_by_code(scheme_2.code.downcase).count).to eq(1)
+ expect(described_class.search_by_code(scheme_2.code.downcase).first.code).to eq(scheme_2.code)
+ end
+ end
+
+ context "when searching by service name" do
+ it "returns case insensitive matching records" do
+ expect(described_class.search_by_service_name(scheme_1.service_name.upcase).count).to eq(1)
+ expect(described_class.search_by_service_name(scheme_1.service_name.downcase).count).to eq(1)
+ expect(described_class.search_by_service_name(scheme_1.service_name.downcase).first.service_name).to eq(scheme_1.service_name)
+ expect(described_class.search_by_service_name(scheme_2.service_name.upcase).count).to eq(1)
+ expect(described_class.search_by_service_name(scheme_2.service_name.downcase).count).to eq(1)
+ expect(described_class.search_by_service_name(scheme_2.service_name.downcase).first.service_name).to eq(scheme_2.service_name)
+ end
+ end
+
+ context "when searching by all searchable field" do
+ it "returns case insensitive matching records" do
+ expect(described_class.search_by(scheme_1.code.upcase).count).to eq(1)
+ expect(described_class.search_by(scheme_1.code.downcase).count).to eq(1)
+ expect(described_class.search_by(scheme_1.code.downcase).first.code).to eq(scheme_1.code)
+ expect(described_class.search_by_service_name(scheme_2.service_name.upcase).count).to eq(1)
+ expect(described_class.search_by_service_name(scheme_2.service_name.downcase).count).to eq(1)
+ expect(described_class.search_by_service_name(scheme_2.service_name.downcase).first.service_name).to eq(scheme_2.service_name)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/organisations_controller_spec.rb b/spec/requests/organisations_controller_spec.rb
index 8470d50ef..cd976453d 100644
--- a/spec/requests/organisations_controller_spec.rb
+++ b/spec/requests/organisations_controller_spec.rb
@@ -30,10 +30,141 @@ RSpec.describe OrganisationsController, type: :request do
get "/organisations", headers: headers, params: {}
expect(response).to redirect_to("/account/sign-in")
end
+
+ it "does not let you see supported housing list" do
+ get "/organisations/#{organisation.id}/supported-housing", headers: headers, params: {}
+ expect(response).to redirect_to("/account/sign-in")
+ end
end
end
context "when user is signed in" do
+ describe "#schemes" do
+ context "when support user" do
+ let(:user) { FactoryBot.create(:user, :support) }
+ let!(:schemes) { FactoryBot.create_list(:scheme, 5) }
+ let!(:same_org_scheme) { FactoryBot.create(:scheme, organisation: user.organisation) }
+
+ before do
+ allow(user).to receive(:need_two_factor_authentication?).and_return(false)
+ sign_in user
+ get "/organisations/#{organisation.id}/supported-housing", headers:, params: {}
+ end
+
+ it "has page heading" do
+ expect(page).to have_content("Supported housing services")
+ end
+
+ it "shows a search bar" do
+ expect(page).to have_field("search", type: "search")
+ end
+
+ it "has hidden accebility field with description" do
+ expected_field = "Supported housing services
"
+ expect(CGI.unescape_html(response.body)).to include(expected_field)
+ end
+
+ it "shows only schemes belonging to the same organisation" do
+ expect(page).to have_content(same_org_scheme.code)
+ schemes.each do |scheme|
+ expect(page).not_to have_content(scheme.code)
+ end
+ end
+
+ context "when searching" do
+ let!(:searched_scheme) { FactoryBot.create(:scheme, code: "CODE321", organisation: user.organisation) }
+ let(:search_param) { "CODE321" }
+
+ before do
+ allow(user).to receive(:need_two_factor_authentication?).and_return(false)
+ get "/organisations/#{organisation.id}/supported-housing?search=#{search_param}"
+ end
+
+ it "returns matching results" do
+ expect(page).to have_content(searched_scheme.code)
+ schemes.each do |scheme|
+ expect(page).not_to have_content(scheme.code)
+ end
+ end
+
+ it "updates the table caption" do
+ expect(page).to have_content("1 scheme found matching ‘#{search_param}’")
+ end
+
+ it "has search in the title" do
+ expect(page).to have_title("#{user.organisation.name} (1 scheme matching ‘#{search_param}’) - Submit social housing lettings and sales data (CORE) - GOV.UK")
+ end
+ end
+ end
+
+ context "when data coordinator user" do
+ let(:user) { FactoryBot.create(:user, :data_coordinator) }
+ let!(:schemes) { FactoryBot.create_list(:scheme, 5) }
+ let!(:same_org_scheme) { FactoryBot.create(:scheme, organisation: user.organisation) }
+
+ before do
+ sign_in user
+ get "/organisations/#{organisation.id}/supported-housing", headers:, params: {}
+ end
+
+ it "has page heading" do
+ expect(page).to have_content("Supported housing services")
+ end
+
+ it "shows a search bar" do
+ expect(page).to have_field("search", type: "search")
+ end
+
+ it "has hidden accebility field with description" do
+ expected_field = "Supported housing services
"
+ expect(CGI.unescape_html(response.body)).to include(expected_field)
+ end
+
+ it "shows only schemes belonging to the same organisation" do
+ expect(page).to have_content(same_org_scheme.code)
+ schemes.each do |scheme|
+ expect(page).not_to have_content(scheme.code)
+ end
+ end
+
+ context "with schemes that are not in scope for the user, i.e. that they do not belong to" do
+ let!(:unauthorised_organisation) { FactoryBot.create(:organisation) }
+
+ before do
+ get "/organisations/#{unauthorised_organisation.id}/supported-housing", headers:, params: {}
+ end
+
+ it "returns not found 404 from org details route" do
+ expect(response).to have_http_status(:not_found)
+ end
+ end
+
+ context "when searching" do
+ let!(:searched_scheme) { FactoryBot.create(:scheme, code: "CODE321", organisation: user.organisation) }
+ let(:search_param) { "CODE321" }
+
+ before do
+ get "/organisations/#{organisation.id}/supported-housing?search=#{search_param}"
+ end
+
+ it "returns matching results" do
+ expect(page).to have_content(searched_scheme.code)
+ schemes.each do |scheme|
+ expect(page).not_to have_content(scheme.code)
+ end
+ end
+
+ it "updates the table caption" do
+ expect(page).to have_content("1 scheme found matching ‘#{search_param}’")
+ end
+
+ 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")
+ end
+ end
+ end
+ end
+
describe "#show" do
context "with an organisation that the user belongs to" do
before do
diff --git a/spec/requests/schemes_controller_spec.rb b/spec/requests/schemes_controller_spec.rb
new file mode 100644
index 000000000..13e612c46
--- /dev/null
+++ b/spec/requests/schemes_controller_spec.rb
@@ -0,0 +1,163 @@
+require "rails_helper"
+
+RSpec.describe SchemesController, type: :request do
+ let(:organisation) { user.organisation }
+ let(:headers) { { "Accept" => "text/html" } }
+ let(:page) { Capybara::Node::Simple.new(response.body) }
+ let(:user) { FactoryBot.create(:user, :support) }
+ let!(:schemes) { FactoryBot.create_list(:scheme, 5) }
+
+ describe "#index" do
+ context "when not signed in" do
+ it "redirects to the sign in page" do
+ get "/supported-housing"
+ 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 "/supported-housing"
+ 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) }
+
+ before do
+ sign_in user
+ get "/supported-housing"
+ end
+
+ it "redirects to the organisation schemes path" do
+ follow_redirect!
+ expect(path).to match("/organisations/#{user.organisation.id}/supported-housing")
+ end
+ end
+
+ context "when signed in as a support user" do
+ before do
+ allow(user).to receive(:need_two_factor_authentication?).and_return(false)
+ sign_in user
+ get "/supported-housing"
+ end
+
+ it "has page heading" do
+ expect(page).to have_content("Supported housing services")
+ end
+
+ it "shows all schemes" do
+ schemes.each do |scheme|
+ expect(page).to have_content(scheme.code)
+ end
+ end
+
+ it "shows a search bar" do
+ expect(page).to have_field("search", type: "search")
+ end
+
+ it "has correct title" do
+ expect(page).to have_title("Supported housing services - Submit social housing lettings and sales data (CORE) - GOV.UK")
+ end
+
+ it "shows the total organisations count" do
+ expect(CGI.unescape_html(response.body)).to match("#{schemes.count} total schemes.")
+ end
+
+ it "has hidden accebility field with description" do
+ expected_field = "Supported housing services
"
+ expect(CGI.unescape_html(response.body)).to include(expected_field)
+ end
+
+ context "when paginating over 20 results" do
+ let(:total_schemes_count) { Scheme.count }
+
+ before do
+ FactoryBot.create_list(:scheme, 20)
+ end
+
+ context "when on the first page" do
+ before do
+ get "/supported-housing"
+ end
+
+ it "shows the total schemes count" do
+ expect(CGI.unescape_html(response.body)).to match("#{total_schemes_count} total schemes.")
+ end
+
+ it "shows which schemes are being shown on the current page" do
+ expect(CGI.unescape_html(response.body)).to match("Showing 1 to 20 of #{total_schemes_count} schemes")
+ end
+
+ 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")
+ 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 "/supported-housing?page=2"
+ end
+
+ it "shows the total schemes count" do
+ expect(CGI.unescape_html(response.body)).to match("#{total_schemes_count} total schemes.")
+ 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
+
+ it "shows which schemes are being shown on the current page" do
+ expect(CGI.unescape_html(response.body)).to match("Showing 21 to 25 of #{total_schemes_count} schemes")
+ end
+
+ 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")
+ end
+ end
+ end
+
+ context "when searching" do
+ let!(:searched_scheme) { FactoryBot.create(:scheme, code: "CODE321") }
+ let(:search_param) { "CODE321" }
+
+ before do
+ get "/supported-housing?search=#{search_param}"
+ end
+
+ it "returns matching results" do
+ expect(page).to have_content(searched_scheme.code)
+ schemes.each do |scheme|
+ expect(page).not_to have_content(scheme.code)
+ end
+ end
+
+ it "updates the table caption" do
+ expect(page).to have_content("1 scheme found matching ‘#{search_param}’")
+ end
+
+ 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")
+ end
+ end
+ end
+ end
+end