diff --git a/packages/twenty-front/src/modules/activities/hooks/__tests__/useActivities.test.tsx b/packages/twenty-front/src/modules/activities/hooks/__tests__/useActivities.test.tsx
index 2985f122a..23229f581 100644
--- a/packages/twenty-front/src/modules/activities/hooks/__tests__/useActivities.test.tsx
+++ b/packages/twenty-front/src/modules/activities/hooks/__tests__/useActivities.test.tsx
@@ -1,14 +1,17 @@
-import { gql } from '@apollo/client';
-import { MockedProvider, MockedResponse } from '@apollo/client/testing';
-import { act, renderHook, waitFor } from '@testing-library/react';
+import { renderHook } from '@testing-library/react';
import { ReactNode } from 'react';
-import { RecoilRoot, useSetRecoilState } from 'recoil';
+import { RecoilRoot } from 'recoil';
import { useActivities } from '@/activities/hooks/useActivities';
-import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
-import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
-import { mockWorkspaceMembers } from '~/testing/mock-data/workspace-members';
+
+jest.mock('@/activities/hooks/useActivityTargetsForTargetableObjects', () => ({
+ useActivityTargetsForTargetableObjects: jest.fn(),
+}));
+
+jest.mock('@/object-record/hooks/useFindManyRecords', () => ({
+ useFindManyRecords: jest.fn(),
+}));
const mockActivityTarget = {
__typename: 'ActivityTarget',
@@ -22,6 +25,7 @@ const mockActivityTarget = {
const mockActivity = {
__typename: 'Task',
+ companyId: '123',
updatedAt: '2021-08-03T19:20:06.000Z',
createdAt: '2021-08-03T19:20:06.000Z',
status: 'DONE',
@@ -33,168 +37,35 @@ const mockActivity = {
id: '234',
};
-const defaultResponseData = {
- pageInfo: {
- hasNextPage: false,
- startCursor: '',
- endCursor: '',
- },
- totalCount: 1,
-};
-
-const mocks: MockedResponse[] = [
- {
- request: {
- query: gql`
- query FindManyActivityTargets(
- $filter: ActivityTargetFilterInput
- $orderBy: [ActivityTargetOrderByInput]
- $lastCursor: String
- $limit: Int
- ) {
- activityTargets(
- filter: $filter
- orderBy: $orderBy
- first: $limit
- after: $lastCursor
- ) {
- edges {
- node {
- __typename
- updatedAt
- createdAt
- activityId
- id
- }
- cursor
- }
- pageInfo {
- hasNextPage
- hasPreviousPage
- startCursor
- endCursor
- }
- totalCount
- }
- }
- `,
- variables: {
- filter: { companyId: { eq: '123' } },
- limit: undefined,
- orderBy: undefined,
- },
- },
- result: jest.fn(() => ({
- data: {
- activityTargets: {
- ...defaultResponseData,
- edges: [
- {
- node: mockActivityTarget,
- cursor: '1',
- },
- ],
- },
- },
- })),
- },
- {
- request: {
- query: gql`
- query FindManyTasks(
- $filter: TaskFilterInput
- $orderBy: [TaskOrderByInput]
- $lastCursor: String
- $limit: Int
- ) {
- tasks(
- filter: $filter
- orderBy: $orderBy
- first: $limit
- after: $lastCursor
- ) {
- edges {
- node {
- __typename
- title
- id
- updatedAt
- createdAt
- body
- status
- dueAt
- }
- cursor
- }
- pageInfo {
- hasNextPage
- hasPreviousPage
- startCursor
- endCursor
- }
- totalCount
- }
- }
- `,
- variables: {
- filter: { id: { in: ['234'] } },
- limit: undefined,
- orderBy: [{}],
- },
- },
- result: jest.fn(() => ({
- data: {
- activities: {
- ...defaultResponseData,
- edges: [
- {
- node: mockActivity,
- cursor: '1',
- },
- ],
- },
- },
- })),
- },
-];
-
const Wrapper = ({ children }: { children: ReactNode }) => (
-
-
-
- {children}
-
-
-
+ {children}
);
describe('useActivities', () => {
- it('returns default response', () => {
- const { result } = renderHook(
- () =>
- useActivities({
- objectNameSingular: CoreObjectNameSingular.Task,
- targetableObjects: [],
- activitiesFilters: {},
- activitiesOrderByVariables: [{}],
- skip: false,
- }),
- { wrapper: Wrapper },
- );
-
- expect(result.current).toEqual({
- activities: [],
- loading: false,
- });
+ afterEach(() => {
+ jest.clearAllMocks();
});
it('fetches activities', async () => {
+ const useActivityTargetsForTargetableObjectsMock = jest.requireMock(
+ '@/activities/hooks/useActivityTargetsForTargetableObjects',
+ );
+ useActivityTargetsForTargetableObjectsMock.useActivityTargetsForTargetableObjects.mockReturnValue(
+ {
+ activityTargets: [mockActivityTarget],
+ loadingActivityTargets: false,
+ },
+ );
+
+ const useFindManyRecordsMock = jest.requireMock(
+ '@/object-record/hooks/useFindManyRecords',
+ );
+ useFindManyRecordsMock.useFindManyRecords.mockReturnValue({
+ records: [mockActivity],
+ });
+
const { result } = renderHook(
() => {
- const setCurrentWorkspaceMember = useSetRecoilState(
- currentWorkspaceMemberState,
- );
-
const activities = useActivities({
objectNameSingular: CoreObjectNameSingular.Task,
targetableObjects: [
@@ -204,21 +75,11 @@ describe('useActivities', () => {
activitiesOrderByVariables: [{}],
skip: false,
});
- return { activities, setCurrentWorkspaceMember };
+ return activities;
},
{ wrapper: Wrapper },
);
- act(() => {
- result.current.setCurrentWorkspaceMember(mockWorkspaceMembers[0]);
- });
-
- await waitFor(() => {
- expect(result.current.activities.loading).toBe(false);
- });
-
- const { activities } = result.current;
-
- expect(activities.activities).toEqual([mockActivity]);
+ expect(result.current.activities).toEqual([mockActivity]);
});
});
diff --git a/packages/twenty-front/src/modules/activities/timelineActivities/hooks/__tests__/useTimelineActivities.test.ts b/packages/twenty-front/src/modules/activities/timelineActivities/hooks/__tests__/useTimelineActivities.test.tsx
similarity index 86%
rename from packages/twenty-front/src/modules/activities/timelineActivities/hooks/__tests__/useTimelineActivities.test.ts
rename to packages/twenty-front/src/modules/activities/timelineActivities/hooks/__tests__/useTimelineActivities.test.tsx
index 1115a4c70..e7d0da054 100644
--- a/packages/twenty-front/src/modules/activities/timelineActivities/hooks/__tests__/useTimelineActivities.test.ts
+++ b/packages/twenty-front/src/modules/activities/timelineActivities/hooks/__tests__/useTimelineActivities.test.tsx
@@ -1,11 +1,17 @@
import { renderHook } from '@testing-library/react';
import { useTimelineActivities } from '@/activities/timelineActivities/hooks/useTimelineActivities';
+import { ReactNode } from 'react';
+import { RecoilRoot } from 'recoil';
jest.mock('@/object-record/hooks/useFindManyRecords', () => ({
useFindManyRecords: jest.fn(),
}));
+const Wrapper = ({ children }: { children: ReactNode }) => (
+ {children}
+);
+
describe('useTimelineActivities', () => {
afterEach(() => {
jest.clearAllMocks();
@@ -52,8 +58,9 @@ describe('useTimelineActivities', () => {
records: mockedTimelineActivities,
});
- const { result } = renderHook(() =>
- useTimelineActivities(mockTargetableObject),
+ const { result } = renderHook(
+ () => useTimelineActivities(mockTargetableObject),
+ { wrapper: Wrapper },
);
expect(result.current.timelineActivities).toEqual(mockedTimelineActivities);
diff --git a/packages/twenty-front/src/modules/activities/timelineActivities/utils/__tests__/filterOutInvalidTimelineActivities.test.ts b/packages/twenty-front/src/modules/activities/timelineActivities/utils/__tests__/filterOutInvalidTimelineActivities.test.ts
index 02ddb9282..1dc114410 100644
--- a/packages/twenty-front/src/modules/activities/timelineActivities/utils/__tests__/filterOutInvalidTimelineActivities.test.ts
+++ b/packages/twenty-front/src/modules/activities/timelineActivities/utils/__tests__/filterOutInvalidTimelineActivities.test.ts
@@ -1,12 +1,20 @@
import { TimelineActivity } from '@/activities/timelineActivities/types/TimelineActivity';
import { filterOutInvalidTimelineActivities } from '@/activities/timelineActivities/utils/filterOutInvalidTimelineActivities';
+import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
+const noteObjectMetadataItem = {
+ nameSingular: CoreObjectNameSingular.Note,
+ namePlural: 'notes',
+ fields: [{ name: 'field1' }, { name: 'field2' }, { name: 'field3' }],
+} as ObjectMetadataItem;
+
describe('filterOutInvalidTimelineActivities', () => {
it('should filter out TimelineActivities with deleted fields from the properties diff', () => {
const events = [
{
id: '1',
+ name: 'event1',
properties: {
diff: {
field1: { before: 'value1', after: 'value2' },
@@ -17,6 +25,7 @@ describe('filterOutInvalidTimelineActivities', () => {
},
{
id: '2',
+ name: 'event2',
properties: {
diff: {
field1: { before: 'value7', after: 'value8' },
@@ -36,12 +45,13 @@ describe('filterOutInvalidTimelineActivities', () => {
const filteredEvents = filterOutInvalidTimelineActivities(
events,
'objectNameSingular',
- [mainObjectMetadataItem],
+ [mainObjectMetadataItem, noteObjectMetadataItem],
);
expect(filteredEvents).toEqual([
{
id: '1',
+ name: 'event1',
properties: {
diff: {
field1: { before: 'value1', after: 'value2' },
@@ -52,6 +62,7 @@ describe('filterOutInvalidTimelineActivities', () => {
},
{
id: '2',
+ name: 'event2',
properties: {
diff: {
field1: { before: 'value7', after: 'value8' },
@@ -66,6 +77,7 @@ describe('filterOutInvalidTimelineActivities', () => {
const events = [
{
id: '1',
+ name: 'event1',
properties: {
diff: {
field3: { before: 'value5', after: 'value6' },
@@ -74,6 +86,7 @@ describe('filterOutInvalidTimelineActivities', () => {
},
{
id: '2',
+ name: 'event2',
properties: {
diff: {
field4: { before: 'value11', after: 'value12' },
@@ -83,13 +96,15 @@ describe('filterOutInvalidTimelineActivities', () => {
] as TimelineActivity[];
const mainObjectMetadataItem = {
+ nameSingular: 'objectNameSingular',
+ namePlural: 'objectNamePlural',
fields: [{ name: 'field1' }, { name: 'field2' }],
} as ObjectMetadataItem;
const filteredEvents = filterOutInvalidTimelineActivities(
events,
'objectNameSingular',
- [mainObjectMetadataItem],
+ [mainObjectMetadataItem, noteObjectMetadataItem],
);
expect(filteredEvents).toEqual([]);
@@ -99,22 +114,26 @@ describe('filterOutInvalidTimelineActivities', () => {
const events = [
{
id: '1',
+ name: 'event1',
properties: {},
},
{
id: '2',
+ name: 'event2',
properties: {},
},
] as TimelineActivity[];
const mainObjectMetadataItem = {
+ nameSingular: 'objectNameSingular',
+ namePlural: 'objectNamePlural',
fields: [{ name: 'field1' }, { name: 'field2' }],
} as ObjectMetadataItem;
const filteredEvents = filterOutInvalidTimelineActivities(
events,
'objectNameSingular',
- [mainObjectMetadataItem],
+ [mainObjectMetadataItem, noteObjectMetadataItem],
);
expect(filteredEvents).toEqual(events);
diff --git a/packages/twenty-front/src/modules/activities/timelineActivities/utils/filterOutInvalidTimelineActivities.ts b/packages/twenty-front/src/modules/activities/timelineActivities/utils/filterOutInvalidTimelineActivities.ts
index 1bdfcbf79..5613db9d4 100644
--- a/packages/twenty-front/src/modules/activities/timelineActivities/utils/filterOutInvalidTimelineActivities.ts
+++ b/packages/twenty-front/src/modules/activities/timelineActivities/utils/filterOutInvalidTimelineActivities.ts
@@ -43,7 +43,8 @@ export const filterOutInvalidTimelineActivities = (
const validDiffEntries = Object.entries(diff).filter(([diffKey]) =>
isNoteOrTask
- ? noteFieldMetadataItemMap.has(diffKey)
+ ? // Note and Task objects have the same field metadata
+ noteFieldMetadataItemMap.has(diffKey)
: fieldMetadataItemMap.has(diffKey),
);
diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/__tests__/useMultiObjectSearch.test.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/__tests__/useMultiObjectSearch.test.tsx
index 63868f068..54fb2aeaa 100644
--- a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/__tests__/useMultiObjectSearch.test.tsx
+++ b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/__tests__/useMultiObjectSearch.test.tsx
@@ -1,194 +1,139 @@
-import { gql } from '@apollo/client';
-import { MockedProvider } from '@apollo/client/testing';
-import { act, renderHook, waitFor } from '@testing-library/react';
-import { RecoilRoot, useSetRecoilState } from 'recoil';
-
-import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
+import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
-import { useMultiObjectSearch } from '@/object-record/relation-picker/hooks/useMultiObjectSearch';
+import {
+ MultiObjectSearch,
+ ObjectRecordForSelect,
+ SelectedObjectRecordId,
+ useMultiObjectSearch,
+} from '@/object-record/relation-picker/hooks/useMultiObjectSearch';
+import { useMultiObjectSearchMatchesSearchFilterAndSelectedItemsQuery } from '@/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterAndSelectedItemsQuery';
+import { useMultiObjectSearchMatchesSearchFilterAndToSelectQuery } from '@/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterAndToSelectQuery';
+import { useMultiObjectSearchSelectedItemsQuery } from '@/object-record/relation-picker/hooks/useMultiObjectSearchSelectedItemsQuery';
+import { renderHook } from '@testing-library/react';
import { FieldMetadataType } from '~/generated/graphql';
-const query = gql`
- query CombinedFindManyRecords(
- $filterNameSingular: NameSingularFilterInput
- $orderByNameSingular: [NameSingularOrderByInput]
- $lastCursorNameSingular: String
- $limitNameSingular: Int
- ) {
- namePlural(
- filter: $filterNameSingular
- orderBy: $orderByNameSingular
- first: $limitNameSingular
- after: $lastCursorNameSingular
- ) {
- edges {
- node {
- __typename
- id
- }
- cursor
- }
- pageInfo {
- hasNextPage
- startCursor
- endCursor
- }
- totalCount
- }
- }
-`;
-const response = {
- namePlural: {
- edges: [{ node: { __typename: 'Custom', id: 'nodeId' }, cursor: 'cursor' }],
- pageInfo: { startCursor: '', hasNextPage: '', endCursor: '' },
- },
-};
+jest.mock(
+ '@/object-record/relation-picker/hooks/useMultiObjectSearchSelectedItemsQuery',
+);
+jest.mock(
+ '@/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterAndSelectedItemsQuery',
+);
+jest.mock(
+ '@/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterAndToSelectQuery',
+);
-const mocks = [
+const objectData: ObjectMetadataItem[] = [
{
- request: {
- query,
- variables: {
- filterNameSingular: { id: { in: ['1'] } },
- orderByNameSingular: [{ createdAt: 'DescNullsLast' }],
- limitNameSingular: 60,
+ createdAt: 'createdAt',
+ id: 'id',
+ isActive: true,
+ isCustom: true,
+ isSystem: false,
+ isRemote: false,
+ labelPlural: 'labelPlural',
+ labelSingular: 'labelSingular',
+ namePlural: 'namePlural',
+ nameSingular: 'nameSingular',
+ updatedAt: 'updatedAt',
+ fields: [
+ {
+ id: 'f6a0a73a-5ee6-442e-b764-39b682471240',
+ name: 'id',
+ label: 'id',
+ type: FieldMetadataType.Uuid,
+ createdAt: '2024-01-01T00:00:00.000Z',
+ updatedAt: '2024-01-01T00:00:00.000Z',
+ isActive: true,
},
- },
- result: jest.fn(() => ({
- data: response,
- })),
- },
- {
- request: {
- query,
- variables: {
- filterNameSingular: { and: [{}, { id: { in: ['1'] } }] },
- orderByNameSingular: [{ createdAt: 'DescNullsLast' }],
- limitNameSingular: 60,
- },
- },
- result: jest.fn(() => ({
- data: response,
- })),
- },
- {
- request: {
- query,
- variables: {
- limitNameSingular: 60,
- filterNameSingular: { not: { id: { in: ['1'] } } },
- orderByNameSingular: [{ createdAt: 'DescNullsLast' }],
- },
- },
- result: jest.fn(() => ({
- data: response,
- })),
+ ],
},
];
-const Wrapper = ({ children }: { children: React.ReactNode }) => (
-
- {children}
-
-);
-
describe('useMultiObjectSearch', () => {
- it('should return object formatted from objectMetadataItemsState', async () => {
- const { result } = renderHook(
- () => ({
- multiObjects: useMultiObjectSearch({
- searchFilterValue: '',
- selectedObjectRecordIds: [
- {
- objectNameSingular: 'nameSingular',
- id: '1',
- },
- ],
- }),
- setObjectMetadata: useSetRecoilState(objectMetadataItemsState),
- }),
- {
- wrapper: Wrapper,
- },
- );
- const objectData: ObjectMetadataItem[] = [
- {
+ const selectedObjectRecordIds: SelectedObjectRecordId[] = [
+ { objectNameSingular: 'object1', id: '1' },
+ { objectNameSingular: 'object2', id: '2' },
+ ];
+ const searchFilterValue = 'searchValue';
+ const limit = 5;
+ const excludedObjectRecordIds: SelectedObjectRecordId[] = [
+ { objectNameSingular: 'object3', id: '3' },
+ { objectNameSingular: 'object4', id: '4' },
+ ];
+ const excludedObjects: CoreObjectNameSingular[] = [];
+
+ const selectedObjectRecords: ObjectRecordForSelect[] = [
+ {
+ objectMetadataItem: objectData[0],
+ record: {
+ __typename: 'ObjectRecord',
+ id: '1',
createdAt: 'createdAt',
- id: 'id',
- isActive: true,
- isCustom: true,
- isSystem: false,
- isRemote: false,
- labelPlural: 'labelPlural',
- labelSingular: 'labelSingular',
- namePlural: 'namePlural',
- nameSingular: 'nameSingular',
updatedAt: 'updatedAt',
- fields: [
- {
- id: 'f6a0a73a-5ee6-442e-b764-39b682471240',
- name: 'id',
- label: 'id',
- type: FieldMetadataType.Uuid,
- createdAt: '2024-01-01T00:00:00.000Z',
- updatedAt: '2024-01-01T00:00:00.000Z',
- isActive: true,
- },
- ],
},
- ];
- act(() => {
- result.current.setObjectMetadata(objectData);
- });
- await waitFor(() => {
- expect(mocks[0].result).toHaveBeenCalled();
- // expect(mocks[1].result).toHaveBeenCalled();
- expect(mocks[2].result).toHaveBeenCalled();
- });
- const expectedData = [
- {
- objectMetadataItem: {
- createdAt: 'createdAt',
- id: 'id',
- isActive: true,
- isCustom: true,
- isSystem: false,
- isRemote: false,
- labelPlural: 'labelPlural',
- labelSingular: 'labelSingular',
- namePlural: 'namePlural',
- nameSingular: 'nameSingular',
- updatedAt: 'updatedAt',
- fields: [
- {
- id: 'f6a0a73a-5ee6-442e-b764-39b682471240',
- name: 'id',
- label: 'id',
- isActive: true,
- type: FieldMetadataType.Uuid,
- createdAt: '2024-01-01T00:00:00.000Z',
- updatedAt: '2024-01-01T00:00:00.000Z',
- },
- ],
- },
- record: { id: 'nodeId', __typename: 'Custom' },
- recordIdentifier: {
- id: 'nodeId',
- name: '',
- avatarUrl: '',
- avatarType: 'rounded',
- linkToShowPage: '/object/nameSingular/nodeId',
- },
+ recordIdentifier: {
+ id: '1',
+ name: 'name',
},
- ];
- expect(result.current.multiObjects.selectedObjectRecords).toStrictEqual(
- expectedData,
- );
- expect(
- result.current.multiObjects.filteredSelectedObjectRecords,
- ).toStrictEqual(expectedData);
- expect(result.current.multiObjects.objectRecordsToSelect).toStrictEqual(
- expectedData,
+ },
+ ];
+ const selectedObjectRecordsLoading = false;
+
+ const selectedAndMatchesSearchFilterObjectRecords: ObjectRecordForSelect[] =
+ [];
+ const selectedAndMatchesSearchFilterObjectRecordsLoading = false;
+
+ const toSelectAndMatchesSearchFilterObjectRecords: ObjectRecordForSelect[] =
+ [];
+ const toSelectAndMatchesSearchFilterObjectRecordsLoading = false;
+
+ beforeEach(() => {
+ (useMultiObjectSearchSelectedItemsQuery as jest.Mock).mockReturnValue({
+ selectedObjectRecords,
+ selectedObjectRecordsLoading,
+ });
+
+ (
+ useMultiObjectSearchMatchesSearchFilterAndSelectedItemsQuery as jest.Mock
+ ).mockReturnValue({
+ selectedAndMatchesSearchFilterObjectRecords,
+ selectedAndMatchesSearchFilterObjectRecordsLoading,
+ });
+
+ (
+ useMultiObjectSearchMatchesSearchFilterAndToSelectQuery as jest.Mock
+ ).mockReturnValue({
+ toSelectAndMatchesSearchFilterObjectRecords,
+ toSelectAndMatchesSearchFilterObjectRecordsLoading,
+ });
+ });
+
+ afterEach(() => {
+ jest.resetAllMocks();
+ });
+
+ it('should return the correct object records and loading state', () => {
+ const { result } = renderHook(() =>
+ useMultiObjectSearch({
+ searchFilterValue,
+ selectedObjectRecordIds,
+ limit,
+ excludedObjectRecordIds,
+ excludedObjects,
+ }),
);
+
+ const expected: MultiObjectSearch = {
+ selectedObjectRecords,
+ filteredSelectedObjectRecords:
+ selectedAndMatchesSearchFilterObjectRecords,
+ objectRecordsToSelect: toSelectAndMatchesSearchFilterObjectRecords,
+ loading:
+ selectedAndMatchesSearchFilterObjectRecordsLoading ||
+ toSelectAndMatchesSearchFilterObjectRecordsLoading ||
+ selectedObjectRecordsLoading,
+ };
+
+ expect(result.current).toEqual(expected);
});
});