diff --git a/app/javascript/packages/document-capture/components/acuant-capture.tsx b/app/javascript/packages/document-capture/components/acuant-capture.tsx index 5ddcf36b6cd..f1b681b4097 100644 --- a/app/javascript/packages/document-capture/components/acuant-capture.tsx +++ b/app/javascript/packages/document-capture/components/acuant-capture.tsx @@ -490,44 +490,50 @@ function AcuantCapture( setIsCapturingEnvironment(false); } + function onAcuantImageCaptureFailure(error: AcuantCaptureFailureError, code: string | undefined) { + const { SEQUENCE_BREAK_CODE } = window.AcuantJavascriptWebSdk; + if (isAcuantCameraAccessFailure(error)) { + if (fullScreenRef.current?.focusTrap) { + suspendFocusTrapForAnticipatedFocus(fullScreenRef.current.focusTrap); + } + + // Internally, Acuant sets a cookie to bail on guided capture if initialization had + // previously failed for any reason, including declined permission. Since the cookie + // never expires, and since we want to re-prompt even if the user had previously + // declined, unset the cookie value when failure occurs for permissions. + setAcuantFailureCookie(null); + + onCameraAccessDeclined(); + } else if (code === SEQUENCE_BREAK_CODE) { + setOwnErrorMessage( + `${t('doc_auth.errors.upload_error')} ${t('errors.messages.try_again') + .split(' ') + .join(NBSP_UNICODE)}`, + ); + + refreshAcuantFailureCookie(); + } else if (error === undefined) { + // Show a more generic error message when there's a cropping error. + // Errors with a value of `undefined` are cropping errors. + setOwnErrorMessage(t('errors.general')); + } else { + setOwnErrorMessage(t('doc_auth.errors.camera.failed')); + } + + setIsCapturingEnvironment(false); + trackEvent('IdV: Image capture failed', { + field: name, + error: getNormalizedAcuantCaptureFailureMessage(error, code), + }); + } + return (
{isCapturingEnvironment && ( setHasStartedCropping(true)} onImageCaptureSuccess={onAcuantImageCaptureSuccess} - onImageCaptureFailure={(error, code) => { - const { SEQUENCE_BREAK_CODE } = window.AcuantJavascriptWebSdk; - if (isAcuantCameraAccessFailure(error)) { - if (fullScreenRef.current?.focusTrap) { - suspendFocusTrapForAnticipatedFocus(fullScreenRef.current.focusTrap); - } - - // Internally, Acuant sets a cookie to bail on guided capture if initialization had - // previously failed for any reason, including declined permission. Since the cookie - // never expires, and since we want to re-prompt even if the user had previously - // declined, unset the cookie value when failure occurs for permissions. - setAcuantFailureCookie(null); - - onCameraAccessDeclined(); - } else if (code === SEQUENCE_BREAK_CODE) { - setOwnErrorMessage( - `${t('doc_auth.errors.upload_error')} ${t('errors.messages.try_again') - .split(' ') - .join(NBSP_UNICODE)}`, - ); - - refreshAcuantFailureCookie(); - } else { - setOwnErrorMessage(t('doc_auth.errors.camera.failed')); - } - - setIsCapturingEnvironment(false); - trackEvent('IdV: Image capture failed', { - field: name, - error: getNormalizedAcuantCaptureFailureMessage(error, code), - }); - }} + onImageCaptureFailure={onAcuantImageCaptureFailure} > {!hasStartedCropping && ( { expect(document.activeElement).to.equal(button); }); + it('shows a generic error if camera starts but cropping error occurs', async () => { + const trackEvent = sinon.spy(); + const { container, getByLabelText, findByText } = render( + + + + + + + , + ); + + initialize({ + // Call `onCropped` with a response of 'undefined' + start: sinon.stub().callsArgWithAsync(1, undefined), + }); + + const button = getByLabelText('Image'); + await userEvent.click(button); + // "Oops, something went wrong. Please try again." + await findByText('errors.general'); + + expect(window.AcuantCameraUI.end).to.have.been.calledOnce(); + expect(container.querySelector('.full-screen')).to.be.null(); + expect(trackEvent).to.have.been.calledWith('IdV: Image capture failed', { + field: 'test', + error: 'Cropping failure', + }); + expect(document.activeElement).to.equal(button); + }); + it('shows error if capture fails: latest version of Acuant SDK', async () => { const trackEvent = sinon.spy(); const { container, getByLabelText, findByText } = render(