diff --git a/app/controllers/concerns/idv_step_concern.rb b/app/controllers/concerns/idv_step_concern.rb index a2a9f9695e0..b643c810ff4 100644 --- a/app/controllers/concerns/idv_step_concern.rb +++ b/app/controllers/concerns/idv_step_concern.rb @@ -45,6 +45,28 @@ def flow_path idv_session.flow_path end + def confirm_hybrid_handoff_needed + setup_for_redo if params[:redo] + + if idv_session.skip_hybrid_handoff? + # We previously skipped hybrid handoff. Keep doing that. + idv_session.flow_path = 'standard' + end + + if !FeatureManagement.idv_allow_hybrid_flow? + # When hybrid flow is unavailable, skip it. + # But don't store that we skipped it in idv_session, in case it is back to + # available when the user tries to redo document capture. + idv_session.flow_path = 'standard' + end + + if idv_session.flow_path == 'standard' + redirect_to idv_document_capture_url + elsif idv_session.flow_path == 'hybrid' + redirect_to idv_link_sent_url + end + end + private def confirm_ssn_step_complete @@ -96,4 +118,16 @@ def extra_analytics_properties end extra end + + def setup_for_redo + idv_session.redo_document_capture = true + + # If we previously skipped hybrid handoff for the user (because they're on a mobile + # device with a camera), skip it _again_ here. + if idv_session.skip_hybrid_handoff? + idv_session.flow_path = 'standard' + else + idv_session.flow_path = nil + end + end end diff --git a/app/controllers/idv/hybrid_handoff_controller.rb b/app/controllers/idv/hybrid_handoff_controller.rb index 0c26a3972d9..d060691386b 100644 --- a/app/controllers/idv/hybrid_handoff_controller.rb +++ b/app/controllers/idv/hybrid_handoff_controller.rb @@ -192,40 +192,6 @@ def confirm_agreement_step_complete redirect_to idv_agreement_url end - def confirm_hybrid_handoff_needed - setup_for_redo if params[:redo] - - if idv_session.skip_hybrid_handoff? - # We previously skipped hybrid handoff. Keep doing that. - idv_session.flow_path = 'standard' - end - - if !FeatureManagement.idv_allow_hybrid_flow? - # When hybrid flow is unavailable, skip it. - # But don't store that we skipped it in idv_session, in case it is back to - # available when the user tries to redo document capture. - idv_session.flow_path = 'standard' - end - - if idv_session.flow_path == 'standard' - redirect_to idv_document_capture_url - elsif idv_session.flow_path == 'hybrid' - redirect_to idv_link_sent_url - end - end - - def setup_for_redo - idv_session.redo_document_capture = true - - # If we previously skipped hybrid handoff for the user (because they're on a mobile - # device with a camera), skip it _again_ here. - if idv_session.skip_hybrid_handoff? - idv_session.flow_path = 'standard' - else - idv_session.flow_path = nil - end - end - def formatted_destination_phone raw_phone = params.require(:doc_auth).permit(:phone) PhoneFormatter.format(raw_phone, country_code: 'US') diff --git a/app/controllers/idv/phone_question_controller.rb b/app/controllers/idv/phone_question_controller.rb new file mode 100644 index 00000000000..2705fc12170 --- /dev/null +++ b/app/controllers/idv/phone_question_controller.rb @@ -0,0 +1,21 @@ +module Idv + class PhoneQuestionController < ApplicationController + include ActionView::Helpers::DateHelper + include IdvStepConcern + include StepIndicatorConcern + + before_action :confirm_verify_info_step_needed + before_action :confirm_agreement_step_complete + before_action :confirm_hybrid_handoff_needed, only: :show + + def show + @title = t('doc_auth.headings.phone_question') + end + + def confirm_agreement_step_complete + return if idv_session.idv_consent_given + + redirect_to idv_agreement_url + end + end +end diff --git a/app/views/idv/phone_question/show.html.erb b/app/views/idv/phone_question/show.html.erb new file mode 100644 index 00000000000..7faff61162b --- /dev/null +++ b/app/views/idv/phone_question/show.html.erb @@ -0,0 +1,32 @@ +<% title @title %> + +<% content_for(:pre_flash_content) do %> + <%= render StepIndicatorComponent.new( + steps: Idv::StepIndicatorConcern::STEP_INDICATOR_STEPS, + current_step: :verify_id, + locale_scope: 'idv', + class: 'margin-x-neg-2 margin-top-neg-4 tablet:margin-x-neg-6 tablet:margin-top-neg-4', + ) %> +<% end %> + +<%= render PageHeadingComponent.new.with_content(@title) %> + +
+ <%= button_to( + '', + method: 'get', + class: 'usa-button usa-button--wide usa-button--big', + ) do %> + <%= t('doc_auth.buttons.have_phone') %> + <% end %> +
+ +

+ + <%= link_to( + t('doc_auth.phone_question.do_not_have'), + '', + ) %> +

+ +<%= render 'idv/doc_auth/cancel', step: 'phone_question' %> diff --git a/config/locales/doc_auth/en.yml b/config/locales/doc_auth/en.yml index 3045f1eba57..b5f01497c83 100644 --- a/config/locales/doc_auth/en.yml +++ b/config/locales/doc_auth/en.yml @@ -8,6 +8,7 @@ en: buttons: add_new_photos: Add new photos continue: Continue + have_phone: Yes, I have a phone take_or_upload_picture_html: 'Take photo or Upload photo' take_picture: Take photo @@ -143,6 +144,7 @@ en: hybrid_handoff: How would you like to add your ID? interstitial: We are processing your images lets_go: How verifying your identity works + phone_question: Do you have a phone you can use to take photos of your ID? review_issues: Check your images and try again secure_account: Secure your account ssn: Enter your Social Security number @@ -230,6 +232,8 @@ en: - 'Verify by mail: We’ll mail a letter to your home address. This takes 5 to 10 days.' welcome: 'You will need your:' + phone_question: + do_not_have: I don’t have a phone tips: document_capture_header_text: 'For best results:' document_capture_hint: Must be a JPG or PNG diff --git a/config/locales/doc_auth/es.yml b/config/locales/doc_auth/es.yml index 324beeb3615..96d3c2dad44 100644 --- a/config/locales/doc_auth/es.yml +++ b/config/locales/doc_auth/es.yml @@ -8,6 +8,7 @@ es: buttons: add_new_photos: Añadir nuevas fotos continue: Continuar + have_phone: Sí tengo teléfono take_or_upload_picture_html: 'Toma una foto o Sube una foto' take_picture: Toma una foto @@ -172,6 +173,8 @@ es: hybrid_handoff: '¿Cómo desea añadir su documento de identidad?' interstitial: Estamos procesando sus imágenes lets_go: Cómo funciona la verificación de su identidad + phone_question: ¿Tiene un teléfono con el que pueda tomar fotos de su documento + de identidad? review_issues: Revise sus imágenes e inténtelo de nuevo secure_account: Asegure su cuenta ssn: Ingrese su número de Seguro Social @@ -267,6 +270,8 @@ es: - 'Verificar por correo: Le enviaremos una carta a su domicilio. Esto tarda entre 5 y 10 días.' welcome: 'Necesitará su:' + phone_question: + do_not_have: No tengo teléfono tips: document_capture_header_text: 'Para obtener los mejores resultados:' document_capture_hint: Debe ser un JPG o PNG diff --git a/config/locales/doc_auth/fr.yml b/config/locales/doc_auth/fr.yml index cfdf5e88925..3ec6678a271 100644 --- a/config/locales/doc_auth/fr.yml +++ b/config/locales/doc_auth/fr.yml @@ -8,6 +8,7 @@ fr: buttons: add_new_photos: Ajoutez de nouvelles photos continue: Continuer + have_phone: Oui j’ai un téléphone take_or_upload_picture_html: 'Prendre une photo ou Télécharger une photo' take_picture: Prendre une photo @@ -178,6 +179,8 @@ fr: hybrid_handoff: Comment voulez-vous ajouter votre identifiant ? interstitial: Nous traitons vos images lets_go: Comment fonctionne la vérification de votre identité + phone_question: Avez-vous un téléphone que vous pouvez utiliser pour prendre des + photos de votre identifiant? review_issues: Vérifiez vos images et essayez à nouveau secure_account: Sécuriser votre compte ssn: Saisissez votre numéro de sécurité sociale @@ -276,6 +279,8 @@ fr: lettre à votre adresse personnelle. Cela prend 5 à 10 jours.' welcome: 'Vous aurez besoin de votre:' + phone_question: + do_not_have: Je n’ai pas de téléphone tips: document_capture_header_text: 'Pour obtenir les meilleurs résultats:' document_capture_hint: Doit être un JPG ou PNG diff --git a/config/routes.rb b/config/routes.rb index c453446771f..597c7019208 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -350,6 +350,7 @@ put '/phone_confirmation' => 'otp_verification#update', as: :nil get '/review' => 'review#new' put '/review' => 'review#create' + get '/phone_question' => 'phone_question#show' get '/session/errors/warning' => 'session_errors#warning' get '/session/errors/state_id_warning' => 'session_errors#state_id_warning' get '/phone/errors/timeout' => 'phone_errors#timeout' diff --git a/spec/controllers/idv/phone_question_controller_spec.rb b/spec/controllers/idv/phone_question_controller_spec.rb new file mode 100644 index 00000000000..d07a2da4d95 --- /dev/null +++ b/spec/controllers/idv/phone_question_controller_spec.rb @@ -0,0 +1,104 @@ +require 'rails_helper' + +RSpec.describe Idv::PhoneQuestionController do + include IdvHelper + + let(:user) { create(:user) } + + before do + stub_sign_in(user) + stub_analytics + stub_attempts_tracker + subject.user_session['idv/doc_auth'] = {} + subject.idv_session.idv_consent_given = true + end + + describe 'before_actions' do + it 'includes authentication before_action' do + expect(subject).to have_actions( + :before, + :confirm_two_factor_authenticated, + ) + end + + it 'includes outage before_action' do + expect(subject).to have_actions( + :before, + :check_for_mail_only_outage, + ) + end + + it 'checks that agreement step is complete' do + expect(subject).to have_actions( + :before, + :confirm_agreement_step_complete, + ) + end + + it 'checks that hybrid_handoff is needed' do + expect(subject).to have_actions( + :before, + :confirm_hybrid_handoff_needed, + ) + end + end + + describe '#show' do + it 'renders the show template' do + get :show + + expect(response).to render_template :show + end + + context 'agreement step is not complete' do + before do + subject.idv_session.idv_consent_given = nil + end + + it 'redirects to idv_agreement_url' do + get :show + + expect(response).to redirect_to(idv_agreement_url) + end + end + + context 'hybrid_handoff already visited' do + it 'redirects to document_capture in standard flow' do + subject.idv_session.flow_path = 'standard' + + get :show + + expect(response).to redirect_to(idv_document_capture_url) + end + + it 'redirects to link_sent in hybrid flow' do + subject.idv_session.flow_path = 'hybrid' + + get :show + + expect(response).to redirect_to(idv_link_sent_url) + end + end + + context 'on mobile device' do + it 'redirects to hybrid_handoff controller' do + subject.idv_session.skip_hybrid_handoff = true + + get :show + + expect(response).to redirect_to(idv_document_capture_url) + end + end + + context 'hybrid flow is not available' do + before do + allow(FeatureManagement).to receive(:idv_allow_hybrid_flow?).and_return(false) + end + + it 'redirects the user straight to document capture' do + get :show + expect(response).to redirect_to(idv_document_capture_url) + end + end + end +end diff --git a/spec/features/idv/doc_auth/phone_question_spec.rb b/spec/features/idv/doc_auth/phone_question_spec.rb new file mode 100644 index 00000000000..79d1faf6bd5 --- /dev/null +++ b/spec/features/idv/doc_auth/phone_question_spec.rb @@ -0,0 +1,45 @@ +require 'rails_helper' + +RSpec.feature 'phone question step', :js do + include IdvStepHelper + include DocAuthHelper + + let(:fake_analytics) { FakeAnalytics.new } + + before do + allow_any_instance_of(ApplicationController).to receive(:analytics).and_return(fake_analytics) + sign_in_and_2fa_user + complete_doc_auth_steps_before_hybrid_handoff_step + visit(idv_phone_question_url) + end + + it 'contains phone question header' do + expect(page).to have_content(t('doc_auth.headings.phone_question')) + end + + it 'contains option to confirm having phone' do + expect(page).to have_content(t('doc_auth.buttons.have_phone')) + end + + it 'contains option to confirm not having phone' do + expect(page).to have_content(t('doc_auth.phone_question.do_not_have')) + end + + it 'allows user to cancel identify verification' do + click_link t('links.cancel') + expect(page).to have_current_path(idv_cancel_path(step: 'phone_question')) + + expect(fake_analytics).to have_logged_event( + 'IdV: cancellation visited', + hash_including(step: 'phone_question'), + ) + + click_spinner_button_and_wait t('idv.cancel.actions.account_page') + + expect(current_path).to eq(account_path) + expect(fake_analytics).to have_logged_event( + 'IdV: cancellation confirmed', + hash_including(step: 'phone_question'), + ) + end +end