diff --git a/src/containers/edit-profile/profile-tab/ProfileTab.tsx b/src/containers/edit-profile/profile-tab/ProfileTab.tsx index 0ad239c989..eff2bd2552 100644 --- a/src/containers/edit-profile/profile-tab/ProfileTab.tsx +++ b/src/containers/edit-profile/profile-tab/ProfileTab.tsx @@ -14,7 +14,7 @@ import { updateProfileData, updateValidityStatus } from '~/redux/features/editProfileSlice' -import { EditProfileForm, MainUserRole } from '~/types' +import { EditProfileForm, MainUserRole, UserRoleEnum } from '~/types' import { styles } from '~/containers/edit-profile/profile-tab/ProfileTab.styles' import { scrollToAndHighlight } from '~/utils/scroll-and-highlight' import { fieldsWithIncreasedHeight } from '~/components/profile-item/complete-profile.constants' @@ -42,10 +42,7 @@ const ProfileTab: FC = () => { nativeLanguage, photo: photo ?? '', professionalSummary: professionalSummary || '', - videoLink: - typeof videoLink === 'string' - ? videoLink - : (videoLink?.[userRole as MainUserRole] ?? '') + videoLink: videoLink?.[userRole as MainUserRole] || '' } const { @@ -62,7 +59,15 @@ const ProfileTab: FC = () => { }) const debouncedUpdateProfileData = useDebounce(() => { - void dispatch(updateProfileData(data)) + const payload = { + ...data, + videoLink: { + student: userRole === UserRoleEnum.Student ? data.videoLink : null, + tutor: userRole === UserRoleEnum.Tutor ? data.videoLink : null + } + } + + dispatch(updateProfileData(payload)) }, 300) const { hash, pathname } = useLocation() diff --git a/src/containers/edit-profile/profile-tab/profile-tab-form/ProfileTabForm.tsx b/src/containers/edit-profile/profile-tab/profile-tab-form/ProfileTabForm.tsx index 99981b6cda..8887a2b193 100644 --- a/src/containers/edit-profile/profile-tab/profile-tab-form/ProfileTabForm.tsx +++ b/src/containers/edit-profile/profile-tab/profile-tab-form/ProfileTabForm.tsx @@ -99,8 +99,7 @@ const ProfileTabForm: FC = ({ void resizeImage(files[0]) } const handleRemovePhoto = () => { - const updatedPhoto = - typeof photo === 'string' ? null : { src: '', name: '' } + const updatedPhoto = '' handleNonInputValueChange('photo', updatedPhoto) } diff --git a/src/containers/navigation-icons/AccountIcon.tsx b/src/containers/navigation-icons/AccountIcon.tsx index 718d91854f..cacad1dca1 100644 --- a/src/containers/navigation-icons/AccountIcon.tsx +++ b/src/containers/navigation-icons/AccountIcon.tsx @@ -12,8 +12,9 @@ import { defaultResponses } from '~/constants' import { styles } from '~/containers/navigation-icons/NavigationIcons.styles' -import { UserResponse, UserRole } from '~/types' +import { UpdatedPhoto, UserResponse, UserRole } from '~/types' import { createUrlPath } from '~/utils/helper-functions' +import { isUpdatedPhoto } from '~/utils/is-updated-photo' interface AccountIconProps { openMenu: (event: MouseEvent) => void @@ -40,8 +41,15 @@ const AccountIcon: FC = ({ openMenu }) => { const { photo: statePhoto } = useAppSelector((state) => state.editProfile) const avatarSrc = useMemo(() => { - if (statePhoto?.src) { - return statePhoto.src + if (isUpdatedPhoto(statePhoto)) { + return (statePhoto as UpdatedPhoto).src + } + + if (typeof statePhoto === 'string') { + return createUrlPath( + import.meta.env.VITE_APP_IMG_USER_URL || '', + statePhoto + ) } if (photo) { diff --git a/src/pages/edit-profile/EditProfile.tsx b/src/pages/edit-profile/EditProfile.tsx index 116255d0d7..92ab06b3ac 100644 --- a/src/pages/edit-profile/EditProfile.tsx +++ b/src/pages/edit-profile/EditProfile.tsx @@ -12,7 +12,6 @@ import Box from '@mui/material/Box' import Divider from '@mui/material/Divider' import useConfirm from '~/hooks/use-confirm' - import { useAppDispatch, useAppSelector } from '~/hooks/use-redux' import Loader from '~/components/loader/Loader' import PageWrapper from '~/components/page-wrapper/PageWrapper' @@ -23,11 +22,10 @@ import { SizeEnum, UpdateUserParams, UserProfileTabsEnum, - UserRole + UserRole, + DataByRole } from '~/types' import { tabsData } from '~/pages/edit-profile/EditProfile.constants' - -import { styles } from '~/pages/edit-profile/EditProfile.styles' import { fetchUserById, updateUser, @@ -38,6 +36,9 @@ import { openAlert } from '~/redux/features/snackbarSlice' import { snackbarVariants } from '~/constants' import { authRoutes } from '~/router/constants/authRoutes' +import { styles } from '~/pages/edit-profile/EditProfile.styles' +import { hasPhotoChanges } from '~/utils/has-photo-changes' + const EditProfile = () => { const [initialEditProfileState, setInitialEditProfileState] = useState< typeof profileState | null @@ -74,8 +75,8 @@ const EditProfile = () => { activeTab === UserProfileTabsEnum.PasswordAndSecurity const hasChanges = ( - initialData: Partial, - currentData: Partial + initialData: Partial | DataByRole, + currentData: Partial | DataByRole ): boolean => { return JSON.stringify(initialData) !== JSON.stringify(currentData) } @@ -87,6 +88,10 @@ const EditProfile = () => { ) } void fetchData() + + return () => { + void fetchData() + } }, [dispatch, userId, userRole]) useEffect(() => { @@ -100,19 +105,29 @@ const EditProfile = () => { const changedFields = useMemo>(() => { if (!initialEditProfileState || !profileState) return {} + const { videoLink: initialVideoLink } = initialEditProfileState + const { videoLink: currentVideoLink } = profileState const { photo: initialPhoto, ...initialData } = initialEditProfileState const { photo: currentPhoto, ...currentData } = profileState - const hasChanged = - hasChanges(initialData, currentData) || initialPhoto !== currentPhoto + const hasPhotoChanged = hasPhotoChanges(initialPhoto, currentPhoto) + + const hasChanged = hasChanges(initialData, currentData) || hasPhotoChanged if (hasChanged) { - const changes: Partial = { ...currentData } + const changes: Partial = { + ...currentData + } + + if (!hasChanges(initialVideoLink, currentVideoLink)) { + delete changes.videoLink + } - if (initialPhoto === currentPhoto) { - delete changes.photo + if (hasPhotoChanged) { + changes.photo = currentPhoto } + return changes } else { return {} @@ -149,6 +164,7 @@ const EditProfile = () => { professionalBlock, aboutStudent, categories, + photo, ...rest } = changedFields @@ -156,11 +172,10 @@ const EditProfile = () => { if (city && country) dataToUpdate.address = { city, country } - if (typeof videoLink === 'string' || typeof videoLink === 'undefined') { - dataToUpdate.videoLink = videoLink ?? '' - } else if (typeof videoLink === 'object') { - dataToUpdate.videoLink = - videoLink[userRole as keyof typeof videoLink] || '' + if (videoLink) { + const updatedVideolink = videoLink[userRole as keyof DataByRole] + + dataToUpdate.videoLink = updatedVideolink } if (notificationSettings) @@ -177,8 +192,8 @@ const EditProfile = () => { dataToUpdate.mainSubjects = categories } - if (typeof profileState.photo === 'object') { - dataToUpdate.photo = profileState.photo + if (typeof photo === 'object' || photo === '') { + dataToUpdate.photo = photo } await dispatch( diff --git a/src/redux/features/editProfileSlice.ts b/src/redux/features/editProfileSlice.ts index 6cae90ce2f..cc1278a4b1 100644 --- a/src/redux/features/editProfileSlice.ts +++ b/src/redux/features/editProfileSlice.ts @@ -7,14 +7,14 @@ import { } from '~/redux/redux.constants' import { DataByRole, - EditProfileForm, + EditProfileFormSubmitData, ErrorResponse, MainUserRole, NotificationSettings, ProfessionalBlock, AboutStudentData, SubjectNameInterface, - UpdatedPhoto, + EditProfilePhoto, UpdateUserParams, UserMainSubject, UserMainSubjectFieldValues, @@ -31,8 +31,8 @@ export interface EditProfileState { city: string | null professionalSummary?: string nativeLanguage: string | null - videoLink: DataByRole | string - photo?: UpdatedPhoto | null + videoLink: DataByRole + photo: EditProfilePhoto categories: DataByRole professionalBlock: ProfessionalBlock aboutStudent: AboutStudentData @@ -113,8 +113,11 @@ const updateStateFromPayload = ( state.city = address?.city ?? null state.professionalSummary = professionalSummary state.nativeLanguage = nativeLanguage - state.photo = photo as UpdatedPhoto | null - state.videoLink = videoLink + state.photo = photo ?? null + state.videoLink = { + [UserRoleEnum.Tutor]: videoLink?.[UserRoleEnum.Tutor] ?? '', + [UserRoleEnum.Student]: videoLink?.[UserRoleEnum.Student] ?? '' + } state.categories = mainSubjects state.professionalBlock = professionalBlock || initialProfessoinalBlock state.aboutStudent = aboutStudent || initialAboutStudent @@ -180,7 +183,10 @@ const editProfileSlice = createSlice({ const { tab, value } = action.payload state.tabValidityStatus[tab] = value }, - updateProfileData: (state, action: PayloadAction) => { + updateProfileData: ( + state, + action: PayloadAction + ) => { const { city, country, @@ -197,9 +203,16 @@ const editProfileSlice = createSlice({ state.firstName = firstName state.lastName = lastName state.nativeLanguage = nativeLanguage - state.photo = photo as UpdatedPhoto + state.photo = photo ?? null state.professionalSummary = professionalSummary - state.videoLink = videoLink + + if (videoLink?.[UserRoleEnum.Tutor] !== null) { + state.videoLink[UserRoleEnum.Tutor] = videoLink[UserRoleEnum.Tutor] + } + + if (videoLink?.[UserRoleEnum.Student] !== null) { + state.videoLink[UserRoleEnum.Student] = videoLink[UserRoleEnum.Student] + } }, addCategory: ( state, diff --git a/src/types/edit-profile/interfaces/editProfile.interfaces.ts b/src/types/edit-profile/interfaces/editProfile.interfaces.ts index c9d1408878..362f66f22b 100644 --- a/src/types/edit-profile/interfaces/editProfile.interfaces.ts +++ b/src/types/edit-profile/interfaces/editProfile.interfaces.ts @@ -1,9 +1,8 @@ import { CategoryInterface, - SubjectNameInterface, - UpdatedPhoto + SubjectNameInterface } from '~/types/common/common.index' -import { UserResponse } from '~/types/user/user.index' +import { EditProfilePhoto, UserResponse, VideoUserRole } from '~/types' export interface EditProfileForm extends Pick { @@ -12,7 +11,12 @@ export interface EditProfileForm professionalSummary: string nativeLanguage: string | null videoLink: string - photo: string | UpdatedPhoto | null + photo: EditProfilePhoto +} + +export interface EditProfileFormSubmitData + extends Omit { + videoLink: { [key in VideoUserRole]: string | null } } export interface ProfessionalCategory { diff --git a/src/types/edit-profile/types/editProfile.types.ts b/src/types/edit-profile/types/editProfile.types.ts index bf7bed97f5..b3f5aef57c 100644 --- a/src/types/edit-profile/types/editProfile.types.ts +++ b/src/types/edit-profile/types/editProfile.types.ts @@ -1,7 +1,8 @@ import { CategoryNameInterface, SubjectNameInterface, - UserMainSubject + UserMainSubject, + UpdatedPhoto } from '~/types' export type OpenProfessionalCategoryModalHandler = ( @@ -13,3 +14,5 @@ export type UserMainSubjectFieldValues = string & boolean & CategoryNameInterface & SubjectNameInterface[] + +export type EditProfilePhoto = UpdatedPhoto | string | null diff --git a/src/types/user/user-interfaces/user.interfaces.ts b/src/types/user/user-interfaces/user.interfaces.ts index 00f93fb55d..4f570d3c51 100644 --- a/src/types/user/user-interfaces/user.interfaces.ts +++ b/src/types/user/user-interfaces/user.interfaces.ts @@ -6,7 +6,7 @@ import { RequestParams, Faq, DataByRole, - UpdatedPhoto, + EditProfilePhoto, UpdateFields, UserStatusEnum, UserMainSubject, @@ -79,7 +79,7 @@ export interface UpdateUserParams extends Partial> { mainSubjects?: DataByRole videoLink?: string - photo?: UpdatedPhoto | null + photo?: EditProfilePhoto } export interface LoginParams { diff --git a/src/utils/are-all-values-empty-strings.ts b/src/utils/are-all-values-empty-strings.ts new file mode 100644 index 0000000000..d03387ccde --- /dev/null +++ b/src/utils/are-all-values-empty-strings.ts @@ -0,0 +1,7 @@ +export const areAllValuesEmptyStrings = (obj: { + [key: string]: string +}): boolean => { + return Object.values(obj).every( + (value) => typeof value === 'string' && value === '' + ) +} diff --git a/src/utils/has-photo-changes.ts b/src/utils/has-photo-changes.ts new file mode 100644 index 0000000000..0808fe8e3f --- /dev/null +++ b/src/utils/has-photo-changes.ts @@ -0,0 +1,38 @@ +import { EditProfilePhoto, UpdatedPhoto } from '~/types' +import { isUpdatedPhoto } from '~/utils/is-updated-photo' +import { areAllValuesEmptyStrings } from '~/utils/are-all-values-empty-strings' + +export const hasPhotoChanges = ( + initialPhoto: EditProfilePhoto, + currentPhoto: EditProfilePhoto +): boolean => { + if (initialPhoto !== '' && currentPhoto === '') { + return true + } + + if ( + typeof initialPhoto === 'string' && + isUpdatedPhoto(currentPhoto) && + (currentPhoto as UpdatedPhoto).name !== initialPhoto + ) { + return true + } + + if ( + initialPhoto === null && + isUpdatedPhoto(currentPhoto) && + areAllValuesEmptyStrings(currentPhoto as UpdatedPhoto) + ) { + return true + } + + if ( + isUpdatedPhoto(initialPhoto) && + isUpdatedPhoto(currentPhoto) && + (initialPhoto as UpdatedPhoto).name !== (currentPhoto as UpdatedPhoto).name + ) { + return true + } + + return false +} diff --git a/src/utils/is-updated-photo.ts b/src/utils/is-updated-photo.ts new file mode 100644 index 0000000000..57040f1183 --- /dev/null +++ b/src/utils/is-updated-photo.ts @@ -0,0 +1,10 @@ +import { EditProfilePhoto } from '~/types' + +export const isUpdatedPhoto = (photo: EditProfilePhoto): boolean => { + return ( + photo !== null && + typeof photo === 'object' && + 'name' in photo && + 'src' in photo + ) +} diff --git a/tests/unit/containers/edit-profile/profile-tab/profile-tab-form/ProfileTabForm.spec.jsx b/tests/unit/containers/edit-profile/profile-tab/profile-tab-form/ProfileTabForm.spec.jsx index f18bfc3e9d..a8d29c22b6 100644 --- a/tests/unit/containers/edit-profile/profile-tab/profile-tab-form/ProfileTabForm.spec.jsx +++ b/tests/unit/containers/edit-profile/profile-tab/profile-tab-form/ProfileTabForm.spec.jsx @@ -56,7 +56,7 @@ describe('ProfileTabForm', () => { const removePhotoBtn = screen.getByRole('button', { name: 'common.remove' }) fireEvent.click(removePhotoBtn) - expect(handleNonInputValueChange).toHaveBeenCalledWith('photo', null) + expect(handleNonInputValueChange).toHaveBeenCalledWith('photo', '') }) it('should handle adding a photo', async () => { diff --git a/tests/unit/pages/edit-profile/EditProfile.spec.jsx b/tests/unit/pages/edit-profile/EditProfile.spec.jsx index 032f41decc..78316f4332 100644 --- a/tests/unit/pages/edit-profile/EditProfile.spec.jsx +++ b/tests/unit/pages/edit-profile/EditProfile.spec.jsx @@ -33,13 +33,14 @@ const mockState = { isSimilarOffersNotification: false, isEmailNotification: false } - } + }, + videoLink: { tutor: '', student: '' } } } const userMock = { role: userRole, - videoLink: { [userRole]: '' }, + videoLink: { tutor: '', student: '' }, mainSubjects: { [userRole]: [] }, firstName: 'John', lastName: 'Doe', @@ -226,53 +227,41 @@ describe('EditProfile', () => { expect(dataToUpdate).toHaveProperty('address', { city, country }) }) - it('should not include photo in dataToUpdate if profileState.photo is not an object', () => { + it('should not include photo in dataToUpdate if profileState.photo is a filled string', () => { const photo = 'stringInsteadOfObject' const profileState = { photo } const rest = {} const dataToUpdate = { ...rest } - if (typeof profileState.photo === 'object') { + if (typeof profileState.photo === 'object' || profileState.photo === '') { dataToUpdate.photo = profileState.photo } expect(dataToUpdate).not.toHaveProperty('photo') }) - it('should not include videoLink in dataToUpdate if videoLink is null', () => { - const videoLink = null - const rest = {} - - const dataToUpdate = { ...rest } - if (videoLink) { - dataToUpdate.videoLink = - typeof videoLink === 'string' ? videoLink : videoLink[userRole] - } - - expect(dataToUpdate).not.toHaveProperty('videoLink') - }) - - it('should include videoLink in dataToUpdate if videoLink is a string', () => { - const videoLink = 'http://video1234556443.com/video' + it('should include photo in dataToUpdate if profileState.photo is empty string', () => { + const photo = '' + const profileState = { photo } const rest = {} const dataToUpdate = { ...rest } - if (videoLink) { - dataToUpdate.videoLink = - typeof videoLink === 'string' ? videoLink : videoLink[userRole] + if (typeof profileState.photo === 'object' || profileState.photo === '') { + dataToUpdate.photo = profileState.photo } - expect(dataToUpdate).toHaveProperty('videoLink', videoLink) + expect(dataToUpdate).toHaveProperty('photo') }) - it('should include videoLink from userRole in dataToUpdate if videoLink is an object', () => { + it('should include string videoLink from userRole in dataToUpdate if videoLink do exist', () => { const videoLink = { tutor: 'http://video1111111.com/video' } const rest = {} const dataToUpdate = { ...rest } if (videoLink) { - dataToUpdate.videoLink = - typeof videoLink === 'string' ? videoLink : videoLink[userRole] + const updatedVideolink = videoLink[userRole] + + dataToUpdate.videoLink = updatedVideolink } expect(dataToUpdate).toHaveProperty('videoLink', videoLink[userRole]) diff --git a/tests/unit/redux/editProfileSlice.spec.js b/tests/unit/redux/editProfileSlice.spec.js index d87500612e..5d9ad21c51 100644 --- a/tests/unit/redux/editProfileSlice.spec.js +++ b/tests/unit/redux/editProfileSlice.spec.js @@ -1,4 +1,5 @@ import { configureStore } from '@reduxjs/toolkit' +import { vi } from 'vitest' import reducer, { setField, updateValidityStatus, @@ -403,7 +404,10 @@ describe('editProfileSlice test', () => { nativeLanguage: 'nativeLanguage', photo: 'photo', professionalSummary: 'professionalSummary', - videoLink: 'videoLink' + videoLink: { + student: undefined, + tutor: 'videoLink' + } }) expect( @@ -417,7 +421,10 @@ describe('editProfileSlice test', () => { nativeLanguage: 'nativeLanguage', photo: 'photo', professionalSummary: 'professionalSummary', - videoLink: 'videoLink' + videoLink: { + student: undefined, + tutor: 'videoLink' + } }) ) ).toEqual(expectedState) diff --git a/tests/unit/utils/are-all-values-empty-strings.spec.js b/tests/unit/utils/are-all-values-empty-strings.spec.js new file mode 100644 index 0000000000..a6f1837646 --- /dev/null +++ b/tests/unit/utils/are-all-values-empty-strings.spec.js @@ -0,0 +1,15 @@ +import { expect } from 'vitest' + +import { areAllValuesEmptyStrings } from '~/utils/are-all-values-empty-strings' + +describe('areAllValuesEmptyStrings', () => { + it('should return true if all values are empty strings', () => { + const obj = { a: '', b: '', c: '' } + expect(areAllValuesEmptyStrings(obj)).toBe(true) + }) + + it('should return false if any value is not an empty string', () => { + const obj = { a: '', b: 'hello', c: '' } + expect(areAllValuesEmptyStrings(obj)).toBe(false) + }) +}) diff --git a/tests/unit/utils/has-photo-changes.spec.js b/tests/unit/utils/has-photo-changes.spec.js new file mode 100644 index 0000000000..75724b2893 --- /dev/null +++ b/tests/unit/utils/has-photo-changes.spec.js @@ -0,0 +1,74 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest' +import { hasPhotoChanges } from '~/utils/has-photo-changes' +import { isUpdatedPhoto } from '~/utils/is-updated-photo' +import { areAllValuesEmptyStrings } from '~/utils/are-all-values-empty-strings' + +vi.mock('~/utils/is-updated-photo', () => ({ + isUpdatedPhoto: vi.fn() +})) + +vi.mock('~/utils/are-all-values-empty-strings', () => ({ + areAllValuesEmptyStrings: vi.fn() +})) + +describe('hasPhotoChanges', () => { + beforeEach(() => { + vi.clearAllMocks() + }) + + it('should return true if initialPhoto is not empty and currentPhoto is empty', () => { + const initialPhoto = 'initialPhoto' + const currentPhoto = '' + isUpdatedPhoto.mockReturnValue(false) + areAllValuesEmptyStrings.mockReturnValue(false) + expect(hasPhotoChanges(initialPhoto, currentPhoto)).toBe(true) + }) + + it('should return true if initialPhoto is a string and currentPhoto is an UpdatedPhoto with a different name', () => { + const initialPhoto = 'initialPhoto' + const currentPhoto = { src: 'src', name: 'newName' } + isUpdatedPhoto.mockReturnValue(true) + areAllValuesEmptyStrings.mockReturnValue(false) + expect(hasPhotoChanges(initialPhoto, currentPhoto)).toBe(true) + }) + + it('should return true if initialPhoto is null and currentPhoto is an UpdatedPhoto with all values empty strings', () => { + const initialPhoto = null + const currentPhoto = { src: '', name: '' } + isUpdatedPhoto.mockReturnValue(true) + areAllValuesEmptyStrings.mockReturnValue(true) + expect(hasPhotoChanges(initialPhoto, currentPhoto)).toBe(true) + }) + + it('should return true if both initialPhoto and currentPhoto are UpdatedPhotos with different names', () => { + const initialPhoto = { src: 'src1', name: 'name1' } + const currentPhoto = { src: 'src2', name: 'name2' } + isUpdatedPhoto.mockReturnValue(true) + areAllValuesEmptyStrings.mockReturnValue(false) + expect(hasPhotoChanges(initialPhoto, currentPhoto)).toBe(true) + }) + + it('should return false if there are no changes', () => { + const initialPhoto = { src: 'src', name: 'name' } + const currentPhoto = { src: 'src', name: 'name' } + isUpdatedPhoto.mockReturnValue(true) + areAllValuesEmptyStrings.mockReturnValue(false) + expect(hasPhotoChanges(initialPhoto, currentPhoto)).toBe(false) + }) + + it('should return false if both initialPhoto and currentPhoto are empty strings', () => { + const initialPhoto = '' + const currentPhoto = '' + isUpdatedPhoto.mockReturnValue(false) + areAllValuesEmptyStrings.mockReturnValue(false) + expect(hasPhotoChanges(initialPhoto, currentPhoto)).toBe(false) + }) + + it('should return false if both initialPhoto and currentPhoto are null', () => { + const initialPhoto = null + const currentPhoto = null + isUpdatedPhoto.mockReturnValue(false) + areAllValuesEmptyStrings.mockReturnValue(false) + expect(hasPhotoChanges(initialPhoto, currentPhoto)).toBe(false) + }) +}) diff --git a/tests/unit/utils/is-updated-photo.spec.js b/tests/unit/utils/is-updated-photo.spec.js new file mode 100644 index 0000000000..8355553b0a --- /dev/null +++ b/tests/unit/utils/is-updated-photo.spec.js @@ -0,0 +1,29 @@ +import { expect } from 'vitest' + +import { isUpdatedPhoto } from '~/utils/is-updated-photo' + +describe('isUpdatedPhoto', () => { + it('should return true if the photo has structure of UpdatedPhoto', () => { + const photo = { name: 'photo-name', src: 'photo-src' } + + expect(isUpdatedPhoto(photo)).toBe(true) + }) + + it('should return false if the photo is null', () => { + const photo = null + + expect(isUpdatedPhoto(photo)).toBe(false) + }) + + it('should return false if the photo is a string', () => { + const photo = 'not-an-object' + + expect(isUpdatedPhoto(photo)).toBe(false) + }) + + it('should return false if the photo does not have a name property', () => { + const photo = { src: 'photo-src' } + + expect(isUpdatedPhoto(photo)).toBe(false) + }) +})