Browse Source

Implement search for logs feature (#608)

pull/619/head
JG 3 years ago committed by baarkerlounger
parent
commit
112c4974a7
  1. 1
      Gemfile
  2. 3
      Gemfile.lock
  3. 4
      app/components/search_component.rb
  4. 17
      app/controllers/case_logs_controller.rb
  5. 12
      app/controllers/organisations_controller.rb
  6. 2
      app/frontend/styles/_table-group.scss
  7. 11
      app/models/case_log.rb
  8. 6
      app/views/case_logs/_log_list.html.erb
  9. 29
      app/views/case_logs/index.html.erb
  10. 60
      app/views/organisations/_organisation_list.html.erb
  11. 29
      app/views/organisations/logs.html.erb
  12. 94
      app/views/users/_user_list.html.erb
  13. 1
      config/environments/test.rb
  14. 18
      spec/factories/case_log.rb
  15. 2
      spec/features/form/saving_data_spec.rb
  16. 59
      spec/features/log_spec.rb
  17. 47
      spec/features/organisation_spec.rb
  18. 96
      spec/models/case_log_spec.rb
  19. 123
      spec/requests/case_logs_controller_spec.rb
  20. 108
      spec/requests/organisations_controller_spec.rb
  21. 4
      spec/services/exports/case_log_export_service_spec.rb

1
Gemfile

@ -87,6 +87,7 @@ group :test do
gem "capybara", require: false gem "capybara", require: false
gem "capybara-lockstep" gem "capybara-lockstep"
gem "factory_bot_rails" gem "factory_bot_rails"
gem "faker"
gem "rspec-rails", require: false gem "rspec-rails", require: false
gem "selenium-webdriver", require: false gem "selenium-webdriver", require: false
gem "simplecov", require: false gem "simplecov", require: false

3
Gemfile.lock

@ -169,6 +169,8 @@ GEM
factory_bot_rails (6.2.0) factory_bot_rails (6.2.0)
factory_bot (~> 6.2.0) factory_bot (~> 6.2.0)
railties (>= 5.0.0) railties (>= 5.0.0)
faker (2.21.0)
i18n (>= 1.8.11, < 2)
ffi (1.15.5) ffi (1.15.5)
globalid (1.0.0) globalid (1.0.0)
activesupport (>= 5.0) activesupport (>= 5.0)
@ -437,6 +439,7 @@ DEPENDENCIES
dotenv-rails dotenv-rails
erb_lint erb_lint
factory_bot_rails factory_bot_rails
faker
govuk-components govuk-components
govuk_design_system_formbuilder govuk_design_system_formbuilder
govuk_markdown govuk_markdown

4
app/components/search_component.rb

@ -11,8 +11,12 @@ class SearchComponent < ViewComponent::Base
def path(current_user) def path(current_user)
if request.path.include?("users") if request.path.include?("users")
user_path(current_user) user_path(current_user)
elsif request.path.include?("organisations") && request.path.include?("logs")
request.path
elsif request.path.include?("organisations") elsif request.path.include?("organisations")
organisations_path organisations_path
elsif request.path.include?("logs")
case_logs_path
end end
end end

17
app/controllers/case_logs_controller.rb

@ -1,6 +1,7 @@
class CaseLogsController < ApplicationController class CaseLogsController < ApplicationController
include Pagy::Backend include Pagy::Backend
include Modules::CaseLogsFilter include Modules::CaseLogsFilter
include Modules::SearchFilter
skip_before_action :verify_authenticity_token, if: :json_api_request? skip_before_action :verify_authenticity_token, if: :json_api_request?
before_action :authenticate, if: :json_api_request? before_action :authenticate, if: :json_api_request?
@ -10,7 +11,17 @@ class CaseLogsController < ApplicationController
def index def index
set_session_filters set_session_filters
@pagy, @case_logs = pagy(filtered_case_logs(current_user.case_logs)) all_logs = current_user.case_logs
@pagy, @case_logs = pagy(
filtered_case_logs(
filtered_collection(
all_logs, search_term
),
),
)
@searched = search_term.presence
@total_count = all_logs.size
respond_to do |format| respond_to do |format|
format.html format.html
@ -85,6 +96,10 @@ private
API_ACTIONS = %w[create show update destroy].freeze API_ACTIONS = %w[create show update destroy].freeze
def search_term
params["search"]
end
def json_api_request? def json_api_request?
API_ACTIONS.include?(request["action"]) && request.format.json? API_ACTIONS.include?(request["action"]) && request.format.json?
end end

12
app/controllers/organisations_controller.rb

@ -55,7 +55,17 @@ class OrganisationsController < ApplicationController
set_session_filters(specific_org: true) set_session_filters(specific_org: true)
organisation_logs = CaseLog.all.where(owning_organisation_id: @organisation.id) organisation_logs = CaseLog.all.where(owning_organisation_id: @organisation.id)
@pagy, @case_logs = pagy(filtered_case_logs(organisation_logs))
@pagy, @case_logs = pagy(
filtered_case_logs(
filtered_collection(
organisation_logs, search_term
),
),
)
@searched = search_term.presence
@total_count = organisation_logs.size
render "logs", layout: "application" render "logs", layout: "application"
else else
redirect_to(case_logs_path) redirect_to(case_logs_path)

2
app/frontend/styles/_table-group.scss

@ -2,7 +2,7 @@
overflow-x: auto; overflow-x: auto;
overflow-y: hidden; overflow-y: hidden;
margin: govuk-spacing(-3) govuk-spacing(-3) govuk-spacing(3); margin: govuk-spacing(-3) govuk-spacing(-3) govuk-spacing(3);
padding: 0 govuk-spacing(3); padding: govuk-spacing(3) govuk-spacing(3);
scrollbar-color: $govuk-text-colour govuk-colour("light-grey"); scrollbar-color: $govuk-text-colour govuk-colour("light-grey");
.govuk-table { .govuk-table {

11
app/models/case_log.rb

@ -51,6 +51,17 @@ class CaseLog < ApplicationRecord
end end
} }
scope :filter_by_id, ->(id) { where(id:) }
scope :filter_by_tenant_code, ->(code) { where("lower(tenant_code) = ?", code.downcase) }
scope :filter_by_propcode, ->(code) { where("lower(propcode) = ?", code.downcase) }
scope :filter_by_postcode, ->(code) { where(postcode_full: code.upcase.gsub(/\s+/, "")) }
scope :search_by, lambda { |param|
filter_by_id(param)
.or(filter_by_tenant_code(param))
.or(filter_by_propcode(param))
.or(filter_by_postcode(param))
}
AUTOGENERATED_FIELDS = %w[id status created_at updated_at discarded_at].freeze AUTOGENERATED_FIELDS = %w[id status created_at updated_at discarded_at].freeze
OPTIONAL_FIELDS = %w[first_time_property_let_as_social_housing tenant_code propcode].freeze OPTIONAL_FIELDS = %w[first_time_property_let_as_social_housing tenant_code propcode].freeze
RENT_TYPE_MAPPING = { 0 => 1, 1 => 2, 2 => 2, 3 => 3, 4 => 3, 5 => 3 }.freeze RENT_TYPE_MAPPING = { 0 => 1, 1 => 2, 2 => 2, 3 => 3, 4 => 3, 5 => 3 }.freeze

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

@ -1,8 +1,12 @@
<section class="app-table-group" tabindex="0" aria-labelledby="<%= title.dasherize %>"> <section class="app-table-group" tabindex="0" aria-labelledby="<%= title.dasherize %>">
<%= govuk_table do |table| %> <%= govuk_table do |table| %>
<%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular], id: title.dasherize) do |caption| %> <%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular], id: title.dasherize) do |caption| %>
<span class="govuk-!-margin-right-4"> <span class="govuk-!-margin-right-4">
<% if defined?(searched) && searched.present? %>
<strong><%= pagy.count %></strong> <%= item_label %> found matching ‘<%= searched %>’ of <strong><%= total_count %></strong> total <%= title.downcase %>. <%= govuk_link_to("Clear search", request.path) %>
<% else %>
<strong><%= pagy.count %></strong> total <%= title.downcase %> <strong><%= pagy.count %></strong> total <%= title.downcase %>
<% end %>
</span> </span>
<%= govuk_link_to "Download (CSV)", "/logs.csv", type: "text/csv" %> <%= govuk_link_to "Download (CSV)", "/logs.csv", type: "text/csv" %>
<% end %> <% end %>

29
app/views/case_logs/index.html.erb

@ -1,8 +1,17 @@
<% content_for :title, "Logs" %> <% item_label = @pagy.count > 1 ? "logs" : "log" %>
<% if @searched.present? %>
<% title = "Logs (search results for ‘#{@searched}’#{@pagy.last > 1 ? ", page #{@pagy.page} of #{@pagy.last}" : ''}) - Submit social housing and sales data (CORE) - GOV.UK" %>
<% else %>
<% title = "Logs #{@pagy.last > 1 ? "(page #{@pagy.page} of #{@pagy.last}) " : ''}- Submit social housing and sales data (CORE) - GOV.UK" %>
<% end %>
<% content_for :title, title %>
<% content_for :tab_title do %>
<%= "Logs" %>
<% end %>
<h1 class="govuk-heading-l">
<%= content_for(:title) %>
</h1>
<div class="app-filter-layout" data-controller="filter-layout"> <div class="app-filter-layout" data-controller="filter-layout">
<div class="govuk-button-group app-filter-toggle"> <div class="govuk-button-group app-filter-toggle">
<%= govuk_button_to "Create a new lettings log", case_logs_path %> <%= govuk_button_to "Create a new lettings log", case_logs_path %>
@ -10,10 +19,10 @@
</div> </div>
<%= render partial: "log_filters" %> <%= render partial: "log_filters" %>
<% if @case_logs.present? %> <div class="app-filter-layout__content">
<div class="app-filter-layout__content"> <%= render SearchComponent.new(current_user:, search_label: "Search by log ID, tenant code, property reference or postcode", value: @searched) %>
<%= render partial: "log_list", locals: { case_logs: @case_logs, title: "Logs", pagy: @pagy } %> <hr class="govuk-section-break govuk-section-break--visible govuk-section-break--m">
<%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "logs" } %> <%= render partial: "log_list", locals: { case_logs: @case_logs, title: "Logs", pagy: @pagy, searched: @searched, item_label:, total_count: @total_count } %>
</div> <%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "logs" } %>
<% end %> </div>
</div> </div>

60
app/views/organisations/_organisation_list.html.erb

@ -1,35 +1,37 @@
<%= govuk_table do |table| %> <section class="app-table-group" tabindex="0" aria-labelledby="<%= title.dasherize %>">
<%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %> <%= govuk_table do |table| %>
<% if searched.present? %> <%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %>
<strong><%= pagy.count %></strong> <%= item_label %> found matching ‘<%= searched %>’ of <strong><%= total_count %></strong> total organisations. <%= govuk_link_to("Clear search", request.path) %> <% if searched.present? %>
<% else %> <strong><%= pagy.count %></strong> <%= item_label %> found matching ‘<%= searched %>’ of <strong><%= total_count %></strong> total organisations. <%= govuk_link_to("Clear search", request.path) %>
<strong><%= pagy.count %></strong> total organisations. <% else %>
<strong><%= pagy.count %></strong> total organisations.
<% end %>
<% end %> <% end %>
<% end %> <%= table.head do |head| %>
<%= table.head do |head| %> <%= head.row do |row| %>
<%= head.row do |row| %> <% row.cell(header: true, text: "Name", html_attributes: {
<% row.cell(header: true, text: "Name", html_attributes: { scope: "col",
scope: "col", }) %>
}) %> <% row.cell(header: true, text: "Registration number", html_attributes: {
<% row.cell(header: true, text: "Registration number", html_attributes: { scope: "col",
scope: "col", }) %>
}) %> <% row.cell(header: true, text: "Type", html_attributes: {
<% row.cell(header: true, text: "Type", html_attributes: { scope: "col",
scope: "col", }) %>
}) %> <% end %>
<% end %> <% end %>
<% end %> <% @organisations.each do |organisation| %>
<% @organisations.each do |organisation| %> <%= table.body do |body| %>
<%= table.body do |body| %> <%= body.row do |row| %>
<%= body.row do |row| %> <% row.cell(header: true, html_attributes: {
<% row.cell(header: true, html_attributes: { scope: "row",
scope: "row", }) do %>
}) do %> <%= govuk_link_to(organisation.name, "organisations/#{organisation.id}/logs") %>
<%= govuk_link_to(organisation.name, "organisations/#{organisation.id}/logs") %> <% end %>
<% row.cell(text: organisation.housing_registration_no) %>
<% row.cell(text: organisation.display_provider_type) %>
<% end %> <% end %>
<% row.cell(text: organisation.housing_registration_no) %>
<% row.cell(text: organisation.display_provider_type) %>
<% end %> <% end %>
<% end %> <% end %>
<% end %> <% end %>
<% end %> </section>

29
app/views/organisations/logs.html.erb

@ -1,9 +1,16 @@
<% content_for :title, "Logs" %> <% item_label = @pagy.count > 1 ? "logs" : "log" %>
<h1 class="govuk-heading-l"> <% if @searched.present? %>
<span class="govuk-caption-l"><%= @organisation.name %></span> <% title = "Logs (search results for ‘#{@searched}’#{@pagy.last > 1 ? ", page #{@pagy.page} of #{@pagy.last}" : ''}) - Submit social housing and sales data (CORE) - GOV.UK" %>
<%= content_for(:title) %> <% else %>
</h1> <% title = "Logs #{@pagy.last > 1 ? "(page #{@pagy.page} of #{@pagy.last}) " : ''}- Submit social housing and sales data (CORE) - GOV.UK" %>
<% end %>
<% content_for :title, title %>
<% content_for :tab_title do %>
<%= "Logs" %>
<% end %>
<%= render SubNavigationComponent.new( <%= render SubNavigationComponent.new(
items: secondary_items(request.path, @organisation.id), items: secondary_items(request.path, @organisation.id),
@ -11,10 +18,10 @@
<div class="app-filter-layout" data-controller="filter-layout"> <div class="app-filter-layout" data-controller="filter-layout">
<%= render partial: "case_logs/log_filters" %> <%= render partial: "case_logs/log_filters" %>
<% if @case_logs.present? %> <div class="app-filter-layout__content">
<div class="app-filter-layout__content"> <%= render SearchComponent.new(current_user:, search_label: "Search by log ID, tenant code, property reference or postcode", value: @searched) %>
<%= render partial: "case_logs/log_list", locals: { case_logs: @case_logs, title: "Logs", pagy: @pagy } %> <hr class="govuk-section-break govuk-section-break--visible govuk-section-break--m">
<%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "logs" } %> <%= render partial: "case_logs/log_list", locals: { case_logs: @case_logs, title: "Logs", pagy: @pagy, searched: @searched, item_label:, total_count: @total_count } %>
</div> <%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "logs" } %>
<% end %> </div>
</div> </div>

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

@ -1,54 +1,56 @@
<%= govuk_table do |table| %> <section class="app-table-group" tabindex="0" aria-labelledby="<%= title.dasherize %>">
<%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %> <%= govuk_table do |table| %>
<span class="govuk-!-margin-right-4"> <%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %>
<% if searched.present? %> <span class="govuk-!-margin-right-4">
<strong><%= pagy.count %></strong> <%= item_label %> found matching ‘<%= searched %>’ of <strong><%= total_count %></strong> total users. <%= govuk_link_to("Clear search", request.path) %> <% if searched.present? %>
<% else %> <strong><%= pagy.count %></strong> <%= item_label %> found matching ‘<%= searched %>’ of <strong><%= total_count %></strong> total users. <%= govuk_link_to("Clear search", request.path) %>
<strong><%= pagy.count %></strong> total users. <% else %>
<strong><%= pagy.count %></strong> total users.
<% end %>
</span>
<% if current_user.support? %>
<% query = searched.present? ? "?search=#{searched}" : nil %>
<%= govuk_link_to "Download (CSV)", "/users.csv#{query}", type: "text/csv" %>
<% end %> <% end %>
</span>
<% if current_user.support? %>
<% query = searched.present? ? "?search=#{searched}" : nil %>
<%= govuk_link_to "Download (CSV)", "/users.csv#{query}", type: "text/csv" %>
<% end %> <% end %>
<% end %> <%= table.head do |head| %>
<%= table.head do |head| %> <%= head.row do |row| %>
<%= head.row do |row| %> <% row.cell(header: true, text: "Name and email adress", html_attributes: {
<% row.cell(header: true, text: "Name and email adress", html_attributes: { scope: "col",
scope: "col", }) %>
}) %> <% row.cell(header: true, text: "Organisation and role", html_attributes: {
<% row.cell(header: true, text: "Organisation and role", html_attributes: { scope: "col",
scope: "col", }) %>
}) %> <% row.cell(header: true, text: "Last logged in", html_attributes: {
<% row.cell(header: true, text: "Last logged in", html_attributes: { scope: "col",
scope: "col", }) %>
}) %> <% end %>
<% end %> <% end %>
<% end %> <% users.each do |user| %>
<% users.each do |user| %> <%= table.body do |body| %>
<%= table.body do |body| %> <%= body.row do |row| %>
<%= body.row do |row| %> <% row.cell(header: true, html_attributes: {
<% row.cell(header: true, html_attributes: { scope: "row",
scope: "row", }) do %>
}) do %> <%= simple_format(user_cell(user), {}, wrapper_tag: "span") %>
<%= simple_format(user_cell(user), {}, wrapper_tag: "span") %> <% if user.is_data_protection_officer? || user.is_key_contact? %>
<% if user.is_data_protection_officer? || user.is_key_contact? %> <br>
<br> <% end %>
<%= user.is_data_protection_officer? ? govuk_tag(
classes: "app-tag--small",
colour: "turquoise",
text: "Data protection officer",
) : "" %>
<%= user.is_key_contact? ? govuk_tag(
classes: "app-tag--small",
colour: "turquoise",
text: "Key contact",
) : "" %>
<% end %> <% end %>
<%= user.is_data_protection_officer? ? govuk_tag( <% row.cell(text: simple_format(org_cell(user), {}, wrapper_tag: "div")) %>
classes: "app-tag--small", <% row.cell(text: user.last_sign_in_at&.to_formatted_s(:govuk_date)) %>
colour: "turquoise",
text: "Data protection officer",
) : "" %>
<%= user.is_key_contact? ? govuk_tag(
classes: "app-tag--small",
colour: "turquoise",
text: "Key contact",
) : "" %>
<% end %> <% end %>
<% row.cell(text: simple_format(org_cell(user), {}, wrapper_tag: "div")) %>
<% row.cell(text: user.last_sign_in_at&.to_formatted_s(:govuk_date)) %>
<% end %> <% end %>
<% end %> <% end %>
<% end %> <% end %>
<% end %> </section>

1
config/environments/test.rb

@ -60,4 +60,5 @@ Rails.application.configure do
# Annotate rendered view with file names. # Annotate rendered view with file names.
# config.action_view.annotate_rendered_view_with_filenames = true # config.action_view.annotate_rendered_view_with_filenames = true
Faker::Config.locale = "en-GB"
end end

18
spec/factories/case_log.rb

@ -11,9 +11,9 @@ FactoryBot.define do
end end
trait :in_progress do trait :in_progress do
status { 1 } status { 1 }
tenant_code { "TH356" } tenant_code { Faker::Name.initials(number: 10) }
postcode_full { "PO5 3TE" } postcode_full { Faker::Address.postcode }
ppostcode_full { "SW2 6HI" } ppostcode_full { Faker::Address.postcode }
age1 { 17 } age1 { 17 }
age2 { 19 } age2 { 19 }
end end
@ -24,7 +24,7 @@ FactoryBot.define do
incfreq { 1 } incfreq { 1 }
end end
trait :conditional_section_complete do trait :conditional_section_complete do
tenant_code { "TH356" } tenant_code { Faker::Name.initials(number: 10) }
age1 { 34 } age1 { 34 }
sex1 { "M" } sex1 { "M" }
ethnic { 2 } ethnic { 2 }
@ -34,7 +34,7 @@ FactoryBot.define do
end end
trait :completed do trait :completed do
status { 2 } status { 2 }
tenant_code { "BZ737" } tenant_code { Faker::Name.initials(number: 10) }
age1 { 35 } age1 { 35 }
sex1 { "F" } sex1 { "F" }
ethnic { 2 } ethnic { 2 }
@ -52,11 +52,11 @@ FactoryBot.define do
reservist { 0 } reservist { 0 }
illness { 1 } illness { 1 }
preg_occ { 2 } preg_occ { 2 }
tenancy_code { "BZ757" } tenancy_code { Faker::Name.initials(number: 10) }
startertenancy { 0 } startertenancy { 0 }
tenancylength { 5 } tenancylength { 5 }
tenancy { 1 } tenancy { 1 }
ppostcode_full { "SE2 6RT" } ppostcode_full { Faker::Address.postcode }
rsnvac { 6 } rsnvac { 6 }
unittype_gn { 7 } unittype_gn { 7 }
beds { 3 } beds { 3 }
@ -74,7 +74,7 @@ FactoryBot.define do
tcharge { 325 } tcharge { 325 }
layear { 2 } layear { 2 }
waityear { 1 } waityear { 1 }
postcode_full { "NW1 5TY" } postcode_full { Faker::Address.postcode }
reasonpref { 1 } reasonpref { 1 }
cbl { 1 } cbl { 1 }
chr { 1 } chr { 1 }
@ -112,7 +112,7 @@ FactoryBot.define do
needstype { 1 } needstype { 1 }
purchaser_code { 798_794 } purchaser_code { 798_794 }
reason { 4 } reason { 4 }
propcode { "123" } propcode { Faker::Name.initials(number: 10) }
majorrepairs { 1 } majorrepairs { 1 }
la { "E09000003" } la { "E09000003" }
prevloc { "E07000105" } prevloc { "E07000105" }

2
spec/features/form/saving_data_spec.rb

@ -73,7 +73,7 @@ RSpec.describe "Form Saving Data" do
it "displays number answers in inputs if they are already saved" do it "displays number answers in inputs if they are already saved" do
visit("/logs/#{id}/property-postcode") visit("/logs/#{id}/property-postcode")
expect(page).to have_field("case-log-postcode-full-field", with: "PO53TE") expect(page).to have_field("case-log-postcode-full-field", with: case_log.postcode_full)
end end
it "displays text answers in inputs if they are already saved" do it "displays text answers in inputs if they are already saved" do

59
spec/features/log_spec.rb

@ -0,0 +1,59 @@
require "rails_helper"
RSpec.describe "Log Features" do
context "when searching for specific logs" do
context "when I am logged in and there are logs in the database" do
let(:user) { FactoryBot.create(:user, last_sign_in_at: Time.zone.now) }
let!(:log_to_search) { FactoryBot.create(:case_log, owning_organisation: user.organisation) }
let!(:same_organisation_log) { FactoryBot.create(:case_log, owning_organisation: user.organisation) }
let!(:another_organisation_log) { FactoryBot.create(:case_log) }
before do
visit("/logs")
fill_in("user[email]", with: user.email)
fill_in("user[password]", with: user.password)
click_button("Sign in")
end
it "displays the logs belonging to the same organisation" do
expect(page).to have_content(log_to_search.id)
expect(page).to have_content(same_organisation_log.id)
expect(page).not_to have_content(another_organisation_log.id)
end
context "when I search for a specific log" do
it "there is a search bar with a message and search button for logs" do
expect(page).to have_field("search")
expect(page).to have_content("Search by log ID, tenant code, property reference or postcode")
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: log_to_search.id)
click_button("Search")
end
it "displays log matching the log ID" do
expect(page).to have_content(log_to_search.id)
expect(page).not_to have_content(same_organisation_log.id)
expect(page).not_to have_content(another_organisation_log.id)
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 the logs belonging to the same organisation after I clear the search result after I clear the search resultss" do
click_link("Clear search")
expect(page).to have_content(log_to_search.id)
expect(page).to have_content(same_organisation_log.id)
expect(page).not_to have_content(another_organisation_log.id)
end
end
end
end
end
end
end

47
spec/features/organisation_spec.rb

@ -84,12 +84,13 @@ RSpec.describe "User Features" do
context "when user is support user" do context "when user is support user" do
context "when viewing logs for specific organisation" do context "when viewing logs for specific organisation" do
let(:user) { FactoryBot.create(:user, :support) } let(:user) { FactoryBot.create(:user, :support) }
let(:number_of_case_logs) { 4 }
let(:first_log) { organisation.case_logs.first } let(:first_log) { organisation.case_logs.first }
let(:otp) { "999111" } let(:otp) { "999111" }
let!(:log_to_search) { FactoryBot.create(:case_log, owning_organisation: user.organisation, managing_organisation_id: organisation.id) }
let!(:other_logs) { FactoryBot.create_list(:case_log, 4, owning_organisation_id: organisation.id, managing_organisation_id: organisation.id) }
let(:number_of_case_logs) { CaseLog.count }
before do before do
FactoryBot.create_list(:case_log, number_of_case_logs, owning_organisation_id: organisation.id, managing_organisation_id: organisation.id)
first_log.update!(startdate: Time.utc(2022, 6, 2, 10, 36, 49)) first_log.update!(startdate: Time.utc(2022, 6, 2, 10, 36, 49))
allow(SecureRandom).to receive(:random_number).and_return(otp) allow(SecureRandom).to receive(:random_number).and_return(otp)
click_link("Sign out") click_link("Sign out")
@ -99,6 +100,48 @@ RSpec.describe "User Features" do
visit("/organisations/#{org_id}/logs") visit("/organisations/#{org_id}/logs")
end end
context "when searching for specific logs" do
it "displays the logs belonging to the same organisation" do
expect(page).to have_content(log_to_search.id)
other_logs.each do |log|
expect(page).to have_content(log.id)
end
end
context "when I search for a specific log" do
it "there is a search bar with a message and search button for logs" do
expect(page).to have_field("search")
expect(page).to have_content("Search by log ID, tenant code, property reference or postcode")
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: log_to_search.id)
click_button("Search")
end
it "displays log matching the log ID" do
expect(page).to have_content(log_to_search.id)
other_logs.each do |log|
expect(page).not_to have_content(log.id)
end
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 the logs belonging to the same organisation after I clear the search result after I clear the search resultss" do
click_link("Clear search")
expect(page).to have_content(log_to_search.id)
end
end
end
end
end
it "can filter case logs" do it "can filter case logs" do
expect(page).to have_content("#{number_of_case_logs} total logs") expect(page).to have_content("#{number_of_case_logs} total logs")
organisation.case_logs.map(&:id).each do |case_log_id| organisation.case_logs.map(&:id).each do |case_log_id|

96
spec/models/case_log_spec.rb

@ -1871,6 +1871,102 @@ RSpec.describe CaseLog do
FactoryBot.create(:case_log, startdate: Time.utc(2022, 6, 3)) FactoryBot.create(:case_log, startdate: Time.utc(2022, 6, 3))
end end
context "when searching logs" do
let!(:case_log_to_search) { FactoryBot.create(:case_log, :completed) }
before do
FactoryBot.create_list(:case_log, 5, :completed)
end
describe "#filter_by_id" do
it "allows searching by a log ID" do
result = described_class.filter_by_id(case_log_to_search.id.to_s)
expect(result.count).to eq(1)
expect(result.first.id).to eq case_log_to_search.id
end
end
describe "#filter_by_tenant_code" do
it "allows searching by a Tenant Code" do
result = described_class.filter_by_tenant_code(case_log_to_search.tenant_code)
expect(result.count).to eq(1)
expect(result.first.id).to eq case_log_to_search.id
end
context "when tenant_code has lower case letters" do
let(:matching_tenant_code_lower_case) { case_log_to_search.tenant_code.downcase }
it "allows searching by a Tenant Code" do
result = described_class.filter_by_tenant_code(matching_tenant_code_lower_case)
expect(result.count).to eq(1)
expect(result.first.id).to eq case_log_to_search.id
end
end
end
describe "#filter_by_propcode" do
it "allows searching by a Property Reference" do
result = described_class.filter_by_propcode(case_log_to_search.propcode)
expect(result.count).to eq(1)
expect(result.first.id).to eq case_log_to_search.id
end
context "when propcode has lower case letters" do
let(:matching_propcode_lower_case) { case_log_to_search.propcode.downcase }
it "allows searching by a Property Reference" do
result = described_class.filter_by_propcode(matching_propcode_lower_case)
expect(result.count).to eq(1)
expect(result.first.id).to eq case_log_to_search.id
end
end
end
describe "#filter_by_postcode" do
it "allows searching by a Property Postcode" do
result = described_class.filter_by_postcode(case_log_to_search.postcode_full)
expect(result.count).to eq(1)
expect(result.first.id).to eq case_log_to_search.id
end
end
describe "#search_by" do
it "allows searching using ID" do
result = described_class.search_by(case_log_to_search.id.to_s)
expect(result.count).to eq(1)
expect(result.first.id).to eq case_log_to_search.id
end
it "allows searching using tenancy code" do
result = described_class.search_by(case_log_to_search.tenant_code)
expect(result.count).to eq(1)
expect(result.first.id).to eq case_log_to_search.id
end
it "allows searching by a Property Reference" do
result = described_class.search_by(case_log_to_search.propcode)
expect(result.count).to eq(1)
expect(result.first.id).to eq case_log_to_search.id
end
it "allows searching by a Property Postcode" do
result = described_class.search_by(case_log_to_search.postcode_full)
expect(result.count).to eq(1)
expect(result.first.id).to eq case_log_to_search.id
end
context "when postcode has spaces and lower case letters" do
let(:matching_postcode_lower_case_with_spaces) { case_log_to_search.postcode_full.downcase.chars.insert(3, " ").join }
it "allows searching by a Property Postcode" do
result = described_class.search_by(matching_postcode_lower_case_with_spaces)
expect(result.count).to eq(1)
expect(result.first.id).to eq case_log_to_search.id
end
end
end
end
context "when filtering by year" do context "when filtering by year" do
it "allows filtering on a single year" do it "allows filtering on a single year" do
expect(described_class.filter_by_years(%w[2021]).count).to eq(2) expect(described_class.filter_by_years(%w[2021]).count).to eq(2)

123
spec/requests/case_logs_controller_spec.rb

@ -8,7 +8,7 @@ RSpec.describe CaseLogsController, type: :request do
let(:api_password) { "test_password" } let(:api_password) { "test_password" }
let(:basic_credentials) do let(:basic_credentials) do
ActionController::HttpAuthentication::Basic ActionController::HttpAuthentication::Basic
.encode_credentials(api_username, api_password) .encode_credentials(api_username, api_password)
end end
let(:headers) do let(:headers) do
@ -184,6 +184,17 @@ RSpec.describe CaseLogsController, type: :request do
expect(page).to have_content("UA984") expect(page).to have_content("UA984")
end end
context "when there are no logs in the database" do
before do
CaseLog.destroy_all
end
it "page has correct title" do
get "/logs", headers: headers, params: {}
expect(page).to have_title("Logs - Submit social housing and sales data (CORE) - GOV.UK")
end
end
context "when filtering" do context "when filtering" do
context "with status filter" do context "with status filter" do
let(:organisation_2) { FactoryBot.create(:organisation) } let(:organisation_2) { FactoryBot.create(:organisation) }
@ -308,6 +319,110 @@ RSpec.describe CaseLogsController, type: :request do
expect(page).not_to have_content("Managing organisation") expect(page).not_to have_content("Managing organisation")
end end
context "when using a search query" do
let(:logs) { FactoryBot.create_list(:case_log, 3, :completed, owning_organisation: user.organisation) }
let(:log_to_search) { FactoryBot.create(:case_log, :completed, owning_organisation: user.organisation) }
it "has search results in the title" do
get "/logs?search=#{log_to_search.id}", headers: headers, params: {}
expect(page).to have_content("Logs (search results for ‘#{log_to_search.id}’) - Submit social housing and sales data (CORE) - GOV.UK")
end
it "shows case logs matching the id" do
get "/logs?search=#{log_to_search.id}", headers: headers, params: {}
expect(page).to have_content(log_to_search.id)
logs.each do |log|
expect(page).not_to have_content(log.id)
end
end
it "shows case logs matching the tenant code" do
get "/logs?search=#{log_to_search.tenant_code}", headers: headers, params: {}
expect(page).to have_content(log_to_search.id)
logs.each do |log|
expect(page).not_to have_content(log.id)
end
end
it "shows case logs matching the property reference" do
get "/logs?search=#{log_to_search.propcode}", headers: headers, params: {}
expect(page).to have_content(log_to_search.id)
logs.each do |log|
expect(page).not_to have_content(log.id)
end
end
it "shows case logs matching the property postcode" do
get "/logs?search=#{log_to_search.postcode_full}", headers: headers, params: {}
expect(page).to have_content(log_to_search.id)
logs.each do |log|
expect(page).not_to have_content(log.id)
end
end
context "when more than one results with matching postcode" do
let!(:matching_postcode_log) { FactoryBot.create(:case_log, :completed, owning_organisation: user.organisation, postcode_full: log_to_search.postcode_full) }
it "displays all matching logs" do
get "/logs?search=#{log_to_search.postcode_full}", headers: headers, params: {}
expect(page).to have_content(log_to_search.id)
expect(page).to have_content(matching_postcode_log.id)
logs.each do |log|
expect(page).not_to have_content(log.id)
end
end
end
context "when there are more than 1 page of search results" do
let(:logs) { FactoryBot.create_list(:case_log, 30, :completed, owning_organisation: user.organisation, postcode_full: "XX1 1YY") }
it "has title with pagination details for page 1" do
get "/logs?search=#{logs[0].postcode_full}", headers: headers, params: {}
expect(page).to have_content("Logs (search results for ‘#{logs[0].postcode_full}’, page 1 of 2) - Submit social housing and sales data (CORE) - GOV.UK")
end
it "has title with pagination details for page 2" do
get "/logs?search=#{logs[0].postcode_full}&page=2", headers: headers, params: {}
expect(page).to have_content("Logs (search results for ‘#{logs[0].postcode_full}’, page 2 of 2) - Submit social housing and sales data (CORE) - GOV.UK")
end
end
context "when search query doesn't match any logs" do
it "doesn't display any logs" do
get "/logs?search=foobar", headers:, params: {}
logs.each do |log|
expect(page).not_to have_content(log.id)
end
expect(page).not_to have_content(log_to_search.id)
end
end
context "when search query is empty" do
it "doesn't display any logs" do
get "/logs?search=", headers:, params: {}
logs.each do |log|
expect(page).not_to have_content(log.id)
end
expect(page).not_to have_content(log_to_search.id)
end
end
context "when search and filter is present" do
let(:matching_postcode) { log_to_search.postcode_full }
let(:matching_status) { "in_progress" }
let!(:log_matching_filter_and_search) { FactoryBot.create(:case_log, :in_progress, owning_organisation: user.organisation, postcode_full: matching_postcode) }
it "shows only logs matching both search and filters" do
get "/logs?search=#{matching_postcode}&status[]=#{matching_status}", headers: headers, params: {}
expect(page).to have_content(log_matching_filter_and_search.id)
expect(page).not_to have_content(log_to_search.id)
logs.each do |log|
expect(page).not_to have_content(log.id)
end
end
end
end
context "when there are less than 20 logs" do context "when there are less than 20 logs" do
before do before do
get "/logs", headers:, params: {} get "/logs", headers:, params: {}
@ -348,7 +463,7 @@ RSpec.describe CaseLogsController, type: :request do
end end
it "does not have pagination in the title" do it "does not have pagination in the title" do
expect(page).to have_title("Logs") expect(page).to have_title("Logs - Submit social housing and sales data (CORE) - GOV.UK")
end end
it "shows the download csv link" do it "shows the download csv link" do
@ -424,7 +539,7 @@ RSpec.describe CaseLogsController, type: :request do
end end
it "has pagination in the title" do it "has pagination in the title" do
expect(page).to have_title("Logs (page 1 of 2)") expect(page).to have_title("Logs (page 1 of 2) - Submit social housing and sales data (CORE) - GOV.UK")
end end
end end
@ -449,7 +564,7 @@ RSpec.describe CaseLogsController, type: :request do
end end
it "has pagination in the title" do it "has pagination in the title" do
expect(page).to have_title("Logs (page 2 of 2)") expect(page).to have_title("Logs (page 2 of 2) - Submit social housing and sales data (CORE) - GOV.UK")
end end
end end
end end

108
spec/requests/organisations_controller_spec.rb

@ -379,10 +379,6 @@ RSpec.describe OrganisationsController, type: :request do
get "/organisations/#{organisation.id}/logs", headers:, params: {} get "/organisations/#{organisation.id}/logs", headers:, params: {}
end end
it "displays the name of the organisation in the header" do
expect(CGI.unescape_html(response.body)).to match("<span class=\"govuk-caption-l\">#{organisation.name}</span>")
end
it "only shows logs for that organisation" do it "only shows logs for that organisation" do
expect(page).to have_content("#{number_of_org1_case_logs} total logs") expect(page).to have_content("#{number_of_org1_case_logs} total logs")
organisation.case_logs.map(&:id).each do |case_log_id| organisation.case_logs.map(&:id).each do |case_log_id|
@ -407,6 +403,110 @@ RSpec.describe OrganisationsController, type: :request do
expect(page).to have_css(".app-sub-navigation") expect(page).to have_css(".app-sub-navigation")
expect(page).to have_content("About this organisation") expect(page).to have_content("About this organisation")
end end
context "when using a search query" do
let(:logs) { FactoryBot.create_list(:case_log, 3, :completed, owning_organisation: user.organisation) }
let(:log_to_search) { FactoryBot.create(:case_log, :completed, owning_organisation: user.organisation) }
it "has search results in the title" do
get "/organisations/#{organisation.id}/logs?search=#{log_to_search.id}", headers: headers, params: {}
expect(page).to have_content("Logs (search results for ‘#{log_to_search.id}’) - Submit social housing and sales data (CORE) - GOV.UK")
end
it "shows case logs matching the id" do
get "/organisations/#{organisation.id}/logs?search=#{log_to_search.id}", headers: headers, params: {}
expect(page).to have_content(log_to_search.id)
logs.each do |log|
expect(page).not_to have_content(log.id)
end
end
it "shows case logs matching the tenant code" do
get "/organisations/#{organisation.id}/logs?search=#{log_to_search.tenant_code}", headers: headers, params: {}
expect(page).to have_content(log_to_search.id)
logs.each do |log|
expect(page).not_to have_content(log.id)
end
end
it "shows case logs matching the property reference" do
get "/organisations/#{organisation.id}/logs?search=#{log_to_search.propcode}", headers: headers, params: {}
expect(page).to have_content(log_to_search.id)
logs.each do |log|
expect(page).not_to have_content(log.id)
end
end
it "shows case logs matching the property postcode" do
get "/organisations/#{organisation.id}/logs?search=#{log_to_search.postcode_full}", headers: headers, params: {}
expect(page).to have_content(log_to_search.id)
logs.each do |log|
expect(page).not_to have_content(log.id)
end
end
context "when more than one results with matching postcode" do
let!(:matching_postcode_log) { FactoryBot.create(:case_log, :completed, owning_organisation: user.organisation, postcode_full: log_to_search.postcode_full) }
it "displays all matching logs" do
get "/organisations/#{organisation.id}/logs?search=#{log_to_search.postcode_full}", headers: headers, params: {}
expect(page).to have_content(log_to_search.id)
expect(page).to have_content(matching_postcode_log.id)
logs.each do |log|
expect(page).not_to have_content(log.id)
end
end
end
context "when there are more than 1 page of search results" do
let(:logs) { FactoryBot.create_list(:case_log, 30, :completed, owning_organisation: user.organisation, postcode_full: "XX1 1YY") }
it "has title with pagination details for page 1" do
get "/organisations/#{organisation.id}/logs?search=#{logs[0].postcode_full}", headers: headers, params: {}
expect(page).to have_content("Logs (search results for ‘#{logs[0].postcode_full}’, page 1 of 2) - Submit social housing and sales data (CORE) - GOV.UK")
end
it "has title with pagination details for page 2" do
get "/organisations/#{organisation.id}/logs?search=#{logs[0].postcode_full}&page=2", headers: headers, params: {}
expect(page).to have_content("Logs (search results for ‘#{logs[0].postcode_full}’, page 2 of 2) - Submit social housing and sales data (CORE) - GOV.UK")
end
end
context "when search query doesn't match any logs" do
it "doesn't display any logs" do
get "/organisations/#{organisation.id}/logs?search=foobar", headers:, params: {}
logs.each do |log|
expect(page).not_to have_content(log.id)
end
expect(page).not_to have_content(log_to_search.id)
end
end
context "when search query is empty" do
it "doesn't display any logs" do
get "/organisations/#{organisation.id}/logs?search=", headers:, params: {}
logs.each do |log|
expect(page).not_to have_content(log.id)
end
expect(page).not_to have_content(log_to_search.id)
end
end
context "when search and filter is present" do
let(:matching_postcode) { log_to_search.postcode_full }
let(:matching_status) { "in_progress" }
let!(:log_matching_filter_and_search) { FactoryBot.create(:case_log, :in_progress, owning_organisation: user.organisation, postcode_full: matching_postcode) }
it "shows only logs matching both search and filters" do
get "/organisations/#{organisation.id}/logs?search=#{matching_postcode}&status[]=#{matching_status}", headers: headers, params: {}
expect(page).to have_content(log_matching_filter_and_search.id)
expect(page).not_to have_content(log_to_search.id)
logs.each do |log|
expect(page).not_to have_content(log.id)
end
end
end
end
end end
context "when viewing a specific organisation details" do context "when viewing a specific organisation details" do

4
spec/services/exports/case_log_export_service_spec.rb

@ -47,7 +47,7 @@ RSpec.describe Exports::CaseLogExportService do
end end
context "and one case log is available for export" do context "and one case log is available for export" do
let!(:case_log) { FactoryBot.create(:case_log, :completed) } let!(:case_log) { FactoryBot.create(:case_log, :completed, tenancy_code: "BZ757", propcode: "123", ppostcode_full: "SE2 6RT", postcode_full: "NW1 5TY", tenant_code: "BZ737") }
let(:expected_data_filename) { "core_2021_2022_jan_mar_f0001_inc0001_pt001.xml" } let(:expected_data_filename) { "core_2021_2022_jan_mar_f0001_inc0001_pt001.xml" }
it "generates a ZIP export file with the expected filename" do it "generates a ZIP export file with the expected filename" do
@ -226,7 +226,7 @@ RSpec.describe Exports::CaseLogExportService do
let(:csv_export_file) { File.open("spec/fixtures/exports/case_logs.csv", "r:UTF-8") } let(:csv_export_file) { File.open("spec/fixtures/exports/case_logs.csv", "r:UTF-8") }
let(:expected_csv_filename) { "export_2022_05_01.csv" } let(:expected_csv_filename) { "export_2022_05_01.csv" }
let(:case_log) { FactoryBot.create(:case_log, :completed) } let(:case_log) { FactoryBot.create(:case_log, :completed, tenancy_code: "BZ757", propcode: "123", ppostcode_full: "SE2 6RT", postcode_full: "NW1 5TY", tenant_code: "BZ737") }
it "generates an CSV export file with the expected content" do it "generates an CSV export file with the expected content" do
expected_content = replace_entity_ids(case_log, csv_export_file.read) expected_content = replace_entity_ids(case_log, csv_export_file.read)

Loading…
Cancel
Save