Skip to content

Commit 3e3650d

Browse files
authored
Merge pull request #2776 from govuk-forms/ldeb-multiple-branches-routes-build-service-options-for-page-only-following
Don't allow goto page to be before routing page for show/edit routes page
2 parents 4736959 + a7d5aae commit 3e3650d

6 files changed

Lines changed: 173 additions & 27 deletions

File tree

app/input_objects/forms/routes_input.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ def routes_attributes=(attributes)
3333
next unless page # Skip if page not found or doesn't belong to form
3434

3535
Forms::RouteInput.new(
36-
route_attrs.symbolize_keys.merge(page:, goto_options: route_build_service.options_for_page(page)),
36+
route_attrs.symbolize_keys.merge(
37+
page:,
38+
goto_options: route_build_service.options_for_goto_page(page, route_attrs["goto"]),
39+
),
3740
)
3841
}.compact
3942
end

app/services/routes/build_service.rb

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,31 @@ def build_routes
2525
end
2626
end
2727

28-
def options_for_page(page)
28+
def options_for_goto_page(page, selected = nil)
2929
next_page = form.next_page_after(page)
3030

3131
return [] unless next_page
3232

33-
# Don't include the current page in the options
34-
options_without_page = all_goto_options.reject { |_, value| value == page.id }
35-
36-
# Replace the next page with the default option
37-
options_without_page.map do |name, value|
38-
if value == next_page.id
39-
["Go to question #{page.position.next}", Forms::RouteInput::DEFAULT_VALUE]
33+
# Don't include the current page or pages before in the options,
34+
# unless the goto page for the existing condition is before the current page,
35+
# in which case do include that one. Also, change the option for the next page
36+
# to a different default option.
37+
next_page_id = next_page.id
38+
drop = true
39+
40+
all_goto_options.filter_map do |option|
41+
_, value = option
42+
43+
if drop
44+
if selected && value == selected
45+
option
46+
elsif value == next_page_id
47+
drop = false
48+
["Go to question #{page.position.next}", Forms::RouteInput::DEFAULT_VALUE]
49+
end
50+
# return nil
4051
else
41-
[name, value]
52+
option
4253
end
4354
end
4455
end
@@ -62,7 +73,7 @@ def build_routes_for_selection_page(page, conditions_by_key)
6273
page:,
6374
answer_value:,
6475
goto: goto_value_for(condition),
65-
goto_options: options_for_page(page),
76+
goto_options: options_for_goto_page(page, condition&.goto_page_id),
6677
label: { text: "Option #{index}: #{answer_value_label}" },
6778
)
6879
end
@@ -78,15 +89,19 @@ def build_route_for_generic_page(page, conditions_by_key)
7889
page_id: page.id,
7990
page:,
8091
goto: goto_value_for(condition),
81-
goto_options: options_for_page(page),
92+
goto_options: options_for_goto_page(page, condition&.goto_page_id),
8293
label: { text: "Go to", hidden: true },
8394
),
8495
]
8596
end
8697

98+
def option_for_select(page)
99+
["#{page.position}. #{page.question_text}", page.id]
100+
end
101+
87102
def all_goto_options
88103
@all_goto_options ||= begin
89-
page_opts = form.pages.map { |p| ["#{p.position}. #{p.question_text}", p.id] }
104+
page_opts = form.pages.map { |p| option_for_select(p) }
90105
page_opts + [END_OF_FORM_OPTION]
91106
end
92107
end

spec/factories/models/forms.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@
6363
add_welsh_translations_to_pages(form.pages) if form.available_languages.include?("cy")
6464
end
6565

66+
after(:stub) do |form|
67+
link_pages_list(form.pages) if form.pages.present?
68+
stub_conditions(form) if form.pages.present?
69+
add_welsh_translations_to_pages(form.pages) if form.available_languages.include?("cy")
70+
end
71+
6672
question_section_completed { true }
6773
end
6874

@@ -169,6 +175,14 @@ def link_pages_list(pages)
169175
pages
170176
end
171177

178+
def stub_conditions(form)
179+
form.instance_eval do
180+
def conditions
181+
pages.flat_map(&:routing_conditions)
182+
end
183+
end
184+
end
185+
172186
def add_welsh_translations_to_pages(pages)
173187
pages.each do |page|
174188
if page.question_text_cy.blank?

spec/input_objects/forms/routes_input_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060

6161
describe "#routes_attributes=" do
6262
let(:goto_options) { [["Go to page 2", pages.second.id]] }
63-
let(:route_build_service) { instance_double(Routes::BuildService, options_for_page: goto_options) }
63+
let(:route_build_service) { instance_double(Routes::BuildService, options_for_goto_page: goto_options) }
6464
let(:pages) { create_list(:page, 2) }
6565
let(:form) { create(:form, id: 1, pages:) }
6666

spec/services/routes/build_service_spec.rb

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@
181181
end
182182
end
183183

184-
describe "#options_for_page" do
184+
describe "#options_for_goto_page" do
185185
let(:pages) do
186186
create_list(:page, 3) do |page, i|
187187
page.id = 101 + i
@@ -191,11 +191,11 @@
191191
end
192192

193193
it "returns an empty array if the page has no next page" do
194-
expect(service.options_for_page(pages.third)).to be_empty
194+
expect(service.options_for_goto_page(pages.third)).to be_empty
195195
end
196196

197197
it "returns a list of all possible goto options" do
198-
options = service.options_for_page(pages.first)
198+
options = service.options_for_goto_page(pages.first)
199199

200200
expected_other_options = [
201201
["3. question 3", pages.third.id],
@@ -207,17 +207,38 @@
207207
end
208208

209209
it "replaces the next page with the default option" do
210-
options = service.options_for_page(pages.first)
210+
options = service.options_for_goto_page(pages.first)
211211
default_option = options.find { |opt| opt[1] == Forms::RouteInput::DEFAULT_VALUE }
212212

213213
expect(default_option).to eq(["Go to question 2", Forms::RouteInput::DEFAULT_VALUE])
214214
end
215215

216216
it "does not include the current page in the options" do
217-
options = service.options_for_page(pages.first)
217+
options = service.options_for_goto_page(pages.first)
218218
page_ids = options.map(&:second)
219219

220220
expect(page_ids).not_to include(pages.first.id)
221221
end
222+
223+
it "does not include pages before the current page in the options" do
224+
options = service.options_for_goto_page(pages.second)
225+
226+
expect(options).to eq [
227+
["Go to question 3", Forms::RouteInput::DEFAULT_VALUE],
228+
["End of the form", "end_of_form"],
229+
]
230+
end
231+
232+
context "when there is a selected goto page and the goto page is before the current page" do
233+
it "includes the goto page in the options" do
234+
options = service.options_for_goto_page(pages.second, pages.first.id)
235+
236+
expect(options).to eq [
237+
["1. #{pages.first.question_text}", pages.first.id],
238+
["Go to question 3", Forms::RouteInput::DEFAULT_VALUE],
239+
["End of the form", "end_of_form"],
240+
]
241+
end
242+
end
222243
end
223244
end

spec/views/routes/show.html.erb_spec.rb

Lines changed: 101 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
require "rails_helper"
22

33
describe "routes/show.html.erb" do
4-
let(:form) { build_stubbed :form, pages: }
4+
let(:form) { build_stubbed :form, :with_pages, pages: }
55
let(:pages) { [] }
6-
let(:routes) { [] }
7-
let(:routes_input) { build :routes_input, form:, routes: }
6+
let(:routes_input) { build(:routes_input, form:).assign_form_values }
87

98
def render_page
109
assign(:current_form, form)
@@ -29,15 +28,32 @@ def render_page
2928
end
3029

3130
context "when the form has pages and routes" do
32-
let(:pages) { build_stubbed_list(:page, 3) }
33-
let(:routes) do
31+
let(:pages) do
3432
[
35-
build(:route_input, page: pages.first, goto: pages.third.id, goto_options: []),
36-
build(:route_input, :default, page: pages.second, goto_options: []),
37-
build(:route_input, :default, page: pages.third, goto_options: []),
33+
build_stubbed(
34+
:page,
35+
id: 101,
36+
routing_conditions: [
37+
build_stubbed(
38+
:condition,
39+
routing_page_id: 101,
40+
goto_page_id: 103,
41+
answer_value: nil,
42+
),
43+
],
44+
),
45+
build_stubbed(:page, id: 102),
46+
build_stubbed(:page, id: 103),
3847
]
3948
end
4049

50+
it "has a summary list with a row for each page" do
51+
render_page
52+
expect(rendered).to have_selector(".govuk-summary-list") do |summary_list|
53+
expect(summary_list).to have_selector(".govuk-summary-list__row", count: pages.length)
54+
end
55+
end
56+
4157
it "displays the page's position and question text" do
4258
render_page
4359
expect(rendered).to have_selector(".govuk-summary-list__key", text: pages.first.position.to_s)
@@ -48,5 +64,82 @@ def render_page
4864
render_page
4965
expect(rendered).to have_selector("#page-#{pages.first.position}")
5066
end
67+
68+
it "has a select field for each page except the last one" do
69+
render_page
70+
expect(rendered).to have_selector('.govuk-select[name="forms_routes_input[routes_attributes][0][goto]"]')
71+
expect(rendered).to have_selector('.govuk-select[name="forms_routes_input[routes_attributes][1][goto]"]')
72+
expect(rendered).not_to have_selector('.govuk-select[name="forms_routes_input[routes_attributes][2][goto]"]')
73+
end
74+
75+
it "has options for where the route should go to for each select field" do
76+
render_page
77+
78+
def select_options(select_field)
79+
select_field.find_all("option").map { [it["value"], it.text] }
80+
end
81+
82+
expect(rendered).to have_selector('.govuk-select[name="forms_routes_input[routes_attributes][0][goto]"]') do |field|
83+
expect(select_options(field)).to eq [
84+
["default", "Go to question 2"],
85+
["103", "3. #{pages.third.question_text}"],
86+
["end_of_form", "End of the form"],
87+
]
88+
end
89+
90+
expect(rendered).to have_selector('.govuk-select[name="forms_routes_input[routes_attributes][1][goto]"]') do |field|
91+
expect(select_options(field)).to eq [
92+
["default", "Go to question 3"],
93+
["end_of_form", "End of the form"],
94+
]
95+
end
96+
end
97+
98+
it "shows the selected goto page for the route" do
99+
render_page
100+
101+
expect(rendered).to have_selector('.govuk-select[name="forms_routes_input[routes_attributes][0][goto]"]') do |field|
102+
expect(field).to have_selector("option[selected]") do |option|
103+
expect(option["value"]).to eq "103"
104+
end
105+
end
106+
107+
expect(rendered).to have_selector('.govuk-select[name="forms_routes_input[routes_attributes][1][goto]"]') do |field|
108+
expect(field).to have_selector("option[selected]") do |option|
109+
expect(option["value"]).to eq "default"
110+
end
111+
end
112+
end
113+
114+
context "when the route goes to a page before the routing page" do
115+
let(:pages) do
116+
[
117+
build_stubbed(:page, id: 101),
118+
build_stubbed(
119+
:page,
120+
id: 102,
121+
routing_conditions: [
122+
build_stubbed(
123+
:condition,
124+
routing_page_id: 102,
125+
goto_page_id: 101,
126+
answer_value: nil,
127+
),
128+
],
129+
),
130+
build_stubbed(:page, id: 103),
131+
]
132+
end
133+
134+
it "shows the selected goto page for the route" do
135+
render_page
136+
137+
expect(rendered).to have_selector('.govuk-select[name="forms_routes_input[routes_attributes][1][goto]"]') do |field|
138+
expect(field).to have_selector("option[selected]") do |option|
139+
expect(option["value"]).to eq "101"
140+
end
141+
end
142+
end
143+
end
51144
end
52145
end

0 commit comments

Comments
 (0)