-
Notifications
You must be signed in to change notification settings - Fork 155
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Emit error events for Alerts (#1328)
- Loading branch information
1 parent
eda0501
commit 97e4f0c
Showing
3 changed files
with
280 additions
and
1 deletion.
There are no files selected for viewing
55 changes: 55 additions & 0 deletions
55
pages/funnel-analytics/with-error-alert-in-wizard.page.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
import React, { useState } from 'react'; | ||
import Wizard, { WizardProps } from '~components/wizard'; | ||
|
||
import { i18nStrings } from '../wizard/common'; | ||
import Alert from '~components/alert'; | ||
import Container from '~components/container'; | ||
import Header from '~components/header'; | ||
|
||
export default function WizardPage() { | ||
const [errorMode, setErrorMode] = useState(0); | ||
|
||
const steps: WizardProps.Step[] = [ | ||
{ | ||
title: 'Step 1', | ||
content: ( | ||
<div> | ||
<div>Content 1</div> | ||
<div>{errorMode === 1 && <Alert type="error">This is an error on the step level</Alert>}</div> | ||
<div> | ||
{errorMode === 2 && ( | ||
<Container header={<Header>A container around the alert</Header>}> | ||
<Alert type="error">This is an error on the substep level</Alert> | ||
</Container> | ||
)} | ||
</div> | ||
<div> | ||
{errorMode === 3 && ( | ||
<> | ||
<Alert type="error">This is an error on the step level</Alert> | ||
<Container header={<Header>A container around the alert</Header>}> | ||
<Alert type="error">This is an error on the substep level</Alert> | ||
</Container> | ||
</> | ||
)} | ||
</div> | ||
</div> | ||
), | ||
}, | ||
{ | ||
title: 'Step 2', | ||
content: <div>Content 2</div>, | ||
}, | ||
]; | ||
|
||
return ( | ||
<Wizard | ||
steps={steps} | ||
i18nStrings={i18nStrings} | ||
activeStepIndex={0} | ||
onNavigate={() => setErrorMode((errorMode + 1) % 4)} | ||
/> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
import React from 'react'; | ||
import { act, render } from '@testing-library/react'; | ||
|
||
import { FunnelMetrics } from '../../../lib/components/internal/analytics'; | ||
import { useFunnel } from '../../../lib/components/internal/analytics/hooks/use-funnel'; | ||
import Alert from '../../../lib/components/alert'; | ||
|
||
import { | ||
AnalyticsFunnel, | ||
AnalyticsFunnelStep, | ||
AnalyticsFunnelSubStep, | ||
} from '../../../lib/components/internal/analytics/components/analytics-funnel'; | ||
|
||
import { mockFunnelMetrics } from '../../internal/analytics/__tests__/mocks'; | ||
|
||
describe('Alert Analytics', () => { | ||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
mockFunnelMetrics(); | ||
}); | ||
|
||
test('sends funnelSubStepError metric when the alert is placed inside a substep', () => { | ||
render( | ||
<AnalyticsFunnel funnelType="single-page" optionalStepNumbers={[]} totalFunnelSteps={1}> | ||
<AnalyticsFunnelStep stepNumber={2} stepNameSelector=".step-name-selector"> | ||
<AnalyticsFunnelSubStep> | ||
<Alert type="error">This is the error text</Alert> | ||
</AnalyticsFunnelSubStep> | ||
</AnalyticsFunnelStep> | ||
</AnalyticsFunnel> | ||
); | ||
|
||
expect(FunnelMetrics.funnelError).not.toHaveBeenCalled(); | ||
|
||
expect(FunnelMetrics.funnelSubStepError).toHaveBeenCalledTimes(1); | ||
expect(FunnelMetrics.funnelSubStepError).toHaveBeenCalledWith( | ||
expect.objectContaining({ | ||
funnelInteractionId: 'mocked-funnel-id', | ||
subStepSelector: expect.any(String), | ||
stepNameSelector: expect.any(String), | ||
subStepAllSelector: expect.any(String), | ||
subStepNameSelector: expect.any(String), | ||
}) | ||
); | ||
}); | ||
|
||
test('sends funnelError metric when the alert is placed inside a step', () => { | ||
render( | ||
<AnalyticsFunnel funnelType="single-page" optionalStepNumbers={[]} totalFunnelSteps={1}> | ||
<AnalyticsFunnelStep stepNumber={2} stepNameSelector=".step-name-selector"> | ||
<Alert type="error">This is the error text</Alert> | ||
</AnalyticsFunnelStep> | ||
</AnalyticsFunnel> | ||
); | ||
|
||
expect(FunnelMetrics.funnelSubStepError).not.toHaveBeenCalled(); | ||
|
||
expect(FunnelMetrics.funnelError).toHaveBeenCalledTimes(1); | ||
expect(FunnelMetrics.funnelError).toHaveBeenCalledWith( | ||
expect.objectContaining({ | ||
funnelInteractionId: 'mocked-funnel-id', | ||
}) | ||
); | ||
}); | ||
|
||
test('does not send any error metric when the alert is invisible', () => { | ||
render( | ||
<AnalyticsFunnel funnelType="single-page" optionalStepNumbers={[]} totalFunnelSteps={1}> | ||
<AnalyticsFunnelStep stepNumber={2} stepNameSelector=".step-name-selector"> | ||
<AnalyticsFunnelSubStep> | ||
<Alert type="error" visible={false}> | ||
This is the error text | ||
</Alert> | ||
</AnalyticsFunnelSubStep> | ||
</AnalyticsFunnelStep> | ||
</AnalyticsFunnel> | ||
); | ||
|
||
expect(FunnelMetrics.funnelSubStepError).not.toHaveBeenCalled(); | ||
expect(FunnelMetrics.funnelError).not.toHaveBeenCalled(); | ||
}); | ||
|
||
test('does not send any error metrics for non-error alerts', () => { | ||
render( | ||
<AnalyticsFunnel funnelType="single-page" optionalStepNumbers={[]} totalFunnelSteps={1}> | ||
<AnalyticsFunnelStep stepNumber={2} stepNameSelector=".step-name-selector"> | ||
<AnalyticsFunnelSubStep> | ||
<Alert>Default</Alert> | ||
<Alert type="info">Info</Alert> | ||
<Alert type="success">Success</Alert> | ||
<Alert type="warning">Warning</Alert> | ||
</AnalyticsFunnelSubStep> | ||
</AnalyticsFunnelStep> | ||
</AnalyticsFunnel> | ||
); | ||
|
||
expect(FunnelMetrics.funnelSubStepError).not.toHaveBeenCalled(); | ||
expect(FunnelMetrics.funnelError).not.toHaveBeenCalled(); | ||
}); | ||
|
||
test('sends a funnelSubStepError metric when there is an error and the user attempts to submit the form', () => { | ||
let funnelNextOrSubmitAttempt: undefined | (() => void) = undefined; | ||
|
||
const ChildComponent = () => { | ||
funnelNextOrSubmitAttempt = useFunnel().funnelNextOrSubmitAttempt; | ||
return <></>; | ||
}; | ||
|
||
const jsx = ( | ||
<AnalyticsFunnel funnelType="single-page" optionalStepNumbers={[]} totalFunnelSteps={1}> | ||
<AnalyticsFunnelStep stepNumber={2} stepNameSelector=".step-name-selector"> | ||
<AnalyticsFunnelSubStep> | ||
<Alert type="error">This is the error text</Alert> | ||
</AnalyticsFunnelSubStep> | ||
|
||
<ChildComponent /> | ||
</AnalyticsFunnelStep> | ||
</AnalyticsFunnel> | ||
); | ||
|
||
const { rerender } = render(jsx); | ||
expect(FunnelMetrics.funnelSubStepError).toHaveBeenCalledTimes(1); | ||
|
||
act(() => funnelNextOrSubmitAttempt!()); | ||
rerender(jsx); | ||
|
||
expect(FunnelMetrics.funnelSubStepError).toHaveBeenCalledTimes(2); | ||
}); | ||
|
||
test('does not send any error metrics when outside of a funnel context', () => { | ||
render(<Alert type="error">This is the error text</Alert>); | ||
expect(FunnelMetrics.funnelSubStepError).not.toHaveBeenCalled(); | ||
expect(FunnelMetrics.funnelError).not.toHaveBeenCalled(); | ||
}); | ||
|
||
test('does not send multiple funnelSubStepError metrics on rerender', () => { | ||
const { rerender } = render( | ||
<AnalyticsFunnel funnelType="single-page" optionalStepNumbers={[]} totalFunnelSteps={1}> | ||
<AnalyticsFunnelStep stepNumber={2} stepNameSelector=".step-name-selector"> | ||
<AnalyticsFunnelSubStep> | ||
<Alert type="error">This is the error text</Alert> | ||
</AnalyticsFunnelSubStep> | ||
</AnalyticsFunnelStep> | ||
</AnalyticsFunnel> | ||
); | ||
|
||
rerender( | ||
<AnalyticsFunnel funnelType="single-page" optionalStepNumbers={[]} totalFunnelSteps={1}> | ||
<AnalyticsFunnelStep stepNumber={2} stepNameSelector=".step-name-selector"> | ||
<AnalyticsFunnelSubStep> | ||
<Alert type="error">This is the error text</Alert> | ||
</AnalyticsFunnelSubStep> | ||
</AnalyticsFunnelStep> | ||
</AnalyticsFunnel> | ||
); | ||
|
||
expect(FunnelMetrics.funnelSubStepError).toHaveBeenCalledTimes(1); | ||
expect(FunnelMetrics.funnelError).not.toHaveBeenCalled(); | ||
}); | ||
|
||
test('does not send multiple funnelError metrics on rerender', () => { | ||
const { rerender } = render( | ||
<AnalyticsFunnel funnelType="single-page" optionalStepNumbers={[]} totalFunnelSteps={1}> | ||
<AnalyticsFunnelStep stepNumber={2} stepNameSelector=".step-name-selector"> | ||
<Alert type="error">This is the error text</Alert> | ||
</AnalyticsFunnelStep> | ||
</AnalyticsFunnel> | ||
); | ||
|
||
rerender( | ||
<AnalyticsFunnel funnelType="single-page" optionalStepNumbers={[]} totalFunnelSteps={1}> | ||
<AnalyticsFunnelStep stepNumber={2} stepNameSelector=".step-name-selector"> | ||
<Alert type="error">This is the error text</Alert> | ||
</AnalyticsFunnelStep> | ||
</AnalyticsFunnel> | ||
); | ||
|
||
expect(FunnelMetrics.funnelError).toHaveBeenCalledTimes(1); | ||
expect(FunnelMetrics.funnelSubStepError).not.toHaveBeenCalled(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters