From d08aa318b8c637b7f77b888722ff98d6ec573dbb Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Tue, 23 Aug 2022 15:56:29 -0400 Subject: [PATCH 1/2] Rename JavaScript addPageAction to trackEvent **Why**: - For alignment to the Ahoy/CloudWatch-based Ruby track_event method, to which it corresponds - For alignment to 18f/identity-analytics trackEvent method, which it wraps - To avoid falsely aligning to NewRelic's addPageAction, to which it does **not** correspond changelog: Internal, Analytics, Align JavaScript and Ruby analytics logging method name --- .../components/acuant-capture.tsx | 12 ++-- .../components/capture-troubleshooting.jsx | 6 +- .../components/review-issues-step.tsx | 4 +- .../document-capture/components/warning.tsx | 6 +- .../document-capture/context/acuant.tsx | 6 +- .../document-capture/context/analytics.jsx | 4 +- .../with-background-encrypted-upload.jsx | 8 +-- app/javascript/packs/document-capture.jsx | 8 +-- .../components/acuant-capture-spec.jsx | 68 +++++++++---------- .../components/capture-advice-spec.jsx | 8 +-- .../capture-troubleshooting-spec.jsx | 12 ++-- .../components/review-issues-step-spec.jsx | 8 +-- .../components/warning-spec.jsx | 8 +-- .../document-capture/context/acuant-spec.jsx | 18 ++--- .../context/failed-capture-attempts-spec.jsx | 14 ++-- .../with-background-encrypted-upload-spec.jsx | 26 +++---- 16 files changed, 108 insertions(+), 108 deletions(-) diff --git a/app/javascript/packages/document-capture/components/acuant-capture.tsx b/app/javascript/packages/document-capture/components/acuant-capture.tsx index 36598517bb5..92a95e8a4fd 100644 --- a/app/javascript/packages/document-capture/components/acuant-capture.tsx +++ b/app/javascript/packages/document-capture/components/acuant-capture.tsx @@ -278,7 +278,7 @@ function AcuantCapture( sharpnessThreshold, } = useContext(AcuantContext); const { isMockClient } = useContext(UploadContext); - const { addPageAction } = useContext(AnalyticsContext); + const { trackEvent } = useContext(AnalyticsContext); const fullScreenRef = useRef(null); const inputRef = useRef(null); const isForceUploading = useRef(false); @@ -351,7 +351,7 @@ function AcuantCapture( size: nextValue.size, }); - addPageAction(`IdV: ${name} image added`, analyticsPayload); + trackEvent(`IdV: ${name} image added`, analyticsPayload); } onChangeAndResetError(nextValue, analyticsPayload); @@ -365,7 +365,7 @@ function AcuantCapture( return any>(fn: T) => (...args: Parameters) => { if (!isSuppressingClickLogging.current) { - addPageAction(`IdV: ${name} image clicked`, { source, ...metadata }); + trackEvent(`IdV: ${name} image clicked`, { source, ...metadata }); } return fn(...args); @@ -415,7 +415,7 @@ function AcuantCapture( function startCaptureOrTriggerUpload(event: MouseEvent) { if (event.target === inputRef.current) { if (forceNativeCamera) { - addPageAction('IdV: Native camera forced after failed attempts', { + trackEvent('IdV: Native camera forced after failed attempts', { field: name, failed_attempts: failedCaptureAttempts, }); @@ -483,7 +483,7 @@ function AcuantCapture( size: getDecodedBase64ByteSize(nextCapture.image.data), }); - addPageAction(`IdV: ${name} image added`, analyticsPayload); + trackEvent(`IdV: ${name} image added`, analyticsPayload); if (assessment === 'success') { onChangeAndResetError(data, analyticsPayload); @@ -528,7 +528,7 @@ function AcuantCapture( } setIsCapturingEnvironment(false); - addPageAction('IdV: Image capture failed', { + trackEvent('IdV: Image capture failed', { field: name, error: getNormalizedAcuantCaptureFailureMessage(error, code), }); diff --git a/app/javascript/packages/document-capture/components/capture-troubleshooting.jsx b/app/javascript/packages/document-capture/components/capture-troubleshooting.jsx index 3477e50bfba..b65878113dc 100644 --- a/app/javascript/packages/document-capture/components/capture-troubleshooting.jsx +++ b/app/javascript/packages/document-capture/components/capture-troubleshooting.jsx @@ -18,7 +18,7 @@ import CaptureAdvice from './capture-advice'; * @param {CaptureTroubleshootingProps} props */ function CaptureTroubleshooting({ children }) { - const { addPageAction } = useContext(AnalyticsContext); + const { trackEvent } = useContext(AnalyticsContext); const [didShowTroubleshooting, setDidShowTroubleshooting] = useState(false); const { failedCaptureAttempts, maxFailedAttemptsBeforeTips, lastAttemptMetadata } = useContext( FailedCaptureAttemptsContext, @@ -28,13 +28,13 @@ function CaptureTroubleshooting({ children }) { const { isAssessedAsGlare, isAssessedAsBlurry } = lastAttemptMetadata; function onCaptureTipsShown() { - addPageAction('IdV: Capture troubleshooting shown', lastAttemptMetadata); + trackEvent('IdV: Capture troubleshooting shown', lastAttemptMetadata); onPageTransition(); } function onCaptureTipsDismissed() { - addPageAction('IdV: Capture troubleshooting dismissed'); + trackEvent('IdV: Capture troubleshooting dismissed'); setDidShowTroubleshooting(true); } diff --git a/app/javascript/packages/document-capture/components/review-issues-step.tsx b/app/javascript/packages/document-capture/components/review-issues-step.tsx index a981efc0d83..44f77b57755 100644 --- a/app/javascript/packages/document-capture/components/review-issues-step.tsx +++ b/app/javascript/packages/document-capture/components/review-issues-step.tsx @@ -79,14 +79,14 @@ function ReviewIssuesStep({ const { t } = useI18n(); const { isMobile } = useContext(DeviceContext); const serviceProvider = useContext(ServiceProviderContext); - const { addPageAction } = useContext(AnalyticsContext); + const { trackEvent } = useContext(AnalyticsContext); const selfieError = errors.find(({ field }) => field === 'selfie')?.error; const [hasDismissed, setHasDismissed] = useState(remainingAttempts === Infinity); const { onPageTransition, changeStepCanComplete } = useContext(FormStepsContext); useDidUpdateEffect(onPageTransition, [hasDismissed]); function onWarningPageDismissed() { - addPageAction('IdV: Capture troubleshooting dismissed'); + trackEvent('IdV: Capture troubleshooting dismissed'); setHasDismissed(true); } diff --git a/app/javascript/packages/document-capture/components/warning.tsx b/app/javascript/packages/document-capture/components/warning.tsx index 2184117c22a..60ae578d4af 100644 --- a/app/javascript/packages/document-capture/components/warning.tsx +++ b/app/javascript/packages/document-capture/components/warning.tsx @@ -50,9 +50,9 @@ function Warning({ location, remainingAttempts, }: WarningProps) { - const { addPageAction } = useContext(AnalyticsContext); + const { trackEvent } = useContext(AnalyticsContext); useEffect(() => { - addPageAction('IdV: warning shown', { location, remaining_attempts: remainingAttempts }); + trackEvent('IdV: warning shown', { location, remaining_attempts: remainingAttempts }); }, []); let actionButtons: ReactComponentElement[] | undefined; @@ -62,7 +62,7 @@ function Warning({ isBig isWide onClick={() => { - addPageAction('IdV: warning action triggered', { location }); + trackEvent('IdV: warning action triggered', { location }); actionOnClick(); }} > diff --git a/app/javascript/packages/document-capture/context/acuant.tsx b/app/javascript/packages/document-capture/context/acuant.tsx index c00e2c0bfff..91879540726 100644 --- a/app/javascript/packages/document-capture/context/acuant.tsx +++ b/app/javascript/packages/document-capture/context/acuant.tsx @@ -214,7 +214,7 @@ function AcuantContextProvider({ children, }: AcuantContextProviderProps) { const { isMobile } = useContext(DeviceContext); - const { addPageAction } = useContext(AnalyticsContext); + const { trackEvent } = useContext(AnalyticsContext); // Only mobile devices should load the Acuant SDK. Consider immediately ready otherwise. const [isReady, setIsReady] = useState(!isMobile); const [isAcuantLoaded, setIsAcuantLoaded] = useState(false); @@ -262,7 +262,7 @@ function AcuantContextProvider({ window.AcuantJavascriptWebSdk.startWorkers(() => { window.AcuantCamera = getActualAcuantCamera(); const { isCameraSupported: nextIsCameraSupported } = window.AcuantCamera; - addPageAction('IdV: Acuant SDK loaded', { + trackEvent('IdV: Acuant SDK loaded', { success: true, isCameraSupported: nextIsCameraSupported, }); @@ -273,7 +273,7 @@ function AcuantContextProvider({ }); }, onFail(code, description) { - addPageAction('IdV: Acuant SDK loaded', { + trackEvent('IdV: Acuant SDK loaded', { success: false, code, description, diff --git a/app/javascript/packages/document-capture/context/analytics.jsx b/app/javascript/packages/document-capture/context/analytics.jsx index df26bbc3187..da28a1a3fa3 100644 --- a/app/javascript/packages/document-capture/context/analytics.jsx +++ b/app/javascript/packages/document-capture/context/analytics.jsx @@ -14,12 +14,12 @@ import { createContext } from 'react'; /** * @typedef AnalyticsContext * - * @prop {TrackEvent} addPageAction Log an action with optional payload. + * @prop {TrackEvent} trackEvent Log an action with optional payload. */ const AnalyticsContext = createContext( /** @type {AnalyticsContext} */ ({ - addPageAction: () => Promise.resolve(), + trackEvent: () => Promise.resolve(), }), ); diff --git a/app/javascript/packages/document-capture/higher-order/with-background-encrypted-upload.jsx b/app/javascript/packages/document-capture/higher-order/with-background-encrypted-upload.jsx index 81984f02dd4..1b6fd17d571 100644 --- a/app/javascript/packages/document-capture/higher-order/with-background-encrypted-upload.jsx +++ b/app/javascript/packages/document-capture/higher-order/with-background-encrypted-upload.jsx @@ -84,7 +84,7 @@ const withBackgroundEncryptedUpload = (Component) => { */ function ComposedComponent({ onChange, onError, ...props }) { const { backgroundUploadURLs, backgroundUploadEncryptKey } = useContext(UploadContext); - const { addPageAction } = useContext(AnalyticsContext); + const { trackEvent } = useContext(AnalyticsContext); /** * @param {Record} nextValues Next values. @@ -103,14 +103,14 @@ const withBackgroundEncryptedUpload = (Component) => { value, ) .catch((error) => { - addPageAction('IdV: document capture async upload encryption', { success: false }); + trackEvent('IdV: document capture async upload encryption', { success: false }); trackError(error); // Rethrow error to skip upload and proceed from next `catch` block. throw error; }) .then((encryptedValue) => { - addPageAction('IdV: document capture async upload encryption', { success: true }); + trackEvent('IdV: document capture async upload encryption', { success: true }); return window.fetch(url, { method: 'PUT', @@ -120,7 +120,7 @@ const withBackgroundEncryptedUpload = (Component) => { }) .then((response) => { const traceId = response.headers.get('X-Amzn-Trace-Id'); - addPageAction('IdV: document capture async upload submitted', { + trackEvent('IdV: document capture async upload submitted', { success: response.ok, trace_id: traceId, status_code: response.status, diff --git a/app/javascript/packs/document-capture.jsx b/app/javascript/packs/document-capture.jsx index 88b621c5e91..9a0d2b2a6ea 100644 --- a/app/javascript/packs/document-capture.jsx +++ b/app/javascript/packs/document-capture.jsx @@ -13,7 +13,7 @@ import { } from '@18f/identity-document-capture'; import { isCameraCapableMobile } from '@18f/identity-device'; import { FlowContext } from '@18f/identity-verify-flow'; -import { trackEvent } from '@18f/identity-analytics'; +import { trackEvent as baseTrackEvent } from '@18f/identity-analytics'; /** @typedef {import('@18f/identity-document-capture').FlowPath} FlowPath */ /** @typedef {import('@18f/identity-i18n').I18n} I18n */ @@ -93,9 +93,9 @@ const device = { }; /** @type {import('@18f/identity-analytics').trackEvent} */ -function addPageAction(event, payload) { +function trackEvent(event, payload) { const { flowPath } = appRoot.dataset; - return trackEvent(event, { ...payload, flow_path: flowPath }); + return baseTrackEvent(event, { ...payload, flow_path: flowPath }); } (async () => { @@ -145,7 +145,7 @@ function addPageAction(event, payload) { [AppContext.Provider, { value: { appName } }], [MarketingSiteContextProvider, { helpCenterRedirectURL, securityAndPrivacyHowItWorksURL }], [DeviceContext.Provider, { value: device }], - [AnalyticsContext.Provider, { value: { addPageAction } }], + [AnalyticsContext.Provider, { value: { trackEvent } }], [ AcuantContextProvider, { diff --git a/spec/javascripts/packages/document-capture/components/acuant-capture-spec.jsx b/spec/javascripts/packages/document-capture/components/acuant-capture-spec.jsx index 8d4bd58d420..61913689e8a 100644 --- a/spec/javascripts/packages/document-capture/components/acuant-capture-spec.jsx +++ b/spec/javascripts/packages/document-capture/components/acuant-capture-spec.jsx @@ -257,9 +257,9 @@ describe('document-capture/components/acuant-capture', () => { }); it('shows error if capture fails', async () => { - const addPageAction = sinon.spy(); + const trackEvent = sinon.spy(); const { container, getByLabelText, findByText } = render( - + @@ -278,7 +278,7 @@ describe('document-capture/components/acuant-capture', () => { await findByText('doc_auth.errors.camera.failed'); expect(window.AcuantCameraUI.end).to.have.been.calledOnce(); expect(container.querySelector('.full-screen')).to.be.null(); - expect(addPageAction).to.have.been.calledWith('IdV: Image capture failed', { + expect(trackEvent).to.have.been.calledWith('IdV: Image capture failed', { field: 'test', error: 'Camera not supported', }); @@ -286,9 +286,9 @@ describe('document-capture/components/acuant-capture', () => { }); it('shows sequence break error', async () => { - const addPageAction = sinon.spy(); + const trackEvent = sinon.spy(); const { container, getByLabelText, findByText } = render( - + @@ -313,7 +313,7 @@ describe('document-capture/components/acuant-capture', () => { await findByText('doc_auth.errors.upload_error errors.messages.try_again'); expect(window.AcuantCameraUI.end).to.have.been.calledOnce(); expect(container.querySelector('.full-screen')).to.be.null(); - expect(addPageAction).to.have.been.calledWith('IdV: Image capture failed', { + expect(trackEvent).to.have.been.calledWith('IdV: Image capture failed', { field: 'test', error: 'iOS 15 GPU Highwater failure (SEQUENCE_BREAK_CODE)', }); @@ -327,10 +327,10 @@ describe('document-capture/components/acuant-capture', () => { }); it('calls onCameraAccessDeclined if camera access is declined', async () => { - const addPageAction = sinon.spy(); + const trackEvent = sinon.spy(); const onCameraAccessDeclined = sinon.stub(); const { container, getByLabelText } = render( - + { expect(window.AcuantCameraUI.end).to.eventually.be.called(), ]); expect(container.querySelector('.full-screen')).to.be.null(); - expect(addPageAction).to.have.been.calledWith('IdV: Image capture failed', { + expect(trackEvent).to.have.been.calledWith('IdV: Image capture failed', { field: 'test', error: 'User or system denied camera access', }); @@ -546,9 +546,9 @@ describe('document-capture/components/acuant-capture', () => { }); it('renders error message if capture succeeds but photo glare exceeds threshold', async () => { - const addPageAction = sinon.spy(); + const trackEvent = sinon.spy(); const { getByText, findByText } = render( - + @@ -573,7 +573,7 @@ describe('document-capture/components/acuant-capture', () => { fireEvent.click(button); const error = await findByText('doc_auth.errors.glare.failed_short'); - expect(addPageAction).to.have.been.calledWith('IdV: test image added', { + expect(trackEvent).to.have.been.calledWith('IdV: test image added', { documentType: 'id', mimeType: 'image/jpeg', source: 'acuant', @@ -596,9 +596,9 @@ describe('document-capture/components/acuant-capture', () => { }); it('renders error message if capture succeeds but photo is too blurry', async () => { - const addPageAction = sinon.spy(); + const trackEvent = sinon.spy(); const { getByText, findByText } = render( - + { fireEvent.click(button); const error = await findByText('doc_auth.errors.sharpness.failed_short'); - expect(addPageAction).to.have.been.calledWith('IdV: test image added', { + expect(trackEvent).to.have.been.calledWith('IdV: test image added', { documentType: 'id', mimeType: 'image/jpeg', source: 'acuant', @@ -690,9 +690,9 @@ describe('document-capture/components/acuant-capture', () => { }); it('removes error message once image is corrected', async () => { - const addPageAction = sinon.spy(); + const trackEvent = sinon.spy(); const { getByText, findByText } = render( - + { fireEvent.click(button); await waitFor(() => !error.textContent); - expect(addPageAction).to.have.been.calledWith('IdV: test image added', { + expect(trackEvent).to.have.been.calledWith('IdV: test image added', { documentType: 'id', mimeType: 'image/jpeg', source: 'acuant', @@ -978,9 +978,9 @@ describe('document-capture/components/acuant-capture', () => { }); it('logs metrics for manual upload', async () => { - const addPageAction = sinon.stub(); + const trackEvent = sinon.stub(); const { getByLabelText } = render( - + @@ -990,7 +990,7 @@ describe('document-capture/components/acuant-capture', () => { const input = getByLabelText('Image'); uploadFile(input, validUpload); - await expect(addPageAction).to.eventually.be.calledWith('IdV: test image added', { + await expect(trackEvent).to.eventually.be.calledWith('IdV: test image added', { height: sinon.match.number, mimeType: 'image/jpeg', source: 'upload', @@ -1001,7 +1001,7 @@ describe('document-capture/components/acuant-capture', () => { }); it('logs clicks', async () => { - const addPageAction = sinon.stub(); + const trackEvent = sinon.stub(); const { getByText, getByLabelText } = render( { } > - + @@ -1029,25 +1029,25 @@ describe('document-capture/components/acuant-capture', () => { const upload = getByText('Upload'); fireEvent.click(upload); - expect(addPageAction).to.have.been.calledThrice(); - expect(addPageAction.getCall(0)).to.have.been.calledWith('IdV: test image clicked', { + expect(trackEvent).to.have.been.calledThrice(); + expect(trackEvent.getCall(0)).to.have.been.calledWith('IdV: test image clicked', { source: 'placeholder', isDrop: false, }); - expect(addPageAction.getCall(1)).to.have.been.calledWith('IdV: test image clicked', { + expect(trackEvent.getCall(1)).to.have.been.calledWith('IdV: test image clicked', { source: 'button', isDrop: false, }); - expect(addPageAction.getCall(2)).to.have.been.calledWith('IdV: test image clicked', { + expect(trackEvent.getCall(2)).to.have.been.calledWith('IdV: test image clicked', { source: 'upload', isDrop: false, }); }); it('logs drag-and-drop as click interaction', () => { - const addPageAction = sinon.stub(); + const trackEvent = sinon.stub(); const { getByLabelText } = render( - + @@ -1057,16 +1057,16 @@ describe('document-capture/components/acuant-capture', () => { const input = getByLabelText('Image'); fireEvent.drop(input); - expect(addPageAction.getCall(0)).to.have.been.calledWith('IdV: test image clicked', { + expect(trackEvent.getCall(0)).to.have.been.calledWith('IdV: test image clicked', { source: 'placeholder', isDrop: true, }); }); it('logs attempts', async () => { - const addPageAction = sinon.stub(); + const trackEvent = sinon.stub(); const { getByLabelText } = render( - + @@ -1076,14 +1076,14 @@ describe('document-capture/components/acuant-capture', () => { const input = getByLabelText('Image'); uploadFile(input, validUpload); - await expect(addPageAction).to.eventually.be.calledWith( + await expect(trackEvent).to.eventually.be.calledWith( 'IdV: test image added', sinon.match({ attempt: 1 }), ); uploadFile(input, validUpload); - await expect(addPageAction).to.eventually.be.calledWith( + await expect(trackEvent).to.eventually.be.calledWith( 'IdV: test image added', sinon.match({ attempt: 2 }), ); diff --git a/spec/javascripts/packages/document-capture/components/capture-advice-spec.jsx b/spec/javascripts/packages/document-capture/components/capture-advice-spec.jsx index 019ea912647..4f8be728db4 100644 --- a/spec/javascripts/packages/document-capture/components/capture-advice-spec.jsx +++ b/spec/javascripts/packages/document-capture/components/capture-advice-spec.jsx @@ -6,15 +6,15 @@ import { render } from '../../../support/document-capture'; describe('document-capture/components/capture-advice', () => { it('logs warning events', async () => { - const addPageAction = sinon.spy(); + const trackEvent = sinon.spy(); const { getByRole } = render( - + {}} /> , ); - expect(addPageAction).to.have.been.calledWith('IdV: warning shown', { + expect(trackEvent).to.have.been.calledWith('IdV: warning shown', { location: 'doc_auth_capture_advice', remaining_attempts: undefined, }); @@ -22,7 +22,7 @@ describe('document-capture/components/capture-advice', () => { const button = getByRole('button'); await userEvent.click(button); - expect(addPageAction).to.have.been.calledWith('IdV: warning action triggered', { + expect(trackEvent).to.have.been.calledWith('IdV: warning action triggered', { location: 'doc_auth_capture_advice', }); }); diff --git a/spec/javascripts/packages/document-capture/components/capture-troubleshooting-spec.jsx b/spec/javascripts/packages/document-capture/components/capture-troubleshooting-spec.jsx index b608a59bf36..35060c7313a 100644 --- a/spec/javascripts/packages/document-capture/components/capture-troubleshooting-spec.jsx +++ b/spec/javascripts/packages/document-capture/components/capture-troubleshooting-spec.jsx @@ -79,17 +79,17 @@ describe('document-capture/context/capture-troubleshooting', () => { }); it('logs events', async () => { - const addPageAction = sinon.spy(); + const trackEvent = sinon.spy(); const { getByRole } = render( - + Default children , ); - expect(addPageAction).to.have.been.calledTwice(); - expect(addPageAction).to.have.been.calledWith('IdV: Capture troubleshooting shown', { + expect(trackEvent).to.have.been.calledTwice(); + expect(trackEvent).to.have.been.calledWith('IdV: Capture troubleshooting shown', { isAssessedAsGlare: false, isAssessedAsBlurry: false, }); @@ -97,7 +97,7 @@ describe('document-capture/context/capture-troubleshooting', () => { const tryAgainButton = getByRole('button', { name: 'idv.failure.button.warning' }); await userEvent.click(tryAgainButton); - expect(addPageAction.callCount).to.equal(4); - expect(addPageAction).to.have.been.calledWith('IdV: Capture troubleshooting dismissed'); + expect(trackEvent.callCount).to.equal(4); + expect(trackEvent).to.have.been.calledWith('IdV: Capture troubleshooting dismissed'); }); }); diff --git a/spec/javascripts/packages/document-capture/components/review-issues-step-spec.jsx b/spec/javascripts/packages/document-capture/components/review-issues-step-spec.jsx index 04fedee1dd4..6c5b3879703 100644 --- a/spec/javascripts/packages/document-capture/components/review-issues-step-spec.jsx +++ b/spec/javascripts/packages/document-capture/components/review-issues-step-spec.jsx @@ -16,15 +16,15 @@ describe('document-capture/components/review-issues-step', () => { const sandbox = useSandbox(); it('logs warning events', async () => { - const addPageAction = sinon.spy(); + const trackEvent = sinon.spy(); const { getByRole } = render( - + , ); - expect(addPageAction).to.have.been.calledWith('IdV: warning shown', { + expect(trackEvent).to.have.been.calledWith('IdV: warning shown', { location: 'doc_auth_review_issues', remaining_attempts: 3, }); @@ -32,7 +32,7 @@ describe('document-capture/components/review-issues-step', () => { const button = getByRole('button'); await userEvent.click(button); - expect(addPageAction).to.have.been.calledWith('IdV: warning action triggered', { + expect(trackEvent).to.have.been.calledWith('IdV: warning action triggered', { location: 'doc_auth_review_issues', }); }); diff --git a/spec/javascripts/packages/document-capture/components/warning-spec.jsx b/spec/javascripts/packages/document-capture/components/warning-spec.jsx index 07d9c1aae87..c8af2f5bf51 100644 --- a/spec/javascripts/packages/document-capture/components/warning-spec.jsx +++ b/spec/javascripts/packages/document-capture/components/warning-spec.jsx @@ -8,10 +8,10 @@ import { render } from '../../../support/document-capture'; describe('document-capture/components/warning', () => { it('renders a warning', async () => { const actionOnClick = sinon.spy(); - const addPageAction = sinon.spy(); + const trackEvent = sinon.spy(); const { getByRole, getByText } = render( - + { , ); - expect(addPageAction).to.have.been.calledWith('IdV: warning shown', { + expect(trackEvent).to.have.been.calledWith('IdV: warning shown', { location: 'example', remaining_attempts: undefined, }); @@ -41,7 +41,7 @@ describe('document-capture/components/warning', () => { expect(getByRole('heading', { name: 'Oops!' })).to.exist(); expect(tryAgainButton).to.exist(); expect(actionOnClick).to.have.been.calledOnce(); - expect(addPageAction).to.have.been.calledWith('IdV: warning action triggered', { + expect(trackEvent).to.have.been.calledWith('IdV: warning action triggered', { location: 'example', }); expect(getByText('Something went wrong')).to.exist(); diff --git a/spec/javascripts/packages/document-capture/context/acuant-spec.jsx b/spec/javascripts/packages/document-capture/context/acuant-spec.jsx index 68170181661..2ce1dc055e4 100644 --- a/spec/javascripts/packages/document-capture/context/acuant-spec.jsx +++ b/spec/javascripts/packages/document-capture/context/acuant-spec.jsx @@ -126,13 +126,13 @@ describe('document-capture/context/acuant', () => { context('successful initialization', () => { let result; - let addPageAction; + let trackEvent; beforeEach(() => { - addPageAction = sinon.spy(); + trackEvent = sinon.spy(); ({ result } = renderHook(() => useContext(AcuantContext), { wrapper: ({ children }) => ( - + {children} @@ -156,7 +156,7 @@ describe('document-capture/context/acuant', () => { }); it('logs', () => { - expect(addPageAction).to.have.been.calledWith('IdV: Acuant SDK loaded', { + expect(trackEvent).to.have.been.calledWith('IdV: Acuant SDK loaded', { success: true, isCameraSupported: true, }); @@ -176,7 +176,7 @@ describe('document-capture/context/acuant', () => { }); it('logs', () => { - expect(addPageAction).to.have.been.calledWith('IdV: Acuant SDK loaded', { + expect(trackEvent).to.have.been.calledWith('IdV: Acuant SDK loaded', { success: true, isCameraSupported: false, }); @@ -186,13 +186,13 @@ describe('document-capture/context/acuant', () => { context('failed initialization', () => { let result; - let addPageAction; + let trackEvent; beforeEach(() => { - addPageAction = sinon.spy(); + trackEvent = sinon.spy(); ({ result } = renderHook(() => useContext(AcuantContext), { wrapper: ({ children }) => ( - + {children} @@ -213,7 +213,7 @@ describe('document-capture/context/acuant', () => { }); it('logs', () => { - expect(addPageAction).to.have.been.calledWith('IdV: Acuant SDK loaded', { + expect(trackEvent).to.have.been.calledWith('IdV: Acuant SDK loaded', { success: false, code: sinon.match.number, description: sinon.match.string, diff --git a/spec/javascripts/packages/document-capture/context/failed-capture-attempts-spec.jsx b/spec/javascripts/packages/document-capture/context/failed-capture-attempts-spec.jsx index f472bcc1972..a19e63e77fd 100644 --- a/spec/javascripts/packages/document-capture/context/failed-capture-attempts-spec.jsx +++ b/spec/javascripts/packages/document-capture/context/failed-capture-attempts-spec.jsx @@ -118,11 +118,11 @@ describe('maxAttemptsBeforeNativeCamera logging tests', () => { * but not both. */ it('calls analytics with native camera message when failed attempts is greater than or equal to 0', async function () { - const addPageAction = sinon.spy(); + const trackEvent = sinon.spy(); const acuantCaptureComponent = ; function TestComponent({ children }) { return ( - + @@ -139,19 +139,19 @@ describe('maxAttemptsBeforeNativeCamera logging tests', () => { const fileInput = result.container.querySelector('input[type="file"]'); expect(fileInput).to.exist(); await user.click(fileInput); - expect(addPageAction).to.have.been.called(); - expect(addPageAction).to.have.been.calledWith( + expect(trackEvent).to.have.been.called(); + expect(trackEvent).to.have.been.calledWith( 'IdV: Native camera forced after failed attempts', { field: 'example', failed_attempts: 0 }, ); }); it('Does not call analytics with native camera message when failed attempts less than 2', async function () { - const addPageAction = sinon.spy(); + const trackEvent = sinon.spy(); const acuantCaptureComponent = ; function TestComponent({ children }) { return ( - + @@ -168,7 +168,7 @@ describe('maxAttemptsBeforeNativeCamera logging tests', () => { const fileInput = result.container.querySelector('input[type="file"]'); expect(fileInput).to.exist(); await user.click(fileInput); - expect(addPageAction).to.not.have.been.calledWith( + expect(trackEvent).to.not.have.been.calledWith( 'IdV: Native camera forced after failed attempts', ); }); diff --git a/spec/javascripts/packages/document-capture/higher-order/with-background-encrypted-upload-spec.jsx b/spec/javascripts/packages/document-capture/higher-order/with-background-encrypted-upload-spec.jsx index 6e848b0c688..b78ad4f0e4c 100644 --- a/spec/javascripts/packages/document-capture/higher-order/with-background-encrypted-upload-spec.jsx +++ b/spec/javascripts/packages/document-capture/higher-order/with-background-encrypted-upload-spec.jsx @@ -132,7 +132,7 @@ describe('document-capture/higher-order/with-background-encrypted-upload', () => describe('upload', () => { async function renderWithResponse(response) { - const addPageAction = sinon.spy(); + const trackEvent = sinon.spy(); const onChange = sinon.spy(); const onError = sinon.spy(); const key = await window.crypto.subtle.generateKey( @@ -145,7 +145,7 @@ describe('document-capture/higher-order/with-background-encrypted-upload', () => ); sandbox.stub(window, 'fetch').callsFake(() => Promise.resolve(response)); render( - + , ); - return { onChange, onError, addPageAction }; + return { onChange, onError, trackEvent }; } context('success', () => { @@ -185,15 +185,15 @@ describe('document-capture/higher-order/with-background-encrypted-upload', () => }); it('logs result', async () => { - const { onChange, addPageAction } = await renderWithResponse(response); + const { onChange, trackEvent } = await renderWithResponse(response); await onChange.getCall(0).args[0].foo_image_url; - expect(addPageAction).to.have.been.calledTwice(); - expect(addPageAction).to.have.been.calledWith( + expect(trackEvent).to.have.been.calledTwice(); + expect(trackEvent).to.have.been.calledWith( 'IdV: document capture async upload encryption', { success: true }, ); - expect(addPageAction).to.have.been.calledWith( + expect(trackEvent).to.have.been.calledWith( 'IdV: document capture async upload submitted', { success: true, trace_id: null, status_code: 200 }, ); @@ -238,7 +238,7 @@ describe('document-capture/higher-order/with-background-encrypted-upload', () => const error = new Error(); sandbox.stub(window.crypto.subtle, 'encrypt').throws(error); sandbox.spy(analytics, 'trackError'); - const { onChange, onError, addPageAction } = await renderWithResponse(response); + const { onChange, onError, trackEvent } = await renderWithResponse(response); const patch = onChange.getCall(0).args[0]; await patch.foo_image_url.catch(() => {}); @@ -246,7 +246,7 @@ describe('document-capture/higher-order/with-background-encrypted-upload', () => sinon.match.instanceOf(BackgroundEncryptedUploadError), { field: 'foo' }, ); - expect(addPageAction).to.have.been.calledWith( + expect(trackEvent).to.have.been.calledWith( 'IdV: document capture async upload encryption', { success: false }, ); @@ -266,15 +266,15 @@ describe('document-capture/higher-order/with-background-encrypted-upload', () => }); it('logs result', async () => { - const { onChange, addPageAction } = await renderWithResponse(response); + const { onChange, trackEvent } = await renderWithResponse(response); await onChange.getCall(0).args[0].foo_image_url.catch(() => {}); - expect(addPageAction).to.have.been.calledTwice(); - expect(addPageAction).to.have.been.calledWith( + expect(trackEvent).to.have.been.calledTwice(); + expect(trackEvent).to.have.been.calledWith( 'IdV: document capture async upload encryption', { success: true }, ); - expect(addPageAction).to.have.been.calledWith( + expect(trackEvent).to.have.been.calledWith( 'IdV: document capture async upload submitted', { success: false, From be566e7dcd95ba07c7856bcf0d26666dee370885 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Wed, 24 Aug 2022 10:41:07 -0400 Subject: [PATCH 2/2] Add README.md for analytics package --- app/javascript/packages/analytics/README.md | 25 +++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 app/javascript/packages/analytics/README.md diff --git a/app/javascript/packages/analytics/README.md b/app/javascript/packages/analytics/README.md new file mode 100644 index 00000000000..372d2939966 --- /dev/null +++ b/app/javascript/packages/analytics/README.md @@ -0,0 +1,25 @@ +# `@18f/identity-analytics` + +Utilities for logging events and errors in the application. + +By default, events logged from the frontend will have their names prefixed with "Frontend:". This +behavior occurs in [`FrontendLogController`][frontend_log_controller.rb]. You can avoid the prefix +by assigning an event mapping method in the controller's `EVENT_MAP` constant. + +[frontend_log_controller.rb]: https://github.com/18F/identity-idp/blob/main/app/controllers/frontend_log_controller.rb + +## Example + +```ts +import { trackEvent, trackError } from '@18f/identity-analytics'; + +button.addEventListener('click', () => { + trackEvent('Button clicked', { success: true }); +}); + +try { + doSomethingRisky(); +} catch (error) { + trackError(error); +} +```