-<% end %>
<%= render 'shared/cancel', link: idv_cancel_path(step: 'welcome') %>
<% end %>
<%= javascript_packs_tag_once('document-capture-welcome') %>
diff --git a/config/application.yml.default b/config/application.yml.default
index ea3d95f4d9e..86e01893ec2 100644
--- a/config/application.yml.default
+++ b/config/application.yml.default
@@ -90,6 +90,7 @@ database_worker_jobs_sslmode: 'verify-full'
database_worker_jobs_username: ''
deleted_user_accounts_report_configs: '[]'
deliver_mail_async: false
+desktop_ft_unlock_setup_option_percent_tested: 0
development_mailer_deliver_method: letter_opener
disable_email_sending: true
disable_logout_get_request: true
@@ -314,6 +315,7 @@ reauthn_window: 1200
recaptcha_enterprise_api_key: ''
recaptcha_enterprise_project_id: ''
recaptcha_mock_validator: true
+recaptcha_request_timeout_in_seconds: 5
recaptcha_secret_key: ''
recaptcha_site_key: ''
recommend_webauthn_platform_for_sms_ab_test_account_creation_percent: 0
@@ -458,6 +460,7 @@ development:
compromised_password_randomizer_value: 1
dashboard_api_token: test_token
dashboard_url: http://localhost:3001/api/service_providers
+ desktop_ft_unlock_setup_option_percent_tested: 100
doc_auth_selfie_desktop_test_mode: true
domain_name: localhost:3000
enable_rate_limiting: false
diff --git a/config/initializers/ab_tests.rb b/config/initializers/ab_tests.rb
index fff91a2dd65..c4a4cce8924 100644
--- a/config/initializers/ab_tests.rb
+++ b/config/initializers/ab_tests.rb
@@ -104,4 +104,15 @@ def self.all
shadow_mode_enabled: IdentityConfig.store.socure_idplus_shadow_mode_percent,
},
).freeze
+
+ DESKTOP_FT_UNLOCK_SETUP = AbTest.new(
+ experiment_name: 'Desktop F/T unlock setup',
+ should_log: [
+ 'User Registration: 2FA Setup visited',
+ :webauthn_setup_submitted,
+ 'Multi-Factor Authentication Setup',
+ ].to_set,
+ buckets: { desktop_ft_unlock_option_shown:
+ IdentityConfig.store.desktop_ft_unlock_setup_option_percent_tested },
+ ).freeze
end
diff --git a/config/initializers/strong_migrations.rb b/config/initializers/strong_migrations.rb
index b7e3d6c5a8a..8320ec2edb2 100644
--- a/config/initializers/strong_migrations.rb
+++ b/config/initializers/strong_migrations.rb
@@ -15,13 +15,13 @@ class IdpStrongMigrations
StrongMigrations.add_check do |method, (table, column, type, _options)|
is_excluded = IdpStrongMigrations::EXCLUDED_COLUMNS.include?([table, column])
if !is_excluded && method == :add_column && column.to_s.ends_with?('_id') && type == :integer
- stop! """
+ stop! "
Columns referencing another table should use :bigint instead of integer.
add_column #{table.inspect}, #{column.inspect}, :bigint
OR
t.bigint #{column.inspect}
- """
+ "
end
end
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 2ba1e98735e..7593fbd2ff2 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -537,11 +537,13 @@ doc_auth.errors.document_capture_canceled: You have canceled uploading photos of
doc_auth.errors.dpi.failed_short: Image is too small or blurry, please try again.
doc_auth.errors.dpi.top_msg: We couldn’t read your ID. Your image size may be too small, or your ID is too small or blurry in the photo. Make sure your ID is large within the image frame and try taking a new picture.
doc_auth.errors.dpi.top_msg_plural: We couldn’t read your ID. Your image sizes may be too small, or your ID is too small or blurry in the photos. Make sure your ID is large within the image frame and try taking new pictures.
+doc_auth.errors.expired_id: You cannot use an expired ID card for identity verification. If your ID card is not expired, make sure everything on your ID is in focus and that the card fills the frame.
doc_auth.errors.file_type.invalid: This file type is not accepted, please choose a JPG or PNG file.
doc_auth.errors.general.fallback_field_level: Please add a new image
doc_auth.errors.general.multiple_back_id_failures: We couldn’t verify the back of your ID. Try taking a new picture.
doc_auth.errors.general.multiple_front_id_failures: We couldn’t verify the front of your ID. Try taking a new picture.
doc_auth.errors.general.network_error: We are having technical difficulties on our end. Please try to submit your images again later.
+doc_auth.errors.general.new_network_error: Try again later.
doc_auth.errors.general.no_liveness: Try taking new pictures.
doc_auth.errors.general.selfie_failure: We couldn’t verify the photo of yourself. Try taking a new picture.
doc_auth.errors.general.selfie_failure_help_link_text: Review more tips for taking clear photos
@@ -555,6 +557,8 @@ doc_auth.errors.http.image_size.failed_short: Image file is not supported, pleas
doc_auth.errors.http.image_size.top_msg: Your image size is too large or too small. Please add images of your ID that are about 2025 x 1275 pixels.
doc_auth.errors.http.pixel_depth.failed_short: Image file is not supported, please try again.
doc_auth.errors.http.pixel_depth.top_msg: The pixel depth of your image file is not supported. Please take new photos of your ID and try again. Supported image pixel depth is 24-bit RGB.
+doc_auth.errors.id_not_found: Make sure you are taking a picture of your physical ID and not of a photo or screenshot.
+doc_auth.errors.low_resolution: We couldn’t verify your ID because your device could not take a clear enough photo. If this error keeps occurring, try using another device or verify your ID at a local Post Office.
doc_auth.errors.not_a_file: The selection was not a valid file.
doc_auth.errors.phone_step_incomplete: You must go to your phone and upload photos of your ID before continuing. We sent you a link with instructions.
doc_auth.errors.pii.birth_date_min_age: Your birthday does not meet the minimum age requirement.
@@ -567,11 +571,21 @@ doc_auth.errors.send_link_limited: You tried too many times, please try again in
doc_auth.errors.sharpness.failed_short: Image is blurry, please try again.
doc_auth.errors.sharpness.top_msg: We couldn’t read your ID. Your photo may be too blurry or dark. Try taking a new picture in a bright area.
doc_auth.errors.sharpness.top_msg_plural: We couldn’t read your ID. Your photos may be too blurry or dark. Try taking new pictures in a bright area.
+doc_auth.errors.unaccepted_id_type: We do not accept passports, military IDs, paper IDs or temporary IDs. You may only use a driver’s license or an ID issued by a U.S. state or territory.
+doc_auth.errors.underage: You must be at least 13 years old to use %{app_name}. If you are 13 or older, try again and make sure your date of birth is in focus.
+doc_auth.errors.unreadable_id: Take your photos in a well-lit area without shadows or glare. Make sure that everything on your ID is in focus and that the card fills the frame.
doc_auth.errors.upload_error: Sorry, something went wrong on our end.
doc_auth.forms.change_file: Change file
doc_auth.forms.choose_file_html: Drag file here or choose from folder
doc_auth.forms.doc_success: We verified your information
doc_auth.forms.selected_file: Selected file
+doc_auth.headers.expired_id: Your ID may have expired
+doc_auth.headers.general.network_error: We are having technical difficulties
+doc_auth.headers.id_not_found: We couldn’t find your ID.
+doc_auth.headers.low_resolution: Your device’s camera may not be supported.
+doc_auth.headers.unaccepted_id_type: Use a driver’s license or a state ID
+doc_auth.headers.underage: Age requirement not met
+doc_auth.headers.unreadable_id: We could not read your ID
doc_auth.headings.address: Update your mailing address
doc_auth.headings.back: Back of your driver’s license or state ID
doc_auth.headings.capture_complete: We verified your ID
@@ -685,6 +699,8 @@ doc_auth.instructions.text1: Other forms of ID are not accepted. We’ll check t
doc_auth.instructions.text2: You will not need your physical SSN card.
doc_auth.instructions.text3: We match your phone number with your personal information and send a one-time code to your phone.
doc_auth.instructions.text4: Your password saves and encrypts your personal information.
+doc_auth.rate_limit_warning.plural_html: For security reasons, you have %{remaining_attempts} attempts remaining.
+doc_auth.rate_limit_warning.singular_html: For security reasons, you have 1 attempt remaining.
doc_auth.tips.document_capture_hint: Must be a JPG or PNG
doc_auth.tips.document_capture_id_text1: Use a flat and dark surface
doc_auth.tips.document_capture_id_text2: Take photos in a well-lit place
diff --git a/config/locales/es.yml b/config/locales/es.yml
index d93a88dbcf3..baffc5ec289 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -548,11 +548,13 @@ doc_auth.errors.document_capture_canceled: Ha cancelado la carga de fotos de su
doc_auth.errors.dpi.failed_short: La imagen es demasiado pequeña o está borrosa; inténtelo de nuevo.
doc_auth.errors.dpi.top_msg: No pudimos leer su identificación. Es posible que el tamaño de su imagen o de su identificación sea demasiado pequeño o que la foto esté borrosa. Asegúrese de que su identificación se vea más grande dentro del marco de la imagen e intente tomar una nueva foto.
doc_auth.errors.dpi.top_msg_plural: No pudimos leer su identificación. Es posible que el tamaño de sus imágenes o de su identificación sea demasiado pequeño o que las fotos estén borrosas. Asegúrese de que su identificación se vea grande dentro del marco de la imagen e intente tomar nuevas fotos.
+doc_auth.errors.expired_id: No puede usar una tarjeta de identificación vencida para verificar su identidad. Si su tarjeta de identificación no está vencida, revise que la información en esta se vea nítida y que la tarjeta llene el marco.
doc_auth.errors.file_type.invalid: No se acepta este tipo de archivo; elija un archivo JPG o PNG.
doc_auth.errors.general.fallback_field_level: Agregue una imagen nueva
doc_auth.errors.general.multiple_back_id_failures: No pudimos verificar el reverso de su identificación. Intente tomar una nueva foto.
doc_auth.errors.general.multiple_front_id_failures: No pudimos verificar el frente de su identificación. Intente tomar una nueva foto.
doc_auth.errors.general.network_error: Estamos teniendo problemas técnicos. Intente enviar sus imágenes de nuevo más tarde.
+doc_auth.errors.general.new_network_error: Vuelva a intentarlo más tarde.
doc_auth.errors.general.no_liveness: Intente tomar nuevas fotos.
doc_auth.errors.general.selfie_failure: No pudimos verificar su foto. Intente tomar una nueva foto.
doc_auth.errors.general.selfie_failure_help_link_text: Consulte más consejos para tomar fotos claras
@@ -566,6 +568,8 @@ doc_auth.errors.http.image_size.failed_short: El archivo de la imagen no es comp
doc_auth.errors.http.image_size.top_msg: El tamaño de la imagen es demasiado grande o demasiado pequeño. Añada imágenes de su identificación de unos 2025 x 1275 píxeles.
doc_auth.errors.http.pixel_depth.failed_short: El archivo de la imagen no es compatible; inténtelo de nuevo.
doc_auth.errors.http.pixel_depth.top_msg: La profundidad de píxel de su archivo de imagen no es compatible. Tome nuevas fotos de su identificación e inténtelo de nuevo. La profundidad de píxel de la imagen admitida es RGB de 24 bits.
+doc_auth.errors.id_not_found: Verifique que está tomando una foto de su identificación física y no de una fotografía o captura de pantalla.
+doc_auth.errors.low_resolution: No pudimos verificar su identificación porque su dispositivo no tomó una fotografía bastante clara. Si se sigue presentando este error, use otro dispositivo o verifique su identidad en una oficina de correos de su localidad.
doc_auth.errors.not_a_file: La selección no era un archivo válido.
doc_auth.errors.phone_step_incomplete: Debe ir a su teléfono y cargar fotos de su identificación antes de continuar. Le enviamos un vínculo con instrucciones.
doc_auth.errors.pii.birth_date_min_age: Su fecha de nacimiento no cumple con el requisito de edad mínima.
@@ -578,11 +582,21 @@ doc_auth.errors.send_link_limited: Lo intentó demasiadas veces; vuelva a intent
doc_auth.errors.sharpness.failed_short: La imagen está borrosa; inténtelo de nuevo.
doc_auth.errors.sharpness.top_msg: No pudimos leer su identificación. Es posible que su foto esté demasiado borrosa u oscura. Intente tomar una nueva foto en un lugar iluminado.
doc_auth.errors.sharpness.top_msg_plural: No pudimos leer su identificación. Es posible que sus fotos estén demasiado borrosas u oscuras. Intente tomar nuevas fotos en un lugar iluminado.
+doc_auth.errors.unaccepted_id_type: No aceptamos pasaportes, identificaciones militares, ni identificaciones impresas o temporales. Solo puede usar una licencia de conducir o una identificación emitida por un estado o territorio de los EE. UU.
+doc_auth.errors.underage: Debe tener al menos 13 años para usar %{app_name}. Si tiene 13 años o más, vuelva a intentarlo y verifique que su fecha de nacimiento se vea nítida.
+doc_auth.errors.unreadable_id: Tome sus fotografías en un lugar bien iluminado sin sombras ni reflejos. Revise que la información en su identificación se vea nítida y que la tarjeta llene el marco.
doc_auth.errors.upload_error: Lo sentimos, algo no funcionó bien.
doc_auth.forms.change_file: Cambiar archivo
doc_auth.forms.choose_file_html: Arrastrar el archivo aquí o seleccionarlo de la carpeta
doc_auth.forms.doc_success: Verificamos su información
doc_auth.forms.selected_file: Archivo seleccionado
+doc_auth.headers.expired_id: Su identificación puede estar vencida
+doc_auth.headers.general.network_error: Estamos teniendo problemas técnicos
+doc_auth.headers.id_not_found: No pudimos encontrar su identificación
+doc_auth.headers.low_resolution: Es posible que la cámara de su dispositivo no sea compatible
+doc_auth.headers.unaccepted_id_type: Use una licencia de conducir o una identificación estatal
+doc_auth.headers.underage: No se cumplió con el requisito de edad
+doc_auth.headers.unreadable_id: No pudimos leer su identificación
doc_auth.headings.address: Actualice su dirección postal
doc_auth.headings.back: Reverso de su licencia de conducir o identificación estatal
doc_auth.headings.capture_complete: Verificamos su identificación
@@ -696,6 +710,8 @@ doc_auth.instructions.text1: No se aceptan otras formas de identificación. Revi
doc_auth.instructions.text2: No necesita la tarjeta física del Seguro Social.
doc_auth.instructions.text3: Revisamos que su número de teléfono coincida con su información personal y enviamos un código de un solo uso a su teléfono.
doc_auth.instructions.text4: Su contraseña guarda y cifra su información personal.
+doc_auth.rate_limit_warning.plural_html: Por motivos de seguridad, le quedan %{remaining_attempts} intentos.
+doc_auth.rate_limit_warning.singular_html: Por motivos de seguridad, le queda un intento.
doc_auth.tips.document_capture_hint: Debe ser un archivo JPG o PNG
doc_auth.tips.document_capture_id_text1: Use una superficie plana y de color oscuro.
doc_auth.tips.document_capture_id_text2: Tome fotos en un lugar bien iluminado
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index e20fa43644b..dc2ae99a5ed 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -537,11 +537,13 @@ doc_auth.errors.document_capture_canceled: Vous avez annulé le téléchargement
doc_auth.errors.dpi.failed_short: Image trop petite ou floue, veuillez réessayer.
doc_auth.errors.dpi.top_msg: Nous n’avons pas pu lire votre pièce d’identité. Il se peut que votre image soit de trop petite taille ou que votre pièce d’identité soit trop petite ou floue sur la photo. Assurez-vous que votre pièce d’identité remplisse le cadre de l’image, puis essayez de prendre une nouvelle photo.
doc_auth.errors.dpi.top_msg_plural: Nous n’avons pas pu lire votre pièce d’identité. Il se peut que votre image soit de trop petite taille ou que votre pièce d’identité soit trop petite ou floue sur les photos. Assurez-vous que votre pièce d’identité remplisse le cadre de l’image, puis essayez de prendre une nouvelle photo.
+doc_auth.errors.expired_id: Il n’est pas possible d’utiliser une pièce d’identité périmée pour confirmer votre identité. Si votre pièce d’identité est toujours valable, veillez à ce que tous les renseignements y figurant soient nets et que l’ensemble de la carte remplisse le cadre.
doc_auth.errors.file_type.invalid: Ce type de fichier n’est pas accepté ; veuillez choisir un fichier JPG ou PNG.
doc_auth.errors.general.fallback_field_level: Veuillez ajouter une nouvelle image
doc_auth.errors.general.multiple_back_id_failures: Nous n’avons pas pu vérifier le verso de votre pièce d’identité. Essayez de prendre une nouvelle photo.
doc_auth.errors.general.multiple_front_id_failures: Nous n’avons pas pu vérifier le recto de votre pièce d’identité. Essayez de prendre une nouvelle photo.
doc_auth.errors.general.network_error: Nous rencontrons actuellement des difficultés techniques de notre côté. Veuillez réessayer d’envoyer vos images ultérieurement.
+doc_auth.errors.general.new_network_error: Veuillez réessayer ultérieurement.
doc_auth.errors.general.no_liveness: Essayez de prendre de nouvelles photos.
doc_auth.errors.general.selfie_failure: Nous n’avons pas pu vérifier votre photo. Essayez de prendre une nouvelle photo.
doc_auth.errors.general.selfie_failure_help_link_text: Consultez plus de conseils pour prendre des photos claires
@@ -555,6 +557,8 @@ doc_auth.errors.http.image_size.failed_short: Le fichier image n’est pas pris
doc_auth.errors.http.image_size.top_msg: La taille de votre image est trop grande ou trop petite. Veuillez ajouter des images de votre pièce d’identité d’environ 2 025 x 1 275 pixels.
doc_auth.errors.http.pixel_depth.failed_short: Le fichier image n’est pas pris en charge, veuillez réessayer.
doc_auth.errors.http.pixel_depth.top_msg: La profondeur de pixel de votre fichier image n’est pas prise en charge. Veuillez prendre de nouvelles photos de votre pièce d’identité et réessayer. La profondeur de pixel de l’image prise en charge est de 24 bits RGB.
+doc_auth.errors.id_not_found: Veillez à prendre une photo de votre pièce d’identité physique et non d’une photo ou d’une capture d’écran.
+doc_auth.errors.low_resolution: Nous n’avons pas pu vérifier votre identité car votre appareil n’a pas pris une photo suffisamment claire. Si cette erreur persiste, utilisez un autre appareil ou confirmez votre identité dans un bureau de poste local.
doc_auth.errors.not_a_file: La sélection n’était pas un fichier valide.
doc_auth.errors.phone_step_incomplete: Vous devez aller sur votre téléphone et télécharger des photos de votre pièce d’identité avant de continuer. Nous vous avons envoyé un lien avec des instructions.
doc_auth.errors.pii.birth_date_min_age: Votre anniversaire ne correspond pas à l’âge minimum requis.
@@ -567,11 +571,21 @@ doc_auth.errors.send_link_limited: Vous avez essayé trop de fois, veuillez rée
doc_auth.errors.sharpness.failed_short: L’image est floue, veuillez réessayer.
doc_auth.errors.sharpness.top_msg: Nous n’avons pas pu lire votre pièce d’identité. Il se peut que votre photo soit trop floue ou trop sombre. Essayez de prendre une nouvelle photo dans un endroit bien éclairé.
doc_auth.errors.sharpness.top_msg_plural: Nous n’avons pas pu lire votre pièce d’identité. Il se peut que vos photos soient trop floues ou trop sombres. Essayez de prendre de nouvelles photos dans un endroit bien éclairé.
+doc_auth.errors.unaccepted_id_type: Nous n’acceptons pas de passeports, de cartes d’identité militaires, de pièces d’identité papier ou provisoires. Vous pouvez uniquement utiliser un permis de conduire ou une pièce d’identité délivré par un État ou un territoire des États-Unis.
+doc_auth.errors.underage: Vous devez être âgé de 13 ans au moins pour utiliser %{app_name}. Si vous avez 13 ans ou plus, réessayez et assurez-vous que votre date de naissance est bien visible.
+doc_auth.errors.unreadable_id: Prenez vos photos dans un endroit bien éclairé sans ombre ou reflet. Veillez à ce que tous les renseignements figurant sur votre pièce d’identité soient nets et que l’ensemble de la pièce soit visible à l’intérieur du cadre.
doc_auth.errors.upload_error: Désolé, il y a eu un problème de notre côté.
doc_auth.forms.change_file: Changer de fichier
doc_auth.forms.choose_file_html: Faites glisser le fichier ici ou choisissez dans un dossier
doc_auth.forms.doc_success: Nous avons vérifié vos informations
doc_auth.forms.selected_file: Fichier sélectionné
+doc_auth.headers.expired_id: Votre pièce d’identité est peut-être périmée
+doc_auth.headers.general.network_error: Nous rencontrons des difficultés techniques
+doc_auth.headers.id_not_found: Nous n’avons pas trouvé votre pièce d’identité
+doc_auth.headers.low_resolution: Il se peut que la caméra de votre appareil ne soit pas prise en charge
+doc_auth.headers.unaccepted_id_type: Utiliser un permis de conduire ou une carte d’identité d’un État
+doc_auth.headers.underage: Condition d’âge non remplie
+doc_auth.headers.unreadable_id: Nous n’avons pas pu lire votre pièce d’identité
doc_auth.headings.address: Mettre à jour votre adresse postale
doc_auth.headings.back: Verso de votre permis de conduire ou de votre carte d’identité d’un État
doc_auth.headings.capture_complete: Nous avons vérifié votre pièce d’identité
@@ -685,6 +699,8 @@ doc_auth.instructions.text1: Les autres pièces d’identité ne sont pas accept
doc_auth.instructions.text2: Vous n’aurez pas besoin de votre carte de sécurité sociale papier.
doc_auth.instructions.text3: Nous comparons votre numéro de téléphone à vos informations personnelles et vous envoyons un code à usage unique sur votre téléphone.
doc_auth.instructions.text4: Votre mot de passe s’enregistre et chiffre vos informations personnelles.
+doc_auth.rate_limit_warning.plural_html: Pour des raisons de sécurité, il vous reste %{remaining_attempts} tentatives.
+doc_auth.rate_limit_warning.singular_html: Pour des raisons de sécurité, il vous reste une tentative.
doc_auth.tips.document_capture_hint: Doit être au format JPG ou PNG
doc_auth.tips.document_capture_id_text1: Utilisez une surface plane et foncée
doc_auth.tips.document_capture_id_text2: Prenez vos photos dans un endroit bien éclairé
diff --git a/config/locales/zh.yml b/config/locales/zh.yml
index 89061d000ad..ea75d8f839a 100644
--- a/config/locales/zh.yml
+++ b/config/locales/zh.yml
@@ -548,11 +548,13 @@ doc_auth.errors.document_capture_canceled: 你已取消了用手机上传身份
doc_auth.errors.dpi.failed_short: 图像太小或模糊,请再试一次。
doc_auth.errors.dpi.top_msg: 我们无法辨认你的身份证件。你的图像尺寸可能太小,或者照片中你的身份证件太小或太模糊。确保你的身份证件在图框中比较大,然后试着重拍一下。
doc_auth.errors.dpi.top_msg_plural: 我们无法读取你的身份证件。你的图像尺寸可能太小,或者照片中你的身份证件太小或模糊。确保你的身份证件在图片框中很大,然后试着拍一张新照片。
+doc_auth.errors.expired_id: 你不能使用过期的身份证件进行身份验证。如果你的身份证件未过期,请确保你的身份证件上的所有内容对焦,而且身份卡填满画面。
doc_auth.errors.file_type.invalid: 这一文件类型我们不接受。请选择一个 JPG 或 PNG 文件。
doc_auth.errors.general.fallback_field_level: 请添加一个新图像
doc_auth.errors.general.multiple_back_id_failures: 我们无法验证你身份证件的背面。尝试重拍一张。
doc_auth.errors.general.multiple_front_id_failures: 我们无法验证你身份证件的正面。尝试重拍一张。
doc_auth.errors.general.network_error: 我们这边有技术困难。请稍后再提交你的图像。
+doc_auth.errors.general.new_network_error: 请稍后再试。
doc_auth.errors.general.no_liveness: 尝试重拍。
doc_auth.errors.general.selfie_failure: 我们无法验证你自己的照片。请重拍一张。
doc_auth.errors.general.selfie_failure_help_link_text: 查看更多有关拍摄清晰照片的提示
@@ -566,6 +568,8 @@ doc_auth.errors.http.image_size.failed_short: 系统不支持图像文件,请
doc_auth.errors.http.image_size.top_msg: 你的图像尺寸太大或太小。请添加你身份证件的图像,其像素应约为 2025 x 1275。
doc_auth.errors.http.pixel_depth.failed_short: 系统不支持图像文件,请再试一次。
doc_auth.errors.http.pixel_depth.top_msg: 你图像文件的像素深度系统不支持。请重拍你的身份证件并再试一次。系统支持的图像像素深度为 24位 RGB。
+doc_auth.errors.id_not_found: 确保你拍摄的是你的实体身份证件,而不是照片或屏幕截图。
+doc_auth.errors.low_resolution: 我们无法验证你的身份,因为你设备拍摄的照片不够清晰。如果这个错误持续发生,请使用其他设备或在当地邮局验证你的身份。
doc_auth.errors.not_a_file: 你选择的不是一个正确的文件。
doc_auth.errors.phone_step_incomplete: 在继续之前你必须使用手机上传你身份证件的图片。我们已给你发了带有说明的链接。
doc_auth.errors.pii.birth_date_min_age: 你的生日不满足最低年龄要求。
@@ -578,11 +582,21 @@ doc_auth.errors.send_link_limited: 你尝试了太多次。请在 %{timeout}后
doc_auth.errors.sharpness.failed_short: 图像模糊,请再试一次。
doc_auth.errors.sharpness.top_msg: 我们无法读取你的身份证件。你的照片可能太模糊或太暗。尝试在明亮的地方重拍一张。
doc_auth.errors.sharpness.top_msg_plural: 我们无法读取你的身份证件。你的照片可能太模糊或太暗。尝试在明亮的地方重拍一张。
+doc_auth.errors.unaccepted_id_type: 我们不接受护照、军人身份证件、纸质身份证件或临时身份证件。你只能使用驾驶执照或美国州或领地颁发的身份证件。
+doc_auth.errors.underage: 你必须年满 13 岁才能使用 %{app_name}。如果你年满 13 岁,请重试并确保你的出生日期对焦。
+doc_auth.errors.unreadable_id: 请在光线充足而且没有阴影或眩光的地方拍照。确保你身份证件上的所有内容都清晰对焦,并且身份卡填满画面。
doc_auth.errors.upload_error: 抱歉,我们这边出错了。
doc_auth.forms.change_file: 更改文件
doc_auth.forms.choose_file_html: 将文件拖到此处或者从文件夹中选择。
doc_auth.forms.doc_success: 我们验证了你的信息
doc_auth.forms.selected_file: 被选文件
+doc_auth.headers.expired_id: 你的身份证件可能已过期
+doc_auth.headers.general.network_error: 我们目前遇到技术困难
+doc_auth.headers.id_not_found: 我们找不到你的身份证件
+doc_auth.headers.low_resolution: 你设备的相机可能不受支持
+doc_auth.headers.unaccepted_id_type: 使用驾驶执照或州颁发的身份证件
+doc_auth.headers.underage: 不符合年龄规定
+doc_auth.headers.unreadable_id: 我们无法读取你的身份证件
doc_auth.headings.address: 更新你的邮政地址
doc_auth.headings.back: 驾照或州政府颁发身份证件的背面。
doc_auth.headings.capture_complete: 我们验证了你的身份证件
@@ -696,6 +710,8 @@ doc_auth.instructions.text1: 其他形式的身份证件不被接受。我们要
doc_auth.instructions.text2: 不需要你社会保障卡实体。
doc_auth.instructions.text3: 我们把你的电话号码与个人信息匹配,并向你的电话发送一个一次性代码。
doc_auth.instructions.text4: 你的密码对你个人信息进行存储并加密。
+doc_auth.rate_limit_warning.plural_html: 出于安全原因,你还有 %{remaining_attempts} 次尝试机会。
+doc_auth.rate_limit_warning.singular_html: 出于安全原因,你还有 1 次尝试机会。
doc_auth.tips.document_capture_hint: 必须是 JPG 或 PNG
doc_auth.tips.document_capture_id_text1: 使用水平和深色表面
doc_auth.tips.document_capture_id_text2: 在光线明亮的地方拍照
diff --git a/config/routes.rb b/config/routes.rb
index a01b1257562..f30ff1f5936 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -361,6 +361,8 @@
put '/document_capture' => 'document_capture#update'
get '/socure/document_capture' => 'socure/document_capture#show'
get '/socure/document_capture_update' => 'socure/document_capture#update', as: :socure_document_capture_update
+ get '/socure/document_capture_errors' => 'socure/document_capture#errors', as: :socure_document_capture_errors
+ get '/socure/document_capture_goto_in_person' => 'socure/document_capture#goto_in_person', as: :socure_document_capture_goto_in_person
# 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
@@ -369,6 +371,8 @@
get '/hybrid_mobile/capture_complete' => 'hybrid_mobile/capture_complete#show'
get '/hybrid_mobile/socure/document_capture' => 'hybrid_mobile/socure/document_capture#show'
get '/hybrid_mobile/socure/document_capture_update' => 'hybrid_mobile/socure/document_capture#update', as: :hybrid_mobile_socure_document_capture_update
+ get '/hybrid_mobile/socure/document_capture_errors' => 'hybrid_mobile/socure/document_capture#errors', as: :hybrid_mobile_socure_document_capture_errors
+ get '/hybrid_mobile/socure/document_capture_goto_in_person' => 'hybrid_mobile/socure/document_capture#goto_in_person', as: :hybrid_mobile_socure_document_capture_goto_in_person
get '/hybrid_handoff' => 'hybrid_handoff#show'
put '/hybrid_handoff' => 'hybrid_handoff#update'
get '/link_sent' => 'link_sent#show'
diff --git a/lib/identity_config.rb b/lib/identity_config.rb
index 2b721e2c7a7..b66644e7792 100644
--- a/lib/identity_config.rb
+++ b/lib/identity_config.rb
@@ -108,6 +108,7 @@ def self.store
config.add(:database_worker_jobs_username, type: :string)
config.add(:deleted_user_accounts_report_configs, type: :json)
config.add(:deliver_mail_async, type: :boolean)
+ config.add(:desktop_ft_unlock_setup_option_percent_tested, type: :integer)
config.add(:development_mailer_deliver_method, type: :symbol, enum: [:file, :letter_opener])
config.add(:disable_email_sending, type: :boolean)
config.add(:disable_logout_get_request, type: :boolean)
@@ -350,6 +351,7 @@ def self.store
config.add(:recaptcha_enterprise_api_key, type: :string)
config.add(:recaptcha_enterprise_project_id, type: :string)
config.add(:recaptcha_mock_validator, type: :boolean)
+ config.add(:recaptcha_request_timeout_in_seconds, type: :integer)
config.add(:recaptcha_secret_key, type: :string)
config.add(:recaptcha_site_key, type: :string)
config.add(:recovery_code_length, type: :integer)
diff --git a/lib/idp/constants.rb b/lib/idp/constants.rb
index 0f2d44f86ca..e9b66cca1d7 100644
--- a/lib/idp/constants.rb
+++ b/lib/idp/constants.rb
@@ -94,7 +94,10 @@ module Vendors
AAL2 = 2
AAL3 = 3
+ MOCK_IDV_APPLICANT_FULL_STATE = 'Montana'
+ MOCK_IDV_APPLICANT_FULL_STATE_ID_JURISDICTION = 'North Dakota'
MOCK_IDV_APPLICANT_STATE_ID_JURISDICTION = 'ND'
+ MOCK_IDV_APPLICANT_STATE = 'MT'
MOCK_IDV_APPLICANT = {
address1: '1 FAKE RD',
address2: nil,
@@ -107,7 +110,7 @@ module Vendors
last_name: 'MCFAKERSON',
middle_name: nil,
name_suffix: 'JR',
- state: 'MT',
+ state: MOCK_IDV_APPLICANT_STATE,
state_id_expiration: '2099-12-31',
state_id_issued: '2019-12-31',
state_id_jurisdiction: MOCK_IDV_APPLICANT_STATE_ID_JURISDICTION,
@@ -138,6 +141,7 @@ module Vendors
MOCK_IDV_APPLICANT_WITH_SSN = MOCK_IDV_APPLICANT.merge(ssn: '900-66-1234').freeze
+ MOCK_IDV_APPLICANT_FULL_IDENTITY_DOC_ADDRESS_STATE = 'Virginia'
MOCK_IDV_APPLICANT_STATE_ID_ADDRESS = MOCK_IDV_APPLICANT_WITH_SSN.merge(
identity_doc_address1: '123 Way St',
identity_doc_address2: '2nd Address Line',
@@ -170,10 +174,5 @@ module Vendors
MOCK_IDV_APPLICANT_SAME_ADDRESS_AS_ID_WITH_PHONE =
MOCK_IDV_APPLICANT_SAME_ADDRESS_AS_ID.merge(phone: '12025551212').freeze
-
- MOCK_IDV_APPLICANT_FULL_STATE_ID_JURISDICTION = 'North Dakota'
- MOCK_IDV_APPLICANT_FULL_STATE = 'Montana'
- MOCK_IDV_APPLICANT_FULL_IDENTITY_DOC_ADDRESS_STATE = 'Virginia'
- MOCK_IDV_APPLICANT_STATE = 'MT'
end
end
diff --git a/lib/linters/analytics_event_name_linter.rb b/lib/linters/analytics_event_name_linter.rb
index eed859cd3d4..bd4d8566d0b 100644
--- a/lib/linters/analytics_event_name_linter.rb
+++ b/lib/linters/analytics_event_name_linter.rb
@@ -3,7 +3,7 @@
module RuboCop
module Cop
module IdentityIdp
- class AnalyticsEventNameLinter < RuboCop::Cop::Cop
+ class AnalyticsEventNameLinter < RuboCop::Cop::Base
RESTRICT_ON_SEND = [:track_event].freeze
# DO NOT ADD TO THIS LIST OR YOU WILL MAKE A KITTEN CRY!
@@ -45,7 +45,6 @@ def on_send(node)
return if LEGACY_EVENT_NAMES.include?(Digest::MD5.hexdigest(actual_name.to_s)[0...7])
add_offense(
first_argument,
- location: :expression,
message: "Event name must match the method name, expected `:#{expected_name}`",
)
end
diff --git a/lib/linters/errors_add_linter.rb b/lib/linters/errors_add_linter.rb
index 3c36112fe4d..72447e0af80 100644
--- a/lib/linters/errors_add_linter.rb
+++ b/lib/linters/errors_add_linter.rb
@@ -14,7 +14,7 @@ module IdentityIdp
# #good
# errors.add(:iss, 'invalid issuer', type: :invalid_issuer)
#
- class ErrorsAddLinter < RuboCop::Cop::Cop
+ class ErrorsAddLinter < RuboCop::Cop::Base
MSG = 'Please set a unique key for this error'
RESTRICT_ON_SEND = [:add].freeze
@@ -29,7 +29,7 @@ def on_send(node)
return if type && type.type == :sym
options = type if type && type.type == :hash
return if options && options.type == :hash && options.keys.map(&:value).include?(:type)
- add_offense(node, location: :expression)
+ add_offense(node)
end
end
end
diff --git a/lib/linters/image_size_linter.rb b/lib/linters/image_size_linter.rb
index b5f37076489..680e3357629 100644
--- a/lib/linters/image_size_linter.rb
+++ b/lib/linters/image_size_linter.rb
@@ -15,13 +15,13 @@ module IdentityIdp
# # good
# image_tag 'example.svg', width: 10, height: 20
#
- class ImageSizeLinter < RuboCop::Cop::Cop
+ class ImageSizeLinter < RuboCop::Cop::Base
MSG = 'Assign width and height to images'
RESTRICT_ON_SEND = [:image_tag].freeze
def on_send(node)
- add_offense(node, location: :expression) if !valid?(node)
+ add_offense(node) if !valid?(node)
end
private
diff --git a/lib/linters/localized_validation_message_linter.rb b/lib/linters/localized_validation_message_linter.rb
index 04f791d07fd..54e56dd483f 100644
--- a/lib/linters/localized_validation_message_linter.rb
+++ b/lib/linters/localized_validation_message_linter.rb
@@ -3,7 +3,7 @@
module RuboCop
module Cop
module IdentityIdp
- class LocalizedValidationMessageLinter < RuboCop::Cop::Cop
+ class LocalizedValidationMessageLinter < RuboCop::Cop::Base
MSG = 'Use proc when translating validation message'
RESTRICT_ON_SEND = [
@@ -33,7 +33,7 @@ class LocalizedValidationMessageLinter < RuboCop::Cop::Cop
def on_send(node)
if translated_validation_message?(node) || translated_validation_helper_message?(node)
- add_offense(node, location: :expression)
+ add_offense(node)
end
end
end
diff --git a/lib/linters/mail_later_linter.rb b/lib/linters/mail_later_linter.rb
index cb70b536f08..12c891d5df3 100644
--- a/lib/linters/mail_later_linter.rb
+++ b/lib/linters/mail_later_linter.rb
@@ -16,7 +16,7 @@ module IdentityIdp
# UserMailer.with(params).signup_with_your_email(user, email).deliver_now_or_later
# ReportMailer.report_mail(data).deliver_now
#
- class MailLaterLinter < RuboCop::Cop::Cop
+ class MailLaterLinter < RuboCop::Cop::Base
MSG = 'Please send mail using deliver_now_or_later instead'
RESTRICT_ON_SEND = [:deliver_now, :deliver_later].freeze
@@ -33,7 +33,7 @@ def on_send(node)
receiver.receiver.const_name
end
- add_offense(node, location: :expression) if mailer_name == 'UserMailer'
+ add_offense(node) if mailer_name == 'UserMailer'
end
end
end
diff --git a/lib/linters/redirect_back_linter.rb b/lib/linters/redirect_back_linter.rb
index cc756ebd54d..ad2fba37d4c 100644
--- a/lib/linters/redirect_back_linter.rb
+++ b/lib/linters/redirect_back_linter.rb
@@ -16,7 +16,7 @@ module IdentityIdp
# #good
# redirect_back fallback_location: '/', allow_other_host: false
#
- class RedirectBackLinter < RuboCop::Cop::Cop
+ class RedirectBackLinter < RuboCop::Cop::Base
MSG = 'Please set a fallback_location and the allow_other_host parameter to false'
RESTRICT_ON_SEND = [:redirect_back].freeze
@@ -26,7 +26,7 @@ class RedirectBackLinter < RuboCop::Cop::Cop
PATTERN
def on_send(node)
- add_offense(node, location: :expression) && return if node.arguments.empty?
+ add_offense(node) && return if node.arguments.empty?
sets_fallback_location, sets_allow_other_host_false = false
redirect_back_matcher(node) do |arguments|
@@ -45,7 +45,7 @@ def on_send(node)
return if sets_fallback_location && sets_allow_other_host_false
- add_offense(node, location: :expression)
+ add_offense(node)
end
end
end
diff --git a/lib/linters/url_options_linter.rb b/lib/linters/url_options_linter.rb
index 0c2303bdab7..3a2a8ba038a 100644
--- a/lib/linters/url_options_linter.rb
+++ b/lib/linters/url_options_linter.rb
@@ -33,7 +33,7 @@ module IdentityIdp
# end
# end
#
- class UrlOptionsLinter < RuboCop::Cop::Cop
+ class UrlOptionsLinter < RuboCop::Cop::Base
MSG = 'Please define url_options when including Rails.application.routes.url_helpers'
RESTRICT_ON_SEND = [:include].freeze
@@ -47,7 +47,7 @@ def on_send(node)
return unless includes_url_helpers?(node)
return if defines_url_options?(node)
- add_offense(node, location: :expression)
+ add_offense(node)
end
private
diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png
deleted file mode 100644
index bc8d68b0279..00000000000
Binary files a/public/apple-touch-icon.png and /dev/null differ
diff --git a/public/browserconfig.xml b/public/browserconfig.xml
deleted file mode 100644
index 4a718762cf5..00000000000
--- a/public/browserconfig.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
- #2b5797
-
-
-
diff --git a/public/favicon-16x16.png b/public/favicon-16x16.png
deleted file mode 100644
index 9cb30c9ad4b..00000000000
Binary files a/public/favicon-16x16.png and /dev/null differ
diff --git a/public/favicon-32x32.png b/public/favicon-32x32.png
deleted file mode 100644
index 9d58b77d905..00000000000
Binary files a/public/favicon-32x32.png and /dev/null differ
diff --git a/public/mstile-150x150.png b/public/mstile-150x150.png
deleted file mode 100644
index 3c12cb3812a..00000000000
Binary files a/public/mstile-150x150.png and /dev/null differ
diff --git a/public/safari-pinned-tab.svg b/public/safari-pinned-tab.svg
deleted file mode 100644
index 6d7cda7d61c..00000000000
--- a/public/safari-pinned-tab.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/spec/components/webauthn_input_component_spec.rb b/spec/components/webauthn_input_component_spec.rb
index 2e9f99d9931..5212698d7e9 100644
--- a/spec/components/webauthn_input_component_spec.rb
+++ b/spec/components/webauthn_input_component_spec.rb
@@ -17,8 +17,26 @@
expect(component.passkey_supported_only?).to eq(false)
end
- it 'exposes boolean alias for show_unsupported_passkey option' do
- expect(component.show_unsupported_passkey?).to eq(false)
+ it 'does not render desktop-ft-unlock-option attribute' do
+ expect(rendered).to have_css('lg-webauthn-input:not([desktop-ft-unlock-option="false"])')
+ end
+
+ context 'with desktop_ft_unlock_option' do
+ let(:options) { super().merge(desktop_ft_unlock_option: true) }
+
+ it 'does render desktop-ft-unlock-option attribute' do
+ expect(rendered).to have_css('lg-webauthn-input[desktop-ft-unlock-option="true"]')
+ end
+
+ context 'in a locale other than english' do
+ before do
+ I18n.locale = I18n.available_locales.sample
+ end
+
+ it 'does not render desktop-ft-unlock-option attribute' do
+ expect(rendered).to have_css('lg-webauthn-input:not([desktop-ft-unlock-option="false"])')
+ end
+ end
end
context 'with platform option' do
diff --git a/spec/config/initializers/ab_tests_spec.rb b/spec/config/initializers/ab_tests_spec.rb
index 8c14e6f0924..724b71e32df 100644
--- a/spec/config/initializers/ab_tests_spec.rb
+++ b/spec/config/initializers/ab_tests_spec.rb
@@ -220,7 +220,7 @@
end
end
- describe '.RECOMMEND_WEBAUTHN_PLATFORM_FOR_SMS_USER' do
+ describe 'RECOMMEND_WEBAUTHN_PLATFORM_FOR_SMS_USER' do
let(:user) { create(:user) }
subject(:bucket) do
@@ -301,4 +301,49 @@
end
end
end
+
+ describe 'DESKTOP_FT_UNLOCK_SETUP' do
+ let(:user) { nil }
+ let(:user_session) { {} }
+
+ subject(:bucket) do
+ AbTests::DESKTOP_FT_UNLOCK_SETUP.bucket(
+ request: nil,
+ service_provider: nil,
+ session: nil,
+ user:,
+ user_session:,
+ )
+ end
+
+ context 'when A/B test is disabled' do
+ before do
+ allow(IdentityConfig.store).to receive(:desktop_ft_unlock_setup_option_percent_tested).
+ and_return(0)
+ reload_ab_tests
+ end
+
+ context 'when it would otherwise assign a bucket' do
+ let(:user) { build(:user) }
+
+ it 'does not return a bucket' do
+ expect(bucket).to be_nil
+ end
+ end
+ end
+
+ context 'when A/B test is enabled' do
+ before do
+ allow(IdentityConfig.store).to receive(:desktop_ft_unlock_setup_option_percent_tested).
+ and_return(100)
+ reload_ab_tests
+ end
+
+ let(:user) { build(:user) }
+
+ it 'returns a bucket' do
+ expect(bucket).not_to be_nil
+ end
+ end
+ end
end
diff --git a/spec/controllers/accounts/connected_accounts/selected_email_controller_spec.rb b/spec/controllers/accounts/connected_accounts/selected_email_controller_spec.rb
index 0b66e62c194..5bf6e380b58 100644
--- a/spec/controllers/accounts/connected_accounts/selected_email_controller_spec.rb
+++ b/spec/controllers/accounts/connected_accounts/selected_email_controller_spec.rb
@@ -90,11 +90,15 @@
response
- expect(@analytics).to have_logged_event(:sp_select_email_submitted, success: true)
+ expect(@analytics).to have_logged_event(
+ :sp_select_email_submitted,
+ success: true,
+ selected_email_id: selected_email.id,
+ )
end
context 'with invalid submission' do
- let(:params) { super().merge(select_email_form: { selected_email_id: nil }) }
+ let(:params) { super().merge(select_email_form: { selected_email_id: '' }) }
it 'redirects to form with flash' do
expect(response).to redirect_to(edit_connected_account_selected_email_path(identity.id))
diff --git a/spec/controllers/accounts/connected_accounts_controller_spec.rb b/spec/controllers/accounts/connected_accounts_controller_spec.rb
new file mode 100644
index 00000000000..2bf473bb5d0
--- /dev/null
+++ b/spec/controllers/accounts/connected_accounts_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe Accounts::ConnectedAccountsController do
+ describe '#show' do
+ let(:user) { create(:user, :fully_registered) }
+
+ before do
+ stub_sign_in(user) if user
+ end
+
+ it 'shows and logs a visit' do
+ stub_analytics
+
+ get :show
+
+ expect(@analytics).to have_logged_event(:connected_accounts_page_visited)
+ end
+ end
+end
diff --git a/spec/controllers/idv/document_capture_controller_spec.rb b/spec/controllers/idv/document_capture_controller_spec.rb
index 88d1ca61523..eb98f5c1f73 100644
--- a/spec/controllers/idv/document_capture_controller_spec.rb
+++ b/spec/controllers/idv/document_capture_controller_spec.rb
@@ -117,6 +117,7 @@
end
let(:idv_vendor) { Idp::Constants::Vendors::LEXIS_NEXIS }
+ let(:vendor_switching_enabled) { true }
before do
allow(IdentityConfig.store).to receive(:doc_auth_vendor).and_return(
@@ -125,6 +126,9 @@
allow(IdentityConfig.store).to receive(:doc_auth_vendor_default).and_return(
idv_vendor,
)
+ allow(IdentityConfig.store).to receive(:doc_auth_vendor_switching_enabled).and_return(
+ vendor_switching_enabled,
+ )
end
it 'has non-nil presenter' do
@@ -142,6 +146,23 @@
end
end
+ context 'socure is the default vendor but facial match is required' do
+ let(:idv_vendor) { Idp::Constants::Vendors::SOCURE }
+ let(:vot) { 'Pb' }
+
+ before do
+ resolved_authn_context = Vot::Parser.new(vector_of_trust: vot).parse
+ allow(controller).to receive(:resolved_authn_context_result).
+ and_return(resolved_authn_context)
+ end
+
+ it 'does not redirect to Socure controller' do
+ get :show
+
+ expect(response).to_not redirect_to idv_socure_document_capture_url
+ end
+ end
+
it 'renders the show template' do
expect(subject).to receive(:render).with(
:show,
@@ -365,6 +386,7 @@
expect(controller).to receive(:selfie_requirement_met?).
and_return(performed_if_needed)
allow(result).to receive(:success?).and_return(true)
+ allow(result).to receive(:errors).and_return(result[:errors])
allow(subject).to receive(:stored_result).and_return(result)
allow(subject).to receive(:extract_pii_from_doc)
end
@@ -374,6 +396,7 @@
it 'stays on document capture' do
put :update
+
expect(response).to redirect_to idv_document_capture_url
end
end
diff --git a/spec/controllers/idv/hybrid_mobile/entry_controller_spec.rb b/spec/controllers/idv/hybrid_mobile/entry_controller_spec.rb
index 8d43b38e37f..15d1bb50191 100644
--- a/spec/controllers/idv/hybrid_mobile/entry_controller_spec.rb
+++ b/spec/controllers/idv/hybrid_mobile/entry_controller_spec.rb
@@ -56,9 +56,29 @@
{}
end
let(:idv_vendor) { Idp::Constants::Vendors::MOCK }
+ let(:vendor_switching_enabled) { true }
+ let(:lexis_nexis_percent) { 100 }
+ let(:acr_values) do
+ [
+ Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF,
+ Saml::Idp::Constants::DEFAULT_AAL_AUTHN_CONTEXT_CLASSREF,
+ ].join(' ')
+ end
before do
+ resolved_authn_context = AuthnContextResolver.new(
+ user: user,
+ service_provider: nil,
+ vtr: nil,
+ acr_values: acr_values,
+ ).result
allow(controller).to receive(:session).and_return(session)
+ allow(controller).to receive(:resolved_authn_context_result).
+ and_return(resolved_authn_context)
+ allow(IdentityConfig.store).to receive(:doc_auth_vendor_switching_enabled).
+ and_return(vendor_switching_enabled)
+ allow(IdentityConfig.store).to receive(:doc_auth_vendor_lexis_nexis_percent).
+ and_return(lexis_nexis_percent)
get :show, params: { 'document-capture-session': session_uuid }
end
@@ -70,6 +90,46 @@
end
end
+ context 'facial match is required' do
+ let(:acr_values) do
+ [
+ Saml::Idp::Constants::IAL2_BIO_REQUIRED_AUTHN_CONTEXT_CLASSREF,
+ Saml::Idp::Constants::DEFAULT_AAL_AUTHN_CONTEXT_CLASSREF,
+ ].join(' ')
+ end
+
+ context 'doc auth vendor is socure with facial match required' do
+ let(:idv_vendor) { Idp::Constants::Vendors::SOCURE }
+
+ it 'redirects to the lexis nexis first step' do
+ expect(response).to redirect_to idv_hybrid_mobile_document_capture_url
+ end
+ end
+
+ context 'doc auth vendor is mock with facial match required' do
+ let(:idv_vendor) { Idp::Constants::Vendors::MOCK }
+
+ it 'redirects to the lexis nexis first step' do
+ expect(response).to redirect_to idv_hybrid_mobile_document_capture_url
+ end
+ end
+
+ context 'lexis nexis is disabled' do
+ let(:idv_vendor) { nil }
+ let(:vendor_switching_enabled) { false }
+ let(:lexis_nexis_percent) { 0 }
+
+ before do
+ allow(IdentityConfig.store).to receive(:doc_auth_vendor_lexis_nexis_percent).
+ and_return(lexis_nexis_percent)
+ end
+
+ it 'causes an 404 error' do
+ expect(response.status).to eq(404)
+ end
+ end
+ end
+
context 'doc auth vendor is lexis nexis' do
let(:idv_vendor) { Idp::Constants::Vendors::LEXIS_NEXIS }
@@ -78,6 +138,14 @@
end
end
+ context 'doc auth vendor is mock' do
+ let(:idv_vendor) { Idp::Constants::Vendors::MOCK }
+
+ it 'redirects to the first step' do
+ expect(response).to redirect_to idv_hybrid_mobile_document_capture_url
+ end
+ end
+
context 'but we already had a session' do
let!(:different_document_capture_session) do
DocumentCaptureSession.create!(
diff --git a/spec/controllers/idv/hybrid_mobile/socure/document_capture_controller_spec.rb b/spec/controllers/idv/hybrid_mobile/socure/document_capture_controller_spec.rb
index ba96dbcfbb4..636e81c430b 100644
--- a/spec/controllers/idv/hybrid_mobile/socure/document_capture_controller_spec.rb
+++ b/spec/controllers/idv/hybrid_mobile/socure/document_capture_controller_spec.rb
@@ -4,6 +4,7 @@
include FlowPolicyHelper
let(:idv_vendor) { Idp::Constants::Vendors::SOCURE }
+ let(:vendor_switching_enabled) { true }
let(:fake_socure_endpoint) { 'https://fake-socure.test' }
let(:user) { create(:user) }
let(:stored_result) { nil }
@@ -24,6 +25,8 @@
and_return(fake_socure_endpoint)
allow(IdentityConfig.store).to receive(:doc_auth_vendor).and_return(idv_vendor)
allow(IdentityConfig.store).to receive(:doc_auth_vendor_default).and_return(idv_vendor)
+ allow(IdentityConfig.store).to receive(:doc_auth_vendor_switching_enabled).
+ and_return(vendor_switching_enabled)
allow(subject).to receive(:stored_result).and_return(stored_result)
@@ -66,11 +69,37 @@
end
context 'when we try to use this controller but we should be using the LN/mock version' do
- let(:idv_vendor) { Idp::Constants::Vendors::LEXIS_NEXIS }
+ context 'when doc_auth_vendor is Lexis Nexis' do
+ let(:idv_vendor) { Idp::Constants::Vendors::LEXIS_NEXIS }
- it 'redirects to the LN/mock controller' do
- get :show
- expect(response).to redirect_to idv_hybrid_mobile_document_capture_url
+ it 'redirects to the LN/mock controller' do
+ get :show
+ expect(response).to redirect_to idv_hybrid_mobile_document_capture_url
+ end
+ end
+
+ context 'when facial match is required' do
+ let(:acr_values) do
+ [
+ Saml::Idp::Constants::IAL2_BIO_REQUIRED_AUTHN_CONTEXT_CLASSREF,
+ Saml::Idp::Constants::DEFAULT_AAL_AUTHN_CONTEXT_CLASSREF,
+ ].join(' ')
+ end
+ before do
+ resolved_authn_context = AuthnContextResolver.new(
+ user: user,
+ service_provider: nil,
+ vtr: nil,
+ acr_values: acr_values,
+ ).result
+ allow(controller).to receive(:resolved_authn_context_result).
+ and_return(resolved_authn_context)
+ end
+
+ it 'redirects to the LN/mock controller' do
+ get :show
+ expect(response).to redirect_to idv_hybrid_mobile_document_capture_url
+ end
end
end
@@ -175,7 +204,7 @@
it 'redirects to idv unavailable url' do
get(:show)
- expect(response).to redirect_to(idv_unavailable_path)
+ expect(response).to redirect_to(idv_hybrid_mobile_socure_document_capture_errors_url)
expect(controller.send(:instance_variable_get, :@url)).not_to be
end
end
@@ -220,7 +249,7 @@
it 'connection timeout still responds to user' do
stub_request(:post, fake_socure_endpoint).to_raise(Faraday::ConnectionFailed)
get(:show)
- expect(response).to redirect_to(idv_unavailable_path)
+ expect(response).to redirect_to(idv_hybrid_mobile_socure_document_capture_errors_url)
end
it 'socure error response still gives a result to user' do
@@ -229,31 +258,34 @@
body: JSON.generate(failed_response_body),
)
get(:show)
- expect(response).to redirect_to(idv_unavailable_path)
+ expect(response).to redirect_to(idv_hybrid_mobile_socure_document_capture_errors_url)
end
+
it 'socure nil response still gives a result to user' do
stub_request(:post, fake_socure_endpoint).to_return(
status: 500,
body: nil,
)
get(:show)
- expect(response).to redirect_to(idv_unavailable_path)
+ expect(response).to redirect_to(idv_hybrid_mobile_socure_document_capture_errors_url)
end
+
it 'socure nil response still gives a result to user' do
stub_request(:post, fake_socure_endpoint).to_return(
status: 401,
body: JSON.generate(response_body_401),
)
get(:show)
- expect(response).to redirect_to(idv_unavailable_path)
+ expect(response).to redirect_to(idv_hybrid_mobile_socure_document_capture_errors_url)
end
+
it 'socure nil response still gives a result to user' do
stub_request(:post, fake_socure_endpoint).to_return(
status: 401,
body: JSON.generate(no_doc_found_response_body),
)
get(:show)
- expect(response).to redirect_to(idv_unavailable_path)
+ expect(response).to redirect_to(idv_hybrid_mobile_socure_document_capture_errors_url)
end
end
end
@@ -297,10 +329,10 @@
)
end
- it 'redirects back to the capture page' do
+ it 'redirects to the error page' do
get(:update)
- expect(response).to redirect_to(idv_hybrid_mobile_socure_document_capture_url)
+ expect(response).to redirect_to(idv_hybrid_mobile_socure_document_capture_errors_url)
expect(@analytics).to have_logged_event('IdV: doc auth document_capture submitted')
end
end
diff --git a/spec/controllers/idv/in_person/address_controller_spec.rb b/spec/controllers/idv/in_person/address_controller_spec.rb
index ed079de766a..638175330c0 100644
--- a/spec/controllers/idv/in_person/address_controller_spec.rb
+++ b/spec/controllers/idv/in_person/address_controller_spec.rb
@@ -5,6 +5,7 @@
include InPersonHelper
let(:user) { build(:user) }
+ let(:pii_from_user) { Idp::Constants::MOCK_IPP_APPLICANT_SAME_ADDRESS_AS_ID_FALSE }
before do
allow(IdentityConfig.store).to receive(:usps_ipp_transliteration_enabled).
@@ -12,7 +13,7 @@
stub_sign_in(user)
stub_up_to(:hybrid_handoff, idv_session: subject.idv_session)
subject.user_session['idv/in_person'] = {
- pii_from_user: Idp::Constants::MOCK_IPP_APPLICANT_SAME_ADDRESS_AS_ID_FALSE.dup,
+ pii_from_user: pii_from_user,
}
subject.idv_session.ssn = nil
stub_analytics
@@ -30,11 +31,22 @@
:before,
:set_usps_form_presenter,
)
+ expect(subject).to have_actions(
+ :before,
+ :confirm_in_person_state_id_step_complete,
+ )
+ expect(subject).to have_actions(
+ :before,
+ :confirm_in_person_address_step_needed,
+ )
end
context '#confirm_in_person_state_id_step_complete' do
- it 'redirects to state id page if not complete' do
+ before do
subject.user_session['idv/in_person'][:pii_from_user].delete(:identity_doc_address1)
+ end
+
+ it 'redirects to state id page if not complete' do
get :show
expect(response).to redirect_to idv_in_person_state_id_url
@@ -42,8 +54,10 @@
end
context '#confirm_in_person_address_step_needed' do
+ before do
+ request.env['HTTP_REFERER'] = idv_in_person_verify_info_url
+ end
it 'remains on page when referer is verify info' do
- subject.request = idv_in_person_verify_info_url
get :show
expect(response).to render_template :show
@@ -68,12 +82,15 @@
expect(response).to render_template :show
end
- it 'redirects to ssn page when address1 present' do
- subject.user_session['idv/in_person'][:pii_from_user][:address1] = '123 Main St'
-
- get :show
+ context 'when address1 present' do
+ before do
+ subject.user_session['idv/in_person'][:pii_from_user][:address1] = '123 Main St'
+ end
+ it 'redirects to ssn page' do
+ get :show
- expect(response).to redirect_to idv_in_person_ssn_url
+ expect(response).to redirect_to idv_in_person_ssn_url
+ end
end
it 'logs idv_in_person_proofing_address_visited' do
@@ -101,10 +118,10 @@
describe '#update' do
context 'valid address details' do
- let(:address1) { '1 FAKE RD' }
+ let(:address1) { Idp::Constants::MOCK_IDV_APPLICANT[:address1] }
let(:address2) { 'APT 1B' }
- let(:city) { 'GREAT FALLS' }
- let(:zipcode) { '59010-4444' }
+ let(:city) { Idp::Constants::MOCK_IDV_APPLICANT[:city] }
+ let(:zipcode) { Idp::Constants::MOCK_IDV_APPLICANT[:zipcode] }
let(:state) { 'Montana' }
let(:params) do
{ in_person_address: {
diff --git a/spec/controllers/idv/in_person/state_id_controller_spec.rb b/spec/controllers/idv/in_person/state_id_controller_spec.rb
index 731f64212f9..b24ab440209 100644
--- a/spec/controllers/idv/in_person/state_id_controller_spec.rb
+++ b/spec/controllers/idv/in_person/state_id_controller_spec.rb
@@ -225,15 +225,21 @@
expect(subject.user_session['idv/in_person'][:pii_from_user]).to_not have_key attr
end
- make_pii
+ build_pii_before_state_id_update
- # pii includes address attrs on re-visiting state id pg
+ # since same_address_as_id was initially true, pii includes residential address attrs,
+ # which are the same as state id address attrs, on re-visiting state id pg
expect(subject.user_session['idv/in_person'][:pii_from_user]).to include(
- address1:,
- address2:,
- city:,
- state:,
- zipcode:,
+ identity_doc_address1:,
+ identity_doc_address2:,
+ identity_doc_city:,
+ identity_doc_address_state:,
+ identity_doc_zipcode:,
+ address1: identity_doc_address1,
+ address2: identity_doc_address2,
+ city: identity_doc_city,
+ state: identity_doc_address_state,
+ zipcode: identity_doc_zipcode,
)
# On Verify, user changes response from "Yes,..." to
@@ -285,7 +291,7 @@
expect(subject.user_session['idv/in_person'][:pii_from_user]).to_not have_key attr
end
- make_pii(same_address_as_id: 'false')
+ build_pii_before_state_id_update(same_address_as_id: 'false')
# On Verify, user changes response from "No,..." to
# "Yes, I live at the address on my state-issued ID
@@ -322,7 +328,7 @@
end
# User picks "No, I live at a different address" on state ID
- make_pii(same_address_as_id: 'false')
+ build_pii_before_state_id_update(same_address_as_id: 'false')
# On Verify, user does not changes response "No,..."
put :update, params: params
diff --git a/spec/controllers/idv/link_sent_controller_spec.rb b/spec/controllers/idv/link_sent_controller_spec.rb
index 9a49ea0c1cd..fe7f3ede25d 100644
--- a/spec/controllers/idv/link_sent_controller_spec.rb
+++ b/spec/controllers/idv/link_sent_controller_spec.rb
@@ -153,6 +153,7 @@
allow(load_result).to receive(:success?).and_return(load_result_success)
allow(load_result).to receive(:selfie_check_performed?).and_return(false)
+ allow(load_result).to receive(:errors).and_return({ message: 'an error message' })
document_capture_session = DocumentCaptureSession.create!(
user: user,
@@ -200,7 +201,6 @@
it 'flashes an error and does not redirect' do
put :update
- expect(flash[:error]).to eq t('doc_auth.errors.phone_step_incomplete')
expect(response.status).to eq(204)
end
end
@@ -232,7 +232,6 @@
put :update
expect(response).to redirect_to(idv_hybrid_handoff_url)
- expect(flash[:error]).to eq(error_message)
end
end
@@ -243,7 +242,6 @@
put :update
expect(response).to have_http_status(204)
- expect(flash[:error]).to eq(t('doc_auth.errors.phone_step_incomplete'))
end
end
end
diff --git a/spec/controllers/idv/socure/document_capture_controller_spec.rb b/spec/controllers/idv/socure/document_capture_controller_spec.rb
index e431b493a6a..09b00461415 100644
--- a/spec/controllers/idv/socure/document_capture_controller_spec.rb
+++ b/spec/controllers/idv/socure/document_capture_controller_spec.rb
@@ -4,6 +4,7 @@
include FlowPolicyHelper
let(:idv_vendor) { Idp::Constants::Vendors::SOCURE }
+ let(:vendor_switching_enabled) { true }
let(:fake_socure_endpoint) { 'https://fake-socure.test' }
let(:user) { create(:user) }
let(:doc_auth_success) { true }
@@ -33,6 +34,8 @@
and_return(fake_socure_endpoint)
allow(IdentityConfig.store).to receive(:doc_auth_vendor).and_return(idv_vendor)
allow(IdentityConfig.store).to receive(:doc_auth_vendor_default).and_return(idv_vendor)
+ allow(IdentityConfig.store).to receive(:doc_auth_vendor_switching_enabled).
+ and_return(vendor_switching_enabled)
allow_any_instance_of(ApplicationController).to receive(:current_user).and_return(user)
allow(subject).to receive(:stored_result).and_return(stored_result)
@@ -79,11 +82,37 @@
end
context 'when we try to use this controller but we should be using the LN/mock version' do
- let(:idv_vendor) { Idp::Constants::Vendors::LEXIS_NEXIS }
+ context 'when doc_auth_vendor is Lexis Nexis' do
+ let(:idv_vendor) { Idp::Constants::Vendors::LEXIS_NEXIS }
- it 'redirects to the LN/mock controller' do
- get :show
- expect(response).to redirect_to idv_document_capture_url
+ it 'redirects to the LN/mock controller' do
+ get :show
+ expect(response).to redirect_to idv_document_capture_url
+ end
+ end
+
+ context 'when facial match is required' do
+ let(:acr_values) do
+ [
+ Saml::Idp::Constants::IAL2_BIO_REQUIRED_AUTHN_CONTEXT_CLASSREF,
+ Saml::Idp::Constants::DEFAULT_AAL_AUTHN_CONTEXT_CLASSREF,
+ ].join(' ')
+ end
+ before do
+ resolved_authn_context = AuthnContextResolver.new(
+ user: user,
+ service_provider: nil,
+ vtr: nil,
+ acr_values: acr_values,
+ ).result
+ allow(controller).to receive(:resolved_authn_context_result).
+ and_return(resolved_authn_context)
+ end
+
+ it 'redirects to the LN/mock controller' do
+ get :show
+ expect(response).to redirect_to idv_document_capture_url
+ end
end
end
@@ -185,10 +214,10 @@
context 'there is no url in the socure response' do
let(:response_body) { {} }
- it 'redirects to idv unavailable url' do
+ it 'redirects to the errors page' do
get(:show)
- expect(response).to redirect_to(idv_unavailable_path)
+ expect(response).to redirect_to(idv_socure_document_capture_errors_url)
expect(controller.send(:instance_variable_get, :@url)).not_to be
end
end
@@ -233,7 +262,7 @@
it 'connection timeout still responds to user' do
stub_request(:post, fake_socure_endpoint).to_raise(Faraday::ConnectionFailed)
get(:show)
- expect(response).to redirect_to(idv_unavailable_path)
+ expect(response).to redirect_to(idv_socure_document_capture_errors_url)
end
it 'socure error response still gives a result to user' do
@@ -242,7 +271,7 @@
body: JSON.generate(failed_response_body),
)
get(:show)
- expect(response).to redirect_to(idv_unavailable_path)
+ expect(response).to redirect_to(idv_socure_document_capture_errors_url)
end
it 'socure nil response still gives a result to user' do
stub_request(:post, fake_socure_endpoint).to_return(
@@ -250,7 +279,7 @@
body: nil,
)
get(:show)
- expect(response).to redirect_to(idv_unavailable_path)
+ expect(response).to redirect_to(idv_socure_document_capture_errors_url)
end
it 'socure nil response still gives a result to user' do
stub_request(:post, fake_socure_endpoint).to_return(
@@ -258,7 +287,7 @@
body: JSON.generate(response_body_401),
)
get(:show)
- expect(response).to redirect_to(idv_unavailable_path)
+ expect(response).to redirect_to(idv_socure_document_capture_errors_url)
end
it 'socure nil response still gives a result to user' do
stub_request(:post, fake_socure_endpoint).to_return(
@@ -266,15 +295,17 @@
body: JSON.generate(no_doc_found_response_body),
)
get(:show)
- expect(response).to redirect_to(idv_unavailable_path)
+ expect(response).to redirect_to(idv_socure_document_capture_errors_url)
end
end
end
describe '#update' do
- it 'returns FOUND (302) and redirects to SSN' do
- get(:update)
+ before do
+ get :update
+ end
+ it 'returns FOUND (302) and redirects to SSN' do
expect(response).to redirect_to(idv_ssn_path)
expect(@analytics).to have_logged_event('IdV: doc auth document_capture submitted')
end
@@ -282,10 +313,10 @@
context 'when doc auth fails' do
let(:doc_auth_success) { false }
- it 'redirects to document capture' do
+ it 'renders the errors' do
get(:update)
- expect(response).to redirect_to(idv_socure_document_capture_path)
+ expect(response).to redirect_to idv_socure_document_capture_errors_url
expect(@analytics).to have_logged_event('IdV: doc auth document_capture submitted')
end
end
@@ -316,8 +347,6 @@
let(:socure_docv_enabled) { false }
it 'the webhook route does not exist' do
- get(:update)
-
expect(response).to be_not_found
end
end
diff --git a/spec/controllers/sign_up/passwords_controller_spec.rb b/spec/controllers/sign_up/passwords_controller_spec.rb
index 9d910e9bf57..e0979324944 100644
--- a/spec/controllers/sign_up/passwords_controller_spec.rb
+++ b/spec/controllers/sign_up/passwords_controller_spec.rb
@@ -171,7 +171,7 @@
end
it 'rejects when confirmation_token is invalid' do
- validator = EmailConfirmationTokenValidator.new(user.email_addresses.first)
+ validator = EmailConfirmationTokenValidator.new(email_address: user.email_addresses.first)
result = validator.submit
expect(result.success?).to eq false
diff --git a/spec/controllers/sign_up/select_email_controller_spec.rb b/spec/controllers/sign_up/select_email_controller_spec.rb
index 4966281dccc..8c6f65575fb 100644
--- a/spec/controllers/sign_up/select_email_controller_spec.rb
+++ b/spec/controllers/sign_up/select_email_controller_spec.rb
@@ -107,6 +107,7 @@
:sp_select_email_submitted,
success: true,
needs_completion_screen_reason: :new_attributes,
+ selected_email_id: selected_email.id,
)
end
@@ -131,6 +132,7 @@
success: false,
error_details: { selected_email_id: { not_found: true } },
needs_completion_screen_reason: :new_attributes,
+ selected_email_id: selected_email.id,
)
end
end
diff --git a/spec/controllers/users/email_confirmations_controller_spec.rb b/spec/controllers/users/email_confirmations_controller_spec.rb
index 8d3197ad185..826b9374f01 100644
--- a/spec/controllers/users/email_confirmations_controller_spec.rb
+++ b/spec/controllers/users/email_confirmations_controller_spec.rb
@@ -4,6 +4,8 @@
describe '#create' do
describe 'Valid email confirmation tokens' do
it 'tracks a valid email confirmation token event' do
+ stub_analytics
+
user = create(:user)
new_email = Faker::Internet.email
@@ -23,6 +25,14 @@
email_record = add_email_form.email_address_record(new_email)
get :create, params: { confirmation_token: email_record.reload.confirmation_token }
+
+ expect(@analytics).to have_logged_event(
+ 'Add Email: Email Confirmation',
+ success: true,
+ errors: {},
+ from_select_email_flow: false,
+ user_id: user.uuid,
+ )
end
context 'when select email feature is disabled' do
@@ -126,6 +136,7 @@
end
it 'adds an email from the service provider consent flow' do
+ stub_analytics
new_email = Faker::Internet.email
add_email_form = AddUserEmailForm.new
add_email_form.submit(user, email: new_email, request_id: sp_request_uuid)
@@ -134,8 +145,16 @@
get :create, params: {
confirmation_token: email_record.reload.confirmation_token,
request_id: sp_request_uuid,
+ from_select_email_flow: 'true',
}
+ expect(@analytics).to have_logged_event(
+ 'Add Email: Email Confirmation',
+ success: true,
+ errors: {},
+ from_select_email_flow: true,
+ user_id: user.uuid,
+ )
expect(response).to redirect_to(sign_up_select_email_url)
end
end
diff --git a/spec/controllers/users/emails_controller_spec.rb b/spec/controllers/users/emails_controller_spec.rb
index bfc142718af..ae29f8e71ea 100644
--- a/spec/controllers/users/emails_controller_spec.rb
+++ b/spec/controllers/users/emails_controller_spec.rb
@@ -1,6 +1,94 @@
require 'rails_helper'
RSpec.describe Users::EmailsController do
+ describe '#show' do
+ subject(:response) { get :show, params: params }
+ let(:params) { {} }
+
+ before do
+ stub_sign_in
+ end
+
+ it 'does not session value for email selection flow' do
+ expect { response }.not_to change { controller.session[:in_select_email_flow] }.from(nil)
+ end
+
+ it 'logs visit' do
+ stub_analytics
+
+ response
+
+ expect(@analytics).to have_logged_event(
+ 'Add Email Address Page Visited',
+ in_select_email_flow: false,
+ )
+ end
+
+ context 'when adding through partner email selection flow' do
+ let(:params) { { in_select_email_flow: true } }
+
+ it 'assigns session value for email selection flow' do
+ expect { response }.to change { controller.session[:in_select_email_flow] }.
+ from(nil).to(true)
+ end
+
+ it 'logs visit with selected email value' do
+ stub_analytics
+
+ response
+
+ expect(@analytics).to have_logged_event(
+ 'Add Email Address Page Visited',
+ in_select_email_flow: true,
+ )
+ end
+ end
+ end
+
+ describe '#add' do
+ subject(:response) { post :add, params: params }
+ let(:user) { create(:user) }
+ let(:params) { { user: { email:, request_id: } } }
+ let(:email) { 'new@example.com' }
+ let(:request_id) { 'request-id-1' }
+
+ before do
+ stub_sign_in(user)
+ end
+
+ it 'logs submission' do
+ stub_analytics
+
+ response
+
+ expect(@analytics).to have_logged_event(
+ 'Add Email Requested',
+ success: true,
+ errors: {},
+ domain_name: 'example.com',
+ in_select_email_flow: false,
+ user_id: user.uuid,
+ )
+ end
+
+ context 'when adding through partner email selection flow' do
+ before do
+ controller.session[:in_select_email_flow] = true
+ end
+
+ it 'logs submission with selected email value' do
+ stub_analytics
+
+ response
+
+ expect(@analytics).to have_logged_event(
+ 'Add Email Requested',
+ hash_including(in_select_email_flow: true),
+ )
+ end
+ end
+ end
+
describe '#verify' do
context 'with malformed payload' do
it 'does not blow up' do
@@ -21,7 +109,10 @@
it 'renders the show view' do
get :show
- expect(@analytics).to have_logged_event('Add Email Address Page Visited')
+ expect(@analytics).to have_logged_event(
+ 'Add Email Address Page Visited',
+ in_select_email_flow: false,
+ )
end
end
@@ -86,6 +177,7 @@
errors: {},
user_id: user.uuid,
domain_name: email.split('@').last,
+ in_select_email_flow: false,
)
post :resend
diff --git a/spec/controllers/users/sessions_controller_spec.rb b/spec/controllers/users/sessions_controller_spec.rb
index c0a3f57cf76..9c614032c48 100644
--- a/spec/controllers/users/sessions_controller_spec.rb
+++ b/spec/controllers/users/sessions_controller_spec.rb
@@ -87,14 +87,13 @@
end
it 'tracks the successful authentication for existing user' do
- stub_analytics
+ stub_analytics(user:)
response
expect(@analytics).to have_logged_event(
'Email and Password Authentication',
success: true,
- user_id: user.uuid,
user_locked_out: false,
rate_limited: false,
valid_captcha_result: true,
@@ -161,14 +160,13 @@
end
it 'tracks as not being from a new device' do
- stub_analytics
+ stub_analytics(user:)
response
expect(@analytics).to have_logged_event(
'Email and Password Authentication',
success: true,
- user_id: user.uuid,
user_locked_out: false,
rate_limited: false,
valid_captcha_result: true,
@@ -222,8 +220,8 @@
attempt_window_max: 12.hours.in_minutes,
},
)
- stub_analytics
user = create(:user, :fully_registered)
+ stub_analytics(user:)
travel_to (3.hours + 1.minute).ago do
2.times do
@@ -252,7 +250,6 @@
expect(@analytics).to have_logged_event(
'Email and Password Authentication',
success: false,
- user_id: user.uuid,
user_locked_out: false,
rate_limited: true,
valid_captcha_result: true,
@@ -267,7 +264,7 @@
it 'tracks the unsuccessful authentication for existing user' do
user = create(:user, :fully_registered)
- stub_analytics
+ stub_analytics(user:)
expect(SCrypt::Engine).to receive(:hash_secret).once.and_call_original
post :create, params: { user: { email: user.email.upcase, password: 'invalid_password' } }
@@ -275,7 +272,6 @@
expect(@analytics).to have_logged_event(
'Email and Password Authentication',
success: false,
- user_id: user.uuid,
user_locked_out: false,
rate_limited: false,
valid_captcha_result: true,
@@ -288,7 +284,7 @@
end
it 'tracks the authentication attempt for nonexistent user' do
- stub_analytics
+ stub_analytics(user: kind_of(AnonymousUser))
expect(SCrypt::Engine).to receive(:hash_secret).once.and_call_original
post :create, params: { user: { email: 'foo@example.com', password: 'password' } }
@@ -296,7 +292,6 @@
expect(@analytics).to have_logged_event(
'Email and Password Authentication',
success: false,
- user_id: 'anonymous-uuid',
user_locked_out: false,
rate_limited: false,
valid_captcha_result: true,
@@ -314,14 +309,13 @@
second_factor_locked_at: Time.zone.now,
)
- stub_analytics
+ stub_analytics(user:)
post :create, params: { user: { email: user.email.upcase, password: user.password } }
expect(@analytics).to have_logged_event(
'Email and Password Authentication',
success: false,
- user_id: user.uuid,
user_locked_out: true,
rate_limited: false,
valid_captcha_result: true,
@@ -365,7 +359,7 @@
it 'tracks unsuccessful authentication for failed reCAPTCHA' do
user = create(:user, :fully_registered)
- stub_analytics
+ stub_analytics(user:)
post :create, params: { user: { email: user.email, password: user.password, score: 0.1 } }
@@ -373,7 +367,6 @@
'Email and Password Authentication',
success: false,
error_details: { recaptcha_token: { blank: true } },
- user_id: user.uuid,
user_locked_out: false,
rate_limited: false,
valid_captcha_result: false,
@@ -400,14 +393,13 @@
:fully_registered,
)
- stub_analytics
+ stub_analytics(user:)
post :create, params: { user: { email: user.email.upcase, password: 'invalid' } }
post :create, params: { user: { email: user.email.upcase, password: 'invalid' } }
expect(@analytics).to have_logged_event(
'Email and Password Authentication',
success: false,
- user_id: user.uuid,
user_locked_out: false,
rate_limited: false,
valid_captcha_result: true,
@@ -420,14 +412,13 @@
it 'tracks the presence of SP request_url in session' do
subject.session[:sp] = { request_url: mock_valid_site }
- stub_analytics
+ stub_analytics(user: kind_of(AnonymousUser))
post :create, params: { user: { email: 'foo@example.com', password: 'password' } }
expect(@analytics).to have_logged_event(
'Email and Password Authentication',
success: false,
- user_id: 'anonymous-uuid',
user_locked_out: false,
rate_limited: false,
valid_captcha_result: true,
@@ -592,14 +583,13 @@
}.to_json,
)
- stub_analytics
+ stub_analytics(user:)
post :create, params: { user: { email: user.email, password: user.password } }
expect(@analytics).to have_logged_event(
'Email and Password Authentication',
success: true,
- user_id: user.uuid,
user_locked_out: false,
rate_limited: false,
valid_captcha_result: true,
@@ -718,14 +708,13 @@
expires: 2.days.from_now,
}
- stub_analytics
+ stub_analytics(user:)
post :create, params: { user: { email: user.email, password: user.password } }
expect(@analytics).to have_logged_event(
'Email and Password Authentication',
success: true,
- user_id: user.uuid,
user_locked_out: false,
rate_limited: false,
valid_captcha_result: true,
@@ -746,14 +735,13 @@
value: RememberDeviceCookie.new(user_id: user.id, created_at: 2.days.ago).to_json,
}
- stub_analytics
+ stub_analytics(user:)
post :create, params: { user: { email: user.email, password: user.password } }
expect(@analytics).to have_logged_event(
'Email and Password Authentication',
success: true,
- user_id: user.uuid,
user_locked_out: false,
rate_limited: false,
valid_captcha_result: true,
diff --git a/spec/controllers/users/two_factor_authentication_setup_controller_spec.rb b/spec/controllers/users/two_factor_authentication_setup_controller_spec.rb
index 1cc8d62a217..574627a8860 100644
--- a/spec/controllers/users/two_factor_authentication_setup_controller_spec.rb
+++ b/spec/controllers/users/two_factor_authentication_setup_controller_spec.rb
@@ -4,6 +4,8 @@
describe 'GET index' do
let(:user) { create(:user) }
+ subject(:response) { get :index }
+
before do
stub_sign_in_before_2fa(user) if user
stub_analytics
@@ -19,6 +21,12 @@
)
end
+ it 'initializes presenter with false ab test bucket value' do
+ response
+
+ expect(assigns(:presenter).desktop_ft_ab_test).to be false
+ end
+
context 'with user having gov or mil email' do
let!(:federal_domain) { create(:federal_email_domain, name: 'gsa.gov') }
let(:user) do
@@ -101,6 +109,20 @@
expect(response).to redirect_to(user_two_factor_authentication_url)
end
end
+
+ context 'with user opted in to desktop ft unlock setup ab test' do
+ before do
+ allow(controller).to receive(:ab_test_bucket).with(
+ :DESKTOP_FT_UNLOCK_SETUP,
+ ).and_return(:desktop_ft_unlock_option_shown)
+ end
+
+ it 'initializes presenter with ab test bucket value' do
+ response
+
+ expect(assigns(:presenter).desktop_ft_ab_test).to eq(true)
+ end
+ end
end
describe '#create' do
diff --git a/spec/factories/in_person_enrollments.rb b/spec/factories/in_person_enrollments.rb
index bfb36296ea4..268164e51b2 100644
--- a/spec/factories/in_person_enrollments.rb
+++ b/spec/factories/in_person_enrollments.rb
@@ -49,9 +49,5 @@
trait :enhanced_ipp do
sponsor_id { IdentityConfig.store.usps_eipp_sponsor_id }
end
-
- trait :with_nil_sponsor_id do
- sponsor_id { nil }
- end
end
end
diff --git a/spec/factories/users.rb b/spec/factories/users.rb
index 8199af755a0..15ed22e6303 100644
--- a/spec/factories/users.rb
+++ b/spec/factories/users.rb
@@ -279,6 +279,7 @@
user: user,
)
create(:in_person_enrollment, :passed, user: user, profile: profile)
+ profile.in_person_verification_pending_at = nil
end
end
diff --git a/spec/features/idv/analytics_spec.rb b/spec/features/idv/analytics_spec.rb
index b224ab7321e..ce49724a1b1 100644
--- a/spec/features/idv/analytics_spec.rb
+++ b/spec/features/idv/analytics_spec.rb
@@ -160,7 +160,7 @@
},
biographical_info: {
birth_year: 1938,
- identity_doc_address_state: 'ND',
+ identity_doc_address_state: 'MT',
state: 'MT',
state_id_jurisdiction: 'ND',
state_id_number: '#############',
diff --git a/spec/features/idv/doc_auth/address_step_spec.rb b/spec/features/idv/doc_auth/address_step_spec.rb
index ed8969a8409..f21a26795db 100644
--- a/spec/features/idv/doc_auth/address_step_spec.rb
+++ b/spec/features/idv/doc_auth/address_step_spec.rb
@@ -2,7 +2,6 @@
RSpec.feature 'doc auth verify step', :js do
include IdvStepHelper
- include DocAuthHelper
let(:puerto_rico_address1_hint) do
"#{t('forms.example')} 150 Calle A Apt 3"
@@ -56,7 +55,10 @@
it 'allows the user to enter in a new address' do
expect_step_indicator_current_step(t('step_indicator.flows.idv.verify_info'))
expect(page).not_to have_content(t('forms.example'))
- fill_out_address_form_ok
+ fill_in 'idv_form_address1', with: '123 Main St'
+ fill_in 'idv_form_city', with: 'Nowhere'
+ select 'Virginia', from: 'idv_form_state'
+ fill_in 'idv_form_zipcode', with: '66044'
click_button t('forms.buttons.submit.update')
expect(page).to have_current_path(idv_verify_info_path)
diff --git a/spec/features/idv/doc_auth/socure_document_capture_spec.rb b/spec/features/idv/doc_auth/socure_document_capture_spec.rb
index be8b28388fc..b645ea89562 100644
--- a/spec/features/idv/doc_auth/socure_document_capture_spec.rb
+++ b/spec/features/idv/doc_auth/socure_document_capture_spec.rb
@@ -24,7 +24,6 @@
allow(IdentityConfig.store).to receive(:ruby_workers_idv_enabled).and_return(false)
allow_any_instance_of(ApplicationController).to receive(:analytics).and_return(fake_analytics)
@docv_transaction_token = stub_docv_document_request
- stub_docv_verification_data_pass
end
before(:all) do
@@ -33,127 +32,185 @@
after(:all) { @user.destroy }
- context 'standard desktop flow' do
+ context 'happy path' do
before do
- visit_idp_from_oidc_sp_with_ial2
- sign_in_and_2fa_user(@user)
- complete_doc_auth_steps_before_document_capture_step
- click_idv_continue
+ stub_docv_verification_data_pass
end
- context 'rate limits calls to backend docauth vendor', allow_browser_log: true do
+ context 'standard desktop flow' do
before do
- allow(IdentityConfig.store).to receive(:doc_auth_max_attempts).and_return(max_attempts)
- (max_attempts - 1).times do
- socure_docv_upload_documents(docv_transaction_token: @docv_transaction_token)
- end
- end
-
- it 'redirects to the rate limited error page' do
- expect(page).to have_current_path(fake_socure_document_capture_app_url)
- visit idv_socure_document_capture_path
- expect(page).to have_current_path(idv_socure_document_capture_path)
- socure_docv_upload_documents(
- docv_transaction_token: @docv_transaction_token,
- )
- visit idv_socure_document_capture_path
- expect(page).to have_current_path(idv_session_errors_rate_limited_path)
- expect(fake_analytics).to have_logged_event(
- 'Rate Limit Reached',
- limiter_type: :idv_doc_auth,
- )
+ visit_idp_from_oidc_sp_with_ial2
+ sign_in_and_2fa_user(@user)
+ complete_doc_auth_steps_before_document_capture_step
+ click_idv_continue
end
- context 'successfully processes image on last attempt' do
+ context 'rate limits calls to backend docauth vendor', allow_browser_log: true do
before do
- allow(IdentityConfig.store).to receive(:ruby_workers_idv_enabled).and_return(false)
- DocAuth::Mock::DocAuthMockClient.reset!
- allow(Analytics).to receive(:new).and_return(fake_analytics)
+ allow(IdentityConfig.store).to receive(:doc_auth_max_attempts).and_return(max_attempts)
+ (max_attempts - 1).times do
+ socure_docv_upload_documents(docv_transaction_token: @docv_transaction_token)
+ end
end
- it 'proceeds to the next page with valid info' do
+ it 'redirects to the rate limited error page' do
expect(page).to have_current_path(fake_socure_document_capture_app_url)
visit idv_socure_document_capture_path
expect(page).to have_current_path(idv_socure_document_capture_path)
socure_docv_upload_documents(
docv_transaction_token: @docv_transaction_token,
)
- visit idv_socure_document_capture_update_path
- expect(page).to have_current_path(idv_ssn_url)
-
visit idv_socure_document_capture_path
-
expect(page).to have_current_path(idv_session_errors_rate_limited_path)
expect(fake_analytics).to have_logged_event(
- :idv_socure_verification_data_requested,
+ 'Rate Limit Reached',
+ limiter_type: :idv_doc_auth,
)
end
+
+ context 'successfully processes image on last attempt' do
+ before do
+ DocAuth::Mock::DocAuthMockClient.reset!
+ end
+
+ it 'proceeds to the next page with valid info' do
+ expect(page).to have_current_path(fake_socure_document_capture_app_url)
+ visit idv_socure_document_capture_path
+ expect(page).to have_current_path(idv_socure_document_capture_path)
+ socure_docv_upload_documents(
+ docv_transaction_token: @docv_transaction_token,
+ )
+
+ visit idv_socure_document_capture_update_path
+ expect(page).to have_current_path(idv_ssn_url)
+
+ visit idv_socure_document_capture_path
+
+ expect(page).to have_current_path(idv_session_errors_rate_limited_path)
+ end
+ end
end
- end
- # ToDo post LG-14010
- context 'network connection errors' do
- xit 'catches network connection errors on document request', allow_browser_log: true do
- # expect(page).to have_content(I18n.t('doc_auth.errors.general.network_error'))
+ context 'network connection errors' do
+ context 'getting the capture path' do
+ before do
+ allow_any_instance_of(Faraday::Connection).to receive(:post).
+ and_raise(Faraday::ConnectionFailed)
+ end
+
+ it 'shows the network error page', js: true do
+ visit_idp_from_oidc_sp_with_ial2
+ sign_in_and_2fa_user(@user)
+
+ complete_doc_auth_steps_before_document_capture_step
+
+ expect(page).to have_content(t('doc_auth.headers.general.network_error'))
+ expect(page).to have_content(t('doc_auth.errors.general.new_network_error'))
+ end
+ end
+
+ # ToDo post LG-14010. Does this belong here, or on the polling page tests?
+ xit 'catches network connection errors on verification data request',
+ allow_browser_log: true do
+ # expect(page).to have_content(I18n.t('doc_auth.errors.general.network_error'))
+ end
end
- xit 'catches network connection errors on verification data request',
- allow_browser_log: true do
- # expect(page).to have_content(I18n.t('doc_auth.errors.general.network_error'))
+ it 'does not track state if state tracking is disabled' do
+ allow(IdentityConfig.store).to receive(:state_tracking_enabled).and_return(false)
+ socure_docv_upload_documents(
+ docv_transaction_token: @docv_transaction_token,
+ )
+
+ expect(DocAuthLog.find_by(user_id: @user.id).state).to be_nil
+ end
+
+ xit 'does track state if state tracking is disabled' do
+ allow(IdentityConfig.store).to receive(:state_tracking_enabled).and_return(true)
+ socure_docv_upload_documents(
+ docv_transaction_token: @docv_transaction_token,
+ )
+
+ expect(DocAuthLog.find_by(user_id: @user.id).state).not_to be_nil
end
end
- it 'does not track state if state tracking is disabled' do
- allow(IdentityConfig.store).to receive(:state_tracking_enabled).and_return(false)
- socure_docv_upload_documents(
- docv_transaction_token: @docv_transaction_token,
- )
+ context 'standard mobile flow' do
+ it 'proceeds to the next page with valid info' do
+ perform_in_browser(:mobile) do
+ visit_idp_from_oidc_sp_with_ial2
+ sign_in_and_2fa_user(@user)
+ complete_doc_auth_steps_before_document_capture_step
- expect(DocAuthLog.find_by(user_id: @user.id).state).to be_nil
+ expect(page).to have_current_path(idv_socure_document_capture_url)
+ expect_step_indicator_current_step(t('step_indicator.flows.idv.verify_id'))
+ click_idv_continue
+ socure_docv_upload_documents(
+ docv_transaction_token: @docv_transaction_token,
+ )
+ visit idv_socure_document_capture_update_path
+ expect(page).to have_current_path(idv_ssn_url)
+
+ expect(DocAuthLog.find_by(user_id: @user.id).state).to eq('NY')
+
+ fill_out_ssn_form_ok
+ click_idv_continue
+ complete_verify_step
+ expect(page).to have_current_path(idv_phone_url)
+ end
+ end
end
+ end
+
+ shared_examples 'a properly categorized Socure error' do |socure_error_code, expected_header_key|
+ before do
+ stub_docv_verification_data_fail_with([socure_error_code])
+
+ visit_idp_from_oidc_sp_with_ial2
+ sign_in_and_2fa_user(@user)
+
+ complete_doc_auth_steps_before_document_capture_step
- xit 'does track state if state tracking is disabled' do
- allow(IdentityConfig.store).to receive(:state_tracking_enabled).and_return(true)
+ click_idv_continue
socure_docv_upload_documents(
docv_transaction_token: @docv_transaction_token,
)
+ visit idv_socure_document_capture_update_path
+ end
- expect(DocAuthLog.find_by(user_id: @user.id).state).not_to be_nil
+ it 'shows the correct error page' do
+ expect(page).to have_content(t(expected_header_key))
end
end
- context 'standard mobile flow' do
- before do
- allow(IdentityConfig.store).to receive(:ruby_workers_idv_enabled).and_return(false)
- allow(Analytics).to receive(:new).and_return(fake_analytics)
- end
+ context 'a type 1 error (because we do not recognize the code)' do
+ it_behaves_like 'a properly categorized Socure error', 'XXXX', 'doc_auth.headers.unreadable_id'
+ end
- it 'proceeds to the next page with valid info' do
- perform_in_browser(:mobile) do
- visit_idp_from_oidc_sp_with_ial2
- sign_in_and_2fa_user(@user)
- complete_doc_auth_steps_before_document_capture_step
+ context 'a type 1 error' do
+ it_behaves_like 'a properly categorized Socure error', 'I848', 'doc_auth.headers.unreadable_id'
+ end
- expect(page).to have_current_path(idv_socure_document_capture_url)
- expect_step_indicator_current_step(t('step_indicator.flows.idv.verify_id'))
- click_idv_continue
- socure_docv_upload_documents(
- docv_transaction_token: @docv_transaction_token,
- )
- visit idv_socure_document_capture_update_path
- expect(page).to have_current_path(idv_ssn_url)
+ context 'a type 2 error' do
+ it_behaves_like 'a properly categorized Socure error',
+ 'I849',
+ 'doc_auth.headers.unaccepted_id_type'
+ end
- expect(DocAuthLog.find_by(user_id: @user.id).state).to eq('NY')
- expect(fake_analytics).to have_logged_event(
- :idv_socure_verification_data_requested,
- )
+ context 'a type 3 error' do
+ it_behaves_like 'a properly categorized Socure error', 'R827', 'doc_auth.headers.expired_id'
+ end
- fill_out_ssn_form_ok
- click_idv_continue
- complete_verify_step
- expect(page).to have_current_path(idv_phone_url)
- end
- end
+ context 'a type 4 error' do
+ it_behaves_like 'a properly categorized Socure error', 'I808', 'doc_auth.headers.low_resolution'
+ end
+
+ context 'a type 5 error' do
+ it_behaves_like 'a properly categorized Socure error', 'R845', 'doc_auth.headers.underage'
+ end
+
+ context 'a type 6 error' do
+ it_behaves_like 'a properly categorized Socure error', 'I856', 'doc_auth.headers.id_not_found'
end
def expect_rate_limited_header(expected_to_be_present)
diff --git a/spec/features/idv/doc_auth/verify_info_step_spec.rb b/spec/features/idv/doc_auth/verify_info_step_spec.rb
index 661c75dc0f9..86960660722 100644
--- a/spec/features/idv/doc_auth/verify_info_step_spec.rb
+++ b/spec/features/idv/doc_auth/verify_info_step_spec.rb
@@ -7,17 +7,16 @@
let(:fake_analytics) { FakeAnalytics.new }
let(:user) { user_with_2fa }
- # values from Idp::Constants::MOCK_IDV_APPLICANT
let(:fake_pii_details) do
{
- document_state: 'MT',
- document_number: '1111111111111',
- document_issued: '2019-12-31',
- document_expiration: '2099-12-31',
- first_name: 'FAKEY',
- last_name: 'MCFAKERSON',
- date_of_birth: '1938-10-06',
- address: '1 FAKE RD',
+ document_state: MOCK_IDV_APPLICANT[:state],
+ document_number: MOCK_IDV_APPLICANT[:state_id_number],
+ document_issued: MOCK_IDV_APPLICANT[:state_id_issued],
+ document_expiration: MOCK_IDV_APPLICANT[:state_id_expiration],
+ first_name: MOCK_IDV_APPLICANT[:first_name],
+ last_name: MOCK_IDV_APPLICANT[:last_name],
+ date_of_birth: MOCK_IDV_APPLICANT[:dob],
+ address: MOCK_IDV_APPLICANT[:address1],
}
end
@@ -247,7 +246,7 @@
context 'AAMVA' do
let(:mock_state_id_jurisdiction) do
- [Idp::Constants::MOCK_IDV_APPLICANT[:state_id_jurisdiction]]
+ [Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_JURISDICTION]
end
context 'when the user lives in an AAMVA supported state' do
diff --git a/spec/features/idv/hybrid_mobile/hybrid_socure_mobile_spec.rb b/spec/features/idv/hybrid_mobile/hybrid_socure_mobile_spec.rb
index d2da26e722f..0245ba60c5a 100644
--- a/spec/features/idv/hybrid_mobile/hybrid_socure_mobile_spec.rb
+++ b/spec/features/idv/hybrid_mobile/hybrid_socure_mobile_spec.rb
@@ -26,160 +26,253 @@
@docv_transaction_token = stub_docv_document_request
end
- it 'proofs and hands off to mobile', js: true do
- user = nil
+ context 'happy path' do
+ it 'proofs and hands off to mobile', js: true do
+ user = nil
- perform_in_browser(:desktop) do
- visit_idp_from_sp_with_ial2(sp)
- user = sign_up_and_2fa_ial1_user
+ perform_in_browser(:desktop) do
+ visit_idp_from_sp_with_ial2(sp)
+ user = sign_up_and_2fa_ial1_user
- complete_doc_auth_steps_before_hybrid_handoff_step
- clear_and_fill_in(:doc_auth_phone, phone_number)
- click_send_link
+ complete_doc_auth_steps_before_hybrid_handoff_step
+ clear_and_fill_in(:doc_auth_phone, phone_number)
+ click_send_link
- expect(page).to have_content(t('doc_auth.headings.text_message'))
- expect(page).to have_content(t('doc_auth.info.you_entered'))
- expect(page).to have_content('+1 415-555-0199')
+ expect(page).to have_content(t('doc_auth.headings.text_message'))
+ expect(page).to have_content(t('doc_auth.info.you_entered'))
+ expect(page).to have_content('+1 415-555-0199')
- # Confirm that Continue button is not shown when polling is enabled
- expect(page).not_to have_content(t('doc_auth.buttons.continue'))
- end
+ # Confirm that Continue button is not shown when polling is enabled
+ expect(page).not_to have_content(t('doc_auth.buttons.continue'))
+ end
- expect(@sms_link).to be_present
+ expect(@sms_link).to be_present
- perform_in_browser(:mobile) do
- visit @sms_link
+ perform_in_browser(:mobile) do
+ visit @sms_link
- # Confirm that jumping to LinkSent page does not cause errors
- visit idv_link_sent_url
- expect(page).to have_current_path(root_url)
+ # Confirm that jumping to LinkSent page does not cause errors
+ visit idv_link_sent_url
+ expect(page).to have_current_path(root_url)
- # Confirm that we end up on the LN / Mock page even if we try to
- # go to the Socure one.
- visit idv_hybrid_mobile_socure_document_capture_url
- expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_url)
+ # Confirm that we end up on the LN / Mock page even if we try to
+ # go to the Socure one.
+ visit idv_hybrid_mobile_socure_document_capture_url
+ expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_url)
- # Confirm that clicking cancel and then coming back doesn't cause errors
- click_link 'Cancel'
- visit idv_hybrid_mobile_socure_document_capture_url
+ # Confirm that clicking cancel and then coming back doesn't cause errors
+ click_link 'Cancel'
+ visit idv_hybrid_mobile_socure_document_capture_url
- # Confirm that jumping to Phone page does not cause errors
- visit idv_phone_url
- expect(page).to have_current_path(root_url)
- visit idv_hybrid_mobile_socure_document_capture_url
+ # Confirm that jumping to Phone page does not cause errors
+ visit idv_phone_url
+ expect(page).to have_current_path(root_url)
+ visit idv_hybrid_mobile_socure_document_capture_url
- # Confirm that jumping to Welcome page does not cause errors
- visit idv_welcome_url
- expect(page).to have_current_path(root_url)
- visit idv_hybrid_mobile_socure_document_capture_url
+ # Confirm that jumping to Welcome page does not cause errors
+ visit idv_welcome_url
+ expect(page).to have_current_path(root_url)
+ visit idv_hybrid_mobile_socure_document_capture_url
- expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_url)
+ expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_url)
- stub_docv_verification_data_pass
- click_idv_continue
- expect(page).to have_current_path(fake_socure_document_capture_app_url)
- socure_docv_upload_documents(docv_transaction_token: @docv_transaction_token)
- visit idv_hybrid_mobile_socure_document_capture_update_url
+ stub_docv_verification_data_pass
+ click_idv_continue
+ expect(page).to have_current_path(fake_socure_document_capture_app_url)
+ socure_docv_upload_documents(docv_transaction_token: @docv_transaction_token)
+ visit idv_hybrid_mobile_socure_document_capture_update_url
- expect(page).to have_current_path(idv_hybrid_mobile_capture_complete_url)
- expect(page).to have_content(strip_nbsp(t('doc_auth.headings.capture_complete')))
- expect(page).to have_text(t('doc_auth.instructions.switch_back'))
- expect_step_indicator_current_step(t('step_indicator.flows.idv.verify_id'))
+ expect(page).to have_current_path(idv_hybrid_mobile_capture_complete_url)
+ expect(page).to have_content(strip_nbsp(t('doc_auth.headings.capture_complete')))
+ expect(page).to have_text(t('doc_auth.instructions.switch_back'))
+ expect_step_indicator_current_step(t('step_indicator.flows.idv.verify_id'))
- # To be fixed in app:
- # Confirm app disallows jumping back to DocumentCapture page
- # visit idv_hybrid_mobile_socure_document_capture_url
- # expect(page).to have_current_path(idv_hybrid_mobile_capture_complete_url)
- end
+ # To be fixed in app:
+ # Confirm app disallows jumping back to DocumentCapture page
+ # visit idv_hybrid_mobile_socure_document_capture_url
+ # expect(page).to have_current_path(idv_hybrid_mobile_capture_complete_url)
+ end
- perform_in_browser(:desktop) do
- expect(page).to_not have_content(t('doc_auth.headings.text_message'), wait: 10)
- expect(page).to have_current_path(idv_ssn_path)
+ perform_in_browser(:desktop) do
+ expect(page).to_not have_content(t('doc_auth.headings.text_message'), wait: 10)
+ expect(page).to have_current_path(idv_ssn_path)
- fill_out_ssn_form_ok
- click_idv_continue
+ fill_out_ssn_form_ok
+ click_idv_continue
- expect(page).to have_content(t('headings.verify'))
- complete_verify_step
+ expect(page).to have_content(t('headings.verify'))
+ complete_verify_step
- prefilled_phone = page.find(id: 'idv_phone_form_phone').value
+ prefilled_phone = page.find(id: 'idv_phone_form_phone').value
- expect(
- PhoneFormatter.format(prefilled_phone),
- ).to eq(
- PhoneFormatter.format(user.default_phone_configuration.phone),
- )
+ expect(
+ PhoneFormatter.format(prefilled_phone),
+ ).to eq(
+ PhoneFormatter.format(user.default_phone_configuration.phone),
+ )
- fill_out_phone_form_ok
- verify_phone_otp
+ fill_out_phone_form_ok
+ verify_phone_otp
- fill_in t('idv.form.password'), with: Features::SessionHelper::VALID_PASSWORD
- click_idv_continue
+ fill_in t('idv.form.password'), with: Features::SessionHelper::VALID_PASSWORD
+ click_idv_continue
- acknowledge_and_confirm_personal_key
+ acknowledge_and_confirm_personal_key
- validate_idv_completed_page(user)
- click_agree_and_continue
+ validate_idv_completed_page(user)
+ click_agree_and_continue
- validate_return_to_sp
+ validate_return_to_sp
+ end
end
- end
- it 'shows the waiting screen correctly after cancelling from mobile and restarting', js: true do
- user = nil
+ it 'shows the waiting screen correctly after cancelling from mobile and restarting', js: true do
+ user = nil
- perform_in_browser(:desktop) do
- user = sign_in_and_2fa_user
- complete_doc_auth_steps_before_hybrid_handoff_step
- clear_and_fill_in(:doc_auth_phone, phone_number)
- click_send_link
+ perform_in_browser(:desktop) do
+ user = sign_in_and_2fa_user
+ complete_doc_auth_steps_before_hybrid_handoff_step
+ clear_and_fill_in(:doc_auth_phone, phone_number)
+ click_send_link
- expect(page).to have_content(t('doc_auth.headings.text_message'))
- end
+ expect(page).to have_content(t('doc_auth.headings.text_message'))
+ end
+
+ expect(@sms_link).to be_present
- expect(@sms_link).to be_present
+ perform_in_browser(:mobile) do
+ visit @sms_link
+ expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_url)
+ expect(page).not_to have_content(t('doc_auth.headings.document_capture_selfie'))
+ click_on t('links.cancel')
+ click_on t('forms.buttons.cancel') # Yes, cancel
+ end
- perform_in_browser(:mobile) do
- visit @sms_link
- expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_url)
- expect(page).not_to have_content(t('doc_auth.headings.document_capture_selfie'))
- click_on t('links.cancel')
- click_on t('forms.buttons.cancel') # Yes, cancel
+ perform_in_browser(:desktop) do
+ expect(page).to_not have_content(t('doc_auth.headings.text_message'), wait: 10)
+ clear_and_fill_in(:doc_auth_phone, phone_number)
+ click_send_link
+
+ expect(page).to have_content(t('doc_auth.headings.text_message'))
+ end
end
- perform_in_browser(:desktop) do
- expect(page).to_not have_content(t('doc_auth.headings.text_message'), wait: 10)
- clear_and_fill_in(:doc_auth_phone, phone_number)
- click_send_link
+ context 'user is rate limited on mobile' do
+ let(:max_attempts) { IdentityConfig.store.doc_auth_max_attempts }
+
+ before do
+ allow(IdentityConfig.store).to receive(:doc_auth_max_attempts).and_return(max_attempts)
+ DocAuth::Mock::DocAuthMockClient.mock_response!(
+ method: :post_front_image,
+ response: DocAuth::Response.new(
+ success: false,
+ errors: { network: I18n.t('doc_auth.errors.general.network_error') },
+ ),
+ )
+ end
+
+ it 'shows capture complete on mobile and error page on desktop', js: true do
+ user = nil
- expect(page).to have_content(t('doc_auth.headings.text_message'))
+ perform_in_browser(:desktop) do
+ user = sign_in_and_2fa_user
+ complete_doc_auth_steps_before_hybrid_handoff_step
+ clear_and_fill_in(:doc_auth_phone, phone_number)
+ click_send_link
+
+ expect(page).to have_content(t('doc_auth.headings.text_message'))
+ end
+
+ expect(@sms_link).to be_present
+
+ perform_in_browser(:mobile) do
+ visit @sms_link
+
+ click_idv_continue
+ expect(page).to have_current_path(fake_socure_document_capture_app_url)
+ stub_docv_verification_data_pass
+ max_attempts.times do
+ socure_docv_upload_documents(docv_transaction_token: @docv_transaction_token)
+ end
+
+ visit idv_hybrid_mobile_socure_document_capture_update_url
+
+ expect(page).to have_current_path(idv_hybrid_mobile_capture_complete_url)
+ expect(page).to have_text(t('doc_auth.instructions.switch_back'))
+ end
+
+ perform_in_browser(:desktop) do
+ expect(page).to have_current_path(idv_session_errors_rate_limited_path, wait: 10)
+ end
+ end
end
- end
- context 'user is rate limited on mobile' do
- let(:max_attempts) { IdentityConfig.store.doc_auth_max_attempts }
+ it 'prefills the phone number used on the phone step if the user has no MFA phone', :js do
+ user = create(:user, :with_authentication_app)
- before do
- allow(IdentityConfig.store).to receive(:doc_auth_max_attempts).and_return(max_attempts)
- DocAuth::Mock::DocAuthMockClient.mock_response!(
- method: :post_front_image,
- response: DocAuth::Response.new(
- success: false,
- errors: { network: I18n.t('doc_auth.errors.general.network_error') },
- ),
- )
+ perform_in_browser(:desktop) do
+ start_idv_from_sp(facial_match_required: false)
+ sign_in_and_2fa_user(user)
+
+ complete_doc_auth_steps_before_hybrid_handoff_step
+ clear_and_fill_in(:doc_auth_phone, phone_number)
+ click_send_link
+ end
+
+ expect(@sms_link).to be_present
+
+ perform_in_browser(:mobile) do
+ visit @sms_link
+
+ expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_url)
+ stub_docv_verification_data_pass
+ click_idv_continue
+ expect(page).to have_current_path(fake_socure_document_capture_app_url)
+ socure_docv_upload_documents(docv_transaction_token: @docv_transaction_token)
+ visit idv_hybrid_mobile_socure_document_capture_update_url
+
+ expect(page).to have_current_path(idv_hybrid_mobile_capture_complete_url)
+ expect(page).to have_text(t('doc_auth.instructions.switch_back'))
+ end
+
+ perform_in_browser(:desktop) do
+ expect(page).to have_current_path(idv_ssn_path, wait: 10)
+
+ fill_out_ssn_form_ok
+ click_idv_continue
+
+ expect(page).to have_content(t('headings.verify'))
+ complete_verify_step
+
+ prefilled_phone = page.find(id: 'idv_phone_form_phone').value
+
+ expect(
+ PhoneFormatter.format(prefilled_phone),
+ ).to eq(
+ PhoneFormatter.format(phone_number),
+ )
+ end
end
+ end
- it 'shows capture complete on mobile and error page on desktop', js: true do
+ shared_examples 'a properly categorized Socure error' do |socure_error_code, expected_header_key|
+ it 'shows the correct error page', js: true do
user = nil
perform_in_browser(:desktop) do
- user = sign_in_and_2fa_user
+ visit_idp_from_sp_with_ial2(sp)
+ user = sign_up_and_2fa_ial1_user
+
complete_doc_auth_steps_before_hybrid_handoff_step
clear_and_fill_in(:doc_auth_phone, phone_number)
click_send_link
expect(page).to have_content(t('doc_auth.headings.text_message'))
+ expect(page).to have_content(t('doc_auth.info.you_entered'))
+ expect(page).to have_content('+1 415-555-0199')
+
+ # Confirm that Continue button is not shown when polling is enabled
+ expect(page).not_to have_content(t('doc_auth.buttons.continue'))
end
expect(@sms_link).to be_present
@@ -187,69 +280,85 @@
perform_in_browser(:mobile) do
visit @sms_link
+ stub_docv_verification_data_fail_with([socure_error_code])
+
click_idv_continue
- expect(page).to have_current_path(fake_socure_document_capture_app_url)
- stub_docv_verification_data_pass
- max_attempts.times do
- socure_docv_upload_documents(docv_transaction_token: @docv_transaction_token)
- end
+ socure_docv_upload_documents(docv_transaction_token: @docv_transaction_token)
visit idv_hybrid_mobile_socure_document_capture_update_url
- expect(page).to have_current_path(idv_hybrid_mobile_capture_complete_url)
- expect(page).to have_text(t('doc_auth.instructions.switch_back'))
+ expect(page).to have_text(t(expected_header_key))
+
+ click_try_again
+
+ expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_path)
end
perform_in_browser(:desktop) do
- expect(page).to have_current_path(idv_session_errors_rate_limited_path, wait: 10)
+ expect(page).to have_current_path(idv_link_sent_path)
end
end
end
- it 'prefills the phone number used on the phone step if the user has no MFA phone', :js do
- user = create(:user, :with_authentication_app)
+ context 'a type 1 error (because we do not recognize the code)' do
+ it_behaves_like 'a properly categorized Socure error', 'XXXX', 'doc_auth.headers.unreadable_id'
+ end
- perform_in_browser(:desktop) do
- start_idv_from_sp(facial_match_required: false)
- sign_in_and_2fa_user(user)
+ context 'a type 1 error' do
+ it_behaves_like 'a properly categorized Socure error', 'I848', 'doc_auth.headers.unreadable_id'
+ end
- complete_doc_auth_steps_before_hybrid_handoff_step
- clear_and_fill_in(:doc_auth_phone, phone_number)
- click_send_link
- end
+ context 'a type 2 error' do
+ it_behaves_like 'a properly categorized Socure error',
+ 'I849',
+ 'doc_auth.headers.unaccepted_id_type'
+ end
- expect(@sms_link).to be_present
+ context 'a type 3 error' do
+ it_behaves_like 'a properly categorized Socure error', 'R827', 'doc_auth.headers.expired_id'
+ end
+
+ context 'a type 4 error' do
+ it_behaves_like 'a properly categorized Socure error', 'I808', 'doc_auth.headers.low_resolution'
+ end
- perform_in_browser(:mobile) do
- visit @sms_link
+ context 'a type 5 error' do
+ it_behaves_like 'a properly categorized Socure error', 'R845', 'doc_auth.headers.underage'
+ end
- expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_url)
- stub_docv_verification_data_pass
- click_idv_continue
- expect(page).to have_current_path(fake_socure_document_capture_app_url)
- socure_docv_upload_documents(docv_transaction_token: @docv_transaction_token)
- visit idv_hybrid_mobile_socure_document_capture_update_url
+ context 'a type 6 error' do
+ it_behaves_like 'a properly categorized Socure error', 'I856', 'doc_auth.headers.id_not_found'
+ end
- expect(page).to have_current_path(idv_hybrid_mobile_capture_complete_url)
- expect(page).to have_text(t('doc_auth.instructions.switch_back'))
+ context 'with a network error requesting the capture app url' do
+ before do
+ allow_any_instance_of(Faraday::Connection).to receive(:post).
+ and_raise(Faraday::ConnectionFailed)
end
- perform_in_browser(:desktop) do
- expect(page).to have_current_path(idv_ssn_path, wait: 10)
+ it 'shows the network error page on the phone and the link sent page on the desktop',
+ js: true do
+ user = nil
- fill_out_ssn_form_ok
- click_idv_continue
+ perform_in_browser(:desktop) do
+ visit_idp_from_sp_with_ial2(sp)
+ user = sign_up_and_2fa_ial1_user
- expect(page).to have_content(t('headings.verify'))
- complete_verify_step
+ complete_doc_auth_steps_before_hybrid_handoff_step
+ clear_and_fill_in(:doc_auth_phone, phone_number)
+ click_send_link
+ end
- prefilled_phone = page.find(id: 'idv_phone_form_phone').value
+ perform_in_browser(:mobile) do
+ visit @sms_link
- expect(
- PhoneFormatter.format(prefilled_phone),
- ).to eq(
- PhoneFormatter.format(phone_number),
- )
+ expect(page).to have_text(t('doc_auth.headers.general.network_error'))
+ expect(page).to have_text(t('doc_auth.errors.general.new_network_error'))
+ end
+
+ perform_in_browser(:desktop) do
+ expect(page).to have_current_path(idv_link_sent_path)
+ end
end
end
end
diff --git a/spec/features/idv/in_person_spec.rb b/spec/features/idv/in_person_spec.rb
index 68bd0646446..c5af6f91e50 100644
--- a/spec/features/idv/in_person_spec.rb
+++ b/spec/features/idv/in_person_spec.rb
@@ -56,7 +56,8 @@
expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS1).twice
expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS2).twice
expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_CITY).twice
- expect(page).to have_text(Idp::Constants::MOCK_IDV_APPLICANT[:state_id_jurisdiction], count: 3)
+ expect(page).to have_text(Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_JURISDICTION, count: 1)
+ expect(page).to have_text(Idp::Constants::MOCK_IDV_APPLICANT_STATE, count: 2)
expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ZIPCODE).twice
expect(page).to have_text(DocAuthHelper::GOOD_SSN_MASKED)
@@ -428,12 +429,13 @@
expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS1)
expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS2)
expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_CITY)
- expect(page).to have_text(Idp::Constants::MOCK_IDV_APPLICANT[:state_id_jurisdiction]).twice
+ expect(page).to have_text(Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_JURISDICTION).once
+ expect(page).to have_text(Idp::Constants::MOCK_IDV_APPLICANT_STATE).twice
expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ZIPCODE)
expect(page).to have_text(InPersonHelper::GOOD_ADDRESS1)
expect(page).to have_text(InPersonHelper::GOOD_CITY)
expect(page).to have_text(InPersonHelper::GOOD_ZIPCODE)
- expect(page).to have_text(Idp::Constants::MOCK_IDV_APPLICANT[:state])
+ expect(page).to have_text(Idp::Constants::MOCK_IDV_APPLICANT_STATE)
expect(page).to have_text(DocAuthHelper::GOOD_SSN_MASKED)
complete_verify_step(user)
diff --git a/spec/features/idv/in_person_threatmetrix_spec.rb b/spec/features/idv/in_person_threatmetrix_spec.rb
index 799acbc5a98..6747cd4cbbc 100644
--- a/spec/features/idv/in_person_threatmetrix_spec.rb
+++ b/spec/features/idv/in_person_threatmetrix_spec.rb
@@ -99,8 +99,12 @@ def deactivate_profile_update_enrollment(status:)
expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS2).twice
expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_CITY).twice
expect(page).to have_text(
- Idp::Constants::MOCK_IDV_APPLICANT[:state_id_jurisdiction],
- count: 3,
+ Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_JURISDICTION,
+ count: 1,
+ )
+ expect(page).to have_text(
+ Idp::Constants::MOCK_IDV_APPLICANT_STATE,
+ count: 2,
)
expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ZIPCODE).twice
expect(page).to have_text(DocAuthHelper::GOOD_SSN_MASKED)
diff --git a/spec/features/idv/steps/in_person/state_id_spec.rb b/spec/features/idv/steps/in_person/state_id_spec.rb
index b9ae0d07f62..7572eba495b 100644
--- a/spec/features/idv/steps/in_person/state_id_spec.rb
+++ b/spec/features/idv/steps/in_person/state_id_spec.rb
@@ -86,7 +86,7 @@
expect(page).to have_field(t('components.memorable_date.year'), with: '1938')
expect(page).to have_field(
t('in_person_proofing.form.state_id.state_id_jurisdiction'),
- with: Idp::Constants::MOCK_IDV_APPLICANT[:state_id_jurisdiction],
+ with: Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_JURISDICTION,
)
expect(page).to have_field(
t('in_person_proofing.form.state_id.state_id_number'),
@@ -110,7 +110,7 @@
)
expect(page).to have_field(
t('in_person_proofing.form.state_id.identity_doc_address_state'),
- with: Idp::Constants::MOCK_IDV_APPLICANT[:state_id_jurisdiction],
+ with: Idp::Constants::MOCK_IDV_APPLICANT_STATE,
)
expect(page).to have_checked_field(
t('in_person_proofing.form.state_id.same_address_as_id_yes'),
diff --git a/spec/features/idv/steps/in_person/verify_info_spec.rb b/spec/features/idv/steps/in_person/verify_info_spec.rb
index 528e6607973..29cf6a5eb1b 100644
--- a/spec/features/idv/steps/in_person/verify_info_spec.rb
+++ b/spec/features/idv/steps/in_person/verify_info_spec.rb
@@ -33,8 +33,12 @@
expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS2).twice
expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_CITY).twice
expect(page).to have_text(
- Idp::Constants::MOCK_IDV_APPLICANT[:state_id_jurisdiction],
- count: 3,
+ Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_JURISDICTION,
+ count: 1,
+ )
+ expect(page).to have_text(
+ Idp::Constants::MOCK_IDV_APPLICANT_STATE,
+ count: 2,
)
expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ZIPCODE).twice
expect(page).to have_text(DocAuthHelper::GOOD_SSN_MASKED)
diff --git a/spec/features/idv/steps/in_person_opt_in_ipp_spec.rb b/spec/features/idv/steps/in_person_opt_in_ipp_spec.rb
index db8de8f064d..c9068552a2d 100644
--- a/spec/features/idv/steps/in_person_opt_in_ipp_spec.rb
+++ b/spec/features/idv/steps/in_person_opt_in_ipp_spec.rb
@@ -58,8 +58,12 @@
expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS2).twice
expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_CITY).twice
expect(page).to have_text(
- Idp::Constants::MOCK_IDV_APPLICANT[:state_id_jurisdiction],
- count: 3,
+ Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_JURISDICTION,
+ count: 1,
+ )
+ expect(page).to have_text(
+ Idp::Constants::MOCK_IDV_APPLICANT_STATE,
+ count: 2,
)
expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ZIPCODE).twice
expect(page).to have_text(DocAuthHelper::GOOD_SSN_MASKED)
@@ -175,8 +179,12 @@
expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS2).twice
expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_CITY).twice
expect(page).to have_text(
- Idp::Constants::MOCK_IDV_APPLICANT[:state_id_jurisdiction],
- count: 3,
+ Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_JURISDICTION,
+ count: 1,
+ )
+ expect(page).to have_text(
+ Idp::Constants::MOCK_IDV_APPLICANT_STATE,
+ count: 2,
)
expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ZIPCODE).twice
expect(page).to have_text(DocAuthHelper::GOOD_SSN_MASKED)
@@ -360,8 +368,12 @@
expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS2).twice
expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_CITY).twice
expect(page).to have_text(
- Idp::Constants::MOCK_IDV_APPLICANT[:state_id_jurisdiction],
- count: 3,
+ Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_JURISDICTION,
+ count: 1,
+ )
+ expect(page).to have_text(
+ Idp::Constants::MOCK_IDV_APPLICANT_STATE,
+ count: 2,
)
expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ZIPCODE).twice
expect(page).to have_text(DocAuthHelper::GOOD_SSN_MASKED)
diff --git a/spec/features/webauthn/hidden_spec.rb b/spec/features/webauthn/hidden_spec.rb
index 57b52cbb92b..0c0ea800316 100644
--- a/spec/features/webauthn/hidden_spec.rb
+++ b/spec/features/webauthn/hidden_spec.rb
@@ -3,6 +3,7 @@
RSpec.describe 'webauthn hide' do
include JavascriptDriverHelper
include WebAuthnHelper
+ include AbTestsHelper
describe 'security key' do
let(:option_id) { 'two_factor_options_form_selection_webauthn' }
@@ -59,6 +60,36 @@
expect(webauthn_option_hidden?).to eq(true)
end
+ context 'when in ab test for desktop setup' do
+ before do
+ allow(IdentityConfig.store).to receive(:desktop_ft_unlock_setup_option_percent_tested).
+ and_return(100)
+ reload_ab_tests
+ end
+
+ it 'displays the authenticator option' do
+ sign_up_and_set_password
+ simulate_platform_authenticator_available
+
+ expect(webauthn_option_hidden?).to eq(false)
+ end
+ end
+
+ context 'when A/B test is disabled' do
+ before do
+ allow(IdentityConfig.store).to receive(:desktop_ft_unlock_setup_option_percent_tested).
+ and_return(0)
+ reload_ab_tests
+ end
+
+ it 'hides the authenticator option' do
+ sign_up_and_set_password
+ simulate_platform_authenticator_available
+
+ expect(webauthn_option_hidden?).to eq(true)
+ end
+ end
+
context 'with supported browser and platform authenticator available',
driver: :headless_chrome_mobile do
it 'displays the authenticator option' do
diff --git a/spec/fixtures/artifacts/test-1.txt b/spec/fixtures/artifacts/test-1.txt
deleted file mode 100644
index ba62c8ecf82..00000000000
--- a/spec/fixtures/artifacts/test-1.txt
+++ /dev/null
@@ -1 +0,0 @@
-local-test-1
diff --git a/spec/fixtures/artifacts/test-2.txt b/spec/fixtures/artifacts/test-2.txt
deleted file mode 100644
index aa355c3cef3..00000000000
--- a/spec/fixtures/artifacts/test-2.txt
+++ /dev/null
@@ -1 +0,0 @@
-local-test-2
diff --git a/spec/fixtures/artifacts/test-3.txt b/spec/fixtures/artifacts/test-3.txt
deleted file mode 100644
index 6a1440f4109..00000000000
--- a/spec/fixtures/artifacts/test-3.txt
+++ /dev/null
@@ -1 +0,0 @@
-local-test-3
diff --git a/spec/forms/add_user_email_form_spec.rb b/spec/forms/add_user_email_form_spec.rb
index 18a1b72b0fe..a6d1d45c1b6 100644
--- a/spec/forms/add_user_email_form_spec.rb
+++ b/spec/forms/add_user_email_form_spec.rb
@@ -8,8 +8,19 @@
describe '#submit' do
let(:new_email) { 'new@example.com' }
-
- subject(:submit) { form.submit(user, email: new_email) }
+ let(:request_id) { 'request-id-1' }
+
+ subject(:submit) { form.submit(user, email: new_email, request_id:) }
+
+ it 'returns a successful result' do
+ expect(submit.to_h).to eq(
+ success: true,
+ errors: {},
+ domain_name: 'example.com',
+ in_select_email_flow: false,
+ user_id: user.uuid,
+ )
+ end
it 'creates a new EmailAddress record for a new email address' do
expect(EmailAddress.find_with_email(new_email)).to be_nil
@@ -79,5 +90,31 @@
end
end
end
+
+ context 'in select email flow' do
+ subject(:form) { AddUserEmailForm.new(in_select_email_flow: true) }
+
+ it 'sends email confirm with parameter value' do
+ send_add_email_confirmation = instance_double(SendAddEmailConfirmation)
+ expect(SendAddEmailConfirmation).to receive(:new).and_return(send_add_email_confirmation)
+ expect(send_add_email_confirmation).to receive(:call).with(
+ email_address: kind_of(EmailAddress),
+ in_select_email_flow: true,
+ request_id:,
+ )
+
+ submit
+ end
+
+ it 'includes extra analytics in result for flow value' do
+ expect(submit.to_h).to eq(
+ success: true,
+ errors: {},
+ domain_name: 'example.com',
+ in_select_email_flow: true,
+ user_id: user.uuid,
+ )
+ end
+ end
end
end
diff --git a/spec/forms/select_email_form_spec.rb b/spec/forms/select_email_form_spec.rb
index 3fea0f38232..539fd7ef340 100644
--- a/spec/forms/select_email_form_spec.rb
+++ b/spec/forms/select_email_form_spec.rb
@@ -14,7 +14,7 @@
let(:selected_email_id) { user.confirmed_email_addresses.take.id }
it 'is successful' do
- expect(response.to_h).to eq(success: true)
+ expect(response.to_h).to eq(success: true, selected_email_id:)
end
context 'with associated identity' do
@@ -29,15 +29,28 @@
end
context 'with an invalid email id' do
- let(:selected_email_id) { nil }
+ let(:selected_email_id) { '' }
it 'is unsuccessful' do
expect(response.to_h).to eq(
success: false,
error_details: { selected_email_id: { not_found: true } },
+ selected_email_id: nil,
)
end
+ context 'with present value that does not convert to numeric' do
+ let(:selected_email_id) { true }
+
+ it 'is unsuccessful without raising exception' do
+ expect(response.to_h).to eq(
+ success: false,
+ error_details: { selected_email_id: { not_found: true } },
+ selected_email_id: nil,
+ )
+ end
+ end
+
context 'with associated identity' do
let(:identity) do
create(
@@ -55,7 +68,7 @@
end
context 'with an unconfirmed email address added' do
- let(:selected_email_id) { user.email_addresses.find_by(confirmed_at: nil) }
+ let(:selected_email_id) { user.email_addresses.find_by(confirmed_at: nil).id }
before do
create(:email_address, :unconfirmed, user:)
@@ -65,6 +78,7 @@
expect(response.to_h).to eq(
success: false,
error_details: { selected_email_id: { not_found: true } },
+ selected_email_id:,
)
end
@@ -85,6 +99,7 @@
expect(response.to_h).to eq(
success: false,
error_details: { selected_email_id: { not_found: true } },
+ selected_email_id:,
)
end
diff --git a/spec/jobs/account_creation_threat_metrix_job_spec.rb b/spec/jobs/account_creation_threat_metrix_job_spec.rb
index cffb09cfda7..091df7d8fda 100644
--- a/spec/jobs/account_creation_threat_metrix_job_spec.rb
+++ b/spec/jobs/account_creation_threat_metrix_job_spec.rb
@@ -3,6 +3,7 @@
RSpec.describe AccountCreationThreatMetrixJob, type: :job do
let(:user) { create(:user, :fully_registered) }
let(:request_ip) { Faker::Internet.ip_v4_address }
+ let(:service_provider) { create(:service_provider) }
let(:threatmetrix_session_id) { SecureRandom.uuid }
let(:authentication_device_profiling) { :collect_only }
let(:lexisnexis_threatmetrix_mock_enabled) { false }
@@ -28,6 +29,7 @@
user_id: user.id,
threatmetrix_session_id: threatmetrix_session_id,
request_ip: request_ip,
+ uuid_prefix: service_provider.app_id,
)
end
diff --git a/spec/jobs/reports/combined_invoice_supplement_report_v2_spec.rb b/spec/jobs/reports/combined_invoice_supplement_report_v2_spec.rb
index e04ce038453..c933253c950 100644
--- a/spec/jobs/reports/combined_invoice_supplement_report_v2_spec.rb
+++ b/spec/jobs/reports/combined_invoice_supplement_report_v2_spec.rb
@@ -51,8 +51,13 @@
let(:user2) { create(:user, profiles: [profile2]) }
let(:profile2) { build(:profile, verified_at: DateTime.new(2018, 6, 1).utc) }
+ let(:report_date) { inside_iaa1 }
- let(:csv) { CSV.parse(report.perform(Time.zone.today), headers: true) }
+ let(:csv) do
+ travel_to report_date do
+ CSV.parse(report.perform(report_date), headers: true)
+ end
+ end
before do
iaa_order1.integrations << build_integration(
@@ -140,6 +145,14 @@
expect(row['issuer_unique_users'].to_i).to eq(2)
end
end
+
+ context 'when IAA ended more than 90 days ago' do
+ let(:report_date) { iaa1_range.end + 90.days }
+
+ it 'is excluded from the report' do
+ expect(csv.length).to eq(0)
+ end
+ end
end
context 'with an IAA with two issuers in September 2020' do
@@ -206,7 +219,11 @@
let(:user10) { create(:user, profiles: [profile10]) }
let(:profile10) { build(:profile, verified_at: DateTime.new(2015, 1, 1).utc) }
- let(:csv) { CSV.parse(report.perform(Time.zone.today), headers: true) }
+ let(:csv) do
+ travel_to inside_iaa2 do
+ CSV.parse(report.perform(Time.zone.today), headers: true)
+ end
+ end
before do
iaa_order2.integrations << build_integration(
@@ -385,6 +402,7 @@
let(:partner_account3) { create(:partner_account) }
let(:iaa3_range) { DateTime.new(2020, 9, 1).utc..DateTime.new(2021, 8, 30).utc }
+ let(:inside_iaa3) { iaa3_range.begin + 1.day }
let(:gtc3) do
create(
@@ -417,7 +435,11 @@
let(:user12) { create(:user, profiles: [profile12]) }
let(:profile12) { build(:profile, verified_at: DateTime.new(2017, 9, 10).utc) }
- let(:csv) { CSV.parse(report.perform(Time.zone.today), headers: true) }
+ let(:csv) do
+ travel_to inside_iaa3 do
+ CSV.parse(report.perform(Time.zone.today), headers: true)
+ end
+ end
before do
iaa_order3.integrations << build_integration(
diff --git a/spec/models/backup_code_configuration_spec.rb b/spec/models/backup_code_configuration_spec.rb
index 05114d8473d..0558f99deef 100644
--- a/spec/models/backup_code_configuration_spec.rb
+++ b/spec/models/backup_code_configuration_spec.rb
@@ -140,8 +140,9 @@ def save_and_find(find:, save: 'just-some-not-null-value')
bc = BackupCodeConfiguration.new
set = BackupCodeConfiguration.selection_presenters([bc])
- expect(set.first).
- instance_of? TwoFactorAuthentication::SignInBackupCodeSelectionPresenter.class
+ expect(set.first).to be_instance_of(
+ TwoFactorAuthentication::SignInBackupCodeSelectionPresenter,
+ )
end
it 'returns only one selection presenter if multiple backup code configurations' do
@@ -149,8 +150,9 @@ def save_and_find(find:, save: 'just-some-not-null-value')
bc2 = BackupCodeConfiguration.new
set = BackupCodeConfiguration.selection_presenters([bc, bc2])
- expect(set.first).
- instance_of? TwoFactorAuthentication::SignInBackupCodeSelectionPresenter.class
+ expect(set.first).to be_instance_of(
+ TwoFactorAuthentication::SignInBackupCodeSelectionPresenter,
+ )
expect(set.size).to eq(1)
end
end
diff --git a/spec/services/account_creation/device_profiling_spec.rb b/spec/services/account_creation/device_profiling_spec.rb
index 3178f51170e..03f3c69bfec 100644
--- a/spec/services/account_creation/device_profiling_spec.rb
+++ b/spec/services/account_creation/device_profiling_spec.rb
@@ -5,6 +5,7 @@
let(:threatmetrix_proofer_result) do
instance_double(Proofing::DdpResult, success?: true, transaction_id: 'ddp-123')
end
+ let(:service_provider) { create(:service_provider) }
let(:threatmetrix_proofer) do
instance_double(
Proofing::LexisNexis::Ddp::Proofer,
@@ -24,6 +25,7 @@
request_ip: Faker::Internet.ip_v4_address,
threatmetrix_session_id: threatmetrix_session_id,
user_email: Faker::Internet.email,
+ uuid_prefix: service_provider.app_id,
)
end
diff --git a/spec/services/email_confirmation_token_validator_spec.rb b/spec/services/email_confirmation_token_validator_spec.rb
index 8be91518c75..d63e3a2d747 100644
--- a/spec/services/email_confirmation_token_validator_spec.rb
+++ b/spec/services/email_confirmation_token_validator_spec.rb
@@ -2,7 +2,7 @@
RSpec.describe EmailConfirmationTokenValidator do
describe '#submit' do
- subject { described_class.new(email_address, current_user) }
+ subject { described_class.new(email_address:, current_user:) }
context 'the email of the user does not match the user confirming' do
let(:current_user) { create(:user, :fully_registered) }
@@ -88,7 +88,7 @@
end
describe '#email_address_already_confirmed_by_user?' do
- subject { described_class.new(email_address) }
+ subject { described_class.new(email_address:) }
context 'the email address was confirmed by the user' do
let(:email_address) do
diff --git a/spec/services/proofing/lexis_nexis/ddp/verification_request_spec.rb b/spec/services/proofing/lexis_nexis/ddp/verification_request_spec.rb
index 199cef5e19a..4e541595f69 100644
--- a/spec/services/proofing/lexis_nexis/ddp/verification_request_spec.rb
+++ b/spec/services/proofing/lexis_nexis/ddp/verification_request_spec.rb
@@ -81,6 +81,25 @@
expected_json = JSON.parse(LexisNexisFixtures.ddp_authentication_request_json)
expect(response_json).to eq(expected_json)
end
+
+ context 'with service provider associated with user' do
+ let(:applicant) do
+ {
+ threatmetrix_session_id: 'UNIQUE_SESSION_ID',
+ email: 'test@example.com',
+ request_ip: '127.0.0.1',
+ uuid_prefix: 'SPNUM',
+ }
+ end
+
+ it 'returns a properly formed request body' do
+ response_json = JSON.parse(subject.body)
+
+ base_json = JSON.parse(LexisNexisFixtures.ddp_authentication_request_json)
+ expected_json = base_json.merge({ 'local_attrib_1' => 'SPNUM' })
+ expect(response_json).to eq(expected_json)
+ end
+ end
end
end
diff --git a/spec/services/usps_in_person_proofing/enrollment_helper_spec.rb b/spec/services/usps_in_person_proofing/enrollment_helper_spec.rb
index e0381815dd5..cb549770d51 100644
--- a/spec/services/usps_in_person_proofing/enrollment_helper_spec.rb
+++ b/spec/services/usps_in_person_proofing/enrollment_helper_spec.rb
@@ -2,6 +2,7 @@
RSpec.describe UspsInPersonProofing::EnrollmentHelper do
include UspsIppHelper
+ include UspsIppServiceHelper
let(:usps_mock_fallback) { false }
let(:user) { build(:user) }
@@ -43,7 +44,7 @@
end
describe '#schedule_in_person_enrollment' do
- context 'when the user does not have a establishing in person enrollment' do
+ context 'when the user does not have an establishing in person enrollment' do
let(:user) { double('user', establishing_in_person_enrollment: nil) }
it 'returns without error' do
@@ -332,9 +333,7 @@
describe '#create_usps_enrollment' do
let(:usps_mock_fallback) { true }
- let(:enrollment) { create(:in_person_enrollment, :with_service_provider) }
let(:usps_eipp_sponsor_id) { '314159265359' }
- let(:is_enhanced_ipp) { true }
let(:pii) do
Pii::Attributes.new_from_hash(
Idp::Constants::MOCK_IDV_APPLICANT,
@@ -369,6 +368,7 @@
profile: nil,
)
end
+ let(:is_enhanced_ipp) { true }
it 'creates an enhanced ipp enrollment' do
expect(proofer).to receive(:request_enroll).with(applicant, is_enhanced_ipp)
@@ -382,31 +382,4 @@
end
end
end
-
- def transliterated_without_change(value)
- UspsInPersonProofing::Transliterator::TransliterationResult.new(
- changed?: false,
- original: value,
- transliterated: value,
- unsupported_chars: [],
- )
- end
-
- def transliterated(value)
- UspsInPersonProofing::Transliterator::TransliterationResult.new(
- changed?: true,
- original: value,
- transliterated: "transliterated_#{value}",
- unsupported_chars: [],
- )
- end
-
- def transliterated_with_failure(value)
- UspsInPersonProofing::Transliterator::TransliterationResult.new(
- changed?: true,
- original: value,
- transliterated: "transliterated_failed_#{value}",
- unsupported_chars: [':'],
- )
- end
end
diff --git a/spec/services/usps_in_person_proofing/proofer_spec.rb b/spec/services/usps_in_person_proofing/proofer_spec.rb
index a31e7fe7d88..eae18260f75 100644
--- a/spec/services/usps_in_person_proofing/proofer_spec.rb
+++ b/spec/services/usps_in_person_proofing/proofer_spec.rb
@@ -1,19 +1,8 @@
require 'rails_helper'
-def expect_facility_fields_to_be_present(facility)
- expect(facility.address).to be_present
- expect(facility.city).to be_present
- expect(facility.name).to be_present
- expect(facility.saturday_hours).to be_present
- expect(facility.state).to be_present
- expect(facility.sunday_hours).to be_present
- expect(facility.weekday_hours).to be_present
- expect(facility.zip_code_4).to be_present
- expect(facility.zip_code_5).to be_present
-end
-
RSpec.describe UspsInPersonProofing::Proofer do
include UspsIppHelper
+ include UspsIppServiceHelper
let(:subject) { UspsInPersonProofing::Proofer.new }
let(:root_url) { 'http://my.root.url' }
@@ -297,8 +286,6 @@ def expect_facility_fields_to_be_present(facility)
end
let(:is_enhanced_ipp) { false }
let(:request_url) { "#{root_url}/ivs-ippaas-api/IPPRest/resources/rest/optInIPPApplicant" }
- let(:usps_ipp_sponsor_id) { '42' }
- let(:ipp_assurance_level) { '1.5' }
before do
stub_request_token
diff --git a/spec/support/analytics_helper.rb b/spec/support/analytics_helper.rb
index 90920f23576..b4da7cc1cdd 100644
--- a/spec/support/analytics_helper.rb
+++ b/spec/support/analytics_helper.rb
@@ -9,7 +9,7 @@ def stub_analytics(user: nil)
end
stub.to receive(:analytics).and_wrap_original do |original|
- expect(original.call.user).to eq(user) if user
+ expect(original.call.user).to match(user) if user
analytics
end
diff --git a/spec/support/fake_analytics.rb b/spec/support/fake_analytics.rb
index b67674988e2..6f43aaa06e7 100644
--- a/spec/support/fake_analytics.rb
+++ b/spec/support/fake_analytics.rb
@@ -171,6 +171,10 @@ def track_event(event, attributes = {})
def browser_attributes
{}
end
+
+ def reset!
+ @events = Hash.new
+ end
end
RSpec.configure do |c|
diff --git a/spec/support/features/doc_auth_helper.rb b/spec/support/features/doc_auth_helper.rb
index 264cee35488..bf374379bbd 100644
--- a/spec/support/features/doc_auth_helper.rb
+++ b/spec/support/features/doc_auth_helper.rb
@@ -9,7 +9,6 @@ module DocAuthHelper
GOOD_SSN = (Idp::Constants::MOCK_IDV_APPLICANT_WITH_SSN[:ssn]).freeze
GOOD_SSN_MASKED = '9**-**-***4'.freeze
- SAMPLE_TMX_SUMMARY_REASON_CODE = { tmx_summary_reason_code: ['Identity_Negative_History'] }.freeze
SSN_THAT_FAILS_RESOLUTION = '123-45-6666'.freeze
SSN_THAT_RAISES_EXCEPTION = '000-00-0000'.freeze
@@ -42,10 +41,6 @@ def click_send_link
click_on t('forms.buttons.send_link')
end
- def click_upload_from_computer
- click_on t('forms.buttons.upload_photos')
- end
-
def complete_doc_auth_steps_before_welcome_step(expect_accessible: false)
visit idv_welcome_url unless current_path == idv_welcome_url
click_idv_continue if current_path == idv_mail_only_warning_path
@@ -130,16 +125,6 @@ def complete_document_capture_step_with_yml(proofing_yml, expected_path: idv_ssn
expect(page).to have_current_path(expected_path, wait: 10)
end
- def complete_doc_auth_steps_before_phone_otp_step(expect_accessible: false, with_selfie: false)
- complete_doc_auth_steps_before_verify_step(
- expect_accessible: expect_accessible,
- with_selfie: with_selfie,
- )
- click_idv_continue
- expect_page_to_have_no_accessibility_violations(page) if expect_accessible
- click_idv_continue
- end
-
def mobile_device
Browser.new(mobile_user_agent)
end
@@ -221,16 +206,6 @@ def complete_proofing_steps(with_selfie: false)
click_agree_and_continue
end
- def mock_general_doc_auth_client_error(method)
- DocAuth::Mock::DocAuthMockClient.mock_response!(
- method: method,
- response: DocAuth::Response.new(
- success: false,
- errors: { error: I18n.t('doc_auth.errors.general.no_liveness') },
- ),
- )
- end
-
def mock_doc_auth_attention_with_barcode
attention_with_barcode_response = instance_double(
Faraday::Response,
@@ -246,121 +221,12 @@ def mock_doc_auth_attention_with_barcode
)
end
- def mock_doc_auth_success_face_match_fail
- failure_response = instance_double(
- Faraday::Response,
- status: 200,
- body: LexisNexisFixtures.true_id_response_with_face_match_fail,
- )
- DocAuth::Mock::DocAuthMockClient.mock_response!(
- method: :get_results,
- response: DocAuth::LexisNexis::Responses::TrueIdResponse.new(
- failure_response,
- DocAuth::LexisNexis::Config.new,
- true, # liveness_checking_enabled
- ),
- )
- end
-
- def mock_doc_auth_failure_face_match_pass
- failure_response = instance_double(
- Faraday::Response,
- status: 200,
- body: LexisNexisFixtures.true_id_response_failure_with_face_match_pass,
- )
- DocAuth::Mock::DocAuthMockClient.mock_response!(
- method: :get_results,
- response: DocAuth::LexisNexis::Responses::TrueIdResponse.new(
- failure_response,
- DocAuth::LexisNexis::Config.new,
- true, # liveness_checking_enabled
- ),
- )
- end
-
- def mock_doc_auth_fail_face_match_fail
- failure_response = instance_double(
- Faraday::Response,
- status: 200,
- body: LexisNexisFixtures.true_id_response_failure_with_face_match_fail,
- )
- DocAuth::Mock::DocAuthMockClient.mock_response!(
- method: :get_results,
- response: DocAuth::LexisNexis::Responses::TrueIdResponse.new(
- failure_response,
- DocAuth::LexisNexis::Config.new,
- true, # liveness_checking_enabled
- ),
- )
- end
-
- def mock_doc_auth_pass_and_portrait_match_not_live
- failure_response = instance_double(
- Faraday::Response,
- status: 200,
- body: LexisNexisFixtures.true_id_response_success_with_portrait_match_not_live,
- )
- DocAuth::Mock::DocAuthMockClient.mock_response!(
- method: :get_results,
- response: DocAuth::LexisNexis::Responses::TrueIdResponse.new(
- failure_response,
- DocAuth::LexisNexis::Config.new,
- true, # liveness_checking_enabled
- ),
- )
- end
-
- def mock_doc_auth_pass_face_match_pass_no_address1
- response = instance_double(
- Faraday::Response,
- status: 200,
- body: LexisNexisFixtures.true_id_response_success_with_liveness,
- )
- DocAuth::Mock::DocAuthMockClient.mock_response!(
- method: :get_results,
- response: DocAuth::LexisNexis::Responses::TrueIdResponse.new(
- response,
- DocAuth::LexisNexis::Config.new,
- true, # liveness_checking_enabled
- ),
- )
- end
-
- def mock_doc_auth_trueid_http_non2xx_status(status)
- network_error_response = instance_double(
- Faraday::Response,
- status: status,
- body: '{}',
- )
- DocAuth::Mock::DocAuthMockClient.mock_response!(
- method: :get_results,
- response: DocAuth::LexisNexis::Responses::TrueIdResponse.new(
- network_error_response,
- DocAuth::LexisNexis::Config.new,
- ),
- )
- end
-
def verify_phone_otp
choose_idv_otp_delivery_method_sms
fill_in_code_with_last_phone_otp
click_submit_default
end
- def fill_out_address_form_ok
- fill_in 'idv_form_address1', with: '123 Main St'
- fill_in 'idv_form_city', with: 'Nowhere'
- select 'Virginia', from: 'idv_form_state'
- fill_in 'idv_form_zipcode', with: '66044'
- end
-
- def fill_out_address_form_resolution_fail
- fill_in 'idv_form_address1', with: '123 Main St'
- fill_in 'idv_form_city', with: 'Nowhere'
- select 'Virginia', from: 'idv_form_state'
- fill_in 'idv_form_zipcode', with: '00000'
- end
-
def fill_out_address_form_fail
fill_in 'idv_form_address1', with: '123 Main St'
fill_in 'idv_form_city', with: 'Nowhere'
@@ -368,10 +234,6 @@ def fill_out_address_form_fail
fill_in 'idv_form_zipcode', with: '1'
end
- def fill_out_doc_auth_phone_form_ok(phone = '415-555-0199')
- fill_in :doc_auth_phone, with: phone
- end
-
def complete_all_idv_steps_with(threatmetrix:)
allow(IdentityConfig.store).to receive(:otp_delivery_blocklist_maxretry).and_return(300)
user = create(:user, :fully_registered)
diff --git a/spec/support/features/document_capture_step_helper.rb b/spec/support/features/document_capture_step_helper.rb
index f5b82332515..9143136c3fe 100644
--- a/spec/support/features/document_capture_step_helper.rb
+++ b/spec/support/features/document_capture_step_helper.rb
@@ -107,6 +107,10 @@ def stub_docv_verification_data_pass
stub_docv_verification_data(body: SocureDocvFixtures.pass_json)
end
+ def stub_docv_verification_data_fail_with(errors)
+ stub_docv_verification_data(body: SocureDocvFixtures.fail_json(errors))
+ end
+
def stub_docv_verification_data(body:)
stub_request(:post, "#{IdentityConfig.store.socure_idplus_base_url}/api/3.0/EmailAuthScore").
to_return(
diff --git a/spec/support/features/idv_step_helper.rb b/spec/support/features/idv_step_helper.rb
index 5833ef4d25d..c8765ed6100 100644
--- a/spec/support/features/idv_step_helper.rb
+++ b/spec/support/features/idv_step_helper.rb
@@ -64,10 +64,6 @@ def click_what_to_bring_link
click_link t('in_person_proofing.body.barcode.learn_more')
end
- def visit_sp_from_in_person_ready_to_verify
- click_sp_link_in_person_ready_to_verify
- end
-
def sp_friendly_name
'Test SP'
end
@@ -76,7 +72,7 @@ def link_text
t('in_person_proofing.body.barcode.return_to_partner_link', sp_name: sp_friendly_name)
end
- def click_sp_link_in_person_ready_to_verify
+ def visit_sp_from_in_person_ready_to_verify
expect(page).to have_content(link_text)
click_link(link_text)
end
@@ -107,14 +103,6 @@ def complete_idv_steps_with_gpo_before_confirmation_step(user = user_with_2fa)
click_continue
end
- def complete_idv_steps_before_confirmation_step(address_verification_mechanism = :phone)
- if address_verification_mechanism == :phone
- complete_idv_steps_with_phone_before_confirmation_step
- else
- complete_idv_steps_with_gpo_before_confirmation_step
- end
- end
-
def complete_idv_steps_before_step(step, user = user_with_2fa)
send(:"complete_idv_steps_before_#{step}_step", user)
end
@@ -146,14 +134,4 @@ def complete_idv_steps_before_ssn(user = user_with_2fa)
fill_out_state_id_form_ok(same_address_as_id: true)
click_idv_continue
end
-
- private
-
- def stub_idv_session(**session_attributes)
- allow(Idv::Session).to receive(:new).and_wrap_original do |original, kwargs|
- result = original.call(**kwargs)
- kwargs[:user_session][:idv].merge!(session_attributes)
- result
- end
- end
end
diff --git a/spec/support/features/in_person_helper.rb b/spec/support/features/in_person_helper.rb
index 41fbaef6b9c..81de665e188 100644
--- a/spec/support/features/in_person_helper.rb
+++ b/spec/support/features/in_person_helper.rb
@@ -47,7 +47,7 @@ def fill_out_state_id_form_ok(same_address_as_id: false, first_name: GOOD_FIRST_
fill_in t('in_person_proofing.form.state_id.address2'), with: GOOD_IDENTITY_DOC_ADDRESS2
fill_in t('in_person_proofing.form.state_id.city'), with: GOOD_IDENTITY_DOC_CITY
fill_in t('in_person_proofing.form.state_id.zipcode'), with: GOOD_IDENTITY_DOC_ZIPCODE
- select GOOD_STATE_ID_JURISDICTION,
+ select GOOD_STATE,
from: t('in_person_proofing.form.state_id.identity_doc_address_state')
if same_address_as_id
choose t('in_person_proofing.form.state_id.same_address_as_id_yes')
@@ -120,17 +120,6 @@ def complete_prepare_step(_user = nil)
click_on t('forms.buttons.continue')
end
- def complete_state_id_step(_user = nil, same_address_as_id: true, first_name: GOOD_FIRST_NAME)
- # Wait for page to load before attempting to fill out form
- expect(page).to have_current_path(idv_in_person_state_id_path, wait: 10)
- fill_out_state_id_form_ok(same_address_as_id: same_address_as_id, first_name:)
- click_idv_continue
- unless same_address_as_id
- expect(page).to have_current_path(idv_in_person_address_path, wait: 10)
- expect_in_person_step_indicator_current_step(t('step_indicator.flows.idv.verify_info'))
- end
- end
-
def complete_state_id_controller(_user = nil, same_address_as_id: true,
first_name: GOOD_FIRST_NAME)
# Wait for page to load before attempting to fill out form
@@ -158,14 +147,6 @@ def complete_verify_step(_user = nil)
click_idv_submit_default
end
- def complete_steps_before_state_id_step
- sign_in_and_2fa_user
- begin_in_person_proofing
- complete_prepare_step
- complete_location_step
- expect(page).to have_current_path(idv_in_person_state_id_path, wait: 10)
- end
-
def complete_steps_before_state_id_controller
sign_in_and_2fa_user
begin_in_person_proofing
@@ -205,11 +186,6 @@ def complete_entire_ipp_flow(user = user_with_2fa, tmx_status = nil, same_addres
end
def expect_in_person_step_indicator_current_step(text)
- expect_in_person_step_indicator
- expect_step_indicator_current_step(text)
- end
-
- def expect_in_person_step_indicator
# Normally we're only concerned with the "current" step, but since some steps are shared between
# flows, we also want to make sure that at least one of the in-person-specific steps exists in
# the step indicator.
@@ -217,20 +193,29 @@ def expect_in_person_step_indicator
'.step-indicator__step',
text: t('step_indicator.flows.idv.find_a_post_office'),
)
+ expect_step_indicator_current_step(text)
end
- def make_pii(same_address_as_id: 'true')
+ def build_pii_before_state_id_update(same_address_as_id: 'true')
pii_from_user[:same_address_as_id] = same_address_as_id
pii_from_user[:identity_doc_address1] = identity_doc_address1
pii_from_user[:identity_doc_address2] = identity_doc_address2
pii_from_user[:identity_doc_city] = identity_doc_city
pii_from_user[:identity_doc_address_state] = identity_doc_address_state
pii_from_user[:identity_doc_zipcode] = identity_doc_zipcode
- pii_from_user[:address1] = address1
- pii_from_user[:address2] = address2
- pii_from_user[:city] = city
- pii_from_user[:state] = state
- pii_from_user[:zipcode] = zipcode
+ if same_address_as_id == 'true'
+ pii_from_user[:address1] = identity_doc_address1
+ pii_from_user[:address2] = identity_doc_address2
+ pii_from_user[:city] = identity_doc_city
+ pii_from_user[:state] = identity_doc_address_state
+ pii_from_user[:zipcode] = identity_doc_zipcode
+ else
+ pii_from_user[:address1] = address1
+ pii_from_user[:address2] = address2
+ pii_from_user[:city] = city
+ pii_from_user[:state] = state
+ pii_from_user[:zipcode] = zipcode
+ end
end
def mark_in_person_enrollment_passed(user)
diff --git a/spec/support/features/verify_step_helper.rb b/spec/support/features/verify_step_helper.rb
deleted file mode 100644
index 2f4c7eaf831..00000000000
--- a/spec/support/features/verify_step_helper.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-module VerifyStepHelper
- include InPersonHelper
-
- def expect_good_state_id_address
- expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS1)
- expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS2)
- expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_CITY)
- expect(page).to have_text(Idp::Constants::MOCK_IDV_APPLICANT[:state_id_jurisdiction])
- expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ZIPCODE)
- end
-
- def expect_good_address
- expect(page).to have_text(InPersonHelper::GOOD_ADDRESS1)
- expect(page).to have_content(t('idv.form.address2'))
- expect(page).to have_text(InPersonHelper::GOOD_CITY)
- expect(page).to have_text(Idp::Constants::MOCK_IDV_APPLICANT[:state])
- expect(page).to have_text(InPersonHelper::GOOD_ZIPCODE)
- end
-end
diff --git a/spec/support/geocoder_stubs.rb b/spec/support/geocoder_stubs.rb
deleted file mode 100644
index 4a5a58728dc..00000000000
--- a/spec/support/geocoder_stubs.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-Geocoder.configure(ip_lookup: :test)
-
-Geocoder::Lookup::Test.add_stub(
- '1.2.3.4', [
- {
- 'city' => 'foo',
- 'country' => 'United States',
- 'state_code' => '',
- },
- ]
-)
-
-Geocoder::Lookup::Test.add_stub(
- '159.142.31.80', [
- {
- 'city' => 'Arlington',
- 'country' => 'United States',
- 'state_code' => 'VA',
- },
- ]
-)
-
-Geocoder::Lookup::Test.add_stub(
- '4.3.2.1', [
- {
- 'city' => '',
- 'country' => '',
- 'state_code' => '',
- },
- ]
-)
-
-Geocoder::Lookup::Test.add_stub(
- '127.0.0.1', [
- {
- 'city' => '',
- 'country' => 'United States',
- 'state_code' => '',
- },
- ]
-)
diff --git a/spec/support/shared_examples/sign_in.rb b/spec/support/shared_examples/sign_in.rb
index bbe09be0a3e..8136ec3cbc2 100644
--- a/spec/support/shared_examples/sign_in.rb
+++ b/spec/support/shared_examples/sign_in.rb
@@ -324,16 +324,34 @@ def user_with_broken_personal_key(scenario)
fake_analytics = FakeAnalytics.new
allow_any_instance_of(ApplicationController).to receive(:analytics).
and_wrap_original do |original|
- original_analytics = original.call
- if original_analytics.request.params[:controller] == 'users/sessions' &&
- original_analytics.request.params[:action] == 'create'
- expect(original_analytics.user).to eq(user)
+ if original.receiver.instance_of?(Users::SessionsController) &&
+ original.receiver.action_name == 'create'
+ expect(original.call.user).to eq(user)
asserted_expected_user = true
end
fake_analytics
end
+ fill_in_credentials_and_submit(user.email, 'wrongpassword')
+ expect(asserted_expected_user).to eq(true)
+ expect(fake_analytics).to have_logged_event(
+ 'reCAPTCHA verify result received',
+ recaptcha_result: {
+ assessment_id: kind_of(String),
+ success: true,
+ score: 1.0,
+ errors: [],
+ reasons: [],
+ },
+ evaluated_as_valid: true,
+ score_threshold: 0.2,
+ recaptcha_action: 'sign_in',
+ form_class: 'RecaptchaMockForm',
+ )
+ asserted_expected_user = false
+ fake_analytics.reset!
+
fill_in :user_recaptcha_mock_score, with: '0.1'
fill_in_credentials_and_submit(user.email, user.password)
expect(asserted_expected_user).to eq(true)
diff --git a/spec/support/socure_docv_fixtures.rb b/spec/support/socure_docv_fixtures.rb
index a0e6ab756c4..1a4bb4e256a 100644
--- a/spec/support/socure_docv_fixtures.rb
+++ b/spec/support/socure_docv_fixtures.rb
@@ -7,6 +7,16 @@ def pass_json
JSON.parse(raw).to_json
end
+ def fail_json(errors)
+ raw = read_fixture_file_at_path('pass.json')
+ body = JSON.parse(raw)
+
+ body['documentVerification']['decision']['value'] = 'reject'
+ body['documentVerification']['reasonCodes'] = errors
+
+ body.to_json
+ end
+
private
def read_fixture_file_at_path(filepath)
diff --git a/spec/support/usps_ipp_service_helper.rb b/spec/support/usps_ipp_service_helper.rb
new file mode 100644
index 00000000000..0b5c9cdd7a8
--- /dev/null
+++ b/spec/support/usps_ipp_service_helper.rb
@@ -0,0 +1,40 @@
+module UspsIppServiceHelper
+ def expect_facility_fields_to_be_present(facility)
+ expect(facility.address).to be_present
+ expect(facility.city).to be_present
+ expect(facility.name).to be_present
+ expect(facility.saturday_hours).to be_present
+ expect(facility.state).to be_present
+ expect(facility.sunday_hours).to be_present
+ expect(facility.weekday_hours).to be_present
+ expect(facility.zip_code_4).to be_present
+ expect(facility.zip_code_5).to be_present
+ end
+
+ def transliterated_without_change(value)
+ UspsInPersonProofing::Transliterator::TransliterationResult.new(
+ changed?: false,
+ original: value,
+ transliterated: value,
+ unsupported_chars: [],
+ )
+ end
+
+ def transliterated(value)
+ UspsInPersonProofing::Transliterator::TransliterationResult.new(
+ changed?: true,
+ original: value,
+ transliterated: "transliterated_#{value}",
+ unsupported_chars: [],
+ )
+ end
+
+ def transliterated_with_failure(value)
+ UspsInPersonProofing::Transliterator::TransliterationResult.new(
+ changed?: true,
+ original: value,
+ transliterated: "transliterated_failed_#{value}",
+ unsupported_chars: [':'],
+ )
+ end
+end
diff --git a/spec/views/idv/shared/_error.html.erb_spec.rb b/spec/views/idv/shared/_error.html.erb_spec.rb
index 20a0fdf5a10..89cb5c1452f 100644
--- a/spec/views/idv/shared/_error.html.erb_spec.rb
+++ b/spec/views/idv/shared/_error.html.erb_spec.rb
@@ -5,7 +5,7 @@
let(:options) { [{ text: 'Example', url: '#example' }] }
let(:heading) { 'Error' }
let(:action) { nil }
- let(:action_secondary) { nil }
+ let(:secondary_action) { nil }
let(:type) { nil }
let(:current_step) { nil }
let(:step_indicator_steps) { nil }
@@ -14,7 +14,7 @@
type: type,
heading: heading,
action: action,
- action_secondary: action_secondary,
+ secondary_action: secondary_action,
current_step: current_step,
options: options,
}
@@ -73,7 +73,7 @@
end
context 'with secondary action' do
- let(:action_secondary) { { text: 'Secondary Action', url: '#secondary' } }
+ let(:secondary_action) { { text: 'Secondary Action', url: '#secondary' } }
it 'renders secondary action button' do
expect(rendered).to have_link('Secondary Action', href: '#secondary')
@@ -81,7 +81,7 @@
end
context 'with form action' do
- let(:action_secondary) { { text: 'Delete', url: '#delete', method: :delete } }
+ let(:secondary_action) { { text: 'Delete', url: '#delete', method: :delete } }
it 'renders action button' do
expect(rendered).to have_button('Delete')