diff --git a/app/javascript/packages/document-capture/context/acuant.jsx b/app/javascript/packages/document-capture/context/acuant.jsx
index 1440e2c12ad..30e3f201c79 100644
--- a/app/javascript/packages/document-capture/context/acuant.jsx
+++ b/app/javascript/packages/document-capture/context/acuant.jsx
@@ -1,5 +1,6 @@
import { createContext, useContext, useMemo, useEffect, useState } from 'react';
import DeviceContext from './device';
+import AnalyticsContext from './analytics';
/** @typedef {import('react').ReactNode} ReactNode */
@@ -9,15 +10,25 @@ import DeviceContext from './device';
* @prop {boolean} isCameraSupported Whether camera is supported.
*/
+/**
+ * @typedef {1|2|400|401|403} AcuantInitializeCode Acuant initialization callback code.
+ *
+ * @see https://github.com/Acuant/JavascriptWebSDKV11/blob/11.4.4/SimpleHTMLApp/webSdk/dist/AcuantJavascriptWebSdk.js#L1327-L1353
+ */
+
/**
* @typedef AcuantCallbackOptions
*
* @prop {()=>void} onSuccess Success callback.
- * @prop {()=>void} onFail Failure callback.
+ * @prop {(code: AcuantInitializeCode, description: string)=>void} onFail Failure callback.
*/
/**
- * @typedef {(credentials:string?,endpoint:string?,AcuantCallbackOptions)=>void} AcuantInitialize
+ * @typedef {(
+ * credentials: string?,
+ * endpoint: string?,
+ * callback: AcuantCallbackOptions,
+ * )=>void} AcuantInitialize
*/
/**
@@ -66,6 +77,7 @@ function AcuantContextProvider({
children,
}) {
const { isMobile } = useContext(DeviceContext);
+ const { addPageAction } = useContext(AnalyticsContext);
// Only mobile devices should load the Acuant SDK. Consider immediately ready otherwise.
const [isReady, setIsReady] = useState(!isMobile);
const [isAcuantLoaded, setIsAcuantLoaded] = useState(false);
@@ -94,13 +106,29 @@ function AcuantContextProvider({
endpoint,
{
onSuccess: () => {
+ addPageAction({
+ label: 'IdV: Acuant SDK loaded',
+ payload: { success: true },
+ });
+
setIsCameraSupported(
/** @type {AcuantGlobal} */ (window).AcuantCamera.isCameraSupported,
);
setIsReady(true);
setIsAcuantLoaded(true);
},
- onFail: () => setIsError(true),
+ onFail(code, description) {
+ addPageAction({
+ label: 'IdV: Acuant SDK loaded',
+ payload: {
+ success: false,
+ code,
+ description,
+ },
+ });
+
+ setIsError(true);
+ },
},
);
};
diff --git a/app/javascript/packs/document-capture.jsx b/app/javascript/packs/document-capture.jsx
index e893e40ad64..c093685b4e1 100644
--- a/app/javascript/packs/document-capture.jsx
+++ b/app/javascript/packs/document-capture.jsx
@@ -141,34 +141,34 @@ loadPolyfills(['fetch', 'crypto']).then(async () => {
render(
-
-
+
-
-
-
+
+
+
-
-
-
-
-
+
+
+
+
+
,
appRoot,
);
diff --git a/package.json b/package.json
index 03efaab7536..2e0a9e6df30 100644
--- a/package.json
+++ b/package.json
@@ -48,6 +48,7 @@
"@testing-library/user-event": "^12.6.0",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
+ "@types/sinon": "^9.0.11",
"chai": "^4.2.0",
"dirty-chai": "^2.0.1",
"eslint": "^7.16.0",
diff --git a/spec/javascripts/packages/document-capture/context/acuant-spec.jsx b/spec/javascripts/packages/document-capture/context/acuant-spec.jsx
index 769eff53aea..ff10335aa25 100644
--- a/spec/javascripts/packages/document-capture/context/acuant-spec.jsx
+++ b/spec/javascripts/packages/document-capture/context/acuant-spec.jsx
@@ -1,6 +1,7 @@
+import sinon from 'sinon';
import { useContext } from 'react';
import { renderHook } from '@testing-library/react-hooks';
-import { DeviceContext } from '@18f/identity-document-capture';
+import { DeviceContext, AnalyticsContext } from '@18f/identity-document-capture';
import AcuantContext, {
Provider as AcuantContextProvider,
} from '@18f/identity-document-capture/context/acuant';
@@ -92,50 +93,97 @@ describe('document-capture/context/acuant', () => {
});
});
- it('provides ready context when successfully loaded', () => {
- const { result } = renderHook(() => useContext(AcuantContext), {
- wrapper: ({ children }) => (
-
- {children}
-
- ),
+ context('successful initialization', () => {
+ let result;
+ let addPageAction;
+
+ beforeEach(() => {
+ addPageAction = sinon.spy();
+ ({ result } = renderHook(() => useContext(AcuantContext), {
+ wrapper: ({ children }) => (
+
+
+ {children}
+
+
+ ),
+ }));
+
+ window.AcuantJavascriptWebSdk = {
+ initialize: (_credentials, _endpoint, { onSuccess }) => onSuccess(),
+ };
+ window.AcuantCamera = { isCameraSupported: true };
+ window.onAcuantSdkLoaded();
});
- window.AcuantJavascriptWebSdk = {
- initialize: (_credentials, _endpoint, { onSuccess }) => onSuccess(),
- };
- window.AcuantCamera = { isCameraSupported: true };
- window.onAcuantSdkLoaded();
+ it('provides ready context', () => {
+ expect(result.current).to.eql({
+ isReady: true,
+ isAcuantLoaded: true,
+ isError: false,
+ isCameraSupported: true,
+ credentials: null,
+ endpoint: null,
+ });
+ });
- expect(result.current).to.eql({
- isReady: true,
- isAcuantLoaded: true,
- isError: false,
- isCameraSupported: true,
- credentials: null,
- endpoint: null,
+ it('logs', () => {
+ expect(addPageAction).to.have.been.calledWith({
+ label: 'IdV: Acuant SDK loaded',
+ payload: {
+ success: true,
+ },
+ });
});
});
- it('has camera availability at time of ready', () => {
- const { result } = renderHook(() => useContext(AcuantContext), {
- wrapper: ({ children }) => (
-
- {children}
-
- ),
+ context('failed initialization', () => {
+ let result;
+ let addPageAction;
+
+ beforeEach(() => {
+ addPageAction = sinon.spy();
+ ({ result } = renderHook(() => useContext(AcuantContext), {
+ wrapper: ({ children }) => (
+
+
+ {children}
+
+
+ ),
+ }));
+
+ window.AcuantJavascriptWebSdk = {
+ initialize: (_credentials, _endpoint, { onFail }) =>
+ onFail(401, 'Server returned a 401 (missing credentials).'),
+ };
+ window.onAcuantSdkLoaded();
});
- window.AcuantJavascriptWebSdk = {
- initialize: (_credentials, _endpoint, { onSuccess }) => onSuccess(),
- };
- window.AcuantCamera = { isCameraSupported: true };
- window.onAcuantSdkLoaded();
+ it('provides error context', () => {
+ expect(result.current).to.eql({
+ isReady: false,
+ isAcuantLoaded: false,
+ isError: true,
+ isCameraSupported: null,
+ credentials: null,
+ endpoint: null,
+ });
+ });
- expect(result.current.isCameraSupported).to.be.true();
+ it('logs', () => {
+ expect(addPageAction).to.have.been.calledWith({
+ label: 'IdV: Acuant SDK loaded',
+ payload: {
+ success: false,
+ code: sinon.match.number,
+ description: sinon.match.string,
+ },
+ });
+ });
});
- it('provides error context when failed to loaded', () => {
+ it('has camera availability at time of ready', () => {
const { result } = renderHook(() => useContext(AcuantContext), {
wrapper: ({ children }) => (
@@ -145,18 +193,12 @@ describe('document-capture/context/acuant', () => {
});
window.AcuantJavascriptWebSdk = {
- initialize: (_credentials, _endpoint, { onFail }) => onFail(),
+ initialize: (_credentials, _endpoint, { onSuccess }) => onSuccess(),
};
+ window.AcuantCamera = { isCameraSupported: true };
window.onAcuantSdkLoaded();
- expect(result.current).to.eql({
- isReady: false,
- isAcuantLoaded: false,
- isError: true,
- isCameraSupported: null,
- credentials: null,
- endpoint: null,
- });
+ expect(result.current.isCameraSupported).to.be.true();
});
it('cleans up after itself on unmount', () => {
diff --git a/yarn.lock b/yarn.lock
index f431ae34b34..465445f7bc7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1222,6 +1222,18 @@
"@types/prop-types" "*"
csstype "^3.0.2"
+"@types/sinon@^9.0.11":
+ version "9.0.11"
+ resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-9.0.11.tgz#7af202dda5253a847b511c929d8b6dda170562eb"
+ integrity sha512-PwP4UY33SeeVKodNE37ZlOsR9cReypbMJOhZ7BVE0lB+Hix3efCOxiJWiE5Ia+yL9Cn2Ch72EjFTRze8RZsNtg==
+ dependencies:
+ "@types/sinonjs__fake-timers" "*"
+
+"@types/sinonjs__fake-timers@*":
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.2.tgz#3a84cf5ec3249439015e14049bd3161419bf9eae"
+ integrity sha512-dIPoZ3g5gcx9zZEszaxLSVTvMReD3xxyyDnQUjA6IYDG9Ba2AV0otMPs+77sG9ojB4Qr2N2Vk5RnKeuA0X/0bg==
+
"@types/testing-library__react-hooks@^3.4.0":
version "3.4.1"
resolved "https://registry.yarnpkg.com/@types/testing-library__react-hooks/-/testing-library__react-hooks-3.4.1.tgz#b8d7311c6c1f7db3103e94095fe901f8fef6e433"