From c083bb15cdb5c845f83d2d39329aa0be10c91c78 Mon Sep 17 00:00:00 2001 From: gitstart-twenty <140154534+gitstart-twenty@users.noreply.github.com> Date: Fri, 15 Mar 2024 12:32:06 -0300 Subject: [PATCH] First batch of `modules/activities` tests (#4446) Co-authored-by: gitstart-twenty Co-authored-by: v1b3m Co-authored-by: Matheus Co-authored-by: Thiago Nascimbeni --- .../hooks/__tests__/useEmailThread.test.ts | 38 +++++++++ .../__tests__/useEmailThreadStates.test.ts | 37 ++++++++ .../useOpenEmailThreadRightDrawer.test.ts | 35 ++++++++ .../hooks/__tests__/useAttachments.test.ts | 51 +++++++++++ .../hooks/__tests__/useActivityById.test.ts | 38 +++++++++ ...tIntoActivityTargetInlineCellCache.test.ts | 52 ++++++++++++ .../notes/hooks/__tests__/useNotes.test.ts | 45 ++++++++++ .../hooks/__tests__/useCompleteTask.test.ts | 47 ++++++++++ ...njectIntoTimelineActivitiesQueries.test.ts | 85 +++++++++++++++++++ 9 files changed, 428 insertions(+) create mode 100644 packages/twenty-front/src/modules/activities/emails/hooks/__tests__/useEmailThread.test.ts create mode 100644 packages/twenty-front/src/modules/activities/emails/hooks/internal/__tests__/useEmailThreadStates.test.ts create mode 100644 packages/twenty-front/src/modules/activities/emails/right-drawer/hooks/__tests__/useOpenEmailThreadRightDrawer.test.ts create mode 100644 packages/twenty-front/src/modules/activities/files/hooks/__tests__/useAttachments.test.ts create mode 100644 packages/twenty-front/src/modules/activities/hooks/__tests__/useActivityById.test.ts create mode 100644 packages/twenty-front/src/modules/activities/inline-cell/hooks/__tests__/useInjectIntoActivityTargetInlineCellCache.test.ts create mode 100644 packages/twenty-front/src/modules/activities/notes/hooks/__tests__/useNotes.test.ts create mode 100644 packages/twenty-front/src/modules/activities/tasks/hooks/__tests__/useCompleteTask.test.ts create mode 100644 packages/twenty-front/src/modules/activities/timeline/hooks/__tests__/useInjectIntoTimelineActivitiesQueries.test.ts diff --git a/packages/twenty-front/src/modules/activities/emails/hooks/__tests__/useEmailThread.test.ts b/packages/twenty-front/src/modules/activities/emails/hooks/__tests__/useEmailThread.test.ts new file mode 100644 index 000000000..a3bccb817 --- /dev/null +++ b/packages/twenty-front/src/modules/activities/emails/hooks/__tests__/useEmailThread.test.ts @@ -0,0 +1,38 @@ +import { act, renderHook } from '@testing-library/react'; + +import { useEmailThread } from '../useEmailThread'; + +const mockSetViewableEmailThreadId = jest.fn(); +const mockUseOpenEmailThreadRightDrawer = jest.fn(); + +jest.mock('recoil', () => ({ + useSetRecoilState: () => mockSetViewableEmailThreadId, +})); + +jest.mock( + '@/activities/emails/right-drawer/hooks/useOpenEmailThreadRightDrawer', + () => ({ + useOpenEmailThreadRightDrawer: () => mockUseOpenEmailThreadRightDrawer, + }), +); + +jest.mock('@/activities/emails/state/viewableEmailThreadIdState', () => ({ + viewableEmailThreadIdState: () => 'mockViewableEmailThreadIdState', +})); + +describe('useEmailThread hook', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('openEmailThread function', () => { + const { result } = renderHook(() => useEmailThread()); + + act(() => { + result.current.openEmailThread('mockThreadId'); + }); + + expect(mockSetViewableEmailThreadId).toHaveBeenCalledWith('mockThreadId'); + expect(mockUseOpenEmailThreadRightDrawer).toHaveBeenCalled(); + }); +}); diff --git a/packages/twenty-front/src/modules/activities/emails/hooks/internal/__tests__/useEmailThreadStates.test.ts b/packages/twenty-front/src/modules/activities/emails/hooks/internal/__tests__/useEmailThreadStates.test.ts new file mode 100644 index 000000000..a1a65c99c --- /dev/null +++ b/packages/twenty-front/src/modules/activities/emails/hooks/internal/__tests__/useEmailThreadStates.test.ts @@ -0,0 +1,37 @@ +import { renderHook } from '@testing-library/react'; + +import { useEmailThreadStates } from '@/activities/emails/hooks/internal/useEmailThreadStates'; + +const mockScopeId = 'mockScopeId'; +const mockGetEmailThreadsPageState = jest.fn(); + +jest.mock( + '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId', + () => ({ + useAvailableScopeIdOrThrow: () => mockScopeId, + }), +); + +jest.mock( + '@/ui/utilities/state/component-state/utils/extractComponentState', + () => ({ + extractComponentState: () => mockGetEmailThreadsPageState, + }), +); + +describe('useEmailThreadStates hook', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('returns the correct scopeId and getEmailThreadsPageState', () => { + const { result } = renderHook(() => + useEmailThreadStates({ emailThreadScopeId: 'mockEmailThreadScopeId' }), + ); + + expect(result.current.scopeId).toBe(mockScopeId); + expect(result.current.getEmailThreadsPageState).toBe( + mockGetEmailThreadsPageState, + ); + }); +}); diff --git a/packages/twenty-front/src/modules/activities/emails/right-drawer/hooks/__tests__/useOpenEmailThreadRightDrawer.test.ts b/packages/twenty-front/src/modules/activities/emails/right-drawer/hooks/__tests__/useOpenEmailThreadRightDrawer.test.ts new file mode 100644 index 000000000..6e77fc509 --- /dev/null +++ b/packages/twenty-front/src/modules/activities/emails/right-drawer/hooks/__tests__/useOpenEmailThreadRightDrawer.test.ts @@ -0,0 +1,35 @@ +import { act } from 'react-dom/test-utils'; +import { renderHook } from '@testing-library/react'; + +import { useOpenEmailThreadRightDrawer } from '@/activities/emails/right-drawer/hooks/useOpenEmailThreadRightDrawer'; +import { RightDrawerHotkeyScope } from '@/ui/layout/right-drawer/types/RightDrawerHotkeyScope'; +import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages'; + +const mockOpenRightDrawer = jest.fn(); +const mockSetHotkeyScope = jest.fn(); + +jest.mock('@/ui/layout/right-drawer/hooks/useRightDrawer', () => ({ + useRightDrawer: () => ({ + openRightDrawer: mockOpenRightDrawer, + }), +})); + +jest.mock('@/ui/utilities/hotkey/hooks/useSetHotkeyScope', () => ({ + useSetHotkeyScope: () => mockSetHotkeyScope, +})); + +test('useOpenEmailThreadRightDrawer opens the email thread right drawer', () => { + const { result } = renderHook(() => useOpenEmailThreadRightDrawer()); + + act(() => { + result.current(); + }); + + expect(mockSetHotkeyScope).toHaveBeenCalledWith( + RightDrawerHotkeyScope.RightDrawer, + { goto: false }, + ); + expect(mockOpenRightDrawer).toHaveBeenCalledWith( + RightDrawerPages.ViewEmailThread, + ); +}); diff --git a/packages/twenty-front/src/modules/activities/files/hooks/__tests__/useAttachments.test.ts b/packages/twenty-front/src/modules/activities/files/hooks/__tests__/useAttachments.test.ts new file mode 100644 index 000000000..5d582322b --- /dev/null +++ b/packages/twenty-front/src/modules/activities/files/hooks/__tests__/useAttachments.test.ts @@ -0,0 +1,51 @@ +import { renderHook } from '@testing-library/react'; + +import { useAttachments } from '../useAttachments'; + +jest.mock('@/object-record/hooks/useFindManyRecords', () => ({ + useFindManyRecords: jest.fn(), +})); + +describe('useAttachments', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('fetches attachments correctly for a given targetableObject', () => { + const mockAttachments = [ + { id: '1', name: 'Attachment 1' }, + { id: 2, name: 'Attachment 2' }, + ]; + const mockTargetableObject = { + id: '1', + targetObjectNameSingular: 'SomeObject', + }; + + const useFindManyRecordsMock = jest.requireMock( + '@/object-record/hooks/useFindManyRecords', + ); + useFindManyRecordsMock.useFindManyRecords.mockReturnValue({ + records: mockAttachments, + }); + + const { result } = renderHook(() => useAttachments(mockTargetableObject)); + + expect(result.current.attachments).toEqual(mockAttachments); + }); + + it('handles case when there are no attachments', () => { + const mockTargetableObject = { + id: '1', + targetObjectNameSingular: 'SomeObject', + }; + + const useFindManyRecordsMock = jest.requireMock( + '@/object-record/hooks/useFindManyRecords', + ); + useFindManyRecordsMock.useFindManyRecords.mockReturnValue({ records: [] }); + + const { result } = renderHook(() => useAttachments(mockTargetableObject)); + + expect(result.current.attachments).toEqual([]); + }); +}); diff --git a/packages/twenty-front/src/modules/activities/hooks/__tests__/useActivityById.test.ts b/packages/twenty-front/src/modules/activities/hooks/__tests__/useActivityById.test.ts new file mode 100644 index 000000000..40f4521a9 --- /dev/null +++ b/packages/twenty-front/src/modules/activities/hooks/__tests__/useActivityById.test.ts @@ -0,0 +1,38 @@ +import { renderHook } from '@testing-library/react'; +import { RecoilRoot } from 'recoil'; + +import { useActivityById } from '../useActivityById'; + +jest.mock('@/object-record/hooks/useFindOneRecord', () => ({ + useFindOneRecord: jest.fn(() => ({ + record: { + activity: { + id: 'test-activity-id', + name: 'Test Activity', + description: 'This is a test activity', + }, + }, + loading: false, + })), +})); + +describe('useActivityById', () => { + it('fetches activity by id and returns the activity and loading state', async () => { + const activityId = 'test-activity-id'; + const { result } = renderHook(() => useActivityById({ activityId }), { + wrapper: RecoilRoot, + }); + + expect(result.current.loading).toBe(false); + + expect(result.current.activity).toEqual({ + activity: { + id: 'test-activity-id', + name: 'Test Activity', + description: 'This is a test activity', + }, + activityTargets: [], + comments: [], + }); + }); +}); diff --git a/packages/twenty-front/src/modules/activities/inline-cell/hooks/__tests__/useInjectIntoActivityTargetInlineCellCache.test.ts b/packages/twenty-front/src/modules/activities/inline-cell/hooks/__tests__/useInjectIntoActivityTargetInlineCellCache.test.ts new file mode 100644 index 000000000..89b990973 --- /dev/null +++ b/packages/twenty-front/src/modules/activities/inline-cell/hooks/__tests__/useInjectIntoActivityTargetInlineCellCache.test.ts @@ -0,0 +1,52 @@ +import { renderHook } from '@testing-library/react'; + +import { useInjectIntoActivityTargetInlineCellCache } from '@/activities/inline-cell/hooks/useInjectIntoActivityTargetInlineCellCache'; +import { Activity } from '@/activities/types/Activity'; + +jest.mock('@/object-metadata/hooks/useObjectMetadataItemOnly', () => ({ + useObjectMetadataItemOnly: jest.fn(() => ({ + objectMetadataItem: { exampleMetadataItem: 'example' }, + })), +})); + +jest.mock( + '@/object-record/cache/hooks/useUpsertFindManyRecordsQueryInCache', + () => ({ + useUpsertFindManyRecordsQueryInCache: jest.fn(() => ({ + upsertFindManyRecordsQueryInCache: jest.fn(), + })), + }), +); + +describe('useInjectIntoActivityTargetInlineCellCache', () => { + it('should inject into activity target inline cell cache as expected', () => { + const { result } = renderHook(() => + useInjectIntoActivityTargetInlineCellCache(), + ); + + const { injectIntoActivityTargetInlineCellCache } = result.current; + + const mockActivityId = 'mockId'; + const mockActivityTargetsToInject = [ + { + id: '1', + name: 'Example Activity Target', + createdAt: '2022-01-01', + updatedAt: '2022-01-01', + activity: { + id: '1', + createdAt: '2022-01-01', + updatedAt: '2022-01-01', + } as Pick, + }, + ]; + injectIntoActivityTargetInlineCellCache({ + activityId: mockActivityId, + activityTargetsToInject: mockActivityTargetsToInject, + }); + + expect( + result.current.injectIntoActivityTargetInlineCellCache, + ).toBeDefined(); + }); +}); diff --git a/packages/twenty-front/src/modules/activities/notes/hooks/__tests__/useNotes.test.ts b/packages/twenty-front/src/modules/activities/notes/hooks/__tests__/useNotes.test.ts new file mode 100644 index 000000000..f64b745f0 --- /dev/null +++ b/packages/twenty-front/src/modules/activities/notes/hooks/__tests__/useNotes.test.ts @@ -0,0 +1,45 @@ +import { renderHook } from '@testing-library/react'; + +import { useNotes } from '@/activities/notes/hooks/useNotes'; +import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity'; + +jest.mock('@/activities/hooks/useActivities', () => ({ + useActivities: jest.fn(() => ({ + activities: [{ id: '1', content: 'Example Note' }], + initialized: true, + loading: false, + })), +})); + +jest.mock('recoil', () => { + const actualRecoil = jest.requireActual('recoil'); + return { + ...actualRecoil, + useRecoilState: jest.fn(() => { + const mockCurrentNotesQueryVariables = { + filter: { + type: { eq: 'Note' }, + }, + orderBy: 'mockOrderBy', + }; + return [mockCurrentNotesQueryVariables, jest.fn()]; + }), + atom: jest.fn(), + }; +}); + +describe('useNotes', () => { + it('should return notes, initialized, and loading as expected', () => { + const mockTargetableObject: ActivityTargetableObject = { + id: '1', + targetObjectNameSingular: 'Example Target', + }; + const { result } = renderHook(() => useNotes(mockTargetableObject)); + + expect(result.current.notes).toEqual([ + { id: '1', content: 'Example Note' }, + ]); + expect(result.current.initialized).toBe(true); + expect(result.current.loading).toBe(false); + }); +}); diff --git a/packages/twenty-front/src/modules/activities/tasks/hooks/__tests__/useCompleteTask.test.ts b/packages/twenty-front/src/modules/activities/tasks/hooks/__tests__/useCompleteTask.test.ts new file mode 100644 index 000000000..e74a741b1 --- /dev/null +++ b/packages/twenty-front/src/modules/activities/tasks/hooks/__tests__/useCompleteTask.test.ts @@ -0,0 +1,47 @@ +import { renderHook } from '@testing-library/react'; + +import { useCompleteTask } from '@/activities/tasks/hooks/useCompleteTask'; + +const mockUpdateOneRecord = jest.fn(); +jest.mock('@/object-record/hooks/useUpdateOneRecord', () => ({ + useUpdateOneRecord: () => ({ + updateOneRecord: mockUpdateOneRecord, + }), +})); + +describe('useCompleteTask', () => { + it('should complete the task when called with true', async () => { + const taskId = 'test-task-id'; + const { result } = renderHook(() => + useCompleteTask({ id: taskId, completedAt: null }), + ); + + const { completeTask } = result.current; + completeTask(true); + + expect(mockUpdateOneRecord).toHaveBeenCalledWith({ + idToUpdate: taskId, + updateOneRecordInput: { + completedAt: expect.any(String), + }, + }); + }); + + it('should uncomplete the task when called with false', async () => { + const taskId = 'test-task-id'; + const { result } = renderHook(() => + useCompleteTask({ id: taskId, completedAt: '2021-01-01T00:00:00' }), + ); + + const { completeTask } = result.current; + + completeTask(false); + + expect(mockUpdateOneRecord).toHaveBeenCalledWith({ + idToUpdate: taskId, + updateOneRecordInput: { + completedAt: null, + }, + }); + }); +}); diff --git a/packages/twenty-front/src/modules/activities/timeline/hooks/__tests__/useInjectIntoTimelineActivitiesQueries.test.ts b/packages/twenty-front/src/modules/activities/timeline/hooks/__tests__/useInjectIntoTimelineActivitiesQueries.test.ts new file mode 100644 index 000000000..7b033859e --- /dev/null +++ b/packages/twenty-front/src/modules/activities/timeline/hooks/__tests__/useInjectIntoTimelineActivitiesQueries.test.ts @@ -0,0 +1,85 @@ +import { renderHook } from '@testing-library/react'; + +import { useInjectIntoActivitiesQueries } from '@/activities/hooks/useInjectIntoActivitiesQueries'; +import { useInjectIntoTimelineActivitiesQueries } from '@/activities/timeline/hooks/useInjectIntoTimelineActivitiesQueries'; +import { Activity } from '@/activities/types/Activity'; +import { ActivityTarget } from '@/activities/types/ActivityTarget'; +import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity'; + +jest.mock('@/activities/hooks/useInjectIntoActivitiesQueries', () => ({ + useInjectIntoActivitiesQueries: jest.fn(() => ({ + injectActivitiesQueries: jest.fn(), + })), +})); + +describe('useInjectIntoTimelineActivitiesQueries', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should inject activities into timeline activities queries correctly', () => { + const mockActivityToInject: Activity = { + id: 'activity1', + createdAt: '2022-01-01T00:00:00', + updatedAt: '2022-01-01T00:00:00', + completedAt: '2022-01-01T00:00:00', + __typename: 'Activity', + reminderAt: '2022-01-01T00:00:00', + dueAt: '2022-01-01T00:00:00', + type: 'Task', + activityTargets: [], + title: 'Activity 1', + body: 'Activity 1 body', + author: { + id: 'author1', + name: { + firstName: 'John', + lastName: 'Doe', + }, + avatarUrl: 'https://example.com/avatar1.jpg', + }, + authorId: 'author1', + assignee: null, + assigneeId: null, + comments: [], + }; + const mockActivityTargetsToInject: ActivityTarget[] = [ + { + id: 'target1', + updatedAt: '2022-01-01T00:00:00', + createdAt: '2022-01-01T00:00:00', + activity: { + id: 'activity1', + createdAt: '2022-01-01T00:00:00', + updatedAt: '2022-01-01T00:00:00', + }, + }, + { + id: 'target2', + updatedAt: '2022-01-01T00:00:00', + createdAt: '2022-01-01T00:00:00', + activity: { + id: 'activity1', + createdAt: '2022-01-01T00:00:00', + updatedAt: '2022-01-01T00:00:00', + }, + }, + ]; + const mockTimelineTargetableObject: ActivityTargetableObject = { + id: 'timelineTarget1', + targetObjectNameSingular: 'Timeline', + }; + + const { result } = renderHook(() => + useInjectIntoTimelineActivitiesQueries(), + ); + + result.current.injectIntoTimelineActivitiesQueries({ + activityToInject: mockActivityToInject, + activityTargetsToInject: mockActivityTargetsToInject, + timelineTargetableObject: mockTimelineTargetableObject, + }); + + expect(useInjectIntoActivitiesQueries).toHaveBeenCalledTimes(1); + }); +});