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 dcc7540660b..acbe5ccd9e5 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,15 +438,19 @@ function AcuantCapture( const { image, cardtype, dpi, moire, glare, sharpness } = nextCapture; const isAssessedAsGlare = glare < glareThreshold; const isAssessedAsBlurry = sharpness < sharpnessThreshold; + const isAssessedAsUnsupported = cardtype !== 1; const { width, height, data } = image; let assessment: AcuantImageAssessment; - if (isAssessedAsGlare) { - setOwnErrorMessage(t('doc_auth.errors.glare.failed_short')); - assessment = 'glare'; - } else if (isAssessedAsBlurry) { + 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 (isAssessedAsUnsupported) { + setOwnErrorMessage(t('doc_auth.errors.card_type')); + assessment = 'unsupported'; } else { assessment = 'success'; } @@ -456,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, @@ -475,7 +480,11 @@ function AcuantCapture( onChangeAndResetError(data, analyticsPayload); onResetFailedCaptureAttempts(); } else { - onFailedCaptureAttempt({ isAssessedAsGlare, isAssessedAsBlurry }); + onFailedCaptureAttempt({ + isAssessedAsGlare, + isAssessedAsBlurry, + isAssessedAsUnsupported, + }); } 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..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,6 +5,7 @@ import useCounter from '../hooks/use-counter'; interface CaptureAttemptMetadata { isAssessedAsGlare: boolean; isAssessedAsBlurry: boolean; + isAssessedAsUnsupported: boolean; } interface FailedCaptureAttemptsContextInterface { @@ -66,6 +67,7 @@ interface FailedCaptureAttemptsContextInterface { const DEFAULT_LAST_ATTEMPT_METADATA: CaptureAttemptMetadata = { isAssessedAsGlare: false, isAssessedAsBlurry: false, + isAssessedAsUnsupported: 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..7c8caa16b9a 100644 --- a/config/locales/doc_auth/es.yml +++ b/config/locales/doc_auth/es.yml @@ -62,6 +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. 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..cedab5fca79 100644 --- a/config/locales/doc_auth/fr.yml +++ b/config/locales/doc_auth/fr.yml @@ -67,6 +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. 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/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', 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..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,6 +92,7 @@ describe('document-capture/context/capture-troubleshooting', () => { expect(trackEvent).to.have.been.calledWith('IdV: Capture troubleshooting shown', { isAssessedAsGlare: false, isAssessedAsBlurry: false, + isAssessedAsUnsupported: 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'));