diff --git a/app/javascript/packages/document-capture/components/document-capture-review-issues.tsx b/app/javascript/packages/document-capture/components/document-capture-review-issues.tsx index a4dc756d626..af3547394dc 100644 --- a/app/javascript/packages/document-capture/components/document-capture-review-issues.tsx +++ b/app/javascript/packages/document-capture/components/document-capture-review-issues.tsx @@ -2,7 +2,7 @@ import { useContext } from 'react'; import { PageHeading } from '@18f/identity-components'; import { FormStepsButton } from '@18f/identity-form-steps'; import { Cancel } from '@18f/identity-verify-flow'; -import { useI18n } from '@18f/identity-react-i18n'; +import { useI18n, HtmlTextWithStrongNoWrap } from '@18f/identity-react-i18n'; import type { FormStepComponentProps } from '@18f/identity-form-steps'; import UnknownError from './unknown-error'; import TipList from './tip-list'; @@ -55,7 +55,6 @@ function DocumentCaptureReviewIssues({ )} +

+ +

{!isFailedDocType && captureHints && ( {t('errors.doc_auth.rate_limited_subheading')}; @@ -101,9 +89,6 @@ function DocumentCaptureWarning({ : t('idv.failure.button.try_online'); const subheading = getSubheading({ nonIppOrFailedResult, - isFailedDocType, - isFailedSelfieLivenessOrQuality, - isFailedSelfie, t, }); const subheadingRef = useRef(null); @@ -142,24 +127,17 @@ function DocumentCaptureWarning({
- - {!isFailedDocType && - !isFailedSelfie && - !isFailedSelfieLivenessOrQuality && - remainingSubmitAttempts <= DISPLAY_ATTEMPTS && ( -

- -

- )} +

+ +

{nonIppOrFailedResult && } diff --git a/app/javascript/packages/document-capture/components/unknown-error.tsx b/app/javascript/packages/document-capture/components/unknown-error.tsx index b3063cc612e..e48209de273 100644 --- a/app/javascript/packages/document-capture/components/unknown-error.tsx +++ b/app/javascript/packages/document-capture/components/unknown-error.tsx @@ -1,6 +1,6 @@ import type { ComponentProps } from 'react'; import { useContext } from 'react'; -import { useI18n, HtmlTextWithStrongNoWrap } from '@18f/identity-react-i18n'; +import { useI18n } from '@18f/identity-react-i18n'; import { FormStepError } from '@18f/identity-form-steps'; import { Link } from '@18f/identity-components'; import formatHTML from '@18f/identity-react-i18n/format-html'; @@ -11,7 +11,6 @@ interface UnknownErrorProps extends ComponentProps<'p'> { isFailedDocType: boolean; isFailedSelfie: boolean; isFailedSelfieLivenessOrQuality: boolean; - remainingSubmitAttempts: number; altFailedDocTypeMsg?: string | null; altIsFailedSelfieDontIncludeAttempts?: boolean; hasDismissed: boolean; @@ -46,7 +45,6 @@ function UnknownError({ isFailedDocType = false, isFailedSelfie = false, isFailedSelfieLivenessOrQuality = false, - remainingSubmitAttempts, altFailedDocTypeMsg = null, altIsFailedSelfieDontIncludeAttempts = false, hasDismissed, @@ -73,14 +71,7 @@ function UnknownError({ ); } if (isFailedDocType && err) { - return ( -

- {err.message}{' '} - -

- ); + return

{err.message}

; } if ((isFailedSelfieLivenessOrQuality || isFailedSelfie) && err) { let selfieHelpCenterLinkText = t('doc_auth.errors.general.selfie_failure_help_link_text'); @@ -90,23 +81,14 @@ function UnknownError({ selfieHelpCenterLinkText = t('doc_auth.errors.alerts.selfie_not_live_help_link_text'); } return ( - <> -

- {err.message}{' '} - {altIsFailedSelfieDontIncludeAttempts && ( - - {selfieHelpCenterLinkText} - - )} -

-

- {!altIsFailedSelfieDontIncludeAttempts && ( - - )} -

- +

+ {err.message}{' '} + {altIsFailedSelfieDontIncludeAttempts && ( + + {selfieHelpCenterLinkText} + + )} +

); } if (err && !hasDismissed) { diff --git a/app/services/doc_auth/error_generator.rb b/app/services/doc_auth/error_generator.rb index 491458d4cb4..2fb20cb29c1 100644 --- a/app/services/doc_auth/error_generator.rb +++ b/app/services/doc_auth/error_generator.rb @@ -339,8 +339,8 @@ def generate_doc_auth_errors(response_info) unknown_error_handler.handle(response_info).to_h end - def self.general_error(liveness_enabled) - liveness_enabled ? Errors::GENERAL_ERROR_LIVENESS : Errors::GENERAL_ERROR + def self.general_error(_liveness_enabled) + Errors::GENERAL_ERROR end def self.wrapped_general_error(liveness_enabled) diff --git a/app/services/doc_auth/errors.rb b/app/services/doc_auth/errors.rb index d080d74b87a..4806f3e3d16 100644 --- a/app/services/doc_auth/errors.rb +++ b/app/services/doc_auth/errors.rb @@ -22,7 +22,6 @@ module Errors EXPIRATION_CHECKS = 'expiration_checks' # expiration date valid, expiration crosscheck FULL_NAME_CHECK = 'full_name_check' GENERAL_ERROR = 'general_error' - GENERAL_ERROR_LIVENESS = 'general_error_liveness' ID_NOT_RECOGNIZED = 'id_not_recognized' ID_NOT_VERIFIED = 'id_not_verified' ISSUE_DATE_CHECKS = 'issue_date_checks' @@ -65,7 +64,6 @@ module Errors EXPIRATION_CHECKS, FULL_NAME_CHECK, GENERAL_ERROR, - GENERAL_ERROR_LIVENESS, ID_NOT_RECOGNIZED, ID_NOT_VERIFIED, ISSUE_DATE_CHECKS, @@ -121,7 +119,6 @@ module Errors MULTIPLE_BACK_ID_FAILURES => { long_msg: MULTIPLE_BACK_ID_FAILURES, field_msg: FALLBACK_FIELD_LEVEL, hints: true }, GENERAL_ERROR => { long_msg: GENERAL_ERROR, field_msg: FALLBACK_FIELD_LEVEL, hints: true }, # Selfie errors - GENERAL_ERROR_LIVENESS => { long_msg: GENERAL_ERROR_LIVENESS, field_msg: FALLBACK_FIELD_LEVEL, hints: false }, SELFIE_FAILURE => { long_msg: SELFIE_FAILURE, field_msg: SELFIE_FAILURE, hints: false }, SELFIE_NOT_LIVE_OR_POOR_QUALITY => { long_msg: SELFIE_NOT_LIVE_OR_POOR_QUALITY, field_msg: SELFIE_FAILURE, hints: false }, }.transform_values(&:freeze).freeze diff --git a/app/services/doc_auth_router.rb b/app/services/doc_auth_router.rb index 565a5342d1f..6d91ac9fa57 100644 --- a/app/services/doc_auth_router.rb +++ b/app/services/doc_auth_router.rb @@ -38,9 +38,6 @@ module DocAuthRouter # i18n-tasks-use t('doc_auth.errors.general.no_liveness') DocAuth::Errors::GENERAL_ERROR => 'doc_auth.errors.general.no_liveness', - # i18n-tasks-use t('doc_auth.errors.dpi.top_msg_plural') - DocAuth::Errors::GENERAL_ERROR_LIVENESS => - 'doc_auth.errors.dpi.top_msg_plural', # i18n-tasks-use t('doc_auth.errors.alerts.id_not_recognized') DocAuth::Errors::ID_NOT_RECOGNIZED => 'doc_auth.errors.alerts.id_not_recognized', diff --git a/app/views/idv/session_errors/warning.html.erb b/app/views/idv/session_errors/warning.html.erb index 389c0371f4b..713e3845dfd 100644 --- a/app/views/idv/session_errors/warning.html.erb +++ b/app/views/idv/session_errors/warning.html.erb @@ -13,7 +13,7 @@ <% c.with_header { t('idv.warning.sessions.heading') } %>

<%= t('idv.failure.sessions.warning') %>

-

<%= t('idv.warning.attempts_html', count: @remaining_submit_attempts) %>

+

<%= t('idv.failure.attempts_html', count: @remaining_submit_attempts) %>

<% c.with_action_button( url: @try_again_path, diff --git a/config/locales/en.yml b/config/locales/en.yml index 138918bf970..4896863606a 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1134,8 +1134,6 @@ idv.unavailable.idv_explanation.without_sp: The agency that you are trying to ac idv.unavailable.next_steps_html: '%{status_page_link_html} or exit %{app_name} and try again later.' idv.unavailable.status_page_link: Get updates on our status page idv.unavailable.technical_difficulties: Unfortunately, we are having technical difficulties and cannot verify your identity at this time. -idv.warning.attempts_html.one: For security reasons, you have one attempt remaining. -idv.warning.attempts_html.other: For security reasons, you have %{count} attempts remaining. idv.warning.sessions.heading: We couldn’t find records matching your personal information idv.warning.state_id.cancel_button: Exit %{app_name} idv.warning.state_id.explanation: Unfortunately, we’re experiencing technical difficulties with IDs from your state and are currently unable to verify your information. diff --git a/config/locales/es.yml b/config/locales/es.yml index ebef38ec735..52ecfa67d3f 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -1133,8 +1133,6 @@ idv.unavailable.idv_explanation.without_sp: La agencia a la que está intentando idv.unavailable.next_steps_html: '%{status_page_link_html} o salga de %{app_name} y vuelva a intentarlo más tarde.' idv.unavailable.status_page_link: Obtenga las actualizaciones en nuestra página de estado idv.unavailable.technical_difficulties: Lamentablemente, tenemos problemas técnicos y no podemos verificar su identidad en este momento. -idv.warning.attempts_html.one: Por motivos de seguridad, le queda un intento. -idv.warning.attempts_html.other: Por motivos de seguridad, le quedan %{count} intentos. idv.warning.sessions.heading: No encontramos registros que coincidan con sus datos personales idv.warning.state_id.cancel_button: Salir de %{app_name} idv.warning.state_id.explanation: Lamentablemente, tenemos problemas técnicos con las identificaciones de su estado y no podemos verificar su información en este momento. diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 623d56008bd..e616383b8b2 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -1134,8 +1134,6 @@ idv.unavailable.idv_explanation.without_sp: L’organisme auquel vous essayez d idv.unavailable.next_steps_html: '%{status_page_link_html} ou quittez le site %{app_name} et réessayez plus tard.' idv.unavailable.status_page_link: Obtenir les dernières informations sur notre page d’état des systèmes. idv.unavailable.technical_difficulties: Malheureusement, nous rencontrons des difficultés techniques et ne pouvons pas vérifier votre identité pour le moment. -idv.warning.attempts_html.one: Pour des raisons de sécurité, il vous reste une tentative. -idv.warning.attempts_html.other: Pour des raisons de sécurité, il vous reste %{count} tentatives. idv.warning.sessions.heading: Nous n’avons pas trouvé de dossiers correspondant à vos informations personnelles idv.warning.state_id.cancel_button: Quitter %{app_name} idv.warning.state_id.explanation: Malheureusement, nous rencontrons des difficultés techniques avec les pièces d’identité de votre État et ne sommes pas en mesure de vérifier vos informations pour le moment. diff --git a/config/locales/zh.yml b/config/locales/zh.yml index cd347f15d7c..979f994230b 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -1138,8 +1138,6 @@ idv.unavailable.idv_explanation.without_sp: 你试图访问的机构需要确保 idv.unavailable.next_steps_html: '%{status_page_link_html} 或者退出 %{app_name},稍后再试。' idv.unavailable.status_page_link: 在我们的状态页面获得最新信息。 idv.unavailable.technical_difficulties: 很遗憾,我们这边现在遇到技术困难,目前无法验证你的身份。 -idv.warning.attempts_html.one: 出于安全考虑,你只能再试一次了。 -idv.warning.attempts_html.other: 出于安全考虑,你只能再试 %{count} 次 了 。 idv.warning.sessions.heading: 我们找不到与你个人信息匹配的记录 idv.warning.state_id.cancel_button: 退出 %{app_name} idv.warning.state_id.explanation: 遗憾的是,处理来自你所在州的身份证件时我们遇到技术困难,目前无法验证你的信息。 diff --git a/spec/features/idv/doc_auth/document_capture_spec.rb b/spec/features/idv/doc_auth/document_capture_spec.rb index 540ef517de2..efb2f6e2ef5 100644 --- a/spec/features/idv/doc_auth/document_capture_spec.rb +++ b/spec/features/idv/doc_auth/document_capture_spec.rb @@ -10,9 +10,6 @@ before(:each) do allow_any_instance_of(ApplicationController).to receive(:analytics).and_return(@fake_analytics) allow_any_instance_of(ServiceProviderSession).to receive(:sp_name).and_return(@sp_name) - - visit_idp_from_oidc_sp_with_ial2 - sign_in_and_2fa_user(@user) end before(:all) do @@ -29,6 +26,8 @@ context 'standard desktop flow' do before do + visit_idp_from_oidc_sp_with_ial2 + sign_in_and_2fa_user(@user) complete_doc_auth_steps_before_document_capture_step end @@ -46,11 +45,12 @@ detail_message = strip_tags(t('doc_auth.errors.doc.doc_type_check')) security_message = strip_tags( t( - 'idv.warning.attempts_html', + 'idv.failure.attempts_html', count: IdentityConfig.store.doc_auth_max_attempts - 1, ), ) - expect(page).to have_content(detail_message << ' ' << security_message) + expect(page).to have_content(detail_message) + expect(page).to have_content(security_message) expect(page).to have_current_path(idv_document_capture_path) click_try_again expect(page).to have_current_path(idv_document_capture_path) @@ -285,370 +285,640 @@ end context 'selfie with error is uploaded' do - before do - allow(IdentityConfig.store).to receive(:doc_auth_max_attempts).and_return(99) - perform_in_browser(:mobile) do - visit_idp_from_oidc_sp_with_ial2(biometric_comparison_required: true) - sign_in_and_2fa_user(@user) - complete_doc_auth_steps_before_document_capture_step + context 'IPP enabled' do + let(:ipp_service_provider) do + create(:service_provider, :active, :in_person_proofing_enabled) end - end - - it 'shows the correct error message for the given error' do - # when the only error is a doc auth error - - perform_in_browser(:mobile) do - attach_images( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_doc_auth_fail_selfie_pass.yml' - ), - ) - attach_selfie( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_doc_auth_fail_selfie_pass.yml' - ), - ) - - submit_images - - h1_error_message = strip_tags(t('errors.doc_auth.rate_limited_heading')) - expect(page).to have_content(h1_error_message) - - body_error_message = strip_tags(t('doc_auth.errors.dpi.top_msg')) - expect(page).to have_content(body_error_message) - - click_try_again - expect(page).to have_current_path(idv_document_capture_path) - - inline_error_message = strip_tags(t('doc_auth.errors.dpi.failed_short')) - expect(page).to have_content(inline_error_message) - - expect(page).to have_current_path(idv_document_capture_url) - - # when doc auth result passes but liveness fails - - attach_images( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_no_liveness.yml' - ), - ) - attach_selfie( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_no_liveness.yml' - ), - ) - - submit_images - - h1_error_message = strip_tags( - t('errors.doc_auth.selfie_not_live_or_poor_quality_heading'), - ) - expect(page).to have_content(h1_error_message) - - body_error_message = strip_tags( - t('doc_auth.errors.alerts.selfie_not_live_or_poor_quality'), - ) - expect(page).to have_content(body_error_message) - - click_try_again - - selfie_inline_error_message = strip_tags(t('doc_auth.errors.general.selfie_failure')) - expect(page).to have_content(selfie_inline_error_message) - - expect(page).to have_current_path(idv_document_capture_path) - - # inline error to be fixed in lg-12999 - - # when there are both doc auth errors and liveness errors - - attach_images( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_doc_auth_fail_and_no_liveness.yml' - ), - ) - attach_selfie( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_doc_auth_fail_and_no_liveness.yml' - ), - ) - - submit_images - - h1_error_message = strip_tags(t('errors.doc_auth.rate_limited_heading')) - expect(page).to have_content(h1_error_message) - - body_error_message = strip_tags(t('doc_auth.errors.dpi.top_msg')) - expect(page).to have_content(body_error_message) - - click_try_again - expect(page).to have_current_path(idv_document_capture_path) - - inline_error_message = strip_tags(t('doc_auth.errors.dpi.failed_short')) - expect(page).to have_content(inline_error_message) - selfie_inline_error_message = strip_tags(t('doc_auth.errors.general.selfie_failure')) - expect(page).not_to have_content(selfie_inline_error_message) - - # when there are both doc auth errors and face match errors - - attach_images( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_doc_auth_fail_face_match_fail.yml' - ), - ) - attach_selfie( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_doc_auth_fail_face_match_fail.yml' - ), - ) - - submit_images - - h1_error_message = strip_tags(t('errors.doc_auth.rate_limited_heading')) - expect(page).to have_content(h1_error_message) - - body_error_message = strip_tags(t('doc_auth.errors.dpi.top_msg')) - expect(page).to have_content(body_error_message) - - click_try_again - expect(page).to have_current_path(idv_document_capture_path) - - inline_error_message = strip_tags(t('doc_auth.errors.dpi.failed_short')) - expect(page).to have_content(inline_error_message) - selfie_inline_error_message = strip_tags(t('doc_auth.errors.general.selfie_failure')) - expect(page).not_to have_content(selfie_inline_error_message) - - # when doc auth result and liveness pass but face match fails - - attach_images( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_portrait_match_failure.yml' - ), - ) - attach_selfie( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_portrait_match_failure.yml' - ), - ) - - submit_images - - h1_error_message = strip_tags(t('errors.doc_auth.selfie_fail_heading')) - expect(page).to have_content(h1_error_message) - - body_error_message = strip_tags(t('doc_auth.errors.general.selfie_failure')) - expect(page).to have_content(body_error_message) - - click_try_again - expect(page).to have_current_path(idv_document_capture_path) - - inline_error_message = strip_tags( - t('doc_auth.errors.general.multiple_front_id_failures'), - ) - expect(page).to have_content(inline_error_message) - selfie_inline_error_message = strip_tags(t('doc_auth.errors.general.selfie_failure')) - expect(page).to have_content(selfie_inline_error_message) - - # when there is a doc auth error on one side of the ID and face match errors - - attach_images( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_back_fail_doc_auth_face_match_errors.yml' - ), - ) - attach_selfie( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_back_fail_doc_auth_face_match_errors.yml' - ), - ) - - submit_images - - h1_error_message = strip_tags(t('errors.doc_auth.rate_limited_heading')) - expect(page).to have_content(h1_error_message) - - body_error_message = strip_tags(t('doc_auth.errors.alerts.barcode_content_check')) - expect(page).to have_content(body_error_message) - - click_try_again - expect(page).to have_current_path(idv_document_capture_path) - - inline_error_message = strip_tags(t('doc_auth.errors.general.fallback_field_level')) - expect(page).to have_content(inline_error_message) - selfie_inline_error_message = strip_tags(t('doc_auth.errors.general.selfie_failure')) - expect(page).not_to have_content(selfie_inline_error_message) - - # when there is a doc auth error on one side of the ID and a liveness error - - attach_images( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_back_fail_doc_auth_liveness_errors.yml' - ), - ) - attach_selfie( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_back_fail_doc_auth_liveness_errors.yml' - ), - ) - - submit_images - - h1_error_message = strip_tags(t('errors.doc_auth.rate_limited_heading')) - expect(page).to have_content(h1_error_message) - - body_error_message = strip_tags(t('doc_auth.errors.alerts.barcode_content_check')) - expect(page).to have_content(body_error_message) - - click_try_again - expect(page).to have_current_path(idv_document_capture_path) - - inline_error_message = strip_tags(t('doc_auth.errors.general.fallback_field_level')) - expect(page).to have_content(inline_error_message) - selfie_inline_error_message = strip_tags(t('doc_auth.errors.general.selfie_failure')) - expect(page).not_to have_content(selfie_inline_error_message) - # when doc auth result is "attention" and face match errors - - attach_images( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_doc_auth_attention_face_match_fail.yml' - ), - ) - attach_selfie( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_doc_auth_attention_face_match_fail.yml' - ), - ) - - submit_images - - h1_error_message = strip_tags(t('errors.doc_auth.rate_limited_heading')) - expect(page).to have_content(h1_error_message) - - body_error_message = strip_tags(t('doc_auth.errors.dpi.top_msg_plural')) - expect(page).to have_content(body_error_message) - - click_try_again - expect(page).to have_current_path(idv_document_capture_path) - - inline_error_message = strip_tags(t('doc_auth.errors.general.fallback_field_level')) - expect(page).to have_content(inline_error_message) - selfie_inline_error_message = strip_tags(t('doc_auth.errors.general.selfie_failure')) - expect(page).not_to have_content(selfie_inline_error_message) - - # when doc auth passes but there are both liveness errors and face match errors - - attach_images( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_liveness_fail_face_match_fail.yml' - ), - ) - attach_selfie( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_liveness_fail_face_match_fail.yml' - ), - ) - - submit_images - - h1_error_message = strip_tags( - t('errors.doc_auth.selfie_not_live_or_poor_quality_heading'), - ) - expect(page).to have_content(h1_error_message) - - body_error_message = strip_tags( - t('doc_auth.errors.alerts.selfie_not_live_or_poor_quality'), - ) - expect(page).to have_content(body_error_message) - - click_try_again - expect(page).to have_current_path(idv_document_capture_path) - - selfie_inline_error_message = strip_tags(t('doc_auth.errors.general.selfie_failure')) - expect(page).to have_content(selfie_inline_error_message) - - # when doc auth, liveness, and face match pass but PII validation fails - - attach_images( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_doc_auth_selfie_pass_pii_fail.yml' - ), - ) - attach_selfie( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_doc_auth_selfie_pass_pii_fail.yml' - ), - ) - - submit_images - - h1_error_message = strip_tags(t('errors.doc_auth.rate_limited_heading')) - expect(page).to have_content(h1_error_message) - - body_error_message = strip_tags(t('doc_auth.errors.alerts.address_check')) - expect(page).to have_content(body_error_message) - - click_try_again - expect(page).to have_current_path(idv_document_capture_path) - - inline_error_message = strip_tags( - t('doc_auth.errors.general.multiple_front_id_failures'), - ) - expect(page).to have_content(inline_error_message) - selfie_inline_error_message = strip_tags(t('doc_auth.errors.general.selfie_failure')) - expect(page).not_to have_content(selfie_inline_error_message) - - # when there are both face match errors and pii errors - - attach_images( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_face_match_fail_and_pii_fail.yml' - ), - ) - attach_selfie( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_face_match_fail_and_pii_fail.yml' - ), - ) - - submit_images - - h1_error_message = strip_tags(t('errors.doc_auth.selfie_fail_heading')) - expect(page).to have_content(h1_error_message) + before do + allow(IdentityConfig.store).to receive( + :doc_auth_selfie_capture_enabled, + ).and_return(true) + allow(IdentityConfig.store).to receive(:in_person_proofing_enabled).and_return(true) + allow(IdentityConfig.store).to receive( + :in_person_proofing_opt_in_enabled, + ).and_return(true) + allow_any_instance_of(ServiceProvider).to receive( + :in_person_proofing_enabled, + ).and_return(true) + allow(IdentityConfig.store).to receive(:doc_auth_max_attempts).and_return(99) + perform_in_browser(:mobile) do + visit_idp_from_sp_with_ial2( + :oidc, + **{ client_id: ipp_service_provider.issuer, + biometric_comparison_required: true }, + ) + sign_in_and_2fa_user(@user) + complete_up_to_how_to_verify_step_for_opt_in_ipp( + biometric_comparison_required: true, + ) + complete_verify_step + end + end - body_error_message = strip_tags(t('doc_auth.errors.general.selfie_failure')) - expect(page).to have_content(body_error_message) + it 'shows the correct error message for the given error' do + # when there are multiple doc auth errors on front and back + + perform_in_browser(:mobile) do + attach_images( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_multiple_doc_auth_failures_both_sides.yml' + ), + ) + attach_selfie( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_multiple_doc_auth_failures_both_sides.yml' + ), + ) + + submit_images + + review_issues_h1_heading = strip_tags(t('errors.doc_auth.rate_limited_heading')) + expect(page).to have_content(review_issues_h1_heading) + + review_issues_subheading = strip_tags(t('errors.doc_auth.rate_limited_subheading')) + expect(page).not_to have_selector('h2', text: review_issues_subheading) + + review_issues_body_message = strip_tags(t('doc_auth.errors.general.no_liveness')) + expect(page).to have_content(review_issues_body_message) + + review_issues_rate_limit_warning = strip_tags( + t( + 'idv.failure.attempts_html', + count: max_attempts - 1, + ), + ) + expect(page).to have_content(review_issues_rate_limit_warning) + + click_try_again + expect(page).to have_current_path(idv_document_capture_path) + + resubmit_page_body_copy = strip_tags(t('doc_auth.errors.general.no_liveness')) + expect(page).to have_content(resubmit_page_body_copy) + + resubmit_page_inline_error_messages = strip_tags( + t('doc_auth.errors.general.fallback_field_level'), + ) + expect(page).to have_content(resubmit_page_inline_error_messages).twice + + resubmit_page_inline_selfie_error_message = strip_tags( + t('doc_auth.errors.general.selfie_failure'), + ) + expect(page).not_to have_content(resubmit_page_inline_selfie_error_message) + + # when there are multiple front doc auth errors + + attach_images( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_multiple_doc_auth_failures_front_side_only.yml' + ), + ) + attach_selfie( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_multiple_doc_auth_failures_front_side_only.yml' + ), + ) + + submit_images + + review_issues_h1_heading = strip_tags(t('errors.doc_auth.rate_limited_heading')) + expect(page).to have_content(review_issues_h1_heading) + + review_issues_subheading = strip_tags(t('errors.doc_auth.rate_limited_subheading')) + expect(page).not_to have_selector('h2', text: review_issues_subheading) + + review_issues_body_message = strip_tags( + t('doc_auth.errors.general.multiple_front_id_failures'), + ) + expect(page).to have_content(review_issues_body_message) + + review_issues_rate_limit_warning = strip_tags( + t('idv.failure.attempts_html', count: max_attempts - 2), + ) + expect(page).to have_content(review_issues_rate_limit_warning) + + click_try_again + expect(page).to have_current_path(idv_document_capture_path) + + resubmit_page_body_copy = strip_tags( + t('doc_auth.errors.general.multiple_front_id_failures'), + ) + expect(page).to have_content(resubmit_page_body_copy) + + resubmit_page_inline_error_messages = strip_tags( + t('doc_auth.errors.general.fallback_field_level'), + ) + expect(page).to have_content(resubmit_page_inline_error_messages).once + + resubmit_page_inline_selfie_error_message = strip_tags( + t('doc_auth.errors.general.selfie_failure'), + ) + expect(page).not_to have_content(resubmit_page_inline_selfie_error_message) + + # when there are multiple back doc auth errors + + attach_images( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_multiple_doc_auth_failures_back_side_only.yml' + ), + ) + attach_selfie( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_multiple_doc_auth_failures_back_side_only.yml' + ), + ) + + submit_images + + review_issues_h1_heading = strip_tags(t('errors.doc_auth.rate_limited_heading')) + expect(page).to have_content(review_issues_h1_heading) + + review_issues_subheading = strip_tags(t('errors.doc_auth.rate_limited_subheading')) + expect(page).not_to have_selector('h2', text: review_issues_subheading) + + review_issues_body_message = strip_tags( + t('doc_auth.errors.general.multiple_back_id_failures'), + ) + expect(page).to have_content(review_issues_body_message) + + review_issues_rate_limit_warning = strip_tags( + t('idv.failure.attempts_html', count: max_attempts - 3), + ) + expect(page).to have_content(review_issues_rate_limit_warning) + + click_try_again + expect(page).to have_current_path(idv_document_capture_path) + + resubmit_page_body_copy = strip_tags( + t('doc_auth.errors.general.multiple_back_id_failures'), + ) + expect(page).to have_content(resubmit_page_body_copy) + + resubmit_page_inline_error_messages = strip_tags( + t('doc_auth.errors.general.fallback_field_level'), + ) + expect(page).to have_content(resubmit_page_inline_error_messages).once + + resubmit_page_inline_selfie_error_message = strip_tags( + t('doc_auth.errors.general.selfie_failure'), + ) + expect(page).not_to have_content(resubmit_page_inline_selfie_error_message) + end + end + end - click_try_again - expect(page).to have_current_path(idv_document_capture_path) + context 'IPP not enabled' do + before do + allow(IdentityConfig.store).to receive(:doc_auth_max_attempts).and_return(99) + perform_in_browser(:mobile) do + visit_idp_from_oidc_sp_with_ial2(biometric_comparison_required: true) + sign_in_and_2fa_user(@user) + complete_doc_auth_steps_before_document_capture_step + end + end - inline_error_message = strip_tags( - t('doc_auth.errors.general.multiple_front_id_failures'), - ) - expect(page).to have_content(inline_error_message) - selfie_inline_error_message = strip_tags(t('doc_auth.errors.general.selfie_failure')) - expect(page).to have_content(selfie_inline_error_message) + it 'shows the correct error message for the given error' do + # when the only error is a doc auth error + + perform_in_browser(:mobile) do + attach_images( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_doc_auth_fail_selfie_pass.yml' + ), + ) + attach_selfie( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_doc_auth_fail_selfie_pass.yml' + ), + ) + + submit_images + + h1_error_message = strip_tags(t('errors.doc_auth.rate_limited_heading')) + expect(page).to have_content(h1_error_message) + + body_error_message = strip_tags(t('doc_auth.errors.dpi.top_msg')) + expect(page).to have_content(body_error_message) + + review_issues_rate_limit_warning = strip_tags( + t( + 'idv.failure.attempts_html', + count: max_attempts - 1, + ), + ) + expect(page).to have_content(review_issues_rate_limit_warning) + + click_try_again + expect(page).to have_current_path(idv_document_capture_path) + + inline_error_message = strip_tags(t('doc_auth.errors.dpi.failed_short')) + expect(page).to have_content(inline_error_message) + + resubmit_page_inline_selfie_error_message = strip_tags( + t('doc_auth.errors.general.selfie_failure'), + ) + expect(page).not_to have_content(resubmit_page_inline_selfie_error_message) + + expect(page).to have_current_path(idv_document_capture_url) + + # when doc auth result passes but liveness fails + + attach_images( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_no_liveness.yml' + ), + ) + attach_selfie( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_no_liveness.yml' + ), + ) + + submit_images + + h1_error_message = strip_tags( + t('errors.doc_auth.selfie_not_live_or_poor_quality_heading'), + ) + expect(page).to have_content(h1_error_message) + + body_error_message = strip_tags( + t('doc_auth.errors.alerts.selfie_not_live_or_poor_quality'), + ) + expect(page).to have_content(body_error_message) + + review_issues_rate_limit_warning = strip_tags( + t('idv.failure.attempts_html', count: max_attempts - 2), + ) + expect(page).to have_content(review_issues_rate_limit_warning) + + click_try_again + expect(page).to have_current_path(idv_document_capture_path) + + resubmit_page_inline_selfie_error_message = strip_tags( + t('doc_auth.errors.general.selfie_failure'), + ) + expect(page).to have_content(resubmit_page_inline_selfie_error_message) + + # when there are both doc auth errors and liveness errors + + attach_images( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_doc_auth_fail_and_no_liveness.yml' + ), + ) + attach_selfie( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_doc_auth_fail_and_no_liveness.yml' + ), + ) + + submit_images + + h1_error_message = strip_tags(t('errors.doc_auth.rate_limited_heading')) + expect(page).to have_content(h1_error_message) + + body_error_message = strip_tags(t('doc_auth.errors.dpi.top_msg')) + expect(page).to have_content(body_error_message) + + review_issues_rate_limit_warning = strip_tags( + t('idv.failure.attempts_html', count: max_attempts - 3), + ) + expect(page).to have_content(review_issues_rate_limit_warning) + + click_try_again + expect(page).to have_current_path(idv_document_capture_path) + + inline_error_message = strip_tags(t('doc_auth.errors.dpi.failed_short')) + expect(page).to have_content(inline_error_message) + resubmit_page_inline_selfie_error_message = strip_tags( + t('doc_auth.errors.general.selfie_failure'), + ) + expect(page).not_to have_content(resubmit_page_inline_selfie_error_message) + + # when there are both doc auth errors and face match errors + + attach_images( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_doc_auth_fail_face_match_fail.yml' + ), + ) + attach_selfie( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_doc_auth_fail_face_match_fail.yml' + ), + ) + + submit_images + + h1_error_message = strip_tags(t('errors.doc_auth.rate_limited_heading')) + expect(page).to have_content(h1_error_message) + + body_error_message = strip_tags(t('doc_auth.errors.dpi.top_msg')) + expect(page).to have_content(body_error_message) + + review_issues_rate_limit_warning = strip_tags( + t('idv.failure.attempts_html', count: max_attempts - 4), + ) + expect(page).to have_content(review_issues_rate_limit_warning) + + click_try_again + expect(page).to have_current_path(idv_document_capture_path) + + inline_error_message = strip_tags(t('doc_auth.errors.dpi.failed_short')) + expect(page).to have_content(inline_error_message) + resubmit_page_inline_selfie_error_message = strip_tags( + t('doc_auth.errors.general.selfie_failure'), + ) + expect(page).not_to have_content(resubmit_page_inline_selfie_error_message) + + # when doc auth result and liveness pass but face match fails + + attach_images( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_portrait_match_failure.yml' + ), + ) + attach_selfie( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_portrait_match_failure.yml' + ), + ) + + submit_images + + h1_error_message = strip_tags(t('errors.doc_auth.selfie_fail_heading')) + expect(page).to have_content(h1_error_message) + + body_error_message = strip_tags(t('doc_auth.errors.general.selfie_failure')) + expect(page).to have_content(body_error_message) + + review_issues_rate_limit_warning = strip_tags( + t('idv.failure.attempts_html', count: max_attempts - 5), + ) + expect(page).to have_content(review_issues_rate_limit_warning) + + click_try_again + expect(page).to have_current_path(idv_document_capture_path) + + inline_error_message = strip_tags( + t('doc_auth.errors.general.multiple_front_id_failures'), + ) + expect(page).to have_content(inline_error_message) + resubmit_page_inline_selfie_error_message = strip_tags( + t('doc_auth.errors.general.selfie_failure'), + ) + expect(page).to have_content(resubmit_page_inline_selfie_error_message) + + # when there is a doc auth error on one side of the ID and face match errors + + attach_images( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_back_fail_doc_auth_face_match_errors.yml' + ), + ) + attach_selfie( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_back_fail_doc_auth_face_match_errors.yml' + ), + ) + + submit_images + + h1_error_message = strip_tags(t('errors.doc_auth.rate_limited_heading')) + expect(page).to have_content(h1_error_message) + + body_error_message = strip_tags(t('doc_auth.errors.alerts.barcode_content_check')) + expect(page).to have_content(body_error_message) + + review_issues_rate_limit_warning = strip_tags( + t('idv.failure.attempts_html', count: max_attempts - 6), + ) + expect(page).to have_content(review_issues_rate_limit_warning) + + click_try_again + expect(page).to have_current_path(idv_document_capture_path) + + inline_error_message = strip_tags(t('doc_auth.errors.general.fallback_field_level')) + expect(page).to have_content(inline_error_message) + resubmit_page_inline_selfie_error_message = strip_tags( + t('doc_auth.errors.general.selfie_failure'), + ) + expect(page).not_to have_content(resubmit_page_inline_selfie_error_message) + + # when there is a doc auth error on one side of the ID and a liveness error + + attach_images( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_back_fail_doc_auth_liveness_errors.yml' + ), + ) + attach_selfie( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_back_fail_doc_auth_liveness_errors.yml' + ), + ) + + submit_images + + h1_error_message = strip_tags(t('errors.doc_auth.rate_limited_heading')) + expect(page).to have_content(h1_error_message) + + body_error_message = strip_tags(t('doc_auth.errors.alerts.barcode_content_check')) + expect(page).to have_content(body_error_message) + + review_issues_rate_limit_warning = strip_tags( + t('idv.failure.attempts_html', count: max_attempts - 7), + ) + expect(page).to have_content(review_issues_rate_limit_warning) + + click_try_again + expect(page).to have_current_path(idv_document_capture_path) + + inline_error_message = strip_tags(t('doc_auth.errors.general.fallback_field_level')) + expect(page).to have_content(inline_error_message) + resubmit_page_inline_selfie_error_message = strip_tags( + t('doc_auth.errors.general.selfie_failure'), + ) + expect(page).not_to have_content(resubmit_page_inline_selfie_error_message) + + # when doc auth result is "attention" and face match errors + + attach_images( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_doc_auth_attention_face_match_fail.yml' + ), + ) + attach_selfie( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_doc_auth_attention_face_match_fail.yml' + ), + ) + + submit_images + + h1_error_message = strip_tags(t('errors.doc_auth.rate_limited_heading')) + expect(page).to have_content(h1_error_message) + + body_error_message = strip_tags(t('doc_auth.errors.general.no_liveness')) + expect(page).to have_content(body_error_message) + + review_issues_rate_limit_warning = strip_tags( + t('idv.failure.attempts_html', count: max_attempts - 8), + ) + expect(page).to have_content(review_issues_rate_limit_warning).once + + click_try_again + expect(page).to have_current_path(idv_document_capture_path) + + inline_error_message = strip_tags(t('doc_auth.errors.general.fallback_field_level')) + expect(page).to have_content(inline_error_message) + resubmit_page_inline_selfie_error_message = strip_tags( + t('doc_auth.errors.general.selfie_failure'), + ) + expect(page).not_to have_content(resubmit_page_inline_selfie_error_message) + + # when doc auth passes but there are both liveness errors and face match errors + + attach_images( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_liveness_fail_face_match_fail.yml' + ), + ) + attach_selfie( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_liveness_fail_face_match_fail.yml' + ), + ) + + submit_images + + h1_error_message = strip_tags( + t('errors.doc_auth.selfie_not_live_or_poor_quality_heading'), + ) + expect(page).to have_content(h1_error_message) + + body_error_message = strip_tags( + t('doc_auth.errors.alerts.selfie_not_live_or_poor_quality'), + ) + expect(page).to have_content(body_error_message) + + review_issues_rate_limit_warning = strip_tags( + t('idv.failure.attempts_html', count: max_attempts - 9), + ) + expect(page).to have_content(review_issues_rate_limit_warning) + + click_try_again + expect(page).to have_current_path(idv_document_capture_path) + + resubmit_page_inline_selfie_error_message = strip_tags( + t('doc_auth.errors.general.selfie_failure'), + ) + expect(page).to have_content(resubmit_page_inline_selfie_error_message) + + # when doc auth, liveness, and face match pass but PII validation fails + + attach_images( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_doc_auth_selfie_pass_pii_fail.yml' + ), + ) + attach_selfie( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_doc_auth_selfie_pass_pii_fail.yml' + ), + ) + + submit_images + + h1_error_message = strip_tags(t('errors.doc_auth.rate_limited_heading')) + expect(page).to have_content(h1_error_message) + + body_error_message = strip_tags(t('doc_auth.errors.alerts.address_check')) + expect(page).to have_content(body_error_message) + + review_issues_rate_limit_warning = strip_tags( + t('idv.failure.attempts_html', count: max_attempts - 10), + ) + expect(page).to have_content(review_issues_rate_limit_warning) + + click_try_again + expect(page).to have_current_path(idv_document_capture_path) + + inline_error_message = strip_tags( + t('doc_auth.errors.general.multiple_front_id_failures'), + ) + expect(page).to have_content(inline_error_message) + resubmit_page_inline_selfie_error_message = strip_tags( + t('doc_auth.errors.general.selfie_failure'), + ) + expect(page).not_to have_content(resubmit_page_inline_selfie_error_message) + + # when there are both face match errors and pii errors + + attach_images( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_face_match_fail_and_pii_fail.yml' + ), + ) + attach_selfie( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_face_match_fail_and_pii_fail.yml' + ), + ) + + submit_images + + h1_error_message = strip_tags(t('errors.doc_auth.selfie_fail_heading')) + expect(page).to have_content(h1_error_message) + + body_error_message = strip_tags(t('doc_auth.errors.general.selfie_failure')) + expect(page).to have_content(body_error_message) + + review_issues_rate_limit_warning = strip_tags( + t('idv.failure.attempts_html', count: max_attempts - 11), + ) + expect(page).to have_content(review_issues_rate_limit_warning) + + click_try_again + expect(page).to have_current_path(idv_document_capture_path) + + inline_error_message = strip_tags( + t('doc_auth.errors.general.multiple_front_id_failures'), + ) + expect(page).to have_content(inline_error_message) + resubmit_page_inline_selfie_error_message = strip_tags( + t('doc_auth.errors.general.selfie_failure'), + ) + expect(page).to have_content(resubmit_page_inline_selfie_error_message) + end end end @@ -674,7 +944,7 @@ detail_message = strip_tags(t('doc_auth.errors.alerts.selfie_not_live_or_poor_quality')) security_message = strip_tags( t( - 'idv.warning.attempts_html', + 'idv.failure.attempts_html', count: IdentityConfig.store.doc_auth_max_attempts - 1, ), ) @@ -712,7 +982,7 @@ detail_message = strip_tags(t('doc_auth.errors.alerts.selfie_not_live_or_poor_quality')) security_message = strip_tags( t( - 'idv.warning.attempts_html', + 'idv.failure.attempts_html', count: IdentityConfig.store.doc_auth_max_attempts - 1, ), ) @@ -750,7 +1020,7 @@ detail_message = strip_tags(t('doc_auth.errors.general.selfie_failure')) security_message = strip_tags( t( - 'idv.warning.attempts_html', + 'idv.failure.attempts_html', count: IdentityConfig.store.doc_auth_max_attempts - 1, ), ) @@ -789,7 +1059,7 @@ detail_message = strip_tags(t('doc_auth.errors.alerts.selfie_not_live_or_poor_quality')) security_message = strip_tags( t( - 'idv.warning.attempts_html', + 'idv.failure.attempts_html', count: IdentityConfig.store.doc_auth_max_attempts - 1, ), ) 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 e9b5ba2f6c7..1a8fd3cf682 100644 --- a/spec/features/idv/doc_auth/verify_info_step_spec.rb +++ b/spec/features/idv/doc_auth/verify_info_step_spec.rb @@ -164,7 +164,7 @@ expect(page).to have_current_path(idv_session_errors_warning_path) expect(page).to have_content( strip_tags( - t('idv.warning.attempts_html.one'), + t('idv.failure.attempts_html.one'), ), ) click_try_again diff --git a/spec/fixtures/ial2_test_credential_multiple_doc_auth_failures_back_side_only.yml b/spec/fixtures/ial2_test_credential_multiple_doc_auth_failures_back_side_only.yml new file mode 100644 index 00000000000..82611c7299d --- /dev/null +++ b/spec/fixtures/ial2_test_credential_multiple_doc_auth_failures_back_side_only.yml @@ -0,0 +1,7 @@ +transaction_status: failed +doc_auth_result: Failed # values: Passed, Failed, Attention, Unknown +failed_alerts: + - name: 2D Barcode Read + result: Failed + - name: 2D Barcode Content + result: Failed diff --git a/spec/fixtures/ial2_test_credential_multiple_doc_auth_failures_both_sides.yml b/spec/fixtures/ial2_test_credential_multiple_doc_auth_failures_both_sides.yml new file mode 100644 index 00000000000..f7aa6c0804a --- /dev/null +++ b/spec/fixtures/ial2_test_credential_multiple_doc_auth_failures_both_sides.yml @@ -0,0 +1,10 @@ +doc_auth_result: Failed +failed_alerts: + - name: 2D Barcode Read + result: Failed + - name: 2D Barcode Content + result: Failed + - name: Photo Printing + result: Failed + - name: Visible Photo Characteristics + result: Failed diff --git a/spec/fixtures/ial2_test_credential_multiple_doc_auth_failures_front_side_only.yml b/spec/fixtures/ial2_test_credential_multiple_doc_auth_failures_front_side_only.yml new file mode 100644 index 00000000000..147b2318bba --- /dev/null +++ b/spec/fixtures/ial2_test_credential_multiple_doc_auth_failures_front_side_only.yml @@ -0,0 +1,7 @@ +transaction_status: failed +doc_auth_result: Failed # values: Passed, Failed, Attention, Unknown +failed_alerts: + - name: Photo Printing + result: Failed + - name: Visible Photo Characteristics + result: Failed diff --git a/spec/javascript/packages/document-capture/components/document-capture-warning-spec.jsx b/spec/javascript/packages/document-capture/components/document-capture-warning-spec.jsx index 4ff7761b482..d2c33dde949 100644 --- a/spec/javascript/packages/document-capture/components/document-capture-warning-spec.jsx +++ b/spec/javascript/packages/document-capture/components/document-capture-warning-spec.jsx @@ -100,7 +100,7 @@ describe('DocumentCaptureWarning', () => { context('not failed result from vendor', () => { const isFailedResult = false; it('renders not failed doc type', () => { - const { getByRole, getByText, queryByText } = renderContent({ + const { getByRole, getByText } = renderContent({ isFailedDocType: false, isFailedResult, inPersonUrl, @@ -109,7 +109,6 @@ describe('DocumentCaptureWarning', () => { validateHeader('errors.doc_auth.rate_limited_heading', 1, true); validateHeader('errors.doc_auth.rate_limited_subheading', 2, true); expect(getByText('general error')).to.be.ok(); - expect(queryByText('idv.warning.attempts_html')).to.be.null(); expect(getByText('idv.failure.attempts_html')).to.be.ok(); expect(getByRole('button', { name: 'idv.failure.button.try_online' })).to.be.ok(); // ipp section @@ -127,10 +126,9 @@ describe('DocumentCaptureWarning', () => { }); // error message section validateHeader('errors.doc_auth.doc_type_not_supported_heading', 1, true); - validateHeader('errors.doc_auth.rate_limited_subheading', 2, false); + validateHeader('errors.doc_auth.rate_limited_subheading', 2, true); expect(getByText(/general error/)).to.be.ok(); - expect(getByText(/idv.warning.attempts_html/)).to.be.ok(); - expect(queryByText('idv.failure.attempts_html')).to.null(); + expect(queryByText('idv.failure.attempts_html')).to.be.ok(); expect(getByRole('button', { name: 'idv.failure.button.try_online' })).to.be.ok(); // ipp section validateIppSection(true); @@ -143,7 +141,7 @@ describe('DocumentCaptureWarning', () => { const isFailedResult = true; it('renders not failed doc type', () => { const isFailedDocType = false; - const { getByRole, getByText, queryByText } = renderContent({ + const { getByRole, getByText } = renderContent({ isFailedDocType, isFailedResult, inPersonUrl, @@ -153,7 +151,6 @@ describe('DocumentCaptureWarning', () => { validateHeader('errors.doc_auth.rate_limited_heading', 1, true); validateHeader('errors.doc_auth.rate_limited_subheading', 2, false); expect(getByText('general error')).to.be.ok(); - expect(queryByText('idv.warning.attempts_html')).to.be.null(); expect(getByText('idv.failure.attempts_html')).to.be.ok(); expect(getByRole('button', { name: 'idv.failure.button.warning' })).to.be.ok(); // the ipp section isn't displayed with isFailedResult=true @@ -174,8 +171,7 @@ describe('DocumentCaptureWarning', () => { validateHeader('errors.doc_auth.doc_type_not_supported_heading', 1, true); validateHeader('errors.doc_auth.rate_limited_subheading', 2, false); expect(getByText(/general error/)).to.be.ok(); - expect(getByText(/idv.warning.attempts_html/)).to.be.ok(); - expect(queryByText('idv.failure.attempts_html')).to.null(); + expect(queryByText('idv.failure.attempts_html')).to.be.ok(); expect(getByRole('button', { name: 'idv.failure.button.warning' })).to.be.ok(); // ipp section not existing validateIppSection(false); @@ -197,10 +193,9 @@ describe('DocumentCaptureWarning', () => { // error message section validateHeader('errors.doc_auth.selfie_fail_heading', 1, true); - validateHeader('errors.doc_auth.rate_limited_subheading', 2, false); + validateHeader('errors.doc_auth.rate_limited_subheading', 2, true); expect(getByText('general error')).to.be.ok(); - expect(getByText('idv.warning.attempts_html')).to.be.ok(); - expect(queryByText('idv.failure.attempts_html')).to.null(); + expect(queryByText('idv.failure.attempts_html')).to.be.ok(); expect(getByRole('button', { name: 'idv.failure.button.try_online' })).to.be.ok(); // ipp section should exist validateIppSection(true); @@ -221,10 +216,9 @@ describe('DocumentCaptureWarning', () => { // error message section validateHeader('errors.doc_auth.selfie_not_live_or_poor_quality_heading', 1, true); - validateHeader('errors.doc_auth.rate_limited_subheading', 2, false); + validateHeader('errors.doc_auth.rate_limited_subheading', 2, true); expect(getByText('general error')).to.be.ok(); - expect(getByText('idv.warning.attempts_html')).to.be.ok(); - expect(queryByText('idv.failure.attempts_html')).to.null(); + expect(queryByText('idv.failure.attempts_html')).to.be.ok(); expect(getByRole('button', { name: 'idv.failure.button.try_online' })).to.be.ok(); // ipp section exists validateIppSection(true); @@ -246,7 +240,7 @@ describe('DocumentCaptureWarning', () => { location: 'doc_auth_review_issues', heading: 'errors.doc_auth.doc_type_not_supported_heading', subheading: '', - error_message_displayed: 'general error idv.warning.attempts_html', + error_message_displayed: 'general error', remaining_submit_attempts: 2, liveness_checking_required: false, }); @@ -256,7 +250,7 @@ describe('DocumentCaptureWarning', () => { const isFailedResult = false; it('renders not failed doc type', () => { const isFailedDocType = false; - const { getByRole, getByText, queryByText } = renderContent({ + const { getByRole, getByText } = renderContent({ isFailedDocType, isFailedResult, inPersonUrl, @@ -266,7 +260,6 @@ describe('DocumentCaptureWarning', () => { validateHeader('errors.doc_auth.rate_limited_heading', 1, true); validateHeader('errors.doc_auth.rate_limited_subheading', 2, false); expect(getByText('general error')).to.be.ok(); - expect(queryByText('idv.warning.attempts_html')).to.null(); expect(getByText('idv.failure.attempts_html')).to.be.ok(); expect(getByRole('button', { name: 'idv.failure.button.warning' })).to.be.ok(); // ipp section not displayed for non ipp @@ -287,8 +280,7 @@ describe('DocumentCaptureWarning', () => { validateHeader('errors.doc_auth.doc_type_not_supported_heading', 1, true); validateHeader('errors.doc_auth.rate_limited_subheading', 2, false); expect(getByText(/general error/)).to.be.ok(); - expect(getByText(/idv.warning.attempts_html/)).to.be.ok(); - expect(queryByText('idv.failure.attempts_html')).to.null(); + expect(queryByText('idv.failure.attempts_html')).to.be.ok(); expect(getByRole('button', { name: 'idv.failure.button.warning' })).to.be.ok(); // ipp section not displayed for non ipp validateIppSection(false); @@ -301,7 +293,7 @@ describe('DocumentCaptureWarning', () => { const isFailedResult = true; it('renders not failed doc type', () => { const isFailedDocType = false; - const { getByRole, getByText, queryByText } = renderContent({ + const { getByRole, getByText } = renderContent({ isFailedDocType, isFailedResult, inPersonUrl, @@ -311,7 +303,6 @@ describe('DocumentCaptureWarning', () => { validateHeader('errors.doc_auth.rate_limited_heading', 1, true); validateHeader('errors.doc_auth.rate_limited_subheading', 2, false); expect(getByText('general error')).to.be.ok(); - expect(queryByText('idv.warning.attempts_html')).to.be.null(); expect(getByText('idv.failure.attempts_html')).to.be.ok(); expect(getByRole('button', { name: 'idv.failure.button.warning' })).to.be.ok(); // the ipp section isn't displayed with isFailedResult=true @@ -331,8 +322,7 @@ describe('DocumentCaptureWarning', () => { validateHeader('errors.doc_auth.doc_type_not_supported_heading', 1, true); validateHeader('errors.doc_auth.rate_limited_subheading', 2, false); expect(getByText(/general error/)).to.be.ok(); - expect(getByText(/idv.warning.attempts_html/)).to.be.ok(); - expect(queryByText('idv.failure.attempts_html')).to.null(); + expect(queryByText('idv.failure.attempts_html')).to.be.ok(); expect(getByRole('button', { name: 'idv.failure.button.warning' })).to.be.ok(); // ipp section not existing validateIppSection(false); diff --git a/spec/javascript/packages/document-capture/components/review-issues-step-spec.jsx b/spec/javascript/packages/document-capture/components/review-issues-step-spec.jsx index c2a959ba8ae..aae43454b83 100644 --- a/spec/javascript/packages/document-capture/components/review-issues-step-spec.jsx +++ b/spec/javascript/packages/document-capture/components/review-issues-step-spec.jsx @@ -230,9 +230,9 @@ describe('document-capture/components/review-issues-step', () => { value={ new I18n({ strings: { - 'idv.warning.attempts_html': { - one: 'One attempt remaining', - other: '%{count} attempts remaining', + 'idv.failure.attempts_html': { + one: 'One attempt remaining to add your ID online', + other: '%{count} attempts remaining to add your ID online', }, 'errors.doc_auth.doc_type_not_supported_heading': 'doc type not supported', 'doc_auth.errors.doc.wrong_id_type_html': @@ -285,9 +285,9 @@ describe('document-capture/components/review-issues-step', () => { value={ new I18n({ strings: { - 'idv.warning.attempts_html': { - one: 'One attempt remaining', - other: '%{count} attempts remaining', + 'idv.failure.attempts_html': { + one: 'One attempt remaining to add your ID online', + other: '%{count} attempts remaining to add your ID online', }, 'errors.doc_auth.doc_type_not_supported_heading': 'doc type not supported', 'doc_auth.errors.doc.wrong_id_type_html': diff --git a/spec/javascript/packages/document-capture/components/unknown-error-spec.jsx b/spec/javascript/packages/document-capture/components/unknown-error-spec.jsx index b79f6e176c9..5da9baebea5 100644 --- a/spec/javascript/packages/document-capture/components/unknown-error-spec.jsx +++ b/spec/javascript/packages/document-capture/components/unknown-error-spec.jsx @@ -1,5 +1,3 @@ -import { I18n } from '@18f/identity-i18n'; -import { I18nContext } from '@18f/identity-react-i18n'; import UnknownError from '@18f/identity-document-capture/components/unknown-error'; import { toFormEntryError } from '@18f/identity-document-capture/services/upload'; import { within } from '@testing-library/dom'; @@ -9,11 +7,7 @@ describe('UnknownError', () => { context('there is no doc type failure', () => { it('render an empty paragraph when no errors', () => { const { container } = render( - , + , ); expect(container.querySelector('p')).to.be.ok(); }); @@ -29,7 +23,6 @@ describe('UnknownError', () => { }, ]} isFailedDocType={false} - remainingSubmitAttempts={10} hasDismissed />, ); @@ -51,7 +44,6 @@ describe('UnknownError', () => { }, ]} isFailedDocType={false} - remainingSubmitAttempts={10} hasDismissed={false} />, ); @@ -70,63 +62,32 @@ describe('UnknownError', () => { context('there is a doc type failure', () => { it('renders error message with errors and is a doc type failure', () => { const { container } = render( - One attempt remaining', - other: 'You have%{count} attempts remaining', - }, - 'errors.doc_auth.doc_type_not_supported_heading': 'doc type not supported', - }, - }) - } - > - - , + , ); const paragraph = container.querySelector('p'); expect(within(paragraph).getByText(/An unknown error occurred/)).to.be.ok(); - expect(within(paragraph).getByText(/2 attempts/)).to.be.ok(); - expect(within(paragraph).getByText(/remaining/)).to.be.ok(); }); it('renders alternative error message with errors and is a doc type failure', () => { const { container } = render( - One attempt remaining', - other: 'You have %{count} attempts remaining', - }, - }, - }) - } - > - - , + , ); const paragraph = container.querySelector('p'); expect(within(paragraph).getByText(/alternative message/)).to.be.ok(); @@ -136,132 +97,66 @@ describe('UnknownError', () => { context('there is a selfie quality/liveness failure', () => { it('renders error message with errors', () => { const { container } = render( - One attempt remaining', - other: 'You have%{count} attempts remaining', - }, - }, - }) - } - > - - , + , ); expect(within(container).getByText(/An unknown error occurred/)).to.be.ok(); - // Expect that the "2 attempts remaining" text appears - expect(within(container).getByText(/2 attempts/)).to.be.ok(); - expect(within(container).getByText(/remaining/)).to.be.ok(); }); it('renders alternative error message without retry information', () => { const { container } = render( - One attempt remaining', - other: 'You have %{count} attempts remaining', - }, - }, - }) - } - > - - , + , ); expect(within(container).getByText(/An unknown error occurred/)).to.be.ok(); - // Don't expect that the "2 attempts remaining" text appears - expect(within(container).queryByText(/2 attempts/)).to.be.null(); }); }); context('there is a selfie facematch failure', () => { it('renders error message with errors', () => { const { container } = render( - One attempt remaining', - other: 'You have%{count} attempts remaining', - }, - }, - }) - } - > - - , + , ); expect(within(container).getByText(/An unknown error occurred/)).to.be.ok(); - // Expect that the "2 attempts remaining" text appears - expect(within(container).getByText(/2 attempts/)).to.be.ok(); - expect(within(container).getByText(/remaining/)).to.be.ok(); }); it('renders alternative error message without retry information', () => { const { container } = render( - One attempt remaining', - other: 'You have %{count} attempts remaining', - }, - }, - }) - } - > - - , + , ); expect(within(container).getByText(/An unknown error occurred/)).to.be.ok(); - // Don't expect that the "2 attempts remaining" text appears - expect(within(container).queryByText(/2 attempts/)).to.be.null(); }); }); }); diff --git a/spec/services/doc_auth/error_generator_spec.rb b/spec/services/doc_auth/error_generator_spec.rb index d46c6bc285a..96b3a344bfa 100644 --- a/spec/services/doc_auth/error_generator_spec.rb +++ b/spec/services/doc_auth/error_generator_spec.rb @@ -330,10 +330,10 @@ def build_error_info( with(hash_including(:response_info, :message)).twice output = described_class.new(config).generate_doc_auth_errors(error_info) expect(output.keys).to contain_exactly(:general, :front, :back, :hints) - expect(output[:general]).to contain_exactly(DocAuth::Errors::GENERAL_ERROR_LIVENESS) + expect(output[:general]).to contain_exactly(DocAuth::Errors::GENERAL_ERROR) expect(output[:front]).to contain_exactly(DocAuth::Errors::FALLBACK_FIELD_LEVEL) expect(output[:back]).to contain_exactly(DocAuth::Errors::FALLBACK_FIELD_LEVEL) - expect(output[:hints]).to eq(false) + expect(output[:hints]).to eq(true) end it 'DocAuthResult is Failed with known alert and specific selfie no liveness error' do diff --git a/spec/services/doc_auth/lexis_nexis/requests/true_id_request_spec.rb b/spec/services/doc_auth/lexis_nexis/requests/true_id_request_spec.rb index 101cc14c778..f527be367d1 100644 --- a/spec/services/doc_auth/lexis_nexis/requests/true_id_request_spec.rb +++ b/spec/services/doc_auth/lexis_nexis/requests/true_id_request_spec.rb @@ -83,24 +83,18 @@ expect(response.success?).to eq(false) expect(response.errors.keys).to contain_exactly(:general, :front, :back, :hints) + expect(response.errors[:general]).to contain_exactly( + DocAuth::Errors::GENERAL_ERROR, + ) + expect(response.errors[:front]).to contain_exactly(DocAuth::Errors::FALLBACK_FIELD_LEVEL) + expect(response.errors[:back]).to contain_exactly(DocAuth::Errors::FALLBACK_FIELD_LEVEL) + expect(response.errors[:hints]).to eq(true) + expect(response.exception).to be_nil + if include_liveness_expected - expect(response.errors[:general]).to contain_exactly( - DocAuth::Errors::GENERAL_ERROR_LIVENESS, - ) - expect(response.errors[:front]).to contain_exactly(DocAuth::Errors::FALLBACK_FIELD_LEVEL) - expect(response.errors[:back]).to contain_exactly(DocAuth::Errors::FALLBACK_FIELD_LEVEL) - expect(response.errors[:hints]).to eq(false) - - expect(response.exception).to be_nil expect(request_stub_liveness).to have_been_requested expect(response.selfie_check_performed?).to be(true) else - expect(response.errors[:general]).to contain_exactly(DocAuth::Errors::GENERAL_ERROR) - expect(response.errors[:front]).to contain_exactly(DocAuth::Errors::FALLBACK_FIELD_LEVEL) - expect(response.errors[:back]).to contain_exactly(DocAuth::Errors::FALLBACK_FIELD_LEVEL) - expect(response.errors[:hints]).to eq(true) - - expect(response.exception).to be_nil expect(request_stub).to have_been_requested expect(response.selfie_check_performed?).to be(false) end diff --git a/spec/services/doc_auth/lexis_nexis/responses/true_id_response_spec.rb b/spec/services/doc_auth/lexis_nexis/responses/true_id_response_spec.rb index d1fcc4049c0..7847a830725 100644 --- a/spec/services/doc_auth/lexis_nexis/responses/true_id_response_spec.rb +++ b/spec/services/doc_auth/lexis_nexis/responses/true_id_response_spec.rb @@ -409,10 +409,10 @@ def get_decision_product(resp) success: false, exception: nil, errors: { - general: [DocAuth::Errors::GENERAL_ERROR_LIVENESS], + general: [DocAuth::Errors::GENERAL_ERROR], front: [DocAuth::Errors::FALLBACK_FIELD_LEVEL], back: [DocAuth::Errors::FALLBACK_FIELD_LEVEL], - hints: false, + hints: true, }, attention_with_barcode: false, doc_type_supported: true, diff --git a/spec/support/features/doc_auth_helper.rb b/spec/support/features/doc_auth_helper.rb index 358f2ec41d1..df242d8105f 100644 --- a/spec/support/features/doc_auth_helper.rb +++ b/spec/support/features/doc_auth_helper.rb @@ -93,12 +93,17 @@ def complete_doc_auth_steps_before_document_capture_step(expect_accessible: fals expect_page_to_have_no_accessibility_violations(page) if expect_accessible end - def complete_up_to_how_to_verify_step_for_opt_in_ipp(remote: true) + def complete_up_to_how_to_verify_step_for_opt_in_ipp(remote: true, + biometric_comparison_required: false) complete_doc_auth_steps_before_welcome_step complete_welcome_step complete_agreement_step if remote - click_on t('forms.buttons.continue_remote') + if biometric_comparison_required + click_on t('forms.buttons.continue_remote_selfie') + else + click_on t('forms.buttons.continue_remote') + end else click_on t('forms.buttons.continue_ipp') end diff --git a/spec/views/idv/session_errors/warning.html.erb_spec.rb b/spec/views/idv/session_errors/warning.html.erb_spec.rb index 82d9a28c249..c308bd373b0 100644 --- a/spec/views/idv/session_errors/warning.html.erb_spec.rb +++ b/spec/views/idv/session_errors/warning.html.erb_spec.rb @@ -26,7 +26,7 @@ it 'shows remaining attempts' do expect(rendered).to have_text( strip_tags( - t('idv.warning.attempts_html', count: remaining_submit_attempts), + t('idv.failure.attempts_html', count: remaining_submit_attempts), ), ) end @@ -45,7 +45,7 @@ expect(rendered).to have_link(t('idv.failure.button.warning'), href: try_again_path) expect(rendered).to have_text( strip_tags( - t('idv.warning.attempts_html', count: remaining_submit_attempts), + t('idv.failure.attempts_html', count: remaining_submit_attempts), ), ) expect(rendered).to have_link(