Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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/fair-olives-wish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/clerk-js': patch
---

Bugfix: Fixed incorrect field validation when using password authentication with email or phone number during sign-up. Optional email and phone fields now correctly display their requirement status.
2 changes: 1 addition & 1 deletion packages/clerk-js/bundlewatch.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
{ "path": "./dist/organizationswitcher*.js", "maxSize": "5KB" },
{ "path": "./dist/organizationlist*.js", "maxSize": "5.5KB" },
{ "path": "./dist/signin*.js", "maxSize": "14KB" },
{ "path": "./dist/signup*.js", "maxSize": "8.5KB" },
{ "path": "./dist/signup*.js", "maxSize": "8.86KB" },
{ "path": "./dist/userbutton*.js", "maxSize": "5KB" },
{ "path": "./dist/userprofile*.js", "maxSize": "16KB" },
{ "path": "./dist/userverification*.js", "maxSize": "5KB" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { describe, expect, it, vi } from 'vitest';

import { CardStateProvider } from '@/ui/elements/contexts';

import { render, screen, waitFor } from '../../../../vitestUtils';
import { fireEvent, render, screen, waitFor } from '../../../../vitestUtils';
import { OptionsProvider } from '../../../contexts';
import { AppearanceProvider } from '../../../customizables';
import { bindCreateFixtures } from '../../../utils/vitest/createFixtures';
Expand Down Expand Up @@ -88,14 +88,83 @@ describe('SignUpStart', () => {
screen.getByText('Password');
});

it('enables optional email', async () => {
it('should keep email optional when phone is primary with password', async () => {
const { wrapper } = await createFixtures(f => {
f.withEmailAddress({ required: false });
f.withPhoneNumber({ required: true });
f.withPassword({ required: true });
});
render(<SignUpStart />, { wrapper });
expect(screen.getByText('Email address').nextElementSibling?.textContent).toBe('Optional');

const emailAddress = screen.getByLabelText('Email address', { selector: 'input' });
expect(emailAddress.ariaRequired).toBe('false');
expect(screen.getByText('Optional')).toBeInTheDocument();

const phoneInput = screen.getByLabelText('Phone number', { selector: 'input' });
expect(phoneInput.ariaRequired).toBe('true');
});

it('should require phone when password is required and phone is primary communication method', async () => {
const { wrapper } = await createFixtures(f => {
f.withPhoneNumber({ required: false, used_for_first_factor: true });
f.withPassword({ required: true });
});
render(<SignUpStart />, { wrapper });

const phoneInput = screen.getByLabelText('Phone number', { selector: 'input' });
expect(phoneInput.ariaRequired).toBe('true');

expect(screen.queryByLabelText('Email address', { selector: 'input' })).not.toBeInTheDocument();
});

it('should require email when only email is enabled with password', async () => {
const { wrapper } = await createFixtures(f => {
f.withEmailAddress({ required: true, used_for_first_factor: true });
f.withPassword({ required: true });
});

render(<SignUpStart />, { wrapper });

const emailAddress = screen.getByLabelText('Email address', { selector: 'input' });
expect(emailAddress.ariaRequired).toBe('true');
expect(screen.queryByText('Optional')).not.toBeInTheDocument();
expect(screen.queryByLabelText('Phone number', { selector: 'input' })).not.toBeInTheDocument();
});

it('should require email when password is required and email is primary communication method', async () => {
const { wrapper } = await createFixtures(f => {
f.withEmailAddress({ required: false, used_for_first_factor: true });
f.withPhoneNumber({ required: false, used_for_first_factor: false });
f.withPassword({ required: true });
});
render(<SignUpStart />, { wrapper });

const emailAddress = screen.getByLabelText('Email address', { selector: 'input' });
expect(emailAddress.ariaRequired).toBe('true');
expect(screen.queryByText('Optional')).not.toBeInTheDocument();

expect(screen.queryByLabelText('Phone number', { selector: 'input' })).not.toBeInTheDocument();
});

it('should require active field when toggling between email and phone with password', async () => {
const { wrapper } = await createFixtures(f => {
f.withEmailAddress({ required: false, used_for_first_factor: true });
f.withPhoneNumber({ required: false, used_for_first_factor: true });
f.withPassword({ required: true });
});
render(<SignUpStart />, { wrapper });

const emailInput = screen.getByLabelText('Email address', { selector: 'input' });
expect(emailInput.ariaRequired).toBe('true');
expect(screen.queryByText('Optional')).not.toBeInTheDocument();

const usePhoneButton = screen.getByText(/use phone/i);
fireEvent.click(usePhoneButton);

const phoneInput = screen.getByLabelText('Phone number', { selector: 'input' });
expect(phoneInput.ariaRequired).toBe('true');

expect(screen.queryByLabelText('Email address', { selector: 'input' })).not.toBeInTheDocument();
});

it('enables optional phone number', async () => {
Expand Down
Loading