diff --git a/app/controllers/concerns/idv/getting_started_ab_test_concern.rb b/app/controllers/concerns/idv/getting_started_ab_test_concern.rb
new file mode 100644
index 00000000000..212404a699c
--- /dev/null
+++ b/app/controllers/concerns/idv/getting_started_ab_test_concern.rb
@@ -0,0 +1,13 @@
+module Idv
+ module GettingStartedAbTestConcern
+ def getting_started_a_b_test_bucket
+ AbTests::IDV_GETTING_STARTED.bucket(sp_session[:request_id] || session.id)
+ end
+
+ def maybe_redirect_for_getting_started_ab_test
+ return if getting_started_a_b_test_bucket != :getting_started
+
+ redirect_to idv_getting_started_url
+ end
+ end
+end
diff --git a/app/controllers/idv/getting_started_controller.rb b/app/controllers/idv/getting_started_controller.rb
new file mode 100644
index 00000000000..e0a38117053
--- /dev/null
+++ b/app/controllers/idv/getting_started_controller.rb
@@ -0,0 +1,84 @@
+module Idv
+ class GettingStartedController < ApplicationController
+ include IdvStepConcern
+ include StepUtilitiesConcern
+
+ before_action :confirm_agreement_needed
+
+ def show
+ analytics.idv_doc_auth_getting_started_visited(**analytics_arguments)
+
+ # Register both Welcome and Agreement steps in DocAuthLog
+ Funnel::DocAuth::RegisterStep.new(current_user.id, sp_session[:issuer]).
+ call('welcome', :view, true)
+ Funnel::DocAuth::RegisterStep.new(current_user.id, sp_session[:issuer]).
+ call('agreement', :view, true)
+
+ @sp_name = decorated_session.sp_name || APP_NAME
+ @title = t('doc_auth.headings.getting_started', sp_name: @sp_name)
+
+ render :show, locals: { flow_session: flow_session }
+ end
+
+ def update
+ flow_session[:skip_upload_step] = true unless FeatureManagement.idv_allow_hybrid_flow?
+ skip_to_capture if params[:skip_upload]
+
+ result = Idv::ConsentForm.new.submit(consent_form_params)
+
+ analytics.idv_doc_auth_getting_started_submitted(
+ **analytics_arguments.merge(result.to_h),
+ )
+
+ if result.success?
+ idv_session.idv_consent_given = true
+
+ create_document_capture_session
+ cancel_previous_in_person_enrollments
+
+ redirect_to idv_hybrid_handoff_url
+ else
+ redirect_to idv_getting_started_url
+ end
+ end
+
+ private
+
+ def analytics_arguments
+ {
+ step: 'getting_started',
+ analytics_id: 'Doc Auth',
+ irs_reproofing: irs_reproofing?,
+ }
+ end
+
+ def create_document_capture_session
+ document_capture_session = DocumentCaptureSession.create(
+ user_id: current_user.id,
+ issuer: sp_session[:issuer],
+ )
+ flow_session[:document_capture_session_uuid] = document_capture_session.uuid
+ end
+
+ def cancel_previous_in_person_enrollments
+ return unless IdentityConfig.store.in_person_proofing_enabled
+ UspsInPersonProofing::EnrollmentHelper.
+ cancel_stale_establishing_enrollments_for_user(current_user)
+ end
+
+ def skip_to_capture
+ flow_session[:skip_upload_step] = true
+ idv_session.flow_path = 'standard'
+ end
+
+ def consent_form_params
+ params.require(:doc_auth).permit(:ial2_consent_given)
+ end
+
+ def confirm_agreement_needed
+ return unless idv_session.idv_consent_given
+
+ redirect_to idv_hybrid_handoff_url
+ end
+ end
+end
diff --git a/app/controllers/idv/welcome_controller.rb b/app/controllers/idv/welcome_controller.rb
index d4913074e27..f352e6ca4ec 100644
--- a/app/controllers/idv/welcome_controller.rb
+++ b/app/controllers/idv/welcome_controller.rb
@@ -3,18 +3,18 @@ class WelcomeController < ApplicationController
include IdvStepConcern
include StepIndicatorConcern
include StepUtilitiesConcern
+ include GettingStartedAbTestConcern
before_action :confirm_welcome_needed
+ before_action :maybe_redirect_for_getting_started_ab_test
def show
analytics.idv_doc_auth_welcome_visited(**analytics_arguments)
- Funnel::DocAuth::RegisterStep.new(current_user.id, sp_session[:issuer]).call(
- 'welcome', :view,
- true
- )
+ Funnel::DocAuth::RegisterStep.new(current_user.id, sp_session[:issuer]).
+ call('welcome', :view, true)
- render :show, locals: { flow_session: flow_session }
+ render :show
end
def update
diff --git a/app/services/analytics_events.rb b/app/services/analytics_events.rb
index ed4c3385dbc..cb522455b77 100644
--- a/app/services/analytics_events.rb
+++ b/app/services/analytics_events.rb
@@ -682,10 +682,17 @@ def idv_doc_auth_exception_visited(step_name:, remaining_attempts:, **extra)
)
end
+ def idv_doc_auth_getting_started_submitted(**extra)
+ track_event('IdV: doc auth getting_started submitted', **extra)
+ end
+
+ def idv_doc_auth_getting_started_visited(**extra)
+ track_event('IdV: doc auth getting_started visited', **extra)
+ end
+
# The "hybrid handoff" step: Desktop user has submitted their choice to
# either continue via desktop ("document_capture" destination) or switch
# to mobile phone ("send_link" destination) to perform document upload.
- # Mobile users still log this event but with skip_upload_step = true
# @identity.idp.previous_event_name IdV: doc auth upload submitted
def idv_doc_auth_hybrid_handoff_submitted(**extra)
track_event('IdV: doc auth hybrid handoff submitted', **extra)
diff --git a/app/services/marketing_site.rb b/app/services/marketing_site.rb
index d18eba07056..02748dd4f5f 100644
--- a/app/services/marketing_site.rb
+++ b/app/services/marketing_site.rb
@@ -15,6 +15,7 @@ class MarketingSite
verify-your-identity/verify-your-identity-in-person
verify-your-identity/phone-number
verify-your-identity/verify-your-address-by-mail
+ verify-your-identity/how-to-verify-your-identity
].to_set.freeze
def self.locale_segment
diff --git a/app/views/idv/getting_started/show.html.erb b/app/views/idv/getting_started/show.html.erb
new file mode 100644
index 00000000000..99d27e1b30e
--- /dev/null
+++ b/app/views/idv/getting_started/show.html.erb
@@ -0,0 +1,100 @@
+<% title @title %>
+
+<%= render 'shared/maintenance_window_alert' do %>
+ <%= render JavascriptRequiredComponent.new(
+ header: t('idv.getting_started.no_js_header'),
+ intro: t('idv.getting_started.no_js_intro', sp_name: @sp_name),
+ ) do %>
+
+ <% if @current_user&.reproof_for_irs?(service_provider: @current_sp) %>
+ <%= render AlertComponent.new(
+ type: :info,
+ message: t('doc_auth.info.irs_reproofing_explanation'),
+ class: ['margin-bottom-2', 'usa-alert--info-important'],
+ )
+ %>
+ <% end %>
+
+ <%= render AlertComponent.new(
+ type: :error,
+ class: [
+ 'js-consent-form-alert',
+ 'margin-bottom-4',
+ flow_session[:error_message].blank? && 'display-none',
+ ].select(&:present?),
+ message: flow_session[:error_message].presence || t('errors.doc_auth.consent_form'),
+ ) %>
+
+ <%= render PageHeadingComponent.new.with_content(@title) %>
+
+ <%= t(
+ 'doc_auth.info.getting_started_html',
+ sp_name: @sp_name,
+ link_html: new_tab_link_to(
+ t('doc_auth.info.getting_started_learn_more'),
+ help_center_redirect_path(
+ category: 'verify-your-identity',
+ article: 'how-to-verify-your-identity',
+ flow: :idv,
+ step: :getting_started,
+ location: 'intro_paragraph',
+ ),
+ ),
+ ) %>
+
+
+ <%= t('doc_auth.getting_started.instructions.getting_started') %>
+
+ <%= render ProcessListComponent.new(heading_level: :h3, class: 'margin-y-3') do |c| %>
+ <%= c.with_item(heading: t('doc_auth.getting_started.instructions.bullet1')) do %>
+ <%= t('doc_auth.getting_started.instructions.text1') %>
+ <% end %>
+ <%= c.with_item(heading: t('doc_auth.getting_started.instructions.bullet2')) do %>
+ <%= t('doc_auth.getting_started.instructions.text2') %>
+ <% end %>
+ <%= c.with_item(heading: t('doc_auth.getting_started.instructions.bullet3')) do %>
+ <%= t('doc_auth.getting_started.instructions.text3') %>
+ <% end %>
+ <%= c.with_item(heading: t('doc_auth.getting_started.instructions.bullet4', app_name: APP_NAME)) do %>
+ <%= t('doc_auth.getting_started.instructions.text4') %>
+ <% end %>
+ <% end %>
+
+ <%= simple_form_for(
+ :doc_auth,
+ url: url_for,
+ method: 'put',
+ html: { autocomplete: 'off', class: 'margin-top-2 margin-bottom-5 js-consent-continue-form' },
+ ) do |f| %>
+ <%= render ClickObserverComponent.new(event_name: 'IdV: consent checkbox toggled') do %>
+ <%= render ValidatedFieldComponent.new(
+ form: f,
+ name: :ial2_consent_given,
+ as: :boolean,
+ label: t('doc_auth.getting_started.instructions.consent', app_name: APP_NAME),
+ required: true,
+ ) %>
+ <% end %>
+
+ <%= new_tab_link_to(
+ t('doc_auth.getting_started.instructions.learn_more'),
+ policy_redirect_url(flow: :idv, step: :getting_started, location: :consent),
+ ) %>
+
+
+ <%= render(
+ SpinnerButtonComponent.new(
+ type: :submit,
+ big: true,
+ wide: true,
+ spin_on_click: false,
+ ).with_content(t('doc_auth.buttons.continue')),
+ ) %>
+
+ <% end %>
+
+ <%= render 'shared/cancel', link: idv_cancel_path(step: 'getting_started') %>
+ <% end %>
+<% end %>
+
+<%= javascript_packs_tag_once('document-capture-welcome') %>
diff --git a/app/views/idv/welcome/show.html.erb b/app/views/idv/welcome/show.html.erb
index 6a852e68406..c5db6c7538a 100644
--- a/app/views/idv/welcome/show.html.erb
+++ b/app/views/idv/welcome/show.html.erb
@@ -12,7 +12,7 @@
<%= render 'shared/maintenance_window_alert' do %>
<%= render JavascriptRequiredComponent.new(
header: t('idv.welcome.no_js_header'),
- intro: t('idv.welcome.no_js_intro', sp_name: decorated_session.sp_name || t('doc_auth.info.no_sp_name')),
+ intro: t('idv.welcome.no_js_intro', sp_name: decorated_session.sp_name || APP_NAME),
) do %>
<% if @current_user&.reproof_for_irs?(service_provider: @current_sp) %>
@@ -26,7 +26,7 @@
<%= render PageHeadingComponent.new.with_content(t('doc_auth.headings.welcome')) %>
- <%= t('doc_auth.info.welcome', sp_name: decorated_session.sp_name || t('doc_auth.info.no_sp_name')) %>
+ <%= t('doc_auth.info.welcome', sp_name: decorated_session.sp_name || APP_NAME) %>
<%= t('doc_auth.instructions.welcome') %>
diff --git a/config/application.yml.default b/config/application.yml.default
index 529d15b7716..2261141a3bf 100644
--- a/config/application.yml.default
+++ b/config/application.yml.default
@@ -127,6 +127,7 @@ idv_acuant_sdk_version_default: '11.8.2'
idv_acuant_sdk_version_alternate: '11.8.1'
idv_acuant_sdk_upgrade_a_b_testing_enabled: false
idv_acuant_sdk_upgrade_a_b_testing_percent: 50
+idv_getting_started_a_b_testing: '{"welcome":100,"getting_started":0}'
idv_send_link_attempt_window_in_minutes: 10
idv_send_link_max_attempts: 5
idv_tmx_test_csp_disabled_emails: '[]'
diff --git a/config/initializers/ab_tests.rb b/config/initializers/ab_tests.rb
index 4df55b07756..2d37e294cc4 100644
--- a/config/initializers/ab_tests.rb
+++ b/config/initializers/ab_tests.rb
@@ -18,4 +18,9 @@ module AbTests
0,
},
)
+
+ IDV_GETTING_STARTED = AbTestBucket.new(
+ experiment_name: 'Idv: Getting Started Experience',
+ buckets: IdentityConfig.store.idv_getting_started_a_b_testing,
+ )
end
diff --git a/config/locales/doc_auth/en.yml b/config/locales/doc_auth/en.yml
index c5f00f01cba..bba28cd9e58 100644
--- a/config/locales/doc_auth/en.yml
+++ b/config/locales/doc_auth/en.yml
@@ -100,6 +100,23 @@ en:
choose_file_html: Drag file here or choose from folder
doc_success: We verified your information
selected_file: Selected file
+ getting_started:
+ instructions:
+ bullet1: Add photos of your ID
+ bullet2: Enter your Social Security number
+ bullet3: Match to your phone number
+ bullet4: Re-enter your %{app_name} password
+ consent: By checking this box, you are letting %{app_name} ask for, use, keep,
+ and share your personal information. We will use it to verify your
+ identity.
+ getting_started: 'You’ll need to:'
+ learn_more: Learn more about our privacy and security measures
+ text1: Use your driver’s license or state ID card. Other forms of ID are not
+ accepted.
+ text2: You will not need your physical SSN card.
+ text3: Your phone number matches you to your personal information. After you
+ match, we’ll send you a code.
+ text4: Your password saves and encrypts your personal information.
headings:
address: Update your mailing address
back: Back
@@ -113,6 +130,7 @@ en:
document_capture_back: Back of your ID
document_capture_front: Front of your ID
front: Front
+ getting_started: Let’s verify your identity for %{sp_name}
hybrid_handoff: How would you like to add your ID?
interstitial: We are processing your images
lets_go: How verifying your identity works
@@ -146,6 +164,9 @@ en:
document_capture_intro_acknowledgment: We’ll collect information about you by
reading your state‑issued ID. We use this information to verify your
identity.
+ getting_started_html: '%{sp_name} needs to make sure you are you — not someone
+ pretending to be you. %{link_html}'
+ getting_started_learn_more: Learn more about what you need to verify your identity
hybrid_handoff: We’ll collect information about you by reading your state‑issued ID.
image_loaded: Image loaded
image_loading: Image loading
@@ -161,7 +182,6 @@ en:
your state‑issued ID.
link_sent_complete_no_polling: When you are done, click Continue here to finish verifying your identity.
link_sent_complete_polling: The next step will load automatically.
- no_sp_name: The agency that you are trying to access
privacy: '%{app_name} is a secure, government website that adheres to the
highest standards in data protection. We use your data to verify your
identity.'
diff --git a/config/locales/doc_auth/es.yml b/config/locales/doc_auth/es.yml
index 7c8caa16b9a..a71f8b4e2e7 100644
--- a/config/locales/doc_auth/es.yml
+++ b/config/locales/doc_auth/es.yml
@@ -125,6 +125,23 @@ es:
carpeta
doc_success: Verificamos sus datos
selected_file: Archivo seleccionado
+ getting_started:
+ instructions:
+ bullet1: Incluir fotos de su identificación
+ bullet2: Introducir su número de Seguro Social
+ bullet3: Vincular su número de teléfono
+ bullet4: Volver a introducir su contraseña de %{app_name}
+ consent: Al marcar esta casilla, usted permite que %{app_name} solicite,
+ utilice, conserve y comparta su información personal. Los utilizamos
+ para verificar su identidad.
+ getting_started: 'Deberá:'
+ learn_more: Obtenga más información sobre nuestras medidas de privacidad y
+ seguridad
+ text1: Su documento de identidad no puede estar caducado.
+ text2: No necesitará la tarjeta con usted.
+ text3: Su número de teléfono se asocia a su información personal. Después de que
+ lo haya asociado, le enviaremos un código.
+ text4: Su contraseña guarda y encripta su información personal.
headings:
address: Actualice su dirección postal
back: Parte Trasera
@@ -138,6 +155,7 @@ es:
document_capture_back: Parte trasera de su documento de identidad
document_capture_front: Parte delantera de su documento de identidad
front: Parte Delantera
+ getting_started: Vamos a verificar su identidad para %{sp_name}
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
@@ -174,6 +192,9 @@ es:
document_capture_intro_acknowledgment: Recopilaremos información sobre usted
leyendo su documento de identidad expedido por el Estado. Usamos esta
información para verificar su identidad.
+ getting_started_html: '%{sp_name} necesita asegurarse de que es usted realmente
+ y no alguien que se hace pasar por usted. %{link_html}'
+ getting_started_learn_more: Obtenga más información sobre lo que necesita para verificar su identidad
hybrid_handoff: Recopilaremos información sobre usted leyendo su documento de
identidad expedido por el estado.
image_loaded: Imagen cargada
@@ -193,7 +214,6 @@ es:
completar la verificación de tu identidad.
link_sent_complete_polling: El siguiente paso se cargará automáticamente una vez
que verifiques tu identidad a través de tu teléfono.
- no_sp_name: La agencia a la que está intentando acceder
privacy: '%{app_name} es un sitio web gubernamental seguro que cumple con las
normas más estrictas de protección de datos. Utilizamos sus datos para
verificar su identidad.'
diff --git a/config/locales/doc_auth/fr.yml b/config/locales/doc_auth/fr.yml
index cedab5fca79..02c71951d7a 100644
--- a/config/locales/doc_auth/fr.yml
+++ b/config/locales/doc_auth/fr.yml
@@ -131,6 +131,23 @@ fr:
dossier
doc_success: Nous avons vérifié vos informations
selected_file: Fichier sélectionné
+ getting_started:
+ instructions:
+ bullet1: Ajoutez des photos de votre pièce d’identité
+ bullet2: Saisissez votre numéro de sécurité sociale
+ bullet3: Faire correspondre à votre numéro de téléphone
+ bullet4: Saisissez à nouveau votre mot de passe %{app_name}
+ consent: En cochant cette case, vous autorisez %{app_name} à demander, utiliser,
+ conserver et partager vos renseignements personnels. Nous les
+ utilisons pour vérifier votre identité.
+ getting_started: 'Vous aurez besoin de :'
+ learn_more: En savoir plus sur nos mesures de confidentialité et de sécurité
+ text1: Utilisez votre permis de conduire ou votre carte d’identité de l’État.
+ Les autres pièces d’identité ne sont pas acceptées.
+ text2: Vous n’aurez pas besoin de votre carte SSN physique.
+ text3: Votre numéro de téléphone correspond à vos informations personnelles. Une
+ fois la correspondance établie, nous vous enverrons un code.
+ text4: Votre mot de passe sauvegarde et crypte vos informations personnelles.
headings:
address: Mettre à jour votre adresse postale
back: Verso
@@ -144,6 +161,7 @@ fr:
document_capture_back: Verso de votre carte d’identité
document_capture_front: Recto de votre carte d’identité
front: Recto
+ getting_started: Vérifions votre identité pour %{sp_name}
hybrid_handoff: Comment voulez-vous ajouter votre identifiant ?
interstitial: Nous traitons vos images
lets_go: Comment fonctionne la vérification de votre identité
@@ -179,6 +197,9 @@ fr:
document_capture_intro_acknowledgment: Nous recueillons des informations sur
vous en lisant votre pièce d’identité délivrée par l’État. Nous
utilisons ces informations pour vérifier votre identité.
+ getting_started_html: '%{sp_name} doit s’assurer que c’est bien vous — et non
+ quelqu’un qui se fait passer pour vous. %{link_html}'
+ getting_started_learn_more: En savoir plus sur ce dont vous avez besoin pour vérifier votre identité
hybrid_handoff: Nous recueillons des informations sur vous en lisant votre carte
d’identité délivrée par l’État.
image_loaded: Image chargée
@@ -200,7 +221,6 @@ fr:
link_sent_complete_polling: L’étape suivante se chargera automatiquement une
fois que vous aurez confirmé votre identifiant à l’aide de votre
téléphone.
- no_sp_name: L’agence à laquelle vous essayez d’accéder
privacy: '%{app_name} est un site gouvernemental sécurisé qui respecte les
normes les plus strictes en matière de protection des données. Nous
utilisons vos données pour vérifier votre identité.'
diff --git a/config/locales/idv/en.yml b/config/locales/idv/en.yml
index 31a9a32e45f..0c2fe8e310d 100644
--- a/config/locales/idv/en.yml
+++ b/config/locales/idv/en.yml
@@ -166,6 +166,10 @@ en:
ssn_label: Social Security number
state: State
zipcode: ZIP Code
+ getting_started:
+ no_js_header: You must enable JavaScript to verify your identity.
+ no_js_intro: '%{sp_name} needs you to verify your identity. You need to enable
+ JavaScript to continue this process.'
images:
come_back_later: Letter with a check mark
index:
diff --git a/config/locales/idv/es.yml b/config/locales/idv/es.yml
index 54626089440..dd8a0e31292 100644
--- a/config/locales/idv/es.yml
+++ b/config/locales/idv/es.yml
@@ -174,6 +174,10 @@ es:
ssn_label: Número de Seguro Social
state: Estado
zipcode: Código postal
+ getting_started:
+ no_js_header: Debe habilitar JavaScript para verificar su identidad.
+ no_js_intro: '%{sp_name} requiere que usted verifique su identidad. Debe
+ habilitar JavaScript para continuar con este proceso.'
images:
come_back_later: Carta con una marca de verificación
index:
diff --git a/config/locales/idv/fr.yml b/config/locales/idv/fr.yml
index 5e7d2fa128e..adc95adb52f 100644
--- a/config/locales/idv/fr.yml
+++ b/config/locales/idv/fr.yml
@@ -181,6 +181,10 @@ fr:
ssn_label: Numéro de sécurité sociale
state: État
zipcode: Code postal
+ getting_started:
+ no_js_header: Vous devez activer JavaScript pour vérifier votre identité.
+ no_js_intro: '%{sp_name} a besoin de vous pour vérifier votre identité. Vous
+ devez activer JavaScript pour poursuivre ce processus.'
images:
come_back_later: Lettre avec un crochet
index:
diff --git a/config/routes.rb b/config/routes.rb
index 26ecb855193..65fb8ce61ea 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -330,6 +330,8 @@
# This route is included in SMS messages sent to users who start the IdV hybrid flow. It
# should be kept short, and should not include underscores ("_").
get '/documents' => 'hybrid_mobile/entry#show', as: :hybrid_mobile_entry
+ get '/getting_started' => 'getting_started#show'
+ put '/getting_started' => 'getting_started#update'
get '/hybrid_mobile/document_capture' => 'hybrid_mobile/document_capture#show'
put '/hybrid_mobile/document_capture' => 'hybrid_mobile/document_capture#update'
get '/hybrid_mobile/capture_complete' => 'hybrid_mobile/capture_complete#show'
diff --git a/lib/identity_config.rb b/lib/identity_config.rb
index 2b122bb88a3..9119edc93f1 100644
--- a/lib/identity_config.rb
+++ b/lib/identity_config.rb
@@ -221,6 +221,7 @@ def self.build_store(config_map)
config.add(:idv_acuant_sdk_version_alternate, type: :string)
config.add(:idv_acuant_sdk_upgrade_a_b_testing_enabled, type: :boolean)
config.add(:idv_acuant_sdk_upgrade_a_b_testing_percent, type: :integer)
+ config.add(:idv_getting_started_a_b_testing, type: :json, options: { symbolize_names: true })
config.add(:idv_send_link_attempt_window_in_minutes, type: :integer)
config.add(:idv_send_link_max_attempts, type: :integer)
config.add(:idv_sp_required, type: :boolean)
diff --git a/spec/controllers/concerns/idv/getting_started_ab_test_concern_spec.rb b/spec/controllers/concerns/idv/getting_started_ab_test_concern_spec.rb
new file mode 100644
index 00000000000..464e40ddf96
--- /dev/null
+++ b/spec/controllers/concerns/idv/getting_started_ab_test_concern_spec.rb
@@ -0,0 +1,106 @@
+require 'rails_helper'
+
+RSpec.describe 'GettingStartedAbTestConcern' do
+ let(:user) { create(:user, :fully_registered, email: 'old_email@example.com') }
+ let(:idv_session) do
+ Idv::Session.new(user_session: subject.user_session, current_user: user, service_provider: nil)
+ end
+
+ module Idv
+ class StepController < ApplicationController
+ include GettingStartedAbTestConcern
+
+ def show
+ render plain: 'Hello'
+ end
+ end
+ end
+
+ describe '#getting_started_a_b_test_bucket' do
+ let(:sp_session) { {} }
+
+ controller Idv::StepController do
+ end
+
+ before do
+ allow(session).to receive(:id).and_return('session-id')
+ allow(controller).to receive(:sp_session).and_return(sp_session)
+ allow(AbTests::IDV_GETTING_STARTED).to receive(:bucket) do |discriminator|
+ case discriminator
+ when 'session-id'
+ :welcome
+ when 'request-id'
+ :getting_started
+ end
+ end
+ end
+
+ it 'returns the bucket based on session id' do
+ expect(controller.getting_started_a_b_test_bucket).to eq(:welcome)
+ end
+
+ context 'with associated sp session request id' do
+ let(:sp_session) { { request_id: 'request-id' } }
+
+ it 'returns the bucket based on request id' do
+ expect(controller.getting_started_a_b_test_bucket).to eq(:getting_started)
+ end
+ end
+ end
+
+ context '#maybe_redirect_for_getting_started_ab_test' do
+ controller Idv::StepController do
+ before_action :maybe_redirect_for_getting_started_ab_test
+ end
+
+ before do
+ sign_in(user)
+ routes.draw do
+ get 'show' => 'idv/step#show'
+ end
+ end
+
+ let(:session_uuid) { SecureRandom.uuid }
+
+ context 'A/B test specifies getting started page' do
+ before do
+ allow(controller).to receive(:getting_started_a_b_test_bucket).
+ and_return(:getting_started)
+ end
+
+ it 'redirects to idv_getting_started_url' do
+ get :show
+
+ expect(response).to redirect_to(idv_getting_started_url)
+ end
+ end
+
+ context 'A/B test specifies welcome page' do
+ before do
+ allow(controller).to receive(:getting_started_a_b_test_bucket).
+ and_return(:welcome)
+ end
+
+ it 'does not redirect users away from welcome page' do
+ get :show
+
+ expect(response.body).to eq('Hello')
+ expect(response.status).to eq(200)
+ end
+ end
+
+ context 'A/B test specifies some other value' do
+ before do
+ allow(controller).to receive(:getting_started_a_b_test_bucket).
+ and_return(:something_else)
+ end
+
+ it 'does not redirect users away from welcome page' do
+ get :show
+
+ expect(response.body).to eq('Hello')
+ expect(response.status).to eq(200)
+ end
+ end
+ end
+end
diff --git a/spec/controllers/idv/getting_started_controller_spec.rb b/spec/controllers/idv/getting_started_controller_spec.rb
new file mode 100644
index 00000000000..0eaaa0ac9c0
--- /dev/null
+++ b/spec/controllers/idv/getting_started_controller_spec.rb
@@ -0,0 +1,124 @@
+require 'rails_helper'
+
+RSpec.describe Idv::GettingStartedController do
+ include IdvHelper
+
+ let(:user) { create(:user) }
+
+ before do
+ stub_sign_in(user)
+ stub_analytics
+ subject.user_session['idv/doc_auth'] = {}
+ 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_outage,
+ )
+ end
+ end
+
+ describe '#show' do
+ let(:analytics_name) { 'IdV: doc auth getting_started visited' }
+ let(:analytics_args) do
+ { step: 'getting_started',
+ analytics_id: 'Doc Auth',
+ irs_reproofing: false }
+ end
+
+ it 'renders the show template' do
+ get :show
+
+ expect(response).to render_template :show
+ end
+
+ it 'sends analytics_visited event' do
+ get :show
+
+ expect(@analytics).to have_logged_event(analytics_name, analytics_args)
+ end
+
+ it 'updates DocAuthLog welcome_view_count' do
+ doc_auth_log = DocAuthLog.create(user_id: user.id)
+
+ expect { get :show }.to(
+ change { doc_auth_log.reload.welcome_view_count }.from(0).to(1),
+ )
+ end
+
+ it 'updates DocAuthLog agreement_view_count' do
+ doc_auth_log = DocAuthLog.create(user_id: user.id)
+
+ expect { get :show }.to(
+ change { doc_auth_log.reload.agreement_view_count }.from(0).to(1),
+ )
+ end
+
+ context 'getting_started already visited' do
+ it 'redirects to hybrid_handoff' do
+ subject.idv_session.idv_consent_given = true
+
+ get :show
+
+ expect(response).to redirect_to(idv_hybrid_handoff_url)
+ end
+ end
+
+ it 'redirects to please call page if fraud review is pending' do
+ profile = create(:profile, :fraud_review_pending)
+
+ stub_sign_in(profile.user)
+
+ get :show
+
+ expect(response).to redirect_to(idv_please_call_url)
+ end
+ end
+
+ describe '#update' do
+ let(:analytics_name) { 'IdV: doc auth getting_started submitted' }
+
+ let(:analytics_args) do
+ { success: true,
+ errors: {},
+ step: 'getting_started',
+ analytics_id: 'Doc Auth',
+ irs_reproofing: false }
+ end
+
+ it 'sends analytics_submitted event with consent given' do
+ put :update, params: { doc_auth: { ial2_consent_given: 1 } }
+
+ expect(@analytics).to have_logged_event(analytics_name, analytics_args)
+ end
+
+ it 'creates a document capture session' do
+ expect { put :update, params: { doc_auth: { ial2_consent_given: 1 } } }.
+ to change { subject.user_session['idv/doc_auth'][:document_capture_session_uuid] }.from(nil)
+ end
+
+ context 'with previous establishing in-person enrollments' do
+ let!(:enrollment) { create(:in_person_enrollment, :establishing, user: user, profile: nil) }
+
+ before do
+ allow(IdentityConfig.store).to receive(:in_person_proofing_enabled).and_return(true)
+ end
+
+ it 'cancels all previous establishing enrollments' do
+ put :update, params: { doc_auth: { ial2_consent_given: 1 } }
+
+ expect(enrollment.reload.status).to eq('cancelled')
+ expect(user.establishing_in_person_enrollment).to be_blank
+ end
+ end
+ end
+end
diff --git a/spec/controllers/idv/welcome_controller_spec.rb b/spec/controllers/idv/welcome_controller_spec.rb
index f8da3e66d5f..ca531e9414b 100644
--- a/spec/controllers/idv/welcome_controller_spec.rb
+++ b/spec/controllers/idv/welcome_controller_spec.rb
@@ -25,6 +25,13 @@
:check_for_outage,
)
end
+
+ it 'includes getting started ab test before_action' do
+ expect(subject).to have_actions(
+ :before,
+ :maybe_redirect_for_getting_started_ab_test,
+ )
+ end
end
describe '#show' do
diff --git a/spec/features/idv/doc_auth/getting_started_spec.rb b/spec/features/idv/doc_auth/getting_started_spec.rb
new file mode 100644
index 00000000000..661ce15beeb
--- /dev/null
+++ b/spec/features/idv/doc_auth/getting_started_spec.rb
@@ -0,0 +1,78 @@
+require 'rails_helper'
+
+RSpec.feature 'getting started step' do
+ include IdvHelper
+ include DocAuthHelper
+
+ let(:fake_analytics) { FakeAnalytics.new }
+ let(:maintenance_window) { [] }
+ let(:sp_name) { 'Test SP' }
+
+ before do
+ allow_any_instance_of(ApplicationController).to receive(:analytics).and_return(fake_analytics)
+ allow_any_instance_of(ServiceProviderSessionDecorator).to receive(:sp_name).and_return(sp_name)
+ stub_const('AbTests::IDV_GETTING_STARTED', FakeAbTestBucket.new)
+ AbTests::IDV_GETTING_STARTED.assign_all(:getting_started)
+
+ visit_idp_from_sp_with_ial2(:oidc)
+ sign_in_and_2fa_user
+ complete_doc_auth_steps_before_welcome_step
+ end
+
+ it 'displays expected content with javascript enabled', :js do
+ expect(page).to have_current_path(idv_getting_started_path)
+
+ # Try to continue with unchecked checkbox
+ click_continue
+ expect(page).to have_current_path(idv_getting_started_path)
+ expect(page).to have_content(t('forms.validation.required_checkbox'))
+
+ complete_getting_started_step
+ expect(page).to have_current_path(idv_hybrid_handoff_path)
+ end
+
+ it 'logs "intro_paragraph" learn more link click' do
+ click_on t('doc_auth.info.getting_started_learn_more')
+
+ expect(fake_analytics).to have_logged_event(
+ 'External Redirect',
+ step: 'getting_started',
+ location: 'intro_paragraph',
+ flow: 'idv',
+ redirect_url: MarketingSite.help_center_article_url(
+ category: 'verify-your-identity',
+ article: 'how-to-verify-your-identity',
+ ),
+ )
+ end
+
+ context 'when JS is disabled' do
+ it 'shows the notice if the user clicks continue without giving consent' do
+ click_continue
+
+ expect(page).to have_current_path(idv_getting_started_url)
+ expect(page).to have_content(t('errors.doc_auth.consent_form'))
+ end
+
+ it 'allows the user to continue after checking the checkbox' do
+ check t('doc_auth.instructions.consent', app_name: APP_NAME)
+ click_continue
+
+ expect(page).to have_current_path(idv_hybrid_handoff_path)
+ end
+ end
+
+ context 'skipping hybrid_handoff step', :js, driver: :headless_chrome_mobile do
+ before do
+ complete_getting_started_step
+ end
+
+ it 'progresses to document capture' do
+ expect(page).to have_current_path(idv_document_capture_url)
+ end
+ end
+
+ def complete_getting_started_step
+ complete_agreement_step # it does the right thing
+ end
+end
diff --git a/spec/views/idv/getting_started/show.html.erb_spec.rb b/spec/views/idv/getting_started/show.html.erb_spec.rb
new file mode 100644
index 00000000000..769e4e3fb31
--- /dev/null
+++ b/spec/views/idv/getting_started/show.html.erb_spec.rb
@@ -0,0 +1,103 @@
+require 'rails_helper'
+
+RSpec.describe 'idv/getting_started/show' do
+ let(:flow_session) { {} }
+ let(:user_fully_authenticated) { true }
+ let(:sp_name) { nil }
+ let(:user) { create(:user) }
+
+ before do
+ @decorated_session = instance_double(ServiceProviderSessionDecorator)
+ @sp_name = 'Login.gov'
+ @title = t('doc_auth.headings.getting_started', sp_name: @sp_name)
+ allow(@decorated_session).to receive(:sp_name).and_return(sp_name)
+ allow(view).to receive(:decorated_session).and_return(@decorated_session)
+ allow(view).to receive(:flow_session).and_return(flow_session)
+ allow(view).to receive(:user_fully_authenticated?).and_return(user_fully_authenticated)
+ allow(view).to receive(:user_signing_up?).and_return(false)
+ allow(view).to receive(:url_for).and_wrap_original do |method, *args, &block|
+ method.call(*args, &block)
+ rescue
+ ''
+ end
+ render
+ end
+
+ context 'in doc auth with an authenticated user' do
+ let(:need_irs_reproofing) { false }
+
+ before do
+ allow(user).to receive(:reproof_for_irs?).and_return(need_irs_reproofing)
+ assign(:current_user, user)
+
+ render
+ end
+
+ it 'does not render the IRS reproofing explanation' do
+ expect(rendered).not_to have_text(t('doc_auth.info.irs_reproofing_explanation'))
+ end
+
+ it 'renders a link to return to the SP' do
+ expect(rendered).to have_link(t('links.cancel'))
+ end
+
+ context 'when trying to log in to the IRS' do
+ let(:need_irs_reproofing) { true }
+
+ it 'renders the IRS reproofing explanation' do
+ expect(rendered).to have_text(t('doc_auth.info.irs_reproofing_explanation'))
+ end
+ end
+ end
+
+ context 'during the acuant maintenance window' do
+ let(:start) { Time.zone.parse('2020-01-01T00:00:00Z') }
+ let(:now) { Time.zone.parse('2020-01-01T12:00:00Z') }
+ let(:finish) { Time.zone.parse('2020-01-01T23:59:59Z') }
+
+ before do
+ allow(IdentityConfig.store).to receive(:acuant_maintenance_window_start).and_return(start)
+ allow(IdentityConfig.store).to receive(:acuant_maintenance_window_finish).and_return(finish)
+ end
+
+ around do |ex|
+ travel_to(now) { ex.run }
+ end
+
+ it 'renders the warning banner but no other content' do
+ render
+
+ expect(rendered).to have_content('We are currently under maintenance')
+ expect(rendered).to_not have_content(t('doc_auth.headings.welcome'))
+ end
+ end
+
+ it 'includes code to track clicks on the consent checkbox' do
+ selector = [
+ 'lg-click-observer[event-name="IdV: consent checkbox toggled"]',
+ '[name="doc_auth[ial2_consent_given]"]',
+ ].join ' '
+
+ expect(rendered).to have_css(selector)
+ end
+
+ it 'renders a link to help center article' do
+ expect(rendered).to have_link(
+ t('doc_auth.info.getting_started_learn_more'),
+ href: help_center_redirect_path(
+ category: 'verify-your-identity',
+ article: 'how-to-verify-your-identity',
+ flow: :idv,
+ step: :getting_started,
+ location: 'intro_paragraph',
+ ),
+ )
+ end
+
+ it 'renders a link to the privacy & security page' do
+ expect(rendered).to have_link(
+ t('doc_auth.getting_started.instructions.learn_more'),
+ href: policy_redirect_url(flow: :idv, step: :getting_started, location: :consent),
+ )
+ end
+end