Skip to content
Open
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
2 changes: 1 addition & 1 deletion app/models/concerns/condition_methods.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module ConditionMethods
def is_exit_page?
!exit_page_markdown.nil?
!exit_page_markdown.nil? || try(:exit_page_id).present?
end

alias_method :exit_page?, :is_exit_page?
Expand Down
4 changes: 3 additions & 1 deletion app/models/condition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class Condition < ApplicationRecord
belongs_to :goto_page, class_name: "Page", optional: true

has_one :form, through: :routing_page
belongs_to :exit_page, optional: true

before_destroy :destroy_postconditions

Expand Down Expand Up @@ -101,11 +102,12 @@ def errors_with_fields
def as_json(options = {})
super(options.reverse_merge(
methods: %i[validation_errors has_routing_errors],
except: %i[exit_page_id],
))
end

def as_form_document_condition
data = as_json(methods: %i[validation_errors])
data = as_json(methods: %i[validation_errors], except: %i[exit_page_id])
data.merge(
"routing_page_id" => routing_page&.external_id,
"check_page_id" => check_page&.external_id,
Expand Down
17 changes: 17 additions & 0 deletions app/models/exit_page.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class ExitPage < ApplicationRecord
extend Mobility
belongs_to :question_page, class_name: "Page", optional: false

validates :heading, presence: true
validates :markdown, presence: true

translates :heading, :markdown

def as_form_document_exit_page
{
"id" => id,
"heading" => heading,
"markdown" => markdown,
}
end
end
1 change: 1 addition & 0 deletions app/models/form_document/condition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class FormDocument::Condition
attribute :exit_page_heading, :string
attribute :validation_errors, DataStructType.new
attribute :exit_page_markdown, :string
attribute :exit_page_id, :integer

def initialize(attributes = {})
attributes.slice!(*self.class.attribute_names)
Expand Down
16 changes: 12 additions & 4 deletions app/models/page.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ class Page < ApplicationRecord
has_many :routing_conditions, class_name: "Condition", foreign_key: "routing_page_id", dependent: :destroy
has_many :check_conditions, class_name: "Condition", foreign_key: "check_page_id", dependent: :destroy
has_many :goto_conditions, class_name: "Condition", foreign_key: "goto_page_id", dependent: :destroy
has_many :exit_pages, foreign_key: :question_page_id, dependent: :destroy

acts_as_list scope: :form

translates :question_text,
Expand Down Expand Up @@ -52,10 +54,15 @@ def destroy_and_update_form!
def save_and_update_form
return true unless has_changes_to_save?

save!
form.save_question_changes!
check_conditions.destroy_all if answer_type_changed_from_selection
check_conditions.destroy_all if answer_settings_changed_from_only_one_option
ActiveRecord::Base.transaction do
save!
form.save_question_changes!

if answer_type_changed_from_selection || answer_settings_changed_from_only_one_option
exit_pages.destroy_all
check_conditions.destroy_all
end
end

true
end
Expand Down Expand Up @@ -110,6 +117,7 @@ def as_form_document_step(next_page)
"type" => "question",
"data" => slice(*%w[question_text hint_text answer_type is_optional answer_settings page_heading guidance_markdown is_repeatable]),
"routing_conditions" => routing_conditions.map(&:as_form_document_condition),
"exit_pages" => exit_pages.map(&:as_form_document_exit_page),
}
end

Expand Down
10 changes: 10 additions & 0 deletions db/migrate/20260625153556_add_exit_page.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class AddExitPage < ActiveRecord::Migration[8.1]
def change
create_table :exit_pages do |t|
t.text :heading, comment: "The title used for the exit page "
t.text :markdown, comment: "The body content in markdown format"
t.belongs_to :question_page, foreign_key: { to_table: :pages, on_delete: :cascade }, null: false, comment: "The page that the exit page belongs to"
t.timestamps
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class CreateExitPagesHeadingAndMarkdownTranslationsForMobilityTableBackend < ActiveRecord::Migration[8.1]
def change
create_table :exit_page_translations do |t|
t.text :heading
t.text :markdown

t.string :locale, null: false
t.references :exit_page, null: false, foreign_key: true, index: false

t.timestamps null: false
end

add_index :exit_page_translations, :locale, name: :index_exit_page_translations_on_locale
add_index :exit_page_translations, %i[exit_page_id locale], name: :index_exit_page_translations_on_exit_page_id_and_locale, unique: true
end
end
5 changes: 5 additions & 0 deletions db/migrate/20260629145441_add_exit_page_to_condition.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddExitPageToCondition < ActiveRecord::Migration[8.1]
def change
add_reference :conditions, :exit_page, foreign_key: { to_table: :exit_pages }, null: true
end
end
27 changes: 26 additions & 1 deletion db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions spec/factories/models/conditions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
exit_page_heading { nil }
exit_page_markdown { nil }

# Define the association but we want to set it to nil by default
association :exit_page
exit_page_id { nil }

trait :with_exit_page do
goto_page { nil }
exit_page_heading { "Exit page heading" }
Expand Down
7 changes: 7 additions & 0 deletions spec/factories/models/exit_page.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FactoryBot.define do
factory :exit_page, class: "ExitPage" do
heading { Faker::Lorem.sentence }
markdown { Faker::Lorem.paragraph }
association :question_page, factory: :page
end
end
11 changes: 10 additions & 1 deletion spec/models/condition_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -264,13 +264,22 @@
end
end

context "when is_exit_page?" do
context "when is_exit_page? using exit_page_markdown" do
let(:condition) { create :condition, routing_page_id: routing_page.id, goto_page_id: nil, exit_page_markdown: "exit page" }

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

context "when exit_page_id is present using ExitPage" do
let(:exit_page) { create :exit_page }
let(:condition) { create :condition, routing_page_id: routing_page.id, goto_page_id: nil, exit_page_id: exit_page.id }

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

describe "#warning_answer_doesnt_exist" do
Expand Down
72 changes: 72 additions & 0 deletions spec/models/exit_page_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
require "rails_helper"

RSpec.describe ExitPage, type: :model do
it "has a valid factory" do
expect(build(:exit_page)).to be_valid
end

describe "validations" do
context "when heading is blank" do
it "is invalid" do
expect(build(:exit_page, heading: nil)).not_to be_valid
end
end

context "when markdown is blank" do
it "is invalid" do
expect(build(:exit_page, markdown: nil)).not_to be_valid
end
end
end

describe "associations" do
let!(:question_page) { create(:page) }
let!(:exit_page) { create(:exit_page, question_page:) }

it "has a question page" do
expect(exit_page.question_page).to eq(question_page)
end

it "is deleted when the question page is deleted" do
expect { question_page.destroy! }.to change(described_class, :count).by(-1)
end

it "the page has exit pages" do
expect(question_page.exit_pages).to eq([exit_page])
end
end

describe "translations" do
let!(:question_page) { create(:page) }
let!(:exit_page) { create(:exit_page, question_page:) }

it "can set and read translated attributes for :en and :cy locales" do
exit_page.heading = "English heading"
exit_page.markdown = "English markdown"

exit_page.heading_cy = "Welsh heading"
exit_page.markdown_cy = "Welsh markdown"
exit_page.save!

exit_page.reload
expect(exit_page.heading).to eq("English heading")
expect(exit_page.heading_cy).to eq("Welsh heading")

expect(exit_page.markdown).to eq("English markdown")
expect(exit_page.markdown_cy).to eq("Welsh markdown")
end
end

describe "#as_form_document_exit_page" do
let!(:question_page) { create(:page) }
let!(:exit_page) { create(:exit_page, question_page:) }

it "returns a hash" do
expect(exit_page.as_form_document_exit_page).to be_a(Hash)
end

it "returns the exit page attributes" do
expect(exit_page.as_form_document_exit_page).to match a_hash_including("id" => exit_page.id, "heading" => exit_page.heading, "markdown" => exit_page.markdown)
end
end
end
2 changes: 1 addition & 1 deletion spec/models/form_document/condition_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
end

it "has all the attributes the original condition has" do
expect(form_document_condition.attributes.except("routing_page_id")).to include(condition.attributes.except("routing_page_id"))
expect(form_document_condition.attributes.except("routing_page_id")).to include(condition.attributes.except("routing_page_id", "exit_page_id"))
expect(form_document_condition.routing_page_id).to eq(condition.routing_page.external_id)
end

Expand Down
Loading