From eabf668ec571ba5abf1f9d4d6a7b5e56d53019d6 Mon Sep 17 00:00:00 2001 From: Arthur Campbell Date: Mon, 5 Jun 2023 12:26:32 +0100 Subject: [PATCH] implement the story for lettings and sales logs under the organisation tab routing and controller methods testing for deleting sales logs, lettings or sales logs for an organisation move storage of relevant routes inside the form object as a comprehensive view model --- app/controllers/delete_logs_controller.rb | 130 +++- app/controllers/organisations_controller.rb | 4 +- app/models/forms/delete_logs_form.rb | 5 +- app/views/logs/delete_lettings_logs.html.erb | 4 +- .../logs/delete_logs_confirmation.html.erb | 4 +- app/views/logs/delete_sales_logs.html.erb | 4 +- app/views/organisations/logs.html.erb | 2 +- config/routes.rb | 8 + spec/requests/delete_logs_controller_spec.rb | 730 +++++++++++++++++- spec/views/logs/delete_lettings_logs_spec.rb | 11 +- 10 files changed, 856 insertions(+), 46 deletions(-) diff --git a/app/controllers/delete_logs_controller.rb b/app/controllers/delete_logs_controller.rb index 2ad1fa294..8ab5a0aba 100644 --- a/app/controllers/delete_logs_controller.rb +++ b/app/controllers/delete_logs_controller.rb @@ -6,13 +6,13 @@ class DeleteLogsController < ApplicationController before_action :session_filters, if: :current_user, except: [:delete_logs] def delete_lettings_logs - @delete_logs_form = delete_logs_form(log_type: :lettings) + @delete_logs_form = delete_logs_form(log_type: :lettings, paths: lettings_logs_paths) render "logs/delete_lettings_logs" end def delete_lettings_logs_with_selected_ids selected_ids = params.require(:selected_ids).split.map(&:to_i) - @delete_logs_form = delete_logs_form(selected_ids:, log_type: :lettings) + @delete_logs_form = delete_logs_form(selected_ids:, log_type: :lettings, paths: lettings_logs_paths) render "logs/delete_lettings_logs" end @@ -21,11 +21,9 @@ class DeleteLogsController < ApplicationController current_user:, log_filters: @session_filters, log_type: :lettings, - } - form_attributes = params.require(:forms_delete_logs_form).permit(:search_term, selected_ids: []) + }.merge lettings_logs_paths attributes = form_attributes.merge(default_attributes) attributes[:selected_ids] = [] unless attributes.key? :selected_ids - @delete_path = delete_logs_lettings_logs_path @delete_logs_form = Forms::DeleteLogsForm.new(attributes) if @delete_logs_form.valid? render "logs/delete_logs_confirmation" @@ -45,13 +43,13 @@ class DeleteLogsController < ApplicationController end def delete_sales_logs - @delete_logs_form = delete_logs_form(log_type: :sales) + @delete_logs_form = delete_logs_form(log_type: :sales, paths: sales_logs_paths) render "logs/delete_sales_logs" end def delete_sales_logs_with_selected_ids selected_ids = params.require(:selected_ids).split.map(&:to_i) - @delete_logs_form = delete_logs_form(selected_ids:, log_type: :sales) + @delete_logs_form = delete_logs_form(selected_ids:, log_type: :sales, paths: sales_logs_paths) render "logs/delete_sales_logs" end @@ -60,11 +58,9 @@ class DeleteLogsController < ApplicationController current_user:, log_filters: @session_filters, log_type: :sales, - } - form_attributes = params.require(:forms_delete_logs_form).permit(:search_term, selected_ids: []) + }.merge sales_logs_paths attributes = form_attributes.merge(default_attributes) attributes[:selected_ids] = [] unless attributes.key? :selected_ids - @delete_path = delete_logs_sales_logs_path @delete_logs_form = Forms::DeleteLogsForm.new(attributes) if @delete_logs_form.valid? render "logs/delete_logs_confirmation" @@ -83,10 +79,120 @@ class DeleteLogsController < ApplicationController redirect_to sales_logs_path, notice: I18n.t("notification.logs_deleted", count: logs.count) end + def delete_lettings_logs_for_organisation + @delete_logs_form = delete_logs_form(log_type: :lettings, paths: lettings_logs_for_organisation_paths) + render "logs/delete_lettings_logs" + end + + def delete_lettings_logs_for_organisation_with_selected_ids + selected_ids = params.require(:selected_ids).split.map(&:to_i) + @delete_logs_form = delete_logs_form(selected_ids:, log_type: :lettings, paths: lettings_logs_for_organisation_paths) + render "logs/delete_lettings_logs" + end + + def delete_lettings_logs_for_organisation_confirmation + default_attributes = { + current_user:, + log_filters: @session_filters, + log_type: :lettings, + }.merge lettings_logs_for_organisation_paths + attributes = form_attributes.merge(default_attributes) + attributes[:selected_ids] = [] unless attributes.key? :selected_ids + @delete_logs_form = Forms::DeleteLogsForm.new(attributes) + if @delete_logs_form.valid? + render "logs/delete_logs_confirmation" + else + render "logs/delete_lettings_logs" + end + end + + def discard_lettings_logs_for_organisation + logs = LettingsLog.find(params.require(:ids)) + logs.each do |log| + authorize log, :destroy? + log.discard! + end + + redirect_to lettings_logs_organisation_path, notice: I18n.t("notification.logs_deleted", count: logs.count) + end + + def delete_sales_logs_for_organisation + @delete_logs_form = delete_logs_form(log_type: :sales, paths: sales_logs_for_organisation_paths) + render "logs/delete_sales_logs" + end + + def delete_sales_logs_for_organisation_with_selected_ids + selected_ids = params.require(:selected_ids).split.map(&:to_i) + @delete_logs_form = delete_logs_form(selected_ids:, log_type: :sales, paths: sales_logs_for_organisation_paths) + render "logs/delete_sales_logs" + end + + def delete_sales_logs_for_organisation_confirmation + default_attributes = { + current_user:, + log_filters: @session_filters, + log_type: :sales, + }.merge sales_logs_for_organisation_paths + attributes = form_attributes.merge(default_attributes) + attributes[:selected_ids] = [] unless attributes.key? :selected_ids + @delete_logs_form = Forms::DeleteLogsForm.new(attributes) + if @delete_logs_form.valid? + render "logs/delete_logs_confirmation" + else + render "logs/delete_sales_logs" + end + end + + def discard_sales_logs_for_organisation + logs = SalesLog.find(params.require(:ids)) + logs.each do |log| + authorize log, :destroy? + log.discard! + end + + redirect_to sales_logs_organisation_path, notice: I18n.t("notification.logs_deleted", count: logs.count) + end + private - def delete_logs_form(selected_ids: nil, log_type:) - Forms::DeleteLogsForm.new(current_user:, search_term:, log_filters: @session_filters, log_type:, selected_ids:) + def delete_logs_form(log_type:, paths:, selected_ids: nil) + Forms::DeleteLogsForm.new(current_user:, search_term:, log_filters: @session_filters, log_type:, selected_ids:, **paths) + end + + def form_attributes + params.require(:forms_delete_logs_form).permit(:search_term, selected_ids: []) + end + + def lettings_logs_paths + { + delete_confirmation_path: delete_logs_confirmation_lettings_logs_path, + back_to_logs_path: lettings_logs_path(search: search_term), + delete_path: delete_logs_lettings_logs_path, + } + end + + def sales_logs_paths + { + delete_confirmation_path: delete_logs_confirmation_sales_logs_path, + back_to_logs_path: sales_logs_path(search: search_term), + delete_path: delete_logs_sales_logs_path, + } + end + + def lettings_logs_for_organisation_paths + { + delete_confirmation_path: delete_lettings_logs_confirmation_organisation_path, + back_to_logs_path: lettings_logs_organisation_path(search: search_term), + delete_path: delete_lettings_logs_organisation_path, + } + end + + def sales_logs_for_organisation_paths + { + delete_confirmation_path: delete_sales_logs_confirmation_organisation_path, + back_to_logs_path: sales_logs_organisation_path(search: search_term), + delete_path: delete_sales_logs_organisation_path, + } end def search_term diff --git a/app/controllers/organisations_controller.rb b/app/controllers/organisations_controller.rb index 7fdcd1d94..4326a0869 100644 --- a/app/controllers/organisations_controller.rb +++ b/app/controllers/organisations_controller.rb @@ -96,6 +96,7 @@ class OrganisationsController < ApplicationController format.html do @search_term = search_term @pagy, @logs = pagy(unpaginated_filtered_logs) + @delete_logs_path = delete_lettings_logs_organisation_path @searched = search_term.presence @total_count = organisation_logs.size @log_type = :lettings @@ -119,13 +120,14 @@ class OrganisationsController < ApplicationController end def sales_logs - organisation_logs = SalesLog.where(owning_organisation_id: @organisation.id) + organisation_logs = SalesLog.visible.where(owning_organisation_id: @organisation.id) unpaginated_filtered_logs = filter_manager.filtered_logs(organisation_logs, search_term, session_filters) respond_to do |format| format.html do @search_term = search_term @pagy, @logs = pagy(unpaginated_filtered_logs) + @delete_logs_path = delete_sales_logs_organisation_path @searched = search_term.presence @total_count = organisation_logs.size @log_type = :sales diff --git a/app/models/forms/delete_logs_form.rb b/app/models/forms/delete_logs_form.rb index 11b1c158e..36fb33919 100644 --- a/app/models/forms/delete_logs_form.rb +++ b/app/models/forms/delete_logs_form.rb @@ -3,7 +3,7 @@ module Forms include ActiveModel::Model include ActiveModel::Validations - attr_reader :logs, :log_type, :selected_ids, :search_term + attr_reader :logs, :log_type, :selected_ids, :search_term, :delete_confirmation_path, :back_to_logs_path, :delete_path validate :at_least_one_log_selected @@ -13,6 +13,9 @@ module Forms @current_user = attributes[:current_user] @logs = FilterService.filter_logs(all_logs, @search_term, attributes[:log_filters], nil, @current_user) @selected_ids = attributes[:selected_ids] || @logs.map(&:id) + @delete_confirmation_path = attributes[:delete_confirmation_path] + @back_to_logs_path = attributes[:back_to_logs_path] + @delete_path = attributes[:delete_path] end def log_count diff --git a/app/views/logs/delete_lettings_logs.html.erb b/app/views/logs/delete_lettings_logs.html.erb index 8ccab7578..b37c37773 100644 --- a/app/views/logs/delete_lettings_logs.html.erb +++ b/app/views/logs/delete_lettings_logs.html.erb @@ -10,7 +10,7 @@

You've selected <%= @delete_logs_form.log_count %> <%= "log".pluralize(@delete_logs_form.log_count) %> to delete

-<%= form_with model: @delete_logs_form, url: delete_logs_confirmation_lettings_logs_path do |f| %> +<%= form_with model: @delete_logs_form, url: @delete_logs_form.delete_confirmation_path do |f| %> <%= f.hidden_field :search_term, value: @delete_logs_form.search_term %> <%= f.govuk_error_summary %> <%= govuk_table do |table| %> @@ -43,6 +43,6 @@ <% end %> <% end %> <%= f.govuk_submit "Continue" do %> - <%= govuk_button_link_to "Cancel", lettings_logs_path(search: @delete_logs_form.search_term), secondary: true %> + <%= govuk_button_link_to "Cancel", @delete_logs_form.back_to_logs_path, secondary: true %> <% end %> <% end %> diff --git a/app/views/logs/delete_logs_confirmation.html.erb b/app/views/logs/delete_logs_confirmation.html.erb index bbd7c5cde..a9b4252d4 100644 --- a/app/views/logs/delete_logs_confirmation.html.erb +++ b/app/views/logs/delete_logs_confirmation.html.erb @@ -16,8 +16,8 @@ <% end %>
- <%= govuk_button_to "Delete logs", @delete_path, method: "delete", params: { ids: @delete_logs_form.selected_ids } %> - <%= form_with url: @delete_path do |f| %> + <%= govuk_button_to "Delete logs", @delete_logs_form.delete_path, method: "delete", params: { ids: @delete_logs_form.selected_ids } %> + <%= form_with url: @delete_logs_form.delete_path do |f| %> <%= f.hidden_field :selected_ids, value: @delete_logs_form.selected_ids %> <%= f.hidden_field :search, value: @delete_logs_form.search_term %> <%= f.govuk_submit "Cancel", secondary: true %> diff --git a/app/views/logs/delete_sales_logs.html.erb b/app/views/logs/delete_sales_logs.html.erb index abbde84d9..2e9310ea4 100644 --- a/app/views/logs/delete_sales_logs.html.erb +++ b/app/views/logs/delete_sales_logs.html.erb @@ -10,7 +10,7 @@

You've selected <%= @delete_logs_form.log_count %> <%= "log".pluralize(@delete_logs_form.log_count) %> to delete

-<%= form_with model: @delete_logs_form, url: delete_logs_confirmation_sales_logs_path do |f| %> +<%= form_with model: @delete_logs_form, url: @delete_logs_form.delete_confirmation_path do |f| %> <%= f.hidden_field :search_term, value: @delete_logs_form.search_term %> <%= f.govuk_error_summary %> <%= govuk_table do |table| %> @@ -43,6 +43,6 @@ <% end %> <% end %> <%= f.govuk_submit "Continue" do %> - <%= govuk_button_link_to "Cancel", sales_logs_path(search: @delete_logs_form.search_term), secondary: true %> + <%= govuk_button_link_to "Cancel", @delete_logs_form.back_to_logs_path, secondary: true %> <% end %> <% end %> diff --git a/app/views/organisations/logs.html.erb b/app/views/organisations/logs.html.erb index 5820398e3..a433cebd2 100644 --- a/app/views/organisations/logs.html.erb +++ b/app/views/organisations/logs.html.erb @@ -34,7 +34,7 @@ total_count: @total_count, csv_download_url: csv_download_url_by_log_type(@log_type, @organisation, search: @search_term, codes_only: false), csv_codes_only_download_url: csv_download_url_by_log_type(@log_type, @organisation, search: @search_term, codes_only: true), - delete_logs_path: "#", + delete_logs_path: @delete_logs_path, } %> <%= render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "logs" } %>
diff --git a/config/routes.rb b/config/routes.rb index e4d3a1e57..fa0cb598e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -122,7 +122,15 @@ Rails.application.routes.draw do get "users", to: "organisations#users" get "users/invite", to: "users/account#new" get "lettings-logs", to: "organisations#lettings_logs" + get "delete-lettings-logs", to: "delete_logs#delete_lettings_logs_for_organisation" + post "delete-lettings-logs", to: "delete_logs#delete_lettings_logs_for_organisation_with_selected_ids" + post "delete-lettings-logs-confirmation", to: "delete_logs#delete_lettings_logs_for_organisation_confirmation" + delete "delete-lettings-logs", to: "delete_logs#discard_lettings_logs_for_organisation" get "sales-logs", to: "organisations#sales_logs" + get "delete-sales-logs", to: "delete_logs#delete_sales_logs_for_organisation" + post "delete-sales-logs", to: "delete_logs#delete_sales_logs_for_organisation_with_selected_ids" + post "delete-sales-logs-confirmation", to: "delete_logs#delete_sales_logs_for_organisation_confirmation" + delete "delete-sales-logs", to: "delete_logs#discard_sales_logs_for_organisation" get "lettings-logs/csv-download", to: "organisations#download_lettings_csv" post "lettings-logs/email-csv", to: "organisations#email_lettings_csv" get "lettings-logs/csv-confirmation", to: "lettings_logs#csv_confirmation" diff --git a/spec/requests/delete_logs_controller_spec.rb b/spec/requests/delete_logs_controller_spec.rb index 68c9aa4eb..97bf24750 100644 --- a/spec/requests/delete_logs_controller_spec.rb +++ b/spec/requests/delete_logs_controller_spec.rb @@ -2,15 +2,18 @@ require "rails_helper" RSpec.describe "DeleteLogs", type: :request do let(:page) { Capybara::Node::Simple.new(response.body) } + let(:user) { create(:user, name: "Richard MacDuff") } - describe "GET delete-logs" do - let(:user) { create(:user, name: "Richard MacDuff") } + before do + allow(user).to receive(:need_two_factor_authentication?).and_return(false) + sign_in user + end + + describe "GET lettings-logs/delete-logs" do let!(:log_1) { create(:lettings_log, :in_progress, created_by: user) } let!(:log_2) { create(:lettings_log, :completed, created_by: user) } before do - allow(user).to receive(:need_two_factor_authentication?).and_return(false) - sign_in user allow(FilterService).to receive(:filter_logs).and_return LettingsLog.all end @@ -50,15 +53,12 @@ RSpec.describe "DeleteLogs", type: :request do end end - describe "POST delete-logs" do - let(:user) { create(:user, name: "Richard MacDuff") } + describe "POST lettings-logs/delete-logs" do let!(:log_1) { create(:lettings_log, :in_progress, created_by: user) } let!(:log_2) { create(:lettings_log, :completed, created_by: user) } let(:selected_ids) { log_1.id } before do - allow(user).to receive(:need_two_factor_authentication?).and_return(false) - sign_in user allow(FilterService).to receive(:filter_logs).and_return LettingsLog.all end @@ -104,8 +104,7 @@ RSpec.describe "DeleteLogs", type: :request do end end - describe "POST delete-logs-confirmation" do - let(:user) { create(:user, name: "Urban Chronotis") } + describe "POST lettings-logs/delete-logs-confirmation" do let(:log_1) { create(:lettings_log, :in_progress) } let(:log_2) { create(:lettings_log, :completed) } let(:log_3) { create(:lettings_log, :in_progress) } @@ -119,8 +118,6 @@ RSpec.describe "DeleteLogs", type: :request do end before do - allow(user).to receive(:need_two_factor_authentication?).and_return(false) - sign_in user post delete_logs_confirmation_lettings_logs_path, params: params end @@ -147,8 +144,6 @@ RSpec.describe "DeleteLogs", type: :request do end it "shows the correct information text to the user in the singular" do - post delete_logs_confirmation_lettings_logs_path, params: params - expect(page).to have_selector("p", text: "You've selected 1 log to delete") end end @@ -213,18 +208,12 @@ RSpec.describe "DeleteLogs", type: :request do end end - describe "DELETE delete-logs" do - let(:urban_chronotis) { create(:user, :data_provider, name: "Urban Chronotis") } - let(:log_1) { create(:lettings_log, :in_progress, created_by: urban_chronotis) } + describe "DELETE lettings-logs/delete-logs" do + let(:log_1) { create(:lettings_log, :in_progress, created_by: user) } let(:params) { { ids: [log_1.id, log_2.id] } } - before do - allow(urban_chronotis).to receive(:need_two_factor_authentication?).and_return(false) - sign_in urban_chronotis - end - context "when the user is authorized to delete the logs provided" do - let(:log_2) { create(:lettings_log, :completed, created_by: urban_chronotis) } + let(:log_2) { create(:lettings_log, :completed, created_by: user) } it "deletes the logs provided" do delete delete_logs_lettings_logs_path, params: params @@ -259,4 +248,699 @@ RSpec.describe "DeleteLogs", type: :request do end end end + + describe "GET sales-logs/delete-logs" do + let!(:log_1) { create(:sales_log, :in_progress, created_by: user) } + let!(:log_2) { create(:sales_log, :completed, created_by: user) } + + before do + allow(FilterService).to receive(:filter_logs).and_return SalesLog.all + end + + it "calls the filter service with the filters in the session and the search term from the query params" do + search = "Schrödinger's cat" + logs_filters = { + "years" => [""], + "status" => ["", "in_progress"], + "user" => "all", + } + get sales_logs_path(logs_filters) # adds the filters to the session + + expect(FilterService).to receive(:filter_logs) { |arg1, arg2, arg3, _arg4, _arg5| + expect(arg1).to contain_exactly(log_1, log_2) + expect(arg2).to eq search + expect(arg3).to eq logs_filters + }.and_return SalesLog.all + + get delete_logs_sales_logs_path(search:) + end + + it "displays the logs returned by the filter service" do + get delete_logs_sales_logs_path + + table_body_rows = page.find_all("tbody tr") + expect(table_body_rows.count).to be 2 + ids_in_table = table_body_rows.map { |row| row.first("td").text } + expect(ids_in_table).to match_array [log_1.id.to_s, log_2.id.to_s] + end + + it "checks all checkboxes by default" do + get delete_logs_sales_logs_path + + checkboxes = page.find_all("tbody tr").map { |row| row.find("input") } + expect(checkboxes.count).to be 2 + expect(checkboxes).to all be_checked + end + end + + describe "POST sales-logs/delete-logs" do + let!(:log_1) { create(:sales_log, :in_progress, created_by: user) } + let!(:log_2) { create(:sales_log, :completed, created_by: user) } + let(:selected_ids) { log_1.id } + + before do + allow(FilterService).to receive(:filter_logs).and_return SalesLog.all + end + + it "throws an error if selected ids are not provided" do + expect { post delete_logs_sales_logs_path }.to raise_error ActionController::ParameterMissing + end + + it "calls the filter service with the filters in the session and the search term from the query params" do + search = "Schrödinger's cat" + logs_filters = { + "years" => [""], + "status" => ["", "in_progress"], + "user" => "all", + } + get sales_logs_path(logs_filters) # adds the filters to the session + + expect(FilterService).to receive(:filter_logs) { |arg1, arg2, arg3, _arg4, _arg5| + expect(arg1).to contain_exactly(log_1, log_2) + expect(arg2).to eq search + expect(arg3).to eq logs_filters + }.and_return SalesLog.all + + post delete_logs_sales_logs_path(search:, selected_ids:) + end + + it "displays the logs returned by the filter service" do + post delete_logs_sales_logs_path(selected_ids:) + + table_body_rows = page.find_all("tbody tr") + expect(table_body_rows.count).to be 2 + ids_in_table = table_body_rows.map { |row| row.first("td").text } + expect(ids_in_table).to match_array [log_1.id.to_s, log_2.id.to_s] + end + + it "only checks the selected checkboxes when selected_ids provided" do + post delete_logs_sales_logs_path(selected_ids:) + + checkboxes = page.find_all("tbody tr").map { |row| row.find("input") } + checkbox_expected_checked = checkboxes.find { |cb| cb.value == log_1.id.to_s } + checkbox_expected_unchecked = checkboxes.find { |cb| cb.value == log_2.id.to_s } + expect(checkbox_expected_checked).to be_checked + expect(checkbox_expected_unchecked).not_to be_checked + end + end + + describe "POST sales-logs/delete-logs-confirmation" do + let(:log_1) { create(:sales_log, :in_progress) } + let(:log_2) { create(:sales_log, :completed) } + let(:log_3) { create(:sales_log, :in_progress) } + let(:params) do + { + forms_delete_logs_form: { + search_term: "milk", + selected_ids: [log_1, log_2].map(&:id), + }, + } + end + + before do + post delete_logs_confirmation_sales_logs_path, params: params + end + + it "requires delete logs form data to be provided" do + expect { post delete_logs_confirmation_sales_logs_path }.to raise_error(ActionController::ParameterMissing) + end + + it "shows the correct title" do + expect(page.find("h1").text).to include "Are you sure you want to delete these logs?" + end + + it "shows the correct information text to the user" do + expect(page).to have_selector("p", text: "You've selected 2 logs to delete") + end + + context "when only one log is selected" do + let(:params) do + { + forms_delete_logs_form: { + search_term: "milk", + selected_ids: [log_1].map(&:id), + }, + } + end + + it "shows the correct information text to the user in the singular" do + expect(page).to have_selector("p", text: "You've selected 1 log to delete") + end + end + + it "shows a warning to the user" do + expect(page).to have_selector(".govuk-warning-text", text: "You will not be able to undo this action") + end + + it "shows a button to delete the selected logs" do + expect(page).to have_selector("form.button_to button", text: "Delete logs") + end + + it "the delete logs button submits the correct data to the correct path" do + form_containing_button = page.find("form.button_to") + + expect(form_containing_button[:action]).to eq delete_logs_sales_logs_path + expect(form_containing_button).to have_field "_method", type: :hidden, with: "delete" + expect(form_containing_button).to have_field "ids[]", type: :hidden, with: log_1.id + expect(form_containing_button).to have_field "ids[]", type: :hidden, with: log_2.id + end + + it "shows a cancel button with the correct style" do + expect(page).to have_selector("button.govuk-button--secondary", text: "Cancel") + end + + it "the cancel button submits the correct data to the correct path" do + form_containing_cancel = page.find_all("form").find { |form| form.has_selector?("button.govuk-button--secondary") } + expect(form_containing_cancel).to have_field("selected_ids", type: :hidden, with: [log_1, log_2].map(&:id).join(" ")) + expect(form_containing_cancel).to have_field("search", type: :hidden, with: "milk") + expect(form_containing_cancel[:method]).to eq "post" + expect(form_containing_cancel[:action]).to eq delete_logs_sales_logs_path + end + + context "when no logs are selected" do + let(:params) do + { + forms_delete_logs_form: { + log_type: :sales, + log_ids: [log_1, log_2, log_3].map(&:id).join(" "), + }, + } + end + + before do + post delete_logs_confirmation_sales_logs_path, params: params + end + + it "renders the list of logs table again" do + expect(page.find("h1").text).to include "Review the logs you want to delete" + end + + it "displays an error message" do + expect(page).to have_selector(".govuk-error-summary", text: "Select at least one log to delete or press cancel to return") + end + + it "renders the table with all checkboxes unchecked" do + checkboxes = page.find_all("tbody tr").map { |row| row.find("input") } + checkboxes.each do |checkbox| + expect(checkbox).not_to be_checked + end + end + end + end + + describe "DELETE sales-logs/delete-logs" do + let(:log_1) { create(:sales_log, :in_progress, created_by: user) } + let(:params) { { ids: [log_1.id, log_2.id] } } + + context "when the user is authorized to delete the logs provided" do + let(:log_2) { create(:sales_log, :completed, created_by: user) } + + it "deletes the logs provided" do + delete delete_logs_sales_logs_path, params: params + log_1.reload + expect(log_1.status).to eq "deleted" + expect(log_1.discarded_at).not_to be nil + log_2.reload + expect(log_2.status).to eq "deleted" + expect(log_2.discarded_at).not_to be nil + end + + it "redirects to the sales log index and displays a notice that the logs have been deleted" do + delete delete_logs_sales_logs_path, params: params + expect(response).to redirect_to sales_logs_path + follow_redirect! + expect(page).to have_selector(".govuk-notification-banner--success") + expect(page).to have_selector(".govuk-notification-banner--success", text: "2 logs have been deleted") + end + end + + context "when the user is not authorized to delete all the logs provided" do + let(:log_2) { create(:sales_log, :completed) } + + it "returns unauthorised and only deletes logs for which the user is authorised" do + delete delete_logs_sales_logs_path, params: params + expect(response).to have_http_status(:unauthorized) + log_1.reload + expect(log_1.status).to eq "deleted" + expect(log_1.discarded_at).not_to be nil + log_2.reload + expect(log_2.discarded_at).to be nil + end + end + end + + context "when a support user navigates to the organisations tab" do + let(:organisation) { create(:organisation, name: "Schmorganisation") } + let(:user) { create(:user, :support, name: "Urban Chronotis") } + + describe "GET organisations/delete-lettings-logs" do + let!(:log_1) { create(:lettings_log, :in_progress, owning_organisation: organisation) } + let!(:log_2) { create(:lettings_log, :completed, owning_organisation: organisation) } + + before do + allow(FilterService).to receive(:filter_logs).and_return LettingsLog.all + end + + it "calls the filter service with the filters in the session and the search term from the query params" do + search = "Schrödinger's cat" + logs_filters = { + "years" => [""], + "status" => ["", "in_progress"], + "user" => "all", + } + get lettings_logs_path(logs_filters) # adds the filters to the session + + expect(FilterService).to receive(:filter_logs) { |arg1, arg2, arg3, _arg4, _arg5| + expect(arg1).to contain_exactly(log_1, log_2) + expect(arg2).to eq search + expect(arg3).to eq logs_filters + }.and_return LettingsLog.all + + get delete_lettings_logs_organisation_path(id: organisation, search:) + end + + it "displays the logs returned by the filter service" do + get delete_lettings_logs_organisation_path(id: organisation) + + table_body_rows = page.find_all("tbody tr") + expect(table_body_rows.count).to be 2 + ids_in_table = table_body_rows.map { |row| row.first("td").text } + expect(ids_in_table).to match_array [log_1.id.to_s, log_2.id.to_s] + end + + it "checks all checkboxes by default" do + get delete_lettings_logs_organisation_path(id: organisation) + + checkboxes = page.find_all("tbody tr").map { |row| row.find("input") } + expect(checkboxes.count).to be 2 + expect(checkboxes).to all be_checked + end + end + + describe "POST organisations/delete-lettings-logs" do + let!(:log_1) { create(:lettings_log, :in_progress, owning_organisation: organisation) } + let!(:log_2) { create(:lettings_log, :completed, owning_organisation: organisation) } + let(:selected_ids) { log_1.id } + + before do + allow(FilterService).to receive(:filter_logs).and_return LettingsLog.all + end + + it "throws an error if selected ids are not provided" do + expect { post delete_lettings_logs_organisation_path(id: organisation) }.to raise_error ActionController::ParameterMissing + end + + it "calls the filter service with the filters in the session and the search term from the query params" do + search = "Schrödinger's cat" + logs_filters = { + "years" => [""], + "status" => ["", "in_progress"], + "user" => "all", + } + get lettings_logs_path(logs_filters) # adds the filters to the session + + expect(FilterService).to receive(:filter_logs) { |arg1, arg2, arg3, _arg4, _arg5| + expect(arg1).to contain_exactly(log_1, log_2) + expect(arg2).to eq search + expect(arg3).to eq logs_filters + }.and_return LettingsLog.all + + post delete_lettings_logs_organisation_path(id: organisation, search:, selected_ids:) + end + + it "displays the logs returned by the filter service" do + post delete_lettings_logs_organisation_path(id: organisation, selected_ids:) + + table_body_rows = page.find_all("tbody tr") + expect(table_body_rows.count).to be 2 + ids_in_table = table_body_rows.map { |row| row.first("td").text } + expect(ids_in_table).to match_array [log_1.id.to_s, log_2.id.to_s] + end + + it "only checks the selected checkboxes when selected_ids provided" do + post delete_lettings_logs_organisation_path(id: organisation, selected_ids:) + + checkboxes = page.find_all("tbody tr").map { |row| row.find("input") } + checkbox_expected_checked = checkboxes.find { |cb| cb.value == log_1.id.to_s } + checkbox_expected_unchecked = checkboxes.find { |cb| cb.value == log_2.id.to_s } + expect(checkbox_expected_checked).to be_checked + expect(checkbox_expected_unchecked).not_to be_checked + end + end + + describe "POST organisations/delete-lettings-logs-confirmation" do + let(:log_1) { create(:lettings_log, :in_progress, owning_organisation: organisation) } + let(:log_2) { create(:lettings_log, :completed, owning_organisation: organisation) } + let(:log_3) { create(:lettings_log, :in_progress, owning_organisation: organisation) } + let(:params) do + { + forms_delete_logs_form: { + search_term: "milk", + selected_ids: [log_1, log_2].map(&:id), + }, + } + end + + before do + post delete_lettings_logs_confirmation_organisation_path(id: organisation), params: params + end + + it "requires delete logs form data to be provided" do + expect { post delete_lettings_logs_confirmation_organisation_path(id: organisation) }.to raise_error(ActionController::ParameterMissing) + end + + it "shows the correct title" do + expect(page.find("h1").text).to include "Are you sure you want to delete these logs?" + end + + it "shows the correct information text to the user" do + expect(page).to have_selector("p", text: "You've selected 2 logs to delete") + end + + context "when only one log is selected" do + let(:params) do + { + forms_delete_logs_form: { + search_term: "milk", + selected_ids: [log_1].map(&:id), + }, + } + end + + it "shows the correct information text to the user in the singular" do + expect(page).to have_selector("p", text: "You've selected 1 log to delete") + end + end + + it "shows a warning to the user" do + expect(page).to have_selector(".govuk-warning-text", text: "You will not be able to undo this action") + end + + it "shows a button to delete the selected logs" do + expect(page).to have_selector("form.button_to button", text: "Delete logs") + end + + it "the delete logs button submits the correct data to the correct path" do + form_containing_button = page.find("form.button_to") + + expect(form_containing_button[:action]).to eq delete_lettings_logs_organisation_path(id: organisation) + expect(form_containing_button).to have_field "_method", type: :hidden, with: "delete" + expect(form_containing_button).to have_field "ids[]", type: :hidden, with: log_1.id + expect(form_containing_button).to have_field "ids[]", type: :hidden, with: log_2.id + end + + it "shows a cancel button with the correct style" do + expect(page).to have_selector("button.govuk-button--secondary", text: "Cancel") + end + + it "the cancel button submits the correct data to the correct path" do + form_containing_cancel = page.find_all("form").find { |form| form.has_selector?("button.govuk-button--secondary") } + expect(form_containing_cancel).to have_field("selected_ids", type: :hidden, with: [log_1, log_2].map(&:id).join(" ")) + expect(form_containing_cancel).to have_field("search", type: :hidden, with: "milk") + expect(form_containing_cancel[:method]).to eq "post" + expect(form_containing_cancel[:action]).to eq delete_lettings_logs_organisation_path(id: organisation) + end + + context "when no logs are selected" do + let(:params) do + { + forms_delete_logs_form: { + log_type: :lettings, + log_ids: [log_1, log_2, log_3].map(&:id).join(" "), + }, + } + end + + before do + post delete_lettings_logs_confirmation_organisation_path(id: organisation, params:) + end + + it "renders the list of logs table again" do + expect(page.find("h1").text).to include "Review the logs you want to delete" + end + + it "displays an error message" do + expect(page).to have_selector(".govuk-error-summary", text: "Select at least one log to delete or press cancel to return") + end + + it "renders the table with all checkboxes unchecked" do + checkboxes = page.find_all("tbody tr").map { |row| row.find("input") } + checkboxes.each do |checkbox| + expect(checkbox).not_to be_checked + end + end + end + end + + describe "DELETE organisations/delete-lettings-logs" do + let(:log_1) { create(:lettings_log, :in_progress, owning_organisation: organisation) } + let(:log_2) { create(:lettings_log, :completed, owning_organisation: organisation) } + let(:params) { { ids: [log_1.id, log_2.id] } } + + before do + delete delete_lettings_logs_organisation_path(id: organisation, params:) + end + + it "deletes the logs provided" do + log_1.reload + expect(log_1.status).to eq "deleted" + expect(log_1.discarded_at).not_to be nil + log_2.reload + expect(log_2.status).to eq "deleted" + expect(log_2.discarded_at).not_to be nil + end + + it "redirects to the lettings log index for that organisation and displays a notice that the logs have been deleted" do + expect(response).to redirect_to lettings_logs_organisation_path(id: organisation) + follow_redirect! + expect(page).to have_selector(".govuk-notification-banner--success") + expect(page).to have_selector(".govuk-notification-banner--success", text: "2 logs have been deleted") + end + end + + describe "GET organisations/delete-sales-logs" do + let!(:log_1) { create(:sales_log, :in_progress, owning_organisation: organisation) } + let!(:log_2) { create(:sales_log, :completed, owning_organisation: organisation) } + + before do + allow(FilterService).to receive(:filter_logs).and_return SalesLog.all + end + + it "calls the filter service with the filters in the session and the search term from the query params" do + search = "Schrödinger's cat" + logs_filters = { + "years" => [""], + "status" => ["", "in_progress"], + "user" => "all", + } + get sales_logs_path(logs_filters) # adds the filters to the session + + expect(FilterService).to receive(:filter_logs) { |arg1, arg2, arg3, _arg4, _arg5| + expect(arg1).to contain_exactly(log_1, log_2) + expect(arg2).to eq search + expect(arg3).to eq logs_filters + }.and_return SalesLog.all + + get delete_sales_logs_organisation_path(id: organisation, search:) + end + + it "displays the logs returned by the filter service" do + get delete_sales_logs_organisation_path(id: organisation) + + table_body_rows = page.find_all("tbody tr") + expect(table_body_rows.count).to be 2 + ids_in_table = table_body_rows.map { |row| row.first("td").text } + expect(ids_in_table).to match_array [log_1.id.to_s, log_2.id.to_s] + end + + it "checks all checkboxes by default" do + get delete_sales_logs_organisation_path(id: organisation) + + checkboxes = page.find_all("tbody tr").map { |row| row.find("input") } + expect(checkboxes.count).to be 2 + expect(checkboxes).to all be_checked + end + end + + describe "POST organisations/delete-sales-logs" do + let!(:log_1) { create(:sales_log, :in_progress, owning_organisation: organisation) } + let!(:log_2) { create(:sales_log, :completed, owning_organisation: organisation) } + let(:selected_ids) { log_1.id } + + before do + allow(FilterService).to receive(:filter_logs).and_return SalesLog.all + end + + it "throws an error if selected ids are not provided" do + expect { post delete_sales_logs_organisation_path(id: organisation) }.to raise_error ActionController::ParameterMissing + end + + it "calls the filter service with the filters in the session and the search term from the query params" do + search = "Schrödinger's cat" + logs_filters = { + "years" => [""], + "status" => ["", "in_progress"], + "user" => "all", + } + get sales_logs_path(logs_filters) # adds the filters to the session + + expect(FilterService).to receive(:filter_logs) { |arg1, arg2, arg3, _arg4, _arg5| + expect(arg1).to contain_exactly(log_1, log_2) + expect(arg2).to eq search + expect(arg3).to eq logs_filters + }.and_return SalesLog.all + + post delete_sales_logs_organisation_path(id: organisation, search:, selected_ids:) + end + + it "displays the logs returned by the filter service" do + post delete_sales_logs_organisation_path(id: organisation, selected_ids:) + + table_body_rows = page.find_all("tbody tr") + expect(table_body_rows.count).to be 2 + ids_in_table = table_body_rows.map { |row| row.first("td").text } + expect(ids_in_table).to match_array [log_1.id.to_s, log_2.id.to_s] + end + + it "only checks the selected checkboxes when selected_ids provided" do + post delete_sales_logs_organisation_path(id: organisation, selected_ids:) + + checkboxes = page.find_all("tbody tr").map { |row| row.find("input") } + checkbox_expected_checked = checkboxes.find { |cb| cb.value == log_1.id.to_s } + checkbox_expected_unchecked = checkboxes.find { |cb| cb.value == log_2.id.to_s } + expect(checkbox_expected_checked).to be_checked + expect(checkbox_expected_unchecked).not_to be_checked + end + end + + describe "POST organisations/delete-sales-logs-confirmation" do + let(:log_1) { create(:sales_log, :in_progress, owning_organisation: organisation) } + let(:log_2) { create(:sales_log, :completed, owning_organisation: organisation) } + let(:log_3) { create(:sales_log, :in_progress, owning_organisation: organisation) } + let(:params) do + { + forms_delete_logs_form: { + search_term: "milk", + selected_ids: [log_1, log_2].map(&:id), + }, + } + end + + before do + post delete_sales_logs_confirmation_organisation_path(id: organisation), params: params + end + + it "requires delete logs form data to be provided" do + expect { post delete_sales_logs_confirmation_organisation_path(id: organisation) }.to raise_error(ActionController::ParameterMissing) + end + + it "shows the correct title" do + expect(page.find("h1").text).to include "Are you sure you want to delete these logs?" + end + + it "shows the correct information text to the user" do + expect(page).to have_selector("p", text: "You've selected 2 logs to delete") + end + + context "when only one log is selected" do + let(:params) do + { + forms_delete_logs_form: { + search_term: "milk", + selected_ids: [log_1].map(&:id), + }, + } + end + + it "shows the correct information text to the user in the singular" do + expect(page).to have_selector("p", text: "You've selected 1 log to delete") + end + end + + it "shows a warning to the user" do + expect(page).to have_selector(".govuk-warning-text", text: "You will not be able to undo this action") + end + + it "shows a button to delete the selected logs" do + expect(page).to have_selector("form.button_to button", text: "Delete logs") + end + + it "the delete logs button submits the correct data to the correct path" do + form_containing_button = page.find("form.button_to") + + expect(form_containing_button[:action]).to eq delete_sales_logs_organisation_path(id: organisation) + expect(form_containing_button).to have_field "_method", type: :hidden, with: "delete" + expect(form_containing_button).to have_field "ids[]", type: :hidden, with: log_1.id + expect(form_containing_button).to have_field "ids[]", type: :hidden, with: log_2.id + end + + it "shows a cancel button with the correct style" do + expect(page).to have_selector("button.govuk-button--secondary", text: "Cancel") + end + + it "the cancel button submits the correct data to the correct path" do + form_containing_cancel = page.find_all("form").find { |form| form.has_selector?("button.govuk-button--secondary") } + expect(form_containing_cancel).to have_field("selected_ids", type: :hidden, with: [log_1, log_2].map(&:id).join(" ")) + expect(form_containing_cancel).to have_field("search", type: :hidden, with: "milk") + expect(form_containing_cancel[:method]).to eq "post" + expect(form_containing_cancel[:action]).to eq delete_sales_logs_organisation_path(id: organisation) + end + + context "when no logs are selected" do + let(:params) do + { + forms_delete_logs_form: { + log_type: :sales, + log_ids: [log_1, log_2, log_3].map(&:id).join(" "), + }, + } + end + + before do + post delete_sales_logs_confirmation_organisation_path(id: organisation, params:) + end + + it "renders the list of logs table again" do + expect(page.find("h1").text).to include "Review the logs you want to delete" + end + + it "displays an error message" do + expect(page).to have_selector(".govuk-error-summary", text: "Select at least one log to delete or press cancel to return") + end + + it "renders the table with all checkboxes unchecked" do + checkboxes = page.find_all("tbody tr").map { |row| row.find("input") } + checkboxes.each do |checkbox| + expect(checkbox).not_to be_checked + end + end + end + end + + describe "DELETE organisations/delete-sales-logs" do + let(:log_1) { create(:sales_log, :in_progress, owning_organisation: organisation) } + let(:log_2) { create(:sales_log, :completed, owning_organisation: organisation) } + let(:params) { { ids: [log_1.id, log_2.id] } } + + before do + delete delete_sales_logs_organisation_path(id: organisation, params:) + end + + it "deletes the logs provided" do + log_1.reload + expect(log_1.status).to eq "deleted" + expect(log_1.discarded_at).not_to be nil + log_2.reload + expect(log_2.status).to eq "deleted" + expect(log_2.discarded_at).not_to be nil + end + + it "redirects to the sales log index for that organisation and displays a notice that the logs have been deleted" do + expect(response).to redirect_to sales_logs_organisation_path(id: organisation) + follow_redirect! + expect(page).to have_selector(".govuk-notification-banner--success") + expect(page).to have_selector(".govuk-notification-banner--success", text: "2 logs have been deleted") + end + end + end end diff --git a/spec/views/logs/delete_lettings_logs_spec.rb b/spec/views/logs/delete_lettings_logs_spec.rb index 8abc489aa..36fb274fe 100644 --- a/spec/views/logs/delete_lettings_logs_spec.rb +++ b/spec/views/logs/delete_lettings_logs_spec.rb @@ -4,7 +4,14 @@ RSpec.describe "logs/delete_lettings_logs.html.erb" do let(:user) { create(:user, :support, name: "Dirk Gently") } let(:lettings_log_1) { create(:lettings_log, tenancycode: "Holistic", propcode: "Detective Agency", created_by: user) } let(:lettings_logs) { [lettings_log_1] } - let(:delete_logs_form) { Forms::DeleteLogsForm.new(log_type: :lettings, current_user: user) } + let(:paths) do + { + delete_confirmation_path: delete_logs_confirmation_lettings_logs_path, + back_to_logs_path: lettings_logs_path(search: "search_term"), + delete_path: delete_logs_lettings_logs_path, + } + end + let(:delete_logs_form) { Forms::DeleteLogsForm.new(log_type: :lettings, current_user: user, **paths) } before do sign_in user @@ -34,7 +41,7 @@ RSpec.describe "logs/delete_lettings_logs.html.erb" do before do lettings_logs << lettings_log_2 allow(FilterService).to receive(:filter_logs).and_return lettings_logs - delete_logs_form = Forms::DeleteLogsForm.new(log_type: :lettings, current_user: user) + delete_logs_form = Forms::DeleteLogsForm.new(log_type: :lettings, current_user: user, **paths) assign(:delete_logs_form, delete_logs_form) end