Skip to content

Commit 32f10ff

Browse files
authored
Merge pull request #20406 from mozilla/fxa-13419
fix(settings): label delete-account button destructively for password less account
2 parents a80ff65 + 3c02b2e commit 32f10ff

5 files changed

Lines changed: 113 additions & 13 deletions

File tree

packages/functional-tests/pages/settings/deleteAccount.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ export class DeleteAccountPage extends SettingsLayout {
2424
return this.page.getByRole('button', { name: 'Continue' });
2525
}
2626

27+
get passwordlessDeleteButton() {
28+
return this.page.getByRole('button', { name: 'Delete account' });
29+
}
30+
2731
get step2Heading() {
2832
return this.page.getByRole('heading', { name: 'Step 2 of 2' });
2933
}

packages/functional-tests/tests/settings/deleteAccount.spec.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,49 @@ test.describe('severity-1 #smoke', () => {
9595
await expect(deleteAccount.tooltip).toHaveText('Incorrect password');
9696
});
9797

98+
test('passwordless account sees "Delete account" button (not "Continue") and can delete without password step', async ({
99+
target,
100+
pages: { page, signin, signinPasswordlessCode, settings, deleteAccount },
101+
testAccountTracker,
102+
}) => {
103+
// Passwordless accounts skip Step 2 (password entry). The Step 1 button
104+
// must therefore read "Delete account" with a destructive style, not
105+
// "Continue", since clicking it deletes immediately.
106+
const { email } = await testAccountTracker.signUpPasswordless();
107+
108+
await signin.clearCache();
109+
await page.goto(
110+
`${target.contentServerUrl}/signin?email=${encodeURIComponent(email)}`
111+
);
112+
await page.waitForURL(/signin_passwordless_code/);
113+
114+
const code = await target.emailClient.getPasswordlessSigninCode(email);
115+
await signinPasswordlessCode.fillOutCodeForm(code);
116+
await expect(settings.settingsHeading).toBeVisible();
117+
118+
await settings.deleteAccountButton.click();
119+
120+
await expect(deleteAccount.deleteAccountHeading).toBeVisible();
121+
// Passwordless accounts have no Step 1 of 2 subtitle
122+
await expect(deleteAccount.step1Heading).toBeHidden();
123+
// The primary "Continue" button must not exist for passwordless accounts
124+
await expect(deleteAccount.continueButton).toBeHidden();
125+
126+
await deleteAccount.checkAllBoxes();
127+
128+
// Assert the button reads "Delete account" and uses the destructive style
129+
await expect(deleteAccount.passwordlessDeleteButton).toBeVisible();
130+
await expect(deleteAccount.passwordlessDeleteButton).toBeEnabled();
131+
await expect(deleteAccount.passwordlessDeleteButton).toHaveClass(
132+
/cta-caution/
133+
);
134+
135+
await deleteAccount.passwordlessDeleteButton.click();
136+
137+
// Account should be deleted immediately without prompting for a password
138+
await expect(page.getByText('Account deleted successfully')).toBeVisible();
139+
});
140+
98141
test('delete account A then sign in with account B, no apollo cache pollution', async ({
99142
target,
100143
pages: { page, deleteAccount, settings, signin },

packages/fxa-settings/src/components/Settings/PageDeleteAccount/en.ftl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ delete-account-chk-box-4 =
3030
3131
3232
delete-account-continue-button = Continue
33+
delete-account-delete-button-passwordless = Delete account
3334
3435
delete-account-password-input =
3536
.label = Enter password

packages/fxa-settings/src/components/Settings/PageDeleteAccount/index.test.tsx

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ import { typeByTestIdFn } from '../../../lib/test-utils';
2222
import { Account, AppContext } from '../../../models';
2323
import { MOCK_EMAIL } from '../../../pages/mocks';
2424
import GleanMetrics from '../../../lib/glean';
25-
import { discardSessionToken, clearSignedInAccountUid } from '../../../lib/cache';
25+
import {
26+
discardSessionToken,
27+
clearSignedInAccountUid,
28+
} from '../../../lib/cache';
2629

2730
jest.mock('../../../lib/cache', () => ({
2831
...jest.requireActual('../../../lib/cache'),
@@ -266,10 +269,42 @@ describe('PageDeleteAccount', () => {
266269
})
267270
);
268271
expect(GleanMetrics.deleteAccount.engage).toHaveBeenCalled();
269-
await userEvent.click(screen.getByRole('button', { name: 'Continue' }));
272+
await userEvent.click(
273+
screen.getByRole('button', { name: 'Delete account' })
274+
);
270275
expect(GleanMetrics.deleteAccount.submit).toHaveBeenCalled();
271276
expect(GleanMetrics.deleteAccount.passwordView).not.toHaveBeenCalled();
272277
});
273278
});
274279
});
280+
281+
describe('step 1 action button', () => {
282+
it('renders as "Continue" with primary style when account has a password', () => {
283+
renderWithRouter(
284+
<AppContext.Provider value={mockAppContext({ account, session })}>
285+
<PageDeleteAccount />
286+
</AppContext.Provider>
287+
);
288+
289+
const button = screen.getByTestId('continue-button');
290+
expect(button.textContent).toContain('Continue');
291+
expect(button).toHaveClass('cta-primary');
292+
expect(button).not.toHaveClass('cta-caution');
293+
});
294+
295+
it('renders as "Delete account" with caution style when account has no password', () => {
296+
renderWithRouter(
297+
<AppContext.Provider
298+
value={mockAppContext({ account: pwdlessAccount, session })}
299+
>
300+
<PageDeleteAccount />
301+
</AppContext.Provider>
302+
);
303+
304+
const button = screen.getByTestId('continue-button');
305+
expect(button.textContent).toContain('Delete account');
306+
expect(button).toHaveClass('cta-caution');
307+
expect(button).not.toHaveClass('cta-primary');
308+
});
309+
});
275310
});

packages/fxa-settings/src/components/Settings/PageDeleteAccount/index.tsx

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ import { AuthUiErrors } from '../../../lib/auth-errors/auth-errors';
1919
import { getLocalizedErrorMessage } from '../../../lib/error-utils';
2020
import GleanMetrics from '../../../lib/glean';
2121
import { useFtlMsgResolver } from '../../../models/hooks';
22-
import { clearSignedInAccountUid, discardSessionToken, setSigningOut } from '../../../lib/cache';
22+
import {
23+
clearSignedInAccountUid,
24+
discardSessionToken,
25+
setSigningOut,
26+
} from '../../../lib/cache';
2327

2428
type FormData = {
2529
password: string;
@@ -253,16 +257,29 @@ export const PageDeleteAccount = (_: RouteComponentProps) => {
253257
Cancel
254258
</button>
255259
</Localized>
256-
<Localized id="delete-account-continue-button">
257-
<button
258-
className="cta-primary mx-2 px-10 py-2"
259-
disabled={!allBoxesChecked}
260-
onClick={() => advanceStep()}
261-
data-testid="continue-button"
262-
>
263-
Continue
264-
</button>
265-
</Localized>
260+
{account.hasPassword ? (
261+
<Localized id="delete-account-continue-button">
262+
<button
263+
className="cta-primary mx-2 px-10 py-2"
264+
disabled={!allBoxesChecked}
265+
onClick={() => advanceStep()}
266+
data-testid="continue-button"
267+
>
268+
Continue
269+
</button>
270+
</Localized>
271+
) : (
272+
<Localized id="delete-account-delete-button-passwordless">
273+
<button
274+
className="cta-caution mx-2 px-10 py-2"
275+
disabled={!allBoxesChecked}
276+
onClick={() => advanceStep()}
277+
data-testid="continue-button"
278+
>
279+
Delete account
280+
</button>
281+
</Localized>
282+
)}
266283
</div>
267284
</div>
268285
)}

0 commit comments

Comments
 (0)