Skip to content

Commit 6e6a652

Browse files
committed
personal details to wicked
1 parent 1613a85 commit 6e6a652

File tree

13 files changed

+249
-151
lines changed

13 files changed

+249
-151
lines changed

.simplecov

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,23 @@
77
# for this to be valuable so that only changed files have tests run
88
if ENV.fetch("COVERAGE", 0).to_i.positive?
99
require "simplecov"
10-
require "undercover/simplecov_formatter"
10+
require "simplecov-lcov"
1111

1212
# This allows both LCOV and HTML formatting -
1313
# lcov for undercover gem, HTML for humans
14-
SimpleCov.formatters = SimpleCov::Formatter::MultiFormatter.new(
15-
[
16-
SimpleCov::Formatter::Undercover,
17-
SimpleCov::Formatter::HTMLFormatter,
18-
],
19-
)
14+
class SimpleCov::Formatter::MergedFormatter
15+
def format(result)
16+
SimpleCov::Formatter::HTMLFormatter.new.format(result)
17+
SimpleCov::Formatter::LcovFormatter.new.format(result)
18+
end
19+
end
20+
21+
SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true
22+
SimpleCov.formatter = SimpleCov::Formatter::MergedFormatter
2023

2124
SimpleCov.start :rails do
2225
enable_coverage :branch
26+
primary_coverage :branch
2327

2428
# This line would enable coverage for view templates, but the slim compiler
2529
# appears to have a bug which puts the whole coverage data out by one line.
@@ -47,13 +51,6 @@ if ENV.fetch("COVERAGE", 0).to_i.positive?
4751
# base mailer, currently unused
4852
add_filter "app/mailers/amazon_ses_mailer.rb"
4953

50-
# legacy rake tasks, unlikely to ever be test covered
51-
add_filter "lib/tasks/audit.rake"
52-
add_filter "lib/tasks/data.rake"
53-
54-
# safe replacement for rake db:migrate, never going to be covered by tests
55-
add_filter "lib/tasks/migrate_swallowing_concurrent_migration_exceptions.rake"
56-
5754
# Each group will be displayed in the report as its own Tab.
5855
add_group "Components", "app/components"
5956
add_group "Queries", "app/queries"
@@ -69,10 +66,10 @@ if ENV.fetch("COVERAGE", 0).to_i.positive?
6966

7067
# However (possibly due to some residual random behaviour in test factories)
7168
# the line coverage needs to be set 0.02 below the reported value.
72-
# Normally this value needs to be 0.01 below the reported value due to rounding issues.
73-
minimum_coverage line: 97.6, branch: 87.38
74-
# Values from test run Fri 6th March 2026
75-
# 97.7% (12705 / 13004) -> 308 + 53 = 361 lines uncovered
76-
# 87.47% (2828 / 3233) -> 179 + 233 = 412 branches uncovered
69+
# Nornmally this value needs to be 0.01 below the reported value due to rounding issues.
70+
minimum_coverage line: 97.14, branch: 87.13
71+
# Values from test run Fri 13th February 2026
72+
# 97.46% (12553 / 12880) -> 327 lines uncovered
73+
# 87.18% (2808 / 3221) -> 192 + 221 = 411 branches uncovered
7774
end
7875
end
Lines changed: 56 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,58 @@
1-
require "multistep/controller"
2-
3-
class Jobseekers::Profiles::PersonalDetailsController < Jobseekers::ProfilesController
4-
include Multistep::Controller
5-
6-
multistep_form Jobseekers::Profile::PersonalDetailsForm, key: :personal_details_form
7-
8-
escape_path { jobseekers_profile_path }
9-
10-
def complete
11-
render "review"
12-
end
13-
14-
private
15-
16-
def store_form!
17-
personal_details_record.assign_attributes(form.attributes.compact)
18-
personal_details_record.save!
19-
end
20-
21-
def personal_details_record
22-
@personal_details_record ||= @profile.personal_details || @profile.build_personal_details
23-
end
24-
25-
def attributes_from_store
26-
personal_details_record.attributes.slice(*self.class.multistep_form.attribute_names)
1+
module Jobseekers::Profiles
2+
class PersonalDetailsController < Jobseekers::BaseController
3+
include Wicked::Wizard
4+
5+
steps(*PersonalDetailsForm::FORMS.keys)
6+
7+
helper_method :escape_path, :back_url
8+
9+
before_action :set_personal_details_record
10+
11+
def show
12+
@form = form_class.new(@personal_details_record.slice(form_class.fields))
13+
render_wizard nil, params: { back_to_review: params[:back_to_review] }
14+
end
15+
16+
def update
17+
@form = form_class.new(params.fetch(form_key, {}).permit(*form_class.fields))
18+
if @form.valid?
19+
@personal_details_record.update!(@form.params_to_save.merge(completed_steps: @personal_details_record.completed_steps.merge(step => :completed)))
20+
if params[:back_to_review]
21+
redirect_to review_jobseekers_profile_personal_details_steps_path
22+
elsif next_step == Wicked::FINISH_STEP
23+
redirect_to finish_wizard_path
24+
else
25+
redirect_to next_wizard_path
26+
end
27+
else
28+
render_wizard
29+
end
30+
end
31+
32+
def finish_wizard_path
33+
review_jobseekers_profile_personal_details_steps_path
34+
end
35+
36+
private
37+
38+
def back_url
39+
previous_wizard_path
40+
end
41+
42+
def form_key
43+
ActiveModel::Naming.param_key(form_class)
44+
end
45+
46+
def escape_path
47+
jobseekers_profile_path
48+
end
49+
50+
def form_class
51+
PersonalDetailsForm::FORMS.fetch(step)
52+
end
53+
54+
def set_personal_details_record
55+
@personal_details_record = current_jobseeker.jobseeker_profile.personal_details || current_jobseeker.jobseeker_profile.build_personal_details
56+
end
2757
end
2858
end

app/controllers/jobseekers/profiles_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class Jobseekers::ProfilesController < Jobseekers::BaseController
1313
display_summary: ->(profile) { profile.personal_details&.completed_steps.present? },
1414
key: "personal_details",
1515
link_text: "Add personal details",
16-
page_path: -> { personal_details_jobseekers_profile_path },
16+
page_path: -> { jobseekers_profile_personal_details_step_path(Wicked::FIRST_STEP) },
1717
},
1818
{
1919
title: "Job preferences",

app/form_models/jobseekers/profile/personal_details_form.rb

Lines changed: 0 additions & 30 deletions
This file was deleted.
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
module Jobseekers::Profiles
2+
class PersonalDetailsForm
3+
STEPS = { name: %i[first_name last_name],
4+
phone_number: %i[phone_number_provided phone_number],
5+
work: [:has_right_to_work_in_uk] }.freeze
6+
7+
class << self
8+
def from_record(record)
9+
new record
10+
end
11+
12+
# TODO: - simplify view so this method can be deleted
13+
def delegated_attributes
14+
STEPS.invert.map { |kl, v| kl.map { |k| { k => v } }.reduce(&:merge) }.reduce(&:merge)
15+
end
16+
end
17+
18+
def initialize(record)
19+
@personal_details = record
20+
end
21+
22+
def next_invalid_step
23+
FORMS.drop_while { |step, form_class|
24+
form_class.new(@personal_details.slice(STEPS.fetch(step))).valid?
25+
}.first.first
26+
end
27+
28+
class PersonalDetailsForm < ::BaseForm
29+
include ActiveModel::Model
30+
include ActiveModel::Attributes
31+
end
32+
33+
class NamesForm < PersonalDetailsForm
34+
attribute :first_name
35+
attribute :last_name
36+
37+
validates :first_name, :last_name, presence: true
38+
39+
class << self
40+
def fields
41+
%i[first_name last_name]
42+
end
43+
end
44+
45+
def params_to_save
46+
{ first_name: first_name, last_name: last_name }
47+
end
48+
end
49+
50+
class PhoneNumberForm < PersonalDetailsForm
51+
attribute :phone_number_provided
52+
attribute :phone_number
53+
54+
validates :phone_number_provided, presence: true
55+
validates :phone_number, presence: true, format: { with: /\A\+?(?:\d\s?){10,13}\z/ }, if: -> { phone_number_provided == "true" }
56+
57+
class << self
58+
def fields
59+
%i[phone_number_provided phone_number]
60+
end
61+
end
62+
63+
def params_to_save
64+
{ phone_number: phone_number, phone_number_provided: phone_number_provided }
65+
end
66+
end
67+
68+
class WorkForm < PersonalDetailsForm
69+
attribute :has_right_to_work_in_uk, :boolean
70+
71+
validates :has_right_to_work_in_uk, inclusion: { in: [true, false] }
72+
73+
class << self
74+
def fields
75+
[:has_right_to_work_in_uk]
76+
end
77+
end
78+
79+
def params_to_save
80+
{ has_right_to_work_in_uk: has_right_to_work_in_uk }
81+
end
82+
end
83+
84+
FORMS = {
85+
name: NamesForm,
86+
phone_number: PhoneNumberForm,
87+
work: WorkForm,
88+
}.freeze
89+
end
90+
end
Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
ruby:
2-
form = Jobseekers::Profile::PersonalDetailsForm.from_record(@profile.personal_details)
2+
model = profile.personal_details
3+
form = Jobseekers::Profiles::PersonalDetailsForm.from_record(model)
34

45
attributes = {
5-
phone_number_provided: { map_values: ->(value) { value.nil? ? "" : t("helpers.label.personal_details_form.phone_number_provided_options.#{value}") } },
6+
phone_number_provided: { map_values: ->(value) { value.nil? ? "" : t("helpers.label.jobseekers_profiles_personal_details_form_phone_number_form.phone_number_provided_options.#{value}") } },
67
phone_number: nil,
78
has_right_to_work_in_uk: { map_values: ->(value) { value.nil? ? "" : t("jobseekers.profiles.personal_details.work.options.#{value}") } },
89
}
910

1011
summary = govuk_summary_list(html_attributes: { id: "personal_details" }) do |summary_list|
1112
summary_list.with_row do |row|
1213
row.with_key text: "Name"
13-
row.with_value text: "#{form.first_name} #{form.last_name}"
14-
row.with_action(text: t("buttons.change"), href: edit_personal_details_jobseekers_profile_path(step: "name"), visually_hidden_text: " name")
14+
row.with_value text: "#{model.first_name} #{model.last_name}"
15+
row.with_action(text: t("buttons.change"), href: jobseekers_profile_personal_details_step_path("name", back_to_review: true), visually_hidden_text: " name")
1516
end
1617

1718
attributes.each do |attribute, options|
18-
step_name = form.class.delegated_attributes[attribute.to_s]
19-
value = form.public_send(attribute)
19+
step_name = form.class.delegated_attributes[attribute]
20+
value = model.public_send(attribute)
2021

2122
if options && options[:map_values]
2223
value = options[:map_values].call(value)
@@ -25,25 +26,25 @@ ruby:
2526
summary_list.with_row do |row|
2627
row.with_key text: t("jobseekers.profiles.personal_details.summary.#{attribute}")
2728
row.with_value text: value
28-
row.with_action(text: t("buttons.change"), href: edit_personal_details_jobseekers_profile_path(step: step_name), visually_hidden_text: attribute.to_s.humanize)
29+
row.with_action(text: t("buttons.change"), href: jobseekers_profile_personal_details_step_path(step_name, back_to_review: true), visually_hidden_text: attribute.to_s.humanize)
2930
end
3031
end
3132

3233
summary_list.with_row do |row|
3334
row.with_key text: t("jobseekers.profiles.personal_details.summary.email")
3435
row.with_value text: (current_jobseeker.email +
35-
govuk_inset_text(text: t("helpers.label.personal_details_form.change_email_html",
36-
link: govuk_link_to(t("helpers.label.personal_details_form.change_email_link_text"),
36+
govuk_inset_text(text: t("helpers.label.jobseekers_profiles_personal_details_form_names_form.change_email_html",
37+
link: govuk_link_to(t("helpers.label.jobseekers_profiles_personal_details_form_names_form.change_email_link_text"),
3738
jobseekers_account_path)),
3839
classes: "govuk-!-margin-top-3")).html_safe
3940
end
4041
end
4142

42-
- if form.completed?
43+
- if model.complete?
4344
= summary
4445
- else
4546
.govuk-inset-text.govuk-inset-text--incomplete
4647
= summary
4748
p.govuk-body-m.govuk-inset-text--header
4849
strong = t("jobseekers.profiles.personal_details.incomplete_message")
49-
= govuk_link_to t("buttons.complete_personal_details"), edit_personal_details_jobseekers_profile_path(step: form.next_step), class: "govuk-button"
50+
= govuk_link_to t("buttons.complete_personal_details"), jobseekers_profile_personal_details_step_path(form.next_invalid_step), class: "govuk-button"

app/views/jobseekers/profiles/personal_details/name.html.slim

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,19 @@
33
- content_for :breadcrumbs do
44
= govuk_back_link text: t("buttons.back"), href: escape_path, html_attributes: { "aria-label" => "Back navigation" }
55

6-
.govuk-grid-row
7-
.govuk-grid-column-two-thirds
8-
= form_for @step, url: { action: :update }, method: :post, as: :personal_details_form do |f|
9-
= f.govuk_error_summary
6+
= form_for @form, url: wizard_path(step, back_to_review: params[:back_to_review]), method: :patch do |f|
7+
= f.govuk_error_summary
108

11-
= f.govuk_fieldset legend: { text: t(".heading"), tag: "h1", size: "l" }, caption: { text: t(".caption"), size: "l" } do
12-
= f.govuk_text_field :first_name,
13-
label: { size: "m" },
14-
autocomplete: "given-name"
9+
= f.govuk_fieldset legend: { text: t(".heading"), tag: "h1", size: "l" }, caption: { text: t(".caption"), size: "l" } do
10+
= f.govuk_text_field :first_name,
11+
label: { size: "m" },
12+
autocomplete: "given-name"
1513

16-
= f.govuk_text_field :last_name,
17-
label: { size: "m" },
18-
autocomplete: "family-name"
14+
= f.govuk_text_field :last_name,
15+
label: { size: "m" },
16+
autocomplete: "family-name"
1917

20-
= f.govuk_submit t("buttons.save_and_continue"), class: "govuk-!-margin-bottom-5"
18+
= f.govuk_submit t("buttons.save_and_continue"), class: "govuk-!-margin-bottom-5"
2119

22-
.govuk-button-group
23-
= govuk_link_to t("buttons.cancel"), jobseekers_profile_path
20+
.govuk-button-group
21+
= govuk_link_to t("buttons.cancel"), jobseekers_profile_path

0 commit comments

Comments
 (0)