diff --git a/Gemfile b/Gemfile index 3d2e6eac6..10ce38f07 100644 --- a/Gemfile +++ b/Gemfile @@ -87,6 +87,7 @@ group :test do gem "capybara", require: false gem "capybara-lockstep" gem "factory_bot_rails" + gem "faker" gem "rspec-rails", require: false gem "selenium-webdriver", require: false gem "simplecov", require: false diff --git a/Gemfile.lock b/Gemfile.lock index df070018c..ea322cb65 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -169,6 +169,8 @@ GEM factory_bot_rails (6.2.0) factory_bot (~> 6.2.0) railties (>= 5.0.0) + faker (2.21.0) + i18n (>= 1.8.11, < 2) ffi (1.15.5) globalid (1.0.0) activesupport (>= 5.0) @@ -437,6 +439,7 @@ DEPENDENCIES dotenv-rails erb_lint factory_bot_rails + faker govuk-components govuk_design_system_formbuilder govuk_markdown diff --git a/app/components/search_component.rb b/app/components/search_component.rb index 35eeefda7..676257263 100644 --- a/app/components/search_component.rb +++ b/app/components/search_component.rb @@ -11,8 +11,12 @@ class SearchComponent < ViewComponent::Base def path(current_user) if request.path.include?("users") user_path(current_user) + elsif request.path.include?("organisations") && request.path.include?("logs") + request.path elsif request.path.include?("organisations") organisations_path + elsif request.path.include?("logs") + case_logs_path end end diff --git a/app/controllers/case_logs_controller.rb b/app/controllers/case_logs_controller.rb index 1217585f0..bdf9e9e9b 100644 --- a/app/controllers/case_logs_controller.rb +++ b/app/controllers/case_logs_controller.rb @@ -1,6 +1,7 @@ class CaseLogsController < ApplicationController include Pagy::Backend include Modules::CaseLogsFilter + include Modules::SearchFilter skip_before_action :verify_authenticity_token, if: :json_api_request? before_action :authenticate, if: :json_api_request? @@ -10,7 +11,17 @@ class CaseLogsController < ApplicationController def index 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| format.html @@ -85,6 +96,10 @@ private API_ACTIONS = %w[create show update destroy].freeze + def search_term + params["search"] + end + def json_api_request? API_ACTIONS.include?(request["action"]) && request.format.json? end diff --git a/app/controllers/organisations_controller.rb b/app/controllers/organisations_controller.rb index 7b5eceac4..ad195b624 100644 --- a/app/controllers/organisations_controller.rb +++ b/app/controllers/organisations_controller.rb @@ -55,7 +55,17 @@ class OrganisationsController < ApplicationController set_session_filters(specific_org: true) 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" else redirect_to(case_logs_path) diff --git a/app/frontend/styles/_table-group.scss b/app/frontend/styles/_table-group.scss index 71905dfec..c5ca328f3 100644 --- a/app/frontend/styles/_table-group.scss +++ b/app/frontend/styles/_table-group.scss @@ -2,7 +2,7 @@ overflow-x: auto; overflow-y: hidden; 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"); .govuk-table { diff --git a/app/models/case_log.rb b/app/models/case_log.rb index 2c09bd650..5533b99be 100644 --- a/app/models/case_log.rb +++ b/app/models/case_log.rb @@ -51,6 +51,17 @@ class CaseLog < ApplicationRecord 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 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 diff --git a/app/views/case_logs/_log_list.html.erb b/app/views/case_logs/_log_list.html.erb index 87c5c1ed7..130120de8 100644 --- a/app/views/case_logs/_log_list.html.erb +++ b/app/views/case_logs/_log_list.html.erb @@ -1,8 +1,12 @@
<%= govuk_table do |table| %> <%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular], id: title.dasherize) do |caption| %> - + + <% if defined?(searched) && searched.present? %> + <%= pagy.count %> <%= item_label %> found matching ‘<%= searched %>’ of <%= total_count %> total <%= title.downcase %>. <%= govuk_link_to("Clear search", request.path) %> + <% else %> <%= pagy.count %> total <%= title.downcase %> + <% end %> <%= govuk_link_to "Download (CSV)", "/logs.csv", type: "text/csv" %> <% end %> diff --git a/app/views/case_logs/index.html.erb b/app/views/case_logs/index.html.erb index 857b1dbd8..8bd085acc 100644 --- a/app/views/case_logs/index.html.erb +++ b/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 %> -

- <%= content_for(:title) %> -

<%= govuk_button_to "Create a new lettings log", case_logs_path %> @@ -10,10 +19,10 @@
<%= render partial: "log_filters" %> - <% if @case_logs.present? %> -
- <%= render partial: "log_list", locals: { case_logs: @case_logs, title: "Logs", pagy: @pagy } %> - <%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "logs" } %> -
- <% end %> +
+ <%= 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, searched: @searched, item_label:, total_count: @total_count } %> + <%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "logs" } %> +
diff --git a/app/views/organisations/_organisation_list.html.erb b/app/views/organisations/_organisation_list.html.erb index 78f60685d..20e35b40c 100644 --- a/app/views/organisations/_organisation_list.html.erb +++ b/app/views/organisations/_organisation_list.html.erb @@ -1,35 +1,37 @@ -<%= 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 organisations. <%= govuk_link_to("Clear search", request.path) %> - <% else %> - <%= pagy.count %> total organisations. +
+ <%= 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 organisations. <%= govuk_link_to("Clear search", request.path) %> + <% else %> + <%= pagy.count %> total organisations. + <% end %> <% end %> - <% end %> - <%= table.head do |head| %> - <%= head.row do |row| %> - <% row.cell(header: true, text: "Name", html_attributes: { - scope: "col", - }) %> - <% row.cell(header: true, text: "Registration number", html_attributes: { - scope: "col", - }) %> - <% row.cell(header: true, text: "Type", html_attributes: { - scope: "col", - }) %> + <%= table.head do |head| %> + <%= head.row do |row| %> + <% row.cell(header: true, text: "Name", html_attributes: { + scope: "col", + }) %> + <% row.cell(header: true, text: "Registration number", html_attributes: { + scope: "col", + }) %> + <% row.cell(header: true, text: "Type", html_attributes: { + scope: "col", + }) %> + <% end %> <% end %> - <% end %> - <% @organisations.each do |organisation| %> - <%= table.body do |body| %> - <%= body.row do |row| %> - <% row.cell(header: true, html_attributes: { - scope: "row", - }) do %> - <%= govuk_link_to(organisation.name, "organisations/#{organisation.id}/logs") %> + <% @organisations.each do |organisation| %> + <%= table.body do |body| %> + <%= body.row do |row| %> + <% row.cell(header: true, html_attributes: { + scope: "row", + }) do %> + <%= 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 %> - <% row.cell(text: organisation.housing_registration_no) %> - <% row.cell(text: organisation.display_provider_type) %> <% end %> <% end %> <% end %> -<% end %> +
diff --git a/app/views/organisations/logs.html.erb b/app/views/organisations/logs.html.erb index a12287530..be6a3d5d3 100644 --- a/app/views/organisations/logs.html.erb +++ b/app/views/organisations/logs.html.erb @@ -1,9 +1,16 @@ -<% content_for :title, "Logs" %> +<% item_label = @pagy.count > 1 ? "logs" : "log" %> -

- <%= @organisation.name %> - <%= content_for(:title) %> -

+<% 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 %> <%= render SubNavigationComponent.new( items: secondary_items(request.path, @organisation.id), @@ -11,10 +18,10 @@
<%= render partial: "case_logs/log_filters" %> - <% if @case_logs.present? %> -
- <%= render partial: "case_logs/log_list", locals: { case_logs: @case_logs, title: "Logs", pagy: @pagy } %> - <%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "logs" } %> -
- <% end %> +
+ <%= 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, searched: @searched, item_label:, total_count: @total_count } %> + <%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "logs" } %> +
diff --git a/app/views/users/_user_list.html.erb b/app/views/users/_user_list.html.erb index a934e211e..171e21220 100644 --- a/app/views/users/_user_list.html.erb +++ b/app/views/users/_user_list.html.erb @@ -1,54 +1,56 @@ -<%= 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 users. <%= govuk_link_to("Clear search", request.path) %> - <% else %> - <%= pagy.count %> total users. +
+ <%= 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 users. <%= govuk_link_to("Clear search", request.path) %> + <% else %> + <%= pagy.count %> total users. + <% end %> + + <% if current_user.support? %> + <% query = searched.present? ? "?search=#{searched}" : nil %> + <%= govuk_link_to "Download (CSV)", "/users.csv#{query}", type: "text/csv" %> <% end %> - - <% if current_user.support? %> - <% query = searched.present? ? "?search=#{searched}" : nil %> - <%= govuk_link_to "Download (CSV)", "/users.csv#{query}", type: "text/csv" %> <% end %> - <% end %> - <%= table.head do |head| %> - <%= head.row do |row| %> - <% row.cell(header: true, text: "Name and email adress", html_attributes: { - scope: "col", - }) %> - <% row.cell(header: true, text: "Organisation and role", html_attributes: { - scope: "col", - }) %> - <% row.cell(header: true, text: "Last logged in", html_attributes: { - scope: "col", - }) %> + <%= table.head do |head| %> + <%= head.row do |row| %> + <% row.cell(header: true, text: "Name and email adress", html_attributes: { + scope: "col", + }) %> + <% row.cell(header: true, text: "Organisation and role", html_attributes: { + scope: "col", + }) %> + <% row.cell(header: true, text: "Last logged in", html_attributes: { + scope: "col", + }) %> + <% end %> <% end %> - <% end %> - <% users.each do |user| %> - <%= table.body do |body| %> - <%= body.row do |row| %> - <% row.cell(header: true, html_attributes: { - scope: "row", - }) do %> - <%= simple_format(user_cell(user), {}, wrapper_tag: "span") %> - <% if user.is_data_protection_officer? || user.is_key_contact? %> -
+ <% users.each do |user| %> + <%= table.body do |body| %> + <%= body.row do |row| %> + <% row.cell(header: true, html_attributes: { + scope: "row", + }) do %> + <%= simple_format(user_cell(user), {}, wrapper_tag: "span") %> + <% if user.is_data_protection_officer? || user.is_key_contact? %> +
+ <% 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 %> - <%= 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", - ) : "" %> + <% 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 %> - <% 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 %> +
diff --git a/config/environments/test.rb b/config/environments/test.rb index 31b83e22e..3de3bd7e9 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -60,4 +60,5 @@ Rails.application.configure do # Annotate rendered view with file names. # config.action_view.annotate_rendered_view_with_filenames = true + Faker::Config.locale = "en-GB" end diff --git a/spec/factories/case_log.rb b/spec/factories/case_log.rb index 612ef9d8a..6a315a068 100644 --- a/spec/factories/case_log.rb +++ b/spec/factories/case_log.rb @@ -11,9 +11,9 @@ FactoryBot.define do end trait :in_progress do status { 1 } - tenant_code { "TH356" } - postcode_full { "PO5 3TE" } - ppostcode_full { "SW2 6HI" } + tenant_code { Faker::Name.initials(number: 10) } + postcode_full { Faker::Address.postcode } + ppostcode_full { Faker::Address.postcode } age1 { 17 } age2 { 19 } end @@ -24,7 +24,7 @@ FactoryBot.define do incfreq { 1 } end trait :conditional_section_complete do - tenant_code { "TH356" } + tenant_code { Faker::Name.initials(number: 10) } age1 { 34 } sex1 { "M" } ethnic { 2 } @@ -34,7 +34,7 @@ FactoryBot.define do end trait :completed do status { 2 } - tenant_code { "BZ737" } + tenant_code { Faker::Name.initials(number: 10) } age1 { 35 } sex1 { "F" } ethnic { 2 } @@ -52,11 +52,11 @@ FactoryBot.define do reservist { 0 } illness { 1 } preg_occ { 2 } - tenancy_code { "BZ757" } + tenancy_code { Faker::Name.initials(number: 10) } startertenancy { 0 } tenancylength { 5 } tenancy { 1 } - ppostcode_full { "SE2 6RT" } + ppostcode_full { Faker::Address.postcode } rsnvac { 6 } unittype_gn { 7 } beds { 3 } @@ -74,7 +74,7 @@ FactoryBot.define do tcharge { 325 } layear { 2 } waityear { 1 } - postcode_full { "NW1 5TY" } + postcode_full { Faker::Address.postcode } reasonpref { 1 } cbl { 1 } chr { 1 } @@ -112,7 +112,7 @@ FactoryBot.define do needstype { 1 } purchaser_code { 798_794 } reason { 4 } - propcode { "123" } + propcode { Faker::Name.initials(number: 10) } majorrepairs { 1 } la { "E09000003" } prevloc { "E07000105" } diff --git a/spec/features/form/saving_data_spec.rb b/spec/features/form/saving_data_spec.rb index 9e8d09f58..863d906e7 100644 --- a/spec/features/form/saving_data_spec.rb +++ b/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 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 it "displays text answers in inputs if they are already saved" do diff --git a/spec/features/log_spec.rb b/spec/features/log_spec.rb new file mode 100644 index 000000000..98aebb3ef --- /dev/null +++ b/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 diff --git a/spec/features/organisation_spec.rb b/spec/features/organisation_spec.rb index 75277a28a..c5725d541 100644 --- a/spec/features/organisation_spec.rb +++ b/spec/features/organisation_spec.rb @@ -84,12 +84,13 @@ RSpec.describe "User Features" do context "when user is support user" do context "when viewing logs for specific organisation" do let(:user) { FactoryBot.create(:user, :support) } - let(:number_of_case_logs) { 4 } let(:first_log) { organisation.case_logs.first } 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 - 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)) allow(SecureRandom).to receive(:random_number).and_return(otp) click_link("Sign out") @@ -99,6 +100,48 @@ RSpec.describe "User Features" do visit("/organisations/#{org_id}/logs") 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 expect(page).to have_content("#{number_of_case_logs} total logs") organisation.case_logs.map(&:id).each do |case_log_id| diff --git a/spec/models/case_log_spec.rb b/spec/models/case_log_spec.rb index 33389c28d..96ee7824f 100644 --- a/spec/models/case_log_spec.rb +++ b/spec/models/case_log_spec.rb @@ -1871,6 +1871,102 @@ RSpec.describe CaseLog do FactoryBot.create(:case_log, startdate: Time.utc(2022, 6, 3)) 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 it "allows filtering on a single year" do expect(described_class.filter_by_years(%w[2021]).count).to eq(2) diff --git a/spec/requests/case_logs_controller_spec.rb b/spec/requests/case_logs_controller_spec.rb index 51fe25fe3..29eb5dc04 100644 --- a/spec/requests/case_logs_controller_spec.rb +++ b/spec/requests/case_logs_controller_spec.rb @@ -8,7 +8,7 @@ RSpec.describe CaseLogsController, type: :request do let(:api_password) { "test_password" } let(:basic_credentials) do ActionController::HttpAuthentication::Basic - .encode_credentials(api_username, api_password) + .encode_credentials(api_username, api_password) end let(:headers) do @@ -184,6 +184,17 @@ RSpec.describe CaseLogsController, type: :request do expect(page).to have_content("UA984") 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 "with status filter" do let(:organisation_2) { FactoryBot.create(:organisation) } @@ -308,6 +319,110 @@ RSpec.describe CaseLogsController, type: :request do expect(page).not_to have_content("Managing organisation") 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 before do get "/logs", headers:, params: {} @@ -348,7 +463,7 @@ RSpec.describe CaseLogsController, type: :request do end 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 it "shows the download csv link" do @@ -424,7 +539,7 @@ RSpec.describe CaseLogsController, type: :request do end 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 @@ -449,7 +564,7 @@ RSpec.describe CaseLogsController, type: :request do end 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 diff --git a/spec/requests/organisations_controller_spec.rb b/spec/requests/organisations_controller_spec.rb index 74c1fe6b6..d965071fe 100644 --- a/spec/requests/organisations_controller_spec.rb +++ b/spec/requests/organisations_controller_spec.rb @@ -379,10 +379,6 @@ RSpec.describe OrganisationsController, type: :request do get "/organisations/#{organisation.id}/logs", headers:, params: {} end - it "displays the name of the organisation in the header" do - expect(CGI.unescape_html(response.body)).to match("#{organisation.name}") - end - it "only shows logs for that organisation" do expect(page).to have_content("#{number_of_org1_case_logs} total logs") 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_content("About this organisation") 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 context "when viewing a specific organisation details" do diff --git a/spec/services/exports/case_log_export_service_spec.rb b/spec/services/exports/case_log_export_service_spec.rb index 0e5f7095e..e172716d9 100644 --- a/spec/services/exports/case_log_export_service_spec.rb +++ b/spec/services/exports/case_log_export_service_spec.rb @@ -47,7 +47,7 @@ RSpec.describe Exports::CaseLogExportService do end 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" } 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(: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 expected_content = replace_entity_ids(case_log, csv_export_file.read)