From ef4defb8de76759a4a872ea40b14ca7edec0d97c Mon Sep 17 00:00:00 2001 From: Alkesh Vaghmaria Date: Fri, 2 Aug 2024 16:03:18 +0100 Subject: [PATCH] magic link --- app/controllers/claims_controller.rb | 10 +++ .../provider/email_address_form.rb | 13 +++- app/models/journeys/page_sequence.rb | 5 ++ .../provider/happy_path_spec.rb | 17 ++--- .../provider/email_address_form_spec.rb | 63 ++++++++++++++++++- spec/models/journeys/page_sequence_spec.rb | 16 +++++ 6 files changed, 115 insertions(+), 9 deletions(-) diff --git a/app/controllers/claims_controller.rb b/app/controllers/claims_controller.rb index b35e3f1412..aa448dc0c3 100644 --- a/app/controllers/claims_controller.rb +++ b/app/controllers/claims_controller.rb @@ -79,9 +79,19 @@ def check_page_is_in_sequence raise ActionController::RoutingError.new("Not Found for #{params[:slug]}") unless page_sequence.in_sequence?(params[:slug]) + handle_magic_link if page_sequence.magic_link? redirect_to claim_path(current_journey_routing_name, next_required_slug) unless page_sequence.has_completed_journey_until?(params[:slug]) end + def handle_magic_link + otp = OneTimePassword::Validator.new(params[:code], answers.sent_one_time_password_at) + if otp.valid? + journey_session.answers.assign_attributes(email_verified: true) + journey_session.save! + session[:slugs] << page_sequence.next_required_slug + end + end + def initialize_session_slug_history session[:slugs] ||= [] end diff --git a/app/forms/journeys/early_years_payment/provider/email_address_form.rb b/app/forms/journeys/early_years_payment/provider/email_address_form.rb index 0f3c9cc783..b2c03c0edd 100644 --- a/app/forms/journeys/early_years_payment/provider/email_address_form.rb +++ b/app/forms/journeys/early_years_payment/provider/email_address_form.rb @@ -6,7 +6,9 @@ class EmailAddressForm < Form def save journey_session.answers.assign_attributes( - email_address: email_address + email_address: email_address, + sent_one_time_password_at: Time.now, + email_verified: email_verified ) journey_session.save! @@ -15,6 +17,15 @@ def save private + def email_address_changed? + email_address != answers.email_address + end + + def email_verified + return nil if email_address_changed? + answers.email_verified + end + def otp_code @otp_code ||= OneTimePassword::Generator.new.code end diff --git a/app/models/journeys/page_sequence.rb b/app/models/journeys/page_sequence.rb index 930c2bcf29..33701c161b 100644 --- a/app/models/journeys/page_sequence.rb +++ b/app/models/journeys/page_sequence.rb @@ -7,6 +7,7 @@ class PageSequence DEAD_END_SLUGS = %w[complete existing-session eligible-later future-eligibility ineligible] OPTIONAL_SLUGS = %w[postcode-search select-home-address reset-claim] + MAGIC_LINK_SLUGS = %w[consent] def initialize(slug_sequence, completed_slugs, current_slug, journey_session) @current_slug = current_slug @@ -61,6 +62,10 @@ def next_required_slug (slugs - completed_slugs - OPTIONAL_SLUGS).first end + def magic_link? + MAGIC_LINK_SLUGS.include?(current_slug) + end + private delegate :answers, to: :@journey_session diff --git a/spec/features/early_years_payment/provider/happy_path_spec.rb b/spec/features/early_years_payment/provider/happy_path_spec.rb index 95d70cb531..27ff142731 100644 --- a/spec/features/early_years_payment/provider/happy_path_spec.rb +++ b/spec/features/early_years_payment/provider/happy_path_spec.rb @@ -1,6 +1,8 @@ require "rails_helper" RSpec.feature "Early years payment provider" do + let(:journey_session) { Journeys::EarlyYearsPayment::Provider::Session.last } + scenario "happy path claim" do when_early_years_payment_provider_journey_configuration_exists @@ -17,13 +19,14 @@ expect(page).to have_content("We have sent an email to johndoe@example.com") mail = ActionMailer::Base.deliveries.last - mail_personalisation = mail[:personalisation].unparsed_value - expect(mail_personalisation[:one_time_password]).to match(/\A\d{6}\Z/) - - # TODO - uncomment below when magic link functionality in place - # expect(page).to have_content("Declaration of Employee Consent") - # check "I confirm that I have obtained consent from my employee and have provided them with the relevant privacy notice." - # click_button "Continue" + otp = mail[:personalisation].unparsed_value[:one_time_password] + expect(otp).to match(/\A\d{6}\Z/) + + visit claim_path(Journeys::EarlyYearsPayment::Provider::ROUTING_NAME, :consent, code: otp) + expect(journey_session.reload.answers.email_verified).to be true + expect(page).to have_content("Declaration of Employee Consent") + check "I confirm that I have obtained consent from my employee and have provided them with the relevant privacy notice." + click_button "Continue" end scenario "send another link" diff --git a/spec/forms/journeys/early_years_payment/provider/email_address_form_spec.rb b/spec/forms/journeys/early_years_payment/provider/email_address_form_spec.rb index eea3909c5a..0957949e5f 100644 --- a/spec/forms/journeys/early_years_payment/provider/email_address_form_spec.rb +++ b/spec/forms/journeys/early_years_payment/provider/email_address_form_spec.rb @@ -5,7 +5,6 @@ let(:journey) { Journeys::EarlyYearsPayment::Provider } let(:journey_session) { build(:early_years_payment_provider_session) } - # let(:params) { ActionController::Parameters.new({journey: "test-journey", slug: "test_slug", claim: claim_params}) } let(:params) do ActionController::Parameters.new(claim: {email_address: email_address}) @@ -18,6 +17,22 @@ describe "#save" do subject { form.save } + around do |example| + travel_to DateTime.new(2024, 1, 1, 12, 0, 0) do + example.run + end + end + + before do + allow(OneTimePassword::Generator).to receive(:new).and_return( + instance_double(OneTimePassword::Generator, code: "111111") + ) + end + + let(:policy) { journey_session.answers.policy } + let(:claim_subject) { I18n.t("#{policy.locale_key}.claim_subject") } + let(:email_subject) { claim_subject } + it { should be_truthy } it "sets the email address" do @@ -26,5 +41,51 @@ eq(email_address) ) end + + it "sends an email" do + subject + + expect(email_address).to have_received_email( + "e0b78a08-601b-40ba-a97f-61fb00a7c951", + email_subject: email_subject, + one_time_password: "111111" + ) + end + + it "updates sent_one_time_password_at" do + subject + expect(journey_session.answers.sent_one_time_password_at).to( + eq(DateTime.new(2024, 1, 1, 12, 0, 0)) + ) + end + + it "resets email_verified" do + subject + expect(journey_session.answers.email_verified).to be_nil + end + + context "when the email address has been previously verified, and a new one is submitted" do + before do + journey_session.answers.assign_attributes(email_address: "new@example.com", email_verified: true) + journey_session.save! + end + + it "resets email_verified" do + subject + expect(journey_session.answers.email_verified).to be_nil + end + end + + context "when the email address submitted has been previously verified, and is the same" do + before do + journey_session.answers.assign_attributes(email_address: email_address, email_verified: true) + journey_session.save! + end + + it "returns email_verified" do + subject + expect(journey_session.answers.email_verified).to be true + end + end end end diff --git a/spec/models/journeys/page_sequence_spec.rb b/spec/models/journeys/page_sequence_spec.rb index 1cd27fd1b1..fe883a2bfe 100644 --- a/spec/models/journeys/page_sequence_spec.rb +++ b/spec/models/journeys/page_sequence_spec.rb @@ -219,4 +219,20 @@ expect(page_sequence.next_required_slug).to eq("second-slug") end end + + describe "#magic_link?" do + subject { page_sequence.magic_link? } + + context "when the current slug is not a magic link" do + let(:current_slug) { "whatever" } + + it { is_expected.to be false } + end + + context "when the current slug is not a magic link" do + let(:current_slug) { "consent" } + + it { is_expected.to be true } + end + end end