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 %> 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