storybook(fxa-settings): recreate ResetPasswordWithRecoveryKeyVerified

Because:

* We're recreating the remaining content-server views in React and
  moving them into Storybook before they go live

This commit:

* Recreates reset_password_with_recovery_key_verified in React with
  metrics, tests, and l10n

Closes #https://mozilla-hub.atlassian.net/browse/FXA-6345
This commit is contained in:
Mill 2022-12-07 11:22:29 -08:00
Родитель 37f5f7d4ea
Коммит eb5bd7d6be
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 191635F49DE50466
10 изменённых файлов: 213 добавлений и 40 удалений

Просмотреть файл

@ -130,15 +130,29 @@ const EVENTS = {
},
// Reset password confirmation
'screen.settings.reset-password-confirmed': {
group: GROUPS.settings,
'screen.reset-password-confirmed': {
group: GROUPS.login,
event: 'reset_password_confirmed_view',
},
'flow.settings.reset-password-confirmed.continue': {
group: GROUPS.settings,
'flow.reset-password-confirmed.continue': {
group: GROUPS.login,
event: 'reset_password_confirmed_continue',
},
// Reset password with recovery key verified
'screen.reset-password-with-recovery-key-verified': {
group: GROUPS.login,
event: 'reset_password_with_recovery_key_verified_view',
},
'flow.reset-password-with-recovery-key-verified.generate-new-key': {
group: GROUPS.login,
event: 'reset_password_with_recovery_key_verified_generate_new_key',
},
'flow.reset-password-with-recovery-key-verified.continue-to-account': {
group: GROUPS.login,
event: 'reset_password_with_recovery_key_verified_continue_to_account',
},
// Save account recovery key
'screen.save-recovery-key': {
group: GROUPS.activity,

Просмотреть файл

@ -2040,6 +2040,32 @@ registerSuite('amplitude', {
assert.equal(arg.event_properties.email_type, 'login');
},
'screen.reset-password-with-recovery-key-verified': () => {
createAmplitudeEvent('screen.reset-password-with-recovery-key-verified');
assert.equal(
logger.info.args[0][1].event_type,
'fxa_login - reset_password_with_recovery_key_verified_view'
);
},
'flow.reset-password-with-recovery-key-verified.generate-new-key': () => {
createAmplitudeEvent(
'flow.reset-password-with-recovery-key-verified.generate-new-key'
);
assert.equal(
logger.info.args[0][1].event_type,
'fxa_login - reset_password_with_recovery_key_verified_generate_new_key'
);
},
'flow.reset-password-with-recovery-key-verified.continue-to-account':
() => {
createAmplitudeEvent(
'flow.reset-password-with-recovery-key-verified.continue-to-account'
);
assert.equal(
logger.info.args[0][1].event_type,
'fxa_login - reset_password_with_recovery_key_verified_continue_to_account'
);
},
'verify-email.verification.clicked': () => {
amplitude(
{

Просмотреть файл

@ -14,8 +14,7 @@ jest.mock('../../lib/metrics', () => ({
describe('Ready', () => {
const customServiceName = 'Example Service';
const viewName = 'example.my-page';
const actionName = 'my_page';
const viewName = 'example-view';
it('renders as expected with default values', () => {
render(<Ready viewName={viewName} />);
@ -27,7 +26,8 @@ describe('Ready', () => {
'Youre now ready to use Account Settings'
);
const passwordResetContinueButton = screen.queryByText('Continue');
// Calling `getByText` will fail if the first two elements aren't in the document,
// but we test anyway to make the intention of the test explicit
expect(passwordResetContinueButton).not.toBeInTheDocument();
expect(passwordResetConfirmation).toBeInTheDocument();
expect(serviceAvailabilityConfirmation).toBeInTheDocument();
@ -44,7 +44,8 @@ describe('Ready', () => {
`Youre now ready to use ${customServiceName}`
);
const passwordResetContinueButton = screen.queryByText('Continue');
// Calling `getByText` will fail if these elements aren't in the document,
// but we test anyway to make the intention of the test explicit
expect(passwordResetContinueButton).not.toBeInTheDocument();
expect(passwordResetConfirmation).toBeInTheDocument();
expect(serviceAvailabilityConfirmation).toBeInTheDocument();
@ -68,7 +69,8 @@ describe('Ready', () => {
`Youre now ready to use ${customServiceName}`
);
const passwordResetContinueButton = screen.getByText('Continue');
// Calling `getByText` would fail if these elements weren't in the document,
// but we test anyway to make the intention of the test explicit
expect(passwordResetContinueButton).toBeInTheDocument();
expect(passwordResetConfirmation).toBeInTheDocument();
expect(serviceAvailabilityConfirmation).toBeInTheDocument();
@ -85,7 +87,6 @@ describe('Ready', () => {
render(
<Ready
viewName={viewName}
baseActionName={actionName}
serviceName={customServiceName}
continueHandler={() => {
console.log('beepboop');
@ -93,8 +94,8 @@ describe('Ready', () => {
/>
);
const passwordResetContinueButton = screen.getByText('Continue');
const clickViewName = `${viewName}.continue`;
const fullActionName = `${actionName}_continue`;
const clickViewName = `${viewName}`;
const fullActionName = `${viewName}.continue`;
fireEvent.click(passwordResetContinueButton);
expect(logViewEvent).toHaveBeenCalledWith(clickViewName, fullActionName, {
entrypoint_variation: 'react',

Просмотреть файл

@ -11,13 +11,11 @@ import { logViewEvent, usePageViewEvent } from '../../lib/metrics';
type ReadyProps = {
serviceName?: string;
continueHandler?: Function;
baseActionName?: string;
viewName: string;
};
const Ready = ({
serviceName = 'Account Settings',
baseActionName,
continueHandler,
viewName,
}: ReadyProps & RouteComponentProps) => {
@ -29,7 +27,7 @@ const Ready = ({
<>
<div className="mb-4">
<Localized id="ready-confirmation">
<h1 className="text-xl mb-2">Your password has been reset</h1>
<h1 className="card-header">Your password has been reset</h1>
</Localized>
</div>
<div className="flex justify-center mx-auto">
@ -48,9 +46,8 @@ const Ready = ({
type="submit"
className="cta-primary cta-base-p font-bold mx-2 flex-1"
onClick={(e) => {
const logViewName = `${viewName}.continue`;
const logActionName = `${baseActionName}_continue`;
logViewEvent(logViewName, logActionName, {
const eventName = `${viewName}.continue`;
logViewEvent(viewName, eventName, {
entrypoint_variation: 'react',
});
continueHandler(e);

Просмотреть файл

@ -15,7 +15,8 @@ jest.mock('../../lib/metrics', () => ({
describe('ResetPasswordConfirmed', () => {
it('renders Ready component as expected', () => {
render(<ResetPasswordConfirmed />);
// Calling `getByText` will fail if these elements aren't in the document,
// but we test anyway to make the intention of the test explicit
const passwordResetConfirmation = screen.getByText(
'Your password has been reset'
);
@ -23,23 +24,19 @@ describe('ResetPasswordConfirmed', () => {
'Youre now ready to use Account Settings'
);
const passwordResetContinueButton = screen.queryByText('Continue');
expect(passwordResetContinueButton).not.toBeInTheDocument();
expect(passwordResetConfirmation).toBeInTheDocument();
expect(serviceAvailabilityConfirmation).toBeInTheDocument();
});
it('emits the expected metrics on render', async () => {
it('emits the expected metrics on render', () => {
render(<ResetPasswordConfirmed />);
expect(usePageViewEvent).toHaveBeenCalledWith(
'settings.reset-password-confirmed',
{
entrypoint_variation: 'react',
}
);
expect(usePageViewEvent).toHaveBeenCalledWith('reset-password-confirmed', {
entrypoint_variation: 'react',
});
});
it('emits the expected metrics when a user clicks `Continue`', async () => {
it('emits the expected metrics when a user clicks `Continue`', () => {
render(
<ResetPasswordConfirmed
continueHandler={() => {
@ -51,8 +48,8 @@ describe('ResetPasswordConfirmed', () => {
fireEvent.click(passwordResetContinueButton);
expect(logViewEvent).toHaveBeenCalledWith(
'settings.reset-password-confirmed.continue',
'reset_password_confirmed_continue',
'reset-password-confirmed',
'reset-password-confirmed.continue',
{
entrypoint_variation: 'react',
}

Просмотреть файл

@ -16,17 +16,9 @@ const ResetPasswordConfirmed = ({
serviceName,
}: ResetPasswordConfirmedProps & RouteComponentProps) => {
// This is pretty ridiculously barebones, but the content in here gets expanded on other, similar views.
const viewName = 'settings.reset-password-confirmed';
const baseActionName = 'reset_password_confirmed';
const viewName = 'reset-password-confirmed';
return (
<Ready
continueHandler={continueHandler}
viewName={viewName}
baseActionName={baseActionName}
serviceName={serviceName}
/>
);
return <Ready {...{ continueHandler, viewName, serviceName }} />;
};
export default ResetPasswordConfirmed;

Просмотреть файл

@ -0,0 +1,2 @@
reset-password-with-recovery-key-verified-generate-new-key = Generate a new account recovery key
reset-password-with-recovery-key-verified-continue-to-account = Continue to my account

Просмотреть файл

@ -0,0 +1,22 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from 'react';
import ResetPasswordWithRecoveryKeyVerified from '.';
import AppLayout from '../../components/AppLayout';
import { LocationProvider } from '@reach/router';
import { Meta } from '@storybook/react';
export default {
title: 'pages/ResetPasswordWithRecoveryKeyVerified',
component: ResetPasswordWithRecoveryKeyVerified,
} as Meta;
export const Default = () => (
<LocationProvider>
<AppLayout>
<ResetPasswordWithRecoveryKeyVerified />
</AppLayout>
</LocationProvider>
);

Просмотреть файл

@ -0,0 +1,64 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from 'react';
import { fireEvent, screen } from '@testing-library/react';
import { renderWithRouter } from '../../models/mocks';
import { getFtlBundle, testAllL10n } from 'fxa-react/lib/test-utils';
import { FluentBundle } from '@fluent/bundle';
import ResetPasswordWithRecoveryKeyVerified from '.';
import { logViewEvent } from '../../lib/metrics';
jest.mock('../../lib/metrics', () => ({
logViewEvent: jest.fn(),
usePageViewEvent: jest.fn(),
}));
describe('ResetPasswordWithRecoveryKeyVerified', () => {
let bundle: FluentBundle;
beforeAll(async () => {
bundle = await getFtlBundle('settings');
});
it('renders default content as expected', () => {
renderWithRouter(<ResetPasswordWithRecoveryKeyVerified />);
testAllL10n(screen, bundle);
const newAccountRecoveryKeyButton = screen.getByText(
'Generate a new account recovery key'
);
const continueToAccountLink = screen.getByText('Continue to my account');
// Calling `getByText` will fail if these elements aren't in the document,
// but we test anyway to make the intention of the test explicit
expect(newAccountRecoveryKeyButton).toBeInTheDocument();
expect(continueToAccountLink).toBeInTheDocument();
});
it('emits the expected metrics when a user generates new recovery keys', async () => {
renderWithRouter(<ResetPasswordWithRecoveryKeyVerified />);
const newAccountRecoveryKeyButton = screen.getByText(
'Generate a new account recovery key'
);
fireEvent.click(newAccountRecoveryKeyButton);
expect(logViewEvent).toHaveBeenCalledWith(
'reset-password-with-recovery-key-verified',
'reset-password-with-recovery-key-verified.generate-new-key',
{
entrypoint_variation: 'react',
}
);
});
it('emits the expected metrics when a user continues to their account', async () => {
renderWithRouter(<ResetPasswordWithRecoveryKeyVerified />);
const continueToAccountLink = screen.getByText('Continue to my account');
fireEvent.click(continueToAccountLink);
expect(logViewEvent).toHaveBeenCalledWith(
'reset-password-with-recovery-key-verified',
'reset-password-with-recovery-key-verified.continue-to-account',
{
entrypoint_variation: 'react',
}
);
});
});

Просмотреть файл

@ -0,0 +1,58 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from 'react';
import { RouteComponentProps, useNavigate } from '@reach/router';
import { FtlMsg } from 'fxa-react/lib/utils';
import { logViewEvent } from '../../lib/metrics';
import Ready from '../../components/Ready';
type ResetPasswordWithRecoveryKeyVerifiedProps = {
serviceName?: string;
};
const ResetPasswordWithRecoveryKeyVerified = ({
serviceName,
}: ResetPasswordWithRecoveryKeyVerifiedProps & RouteComponentProps) => {
const navigate = useNavigate();
const viewName = 'reset-password-with-recovery-key-verified';
return (
<>
<Ready {...{ viewName, serviceName }} />
<div className="flex justify-center mx-auto m-6">
<button
className="cta-primary cta-xl"
onClick={() => {
const eventName = `${viewName}.generate-new-key`;
logViewEvent(viewName, eventName, {
entrypoint_variation: 'react',
});
navigate('/settings/account_recovery');
}}
>
<FtlMsg id="reset-password-with-recovery-key-verified-generate-new-key">
Generate a new account recovery key
</FtlMsg>
</button>
</div>
<button
className="link-blue text-sm"
onClick={() => {
const eventName = `${viewName}.continue-to-account`;
logViewEvent(viewName, eventName, {
entrypoint_variation: 'react',
});
navigate('/settings');
}}
>
<FtlMsg id="reset-password-with-recovery-key-verified-continue-to-account">
Continue to my account
</FtlMsg>
</button>
</>
);
};
export default ResetPasswordWithRecoveryKeyVerified;