From 73ec1f841d4b87c639da30fe1b0f8d373c510c91 Mon Sep 17 00:00:00 2001 From: "@daniel-lxs" <57051444+daniel-lxs@users.noreply.github.com> Date: Thu, 12 Nov 2026 05:20:34 +0000 Subject: [PATCH 1/2] [Fix] Profile offers email changes without verification delivery --- .../settings/UserProfileSection.test.tsx | 45 +++++++++++++++++++ .../settings/UserProfileSection.tsx | 23 ++++++++-- .../pages/PersonalSettingsPage.test.tsx | 4 +- .../settings/pages/PersonalSettingsPage.tsx | 3 ++ .../pages/PersonalSettingsRoute.test.tsx | 2 + .../settings/pages/PersonalSettingsRoute.tsx | 1 + .../trpc/commands/preferences/index.test.ts | 17 +++++++ .../src/trpc/commands/preferences/index.ts | 2 + 8 files changed, 91 insertions(+), 6 deletions(-) diff --git a/apps/web/src/components/settings/UserProfileSection.test.tsx b/apps/web/src/components/settings/UserProfileSection.test.tsx index 69e1d3c446..d086cfe322 100644 --- a/apps/web/src/components/settings/UserProfileSection.test.tsx +++ b/apps/web/src/components/settings/UserProfileSection.test.tsx @@ -139,6 +139,7 @@ describe('UserProfileSection', () => { it('updates name and email from the expanded profile form', async () => { render( { render( { }); expect(toast.error).toHaveBeenCalledWith('Email update failed'); }); + + it('keeps email read-only while allowing name changes without verification delivery', async () => { + render( + , + ); + fireEvent.click(screen.getByRole('button', { name: 'Edit' })); + expect(screen.getByLabelText('Email')).toHaveAttribute('readonly'); + expect(screen.getByLabelText('Email')).toHaveAccessibleDescription( + 'Email changes require email verification to be enabled for this deployment.', + ); + fireEvent.change(screen.getByLabelText('Name'), { + target: { value: 'New Name' }, + }); + fireEvent.click(screen.getByRole('button', { name: 'Save changes' })); + await waitFor(() => + expect(updateUserMock).toHaveBeenCalledWith({ name: 'New Name' }), + ); + expect(changeEmailMock).not.toHaveBeenCalled(); + expect(toast.success).toHaveBeenCalledWith('Profile updated.'); + }); + + it('does not submit an email draft after email capability becomes unavailable', async () => { + const profile = { + email: 'ada@example.com', + imageUrl: '', + name: 'Ada User', + }; + const view = render( + , + ); + fireEvent.click(screen.getByRole('button', { name: 'Edit' })); + fireEvent.change(screen.getByLabelText('Email'), { + target: { value: 'new@example.com' }, + }); + view.rerender( + , + ); + fireEvent.click(screen.getByRole('button', { name: 'Save changes' })); + expect(changeEmailMock).not.toHaveBeenCalled(); + expect(updateUserMock).not.toHaveBeenCalled(); + }); }); diff --git a/apps/web/src/components/settings/UserProfileSection.tsx b/apps/web/src/components/settings/UserProfileSection.tsx index b2b331a084..823c8ec2c9 100644 --- a/apps/web/src/components/settings/UserProfileSection.tsx +++ b/apps/web/src/components/settings/UserProfileSection.tsx @@ -35,9 +35,11 @@ function getAuthErrorMessage( } export function UserProfileSection({ + canChangeEmail = false, canChangePassword = false, profile, }: { + canChangeEmail?: boolean; canChangePassword?: boolean; profile: UserProfileSectionProfile; }) { @@ -53,7 +55,7 @@ export function UserProfileSection({ const trimmedName = name.trim(); const trimmedEmail = email.trim(); const hasNameChange = trimmedName !== profile.name; - const hasEmailChange = trimmedEmail !== profile.email; + const hasEmailChange = canChangeEmail && trimmedEmail !== profile.email; // Only show the Remove control for avatars uploaded through this app's // /api/avatars/ endpoint. OAuth-provider avatar URLs are not removable here // — the user would lose their provider-supplied picture with no way back. @@ -285,8 +287,8 @@ export function UserProfileSection({ ) : (

- Update your name or email. Your profile picture can be changed - from the profile view. + Update your name{canChangeEmail ? ' or email' : ''}. Your profile + picture can be changed from the profile view.

@@ -303,13 +305,26 @@ export function UserProfileSection({ setEmail(event.target.value)} disabled={isSubmitting} /> + {!canChangeEmail && ( +

+ Email changes require email verification to be enabled for + this deployment. +

+ )}