From 08252051ab0577d57dd575b8c9f74ee936f5c573 Mon Sep 17 00:00:00 2001 From: Samuel Culley Date: Thu, 30 Apr 2026 09:58:40 +0100 Subject: [PATCH 1/2] Display multiple branches on add/edit page This is based on the Group's multiple_branches_enabled feature flag - so if this is toggled off, then it will use the original add and edit page view. When the flag is turned on for the group, the display logic assumes that the form has been created using the new Routes process, and will not be particularly compatible with forms that have been created using the old routing pages (although it might work if the routing is relatively simple). There is no validation displayed for these changes, as we're not ready to start deciding how to validate the new multiple-branch routes. --- .../page_list_component/view.html.erb | 68 +++++++++++------ app/components/page_list_component/view.rb | 8 ++ app/views/pages/index.html.erb | 4 +- config/locales/en.yml | 3 + .../page_list_component_preview.rb | 40 ++++++++-- .../page_list_component/view_spec.rb | 75 ++++++++++++++++++- spec/views/pages/index.html.erb_spec.rb | 2 +- 7 files changed, 169 insertions(+), 31 deletions(-) diff --git a/app/components/page_list_component/view.html.erb b/app/components/page_list_component/view.html.erb index 9cdccdd8de..db47a2d965 100644 --- a/app/components/page_list_component/view.html.erb +++ b/app/components/page_list_component/view.html.erb @@ -32,33 +32,57 @@ - <% page.routing_conditions.each do |condition| %> - <% condition_page = self.condition_page(condition) %> - <% condition_page_position = self.condition_page_position(condition) %> -
"> -
- <%= t("page_conditions.condition_name", question_number: condition_page_position) %> + <% if FeatureService.new(group: @form.group).enabled?(:multiple_branches) && page.routing_conditions.present? %> +
+
+ <%= t("page_conditions.condition_name", question_number: page.position) %>
-
-
    - <% condition.validation_errors.each do |error| %> -
  • - <%= PageListComponent::ErrorSummary::View.generate_error_message(error.name, condition:, page: condition_page) %> -
  • - <% end %> -
- -

- <%= condition_description(condition) %> -

- -
- <%= govuk_link_to show_routes_path(form_id: @form.id, page_id: condition.check_page_id) do %> - <%= t("forms.form_overview.edit_with_visually_hidden_text_html", visually_hidden_text: t("page_conditions.condition_name", question_number: condition_page_position)) %> + <% if page.routing_conditions.first.answer_value.present? %> +

<%= I18n.t("page_conditions.if_answer_is") %>

+
    + <% page.routing_conditions.each do |condition| %> +
  • + <%= condition_description2(condition) %> +
  • + <% end %> +
+ <% else %> +

+ <%= condition_description2(page.routing_conditions.first) %> +

<% end %>
+ <% else %> + <% page.routing_conditions.each do |condition| %> + <% condition_page = self.condition_page(condition) %> + <% condition_page_position = self.condition_page_position(condition) %> +
"> +
+ <%= t("page_conditions.condition_name", question_number: condition_page_position) %> +
+ +
+
    + <% condition.validation_errors.each do |error| %> +
  • + <%= PageListComponent::ErrorSummary::View.generate_error_message(error.name, condition:, page: condition_page) %> +
  • + <% end %> +
+ +

+ <%= condition_description(condition) %> +

+ +
+ <%= govuk_link_to show_routes_path(form_id: @form.id, page_id: condition.check_page_id) do %> + <%= t("forms.form_overview.edit_with_visually_hidden_text_html", visually_hidden_text: t("page_conditions.condition_name", question_number: condition_page_position)) %> + <% end %> +
+
+ <% end %> <% end %> <% end %> diff --git a/app/components/page_list_component/view.rb b/app/components/page_list_component/view.rb index 86cd04f353..8d633a65a3 100644 --- a/app/components/page_list_component/view.rb +++ b/app/components/page_list_component/view.rb @@ -28,6 +28,14 @@ def condition_description(condition) end end + def condition_description2(condition) + if condition.answer_value.present? + I18n.t("page_conditions.condition_description2", goto_page_question_text: goto_page_text_for_condition(condition), answer_value: answer_value_text_for_condition(condition)) + else + I18n.t("page_conditions.unconditional_description", goto_page_question_text: goto_page_text_for_condition(condition)) + end + end + def condition_check_page_text(condition) check_page = @pages.find { |page| page.id == condition.check_page_id } I18n.t("page_conditions.condition_check_page_text", check_page_question_text: check_page.question_text) diff --git a/app/views/pages/index.html.erb b/app/views/pages/index.html.erb index f98839923f..8308f25645 100644 --- a/app/views/pages/index.html.erb +++ b/app/views/pages/index.html.erb @@ -4,7 +4,9 @@
- <%= render PageListComponent::ErrorSummary::View.new(current_form) %> + <% unless FeatureService.new(group: current_form.group).enabled?(:multiple_branches) %> + <%= render PageListComponent::ErrorSummary::View.new(current_form) %> + <% end %>

<%= current_form.name %> - diff --git a/config/locales/en.yml b/config/locales/en.yml index 7690e44d89..df8beeb31b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1367,6 +1367,7 @@ en: condition_compact_html_secondary_skip: Go to %{goto_page_question_number}, “%{goto_page_question_text}” condition_compact_html_secondary_skip_to_end_of_form: Go to end of form condition_description: If %{check_page_question_text} is answered as %{answer_value} go to %{goto_page_question_text} + condition_description2: "%{answer_value} go to %{goto_page_question_text}" condition_goto_exit_page: exit page, “%{exit_page_heading}” condition_goto_page_end_of_form: end of form. condition_goto_page_text: "%{goto_page_question_number}, “%{goto_page_question_text}”" @@ -1376,10 +1377,12 @@ en: end_of_form: End of form exit_page: Add an exit page exit_page_label: An ‘exit page’ to leave the form + if_answer_is: 'If the answer is:' none_of_the_above: None of the above route: Route secondary_skip_description: After %{check_page_question_text} go to %{goto_page_question_text} skip_condition_route_page_text: "%{route_page_question_number}, “%{route_page_question_text}”" + unconditional_description: Go to %{goto_page_question_text} page_route_card: any_other_answer: Route for any other answer conditional_answer_value: "%{answer_value}" diff --git a/spec/components/page_list_component/page_list_component_preview.rb b/spec/components/page_list_component/page_list_component_preview.rb index 5efd5bd837..80cfd6cecf 100644 --- a/spec/components/page_list_component/page_list_component_preview.rb +++ b/spec/components/page_list_component/page_list_component_preview.rb @@ -1,9 +1,11 @@ class PageListComponent::PageListComponentPreview < ViewComponent::Preview include FactoryBot::Syntax::Methods + # TODO: the :with_group trait for each form is only needed while the multiple_branches_enabled feature column is in + # use - we can remove it once we remove the feature flag column def default pages = [] - form = build(:form, id: 0, pages:) + form = build(:form, :with_group, id: 0, pages:) render(PageListComponent::View.new(pages:, form:)) end @@ -11,7 +13,7 @@ def with_pages_and_no_conditions pages = [build(:page, id: 1, position: 1, question_text: "Enter your name", routing_conditions: []), build(:page, id: 2, position: 2, question_text: "What is your pet's phone number?", routing_conditions: []), build(:page, id: 3, position: 3, question_text: "How many pets do you own?", routing_conditions: [])] - form = build(:form, id: 0, pages:) + form = build(:form, :with_group, id: 0, pages:) render(PageListComponent::View.new(pages:, form:)) end @@ -20,7 +22,7 @@ def with_pages_and_one_condition pages = [build(:page, id: 1, position: 1, question_text: "Enter your name", routing_conditions: [condition]), build(:page, id: 2, position: 2, question_text: "What is your pet's phone number?", routing_conditions: []), build(:page, id: 3, position: 3, question_text: "How many pets do you own?", routing_conditions: [])] - form = build(:form, id: 0, pages:) + form = build(:form, :with_group, id: 0, pages:) # We need to build the records rather than create them so that we don't save them to the database when we view the # preview. However, this means that the associations aren't available so we need to manually set the associations @@ -41,7 +43,7 @@ def with_pages_and_multiple_conditions pages = [(build :page, id: 1, position: 1, question_text: "Enter your name", routing_conditions: routing_conditions_1), (build :page, id: 2, position: 2, question_text: "What is your pet's phone number?", routing_conditions: routing_conditions_2), (build :page, id: 3, position: 3, question_text: "How many pets do you own?", routing_conditions: [])] - form = build(:form, id: 0, pages:) + form = build(:form, :with_group, id: 0, pages:) # We need to build the records rather than create them so that we don't save them to the database when we view the # preview. However, this means that the associations aren't available so we need to manually set the associations @@ -65,7 +67,7 @@ def with_pages_and_conditions_with_errors pages = [(build :page, id: 1, position: 1, question_text: "Enter your name", routing_conditions: routing_conditions_1), (build :page, id: 2, position: 2, question_text: "What is your pet's phone number?", routing_conditions: routing_conditions_2), (build :page, id: 3, position: 3, question_text: "How many pets do you own?", routing_conditions: [])] - form = build(:form, id: 1, pages:) + form = build(:form, :with_group, id: 1, pages:) # We need to build the records rather than create them so that we don't save them to the database when we view the # preview. However, this means that the associations aren't available so we need to manually set the associations @@ -79,4 +81,32 @@ def with_pages_and_conditions_with_errors render(PageListComponent::View.new(pages:, form:)) end + + def with_multiple_branches + routing_conditions_1 = [ + (build :condition, id: 1, routing_page_id: 1, check_page_id: nil, goto_page_id: 3, answer_value: "Wales"), + (build :condition, id: 2, routing_page_id: 1, check_page_id: nil, goto_page_id: 4, answer_value: "England"), + (build :condition, id: 3, routing_page_id: 1, check_page_id: nil, goto_page_id: 5, answer_value: "Scotland"), + ] + + routing_conditions_2 = [build(:condition, id: 4, routing_page_id: 2, check_page_id: nil, answer_value: "Don't know", goto_page_id: nil, skip_to_end: true)] + + pages = [ + (build :page, id: 1, position: 1, question_text: "What country are you in?", routing_conditions: routing_conditions_1), + (build :page, id: 2, position: 2, question_text: "What is your pet's phone number?", routing_conditions: routing_conditions_2), + (build :page, id: 3, position: 3, question_text: "How many pets do you own?", routing_conditions: []), + (build :page, id: 4, position: 4, question_text: "What kind of pet do you want?", routing_conditions: []), + ] + + form = build(:form, :with_group, id: 1, pages:) + form.group.multiple_branches_enabled = true + + (routing_conditions_1 + routing_conditions_2).each do |condition| + condition.routing_page = pages.select { |page| page.id == condition.routing_page_id }.first + condition.goto_page = pages.select { |page| page.id == condition.goto_page_id }.first + condition.form = form + end + + render(PageListComponent::View.new(pages:, form:)) + end end diff --git a/spec/components/page_list_component/view_spec.rb b/spec/components/page_list_component/view_spec.rb index 35ffd05979..4a83b4fb21 100644 --- a/spec/components/page_list_component/view_spec.rb +++ b/spec/components/page_list_component/view_spec.rb @@ -5,7 +5,7 @@ let(:pages) { form.reload.pages } let(:page_list_component) { described_class.new(pages:, form:) } - describe "rendering component" do + describe "rendering component", feature_multiple_branches: false do context "when there are no pages" do before do render_inline(page_list_component) @@ -182,7 +182,7 @@ end end - describe "class methods" do + describe "class methods", feature_multiple_branches: false do let(:form) { create :form, :with_pages } describe "show_up_button" do @@ -322,5 +322,76 @@ end end end + + describe "#condition_description2", :feature_multiple_branches do + context "when condition has all values set" do + let(:condition) do + create( + :condition, + routing_page_id: pages.first.id, + check_page_id: pages.first.id, + answer_value: "Option 1", + goto_page_id: pages.third.id, + ) + end + + it "returns complete condition description" do + expected_text = "“#{condition.answer_value}” go to #{pages.third.position}, “#{pages.third.question_text}”" + expect(page_list_component.condition_description2(condition)).to eq(expected_text) + end + end + + context "when answer value is 'none_of_the_above'" do + let(:condition) do + create( + :condition, + routing_page_id: pages.first.id, + check_page_id: pages.first.id, + answer_value: "none_of_the_above", + goto_page_id: pages.third.id, + ) + end + + it "returns description with 'None of the above' text" do + expected_text = "“None of the above” go to #{pages.third.position}, “#{pages.third.question_text}”" + expect(page_list_component.condition_description2(condition)).to eq(expected_text) + end + end + + context "when skip_to_end is true" do + let(:condition) do + create( + :condition, + routing_page_id: pages.first.id, + check_page_id: pages.first.id, + answer_value: "Option 1", + goto_page_id: nil, + skip_to_end: true, + ) + end + + it "returns description with 'Check your answers' text" do + expected_text = "“#{condition.answer_value}” go to end of form." + expect(page_list_component.condition_description2(condition)).to eq(expected_text) + end + end + + context "when showing an unconditional route" do + let(:condition) do + create( + :condition, + routing_page_id: pages.second.id, + check_page_id: pages.first.id, + answer_value: nil, + goto_page_id: pages.fourth.id, + ) + end + + it "returns correct description" do + expected_text = "Go to #{pages.fourth.position}, “#{pages.fourth.question_text}”" + expect(page_list_component.condition_description2(condition)).to eq(expected_text) + end + end + end end end diff --git a/spec/views/pages/index.html.erb_spec.rb b/spec/views/pages/index.html.erb_spec.rb index ce38b3f482..d5ab92d7e2 100644 --- a/spec/views/pages/index.html.erb_spec.rb +++ b/spec/views/pages/index.html.erb_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -describe "pages/index.html.erb" do +describe "pages/index.html.erb", :feature_multiple_branches do let(:form) { create :form, pages: } let(:pages) { [] } let(:mark_complete_input) { Forms::MarkPagesSectionCompleteInput.new(form:).assign_form_values } From 95ae7a4c871083744b7f5d82c5bbc830fb3f77b0 Mon Sep 17 00:00:00 2001 From: Samuel Culley Date: Tue, 5 May 2026 14:34:29 +0100 Subject: [PATCH 2/2] Update feature flag check for routes page --- app/views/pages/index.html.erb | 2 +- spec/views/pages/index.html.erb_spec.rb | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/views/pages/index.html.erb b/app/views/pages/index.html.erb index 8308f25645..7298088180 100644 --- a/app/views/pages/index.html.erb +++ b/app/views/pages/index.html.erb @@ -22,7 +22,7 @@ <% end %>
<%= govuk_button_link_to t("pages.index.add_question"), start_new_question_path(form_id: current_form.id), class:"govuk-!-margin-bottom-3 govuk-!-margin-top-3" %> - <% if current_form.group&.multiple_branches_enabled %> + <% if FeatureService.new(group: current_form.group).enabled?(:multiple_branches) %> <%= govuk_button_link_to t("pages.index.routes"), routes_path(current_form.id), secondary: true, class:"govuk-!-margin-bottom-3 govuk-!-margin-top-3" %> <% else %> <%= govuk_button_link_to t("pages.index.add_a_question_route"), routing_page_path(current_form.id), secondary: true, class:"govuk-!-margin-bottom-3 govuk-!-margin-top-3" %> diff --git a/spec/views/pages/index.html.erb_spec.rb b/spec/views/pages/index.html.erb_spec.rb index d5ab92d7e2..e67bd2d516 100644 --- a/spec/views/pages/index.html.erb_spec.rb +++ b/spec/views/pages/index.html.erb_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -describe "pages/index.html.erb", :feature_multiple_branches do +describe "pages/index.html.erb", feature_multiple_branches: false do let(:form) { create :form, pages: } let(:pages) { [] } let(:mark_complete_input) { Forms::MarkPagesSectionCompleteInput.new(form:).assign_form_values } @@ -43,9 +43,8 @@ end end - describe "when the group has multiple branches enabled" do - let(:group) { create(:group, multiple_branches_enabled: true) } - let(:form) { create(:form, :with_group, group:) } + describe "when the multiple branches feature is enabled", :feature_multiple_branches do + let(:form) { create(:form) } it "has a link to add a page routing" do expect(rendered).to have_link("Edit question routes", href: routes_path(form.id))