Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion app/controllers/forms/exit_pages_controller.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
module Forms
class ExitPagesController < StepController
def show
return redirect_to form_step_path(@form.id, @form.form_slug, current_context.next_step_slug) unless current_context.can_visit?(@step.id)
unless current_context.can_visit?(@step.id) && @step.exit_page_condition_matches?
return redirect_to form_step_path(@form.id, @form.form_slug, current_context.next_step_slug)
end

@back_link = form_step_path(@form.id, @form.form_slug, @step.id)
@condition = @step.routing_conditions.first

if @condition.new_style_exit_page?
@exit_page = @step.exit_pages.find { it.id == @condition.exit_page_id }
raise KeyError, "Couldn't find ExitPage with id=#{@condition.exit_page_id}" if @exit_page.nil?
else
@exit_page = ExitPage.new(
id: nil,
heading: @condition.exit_page_heading,
markdown: @condition.exit_page_markdown,
)
end
end
end
end
15 changes: 10 additions & 5 deletions app/models/condition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ class Condition
delegate(
:id,
:answer_value,
:exit_page_id,
:exit_page_heading,
:exit_page_markdown,
:validation_errors,
Expand All @@ -28,7 +27,11 @@ def check_page_id
end

def goto_page_id
form_document_condition.goto_page_id.to_s
form_document_condition.goto_page_id&.to_s
end

def exit_page_id
form_document_condition.try(:exit_page_id)&.to_s
end

def default?
Expand All @@ -41,10 +44,12 @@ def match?(answer_value)
self.answer_value == answer_value
end

def exit_page?
return false unless form_document_condition.respond_to?(:exit_page_markdown)
def new_style_exit_page?
form_document_condition.respond_to?(:exit_page_id)
end

form_document_condition.try(:exit_page_id).present? || form_document_condition.exit_page_markdown.is_a?(String)
def exit_page?
form_document_condition.try(:exit_page_id).present? || form_document_condition.try(:exit_page_markdown).is_a?(String)
end

def skip_to_end?
Expand Down
3 changes: 2 additions & 1 deletion app/models/exit_page.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ class ExitPage
attr_reader :id, :heading, :markdown

def initialize(id:, heading:, markdown:)
@id = id
@id = id.nil? ? id : id.to_s
@heading = heading
@markdown = markdown
end
Expand All @@ -18,6 +18,7 @@ def self.from_form_document(form_document_exit_page)
def ==(other)
super ||
other.class == self.class &&
!!id &&
other.id == id
end
end
6 changes: 3 additions & 3 deletions app/views/forms/exit_pages/show.html.erb
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<% set_page_title(form_title(form_name: @current_context.form.name, page_name: @condition.exit_page_heading, mode: @mode)) %>
<% set_page_title(form_title(form_name: @current_context.form.name, page_name: @exit_page.heading, mode: @mode)) %>

<% content_for :back_link do %>
<%= link_to t("forms.back"), @back_link, class: "govuk-back-link" %>
<% end %>

<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<h1 class="govuk-heading-l"> <%= @condition.exit_page_heading %></h1>
<%= HtmlMarkdownSanitizer.new.render_scrubbed_markdown(@condition.exit_page_markdown) %>
<h1 class="govuk-heading-l"> <%= @exit_page.heading %></h1>
<%= HtmlMarkdownSanitizer.new.render_scrubbed_markdown(@exit_page.markdown) %>
<%= render SupportDetailsComponent::View.new(@support_details) %>
</div>
</div>
1 change: 1 addition & 0 deletions spec/factories/v2_step.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
sequence(:position) { |n| n }
next_step_id { nil }

exit_pages { [] }
routing_conditions { [] }

type { nil }
Expand Down
5 changes: 3 additions & 2 deletions spec/features/fill_in_form_with_exit_page_spec.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
require "rails_helper"

feature "Fill in and submit a form with an exit page", type: :feature do
let(:routing_conditions) { [build(:v2_condition, :with_exit_page, routing_page_id: 1, answer_value: "Option 1", exit_page_heading: "This is an exit_page", exit_page_markdown: "This is the contents")] }
let(:steps) { [build(:v2_selection_question_step, id: 1, routing_conditions:, question_text:)] }
let(:exit_page) { build(:v2_exit_page, heading: "This is an exit_page", markdown: "This is the contents") }
let(:routing_conditions) { [build(:v2_condition, :with_exit_page, routing_page_id: 1, answer_value: "Option 1", exit_page:)] }
let(:steps) { [build(:v2_selection_question_step, :with_exit_page, id: 1, routing_conditions:, question_text:, exit_page:)] }
let(:form) { build :v2_form_document, :live, form_id: 1, name: "Fill in this form", steps:, start_page: 1, send_copy_of_answers: "enabled" }
let(:question_text) { Faker::Lorem.question }
let(:reference) { Faker::Alphanumeric.alphanumeric(number: 8).upcase }
Expand Down
106 changes: 106 additions & 0 deletions spec/models/condition_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,38 @@
end
end

describe "#goto_page_id" do
context "when goto_page_id is nil" do
let(:form_document_condition) { build(:v2_condition, goto_page_id: nil) }

it "returns nil" do
expect(condition.goto_page_id).to be_nil
end
end
end

describe "#exit_page_id" do
context "when form document does not have exit_page_id" do
let(:form_document_condition) do
condition = build(:v2_condition, exit_page_id: nil)
condition.attributes.delete(:exit_page_id)
condition
end

it "returns nil" do
expect(condition.exit_page_id).to be_nil
end
end

context "when exit_page_id is nil" do
let(:form_document_condition) { build(:v2_condition, exit_page_id: nil) }

it "returns nil" do
expect(condition.exit_page_id).to be_nil
end
end
end

describe "#default?" do
context "when condition.answer_value is nil" do
let(:form_document_condition) do
Expand Down Expand Up @@ -131,6 +163,30 @@
end
end

describe "#new_style_exit_page?" do
context "when condition has exit_page_id attribute" do
let(:form_document_condition) do
build(:v2_condition, exit_page_id: nil)
end

it "returns true" do
expect(condition.new_style_exit_page?).to be true
end
end

context "when condition does not have exit_page_id attribute" do
let(:form_document_condition) do
condition = build(:v2_condition, exit_page_id: nil)
condition.attributes.delete(:exit_page_id)
condition
end

it "returns false" do
expect(condition.new_style_exit_page?).to be false
end
end
end

describe "#exit_page?" do
context "when condition has goto page id" do
let(:form_document_condition) do
Expand All @@ -143,6 +199,31 @@
it "returns false" do
expect(condition.exit_page?).to be false
end

context "and does not have exit_page_id attribute" do
let(:form_document_condition) do
condition = build(:v2_condition, goto_page_id: Faker::Alphanumeric.alphanumeric(number: 8))
condition.attributes.delete(:exit_page_id)
condition
end

it "returns false" do
expect(condition.exit_page?).to be false
end
end

context "and does not have exit page content attributes" do
let(:form_document_condition) do
condition = build(:v2_condition, goto_page_id: Faker::Alphanumeric.alphanumeric(number: 8))
condition.attributes.delete(:exit_page_heading)
condition.attributes.delete(:exit_page_markdown)
condition
end

it "returns false" do
expect(condition.exit_page?).to be false
end
end
end

context "when condition has exit_page_id" do
Expand All @@ -156,6 +237,19 @@
it "returns true" do
expect(condition.exit_page?).to be true
end

context "and does not have exit page content attributes" do
let(:form_document_condition) do
condition = build(:v2_condition, exit_page_id: 10)
condition.attributes.delete(:exit_page_heading)
condition.attributes.delete(:exit_page_markdown)
condition
end

it "returns true" do
expect(condition.exit_page?).to be true
end
end
end

context "when condition has exit page content" do
Expand All @@ -170,6 +264,18 @@
it "returns true" do
expect(condition.exit_page?).to be true
end

context "and does not have exit_page_id attribute" do
let(:form_document_condition) do
condition = build(:v2_condition, exit_page_heading: Faker::Lorem.sentence, exit_page_markdown: Faker::Lorem.paragraph)
condition.attributes.delete(:exit_page_id)
condition
end

it "returns true" do
expect(condition.exit_page?).to be true
end
end
end
end

Expand Down
6 changes: 3 additions & 3 deletions spec/models/exit_page_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
describe "#initialize" do
it "sets the attributes" do
exit_page = described_class.new(
id: 1,
id: "1",
heading: "You are not elegible for this service",
markdown: "Here’s what to do next: ...",
)

expect(exit_page).to have_attributes(
id: 1,
id: "1",
heading: "You are not elegible for this service",
markdown: "Here’s what to do next: ...",
)
Expand All @@ -24,7 +24,7 @@

expect(exit_page).to be_an described_class
expect(exit_page).to have_attributes(
id: form_document_exit_page.id,
id: form_document_exit_page.id.to_s,
heading: form_document_exit_page.heading,
markdown: form_document_exit_page.markdown,
)
Expand Down
114 changes: 114 additions & 0 deletions spec/requests/forms/exit_pages_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
require "rails_helper"

RSpec.describe Forms::ExitPagesController, type: :request do
let(:exit_page) { build(:v2_exit_page) }
let(:steps) { [first_step_in_form, step_with_exit_page, next_step_in_form] }
let(:first_step_in_form) { build(:v2_question_step, :with_text_settings, id: 1, next_step_id: 2) }
let(:step_with_exit_page) { build(:v2_selection_question_step, :with_exit_page, id: 2, next_step_id: 3, exit_page:) }
let(:next_step_in_form) { build(:v2_question_step, id: 3, next_step_id: nil) }
let(:form) { build(:v2_form_document, steps:, start_page: 1) }

let(:answer) { "Option 1" }

let(:store) do
{
answers: {
form.form_id.to_s => {
first_step_in_form.id.to_s => { text: "first answer" },
step_with_exit_page.id.to_s => { selection: answer },
},
},
}
end

before do
ActiveResource::HttpMock.respond_to do |mock|
mock.get "/api/v2/forms/#{form.form_id}/live", { "Accept" => "application/json" }, form.to_json, 200
end

allow(Flow::Context).to receive(:new).and_wrap_original do |original_method, **kwargs|
original_method.call(**kwargs, store:)
end
end

describe "GET #show" do
it "returns http success" do
get exit_page_path(mode: "form", form_id: form.form_id, form_slug: form.form_slug, step_slug: step_with_exit_page.id)
expect(response).to have_http_status(:success)
end

it "renders an exit page" do
get exit_page_path(mode: "form", form_id: form.form_id, form_slug: form.form_slug, step_slug: step_with_exit_page.id)
expect(response).to render_template(:show)
expect(assigns(:exit_page)).to eq ExitPage.from_form_document(exit_page)
end

context "when the question with an exit page has been answered and the exit page should not be shown" do
let(:answer) { "Option 2" }

it "redirects to the next unanswered question" do
get exit_page_path(mode: "form", form_id: form.form_id, form_slug: form.form_slug, step_slug: step_with_exit_page.id)
expect(response).to redirect_to form_step_path(mode: "form", form_id: form.form_id, form_slug: form.form_slug, step_slug: next_step_in_form.id)
end
end

context "when the form filler has not answered any questions" do
let(:store) { { answers: {} } }

it "redirects to the start of the form" do
get exit_page_path(mode: "form", form_id: form.form_id, form_slug: form.form_slug, step_slug: step_with_exit_page.id)
expect(response).to redirect_to form_step_path(mode: "form", form_id: form.form_id, form_slug: form.form_slug, step_slug: first_step_in_form.id)
end
end

context "when the step does not have an exit page" do
let(:step_without_exit_page) { build(:v2_selection_question_step, id: 2, next_step_id: 3) }
let(:step_with_exit_page) { step_without_exit_page }

it "redirects to the next unanswered question" do
get exit_page_path(mode: "form", form_id: form.form_id, form_slug: form.form_slug, step_slug: step_without_exit_page.id)
expect(response).to redirect_to form_step_path(mode: "form", form_id: form.form_id, form_slug: form.form_slug, step_slug: next_step_in_form.id)
end
end
end

context "when the exit page is missing from the form document" do
let(:condition_with_exit_page) { build(:v2_condition, :with_exit_page, routing_page_id: 2, exit_page:) }
let(:step_without_exit_page) { build(:v2_selection_question_step, routing_conditions: [condition_with_exit_page], id: 2, next_step_id: 3) }
let(:step_with_exit_page) { step_without_exit_page }

it "raises an error" do
expect {
get exit_page_path(mode: "form", form_id: form.form_id, form_slug: form.form_slug, step_slug: step_with_exit_page.id)
}.to raise_error(/Couldn't find ExitPage/)
end
end

context "when the form document is using old-style exit pages" do
let(:exit_page_heading) { Faker::Lorem.sentence }
let(:exit_page_markdown) { Faker::Lorem.paragraph }

let(:condition_with_exit_page) do
condition = build(:v2_condition, routing_page_id: 2, goto_page_id: nil, skip_to_end: nil, exit_page_id: nil, exit_page_heading:, exit_page_markdown:)
condition.attributes.delete(:exit_page_id)
condition
end

let(:step_without_exit_page) do
step = build(:v2_selection_question_step, routing_conditions: [condition_with_exit_page], id: 2, next_step_id: 3)
step.attributes.delete(:exit_pages)
step
end

let(:step_with_exit_page) { step_without_exit_page }

it "falls back to the exit page content in the condition" do
get exit_page_path(mode: "form", form_id: form.form_id, form_slug: form.form_slug, step_slug: step_with_exit_page.id)
expect(response).to render_template(:show)
expect(assigns(:exit_page)).to have_attributes(
heading: exit_page_heading,
markdown: exit_page_markdown,
)
end
end
end
Loading