Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/mosaic-add-contact-empty-error.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/mosaic': patch
---

Show the generic error message in the `UserProfile` add email and add phone dialogs when sending a verification fails with a Clerk response that carries no error details. Previously these dialogs showed no message.
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import type { EmailAddressJSON, EnterpriseAccountConnectionJSON } from '@clerk/shared/types';
import { act, screen, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { http, HttpResponse } from 'msw';
import { describe, expect, it, vi } from 'vitest';

import {
demandReverificationOnce,
type FakeFapiSeed,
fapiUrl,
holdRequests,
serveFapi,
VERIFICATION_CODE,
verifyEmailOutOfBand,
worker,
} from '../../../../__tests__/feature/fake-fapi';
import {
fapiClient,
Expand Down Expand Up @@ -83,6 +86,21 @@ describe('the user profile email section', () => {
expect(emailRow()).not.toHaveTextContent('Unverified');
});

it('says something went wrong when sending the code fails without details', async () => {
const { actor } = await renderSection(signedInWithEmails([]));
worker.use(
http.post(fapiUrl('/v1/me/email_addresses/:id/prepare_verification'), () =>
HttpResponse.json({ errors: [] }, { status: 500 }),
),
);

await actor.click(screen.getByRole('button', { name: 'Add email' }));
await actor.type(screen.getByRole('textbox', { name: 'Email' }), 'new@example.com');
await actor.click(screen.getByRole('button', { name: 'Continue' }));

expect(await screen.findByText('Something went wrong. Please try again.')).toBeInTheDocument();
});

it('holds the code field until the code has been sent', async () => {
const { actor } = await renderSection(signedInWithEmails([]));
const prepare = holdRequests('post', '/v1/me/email_addresses/:id/prepare_verification');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import type { EnterpriseAccountConnectionJSON, PhoneNumberJSON } from '@clerk/shared/types';
import { act, screen, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { http, HttpResponse } from 'msw';
import { describe, expect, it } from 'vitest';

import {
demandReverificationOnce,
type FakeFapiSeed,
fapiUrl,
holdRequests,
serveFapi,
VERIFICATION_CODE,
worker,
} from '../../../../__tests__/feature/fake-fapi';
import type { FapiAttributeOverrides } from '../../../../__tests__/feature/fapi';
import {
Expand Down Expand Up @@ -125,6 +128,19 @@ describe('the user profile phone section', () => {
expect(screen.getByText('+49')).toBeInTheDocument();
});

it('says something went wrong when sending the code fails without details', async () => {
const { actor } = await renderSection();
worker.use(
http.post(fapiUrl('/v1/me/phone_numbers/:id/prepare_verification'), () =>
HttpResponse.json({ errors: [] }, { status: 500 }),
),
);

await addPhone(actor, '8015550100');

expect(await screen.findByText('Something went wrong. Please try again.')).toBeInTheDocument();
});

it('keeps the dialog open and says the code was wrong', async () => {
const { actor } = await renderSection();

Expand Down
8 changes: 7 additions & 1 deletion packages/mosaic/src/utils/__tests__/errors.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { ReactNode } from 'react';
import { describe, expect, it, vi } from 'vitest';

import { isLocalizableError, MosaicLocalizationProvider, resolveLocalization, useErrorText } from '../../localization';
import { save, SaveError, toLocalizableError, toLocalizableErrors } from '../errors';
import { save, SaveError, toFormError, toLocalizableError, toLocalizableErrors, UNEXPECTED_ERROR } from '../errors';

function errorText() {
const wrapper = ({ children }: { children: ReactNode }) => (
Expand Down Expand Up @@ -32,6 +32,12 @@ describe('save', () => {
});
});

describe('toFormError', () => {
it('reads a save that carried nothing as the generic error', () => {
expect(toFormError(new SaveError({})).global).toEqual(UNEXPECTED_ERROR);
});
});

describe('toLocalizableError', () => {
it('reads the global error a save left, code and all', () => {
const cause = new SaveError({ global: { code: 'action_blocked', message: 'Raw server sentence.' } });
Expand Down
2 changes: 1 addition & 1 deletion packages/mosaic/src/utils/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export function toLocalizableErrors(cause: unknown): ErrorDescription[] {
export function toFormError(cause: unknown): FormError {
if (cause instanceof SaveError) {
const { global, fields } = cause.formError;
return { global, fields };
return global || fields ? { global, fields } : { global: UNEXPECTED_ERROR };
}
console.error('[Clerk] Could not localize error', cause);
return { global: UNEXPECTED_ERROR };
Expand Down
Loading