From f35a3f60e73f0f0b3ed517cf69fd4afb74b61688 Mon Sep 17 00:00:00 2001 From: chao-xian Date: Fri, 15 May 2026 14:04:12 +0100 Subject: [PATCH 1/6] Update Form model to check from API send_copy_of_answers A new attribute has been added to Forms. It allows editors to set whether fillers get to see the CopyOfAnswers page on their form. --- app/models/form.rb | 5 +++++ spec/factories/v2_form_document.rb | 1 + spec/models/form_spec.rb | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/app/models/form.rb b/app/models/form.rb index e8560df6b..2b0ae9414 100644 --- a/app/models/form.rb +++ b/app/models/form.rb @@ -17,6 +17,7 @@ def initialize(form_document) :start_page, :send_daily_submission_batch, :send_weekly_submission_batch, + :send_copy_of_answers, :submission_email, :submission_type, :support_email, @@ -64,6 +65,10 @@ def welsh? language == :cy end + def copy_of_answers_enabled? + send_copy_of_answers == "enabled" + end + def multilingual? available_languages.count > 1 end diff --git a/spec/factories/v2_form_document.rb b/spec/factories/v2_form_document.rb index 313ce63e3..d37eb7753 100644 --- a/spec/factories/v2_form_document.rb +++ b/spec/factories/v2_form_document.rb @@ -23,6 +23,7 @@ s3_bucket_name { nil } s3_bucket_region { nil } updated_at { Time.current.iso8601(3) } + send_copy_of_answers { "disabled" } send_daily_submission_batch { false } send_weekly_submission_batch { false } diff --git a/spec/models/form_spec.rb b/spec/models/form_spec.rb index bbbe363d1..5227f0c3b 100644 --- a/spec/models/form_spec.rb +++ b/spec/models/form_spec.rb @@ -179,6 +179,24 @@ end end + describe "#copy_of_answers_enabled?" do + context "when send_copy_of_answers is \"enabled\"" do + let(:form_document) { build :v2_form_document, send_copy_of_answers: "enabled" } + + it "returns true" do + expect(form.copy_of_answers_enabled?).to be true + end + end + + context "when send_copy_of_answers is \"disabled\"" do + let(:form_document) { build :v2_form_document, send_copy_of_answers: "disabled" } + + it "returns false" do + expect(form.copy_of_answers_enabled?).to be false + end + end + end + describe "#document_json" do let(:form_document) { build :v2_form_document, :live, :s3_submissions_enabled } From 95065fd837c19e28a18ff04b7319113b51678603 Mon Sep 17 00:00:00 2001 From: chao-xian Date: Fri, 15 May 2026 16:33:43 +0100 Subject: [PATCH 2/6] Update logic showing CopyOfAnswerst to use Form's flag We don't just want to show the page on checking the feature flag, but also if it's allowed for the form itself anyway. --- app/controllers/forms/step_controller.rb | 2 +- spec/requests/forms/step_controller_spec.rb | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/app/controllers/forms/step_controller.rb b/app/controllers/forms/step_controller.rb index 5b2c7934f..0f41ba209 100644 --- a/app/controllers/forms/step_controller.rb +++ b/app/controllers/forms/step_controller.rb @@ -149,7 +149,7 @@ def next_step_changing def next_step_in_form_path if @step.next_step_slug_after_routing == CheckYourAnswersStep::CHECK_YOUR_ANSWERS_STEP_SLUG - if FeatureService.enabled?("filler_answer_email_enabled") + if FeatureService.enabled?("filler_answer_email_enabled") && @form.copy_of_answers_enabled? copy_of_answers_path(form_id: @form.id, form_slug: @form.form_slug) else check_answers_path diff --git a/spec/requests/forms/step_controller_spec.rb b/spec/requests/forms/step_controller_spec.rb index 473198d4a..d38faa95c 100644 --- a/spec/requests/forms/step_controller_spec.rb +++ b/spec/requests/forms/step_controller_spec.rb @@ -12,6 +12,7 @@ what_happens_next_markdown: "Good things come to those that wait", declaration_text: "agree to the declaration", available_languages:, + send_copy_of_answers: "enabled", steps: steps_data) end @@ -720,6 +721,25 @@ post save_form_step_path(mode:, form_id: 2, form_slug: form_data.form_slug, step_slug: 2), params: { question: { text: "answer text" } } expect(response).to redirect_to(copy_of_answers_path(2, form_data.form_slug, mode:)) end + + context "when send_copy_of_answers is disabled on the form" do + let(:form_data) do + build(:v2_form_document, :with_support, + form_id: 2, + start_page: first_step_id, + privacy_policy_url: "http://www.example.gov.uk/privacy_policy", + what_happens_next_markdown: "Good things come to those that wait", + declaration_text: "agree to the declaration", + available_languages:, + send_copy_of_answers: "disabled", + steps: steps_data) + end + + it "Redirects to the check your answers page" do + post save_form_step_path(mode:, form_id: 2, form_slug: form_data.form_slug, step_slug: 2), params: { question: { text: "answer text" } } + expect(response).to redirect_to(check_your_answers_path(2, form_data.form_slug, mode:)) + end + end end end From 1bf64ace3ad72c11a9f6e03772cf4907edbd08c5 Mon Sep 17 00:00:00 2001 From: chao-xian Date: Fri, 15 May 2026 16:36:30 +0100 Subject: [PATCH 3/6] Update the Check Your Answers back button To also check that the form itself is meant to show the Copy Of Answers page. --- .../forms/check_your_answers_controller.rb | 2 +- .../check_your_answers_controller_spec.rb | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/app/controllers/forms/check_your_answers_controller.rb b/app/controllers/forms/check_your_answers_controller.rb index 021f25693..172132b19 100644 --- a/app/controllers/forms/check_your_answers_controller.rb +++ b/app/controllers/forms/check_your_answers_controller.rb @@ -77,7 +77,7 @@ def setup_check_your_answers end def back_link - if FeatureService.enabled?("filler_answer_email_enabled") + if FeatureService.enabled?("filler_answer_email_enabled") && current_context.form.copy_of_answers_enabled? copy_of_answers_path(form_id: current_context.form.id, form_slug: current_context.form.form_slug) else previous_step = current_context.previous_step(CheckYourAnswersStep::CHECK_YOUR_ANSWERS_STEP_SLUG) diff --git a/spec/requests/forms/check_your_answers_controller_spec.rb b/spec/requests/forms/check_your_answers_controller_spec.rb index 60b12cc6f..1198ef512 100644 --- a/spec/requests/forms/check_your_answers_controller_spec.rb +++ b/spec/requests/forms/check_your_answers_controller_spec.rb @@ -18,6 +18,7 @@ support_email: "help@example.gov.uk", support_url: "https://example.gov.uk/help", support_url_text: "Get help", + send_copy_of_answers: "enabled", submission_email:) end @@ -194,6 +195,32 @@ include_examples "for notification references" end + + context "when send_copy_of_answers is disabled on the form" do + let(:form_data) do + build(:v2_form_document, :with_support, :with_submission_email, + form_id: form_id, + start_page: 1, + privacy_policy_url: "http://www.example.gov.uk/privacy_policy", + what_happens_next_markdown: "Good things come to those that wait", + declaration_text: "agree to the declaration", + steps: steps_data, + support_phone: "0203 222 2222", + support_email: "help@example.gov.uk", + support_url: "https://example.gov.uk/help", + support_url_text: "Get help", + send_copy_of_answers: "disabled", + submission_email:) + end + + before do + get check_your_answers_path(mode:, form_id:, form_slug: form_data.form_slug) + end + + it "Displays a back link to the last step" do + expect(response.body).to include(form_step_path(mode:, form_id:, form_slug: form_data.form_slug, step_slug: 2)) + end + end end end From e5cd01f13a6609dd520143fb4b193d8129ba4c34 Mon Sep 17 00:00:00 2001 From: chao-xian Date: Fri, 15 May 2026 16:43:49 +0100 Subject: [PATCH 4/6] Update CopyOfAnswersController to redirect if not enabled on form --- .../forms/copy_of_answers_controller.rb | 2 +- .../forms/copy_of_answers_controller_spec.rb | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/app/controllers/forms/copy_of_answers_controller.rb b/app/controllers/forms/copy_of_answers_controller.rb index eed10d066..98d8bab1d 100644 --- a/app/controllers/forms/copy_of_answers_controller.rb +++ b/app/controllers/forms/copy_of_answers_controller.rb @@ -45,7 +45,7 @@ def back_link end def redirect_if_feature_disabled - return if FeatureService.enabled?(:filler_answer_email_enabled) + return if FeatureService.enabled?(:filler_answer_email_enabled) && current_context.form.copy_of_answers_enabled? redirect_to check_your_answers_path(form_id: current_context.form.id, form_slug: current_context.form.form_slug) end diff --git a/spec/requests/forms/copy_of_answers_controller_spec.rb b/spec/requests/forms/copy_of_answers_controller_spec.rb index 02a454aa9..9e9cb56ee 100644 --- a/spec/requests/forms/copy_of_answers_controller_spec.rb +++ b/spec/requests/forms/copy_of_answers_controller_spec.rb @@ -2,7 +2,7 @@ RSpec.describe Forms::CopyOfAnswersController, type: :request do let(:form) do - build(:v2_form_document, :with_support, form_id: 2, start_page: 1, steps:, available_languages:) + build(:v2_form_document, :with_support, form_id: 2, start_page: 1, steps:, available_languages:, send_copy_of_answers: "enabled") end let(:steps) do @@ -51,6 +51,20 @@ end end + context "when the feature flag is enabled but send_copy_of_answers is disabled on the form", :feature_filler_answer_email_enabled do + let(:form) do + build(:v2_form_document, :with_support, form_id: 2, start_page: 1, steps:, available_languages:, send_copy_of_answers: "disabled") + end + + before do + get copy_of_answers_path(mode:, form_id: form.form_id, form_slug: form.form_slug) + end + + it "redirects to check your answers" do + expect(response).to redirect_to(check_your_answers_path(form_id: form.form_id, form_slug: form.form_slug, mode:)) + end + end + context "when the feature flag is enabled", :feature_filler_answer_email_enabled do before do get copy_of_answers_path(mode:, form_id: form.form_id, form_slug: form.form_slug) From ad66a7252071b584f7bd0a555842fe95d9af6706 Mon Sep 17 00:00:00 2001 From: chao-xian Date: Thu, 21 May 2026 14:57:51 +0100 Subject: [PATCH 5/6] WIP --- Gemfile.lock | 1 - .../forms/check_your_answers_controller.rb | 16 +++-- .../forms/check_your_answers/show.html.erb | 16 +++-- .../check_your_answers_controller_spec.rb | 69 +++++++++++++++++++ 4 files changed, 90 insertions(+), 12 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 2636b7efc..06338e090 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -860,7 +860,6 @@ CHECKSUMS bootsnap (1.24.3) sha256=f7fa3d20597e2f0aa52b0a1aba83fb54d4f79e9c2e210ec4fa1e8895514dcad8 brakeman (8.0.4) sha256=7bf921fa9638544835df9aa7b3e720a9a72c0267f34f92135955edd80d4dcf6f builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f - bundler (4.0.11) sha256=5bcec0fb78302e48d02ee46f10ee6e6942be647ba5b44a6d1ddfda9a240ce785 bundler-audit (0.9.3) sha256=81c8766c71e47d0d28a0f98c7eed028539f21a6ea3cd8f685eb6f42333c9b4e9 capybara (3.40.0) sha256=42dba720578ea1ca65fd7a41d163dd368502c191804558f6e0f71b391054aeef coercible (1.0.0) sha256=5081ad24352cc8435ce5472bc2faa30260c7ea7f2102cc6a9f167c4d9bffaadc diff --git a/app/controllers/forms/check_your_answers_controller.rb b/app/controllers/forms/check_your_answers_controller.rb index 172132b19..dd01a6f06 100644 --- a/app/controllers/forms/check_your_answers_controller.rb +++ b/app/controllers/forms/check_your_answers_controller.rb @@ -11,22 +11,24 @@ def show return redirect_to form_step_path(current_context.form.id, current_context.form.form_slug, current_context.next_step_slug, nil) unless current_context.can_visit?(CheckYourAnswersStep::CHECK_YOUR_ANSWERS_STEP_SLUG) setup_check_your_answers - email_confirmation_input = EmailConfirmationInput.new + skip_confirmation_email = skip_confirmation_email? + email_confirmation_input = skip_confirmation_email ? EmailConfirmationInput.new(send_confirmation: "skip_confirmation") : EmailConfirmationInput.new @support_details = current_context.support_details - render template: "forms/check_your_answers/show", locals: { email_confirmation_input: } + render template: "forms/check_your_answers/show", locals: { email_confirmation_input:, skip_confirmation_email: } end def submit_answers @support_details = current_context.support_details - email_confirmation_input = EmailConfirmationInput.new(email_confirmation_input_params) + skip_confirmation_email = skip_confirmation_email? + email_confirmation_input = skip_confirmation_email ? EmailConfirmationInput.new(send_confirmation: "skip_confirmation") : EmailConfirmationInput.new(email_confirmation_input_params) requested_email_confirmation = email_confirmation_input.send_confirmation == "send_email" unless email_confirmation_input.valid? setup_check_your_answers - return render template: "forms/check_your_answers/show", locals: { email_confirmation_input: }, status: :unprocessable_content + return render template: "forms/check_your_answers/show", locals: { email_confirmation_input:, skip_confirmation_email: }, status: :unprocessable_content end return redirect_to error_repeat_submission_path(@form.id) if current_context.form_submitted? @@ -52,7 +54,7 @@ def submit_answers rescue FormSubmissionService::ConfirmationEmailToAddressError setup_check_your_answers email_confirmation_input.errors.add(:confirmation_email_address, :invalid_email) - render template: "forms/check_your_answers/show", locals: { email_confirmation_input: }, status: :unprocessable_content + render template: "forms/check_your_answers/show", locals: { email_confirmation_input:, skip_confirmation_email: }, status: :unprocessable_content end rescue StandardError => e log_rescued_exception(e) @@ -76,6 +78,10 @@ def setup_check_your_answers end end + def skip_confirmation_email? + current_context.wants_copy_of_answers? && current_context.get_copy_of_answers_email_address.present? + end + def back_link if FeatureService.enabled?("filler_answer_email_enabled") && current_context.form.copy_of_answers_enabled? copy_of_answers_path(form_id: current_context.form.id, form_slug: current_context.form.form_slug) diff --git a/app/views/forms/check_your_answers/show.html.erb b/app/views/forms/check_your_answers/show.html.erb index 2093a4a39..c74589ad1 100644 --- a/app/views/forms/check_your_answers/show.html.erb +++ b/app/views/forms/check_your_answers/show.html.erb @@ -21,14 +21,18 @@
- <%= form.govuk_radio_buttons_fieldset(:send_confirmation, legend: { size: 'm', tag: 'h2' }) do %> - <%= form.govuk_radio_button :send_confirmation, 'send_email', link_errors: :true do %> - <%= form.govuk_email_field :confirmation_email_address, autocomplete: 'email', spellcheck: false %> + <% if skip_confirmation_email %> +

We’ll send a confirmation email with a copy of your answers to your GOV.UK One Login email address: <%= @current_context.get_copy_of_answers_email_address %>

+ <% else %> + <%= form.govuk_radio_buttons_fieldset(:send_confirmation, legend: { size: 'm', tag: 'h2' }) do %> + <%= form.govuk_radio_button :send_confirmation, 'send_email', link_errors: :true do %> + <%= form.govuk_email_field :confirmation_email_address, autocomplete: 'email', spellcheck: false %> + <% end %> + <%= form.govuk_radio_button :send_confirmation, 'skip_confirmation' %> <% end %> - <%= form.govuk_radio_button :send_confirmation, 'skip_confirmation' %> - <% end %> - <%= form.hidden_field :confirmation_email_reference, id: 'confirmation-email-reference' %> + <%= form.hidden_field :confirmation_email_reference, id: 'confirmation-email-reference' %> + <% end %> <%if @current_context.form.try(:declaration_markdown).present? %>

<%= t('form.check_your_answers.declaration') %>

diff --git a/spec/requests/forms/check_your_answers_controller_spec.rb b/spec/requests/forms/check_your_answers_controller_spec.rb index 1198ef512..5462990da 100644 --- a/spec/requests/forms/check_your_answers_controller_spec.rb +++ b/spec/requests/forms/check_your_answers_controller_spec.rb @@ -196,6 +196,49 @@ include_examples "for notification references" end + context "when the user has said yes to copy of answers and has a One Login email" do + let(:store) do + { + answers:, + confirmation_details: { + form_id.to_s => { + "wants_copy_of_answers" => true, + "copy_of_answers_email_address" => "user@example.gov.uk", + }, + }, + }.with_indifferent_access + end + + before do + get check_your_answers_path(mode:, form_id:, form_slug: form_data.form_slug) + end + + it "hides the confirmation email question" do + expect(response.body).not_to include("email_confirmation_input[send_confirmation]") + end + end + + context "when the user has said yes to copy of answers but has no One Login email" do + let(:store) do + { + answers:, + confirmation_details: { + form_id.to_s => { + "wants_copy_of_answers" => true, + }, + }, + }.with_indifferent_access + end + + before do + get check_your_answers_path(mode:, form_id:, form_slug: form_data.form_slug) + end + + it "shows the confirmation email question" do + expect(response.body).to include("email_confirmation_input[send_confirmation]") + end + end + context "when send_copy_of_answers is disabled on the form" do let(:form_data) do build(:v2_form_document, :with_support, :with_submission_email, @@ -303,6 +346,32 @@ include_examples "for notification references" end + context "when the user has said yes to copy of answers and has a One Login email" do + let(:store) do + { + answers:, + confirmation_details: { + form_id.to_s => { + "wants_copy_of_answers" => true, + "copy_of_answers_email_address" => "user@example.gov.uk", + }, + }, + }.with_indifferent_access + end + + before do + travel_to frozen_time do + perform_enqueued_jobs do + post form_submit_answers_path(form_id:, form_slug: "form-name", mode:), params: {} + end + end + end + + it "submits successfully without email_confirmation_input params" do + expect(response).to redirect_to(form_submitted_path) + end + end + context "when the submission type is s3" do let(:form_data) do build(:v2_form_document, :s3_submissions_enabled, form_id:, steps: steps_data, start_page: 1) From 36a7138676b28b93c0503089eee17cd4c21af4b5 Mon Sep 17 00:00:00 2001 From: chao-xian Date: Fri, 22 May 2026 13:55:26 +0100 Subject: [PATCH 6/6] WIP --- app/views/forms/check_your_answers/show.html.erb | 2 +- app/views/forms/submitted/submitted.html.erb | 3 +++ config/locales/en.yml | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/views/forms/check_your_answers/show.html.erb b/app/views/forms/check_your_answers/show.html.erb index c74589ad1..a664bc6a9 100644 --- a/app/views/forms/check_your_answers/show.html.erb +++ b/app/views/forms/check_your_answers/show.html.erb @@ -22,7 +22,7 @@
<% if skip_confirmation_email %> -

We’ll send a confirmation email with a copy of your answers to your GOV.UK One Login email address: <%= @current_context.get_copy_of_answers_email_address %>

+

<%= t('form.check_your_answers.send_copy', email_address: @current_context.get_copy_of_answers_email_address) %>

<% else %> <%= form.govuk_radio_buttons_fieldset(:send_confirmation, legend: { size: 'm', tag: 'h2' }) do %> <%= form.govuk_radio_button :send_confirmation, 'send_email', link_errors: :true do %> diff --git a/app/views/forms/submitted/submitted.html.erb b/app/views/forms/submitted/submitted.html.erb index aaabe62bf..38f10499e 100644 --- a/app/views/forms/submitted/submitted.html.erb +++ b/app/views/forms/submitted/submitted.html.erb @@ -3,6 +3,9 @@
<%= render partial: "forms/submitted/submitted_panel", locals: {current_context: @current_context, } %> + <% if @current_context.wants_copy_of_answers? && @current_context.get_copy_of_answers_email_address.present? %> +

<%= t('form.submitted.copy_of_answers_sent') %>

+ <% end %> <% if @current_context.requested_email_confirmation? %>

<%= t('form.submitted.email_sent') %>

diff --git a/config/locales/en.yml b/config/locales/en.yml index b16b5aeec..5bff14e9c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -364,11 +364,13 @@ en: agree_and_submit: Agree and submit declaration: Declaration not_completed: Not completed + send_copy: "We’ll send a confirmation email with a copy of your answers to your GOV.UK One Login email address: %{email_address}" submit: Submit title: Check your answers before submitting your form submitted: continue_to_pay: Continue to pay email_sent: We’ve sent you a confirmation email. + copy_of_answers_sent: We’ve sent you a confirmation email with a copy of your answers. need_to_pay: You still need to pay title: Your form has been submitted what_happens_next: What happens next