Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
4a1aa1a
Add failing test
charleyf Feb 6, 2024
b2d052d
Change component so test passes
charleyf Feb 6, 2024
de806a7
Add tests for selfie quality/liveness
charleyf Feb 6, 2024
e648a99
Add failing test for failed selfie
charleyf Feb 6, 2024
484b02e
Make tests pass
charleyf Feb 6, 2024
0ce9826
Add heading text
charleyf Feb 6, 2024
e318726
Draft in selfie field error
charleyf Feb 6, 2024
9e1e2ce
Pass isFailedSelfie through from response
charleyf Feb 6, 2024
7ff6c51
Remove unnessecary error message and routing
charleyf Feb 6, 2024
84e3538
Add in line error text
charleyf Feb 6, 2024
a5aeec3
Send correct errors when selfie fails
charleyf Feb 7, 2024
123529b
Fix name of error
charleyf Feb 7, 2024
6759d9b
Return correct errors for selfie fail
charleyf Feb 7, 2024
9c2d7c2
Hide subheading when isFailedSelfie
charleyf Feb 7, 2024
06ba140
Show correct error messages
charleyf Feb 8, 2024
e46a6ce
Merge branch 'main' into charley/lg-12080-error-messages-for-facematc…
charleyf Feb 12, 2024
9d8a672
Fix todo comment so lint passes
charleyf Feb 12, 2024
08dcc43
Merge branch 'main' into charley/lg-12080-error-messages-for-facematc…
charleyf Feb 14, 2024
3742c18
Fix formating in yaml file
charleyf Feb 14, 2024
7332cc9
Merge branch 'main' into charley/lg-12080-error-messages-for-facematc…
charleyf Feb 14, 2024
f7261f3
Change heading text to be correct
charleyf Feb 14, 2024
0a29111
Fix test to find correct heading
charleyf Feb 14, 2024
64ef134
changelog: User-Facing Improvements, In-person proofing, add frontend…
charleyf Feb 14, 2024
f0f1a6e
Fix spec to look for new fields
charleyf Feb 14, 2024
d680b58
Edit file and add test to reflect match fail
charleyf Feb 15, 2024
8fe6c4d
Add missing error field keys
charleyf Feb 15, 2024
46cce39
Fix error message count in test
charleyf Feb 15, 2024
ed2bd0b
Receive the selfie failure from the BE
charleyf Feb 20, 2024
15702df
Remove comment note
charleyf Feb 20, 2024
ff2b634
Add comment to clarify test differences
charleyf Feb 20, 2024
e0afab4
Remove duplicate text
charleyf Feb 20, 2024
3735fa6
Fix comments
charleyf Feb 20, 2024
7208fb4
Make uppercasing match the rest of the codebase
charleyf Feb 22, 2024
6d90605
Remove incorrect comments about tests
charleyf Feb 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
import type { ReviewIssuesStepValue } from './review-issues-step';
Comment thread
charleyf marked this conversation as resolved.
Comment thread
charleyf marked this conversation as resolved.

interface DocumentCaptureReviewIssuesProps extends FormStepComponentProps<ReviewIssuesStepValue> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested with the no liveness yml and it worked! See below for screenshots.

Failure Page:

NoLivenessFailurePage

Failure Page (scrolled down to shown the IPP prompt):

NoLivenessFailurePageShowsIPP

Retry Page:

NoLivenessRetryPage

isFailedSelfie: boolean;
isFailedDocType: boolean;
isFailedSelfieLivenessOrQuality: boolean;
remainingSubmitAttempts: number;
Expand All @@ -25,6 +26,7 @@ interface DocumentCaptureReviewIssuesProps extends FormStepComponentProps<Review

function DocumentCaptureReviewIssues({
isFailedDocType,
isFailedSelfie,
isFailedSelfieLivenessOrQuality,
remainingSubmitAttempts = Infinity,
captureHints,
Expand Down Expand Up @@ -54,6 +56,7 @@ function DocumentCaptureReviewIssues({
unknownFieldErrors={unknownFieldErrors}
remainingSubmitAttempts={remainingSubmitAttempts}
isFailedDocType={isFailedDocType}
isFailedSelfie={isFailedSelfie}
isFailedSelfieLivenessOrQuality={isFailedSelfieLivenessOrQuality}
altIsFailedSelfieDontIncludeAttempts
altFailedDocTypeMsg={isFailedDocType ? t('doc_auth.errors.doc.wrong_id_type_html') : null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import AnalyticsContext from '../context/analytics';
interface DocumentCaptureWarningProps {
isFailedDocType: boolean;
isFailedResult: boolean;
isFailedSelfie: boolean;
isFailedSelfieLivenessOrQuality: boolean;
remainingSubmitAttempts: number;
actionOnClick?: () => void;
Expand All @@ -23,22 +24,51 @@ const DISPLAY_ATTEMPTS = 3;

type GetHeadingArguments = {
isFailedDocType: boolean;
isFailedSelfie: boolean;
isFailedSelfieLivenessOrQuality: boolean;
t: typeof I18n.prototype.t;
};
function getHeading({ isFailedDocType, isFailedSelfieLivenessOrQuality, t }: GetHeadingArguments) {
function getHeading({
isFailedDocType,
isFailedSelfie,
isFailedSelfieLivenessOrQuality,
t,
}: GetHeadingArguments) {
if (isFailedDocType) {
return t('errors.doc_auth.doc_type_not_supported_heading');
}
if (isFailedSelfieLivenessOrQuality) {
return t('errors.doc_auth.selfie_not_live_or_poor_quality_heading');
}
if (isFailedSelfie) {
return t('errors.doc_auth.selfie_fail_heading');
}
return t('errors.doc_auth.rate_limited_heading');
}

function getSubheading({
nonIppOrFailedResult,
isFailedDocType,
isFailedSelfieLivenessOrQuality,
isFailedSelfie,
t,
}) {
const showSubheading =
!nonIppOrFailedResult &&
!isFailedDocType &&
!isFailedSelfieLivenessOrQuality &&
!isFailedSelfie;

if (showSubheading) {
return <h2>{t('errors.doc_auth.rate_limited_subheading')}</h2>;
}
return undefined;
}

function DocumentCaptureWarning({
isFailedDocType,
isFailedResult,
isFailedSelfie,
isFailedSelfieLivenessOrQuality,
remainingSubmitAttempts,
actionOnClick,
Expand All @@ -50,13 +80,22 @@ function DocumentCaptureWarning({
const { trackEvent } = useContext(AnalyticsContext);

const nonIppOrFailedResult = !inPersonURL || isFailedResult;
const heading = getHeading({ isFailedDocType, isFailedSelfieLivenessOrQuality, t });
const heading = getHeading({
isFailedDocType,
isFailedSelfie,
isFailedSelfieLivenessOrQuality,
t,
});
const actionText = nonIppOrFailedResult
? t('idv.failure.button.warning')
: t('idv.failure.button.try_online');
const subheading = !nonIppOrFailedResult &&
!isFailedDocType &&
!isFailedSelfieLivenessOrQuality && <h2>{t('errors.doc_auth.rate_limited_subheading')}</h2>;
const subheading = getSubheading({
nonIppOrFailedResult,
isFailedDocType,
isFailedSelfieLivenessOrQuality,
isFailedSelfie,
t,
});
const subheadingRef = useRef<HTMLDivElement>(null);
const errorMessageDisplayedRef = useRef<HTMLDivElement>(null);

Expand Down Expand Up @@ -95,12 +134,14 @@ function DocumentCaptureWarning({
unknownFieldErrors={unknownFieldErrors}
remainingSubmitAttempts={remainingSubmitAttempts}
isFailedDocType={isFailedDocType}
isFailedSelfie={isFailedSelfie}
isFailedSelfieLivenessOrQuality={isFailedSelfieLivenessOrQuality}
hasDismissed={hasDismissed}
/>
</div>

{!isFailedDocType &&
!isFailedSelfie &&
!isFailedSelfieLivenessOrQuality &&
remainingSubmitAttempts <= DISPLAY_ATTEMPTS && (
<p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ function DocumentCapture({ onStepChange = () => {} }: DocumentCaptureProps) {
? withProps({
remainingSubmitAttempts: submissionError.remainingSubmitAttempts,
isFailedResult: submissionError.isFailedResult,
isFailedSelfie: submissionError.isFailedSelfie,
isFailedDocType: submissionError.isFailedDocType,
isFailedSelfieLivenessOrQuality:
submissionError.selfieNotLive || submissionError.selfieNotGoodQuality,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export interface ReviewIssuesStepValue {
interface ReviewIssuesStepProps extends FormStepComponentProps<ReviewIssuesStepValue> {
remainingSubmitAttempts?: number;
isFailedResult?: boolean;
isFailedSelfie?: boolean;
isFailedDocType?: boolean;
isFailedSelfieLivenessOrQuality?: boolean;
captureHints?: boolean;
Expand All @@ -56,6 +57,7 @@ function ReviewIssuesStep({
remainingSubmitAttempts = Infinity,
isFailedResult = false,
isFailedDocType = false,
isFailedSelfie = false,
isFailedSelfieLivenessOrQuality = false,
pii,
captureHints = false,
Expand Down Expand Up @@ -120,6 +122,7 @@ function ReviewIssuesStep({
<DocumentCaptureWarning
isFailedDocType={isFailedDocType}
isFailedResult={isFailedResult}
isFailedSelfie={isFailedSelfie}
isFailedSelfieLivenessOrQuality={isFailedSelfieLivenessOrQuality}
remainingSubmitAttempts={remainingSubmitAttempts}
unknownFieldErrors={unknownFieldErrors}
Expand All @@ -131,6 +134,7 @@ function ReviewIssuesStep({
// Show review issue screen, hasDismissed = true
return (
<DocumentCaptureReviewIssues
isFailedSelfie={isFailedSelfie}
isFailedDocType={isFailedDocType}
isFailedSelfieLivenessOrQuality={isFailedSelfieLivenessOrQuality}
remainingSubmitAttempts={remainingSubmitAttempts}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import MarketingSiteContext from '../context/marketing-site';
interface UnknownErrorProps extends ComponentProps<'p'> {
unknownFieldErrors: FormStepError<{ front: string; back: string }>[];
isFailedDocType: boolean;
isFailedSelfie: boolean;
isFailedSelfieLivenessOrQuality: boolean;
remainingSubmitAttempts: number;
altFailedDocTypeMsg?: string | null;
Expand Down Expand Up @@ -43,6 +44,7 @@ function getError({ unknownFieldErrors }: GetErrorArguments) {
function UnknownError({
unknownFieldErrors = [],
isFailedDocType = false,
isFailedSelfie = false,
isFailedSelfieLivenessOrQuality = false,
remainingSubmitAttempts,
altFailedDocTypeMsg = null,
Expand Down Expand Up @@ -80,7 +82,7 @@ function UnknownError({
</p>
);
}
if (isFailedSelfieLivenessOrQuality && err) {
if ((isFailedSelfieLivenessOrQuality || isFailedSelfie) && err) {
return (
<>
<p>{err.message}</p>
Expand Down
5 changes: 5 additions & 0 deletions app/javascript/packages/document-capture/context/upload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ export interface UploadErrorResponse {
*/
result_failed: boolean;

/**
* Whether the selfie captured matched the image on the id.
*/
selfie_status?: string;

/**
* Whether the doc type is clearly not supported type.
*/
Expand Down
4 changes: 4 additions & 0 deletions app/javascript/packages/document-capture/services/upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ export class UploadFormEntriesError extends FormError {

isFailedDocType = false;

isFailedSelfie = false;

selfieNotLive = false;

selfieNotGoodQuality = false;
Expand Down Expand Up @@ -126,6 +128,8 @@ const upload: UploadImplementation = async function (payload, { method = 'POST',

error.isFailedResult = !!result.result_failed;

error.isFailedSelfie = result.selfie_status === 'fail';

error.isFailedDocType = !result.doc_type_supported;

error.selfieNotLive = result.selfie_live === undefined ? false : !result.selfie_live;
Expand Down
17 changes: 17 additions & 0 deletions app/services/doc_auth/error_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,24 @@ def generate_doc_auth_errors(response_info)
image_metric_errors = get_image_metric_errors(response_info[:image_metrics])
return image_metric_errors.to_h unless image_metric_errors.empty?

# If we have a selfie failure (selfie does not match id), then all three fields:
# front, back, and selfie should fail with specific in line errors.
liveness_enabled = response_info[:liveness_enabled]
selfie_error = get_selfie_error(liveness_enabled, response_info)
if selfie_error == Errors::SELFIE_FAILURE
Comment thread
charleyf marked this conversation as resolved.
# This returns the same sort of object that ErrorResult.to_h returns
Comment thread
charleyf marked this conversation as resolved.
# but we need to do something more complex than that's set up to handle
return {
general: [Errors::SELFIE_NOT_LIVE],
front: [Errors::MULTIPLE_FRONT_ID_FAILURES],
back: [Errors::MULTIPLE_BACK_ID_FAILURES],
selfie: [Errors::SELFIE_FAILURE],
hints: false,
}
end

# This block can also return errors for front, back, and/or selfie
# But we only get here if none of the returns above are triggered
alert_errors = get_error_messages(liveness_enabled, response_info)
alert_error_count += 1 if alert_errors.include?(SELFIE)

Expand Down
12 changes: 5 additions & 7 deletions app/services/doc_auth/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ module Errors
SELFIE_FAILURE = 'selfie_failure'
SELFIE_NOT_LIVE = 'selfie_not_live'
SELFIE_POOR_QUALITY = 'selfie_poor_quality'
SELFIE_NOT_LIVE_POOR_QUALITY_FIELD = 'selfie_not_live_poor_quality'
SEX_CHECK = 'sex_check'
VISIBLE_COLOR_CHECK = 'visible_color_check'
VISIBLE_PHOTO_CHECK = 'visible_photo_check'
Expand Down Expand Up @@ -114,15 +113,14 @@ module Errors
VISIBLE_PHOTO_CHECK => { long_msg: VISIBLE_PHOTO_CHECK, field_msg: FALLBACK_FIELD_LEVEL, hints: true },
SEX_CHECK => { long_msg: SEX_CHECK, field_msg: FALLBACK_FIELD_LEVEL, hints: true },
VISIBLE_COLOR_CHECK => { long_msg: VISIBLE_COLOR_CHECK, field_msg: FALLBACK_FIELD_LEVEL, hints: true },
# Multiple Errors
# Multiple errors
MULTIPLE_FRONT_ID_FAILURES => { long_msg: MULTIPLE_FRONT_ID_FAILURES, field_msg: FALLBACK_FIELD_LEVEL, hints: true },
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 },
# TODO, theses messages need modifying
# Liveness, use general error for now
SELFIE_FAILURE => { long_msg: GENERAL_ERROR, field_msg: FALLBACK_FIELD_LEVEL, hints: false },
SELFIE_NOT_LIVE => { long_msg: SELFIE_NOT_LIVE, field_msg: SELFIE_NOT_LIVE_POOR_QUALITY_FIELD, hints: false },
SELFIE_POOR_QUALITY => { long_msg: SELFIE_POOR_QUALITY, field_msg: SELFIE_NOT_LIVE_POOR_QUALITY_FIELD, hints: false },
# Selfie errors
SELFIE_FAILURE => { long_msg: SELFIE_FAILURE, field_msg: SELFIE_FAILURE, hints: false },
SELFIE_NOT_LIVE => { long_msg: SELFIE_NOT_LIVE, field_msg: SELFIE_FAILURE, hints: false },
SELFIE_POOR_QUALITY => { long_msg: SELFIE_POOR_QUALITY, field_msg: SELFIE_FAILURE, hints: false },
}
# rubocop:enable Layout/LineLength
end
Expand Down
5 changes: 2 additions & 3 deletions app/services/doc_auth_router.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,12 @@ module DocAuthRouter
# i18n-tasks-use t('doc_auth.errors.alerts.ref_control_number_check')
DocAuth::Errors::REF_CONTROL_NUMBER_CHECK =>
'doc_auth.errors.alerts.ref_control_number_check',
# i18n-tasks-use t('doc_auth.errors.general.selfie_failure')
DocAuth::Errors::SELFIE_FAILURE => 'doc_auth.errors.general.selfie_failure',
# i18n-tasks-use t('doc_auth.errors.alerts.selfie_not_live')
DocAuth::Errors::SELFIE_NOT_LIVE => 'doc_auth.errors.alerts.selfie_not_live',
# i18n-tasks-use t('doc_auth.errors.alerts.selfie_poor_quality')
DocAuth::Errors::SELFIE_POOR_QUALITY => 'doc_auth.errors.alerts.selfie_poor_quality',
# i18n-tasks-use t('doc_auth.errors.alerts.selfie_not_live_poor_quality')
DocAuth::Errors::SELFIE_NOT_LIVE_POOR_QUALITY_FIELD =>
'doc_auth.errors.alerts.selfie_not_live_poor_quality',
# i18n-tasks-use t('doc_auth.errors.alerts.sex_check')
DocAuth::Errors::SEX_CHECK => 'doc_auth.errors.alerts.sex_check',
# i18n-tasks-use t('doc_auth.errors.alerts.visible_color_check')
Expand Down
2 changes: 1 addition & 1 deletion config/locales/doc_auth/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ en:
ref_control_number_check: We couldn’t read the control number barcode. Try taking new pictures.
selfie_not_live: 'Try taking a photo of yourself again. Make sure your whole
face is clear and visible in the photo.'
selfie_not_live_poor_quality: 'We couldn’t verify the photo of yourself. Try taking a new picture.'
selfie_poor_quality: 'Try taking a photo of yourself again. Make sure your whole
face is clear and visible in the photo.'
sex_check: We couldn’t read the sex on your ID. Try taking new pictures.
Expand Down Expand Up @@ -85,6 +84,7 @@ en:
network_error: We are having technical difficulties on our end. Please try to
submit your images again later.
no_liveness: Try taking new pictures.
selfie_failure: We couldn’t verify the photo of yourself. Try taking a new picture.
glare:
failed_short: Image has glare, please try again.
top_msg: We couldn’t read your ID. Your photo may have glare. Make sure that the
Expand Down
2 changes: 1 addition & 1 deletion config/locales/doc_auth/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ es:
control. Intente tomar nuevas fotografías.
selfie_not_live: 'Intenta volver a tomarte una foto. Asegúrate de que tu rostro
completo esté claro y visible en la foto.'
selfie_not_live_poor_quality: 'No pudimos verificar su foto. Trate de tomarse otra foto.'
selfie_poor_quality: 'Intenta volver a tomarte una foto. Asegúrate de que tu
rostro completo esté claro y visible en la foto.'
sex_check: No pudimos leer el sexo en su documento de identidad. Intente tomar
Expand Down Expand Up @@ -108,6 +107,7 @@ es:
network_error: Estamos teniendo problemas técnicos por nuestra parte. Intente
enviar sus imágenes de nuevo más tarde.
no_liveness: Intente tomar nuevas fotografías.
selfie_failure: No pudimos verificar su foto. Trate de tomarse otra foto.
glare:
failed_short: Hay reflejos en la imagen, por favor inténtelo de nuevo.
top_msg: No pudimos leer su identificación. Es posible que su foto tenga
Expand Down
4 changes: 2 additions & 2 deletions config/locales/doc_auth/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ fr:
contrôle. Essayez de prendre de nouvelles photos.
selfie_not_live: 'Rééssayez de vous prendre en photo. Assurez-vous que lensemble
de votre visage est clair et visible sur la photo.'
selfie_not_live_poor_quality: 'Nous n’avons pas réussi à vérifier votre photo.
Essayez à nouveau avec une nouvelle photo.'
selfie_poor_quality: 'Rééssayez de vous prendre en photo. Assurez-vous que
l’ensemble de votre visage est clair et visible sur la photo.'
sex_check: Nous n’avons pas pu lire le sexe sur votre pièce d’identité. Essayez
Expand Down Expand Up @@ -114,6 +112,8 @@ fr:
network_error: Nous avons des difficultés techniques de notre côté. Veuillez
essayer de soumettre à nouveau vos images plus tard.
no_liveness: Essayez de prendre de nouvelles photos.
selfie_failure: Nous n’avons pas réussi à vérifier votre photo. Essayez à
nouveau avec une nouvelle photo.
glare:
failed_short: L’image a des reflets, veuillez réessayer.
top_msg: Nous n’avons pas pu lire votre pièce d’identité. Votre photo peut avoir
Expand Down
1 change: 1 addition & 0 deletions config/locales/errors/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ en:
rate_limited_text_html: 'For your security, we limit the number of times you can
attempt to verify a document online. <strong>Try again in
%{timeout}.</strong>'
selfie_fail_heading: We couldn’t match the photo of yourself to your ID
selfie_not_live_or_poor_quality_heading: We could not verify the photo of yourself
send_link_limited: You tried too many times, please try again in %{timeout}. You
can also go back and choose to use your computer instead.
Expand Down
1 change: 1 addition & 0 deletions config/locales/errors/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ es:
rate_limited_text_html: 'Por su seguridad, limitamos el número de veces que
puede intentar verificar un documento en línea. <strong>Inténtelo de
nuevo en %{timeout}.</strong>'
selfie_fail_heading: No pudimos cotejar tu foto con tu identificación
selfie_not_live_or_poor_quality_heading: No pudimos verificar tu foto
send_link_limited: Ha intentado demasiadas veces, por favor, inténtelo de nuevo
en %{timeout}. También puede retroceder y elegir utilizar su computadora
Expand Down
2 changes: 2 additions & 0 deletions config/locales/errors/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ fr:
rate_limited_text_html: 'Pour votre sécurité, nous limitons le nombre de fois où
vous pouvez tenter de vérifier un document en ligne. <strong>Veuillez
réessayer dans %{timeout}.</strong>'
selfie_fail_heading: Nous n’avons pas pu faire correspondre votre photo à votre
pièce d’identité
selfie_not_live_or_poor_quality_heading: Nous n’avons pas pu vérifier votre photo
send_link_limited: Vous avez essayé trop de fois, veuillez réessayer dans
%{timeout}. Vous pouvez également revenir en arrière et choisir
Expand Down
Loading