Skip to content

Commit a8a692f

Browse files
fix(mosaic): disable phone verification cancel while pending
1 parent 821215c commit a8a692f

3 files changed

Lines changed: 57 additions & 1 deletion

File tree

‎.changeset/quiet-phones-wait.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/mosaic': patch
3+
---
4+
5+
Disable Cancel while verifying or resending a phone verification code in `UserProfile`.

‎packages/mosaic/src/features/user-profile/user-profile-phone-section/__tests__/user-profile-phone-section.feature.test.tsx‎

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { EnterpriseAccountConnectionJSON, PhoneNumberJSON } from '@clerk/shared/types';
22
import { act, screen, waitFor, within } from '@testing-library/react';
33
import userEvent from '@testing-library/user-event';
4-
import { describe, expect, it } from 'vitest';
4+
import { describe, expect, it, vi } from 'vitest';
55

66
import {
77
demandReverificationOnce,
@@ -143,6 +143,56 @@ describe('the user profile phone section', () => {
143143
expect(await screen.findByRole('button', { name: /Resend \(\d+\)/ })).toBeDisabled();
144144
});
145145

146+
it('disables Cancel while verifying a phone code and allows cancellation after an error', async () => {
147+
const { actor } = await renderSection();
148+
await addPhone(actor, '8015550100');
149+
const attempt = holdRequests('post', '/v1/me/phone_numbers/:id/attempt_verification');
150+
151+
try {
152+
await enterCode(actor, '000000');
153+
await waitFor(() => expect(attempt.requests).toHaveLength(1));
154+
expect(screen.getByRole('button', { name: 'Cancel' })).toBeDisabled();
155+
expect(screen.getByRole('dialog', { name: 'Verify your phone number' })).toBeVisible();
156+
} finally {
157+
attempt.release();
158+
}
159+
160+
expect(await screen.findByText('Incorrect code')).toBeVisible();
161+
const cancel = screen.getByRole('button', { name: 'Cancel' });
162+
expect(cancel).toBeEnabled();
163+
await actor.click(cancel);
164+
await waitFor(() => expect(screen.queryByRole('dialog')).toBeNull());
165+
});
166+
167+
it('disables Cancel while resending a phone code and allows cancellation once sent', async ({ onTestFinished }) => {
168+
vi.useFakeTimers({ shouldAdvanceTime: true });
169+
onTestFinished(() => vi.useRealTimers());
170+
await renderSection();
171+
const actor = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
172+
await addPhone(actor, '8015550100');
173+
await screen.findByRole('textbox', { name: 'Verification code' });
174+
const prepare = holdRequests('post', '/v1/me/phone_numbers/:id/prepare_verification');
175+
176+
try {
177+
for (let second = 0; second < 30; second++) {
178+
await act(() => vi.advanceTimersByTimeAsync(1000));
179+
}
180+
const resend = screen.getByRole('button', { name: /Resend/ });
181+
await waitFor(() => expect(resend).toBeEnabled());
182+
await actor.click(resend);
183+
await waitFor(() => expect(prepare.requests).toHaveLength(1));
184+
expect(screen.getByRole('button', { name: 'Cancel' })).toBeDisabled();
185+
expect(screen.getByRole('dialog', { name: 'Verify your phone number' })).toBeVisible();
186+
} finally {
187+
prepare.release();
188+
}
189+
190+
const cancel = screen.getByRole('button', { name: 'Cancel' });
191+
await waitFor(() => expect(cancel).toBeEnabled());
192+
await actor.click(cancel);
193+
await waitFor(() => expect(screen.queryByRole('dialog')).toBeNull());
194+
});
195+
146196
it('verifies a number that was left unverified', async () => {
147197
const pending = fapiPhoneNumber({ id: 'idn_new', phone_number: '+18015550199' });
148198
const { actor } = await renderSection(signedIn([HOME, pending]));

‎packages/mosaic/src/features/user-profile/user-profile-phone.steps.tsx‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ export function VerifyPhoneStep(props: VerifyPhoneStepProps) {
183183
<Card.Footer>
184184
{props.secondaryAction ?? (
185185
<Dialog.Close
186+
disabled={props.isPending || props.isResending}
186187
render={
187188
<Button
188189
variant='outline'

0 commit comments

Comments
 (0)