Skip to content
Open
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
101 changes: 101 additions & 0 deletions src/components/FormFields/tests/RegisterAccountFields.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,107 @@ describe('RegisterAccountFields', () => {
expect(confirmPasswordError).toBeUndefined();
}
});

describe('Password Whitespace Handling', () => {
it.each<[
title: string,
password: string,
confirmPassword: string,
expectError: boolean,
]>([
['trims leading spaces from password', ' password123', 'password123', false],
['trims trailing spaces from password', 'password123 ', 'password123', false],
['trims leading and trailing spaces from password', ' password123 ', 'password123', false],
['trims leading spaces from confirmPassword', 'password123', ' password123', false],
['trims trailing spaces from confirmPassword', 'password123', 'password123 ', false],
['trims leading and trailing spaces from confirmPassword', 'password123', ' password123 ', false],
['trims spaces from both fields', ' password123 ', ' password123 ', false],
['shows error when passwords differ after trimming', 'password123', 'password456', true],
['trims multiple leading spaces', ' password123', 'password123', false],
['trims multiple trailing spaces', 'password123 ', 'password123', false],
['trims tabs and spaces', '\tpassword123 ', ' password123\t', false],
['shows error with different passwords and spaces', ' password123 ', ' differentpassword ', true],
])('%s', async (_title, password, confirmPassword, expectError) => {
const form = renderComponent();

form.setValue('password', password);
form.setValue('confirmPassword', confirmPassword);
await form.trigger(['password', 'confirmPassword']);

const confirmPasswordError = form.getFieldState('confirmPassword').error;
if (expectError) {
expect(confirmPasswordError?.message).toMatch(/do not match/i);
} else {
const passwordError = form.getFieldState('password').error;
expect(passwordError).toBeUndefined();
expect(confirmPasswordError).toBeUndefined();
}
});

it('does not trim internal spaces in passwords', async () => {
const form = renderComponent();

const passwordWithSpaces = 'pass word 123';
form.setValue('password', passwordWithSpaces);
form.setValue('confirmPassword', passwordWithSpaces);
await form.trigger(['password', 'confirmPassword']);

const confirmPasswordError = form.getFieldState('confirmPassword').error;
const passwordError = form.getFieldState('password').error;

expect(passwordError).toBeUndefined();
expect(confirmPasswordError).toBeUndefined();
});

it('shows error when internal spaces differ between password and confirmPassword', async () => {
const form = renderComponent();

form.setValue('password', 'pass word123');
form.setValue('confirmPassword', 'password123');
await form.trigger(['password', 'confirmPassword']);

const confirmPasswordError = form.getFieldState('confirmPassword').error;
expect(confirmPasswordError?.message).toMatch(/do not match/i);
});

it('validates minimum length after trimming whitespace', async () => {
const form = renderComponent();

// Single character with spaces should fail after trimming
form.setValue('password', ' 1 ');
await form.trigger('password');

const passwordError = form.getFieldState('password').error;
expect(passwordError?.message).toMatch(/at least 2 characters/i);
});

it('validates maximum length after trimming whitespace', async () => {
const form = renderComponent();

// Create a password that's 76 characters (exceeds max of 75)
const longPassword = 'a'.repeat(76);
form.setValue('password', ` ${longPassword} `);
await form.trigger('password');

const passwordError = form.getFieldState('password').error;
expect(passwordError?.message).toMatch(/no more than 75 characters/i);
});

it('handles empty strings with only whitespace', async () => {
const form = renderComponent();

form.setValue('password', ' ');
form.setValue('confirmPassword', ' ');
await form.trigger(['password', 'confirmPassword']);

const passwordError = form.getFieldState('password').error;
const confirmPasswordError = form.getFieldState('confirmPassword').error;

// Should fail validation because after trimming, it's empty
expect(passwordError?.message).toMatch(/at least 2 characters/i);
expect(confirmPasswordError?.message).toMatch(/confirm your password/i);
});
});
});

describe('Country Dropdown', () => {
Expand Down
4 changes: 2 additions & 2 deletions src/constants/checkout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,10 @@ export const PlanDetailsRegisterPageSchema = () => (z.object({
username: z.string().trim()
.min(2, 'Username must be between 2 and 30 characters long.')
.max(30, 'Username must be between 2 and 30 characters long.'),
password: z.string()
password: z.string().trim()
.min(2, 'This password is too short. It must contain at least 2 characters.')
.max(75, 'This password is too long. It must contain no more than 75 characters.'),
confirmPassword: z.string()
confirmPassword: z.string().trim()
.min(8, 'Please confirm your password')
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The minimum length validation for confirmPassword is inconsistent with the password field. The password field requires a minimum of 2 characters, but confirmPassword requires a minimum of 8 characters. These should be the same value.

Change line 88 to:

.min(2, 'Please confirm your password')

This inconsistency would cause validation errors when users enter matching passwords between 2-7 characters.

Suggested change
.min(8, 'Please confirm your password')
.min(2, 'Please confirm your password')

Copilot uses AI. Check for mistakes.
.max(75, 'This password is too long. It must contain no more than 75 characters.'),
Comment on lines +84 to 89
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trimming password fields is a security anti-pattern. While trimming leading/trailing whitespace makes sense for usernames and emails, passwords should preserve all characters including leading and trailing spaces, as users may intentionally include them for security.

Instead of trimming the password fields, consider:

  1. Validating that passwords don't contain only whitespace
  2. Warning users about leading/trailing spaces in the UI
  3. Allowing the passwords to be stored as-is to preserve user intent

This change could lock out users who intentionally included spaces in their passwords.

Copilot uses AI. Check for mistakes.
country: z.string().trim()
Expand Down
Loading