From e4bc89c5392de5f74d9c32a93019038660887e3b Mon Sep 17 00:00:00 2001 From: Eileen McFarland Date: Mon, 17 Jul 2023 13:54:36 -0400 Subject: [PATCH 1/6] display error messages --- .../document-capture/components/acuant-capture.tsx | 10 +++++++--- .../context/failed-capture-attempts.tsx | 2 ++ config/locales/doc_auth/en.yml | 1 + config/locales/doc_auth/es.yml | 1 + config/locales/doc_auth/fr.yml | 1 + 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/app/javascript/packages/document-capture/components/acuant-capture.tsx b/app/javascript/packages/document-capture/components/acuant-capture.tsx index dcc7540660b..47786da6033 100644 --- a/app/javascript/packages/document-capture/components/acuant-capture.tsx +++ b/app/javascript/packages/document-capture/components/acuant-capture.tsx @@ -30,7 +30,7 @@ import type { } from './acuant-camera'; type AcuantDocumentTypeLabel = 'id' | 'passport' | 'none'; -type AcuantImageAssessment = 'success' | 'glare' | 'blurry'; +type AcuantImageAssessment = 'success' | 'glare' | 'blurry' | 'unsupported'; type ImageSource = 'acuant' | 'upload'; interface ImageAnalyticsPayload { @@ -438,10 +438,14 @@ function AcuantCapture( const { image, cardtype, dpi, moire, glare, sharpness } = nextCapture; const isAssessedAsGlare = glare < glareThreshold; const isAssessedAsBlurry = sharpness < sharpnessThreshold; + const isUnsupportedDocument = cardtype !== 1; const { width, height, data } = image; let assessment: AcuantImageAssessment; - if (isAssessedAsGlare) { + if (isUnsupportedDocument) { + setOwnErrorMessage(t('doc_auth.errors.card_type')); + assessment = 'unsupported'; + } else if (isAssessedAsGlare) { setOwnErrorMessage(t('doc_auth.errors.glare.failed_short')); assessment = 'glare'; } else if (isAssessedAsBlurry) { @@ -475,7 +479,7 @@ function AcuantCapture( onChangeAndResetError(data, analyticsPayload); onResetFailedCaptureAttempts(); } else { - onFailedCaptureAttempt({ isAssessedAsGlare, isAssessedAsBlurry }); + onFailedCaptureAttempt({ isAssessedAsGlare, isAssessedAsBlurry, isUnsupportedDocument }); } setIsCapturingEnvironment(false); diff --git a/app/javascript/packages/document-capture/context/failed-capture-attempts.tsx b/app/javascript/packages/document-capture/context/failed-capture-attempts.tsx index 12d300bd0a5..2d891b78fee 100644 --- a/app/javascript/packages/document-capture/context/failed-capture-attempts.tsx +++ b/app/javascript/packages/document-capture/context/failed-capture-attempts.tsx @@ -5,6 +5,7 @@ import useCounter from '../hooks/use-counter'; interface CaptureAttemptMetadata { isAssessedAsGlare: boolean; isAssessedAsBlurry: boolean; + isUnsupportedDocument: boolean; } interface FailedCaptureAttemptsContextInterface { @@ -66,6 +67,7 @@ interface FailedCaptureAttemptsContextInterface { const DEFAULT_LAST_ATTEMPT_METADATA: CaptureAttemptMetadata = { isAssessedAsGlare: false, isAssessedAsBlurry: false, + isUnsupportedDocument: false, }; const FailedCaptureAttemptsContext = createContext({ diff --git a/config/locales/doc_auth/en.yml b/config/locales/doc_auth/en.yml index c91745ff7db..c5f00f01cba 100644 --- a/config/locales/doc_auth/en.yml +++ b/config/locales/doc_auth/en.yml @@ -52,6 +52,7 @@ en: browser or system settings, reload this page, or upload a photo instead. failed: Camera failed to start, please try again. + card_type: Try again with your driver’s license or state ID card. dpi: failed_short: Image is too small or blurry, please try again. top_msg: We couldn’t read your ID. Your image size may be too small, or your ID diff --git a/config/locales/doc_auth/es.yml b/config/locales/doc_auth/es.yml index c741fd81ca6..d0a55342fd5 100644 --- a/config/locales/doc_auth/es.yml +++ b/config/locales/doc_auth/es.yml @@ -62,6 +62,7 @@ es: compruebe la configuración de su navegador o sistema, recargue esta página o suba una foto en su lugar. failed: No se ha podido encender la cámara, por favor, inténtelo de nuevo. + card_type: Solo se aceptan licencias de conducir o documentos de identidad estatales. dpi: failed_short: La imagen es demasiado pequeña o está borrosa, por favor inténtelo de nuevo. diff --git a/config/locales/doc_auth/fr.yml b/config/locales/doc_auth/fr.yml index 24ff5d1f60a..68d19f19985 100644 --- a/config/locales/doc_auth/fr.yml +++ b/config/locales/doc_auth/fr.yml @@ -67,6 +67,7 @@ fr: Veuillez vérifier les paramètres de votre navigateur ou de votre système, recharger cette page ou télécharger une photo à la place. failed: L’appareil photo n’a pas réussi à démarrer, veuillez réessayer. + card_type: Réessayez avec votre permis de conduire ou carte d'identité délivrée par l'État. dpi: failed_short: L’image est trop petite ou floue, veuillez réessayer. top_msg: Nous n’avons pas pu lire votre pièce d’identité. La taille de votre From b09451f58d842895df45d0534e1b828ba404fd4b Mon Sep 17 00:00:00 2001 From: Eileen McFarland Date: Mon, 17 Jul 2023 13:55:54 -0400 Subject: [PATCH 2/6] changelog: Bug Fixes, Docauth, block submission of non-ids From a75e6124c9fbfe193ded140af06c84086677eb7a Mon Sep 17 00:00:00 2001 From: Eileen McFarland Date: Tue, 18 Jul 2023 15:42:46 -0400 Subject: [PATCH 3/6] fix build --- config/locales/doc_auth/es.yml | 3 ++- config/locales/doc_auth/fr.yml | 3 ++- .../components/capture-troubleshooting-spec.jsx | 1 + .../document-capture/components/document-capture-spec.jsx | 1 + .../document-capture/components/documents-step-spec.jsx | 2 +- 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/config/locales/doc_auth/es.yml b/config/locales/doc_auth/es.yml index d0a55342fd5..7c8caa16b9a 100644 --- a/config/locales/doc_auth/es.yml +++ b/config/locales/doc_auth/es.yml @@ -62,7 +62,8 @@ es: compruebe la configuración de su navegador o sistema, recargue esta página o suba una foto en su lugar. failed: No se ha podido encender la cámara, por favor, inténtelo de nuevo. - card_type: Solo se aceptan licencias de conducir o documentos de identidad estatales. + card_type: Solo se aceptan licencias de conducir o documentos de identidad + estatales. dpi: failed_short: La imagen es demasiado pequeña o está borrosa, por favor inténtelo de nuevo. diff --git a/config/locales/doc_auth/fr.yml b/config/locales/doc_auth/fr.yml index 68d19f19985..cedab5fca79 100644 --- a/config/locales/doc_auth/fr.yml +++ b/config/locales/doc_auth/fr.yml @@ -67,7 +67,8 @@ fr: Veuillez vérifier les paramètres de votre navigateur ou de votre système, recharger cette page ou télécharger une photo à la place. failed: L’appareil photo n’a pas réussi à démarrer, veuillez réessayer. - card_type: Réessayez avec votre permis de conduire ou carte d'identité délivrée par l'État. + card_type: Réessayez avec votre permis de conduire ou carte d’identité délivrée + par l’État. dpi: failed_short: L’image est trop petite ou floue, veuillez réessayer. top_msg: Nous n’avons pas pu lire votre pièce d’identité. La taille de votre diff --git a/spec/javascript/packages/document-capture/components/capture-troubleshooting-spec.jsx b/spec/javascript/packages/document-capture/components/capture-troubleshooting-spec.jsx index 35060c7313a..99fa2233b76 100644 --- a/spec/javascript/packages/document-capture/components/capture-troubleshooting-spec.jsx +++ b/spec/javascript/packages/document-capture/components/capture-troubleshooting-spec.jsx @@ -92,6 +92,7 @@ describe('document-capture/context/capture-troubleshooting', () => { expect(trackEvent).to.have.been.calledWith('IdV: Capture troubleshooting shown', { isAssessedAsGlare: false, isAssessedAsBlurry: false, + isUnsupportedDocument: false, }); const tryAgainButton = getByRole('button', { name: 'idv.failure.button.warning' }); diff --git a/spec/javascript/packages/document-capture/components/document-capture-spec.jsx b/spec/javascript/packages/document-capture/components/document-capture-spec.jsx index 8f681bd89e7..c685a5dc62c 100644 --- a/spec/javascript/packages/document-capture/components/document-capture-spec.jsx +++ b/spec/javascript/packages/document-capture/components/document-capture-spec.jsx @@ -91,6 +91,7 @@ describe('document-capture/components/document-capture', () => { image: { data: validUpload, }, + cardtype: 1, }); }); diff --git a/spec/javascript/packages/document-capture/components/documents-step-spec.jsx b/spec/javascript/packages/document-capture/components/documents-step-spec.jsx index e1d5adf102a..0a289f0386a 100644 --- a/spec/javascript/packages/document-capture/components/documents-step-spec.jsx +++ b/spec/javascript/packages/document-capture/components/documents-step-spec.jsx @@ -66,7 +66,7 @@ describe('document-capture/components/documents-step', () => { ); initialize(); - const result = { sharpness: 100, image: { data: '' } }; + const result = { sharpness: 100, image: { data: '' }, cardtype: 1 }; window.AcuantCameraUI.start.callsFake(({ onCropped }) => onCropped({ ...result, glare: 10 })); await userEvent.click(getByLabelText('doc_auth.headings.document_capture_front')); From 658b0361d4ac2960e773aeaae6d5d24473eeb627 Mon Sep 17 00:00:00 2001 From: Eileen McFarland Date: Wed, 19 Jul 2023 11:27:01 -0400 Subject: [PATCH 4/6] add testing for unsupported doc error message and log --- .../components/acuant-capture.tsx | 11 +++-- .../components/acuant-capture-spec.jsx | 44 +++++++++++++++++++ 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/app/javascript/packages/document-capture/components/acuant-capture.tsx b/app/javascript/packages/document-capture/components/acuant-capture.tsx index 47786da6033..1e0d15f7b2e 100644 --- a/app/javascript/packages/document-capture/components/acuant-capture.tsx +++ b/app/javascript/packages/document-capture/components/acuant-capture.tsx @@ -438,11 +438,11 @@ function AcuantCapture( const { image, cardtype, dpi, moire, glare, sharpness } = nextCapture; const isAssessedAsGlare = glare < glareThreshold; const isAssessedAsBlurry = sharpness < sharpnessThreshold; - const isUnsupportedDocument = cardtype !== 1; + const isAssessedAsUnsupported = cardtype !== 1; const { width, height, data } = image; let assessment: AcuantImageAssessment; - if (isUnsupportedDocument) { + if (isAssessedAsUnsupported) { setOwnErrorMessage(t('doc_auth.errors.card_type')); assessment = 'unsupported'; } else if (isAssessedAsGlare) { @@ -460,6 +460,7 @@ function AcuantCapture( height, mimeType: 'image/jpeg', // Acuant Web SDK currently encodes all images as JPEG source: 'acuant', + isAssessedAsUnsupported, documentType: getDocumentTypeLabel(cardtype), dpi, moire, @@ -479,7 +480,11 @@ function AcuantCapture( onChangeAndResetError(data, analyticsPayload); onResetFailedCaptureAttempts(); } else { - onFailedCaptureAttempt({ isAssessedAsGlare, isAssessedAsBlurry, isUnsupportedDocument }); + onFailedCaptureAttempt({ + isAssessedAsGlare, + isAssessedAsBlurry, + isUnsupportedDocument: isAssessedAsUnsupported, + }); } setIsCapturingEnvironment(false); diff --git a/spec/javascript/packages/document-capture/components/acuant-capture-spec.jsx b/spec/javascript/packages/document-capture/components/acuant-capture-spec.jsx index 96bf37f3063..320abd0c920 100644 --- a/spec/javascript/packages/document-capture/components/acuant-capture-spec.jsx +++ b/spec/javascript/packages/document-capture/components/acuant-capture-spec.jsx @@ -547,6 +547,47 @@ describe('document-capture/components/acuant-capture', () => { ); }); + it('renders error message and logs metadata if capture succeeds but the document type identified is unsupported', async () => { + const trackEvent = sinon.spy(); + const { getByText, findByText } = render( + + + + + + + , + ); + + initialize({ + start: sinon.stub().callsFake(async (callbacks) => { + await Promise.resolve(); + callbacks.onCaptured(); + await Promise.resolve(); + callbacks.onCropped({ + ...ACUANT_CAPTURE_SUCCESS_RESULT, + cardtype: 2, + }); + }), + }); + + const button = getByText('doc_auth.buttons.take_picture'); + fireEvent.click(button); + + const error = await findByText('doc_auth.errors.card_type'); + + expect(trackEvent).to.have.been.calledWith( + 'IdV: test image added', + sinon.match({ + documentType: 'passport', + isAssessedAsUnsupported: true, + assessment: 'unsupported', + }), + ); + + expect(error).to.be.ok(); + }); + it('renders error message if capture succeeds but photo glare exceeds threshold', async () => { const trackEvent = sinon.spy(); const { getByText, findByText } = render( @@ -585,6 +626,7 @@ describe('document-capture/components/acuant-capture', () => { height: 1104, sharpnessScoreThreshold: sinon.match.number, glareScoreThreshold: 50, + isAssessedAsUnsupported: false, isAssessedAsBlurry: false, isAssessedAsGlare: true, assessment: 'glare', @@ -639,6 +681,7 @@ describe('document-capture/components/acuant-capture', () => { height: 1104, sharpnessScoreThreshold: 50, glareScoreThreshold: sinon.match.number, + isAssessedAsUnsupported: false, isAssessedAsBlurry: true, isAssessedAsGlare: false, assessment: 'blurry', @@ -746,6 +789,7 @@ describe('document-capture/components/acuant-capture', () => { height: 1104, sharpnessScoreThreshold: 50, glareScoreThreshold: sinon.match.number, + isAssessedAsUnsupported: false, isAssessedAsBlurry: true, isAssessedAsGlare: false, assessment: 'blurry', From eee0b8f81173e8eed4464035a4d3bbbf86d03fc6 Mon Sep 17 00:00:00 2001 From: Eileen McFarland Date: Wed, 19 Jul 2023 13:12:26 -0400 Subject: [PATCH 5/6] refactor in response to feedback --- .../document-capture/components/acuant-camera.tsx | 13 +++++-------- .../document-capture/components/acuant-capture.tsx | 12 ++++++------ 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/app/javascript/packages/document-capture/components/acuant-camera.tsx b/app/javascript/packages/document-capture/components/acuant-camera.tsx index cbdc2243d43..b6707d54eaa 100644 --- a/app/javascript/packages/document-capture/components/acuant-camera.tsx +++ b/app/javascript/packages/document-capture/components/acuant-camera.tsx @@ -93,14 +93,11 @@ interface AcuantCameraUIOptions { text: AcuantCameraUIText; } -/** - * Document type. - * - * 0 = None - * 1 = ID - * 2 = Passport - */ -export type AcuantDocumentType = 0 | 1 | 2; +export enum AcuantDocumentType { + NONE = 0, + ID = 1, + PASSPORT = 2, +} export type AcuantCaptureFailureError = | undefined // Cropping failure (SDK v11.5.0, L1171) diff --git a/app/javascript/packages/document-capture/components/acuant-capture.tsx b/app/javascript/packages/document-capture/components/acuant-capture.tsx index 1e0d15f7b2e..7198bfffd6b 100644 --- a/app/javascript/packages/document-capture/components/acuant-capture.tsx +++ b/app/javascript/packages/document-capture/components/acuant-capture.tsx @@ -442,15 +442,15 @@ function AcuantCapture( const { width, height, data } = image; let assessment: AcuantImageAssessment; - if (isAssessedAsUnsupported) { - setOwnErrorMessage(t('doc_auth.errors.card_type')); - assessment = 'unsupported'; + if (isAssessedAsBlurry) { + setOwnErrorMessage(t('doc_auth.errors.sharpness.failed_short')); + assessment = 'blurry'; } else if (isAssessedAsGlare) { setOwnErrorMessage(t('doc_auth.errors.glare.failed_short')); assessment = 'glare'; - } else if (isAssessedAsBlurry) { - setOwnErrorMessage(t('doc_auth.errors.sharpness.failed_short')); - assessment = 'blurry'; + } else if (isAssessedAsUnsupported) { + setOwnErrorMessage(t('doc_auth.errors.card_type')); + assessment = 'unsupported'; } else { assessment = 'success'; } From a30550c6d962a47b80bccc33cd44dc762b1fb641 Mon Sep 17 00:00:00 2001 From: Eileen McFarland Date: Wed, 19 Jul 2023 14:59:33 -0400 Subject: [PATCH 6/6] clean up variable names in CaptureAttemptMetadata --- .../packages/document-capture/components/acuant-capture.tsx | 2 +- .../document-capture/context/failed-capture-attempts.tsx | 4 ++-- .../components/capture-troubleshooting-spec.jsx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/javascript/packages/document-capture/components/acuant-capture.tsx b/app/javascript/packages/document-capture/components/acuant-capture.tsx index 7198bfffd6b..acbe5ccd9e5 100644 --- a/app/javascript/packages/document-capture/components/acuant-capture.tsx +++ b/app/javascript/packages/document-capture/components/acuant-capture.tsx @@ -483,7 +483,7 @@ function AcuantCapture( onFailedCaptureAttempt({ isAssessedAsGlare, isAssessedAsBlurry, - isUnsupportedDocument: isAssessedAsUnsupported, + isAssessedAsUnsupported, }); } diff --git a/app/javascript/packages/document-capture/context/failed-capture-attempts.tsx b/app/javascript/packages/document-capture/context/failed-capture-attempts.tsx index 2d891b78fee..b55935a7091 100644 --- a/app/javascript/packages/document-capture/context/failed-capture-attempts.tsx +++ b/app/javascript/packages/document-capture/context/failed-capture-attempts.tsx @@ -5,7 +5,7 @@ import useCounter from '../hooks/use-counter'; interface CaptureAttemptMetadata { isAssessedAsGlare: boolean; isAssessedAsBlurry: boolean; - isUnsupportedDocument: boolean; + isAssessedAsUnsupported: boolean; } interface FailedCaptureAttemptsContextInterface { @@ -67,7 +67,7 @@ interface FailedCaptureAttemptsContextInterface { const DEFAULT_LAST_ATTEMPT_METADATA: CaptureAttemptMetadata = { isAssessedAsGlare: false, isAssessedAsBlurry: false, - isUnsupportedDocument: false, + isAssessedAsUnsupported: false, }; const FailedCaptureAttemptsContext = createContext({ diff --git a/spec/javascript/packages/document-capture/components/capture-troubleshooting-spec.jsx b/spec/javascript/packages/document-capture/components/capture-troubleshooting-spec.jsx index 99fa2233b76..10e6e9b8419 100644 --- a/spec/javascript/packages/document-capture/components/capture-troubleshooting-spec.jsx +++ b/spec/javascript/packages/document-capture/components/capture-troubleshooting-spec.jsx @@ -92,7 +92,7 @@ describe('document-capture/context/capture-troubleshooting', () => { expect(trackEvent).to.have.been.calledWith('IdV: Capture troubleshooting shown', { isAssessedAsGlare: false, isAssessedAsBlurry: false, - isUnsupportedDocument: false, + isAssessedAsUnsupported: false, }); const tryAgainButton = getByRole('button', { name: 'idv.failure.button.warning' });