diff --git a/app/controllers/pages/exit_pages_controller.rb b/app/controllers/pages/exit_pages_controller.rb index 43a53f0186..e8a93aeb2b 100644 --- a/app/controllers/pages/exit_pages_controller.rb +++ b/app/controllers/pages/exit_pages_controller.rb @@ -1,6 +1,7 @@ class Pages::ExitPagesController < PagesController - before_action :check_multiple_branches_enabled before_action :check_user_has_permission + before_action :check_multiple_branches_enabled + before_action :check_page_can_have_exit_pages def new exit_page_input = Pages::ExitPageInput.new(page: page) @@ -18,6 +19,46 @@ def create end end + def edit + exit_page_input = Pages::UpdateExitPageInput.new(exit_page:).assign_exit_page_values + + render locals: { exit_page_input:, preview_html: preview_html(exit_page_input), check_preview_validation: false } + end + + def update + exit_page_input = Pages::UpdateExitPageInput.new(update_exit_page_input_params.merge(exit_page:)) + + if exit_page_input.submit + redirect_to routes_path(form_id: current_form.id), success: t("banner.success.exit_page_saved") + else + render :edit, locals: { exit_page_input:, preview_html: preview_html(exit_page_input), check_preview_validation: true }, status: :unprocessable_content + end + end + + def delete + delete_confirmation_input = Pages::DeleteExitPageInput.new + + render locals: { delete_confirmation_input:, page:, exit_page: } + end + + def destroy + delete_confirmation_input = Pages::DeleteExitPageInput.new(params.require(:pages_delete_exit_page_input).permit(:confirm)) + + unless delete_confirmation_input.valid? + return render :delete, locals: { delete_confirmation_input:, page:, exit_page: }, status: :unprocessable_content + end + + unless delete_confirmation_input.confirmed? + return redirect_to edit_exit_page_path(@current_form.id, page.id, exit_page.id) + end + + current_form.save_question_changes! do + exit_page.destroy! + end + + redirect_to routes_path(@current_form.id), success: t("banner.success.exit_page_deleted") + end + def render_preview exit_page_input = Pages::ExitPageInput.new(markdown: params[:markdown]) exit_page_input.validate if params[:check_preview_validation] == "true" @@ -27,10 +68,18 @@ def render_preview private + def exit_page + @exit_page ||= page.exit_pages.find(params.require(:id)) + end + def exit_page_input_params params.require(:pages_exit_page_input).permit(:heading, :markdown).merge(page:) end + def update_exit_page_input_params + params.require(:pages_update_exit_page_input).permit(:heading, :markdown).merge(page:) + end + def check_user_has_permission authorize current_form, :can_edit_form? end @@ -41,6 +90,12 @@ def check_multiple_branches_enabled render "errors/not_found", status: :not_found, formats: :html end + def check_page_can_have_exit_pages + return if Forms::RoutesInput.route_with_selection_options?(page) + + render "errors/not_found", status: :not_found, formats: :html + end + def preview_html(exit_page_input_object) return t("exit_page.no_content_added_html") if exit_page_input_object.markdown.blank? diff --git a/app/input_objects/pages/delete_exit_page_input.rb b/app/input_objects/pages/delete_exit_page_input.rb new file mode 100644 index 0000000000..ba4f0bb96b --- /dev/null +++ b/app/input_objects/pages/delete_exit_page_input.rb @@ -0,0 +1,2 @@ +class Pages::DeleteExitPageInput < DeleteConfirmationInput +end diff --git a/app/input_objects/pages/exit_page_input.rb b/app/input_objects/pages/exit_page_input.rb index c51bc9ed62..b84494bc63 100644 --- a/app/input_objects/pages/exit_page_input.rb +++ b/app/input_objects/pages/exit_page_input.rb @@ -1,13 +1,13 @@ class Pages::ExitPageInput < BaseInput attr_accessor :page, :markdown, :heading - validates :heading, :markdown, presence: true - validates :heading, length: { maximum: 250 } - validates :markdown, markdown: { allow_headings: true } + include ExitPageValidation def submit return false if invalid? - ExitPage.create!(question_page: page, heading:, markdown:) + page.form.save_question_changes! do + ExitPage.create!(question_page: page, heading:, markdown:) + end end end diff --git a/app/input_objects/pages/update_exit_page_input.rb b/app/input_objects/pages/update_exit_page_input.rb new file mode 100644 index 0000000000..732b86409f --- /dev/null +++ b/app/input_objects/pages/update_exit_page_input.rb @@ -0,0 +1,21 @@ +class Pages::UpdateExitPageInput < BaseInput + attr_accessor :page, :markdown, :heading, :exit_page + + include ExitPageValidation + + def assign_exit_page_values + self.page = exit_page.question_page + self.heading = exit_page.heading + self.markdown = exit_page.markdown + + self + end + + def submit + return false if invalid? + + page.form.save_question_changes! do + exit_page.update!(heading: heading, markdown: markdown) + end + end +end diff --git a/app/models/concerns/exit_page_validation.rb b/app/models/concerns/exit_page_validation.rb new file mode 100644 index 0000000000..002b843ca5 --- /dev/null +++ b/app/models/concerns/exit_page_validation.rb @@ -0,0 +1,9 @@ +module ExitPageValidation + extend ActiveSupport::Concern + + included do + validates :heading, :markdown, presence: true + validates :heading, length: { maximum: 250 } + validates :markdown, markdown: { allow_headings: true } + end +end diff --git a/app/models/exit_page.rb b/app/models/exit_page.rb index 94ad3eb143..e28c2c0b0f 100644 --- a/app/models/exit_page.rb +++ b/app/models/exit_page.rb @@ -25,4 +25,10 @@ def self.positions_for_page(question_page) [exit_page_id, index + 1] end end + + def options_to_this_exit_page + return [] if conditions.nil? + + conditions.pluck(:answer_value) + end end diff --git a/app/models/form.rb b/app/models/form.rb index 22cf9c7ce6..7dc3f9cebe 100644 --- a/app/models/form.rb +++ b/app/models/form.rb @@ -51,11 +51,18 @@ class Form < ApplicationRecord attr_accessor :task_status_service - def save_question_changes! - self.question_section_completed = false - # Make sure the updated_at is updated as we use this to determine if the form has changed in forms-runner. - touch unless changed? - save_draft! + # Takes an optional blocl which will be called in the same transaction + # as the save. + def save_question_changes!(&block) + ActiveRecord::Base.transaction do + self.question_section_completed = false + + block.call if block_given? + + # Make sure the updated_at is updated as we use this to determine if the form has changed in forms-runner. + touch unless changed? + save_draft! + end end def save_draft! diff --git a/app/views/pages/exit_pages/delete.html.erb b/app/views/pages/exit_pages/delete.html.erb new file mode 100644 index 0000000000..134e472bb7 --- /dev/null +++ b/app/views/pages/exit_pages/delete.html.erb @@ -0,0 +1,13 @@ +<% set_page_title(title_with_error_prefix(t("page_titles.delete_exit_page"), delete_confirmation_input.errors&.any?)) %> +<% content_for :back_link, govuk_back_link_to(edit_exit_page_path(form_id: @current_form.id, page_id: page.id, id: exit_page.id), t(".back_link")) %> + +
+
+ <%= render( + delete_confirmation_input, + url: exit_page_path(@current_form.id, page.id, exit_page.id), + caption_text: t(".caption", exit_page_number: exit_page.position, heading: exit_page.heading), + legend_text: t(".title"), + ) %> +
+
diff --git a/app/views/pages/exit_pages/edit.html.erb b/app/views/pages/exit_pages/edit.html.erb new file mode 100644 index 0000000000..294a27bf05 --- /dev/null +++ b/app/views/pages/exit_pages/edit.html.erb @@ -0,0 +1,58 @@ +<% set_page_title(title_with_error_prefix(t("page_titles.exit_page_edit"), exit_page_input.errors&.any?)) %> +<% content_for :back_link, govuk_back_link_to(routes_path(@current_form.id), t(".back_link")) %> + +
+
+ + <%= form_with model: exit_page_input, url: exit_page_path(@current_form.id, exit_page_input.exit_page.question_page.id, exit_page_input.exit_page.id), method: 'PATCH' do |f| %> + <%= f.govuk_error_summary %> +

+ <%= t("page_titles.exit_page_new_caption", question_number: exit_page_input.page.position) %> + - + <%= t("page_titles.exit_page_edit") %> +

+ + <% options = exit_page_input.exit_page.options_to_this_exit_page %> + + <%= govuk_summary_list(actions: false) do |summary_list| + summary_list.with_row do |row| + row.with_key { t("page_route_card.question_name_short", question_number: exit_page_input.page.position) } + row.with_value { "#{exit_page_input.page.question_text}" } + end; + + summary_list.with_row do |row| + row.with_key { t(".options_to_this_exit_page") } + row.with_value do + if options.empty? + t(".no_options_to_this_exit_page") + elsif options.one? + options.first + else + content_tag(:ul, class: "govuk-list govuk-list--bullet") do + options.collect { |option| concat(content_tag(:li, option)) } + end + end + end + end; + end %> + + <%= f.govuk_text_field( :heading, label: { size: 'm' } ) %> + + <%= render MarkdownEditorComponent::View.new(:markdown, + form_builder: f, + render_preview_path: render_preview_exit_pages_path(form_id: @current_form.id, page_id: exit_page_input.exit_page.question_page.id, check_preview_validation:), + preview_html: preview_html, + form_model: exit_page_input, + label: t("helpers.label.pages_exit_page_input.markdown"), + hint: t("helpers.hint.pages_exit_page_input.markdown"), + allow_headings: true) %> + +
+ <%= f.govuk_submit t("save_and_continue") %> + <%= govuk_button_link_to t('.delete_exit_page'), delete_exit_page_path(@current_form.id, exit_page_input.exit_page.question_page.id, exit_page_input.exit_page.id), warning: true %> + <%= govuk_link_to t('cancel'), routes_path(@current_form.id) %> +
+ <% end %> +
+
+ diff --git a/config/locales/en.yml b/config/locales/en.yml index b1caffb3a5..61f853c676 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1741,10 +1741,12 @@ en: copy_of_answers: Give people the option to get a copy of their answers by email date_settings: Are you asking for someone’s date of birth? declaration: Add a declaration for people to agree to + delete_exit_page: Are you sure you want to delete this exit page? delete_secondary_skip: Are you sure you want to delete the route for any other answer? edit_exit_page: Edit exit page email_code_sent: Confirmation code sent error_prefix: 'Error: ' + exit_page_edit: Edit exit page exit_page_new: Add exit page exit_page_new_caption: Question %{question_number}’s exit pages forbidden: You cannot view this page @@ -1897,6 +1899,15 @@ en: destroy: success: The question, ‘%{question_text}’, has been deleted exit_pages: + delete: + back_link: Back to edit exit page + caption: 'Exit page %{exit_page_number}: %{heading}' + title: Are you sure you want to delete this exit page? + edit: + back_link: Back to edit question routes + delete_exit_page: Delete exit page + no_options_to_this_exit_page: No route to this exit page + options_to_this_exit_page: Options that go to this exit page new: back_link: Back to edit question routes heading: Edit question diff --git a/config/locales/input_objects/update_exit_pages.yml b/config/locales/input_objects/update_exit_pages.yml new file mode 100644 index 0000000000..3fecc8b811 --- /dev/null +++ b/config/locales/input_objects/update_exit_pages.yml @@ -0,0 +1,23 @@ +--- +en: + activemodel: + errors: + models: + pages/update_exit_page_input: + attributes: + heading: + blank: Enter a page heading + too_long: Page heading must be %{count} characters or less + markdown: + blank: Enter page content + too_long: Page content must be %{count} characters or less + unsupported_markdown_syntax: Page content can only contain formatting for links, subheadings (##), bulleted lists (*), or numbered lists (1.) + helpers: + hint: + pages_update_exit_page_input: + heading: Use a heading that summarises why someone cannot continue with the form. For example, ‘You’re not eligible for this service’. + markdown: Explain why they cannot continue to use the form and, if possible, tell them what they should do instead. + label: + pages_update_exit_page_input: + heading: Page heading + markdown: Page content diff --git a/config/routes.rb b/config/routes.rb index 537e7dda89..6063a23a94 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -160,7 +160,11 @@ end end - resources :exit_pages, only: %i[new create], path: "exit-pages", module: :pages do + resources :exit_pages, except: %i[index show], path: "exit-pages", module: :pages do + member do + get "/delete" => "exit_pages#delete", as: :delete + end + collection do post "/preview" => "exit_pages#render_preview", as: :render_preview end diff --git a/spec/input_objects/pages/exit_page_input_spec.rb b/spec/input_objects/pages/exit_page_input_spec.rb index 05e8da2dbc..7569f6507d 100644 --- a/spec/input_objects/pages/exit_page_input_spec.rb +++ b/spec/input_objects/pages/exit_page_input_spec.rb @@ -6,32 +6,8 @@ let(:heading) { "the heading" } let(:markdown) { "some markdown" } - describe "validations" do - it "is invalid if heading is nil" do - error_message = I18n.t("activemodel.errors.models.pages/exit_page_input.attributes.heading.blank") - exit_page_input.heading = nil - expect(exit_page_input).to be_invalid - expect(exit_page_input.errors.full_messages_for(:heading)).to include("Heading #{error_message}") - end - - it "is invalid if markdown is nil" do - error_message = I18n.t("activemodel.errors.models.pages/exit_page_input.attributes.markdown.blank") - exit_page_input.markdown = nil - expect(exit_page_input).to be_invalid - expect(exit_page_input.errors.full_messages_for(:markdown)).to include("Markdown #{error_message}") - end - - it "is invalid if heading is too long" do - error_message = I18n.t("activemodel.errors.models.pages/exit_page_input.attributes.heading.too_long", count: 250) - exit_page_input.heading = "a" * 251 - expect(exit_page_input).to be_invalid - expect(exit_page_input.errors.full_messages_for(:heading)).to include("Heading #{error_message}") - end - - it_behaves_like "a markdown field with headings allowed", :mark_complete do - let(:model) { exit_page_input } - let(:attribute) { :markdown } - end + it_behaves_like "validates exit pages" do + let(:model) { exit_page_input } end describe "#submit" do @@ -40,6 +16,7 @@ let(:heading) { "the heading" } let(:markdown) { "some markdown" } let(:page) { create :page } + let(:form) { page.form } it "returns a truthy value" do expect(exit_page_input.submit).to be_truthy @@ -55,6 +32,12 @@ expect(page.exit_pages.first.markdown).to eq(markdown) end + it "sets question_section_completed to false and updates the draft" do + form.question_section_completed = true + expect { exit_page_input.submit }.to change(form, :question_section_completed).from(true).to(false) + .and(change { form.draft_form_document.reload.updated_at }) + end + context "when the exit page is invalid" do let(:heading) { nil } diff --git a/spec/input_objects/pages/update_exit_page_input_spec.rb b/spec/input_objects/pages/update_exit_page_input_spec.rb new file mode 100644 index 0000000000..b9b6be59af --- /dev/null +++ b/spec/input_objects/pages/update_exit_page_input_spec.rb @@ -0,0 +1,47 @@ +require "rails_helper" + +RSpec.describe Pages::UpdateExitPageInput, type: :model do + subject(:exit_page_input) { described_class.new(heading:, markdown:) } + + let(:heading) { "the heading" } + let(:markdown) { "some markdown" } + + it_behaves_like "validates exit pages" do + let(:model) { exit_page_input } + end + + describe "#submit" do + subject(:exit_page_input) { described_class.new(heading:, markdown:, page:, exit_page:) } + + let(:heading) { "the heading" } + let(:markdown) { "some markdown" } + let(:form) { page.form } + let(:page) { create :page } + let(:exit_page) { create :exit_page, question_page: page } + + it "returns a truthy value" do + expect(exit_page_input.submit).to be_truthy + end + + it "updates the exit page" do + exit_page_input.submit + expect(exit_page.question_page).to eq(page) + expect(exit_page.heading).to eq(heading) + expect(exit_page.markdown).to eq(markdown) + end + + it "sets question_section_completed to false and updates the draft" do + form.question_section_completed = true + expect { exit_page_input.submit }.to change(form, :question_section_completed).from(true).to(false) + .and(change { form.draft_form_document.reload.updated_at }) + end + + context "when the exit page is invalid" do + let(:heading) { nil } + + it "returns false" do + expect(exit_page_input.submit).to be false + end + end + end +end diff --git a/spec/models/exit_page_spec.rb b/spec/models/exit_page_spec.rb index 4da57eae6d..959e7eb019 100644 --- a/spec/models/exit_page_spec.rb +++ b/spec/models/exit_page_spec.rb @@ -101,4 +101,23 @@ ) end end + + describe "#options_to_this_exit_page" do + it "when the exit_page has linked conditions, returns an array of the answer values of conditions that go to this exit page" do + question_page = create(:page) + exit_page = create(:exit_page, question_page:) + create(:condition, routing_page: question_page, exit_page_id: exit_page.id, answer_value: "option 1") + create(:condition, exit_page:, answer_value: "option 2") + create(:condition, exit_page:, answer_value: "option 3") + + expect(exit_page.options_to_this_exit_page).to eq(["option 1", "option 2", "option 3"]) + end + + it "when the exit_page has no linked conditions, returns an empty array" do + question_page = create(:page) + exit_page = create(:exit_page, question_page:) + + expect(exit_page.options_to_this_exit_page).to eq([]) + end + end end diff --git a/spec/models/form_spec.rb b/spec/models/form_spec.rb index 8140b71aeb..f66f33ab1f 100644 --- a/spec/models/form_spec.rb +++ b/spec/models/form_spec.rb @@ -666,6 +666,25 @@ }.to(change { form.reload.draft_form_document.content["updated_at"] }) end end + + context "when a block is passed" do + it "calls the block" do + expect { + form.save_question_changes! do + form.name = "New name" + end + }.to change { form.reload.name }.to("New name") + end + + it "does not update draft if the block fails" do + expect { + form.save_question_changes! do + form.name = "New name" + raise "Something went wrong" + end + }.to raise_error("Something went wrong").and(not_change { form.reload.draft_form_document.content["name"] }).and(not_change { form.reload.updated_at }) + end + end end describe "#save_draft!" do diff --git a/spec/requests/pages/exit_pages_controller_spec.rb b/spec/requests/pages/exit_pages_controller_spec.rb index 4da13932a4..ad611a422d 100644 --- a/spec/requests/pages/exit_pages_controller_spec.rb +++ b/spec/requests/pages/exit_pages_controller_spec.rb @@ -1,7 +1,7 @@ require "rails_helper" RSpec.describe Pages::ExitPagesController, :feature_multiple_branches, type: :request do - let(:form) { create(:form, :with_group, :with_pages, pages_count: 1, group:) } + let(:form) { create(:form, :with_group, :with_pages, pages: [create(:page, :with_selection_settings)], group:) } let(:page) { form.pages.first } let(:group) { create(:group, organisation: test_org, memberships: [create(:membership, user: standard_user)]) } @@ -75,6 +75,146 @@ end end + describe "#edit" do + let(:exit_page) { create(:exit_page, question_page: page) } + + before do + get edit_exit_page_path(form.id, page.id, exit_page.id) + end + + it "renders the template" do + expect(response).to have_rendered("exit_pages/edit") + expect(response.body).to include("Edit exit page") + end + + context "when the multiple branches feature is not enabled", feature_multiple_branches: false do + it "is forbidden" do + expect(response).to have_http_status(:not_found) + end + end + + context "when the user is not in the form's group" do + let(:group) { create(:group, organisation: test_org, memberships: []) } + + it "returns a forbidden status code" do + expect(response).to have_http_status :forbidden + end + end + end + + describe "#update" do + let(:exit_page) { create(:exit_page, question_page: page) } + + before do + patch exit_page_path(form.id, page.id, exit_page.id), params: { pages_update_exit_page_input: { heading: "Exit Page Heading", markdown: "Exit Page Markdown" } } + end + + it "updates the exit page" do + expect(response).to redirect_to(routes_path(form.id)) + expect(flash[:success]).to eq(I18n.t("banner.success.exit_page_saved")) + + expect(exit_page.reload.heading).to eq("Exit Page Heading") + expect(exit_page.reload.markdown).to eq("Exit Page Markdown") + end + + context "when the exit page is invalid" do + before do + patch exit_page_path(form.id, page.id, exit_page.id), params: { pages_update_exit_page_input: { heading: nil, markdown: nil } } + end + + it "renders the exit page template" do + expect(response).to have_http_status(:unprocessable_content) + expect(response).to render_template("exit_pages/edit") + end + end + + context "when the multiple branches feature is not enabled", feature_multiple_branches: false do + it "is forbidden" do + expect(response).to have_http_status(:not_found) + end + end + + context "when the user is not in the form's group" do + let(:group) { create(:group, organisation: test_org, memberships: []) } + + it "returns a forbidden status code" do + expect(response).to have_http_status :forbidden + end + end + end + + describe "#delete" do + let(:exit_page) { create(:exit_page, question_page: page) } + + before do + get delete_exit_page_path(form_id: form.id, page_id: page.id, id: exit_page.id) + end + + it "renders the template" do + expect(response).to have_rendered("exit_pages/delete") + expect(response.body).to include("Are you sure you want to delete this exit page?") + end + + context "when the multiple branches feature is not enabled", feature_multiple_branches: false do + it "is forbidden" do + expect(response).to have_http_status(:not_found) + end + end + + context "when the user is not in the form's group" do + let(:group) { create(:group, organisation: test_org, memberships: []) } + + it "returns a forbidden status code" do + expect(response).to have_http_status :forbidden + end + end + end + + describe "#destroy" do + subject!(:exit_page) { create(:exit_page, question_page: page) } + + let(:form) { super().tap { |f| f.question_section_completed = true } } + let(:exit_page_url) { exit_page_path(form_id: form.id, page_id: page.id, id: exit_page.id) } + let(:params) { { pages_delete_exit_page_input: { confirm: "yes" } } } + + it "deletes the exit page" do + expect { delete exit_page_url, params: }.to change(page.reload.exit_pages, :count).by(-1) + end + + it "sets question_section_completed to false and updates the draft" do + expect { delete exit_page_url, params: }.to change { form.reload.question_section_completed? }.from(true).to(false) + .and(change { form.draft_form_document.reload.updated_at }) + end + + it "redirects to the routes page" do + delete(exit_page_url, params:) + expect(response).to redirect_to(routes_path(form_id: form.id)) + end + + it "displays a success flash message" do + delete(exit_page_url, params:) + expect(flash[:success]).to eq(I18n.t("banner.success.exit_page_deleted")) + end + + context "when not confirmed" do + let(:params) { { pages_delete_exit_page_input: { confirm: "no" } } } + + it "redirects to the edit exit page page" do + delete(exit_page_url, params:) + expect(response).to redirect_to(edit_exit_page_path(form_id: form.id, page_id: page.id, id: exit_page.id)) + end + end + + context "when the params are invalid" do + let(:params) { { pages_delete_exit_page_input: { confirm: nil } } } + + it "returns an unprocessable content response" do + delete(exit_page_url, params:) + expect(response).to have_http_status :unprocessable_content + end + end + end + describe "#render_preview" do let(:markdown) { "[Markdown](https://example.com)" } diff --git a/spec/support/shared_examples/exit_page_validation.rb b/spec/support/shared_examples/exit_page_validation.rb new file mode 100644 index 0000000000..db965c3430 --- /dev/null +++ b/spec/support/shared_examples/exit_page_validation.rb @@ -0,0 +1,23 @@ +RSpec.shared_examples "validates exit pages" do + it "is invalid if heading is nil" do + model.heading = nil + expect(model).to be_invalid + expect(model.errors.where(:heading, :blank)).to be_present + end + + it "is invalid if markdown is nil" do + exit_page_input.markdown = nil + expect(exit_page_input).to be_invalid + expect(model.errors.where(:markdown, :blank)).to be_present + end + + it "is invalid if heading is too long" do + exit_page_input.heading = "a" * 251 + expect(exit_page_input).to be_invalid + expect(model.errors.where(:heading, :too_long)).to be_present + end + + it_behaves_like "a markdown field with headings allowed" do + let(:attribute) { :markdown } + end +end diff --git a/spec/views/pages/exit_pages/delete.html.erb_spec.rb b/spec/views/pages/exit_pages/delete.html.erb_spec.rb new file mode 100644 index 0000000000..4ea07acee5 --- /dev/null +++ b/spec/views/pages/exit_pages/delete.html.erb_spec.rb @@ -0,0 +1,43 @@ +require "rails_helper" + +RSpec.describe "pages/exit_pages/delete" do + let(:delete_confirmation_input) { Forms::DeleteConfirmationInput.new } + let(:current_form) { create :form } + let(:page) { create :page, form: current_form } + let(:exit_page) { create :exit_page, question_page: page, heading: "the heading" } + + before do + assign(:current_form, current_form) + render locals: { page:, exit_page:, delete_confirmation_input: } + end + + it "has a page title" do + expect(view.content_for(:title)).to include "Are you sure you want to delete this exit page?" + end + + it "has a heading" do + expect(rendered).to have_css "h1", text: "Are you sure you want to delete this exit page?" + end + + it "has a heading caption with the question text" do + expect(rendered).to have_css ".govuk-caption-l", text: "Exit page 1: the heading" + end + + it "has a back link to the edit exit page" do + expect(view.content_for(:back_link)).to have_link("Back to edit exit page", href: edit_exit_page_path(form_id: current_form.id, page_id: page.id, id: exit_page.id)) + end + + it "has a delete confirmation input to confirm deletion of the page" do + expect(rendered).to render_template "input_objects/_delete_confirmation_input" + end + + describe "delete confirmation input" do + it "posts to the destroy action" do + expect(rendered).to have_element "form", action: "/forms/#{current_form.id}/pages/#{page.id}/exit-pages/#{exit_page.id}", method: "post" + end + + it "does not have a hint" do + expect(rendered).not_to have_css ".govuk-hint" + end + end +end diff --git a/spec/views/pages/exit_pages/edit.html.erb_spec.rb b/spec/views/pages/exit_pages/edit.html.erb_spec.rb new file mode 100644 index 0000000000..a9853ba8e7 --- /dev/null +++ b/spec/views/pages/exit_pages/edit.html.erb_spec.rb @@ -0,0 +1,87 @@ +require "rails_helper" + +describe "exit_pages/edit.html.erb" do + let(:form) { build_stubbed :form, :with_pages, pages: [build_stubbed(:page, id: 1)] } + let(:page) { form.pages.first } + let(:exit_page) { build_stubbed(:exit_page, question_page: page) } + let(:exit_page_input) { Pages::UpdateExitPageInput.new(page:, exit_page:) } + + def render_page + assign(:current_form, form) + assign(:page, page) + render template: "pages/exit_pages/edit", locals: { exit_page_input:, preview_html: "", check_preview_validation: false } + end + + it "has the correct title" do + render_page + expect(view.content_for(:title)).to have_content("Edit exit page") + end + + it "has the correct back link" do + render_page + expect(view.content_for(:back_link)).to have_link("Back to edit question routes", href: routes_path(form.id)) + end + + it "has the correct heading and caption" do + render_page + expect(rendered).to have_selector "h1", text: "Question 1’s exit pages" + expect(rendered).to have_selector "h1", text: "Edit exit page" + end + + it "shows the page title as a summary list" do + render_page + expect(rendered).to have_css(".govuk-summary-list__key", text: "Question #{page.position}") + expect(rendered).to have_css(".govuk-summary-list__value", text: page.question_text) + end + + context "when the exit page has no options" do + before do + allow(exit_page).to receive(:options_to_this_exit_page).and_return([]) + end + + it "shows the page title as a summary list" do + render_page + + expect(rendered).to have_css(".govuk-summary-list__key", text: "Options that go to this exit page") + expect(rendered).to have_css(".govuk-summary-list__value", text: "No route to this exit page") + end + end + + context "when the exit page has has a single option" do + let(:options) { %w[option1] } + + before do + allow(exit_page).to receive(:options_to_this_exit_page).and_return(options) + end + + it "shows the page title as a summary list" do + render_page + + expect(rendered).to have_css(".govuk-summary-list__key", text: "Options that go to this exit page") + expect(rendered).to have_css(".govuk-summary-list__value:text()", text: "option1", exact: true) + end + end + + context "when the exit page has multiple options" do + let(:options) { %w[option1 option2 option3] } + + before do + allow(exit_page).to receive(:options_to_this_exit_page).and_return(options) + end + + it "shows the page title as a summary list" do + render_page + + expect(rendered).to have_css(".govuk-summary-list__key", text: "Options that go to this exit page") + expect(rendered).to have_css("li", text: options.first) + expect(rendered).to have_css("li", text: options.second) + expect(rendered).to have_css("li", text: options.third) + end + end + + it "shows error messages" do + exit_page_input.errors.add(:heading, "Error: Heading can't be blank") + render_page + expect(rendered).to have_text("Error: Heading can't be blank") + end +end