Skip to content

Conversation

@Agarwalchetan
Copy link

@Agarwalchetan Agarwalchetan commented Jan 27, 2026

fixes #38364

What was the problem?

RegisterForm.tsx had grown significantly complex and required disabling the ESLint complexity rule:

/* eslint-disable complexity */

The file (~316 lines) mixed multiple responsibilities:

  • Form validation logic
  • API error handling
  • Settings-based conditional logic
  • Large, repetitive JSX blocks
  • UI rendering and business logic tightly coupled

This made the component harder to read, maintain, and safely evolve.


What does this PR do?

This PR performs a pure refactor to reduce complexity without changing any behavior or UI.

Key changes

  1. Removed eslint-disable complexity

    • Complexity is now reduced structurally, not suppressed.
  2. Extracted validation logic into a custom hook

    • useRegisterFormValidation
    • Centralizes all field validation rules
    • Preserves existing react-hook-form behavior
  3. Extracted registration error handling

    • useRegisterErrorHandler
    • Centralizes API error mapping, toasts, and redirects
    • Keeps all existing error messages and flows unchanged
  4. Reduced repetitive JSX

    • Introduced FormFieldInput for common field patterns
    • Introduced PasswordFieldWithVerifier for password + verifier + confirmation logic
    • Accessibility (aria-*) attributes preserved
  5. Simplified RegisterForm.tsx

    • Reduced from ~316 lines → ~171 lines
    • Focused on layout and composition only

What this PR does NOT do

  • No functional changes
  • No UX or visual changes
  • No new dependencies
  • No validation rule changes
  • No error message changes

This is strictly a refactor.


Why this approach?

  • Restores ESLint complexity enforcement instead of bypassing it
  • Improves separation of concerns
  • Aligns with patterns already used in similar forms (e.g. LoginForm)
  • Makes future changes safer and easier

Files changed

  • Modified

    • packages/web-ui-registration/src/RegisterForm.tsx
  • Added

    • hooks/useRegisterFormValidation.ts
    • hooks/useRegisterErrorHandler.ts
    • components/FormFieldInput.tsx
    • components/PasswordFieldWithVerifier.tsx

Summary by CodeRabbit

  • Refactor
    • Centralized registration validation and error handling for more consistent, localized feedback.
  • New Features
    • Password field with strength verifier and optional confirmation.
    • Modular, accessible form fields with clearer inline error messaging and ARIA support.
    • Conditional flows for activation/wait messaging and optional reason-for-joining field.
  • Tests
    • Expanded suites covering rendering, validation rules, error mappings, accessibility, and submission behavior.

✏️ Tip: You can customize this high-level summary in your review settings.

…subcomponents

- Extract form validation logic into useRegisterFormValidation hook
- Extract error handling logic into useRegisterErrorHandler hook
- Create FormFieldInput component for reusable form fields
- Create PasswordFieldWithVerifier component for password fields
- Refactor RegisterForm to use new hooks and components
- Remove eslint-disable complexity comment

This refactoring reduces RegisterForm.tsx from 316 to 171 lines (-145 lines)
and improves maintainability by separating concerns.
@dionisio-bot
Copy link
Contributor

dionisio-bot bot commented Jan 27, 2026

Looks like this PR is not ready to merge, because of the following issues:

  • This PR is missing the 'stat: QA assured' label
  • This PR is missing the required milestone or project

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@changeset-bot
Copy link

changeset-bot bot commented Jan 27, 2026

⚠️ No Changeset found

Latest commit: a870b2a

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 27, 2026

Walkthrough

Refactors RegisterForm by extracting validation into useRegisterFormValidation, error handling into useRegisterErrorHandler, and reusable UI into FormFieldInput and PasswordFieldWithVerifier; replaces inline field JSX and per-field error/validation wiring while preserving behavior and accessibility.

Changes

Cohort / File(s) Summary
RegisterForm refactor
packages/web-ui-registration/src/RegisterForm.tsx
Replaces inline field components, per-field registration and inline error callbacks with centralized validationRules, handleRegisterError, and subcomponents; removes eslint complexity suppression.
Validation hook
packages/web-ui-registration/src/hooks/useRegisterFormValidation.ts
New hook producing validationRules, requireNameForRegister, requiresPasswordConfirmation, manuallyApproveNewUsersRequired, and passwordIsValid based on settings and form watch.
Error handler hook
packages/web-ui-registration/src/hooks/useRegisterErrorHandler.ts
New hook exposing handleRegisterError to map API errors to field/server errors, dispatch rate-limit toasts, and route to login/activation flows.
Form components
packages/web-ui-registration/src/components/FormFieldInput.tsx, packages/web-ui-registration/src/components/PasswordFieldWithVerifier.tsx
Added FormFieldInput (text/textarea with label, ARIA, error) and PasswordFieldWithVerifier (password + verifier, optional confirmation, ARIA/error handling).
Tests
packages/web-ui-registration/src/RegisterForm.spec.tsx, .../components/FormFieldInput.spec.tsx, .../components/PasswordFieldWithVerifier.spec.tsx, .../hooks/useRegisterErrorHandler.spec.tsx, .../hooks/useRegisterFormValidation.spec.tsx
New comprehensive tests covering rendering, conditional fields, validation rules, error mapping, ARIA attributes, submission payloads, and hook behaviors.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant User
participant RegisterForm
participant ValidationHook as useRegisterFormValidation
participant PasswordVerifier
participant API
participant ErrorHandler as useRegisterErrorHandler

User->>RegisterForm: fill fields & submit
RegisterForm->>ValidationHook: request validationRules / passwordIsValid
alt password verification needed
RegisterForm->>PasswordVerifier: request strength/check
PasswordVerifier-->>RegisterForm: strength result
end
RegisterForm->>API: send registration mutation
API-->>RegisterForm: success / error
alt success (activation required)
RegisterForm->>User: show activation/wait messaging
else error
RegisterForm->>ErrorHandler: handleRegisterError(error)
ErrorHandler-->>RegisterForm: set field errors / server error / route action / toast
RegisterForm->>User: display field errors or server callout
end

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

stat: ready to merge, stat: QA assured

Suggested reviewers

  • dougfabris
  • cardoso
  • tassoevan

Poem

🐰 I hopped through fields and rules anew,
Pulled errors tidy into a single view.
Hooks stitched neat, components light,
Register hums, all clean and bright. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Out of Scope Changes check ❓ Inconclusive The PR contains added test files (RegisterForm.spec.tsx and component/hook specs) that are technically out of scope for a refactor-only issue, but this appears intentional per author comments acknowledging the divergence and offering explicit options to either keep or remove them. Clarify whether test files should remain in this PR or be deferred to follow-up work, as they expand scope beyond the pure refactor objective stated in the linked issue.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main change: a refactor of RegisterForm.tsx to reduce complexity by extracting hooks and subcomponents, which is the primary focus of all modifications.
Linked Issues check ✅ Passed All code-level objectives from issue #38364 are met: eslint-disable removed, validation logic extracted to useRegisterFormValidation, error handling extracted to useRegisterErrorHandler, JSX duplication reduced via FormFieldInput and PasswordFieldWithVerifier, and no breaking changes introduced.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 5 files

Copy link
Member

@MartinSchoeler MartinSchoeler left a comment

Choose a reason for hiding this comment

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

Can you add some unit tests to the component to make sure the behavior and visual is still the same?

@Agarwalchetan
Copy link
Author

Yes, that makes sense,
I’ll add unit tests to cover the main behaviors (rendering, validation, conditional fields) to ensure no regressions from the refactor.

…refactoring

- Add RegisterForm.spec.tsx with 26+ tests covering:
  * Rendering all required and conditional fields
  * Validation for all form fields
  * Password confirmation matching
  * Form submission behavior
  * Accessibility attributes

- Add useRegisterFormValidation.spec.tsx with 7 tests covering:
  * Validation rules generation
  * Settings-based conditional requirements
  * Password matching logic

- Add useRegisterErrorHandler.spec.tsx with 9 tests covering:
  * All error scenarios (invalid email, duplicate username, etc.)
  * Toast message dispatching
  * Login route redirects

- Add FormFieldInput.spec.tsx with 10 tests covering:
  * Text and textarea rendering
  * Required field handling
  * Error display and accessibility

- Add PasswordFieldWithVerifier.spec.tsx with 10 tests covering:
  * Password and confirmation field rendering
  * Conditional confirmation field
  * Password validation state
  * Error handling

All tests use @testing-library/react and mockAppRoot for proper
isolation and follow existing project testing patterns.
@Agarwalchetan
Copy link
Author

Update on tests:

I added comprehensive unit tests (56 tests across RegisterForm, related hooks, and components) to validate that the refactor didn’t introduce any behavioral or rendering regressions.

While working on this, I found that web-ui-registration doesn’t currently have Jest infrastructure (no test script or preset), and attempts to wire it up using @rocket.chat/jest-presets run into module resolution issues. This appears to be existing technical debt in this package rather than an issue with the tests themselves.

To still validate the refactor, I ran the tests locally with a temporary Jest setup, and all tests passed.

I’m happy to:
– keep the tests as-is and follow up with a separate PR/issue to enable Jest for this package, or
– remove the test files if you’d prefer to keep this PR strictly refactor-only.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@packages/web-ui-registration/src/RegisterForm.spec.tsx`:
- Around line 356-366: The test currently only checks for a generic form and can
pass if CustomFieldsForm is removed; update the RegisterForm.spec.tsx to
explicitly assert that CustomFieldsForm is rendered by mocking either the
useAccountsCustomFields hook or the CustomFieldsForm component: replace or mock
CustomFieldsForm (or stub the useAccountsCustomFields hook) to render a unique
sentinel element (e.g., data-testid or a distinctive text) and then render
RegisterForm and assert screen.getByTestId or getByText for that sentinel;
reference the RegisterForm component and the
CustomFieldsForm/useAccountsCustomFields symbols when adding the mock and the
assertion so the test fails if the integration is removed.
- Around line 7-9: The tests currently use the global userEvent instance; change
to creating a per-test instance by calling userEvent.setup() in a beforeEach and
storing the returned user (e.g., const user = userEvent.setup()) for all
interactions in that test; update usages in RegisterForm.spec.tsx to call
methods on that local user instead of the global userEvent, and keep
mockSetLoginRoute and mockRegisterMethod as-is to avoid shared interaction state
across tests.
🧹 Nitpick comments (6)
packages/web-ui-registration/src/hooks/useRegisterErrorHandler.spec.tsx (1)

34-46: Consider adding a test for error-invalid-email via errorType.

The hook implementation checks both error.error and error.errorType for the invalid email case (line 23 in the hook: [error.error, error.errorType].includes('error-invalid-email')), but only the error.error path is tested here. Adding a test for { errorType: 'error-invalid-email' } would provide complete coverage of this branch.

💡 Suggested additional test
+    it('should handle error-invalid-email error via errorType', () => {
+        const { result } = renderHook(() => useRegisterErrorHandler(mockSetError, mockSetServerError, mockSetLoginRoute), {
+            wrapper: defaultAppRoot,
+        });
+
+        const error = { errorType: 'error-invalid-email' };
+        result.current.handleRegisterError(error);
+
+        expect(mockSetError).toHaveBeenCalledWith('email', {
+            type: 'invalid-email',
+            message: 'Invalid email format',
+        });
+    });
+
     it('should handle error-user-already-exists error', () => {
packages/web-ui-registration/src/components/FormFieldInput.spec.tsx (2)

56-66: Consider using a properly typed error mock to avoid as any.

The as any type assertion bypasses type checking. Consider creating a typed helper or importing the FieldError type from react-hook-form.

♻️ Suggested improvement
+import type { FieldError } from 'react-hook-form';
+
+const createFieldError = (type: string, message: string): FieldError => ({ type: type as any, message });
+
 // Then in tests:
-const error = { type: 'required', message: 'This field is required' };
-<FormFieldInput label='Test Field' fieldId='test-id' error={error as any} register={mockRegister} />
+const error = createFieldError('required', 'This field is required');
+<FormFieldInput label='Test Field' fieldId='test-id' error={error} register={mockRegister} />

15-117: Good test coverage overall, but consider adding react-hook-form integration tests.

The tests comprehensively cover rendering and accessibility, but don't verify that the register props (onChange, onBlur, ref) are correctly passed to the underlying input element. This could catch integration issues with react-hook-form.

♻️ Optional test to verify register integration
it('should pass register props to input element', () => {
    render(
        <FormFieldInput label='Test Field' fieldId='test-id' register={mockRegister} />,
        { wrapper: defaultAppRoot },
    );

    const input = screen.getByRole('textbox');
    expect(mockRegister.ref).toHaveBeenCalledWith(input);
});
packages/web-ui-registration/src/components/PasswordFieldWithVerifier.spec.tsx (1)

118-126: Avoid as any by typing errors with FieldError from react-hook-form.

This keeps the test aligned with library typings and avoids masking type regressions.

♻️ Proposed refactor
 import { mockAppRoot } from '@rocket.chat/mock-providers';
 import { render, screen } from '@testing-library/react';
+import type { FieldError } from 'react-hook-form';

@@
-        const error = { type: 'required', message: 'Password is required' };
+        const error: FieldError = { type: 'required', message: 'Password is required' };

@@
-        const error = { type: 'validate', message: "Passwords don't match" };
+        const error: FieldError = { type: 'validate', message: "Passwords don't match" };

Also applies to: 136-147

packages/web-ui-registration/src/hooks/useRegisterFormValidation.spec.tsx (1)

96-105: Remove the inline comment in the test.
It’s redundant with the test name and violates the “no implementation comments” guideline. As per coding guidelines, ...

💡 Proposed change
-        // With disabled password policy, any password is valid
         expect(result.current.passwordIsValid).toBe(true);
packages/web-ui-registration/src/RegisterForm.spec.tsx (1)

10-10: Remove inline comments in tests.

The test names already convey intent; inline comments add noise. As per coding guidelines, avoid code comments in implementation/tests.

🧹 Example cleanup
-// Mock the useRegisterMethod hook
 ...
-            // Fill in the form
 ...
-            // Submit the form
 ...
-            // The CustomFieldsForm is already tested in ui-client
-            // We just verify it's rendered in RegisterForm
 ...
-            // CustomFieldsForm component should be in the document
-            // This is an integration test to ensure the component is properly included

Also applies to: 276-283, 358-364

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 10 files

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/web-ui-registration/src/RegisterForm.spec.tsx`:
- Line 21: The variable declaration has an unintended space ("defaultApp Root")
causing all references to defaultAppRoot to be undefined; change the declaration
to match usages by renaming the identifier to defaultAppRoot and assign it the
mock value from mockAppRoot() (i.e., make the declaration const defaultAppRoot =
mockAppRoot()), then run tests to ensure all references (e.g., in the test cases
at lines referencing defaultAppRoot) resolve correctly.
🧹 Nitpick comments (1)
packages/web-ui-registration/src/RegisterForm.spec.tsx (1)

10-13: Drop inline comments to keep tests concise.

The comments restate intent already covered by the test names. As per coding guidelines, avoid implementation comments.

♻️ Suggested cleanup
-// Per-test user instance
 let user: ReturnType<typeof userEvent.setup>;

-// Mock the useRegisterMethod hook
 jest.mock('./hooks/useRegisterMethod', () => ({
     useRegisterMethod: () => ({
         mutate: mockRegisterMethod,
         isPending: false,
     }),
 }));
@@
-            // Fill in the form
             await user.type(screen.getByRole('textbox', { name: /name/i }), 'John Doe');
             await user.type(screen.getByRole('textbox', { name: /email/i }), '[email protected]');
             await user.type(screen.getByRole('textbox', { name: /username/i }), 'johndoe');
             await user.type(screen.getByLabelText(/password/i), 'password123');
 
-            // Submit the form
             await user.click(screen.getByRole('button', { name: /join your team/i }));
@@
-            // Mock custom fields to verify CustomFieldsForm integration
             const mockCustomFields = [
                 { _id: 'field1', type: 'text', label: 'Custom Field 1', required: true, defaultValue: '', public: true },
             ];
@@
-            // Verify CustomFieldsForm integration - detailed tests are in ui-client
             const form = screen.getByRole('form');

Also applies to: 280-287, 362-375

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 10 files

@Agarwalchetan
Copy link
Author

I’ve addressed the requested changes and fixed the follow-ups. Re-requesting review when you have a moment, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor(registration): reduce RegisterForm complexity by extracting hooks and subcomponents

2 participants