diff --git a/app/controllers/forms/exit_pages_controller.rb b/app/controllers/forms/exit_pages_controller.rb new file mode 100644 index 000000000..22f4f339f --- /dev/null +++ b/app/controllers/forms/exit_pages_controller.rb @@ -0,0 +1,10 @@ +module Forms + class ExitPagesController < PageController + def show + return redirect_to form_page_path(@step.form_id, @step.form_slug, current_context.next_page_slug) unless current_context.can_visit?(@step.page_slug) + + @back_link = form_page_path(@step.form_id, @step.form_slug, @step.page_slug) + @condition = @step.routing_conditions.first + end + end +end diff --git a/app/controllers/forms/page_controller.rb b/app/controllers/forms/page_controller.rb index 7b88ea75a..b8627a598 100644 --- a/app/controllers/forms/page_controller.rb +++ b/app/controllers/forms/page_controller.rb @@ -72,6 +72,7 @@ def back_link(page_slug) def redirect_post_save return redirect_to review_file_page, success: t("banner.success.file_uploaded") if answered_file_question? + return redirect_to exit_page_path(form_id: @step.form_id, form_slug: @step.form_slug, page_slug: @step.page_slug) if @step.exit_page_condition_matches? redirect_to next_page end diff --git a/app/models/step.rb b/app/models/step.rb index 96a794738..615a19ab2 100644 --- a/app/models/step.rb +++ b/app/models/step.rb @@ -79,6 +79,10 @@ def end_page? end def next_page_slug_after_routing + if exit_page_condition_matches? + return nil + end + if first_condition_default? return goto_condition_page_slug(routing_conditions.first) end @@ -106,6 +110,16 @@ def conditions_with_goto_errors end end + def has_exit_page_condition? + return false unless routing_conditions&.first.respond_to?(:exit_page_markdown) + + routing_conditions.first.exit_page_markdown.is_a?(String) + end + + def exit_page_condition_matches? + first_condition_matches? && has_exit_page_condition? + end + private def goto_condition_page_slug(condition) diff --git a/app/views/forms/exit_pages/show.html.erb b/app/views/forms/exit_pages/show.html.erb new file mode 100644 index 000000000..83f90ab4d --- /dev/null +++ b/app/views/forms/exit_pages/show.html.erb @@ -0,0 +1,13 @@ +<% set_page_title(form_title(form_name: @current_context.form.name, page_name: @condition.exit_page_heading, mode: @mode)) %> + +<% content_for :back_link do %> + <%= link_to "Back", @back_link, class: "govuk-back-link" %> +<% end %> + +
+
+

<%= @condition.exit_page_heading %>

+ <%= HtmlMarkdownSanitizer.new.render_scrubbed_markdown(@condition.exit_page_markdown) %> + <%= render SupportDetailsComponent::View.new(@support_details) %> +
+
diff --git a/config/routes.rb b/config/routes.rb index a4a20f12f..b340a2687 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -27,6 +27,10 @@ answer_constraints = { answer_index: /\d+/ } page_answer_defaults = { answer_index: 1 } + get "/:page_slug/exit" => "forms/exit_pages#show", + as: :exit_page, + constraints: page_constraints + get "/:page_slug/add-another-answer/change" => "forms/add_another_answer#show", as: :change_add_another_answer, constraints: page_constraints, diff --git a/spec/features/fill_in_form_with_exit_page_spec.rb b/spec/features/fill_in_form_with_exit_page_spec.rb new file mode 100644 index 000000000..0553e6c7c --- /dev/null +++ b/spec/features/fill_in_form_with_exit_page_spec.rb @@ -0,0 +1,105 @@ +require "rails_helper" + +feature "Fill in and submit a form with an exit page", type: :feature do + let(:routing_conditions) { [DataStruct.new(routing_page_id: 1, check_page_id: 1, answer_value: "Option 1", goto_page_id: nil, exit_page_heading: "This is an exit_page", exit_page_markdown: "This is the contents", validation_errors: [])] } + let(:steps) { [(build :v2_question_page_step, :with_selections_settings, id: 1, routing_conditions:, question_text:)] } + let(:form) { build :v2_form_document, :live?, id: 1, name: "Fill in this form", steps:, start_page: 1 } + let(:question_text) { Faker::Lorem.question } + let(:reference) { Faker::Alphanumeric.alphanumeric(number: 8).upcase } + + let(:req_headers) do + { + "X-API-Token" => Settings.forms_api.auth_key, + "Accept" => "application/json", + } + end + + let(:post_headers) do + { + "X-API-Token" => Settings.forms_api.auth_key, + "Content-Type" => "application/json", + } + end + + before do + ActiveResource::HttpMock.respond_to do |mock| + mock.get "/api/v2/forms/1/live", req_headers, form.to_json, 200 + end + + allow(ReferenceNumberService).to receive(:generate).and_return(reference) + end + + scenario "As a form filler" do + when_i_visit_the_form_start_page + then_i_should_see_the_first_question + + when_i_choose_the_exit_option + and_i_click_on_continue + then_i_should_see_the_exit_page + + when_i_click_back + when_i_dont_choose_the_exit_option + and_i_click_on_continue + then_i_should_see_the_check_your_answers_page + + when_i_opt_out_of_email_confirmation + and_i_submit_my_form + then_my_form_should_be_submitted + and_i_should_receive_a_reference_number + end + + def when_i_visit_the_form_start_page + visit form_path(mode: "form", form_id: 1, form_slug: "fill-in-this-form") + expect_page_to_have_no_axe_errors(page) + end + + def then_i_should_see_the_first_question + expect(page.find("h1")).to have_text question_text + end + + def when_i_choose_the_exit_option + choose "Option 1" + end + + def when_i_dont_choose_the_exit_option + choose "Option 2" + end + + def and_i_click_on_continue + click_button "Continue" + end + + def then_i_should_see_the_check_your_answers_page + expect(page.find("h1")).to have_text "Check your answers before submitting your form" + expect(page).to have_text question_text + expect(page).to have_text "Option 2" + expect_page_to_have_no_axe_errors(page) + end + + def when_i_click_back + click_on "Back" + end + + def then_i_should_see_the_exit_page + expect(page.find("h1")).to have_text "This is an exit_page" + expect(page).to have_text "This is the contents" + expect_page_to_have_no_axe_errors(page) + end + + def when_i_opt_out_of_email_confirmation + choose "No" + end + + def and_i_submit_my_form + click_on "Submit" + end + + def then_my_form_should_be_submitted + expect(page.find("h1")).to have_text "Your form has been submitted" + expect_page_to_have_no_axe_errors(page) + end + + def and_i_should_receive_a_reference_number + expect(page).to have_text reference + end +end diff --git a/spec/models/step_spec.rb b/spec/models/step_spec.rb index 34f5167d4..2eb5b2f92 100644 --- a/spec/models/step_spec.rb +++ b/spec/models/step_spec.rb @@ -420,4 +420,49 @@ end end end + + describe "#has_exit_page_condition?" do + it "returns false when no routing conditions" do + expect(step.has_exit_page_condition?).to be false + end + + it "returns false when first routing condition is not exit page" do + page.routing_conditions = [OpenStruct.new(answer_value: "Yes", goto_page_id: "5")] + expect(step.has_exit_page_condition?).to be false + end + + it "returns false when first routing condition contains markdown exit_page_markdown" do + page.routing_conditions = [OpenStruct.new(exit_page_markdown: 12)] + expect(step.has_exit_page_condition?).to be false + end + + it "returns true when first routing condition contains string markdown exit_page_markdown" do + page.routing_conditions = [OpenStruct.new(exit_page_markdown: "")] + expect(step.has_exit_page_condition?).to be true + end + end + + describe "#exit_page_condition_matches?" do + let(:selection) { "Yes" } + let(:question) { instance_double(Question::Selection, selection:) } + let(:routing_conditions) { [OpenStruct.new(answer_value: "Yes", exit_page_markdown: "string")] } + let(:page) { build(:page, id: 2, position: 1, routing_conditions:) } + + it "returns true when condition matches and condition is an exit page" do + expect(step.exit_page_condition_matches?).to be true + end + + it "when condition matches but not an exit page it returns false" do + routing_conditions.first.exit_page_markdown = nil + expect(step.exit_page_condition_matches?).to be false + end + + context "when condition doesn't match" do + let(:selection) { "No" } + + it "returns false" do + expect(step.exit_page_condition_matches?).to be false + end + end + end end diff --git a/spec/requests/forms/page_controller_spec.rb b/spec/requests/forms/page_controller_spec.rb index dba6da398..993093d92 100644 --- a/spec/requests/forms/page_controller_spec.rb +++ b/spec/requests/forms/page_controller_spec.rb @@ -651,6 +651,26 @@ end end end + + context "when the page is a an exit question" do + let(:first_step_in_form) do + build :v2_question_page_step, :with_selections_settings, + id: 1, + next_step_id: 2, + routing_conditions: [DataStruct.new(id: 1, routing_page_id: 1, check_page_id: 1, goto_page_id: nil, answer_value: "Option 1", validation_errors: [], exit_page_markdown: "Exit page markdown", exit_page_heading: "exit page heading")], + is_optional: false + end + + it "redirects to the exit page when exit page answer given" do + post save_form_page_path(mode:, form_id: 2, form_slug: form_data.form_slug, page_slug: 1, params: { question: { selection: "Option 1" }, changing_existing_answer: false }) + expect(response).to redirect_to exit_page_path(mode:, form_id: 2, form_slug: form_data.form_slug, page_slug: 1) + end + + it "redirects to the next step in the form when any other answer given" do + post save_form_page_path(mode:, form_id: 2, form_slug: form_data.form_slug, page_slug: 1, params: { question: { selection: "Option 2" }, changing_existing_answer: false }) + expect(response).to redirect_to form_page_path(mode:, form_id: 2, form_slug: form_data.form_slug, page_slug: 2) + end + end end def log_lines diff --git a/spec/views/forms/exit_pages/show.html.erb_spec.rb b/spec/views/forms/exit_pages/show.html.erb_spec.rb new file mode 100644 index 000000000..08236329e --- /dev/null +++ b/spec/views/forms/exit_pages/show.html.erb_spec.rb @@ -0,0 +1,38 @@ +require "rails_helper" + +describe "forms/exit_pages/show.html.erb" do + let(:form) { build :form, :with_support, id: 1, name: "exit page form" } + let(:mode) { OpenStruct.new(preview_draft?: false, preview_archived?: false, preview_live?: false) } + let(:condition) { OpenStruct.new({ exit_page_heading: "heading", exit_page_markdown: " * first line\n * second line\n" }) } + let(:support_details) { OpenStruct.new(email: form.support_email) } + + before do + assign(:current_context, OpenStruct.new(form:)) + assign(:mode, mode) + assign(:condition, condition) + assign(:back_link, "/back") + assign(:support_details, support_details) + + render + end + + it "has the correct title" do + expect(view.content_for(:title)).to eq "heading - exit page form" + end + + it "has a back link" do + expect(view.content_for(:back_link)).to have_link("Back", href: "/back") + end + + it "has the correct heading" do + expect(rendered).to have_css("h1", text: condition.exit_page_heading) + end + + it "displays the markdown" do + expect(rendered).to have_css("li", text: "second line") + end + + it "displays the help link" do + expect(rendered).to have_text(I18n.t("support_details.get_help_with_this_form")) + end +end