Skip to content

Commit

Permalink
fix(clerk-js): Fix custom username field error to allow fallbacks (#4858
Browse files Browse the repository at this point in the history
)
  • Loading branch information
panteliselef authored Jan 9, 2025
1 parent 7aa9cdd commit 0994963
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 11 deletions.
5 changes: 5 additions & 0 deletions .changeset/hip-spiders-punch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/clerk-js': patch
---

Fixes username form field errors to display messages according to the respective code sent in the error response.
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { OAUTH_PROVIDERS } from '@clerk/types';
import { ClerkAPIResponseError } from '@clerk/shared/error';
import { OAUTH_PROVIDERS } from '@clerk/shared/oauth';
import { waitFor } from '@testing-library/dom';
import React from 'react';

import { render, screen } from '../../../../testUtils';
import { render, runFakeTimers, screen } from '../../../../testUtils';
import { bindCreateFixtures } from '../../../utils/test/createFixtures';
import { SignUpContinue } from '../SignUpContinue';

Expand Down Expand Up @@ -106,6 +108,86 @@ describe('SignUpContinue', () => {
screen.getByText(`Continue with ${name}`);
});

it('renders error for invalid username length', async () => {
const { wrapper, fixtures } = await createFixtures(f => {
f.withEmailAddress({ required: true });
f.withUsername({ required: true });
f.startSignUpWithEmailAddress({
emailVerificationStatus: 'verified',
});
});

fixtures.signUp.update.mockRejectedValue(
new ClerkAPIResponseError('Error', {
data: [
{
code: 'form_username_invalid_length',
long_message: 'some server error',
message: 'some server error',
meta: { param_name: 'username' },
},
],
status: 400,
}),
);

await runFakeTimers(async timers => {
const { userEvent } = render(<SignUpContinue />, { wrapper });
expect(screen.queryByText(/username/i)).toBeInTheDocument();
await userEvent.type(screen.getByLabelText(/username/i), 'clerkUser');
timers.runOnlyPendingTimers();
const button = screen.getByText('Continue');
await userEvent.click(button);
timers.runOnlyPendingTimers();

await waitFor(() => expect(fixtures.signUp.update).toHaveBeenCalled());
timers.runOnlyPendingTimers();
await waitFor(() =>
expect(screen.queryByText(/^Your username must be between 4 and 40 characters long./i)).toBeInTheDocument(),
);
});
});

it('renders error for existing username', async () => {
const { wrapper, fixtures } = await createFixtures(f => {
f.withEmailAddress({ required: true });
f.withUsername({ required: true });
f.startSignUpWithEmailAddress({
emailVerificationStatus: 'verified',
});
});

fixtures.signUp.update.mockRejectedValue(
new ClerkAPIResponseError('Error', {
data: [
{
code: 'form_identifier_exists',
long_message: 'some server error',
message: 'some server error',
meta: { param_name: 'username' },
},
],
status: 400,
}),
);

await runFakeTimers(async timers => {
const { userEvent } = render(<SignUpContinue />, { wrapper });
expect(screen.queryByText(/username/i)).toBeInTheDocument();
await userEvent.type(screen.getByLabelText(/username/i), 'clerkUser');
timers.runOnlyPendingTimers();
const button = screen.getByText('Continue');
await userEvent.click(button);
timers.runOnlyPendingTimers();

await waitFor(() => expect(fixtures.signUp.update).toHaveBeenCalled());
timers.runOnlyPendingTimers();
await waitFor(() =>
expect(screen.queryByText(/^This username is taken. Please try another./i)).toBeInTheDocument(),
);
});
});

describe('Sign in Link', () => {
it('Shows the Sign In message with the appropriate link', async () => {
const { wrapper, fixtures } = await createFixtures(f => {
Expand Down
6 changes: 6 additions & 0 deletions packages/clerk-js/src/ui/utils/test/fixtureHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ const createSignUpFixtureHelpers = (baseClient: ClientJSON) => {
status: emailVerificationStatus,
},
},
missing_fields: [],
} as SignUpJSON;
};

Expand Down Expand Up @@ -341,6 +342,11 @@ const createUserSettingsFixtureHelpers = (environment: EnvironmentJSON) => {
mode: SIGN_UP_MODES.PUBLIC,
};

us.username_settings = {
min_length: 4,
max_length: 40,
};

const emptyAttribute = {
first_factors: [],
second_factors: [],
Expand Down
27 changes: 18 additions & 9 deletions packages/clerk-js/src/ui/utils/usernameUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,28 @@ type LocalizationConfigProps = {
usernameSettings: Pick<UsernameSettingsData, 'max_length' | 'min_length'>;
};

export const createUsernameError = (errors: ClerkAPIError[], localizationConfig: LocalizationConfigProps) => {
const INVALID_LENGTH = 'form_username_invalid_length';

export const createUsernameError = (
errors: ClerkAPIError[],
localizationConfig: LocalizationConfigProps,
): ClerkAPIError | string | undefined => {
const { t, usernameSettings } = localizationConfig;

const clerkApiError = errors[0] as ClerkAPIError | undefined;

if (!localizationConfig) {
return errors[0].longMessage;
return clerkApiError;
}

const msg = t(
localizationKeys('unstable__errors.form_username_invalid_length', {
min_length: usernameSettings.min_length,
max_length: usernameSettings.max_length,
}),
);
if (clerkApiError?.code === INVALID_LENGTH) {
return t(
localizationKeys(`unstable__errors.${INVALID_LENGTH}`, {
min_length: usernameSettings.min_length,
max_length: usernameSettings.max_length,
}),
);
}

return msg;
return clerkApiError;
};

0 comments on commit 0994963

Please sign in to comment.