diff --git a/src/components/UserSettings/AvatarUpload.tsx b/src/components/UserSettings/AvatarUpload.tsx index 861bdf523c..f1c490da85 100644 --- a/src/components/UserSettings/AvatarUpload.tsx +++ b/src/components/UserSettings/AvatarUpload.tsx @@ -25,6 +25,7 @@ export default function AvatarUpload(props: { doneCallback?: () => void }) { async function upload(e: React.FormEvent) { e.preventDefault(); + e.stopPropagation(); if (file) { setLoading(true); await uploadAvatar(getUserId(), file) diff --git a/src/components/UserSettings/UserSettings.tsx b/src/components/UserSettings/UserSettings.tsx index 1557e1151e..e96461b01b 100644 --- a/src/components/UserSettings/UserSettings.tsx +++ b/src/components/UserSettings/UserSettings.tsx @@ -34,12 +34,20 @@ export enum UserSettingsIds { SelectUiLang = "user-settings-ui-lang", } -export default (): ReactElement => { - const potentialUser = getCurrentUser(); - return potentialUser ? : ; -}; +export default function UserSettingsGetUser(): ReactElement { + const [potentialUser, setPotentialUser] = useState(getCurrentUser()); -export function UserSettings(props: { user: User }): ReactElement { + return potentialUser ? ( + + ) : ( + + ); +} + +export function UserSettings(props: { + user: User; + setUser: (user?: User) => void; +}): ReactElement { const [name, setName] = useState(props.user.name); const [phone, setPhone] = useState(props.user.phone); const [email, setEmail] = useState(props.user.email); @@ -70,9 +78,11 @@ export function UserSettings(props: { user: User }): ReactElement { phone, email: punycode.toUnicode(email), uiLang, + hasAvatar: !!avatar, }); updateLangFromUser(); enqueueSnackbar(t("userSettings.updateSuccess")); + props.setUser(getCurrentUser()); } else { setEmailTaken(true); } diff --git a/src/components/UserSettings/tests/UserSettings.test.tsx b/src/components/UserSettings/tests/UserSettings.test.tsx index 1bf0d2c5c9..c9f6c9dad7 100644 --- a/src/components/UserSettings/tests/UserSettings.test.tsx +++ b/src/components/UserSettings/tests/UserSettings.test.tsx @@ -5,13 +5,16 @@ import userEvent from "@testing-library/user-event"; import "tests/reactI18nextMock"; import { User } from "api/models"; -import { +import UserSettingsGetUser, { UserSettings, UserSettingsIds, } from "components/UserSettings/UserSettings"; import { newUser } from "types/user"; +const mockGetAvatar = jest.fn(); +const mockGetCurrentUser = jest.fn(); const mockIsEmailTaken = jest.fn(); +const mockSetUser = jest.fn(); const mockUpdateUser = jest.fn(); jest.mock("notistack", () => ({ @@ -23,6 +26,11 @@ jest.mock("backend", () => ({ isEmailTaken: (...args: any[]) => mockIsEmailTaken(...args), updateUser: (...args: any[]) => mockUpdateUser(...args), })); +jest.mock("backend/localStorage", () => ({ + getAvatar: (...args: any[]) => mockGetAvatar(...args), + getCurrentUser: (...args: any[]) => mockGetCurrentUser(...args), +})); + // Mock "i18n", else `thrown: "Error: Error: connect ECONNREFUSED ::1:80 [...]` jest.mock("i18n", () => ({ updateLangFromUser: jest.fn(), @@ -37,7 +45,10 @@ const mockUser = (): User => { }; const setupMocks = (): void => { + mockGetAvatar.mockReturnValue(""); + mockGetCurrentUser.mockReturnValue(mockUser()); mockIsEmailTaken.mockResolvedValue(false); + mockSetUser.mockImplementation(async (user?: User) => {}); mockUpdateUser.mockImplementation((user: User) => user); }; @@ -50,7 +61,13 @@ afterEach(cleanup); const renderUserSettings = async (user = mockUser()): Promise => { await act(async () => { - render(); + render(); + }); +}; + +const renderUserSettingsGetUser = async (): Promise => { + await act(async () => { + render(); }); }; @@ -90,6 +107,38 @@ describe("UserSettings", () => { await typeAndCheckEnabled(UserSettingsIds.FieldPhone); }); + it("disables button when change is saved", async () => { + const agent = userEvent.setup(); + const stringToType = "?"; + const user = mockUser(); + await renderUserSettingsGetUser(); + const submitButton = screen.getByTestId(UserSettingsIds.ButtonSubmit); + + const typeAndCheckEnabled = async (id: UserSettingsIds): Promise => { + expect(submitButton).toBeDisabled(); + await act(async () => { + await agent.type(screen.getByTestId(id), stringToType); + }); + expect(submitButton).toBeEnabled(); + await act(async () => { + await agent.click(submitButton); + }); + expect(submitButton).toBeDisabled(); + }; + + user.email += stringToType; + mockGetCurrentUser.mockReturnValueOnce({ ...user }); + await typeAndCheckEnabled(UserSettingsIds.FieldEmail); + + user.name += stringToType; + mockGetCurrentUser.mockReturnValueOnce({ ...user }); + await typeAndCheckEnabled(UserSettingsIds.FieldName); + + user.phone += stringToType; + mockGetCurrentUser.mockReturnValueOnce({ ...user }); + await typeAndCheckEnabled(UserSettingsIds.FieldPhone); + }); + it("updates user when something is changed and submitted", async () => { const agent = userEvent.setup(); await renderUserSettings();