diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__mocks__/ApolloMetadataClientProvider.tsx b/packages/twenty-front/src/modules/object-metadata/hooks/__mocks__/ApolloMetadataClientProvider.tsx new file mode 100644 index 000000000..61a829cc9 --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__mocks__/ApolloMetadataClientProvider.tsx @@ -0,0 +1,21 @@ +import { ReactNode } from 'react'; +import { + ApolloClient, + NormalizedCacheObject, + useApolloClient, +} from '@apollo/client'; + +import { ApolloMetadataClientContext } from '@/object-metadata/context/ApolloClientMetadataContext'; + +export const TestApolloMetadataClientProvider = ({ + children, +}: { + children: ReactNode; +}) => { + const client = useApolloClient() as ApolloClient; + return ( + + {client ? children : ''} + + ); +}; diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__mocks__/useCreateOneObjectRecordMetadataItem.ts b/packages/twenty-front/src/modules/object-metadata/hooks/__mocks__/useCreateOneObjectRecordMetadataItem.ts new file mode 100644 index 000000000..37adb3417 --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__mocks__/useCreateOneObjectRecordMetadataItem.ts @@ -0,0 +1,50 @@ +import { gql } from '@apollo/client'; + +export const query = gql` + mutation CreateOneObjectMetadataItem($input: CreateOneObjectInput!) { + createOneObject(input: $input) { + id + dataSourceId + nameSingular + namePlural + labelSingular + labelPlural + description + icon + isCustom + isActive + createdAt + updatedAt + labelIdentifierFieldMetadataId + imageIdentifierFieldMetadataId + } + } +`; + +export const variables = { + input: { + object: { + labelPlural: 'View Filters', + labelSingular: 'View Filter', + nameSingular: 'viewFilter', + namePlural: 'viewFilters', + }, + }, +}; + +export const responseData = { + id: '', + dataSourceId: '', + nameSingular: 'viewFilter', + namePlural: 'viewFilters', + labelSingular: 'View Filter', + labelPlural: 'View Filters', + description: '', + icon: '', + isCustom: false, + isActive: true, + createdAt: '', + updatedAt: '', + labelIdentifierFieldMetadataId: '', + imageIdentifierFieldMetadataId: '', +}; diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__mocks__/useCreateOneRelationMetadataItem.ts b/packages/twenty-front/src/modules/object-metadata/hooks/__mocks__/useCreateOneRelationMetadataItem.ts new file mode 100644 index 000000000..018d40e92 --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__mocks__/useCreateOneRelationMetadataItem.ts @@ -0,0 +1,45 @@ +import { gql } from '@apollo/client'; + +export const query = gql` + mutation CreateOneRelationMetadata($input: CreateOneRelationInput!) { + createOneRelation(input: $input) { + id + relationType + fromObjectMetadataId + toObjectMetadataId + fromFieldMetadataId + toFieldMetadataId + createdAt + updatedAt + } + } +`; + +export const variables = { + input: { + relation: { + fromDescription: null, + fromIcon: undefined, + fromLabel: 'label', + fromName: 'label', + fromObjectMetadataId: 'objectMetadataId', + relationType: 'ONE_TO_ONE', + toDescription: null, + toIcon: undefined, + toLabel: 'Another label', + toName: 'anotherLabel', + toObjectMetadataId: 'objectMetadataId1', + }, + }, +}; + +export const responseData = { + id: '', + relationType: 'ONE_TO_ONE', + fromObjectMetadataId: 'objectMetadataId', + toObjectMetadataId: 'objectMetadataId1', + fromFieldMetadataId: '', + toFieldMetadataId: '', + createdAt: '', + updatedAt: '', +}; diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__mocks__/useDeleteOneObjectMetadataItem.ts b/packages/twenty-front/src/modules/object-metadata/hooks/__mocks__/useDeleteOneObjectMetadataItem.ts new file mode 100644 index 000000000..42ca0a995 --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__mocks__/useDeleteOneObjectMetadataItem.ts @@ -0,0 +1,41 @@ +import { gql } from '@apollo/client'; + +export const query = gql` + mutation DeleteOneObjectMetadataItem($idToDelete: ID!) { + deleteOneObject(input: { id: $idToDelete }) { + id + dataSourceId + nameSingular + namePlural + labelSingular + labelPlural + description + icon + isCustom + isActive + createdAt + updatedAt + labelIdentifierFieldMetadataId + imageIdentifierFieldMetadataId + } + } +`; + +export const variables = { idToDelete: 'idToDelete' }; + +export const responseData = { + id: '', + dataSourceId: '', + nameSingular: '', + namePlural: '', + labelSingular: '', + labelPlural: '', + description: '', + icon: '', + isCustom: false, + isActive: true, + createdAt: '', + updatedAt: '', + labelIdentifierFieldMetadataId: '', + imageIdentifierFieldMetadataId: '', +}; diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__mocks__/useFieldMetadataItem.ts b/packages/twenty-front/src/modules/object-metadata/hooks/__mocks__/useFieldMetadataItem.ts new file mode 100644 index 000000000..66a29db3f --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__mocks__/useFieldMetadataItem.ts @@ -0,0 +1,107 @@ +import { gql } from '@apollo/client'; + +const baseFields = ` + id + type + name + label + description + icon + isCustom + isActive + isNullable + createdAt + updatedAt +`; + +export const queries = { + eraseMetadataField: gql` + mutation DeleteOneFieldMetadataItem($idToDelete: ID!) { + deleteOneField(input: { id: $idToDelete }) { + ${baseFields} + } + } + `, + activateMetadataField: gql` + mutation UpdateOneFieldMetadataItem( + $idToUpdate: ID! + $updatePayload: UpdateFieldInput! + ) { + updateOneField(input: { id: $idToUpdate, update: $updatePayload }) { + ${baseFields} + } + } + `, + createMetadataField: gql` + mutation CreateOneFieldMetadataItem($input: CreateOneFieldMetadataInput!) { + createOneField(input: $input) { + ${baseFields} + defaultValue + options + } + } + `, +}; + +const fieldId = '2c43466a-fe9e-4005-8d08-c5836067aa6c'; +export const objectMetadataId = '25611fce-6637-4089-b0ca-91afeec95784'; + +export const variables = { + eraseMetadataField: { idToDelete: fieldId }, + activateMetadataField: { + idToUpdate: fieldId, + updatePayload: { isActive: true, label: undefined }, + }, + createMetadataField: { + input: { + field: { + defaultValue: undefined, + description: null, + icon: undefined, + label: 'fieldLabel', + name: 'fieldLabel', + options: undefined, + objectMetadataId, + type: 'TEXT', + }, + }, + }, + disableMetadataField: { + idToUpdate: fieldId, + updatePayload: { isActive: false, label: undefined }, + }, + editMetadataField: { + idToUpdate: '2c43466a-fe9e-4005-8d08-c5836067aa6c', + updatePayload: { + defaultValue: undefined, + description: null, + icon: undefined, + label: 'New label', + name: 'newLabel', + options: undefined, + }, + }, +}; + +const defaultResponseData = { + id: '2c43466a-fe9e-4005-8d08-c5836067aa6c', + type: 'type', + name: 'name', + label: 'label', + description: 'description', + icon: 'icon', + isCustom: false, + isActive: true, + isNullable: false, + createdAt: '1977-09-28T13:56:55.157Z', + updatedAt: '1996-10-10T08:27:57.117Z', +}; + +export const responseData = { + default: defaultResponseData, + createMetadataField: { + ...defaultResponseData, + defaultValue: '', + options: [], + }, +}; diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__mocks__/useFindManyObjectMetadataItems.ts b/packages/twenty-front/src/modules/object-metadata/hooks/__mocks__/useFindManyObjectMetadataItems.ts new file mode 100644 index 000000000..e24af05de --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__mocks__/useFindManyObjectMetadataItems.ts @@ -0,0 +1,94 @@ +import { gql } from '@apollo/client'; + +export const query = gql` + query ObjectMetadataItems( + $objectFilter: objectFilter + $fieldFilter: fieldFilter + ) { + objects(paging: { first: 1000 }, filter: $objectFilter) { + edges { + node { + id + dataSourceId + nameSingular + namePlural + labelSingular + labelPlural + description + icon + isCustom + isActive + isSystem + createdAt + updatedAt + labelIdentifierFieldMetadataId + imageIdentifierFieldMetadataId + fields(paging: { first: 1000 }, filter: $fieldFilter) { + edges { + node { + id + type + name + label + description + icon + isCustom + isActive + isSystem + isNullable + createdAt + updatedAt + fromRelationMetadata { + id + relationType + toObjectMetadata { + id + dataSourceId + nameSingular + namePlural + isSystem + } + toFieldMetadataId + } + toRelationMetadata { + id + relationType + fromObjectMetadata { + id + dataSourceId + nameSingular + namePlural + isSystem + } + fromFieldMetadataId + } + defaultValue + options + } + } + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + totalCount + } + } + } + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + totalCount + } + } +`; + +export const variables = { objectFilter: undefined, fieldFilter: undefined }; + +export const responseData = { + objects: { edges: [] }, +}; diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__mocks__/useObjectMetadataItemForSettings.ts b/packages/twenty-front/src/modules/object-metadata/hooks/__mocks__/useObjectMetadataItemForSettings.ts new file mode 100644 index 000000000..017dca504 --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__mocks__/useObjectMetadataItemForSettings.ts @@ -0,0 +1,54 @@ +import { gql } from '@apollo/client'; + +export const query = gql` + mutation UpdateOneObjectMetadataItem( + $idToUpdate: ID! + $updatePayload: UpdateObjectInput! + ) { + updateOneObject(input: { id: $idToUpdate, update: $updatePayload }) { + id + dataSourceId + nameSingular + namePlural + labelSingular + labelPlural + description + icon + isCustom + isActive + createdAt + updatedAt + labelIdentifierFieldMetadataId + imageIdentifierFieldMetadataId + } + } +`; + +export const variables = { + idToUpdate: 'idToUpdate', + updatePayload: { + description: 'newDescription', + icon: undefined, + labelPlural: 'labelPlural', + labelSingular: 'labelSingular', + namePlural: 'labelPlural', + nameSingular: 'labelSingular', + }, +}; + +export const responseData = { + id: 'idToUpdate', + dataSourceId: 'dataSourceId', + nameSingular: 'nameSingular', + namePlural: 'namePlural', + labelSingular: 'labelSingular', + labelPlural: 'labelPlural', + description: 'newDescription', + icon: '', + isCustom: false, + isActive: true, + createdAt: '', + updatedAt: '', + labelIdentifierFieldMetadataId: '', + imageIdentifierFieldMetadataId: '', +}; diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useColumnDefinitionsFromFieldMetadata.test.ts b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useColumnDefinitionsFromFieldMetadata.test.ts new file mode 100644 index 000000000..cdcf7e91f --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useColumnDefinitionsFromFieldMetadata.test.ts @@ -0,0 +1,72 @@ +import { renderHook } from '@testing-library/react'; + +import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata'; +import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; +import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock'; +import { Nullable } from '~/types/Nullable'; + +describe('useColumnDefinitionsFromFieldMetadata', () => { + it('should return empty definitions if no object is passed', () => { + const { result } = renderHook( + (objectMetadataItem?: Nullable) => { + return useColumnDefinitionsFromFieldMetadata(objectMetadataItem); + }, + ); + + expect(Array.isArray(result.current.columnDefinitions)).toBe(true); + expect(Array.isArray(result.current.filterDefinitions)).toBe(true); + expect(Array.isArray(result.current.sortDefinitions)).toBe(true); + expect(result.current.columnDefinitions.length).toBe(0); + expect(result.current.filterDefinitions.length).toBe(0); + expect(result.current.sortDefinitions.length).toBe(0); + }); + + it('should return empty definitions if object has no fields matching criteria', () => { + const mockObjectMetadataItems = getObjectMetadataItemsMock(); + + const { result } = renderHook( + (objectMetadataItem?: Nullable) => { + return useColumnDefinitionsFromFieldMetadata(objectMetadataItem); + }, + { + initialProps: mockObjectMetadataItems[0], + }, + ); + + expect(result.current.columnDefinitions.length).toBe(0); + expect(result.current.filterDefinitions.length).toBe(0); + expect(result.current.sortDefinitions.length).toBe(0); + }); + + it('should return expected definitions', () => { + const mockObjectMetadataItems = getObjectMetadataItemsMock(); + + const { result } = renderHook( + (objectMetadataItem?: Nullable) => { + return useColumnDefinitionsFromFieldMetadata(objectMetadataItem); + }, + { + initialProps: mockObjectMetadataItems[1], + }, + ); + + const { columnDefinitions, filterDefinitions, sortDefinitions } = + result.current; + + expect(columnDefinitions.length).toBe(3); + expect(filterDefinitions.length).toBe(3); + expect(sortDefinitions.length).toBe(3); + + expect(columnDefinitions[0].label).toBe('Expiration date'); + expect(columnDefinitions[1].label).toBe('Name'); + expect(columnDefinitions[2].label).toBe('Revocation date'); + + expect(filterDefinitions[0].label).toBe('Expiration date'); + expect(filterDefinitions[1].label).toBe('Name'); + expect(filterDefinitions[2].label).toBe('Revocation date'); + + expect(sortDefinitions[0].label).toBe('Expiration date'); + expect(sortDefinitions[1].label).toBe('Name'); + expect(sortDefinitions[2].label).toBe('Revocation date'); + }); +}); diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useCreateOneObjectRecordMetadataItem.test.tsx b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useCreateOneObjectRecordMetadataItem.test.tsx new file mode 100644 index 000000000..588e6d9a0 --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useCreateOneObjectRecordMetadataItem.test.tsx @@ -0,0 +1,59 @@ +import { ReactNode } from 'react'; +import { MockedProvider } from '@apollo/client/testing'; +import { act, renderHook } from '@testing-library/react'; +import { RecoilRoot } from 'recoil'; + +import { useCreateOneObjectRecordMetadataItem } from '@/object-metadata/hooks/useCreateOneObjectMetadataItem'; + +import { TestApolloMetadataClientProvider } from '../__mocks__/ApolloMetadataClientProvider'; +import { + query, + responseData, + variables, +} from '../__mocks__/useCreateOneObjectRecordMetadataItem'; + +const mocks = [ + { + request: { + query, + variables, + }, + result: jest.fn(() => ({ + data: { + createOneObject: responseData, + }, + })), + }, +]; + +const Wrapper = ({ children }: { children: ReactNode }) => ( + + + + {children} + + + +); + +describe('useCreateOneObjectRecordMetadataItem', () => { + it('should work as expected', async () => { + const { result } = renderHook( + () => useCreateOneObjectRecordMetadataItem(), + { + wrapper: Wrapper, + }, + ); + + await act(async () => { + const res = await result.current.createOneObjectMetadataItem({ + labelPlural: 'View Filters', + labelSingular: 'View Filter', + nameSingular: 'viewFilter', + namePlural: 'viewFilters', + }); + + expect(res.data).toEqual({ createOneObject: responseData }); + }); + }); +}); diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useCreateOneRelationMetadataItem.test.tsx b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useCreateOneRelationMetadataItem.test.tsx new file mode 100644 index 000000000..46e41021a --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useCreateOneRelationMetadataItem.test.tsx @@ -0,0 +1,64 @@ +import { ReactNode } from 'react'; +import { MockedProvider } from '@apollo/client/testing'; +import { act, renderHook } from '@testing-library/react'; +import { RecoilRoot } from 'recoil'; + +import { useCreateOneRelationMetadataItem } from '@/object-metadata/hooks/useCreateOneRelationMetadataItem'; +import { RelationMetadataType } from '~/generated/graphql'; + +import { TestApolloMetadataClientProvider } from '../__mocks__/ApolloMetadataClientProvider'; +import { + query, + responseData, + variables, +} from '../__mocks__/useCreateOneRelationMetadataItem'; + +const mocks = [ + { + request: { + query, + variables, + }, + result: jest.fn(() => ({ + data: { + createOneRelation: responseData, + }, + })), + }, +]; + +const Wrapper = ({ children }: { children: ReactNode }) => ( + + + + {children} + + + +); + +describe('useCreateOneRelationMetadataItem', () => { + it('should work as expected', async () => { + const { result } = renderHook(() => useCreateOneRelationMetadataItem(), { + wrapper: Wrapper, + }); + + await act(async () => { + const res = await result.current.createOneRelationMetadataItem({ + relationType: RelationMetadataType.OneToOne, + field: { + label: 'label', + }, + objectMetadataId: 'objectMetadataId', + connect: { + field: { + label: 'Another label', + }, + objectMetadataId: 'objectMetadataId1', + }, + }); + + expect(res.data).toEqual({ createOneRelation: responseData }); + }); + }); +}); diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useDeleteOneObjectMetadataItem.test.tsx b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useDeleteOneObjectMetadataItem.test.tsx new file mode 100644 index 000000000..f4e6a96d6 --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useDeleteOneObjectMetadataItem.test.tsx @@ -0,0 +1,52 @@ +import { ReactNode } from 'react'; +import { MockedProvider } from '@apollo/client/testing'; +import { act, renderHook } from '@testing-library/react'; +import { RecoilRoot } from 'recoil'; + +import { useDeleteOneObjectMetadataItem } from '@/object-metadata/hooks/useDeleteOneObjectMetadataItem'; + +import { TestApolloMetadataClientProvider } from '../__mocks__/ApolloMetadataClientProvider'; +import { + query, + responseData, + variables, +} from '../__mocks__/useDeleteOneObjectMetadataItem'; + +const mocks = [ + { + request: { + query, + variables, + }, + result: jest.fn(() => ({ + data: { + deleteOneObject: responseData, + }, + })), + }, +]; + +const Wrapper = ({ children }: { children: ReactNode }) => ( + + + + {children} + + + +); + +describe('useDeleteOneObjectMetadataItem', () => { + it('should work as expected', async () => { + const { result } = renderHook(() => useDeleteOneObjectMetadataItem(), { + wrapper: Wrapper, + }); + + await act(async () => { + const res = + await result.current.deleteOneObjectMetadataItem('idToDelete'); + + expect(res.data).toEqual({ deleteOneObject: responseData }); + }); + }); +}); diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useFieldMetadataItem.test.tsx b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useFieldMetadataItem.test.tsx new file mode 100644 index 000000000..7527677e7 --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useFieldMetadataItem.test.tsx @@ -0,0 +1,172 @@ +import { ReactNode } from 'react'; +import { MockedProvider } from '@apollo/client/testing'; +import { act, renderHook } from '@testing-library/react'; +import { RecoilRoot } from 'recoil'; + +import { useFieldMetadataItem } from '@/object-metadata/hooks/useFieldMetadataItem'; +import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; +import { FieldMetadataType } from '~/generated/graphql'; + +import { TestApolloMetadataClientProvider } from '../__mocks__/ApolloMetadataClientProvider'; +import { + objectMetadataId, + queries, + responseData, + variables, +} from '../__mocks__/useFieldMetadataItem'; + +const fieldMetadataItem: FieldMetadataItem = { + id: '2c43466a-fe9e-4005-8d08-c5836067aa6c', + createdAt: '', + label: 'label', + name: 'name', + type: FieldMetadataType.Text, + updatedAt: '', +}; + +const mocks = [ + { + request: { + query: queries.eraseMetadataField, + variables: variables.eraseMetadataField, + }, + result: jest.fn(() => ({ + data: { + deleteOneField: responseData.default, + }, + })), + }, + { + request: { + query: queries.activateMetadataField, + variables: variables.activateMetadataField, + }, + result: jest.fn(() => ({ + data: { + updateOneField: responseData.default, + }, + })), + }, + { + request: { + query: queries.createMetadataField, + variables: variables.createMetadataField, + }, + result: jest.fn(() => ({ + data: { + createOneField: responseData.createMetadataField, + }, + })), + }, + { + request: { + query: queries.activateMetadataField, + variables: variables.disableMetadataField, + }, + result: jest.fn(() => ({ + data: { + updateOneField: responseData.default, + }, + })), + }, + { + request: { + query: queries.activateMetadataField, + variables: variables.editMetadataField, + }, + result: jest.fn(() => ({ + data: { + updateOneField: responseData.default, + }, + })), + }, +]; + +const Wrapper = ({ children }: { children: ReactNode }) => ( + + + + {children} + + + +); + +describe('useFieldMetadataItem', () => { + it('should activateMetadataField', async () => { + const { result } = renderHook(() => useFieldMetadataItem(), { + wrapper: Wrapper, + }); + + await act(async () => { + const res = await result.current.activateMetadataField(fieldMetadataItem); + + expect(res.data).toEqual({ + updateOneField: responseData.default, + }); + }); + }); + + it('should createMetadataField', async () => { + const { result } = renderHook(() => useFieldMetadataItem(), { + wrapper: Wrapper, + }); + + await act(async () => { + const res = await result.current.createMetadataField({ + label: 'fieldLabel', + objectMetadataId, + type: FieldMetadataType.Text, + }); + + expect(res.data).toEqual({ + createOneField: responseData.createMetadataField, + }); + }); + }); + + it('should disableMetadataField', async () => { + const { result } = renderHook(() => useFieldMetadataItem(), { + wrapper: Wrapper, + }); + + await act(async () => { + const res = await result.current.disableMetadataField(fieldMetadataItem); + + expect(res.data).toEqual({ + updateOneField: responseData.default, + }); + }); + }); + + it('should eraseMetadataField', async () => { + const { result } = renderHook(() => useFieldMetadataItem(), { + wrapper: Wrapper, + }); + + await act(async () => { + const res = await result.current.eraseMetadataField(fieldMetadataItem); + + expect(res.data).toEqual({ + deleteOneField: responseData.default, + }); + }); + }); + + it('should editMetadataField', async () => { + const { result } = renderHook(() => useFieldMetadataItem(), { + wrapper: Wrapper, + }); + + await act(async () => { + const res = await result.current.editMetadataField({ + id: fieldMetadataItem.id, + label: 'New label', + }); + + expect(res.data).toEqual({ + updateOneField: responseData.default, + }); + }); + }); +}); diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useFilterOutUnexistingObjectMetadataItems.test.tsx b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useFilterOutUnexistingObjectMetadataItems.test.tsx new file mode 100644 index 000000000..e544f642c --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useFilterOutUnexistingObjectMetadataItems.test.tsx @@ -0,0 +1,38 @@ +import { renderHook } from '@testing-library/react'; +import { RecoilRoot, useSetRecoilState } from 'recoil'; + +import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; +import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock'; + +import { useFilterOutUnexistingObjectMetadataItems } from '../useFilterOutUnexistingObjectMetadataItems'; + +const mockObjectMetadataItems = getObjectMetadataItemsMock(); + +describe('useFilterOutUnexistingObjectMetadataItems', () => { + it('should work as expected', async () => { + const { result } = renderHook( + () => { + const setMetadataItems = useSetRecoilState(objectMetadataItemsState); + + setMetadataItems(mockObjectMetadataItems.slice(1)); + return useFilterOutUnexistingObjectMetadataItems(); + }, + { + wrapper: RecoilRoot, + }, + ); + + const objectExists = result.current.filterOutUnexistingObjectMetadataItems( + mockObjectMetadataItems[0], + ); + + expect(objectExists).toBe(false); + + const secondObjectExists = + result.current.filterOutUnexistingObjectMetadataItems( + mockObjectMetadataItems[1], + ); + + expect(secondObjectExists).toBe(true); + }); +}); diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useFindManyObjectMetadataItems.test.tsx b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useFindManyObjectMetadataItems.test.tsx new file mode 100644 index 000000000..44967e53c --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useFindManyObjectMetadataItems.test.tsx @@ -0,0 +1,54 @@ +import { ReactNode } from 'react'; +import { MockedProvider } from '@apollo/client/testing'; +import { renderHook } from '@testing-library/react'; +import { RecoilRoot } from 'recoil'; + +import { useFindManyObjectMetadataItems } from '@/object-metadata/hooks/useFindManyObjectMetadataItems'; +import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope'; + +import { TestApolloMetadataClientProvider } from '../__mocks__/ApolloMetadataClientProvider'; +import { + query, + responseData, + variables, +} from '../__mocks__/useFindManyObjectMetadataItems'; + +const mocks = [ + { + request: { + query, + variables, + }, + result: jest.fn(() => ({ + data: { + objects: responseData, + }, + })), + }, +]; + +const Wrapper = ({ children }: { children: ReactNode }) => ( + + + + + {children} + + + + +); + +describe('useFindManyObjectMetadataItems', () => { + it('should activateMetadataField', async () => { + const { result } = renderHook(() => useFindManyObjectMetadataItems(), { + wrapper: Wrapper, + }); + + const { loading, error, objectMetadataItems } = result.current; + + expect(loading).toBe(true); + expect(error).toBeUndefined(); + expect(objectMetadataItems).toEqual([]); + }); +}); diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useGetObjectOrderByField.test.ts b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useGetObjectOrderByField.test.ts new file mode 100644 index 000000000..c71a90358 --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useGetObjectOrderByField.test.ts @@ -0,0 +1,21 @@ +import { renderHook } from '@testing-library/react'; + +import { useGetObjectOrderByField } from '@/object-metadata/hooks/useGetObjectOrderByField'; +import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock'; + +const mockObjectMetadataItems = getObjectMetadataItemsMock(); + +describe('useGetObjectOrderByField', () => { + it('should work as expected', () => { + const objectMetadataItem = mockObjectMetadataItems.find( + (item) => item.nameSingular === 'person', + )!; + + const { result } = renderHook(() => + useGetObjectOrderByField({ objectMetadataItem })('AscNullsLast'), + ); + expect(result.current).toEqual({ + name: { firstName: 'AscNullsLast', lastName: 'AscNullsLast' }, + }); + }); +}); diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useGetObjectRecordIdentifierByNameSingular.test.tsx b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useGetObjectRecordIdentifierByNameSingular.test.tsx new file mode 100644 index 000000000..a62f1094c --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useGetObjectRecordIdentifierByNameSingular.test.tsx @@ -0,0 +1,73 @@ +import { renderHook } from '@testing-library/react'; +import { RecoilRoot, useSetRecoilState } from 'recoil'; + +import { useGetObjectRecordIdentifierByNameSingular } from '@/object-metadata/hooks/useGetObjectRecordIdentifierByNameSingular'; +import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; +import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock'; + +const mockObjectMetadataItems = getObjectMetadataItemsMock(); + +describe('useGetObjectRecordIdentifierByNameSingular', () => { + it('should work as expected', async () => { + const { result, rerender } = renderHook( + ({ + record, + objectNameSingular, + }: { + record: any; + objectNameSingular: string; + }) => { + const setMetadataItems = useSetRecoilState(objectMetadataItemsState); + + setMetadataItems(mockObjectMetadataItems); + + return useGetObjectRecordIdentifierByNameSingular()( + record, + objectNameSingular, + ); + }, + { + wrapper: RecoilRoot, + initialProps: { + record: { id: 'recordId' } as any, + objectNameSingular: 'viewSort', + }, + }, + ); + + expect(result.current.linkToShowPage).toBe('/object/viewSort/recordId'); + + rerender({ + record: { id: 'recordId', avatarUrl: 'https://fake-url.com' }, + objectNameSingular: 'opportunity', + }); + + expect(result.current.linkToShowPage).toBe('/opportunities/recordId'); + + rerender({ + record: { + id: 'recordId', + name: { firstName: 'John', lastName: 'Connor' }, + }, + objectNameSingular: 'person', + }); + + expect(result.current.linkToShowPage).toBe('/object/person/recordId'); + expect(result.current.name).toBe('John Connor'); + expect(result.current.avatarType).toBe('rounded'); + + rerender({ + record: { + id: 'recordId', + domainName: 'https://cool-company.com', + }, + objectNameSingular: 'company', + }); + + expect(result.current.linkToShowPage).toBe('/object/company/recordId'); + expect(result.current.avatarUrl).toBe( + 'https://favicon.twenty.com/cool-company.com', + ); + expect(result.current.avatarType).toBe('squared'); + }); +}); diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useMapFieldMetadataToGraphQLQuery.test.tsx b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useMapFieldMetadataToGraphQLQuery.test.tsx new file mode 100644 index 000000000..8e9940e6d --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useMapFieldMetadataToGraphQLQuery.test.tsx @@ -0,0 +1,177 @@ +import { renderHook } from '@testing-library/react'; +import { RecoilRoot, useSetRecoilState } from 'recoil'; + +import { useMapFieldMetadataToGraphQLQuery } from '@/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery'; +import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; +import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; +import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock'; +import { RelationMetadataType } from '~/generated/graphql'; + +const mockObjectMetadataItems = getObjectMetadataItemsMock(); + +const formatGQLString = (inputString: string) => + inputString.replace(/^\s*[\r\n]/gm, ''); + +const getOneToManyRelation = () => { + const objectMetadataItem = mockObjectMetadataItems.find( + (item) => item.nameSingular === 'opportunity', + )!; + + return { + field: objectMetadataItem.fields.find((field) => field.name === 'company')!, + res: `company + { + id + xLink + { + label + url + } + linkedinLink + { + label + url + } +domainName + annualRecurringRevenue + { + amountMicros + currencyCode + } +createdAt +address +updatedAt +name +accountOwnerId +employees +id +idealCustomerProfile + }`, + }; +}; + +const getOneToOneRelationField = () => { + const objectMetadataItem = mockObjectMetadataItems.find( + (item) => item.nameSingular === 'opportunity', + )!; + + const oneToManyfield = objectMetadataItem.fields.find( + (field) => field.name === 'company', + )!; + + const field: FieldMetadataItem = { + ...oneToManyfield, + toRelationMetadata: { + ...oneToManyfield.toRelationMetadata!, + relationType: RelationMetadataType.OneToOne, + }, + }; + + return field; +}; + +const getOneToManyFromRelationField = () => { + const objectMetadataItem = mockObjectMetadataItems.find( + (item) => item.nameSingular === 'person', + )!; + + const field = objectMetadataItem.fields.find( + (field) => field.name === 'opportunities', + )!; + + return { + field, + res: `opportunities + { + edges { + node { + id + personId +pointOfContactId +updatedAt +companyId +pipelineStepId +probability +closeDate + amount + { + amountMicros + currencyCode + } +id +createdAt + } + } + }`, + }; +}; + +const getFullNameRelation = () => { + const objectMetadataItem = mockObjectMetadataItems.find( + (item) => item.nameSingular === 'person', + )!; + + const field = objectMetadataItem.fields.find( + (field) => field.name === 'name', + )!; + + return { + field, + res: `\n name\n {\n firstName\n lastName\n }\n `, + }; +}; + +describe('useMapFieldMetadataToGraphQLQuery', () => { + it('should work as expected', async () => { + const { result } = renderHook( + () => { + const setMetadataItems = useSetRecoilState(objectMetadataItemsState); + setMetadataItems(mockObjectMetadataItems); + + return { + mapFieldMetadataToGraphQLQuery: useMapFieldMetadataToGraphQLQuery(), + }; + }, + { + wrapper: RecoilRoot, + }, + ); + + const oneToManyRelation = getOneToManyRelation(); + + const { mapFieldMetadataToGraphQLQuery } = result.current; + + const oneToManyRelationFieldRes = mapFieldMetadataToGraphQLQuery( + oneToManyRelation.field, + ); + + expect(formatGQLString(oneToManyRelationFieldRes)).toEqual( + oneToManyRelation.res, + ); + + const oneToOneRelation = getOneToOneRelationField(); + + const oneToOneRelationFieldRes = + mapFieldMetadataToGraphQLQuery(oneToOneRelation); + + expect(formatGQLString(oneToOneRelationFieldRes)).toEqual( + oneToManyRelation.res, + ); + + const oneToManyFromRelation = getOneToManyFromRelationField(); + const oneToManyFromRelationFieldRes = mapFieldMetadataToGraphQLQuery( + oneToManyFromRelation.field, + ); + + expect(formatGQLString(oneToManyFromRelationFieldRes)).toEqual( + oneToManyFromRelation.res, + ); + + const fullNameRelation = getFullNameRelation(); + const fullNameFieldRes = mapFieldMetadataToGraphQLQuery( + fullNameRelation.field, + ); + + expect(fullNameFieldRes).toEqual(fullNameRelation.res); + }); +}); diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useMapToObjectRecordIdentifier.test.tsx b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useMapToObjectRecordIdentifier.test.tsx new file mode 100644 index 000000000..03c09f5d6 --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useMapToObjectRecordIdentifier.test.tsx @@ -0,0 +1,34 @@ +import { renderHook } from '@testing-library/react'; +import { RecoilRoot } from 'recoil'; + +import { useMapToObjectRecordIdentifier } from '@/object-metadata/hooks/useMapToObjectRecordIdentifier'; +import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock'; + +const mockObjectMetadataItems = getObjectMetadataItemsMock(); + +describe('useMapToObjectRecordIdentifier', () => { + it('should work as expected', async () => { + const { result } = renderHook( + () => { + const objectMetadataItem = mockObjectMetadataItems.find( + (item) => item.nameSingular === 'person', + )!; + + return useMapToObjectRecordIdentifier({ + objectMetadataItem, + })({ id: 'id', name: { firstName: 'Sheldon', lastName: 'Cooper' } }); + }, + { + wrapper: RecoilRoot, + }, + ); + + expect(result.current).toEqual({ + id: 'id', + name: 'Sheldon Cooper', + avatarUrl: null, + avatarType: 'rounded', + linkToShowPage: '/object/person/id', + }); + }); +}); diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useObjectMetadataItem.test.tsx b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useObjectMetadataItem.test.tsx new file mode 100644 index 000000000..e8d124ce8 --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useObjectMetadataItem.test.tsx @@ -0,0 +1,66 @@ +import { ReactNode } from 'react'; +import { MockedProvider } from '@apollo/client/testing'; +import { renderHook } from '@testing-library/react'; +import { RecoilRoot } from 'recoil'; + +import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; + +import { TestApolloMetadataClientProvider } from '../__mocks__/ApolloMetadataClientProvider'; + +const Wrapper = ({ children }: { children: ReactNode }) => ( + + + + {children} + + + +); + +describe('useObjectMetadataItem', () => { + it('should return correct properties', async () => { + const { result } = renderHook( + () => useObjectMetadataItem({ objectNameSingular: 'opportunity' }), + { + wrapper: Wrapper, + }, + ); + + const { + basePathToShowPage, + objectMetadataItem, + labelIdentifierFieldMetadata, + getRecordFromCache, + findManyRecordsQuery, + modifyRecordFromCache, + findOneRecordQuery, + createOneRecordMutation, + updateOneRecordMutation, + deleteOneRecordMutation, + executeQuickActionOnOneRecordMutation, + createManyRecordsMutation, + deleteManyRecordsMutation, + mapToObjectRecordIdentifier, + getObjectOrderByField, + } = result.current; + + expect(labelIdentifierFieldMetadata).toBeUndefined(); + expect(basePathToShowPage).toBe('/object/opportunity/'); + expect(objectMetadataItem.id).toBe('20202020-cae9-4ff4-9579-f7d9fe44c937'); + expect(typeof getRecordFromCache).toBe('function'); + expect(typeof modifyRecordFromCache).toBe('function'); + expect(typeof mapToObjectRecordIdentifier).toBe('function'); + expect(typeof getObjectOrderByField).toBe('function'); + expect(findManyRecordsQuery).toHaveProperty('kind', 'Document'); + expect(findOneRecordQuery).toHaveProperty('kind', 'Document'); + expect(createOneRecordMutation).toHaveProperty('kind', 'Document'); + expect(updateOneRecordMutation).toHaveProperty('kind', 'Document'); + expect(deleteOneRecordMutation).toHaveProperty('kind', 'Document'); + expect(executeQuickActionOnOneRecordMutation).toHaveProperty( + 'kind', + 'Document', + ); + expect(createManyRecordsMutation).toHaveProperty('kind', 'Document'); + expect(deleteManyRecordsMutation).toHaveProperty('kind', 'Document'); + }); +}); diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useObjectMetadataItemForSettings.test.tsx b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useObjectMetadataItemForSettings.test.tsx new file mode 100644 index 000000000..dd382ebcb --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useObjectMetadataItemForSettings.test.tsx @@ -0,0 +1,130 @@ +import { ReactNode } from 'react'; +import { MockedProvider } from '@apollo/client/testing'; +import { act, renderHook } from '@testing-library/react'; +import { RecoilRoot, useSetRecoilState } from 'recoil'; + +import { + query, + responseData, + variables, +} from '@/object-metadata/hooks/__mocks__/useObjectMetadataItemForSettings'; +import { useObjectMetadataItemForSettings } from '@/object-metadata/hooks/useObjectMetadataItemForSettings'; +import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; +import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock'; + +import { TestApolloMetadataClientProvider } from '../__mocks__/ApolloMetadataClientProvider'; + +const mocks = [ + { + request: { + query, + variables, + }, + result: jest.fn(() => ({ + data: { + updateOneObject: responseData, + }, + })), + }, +]; + +const Wrapper = ({ children }: { children: ReactNode }) => ( + + + + {children} + + + +); + +const mockObjectMetadataItems = getObjectMetadataItemsMock(); + +describe('useObjectMetadataItemForSettings', () => { + it('should findActiveObjectMetadataItemBySlug', async () => { + const { result } = renderHook( + () => { + const setMetadataItems = useSetRecoilState(objectMetadataItemsState); + setMetadataItems(mockObjectMetadataItems); + + return useObjectMetadataItemForSettings(); + }, + { + wrapper: Wrapper, + }, + ); + + act(() => { + const res = result.current.findActiveObjectMetadataItemBySlug('people'); + expect(res).toBeDefined(); + expect(res?.namePlural).toBe('people'); + }); + }); + + it('should findObjectMetadataItemById', async () => { + const { result } = renderHook( + () => { + const setMetadataItems = useSetRecoilState(objectMetadataItemsState); + setMetadataItems(mockObjectMetadataItems); + + return useObjectMetadataItemForSettings(); + }, + { + wrapper: Wrapper, + }, + ); + + act(() => { + const res = result.current.findObjectMetadataItemById( + '20202020-480c-434e-b4c7-e22408b97047', + ); + expect(res).toBeDefined(); + expect(res?.namePlural).toBe('companies'); + }); + }); + + it('should findObjectMetadataItemByNamePlural', async () => { + const { result } = renderHook( + () => { + const setMetadataItems = useSetRecoilState(objectMetadataItemsState); + setMetadataItems(mockObjectMetadataItems); + + return useObjectMetadataItemForSettings(); + }, + { + wrapper: Wrapper, + }, + ); + + act(() => { + const res = + result.current.findObjectMetadataItemByNamePlural('opportunities'); + expect(res).toBeDefined(); + expect(res?.namePlural).toBe('opportunities'); + }); + }); + + it('should editObjectMetadataItem', async () => { + const { result } = renderHook( + () => { + const setMetadataItems = useSetRecoilState(objectMetadataItemsState); + setMetadataItems(mockObjectMetadataItems); + + return useObjectMetadataItemForSettings(); + }, + { + wrapper: Wrapper, + }, + ); + + await act(async () => { + const res = await result.current.editObjectMetadataItem({ + id: 'idToUpdate', + description: 'newDescription', + labelPlural: 'labelPlural', + labelSingular: 'labelSingular', + }); + expect(res.data).toEqual({ updateOneObject: responseData }); + }); + }); +}); diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useObjectNamePluralFromSingular.test.tsx b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useObjectNamePluralFromSingular.test.tsx new file mode 100644 index 000000000..dc88c77e5 --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useObjectNamePluralFromSingular.test.tsx @@ -0,0 +1,17 @@ +import { renderHook } from '@testing-library/react'; +import { RecoilRoot } from 'recoil'; + +import { useObjectNamePluralFromSingular } from '../useObjectNamePluralFromSingular'; + +describe('useObjectNamePluralFromSingular', () => { + it('should work as expected', async () => { + const { result } = renderHook( + () => useObjectNamePluralFromSingular({ objectNameSingular: 'person' }), + { + wrapper: RecoilRoot, + }, + ); + + expect(result.current.objectNamePlural).toBe('people'); + }); +}); diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useObjectNameSingularFromPlural.test.ts b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useObjectNameSingularFromPlural.test.ts new file mode 100644 index 000000000..1016ddd30 --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useObjectNameSingularFromPlural.test.ts @@ -0,0 +1,17 @@ +import { renderHook } from '@testing-library/react'; +import { RecoilRoot } from 'recoil'; + +import { useObjectNameSingularFromPlural } from '../useObjectNameSingularFromPlural'; + +describe('useObjectNameSingularFromPlural', () => { + it('should work as expected', async () => { + const { result } = renderHook( + () => useObjectNameSingularFromPlural({ objectNamePlural: 'people' }), + { + wrapper: RecoilRoot, + }, + ); + + expect(result.current.objectNameSingular).toBe('person'); + }); +}); diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useRecordOptimisticEffect.test.tsx b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useRecordOptimisticEffect.test.tsx new file mode 100644 index 000000000..24a29c8dd --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useRecordOptimisticEffect.test.tsx @@ -0,0 +1,38 @@ +import { ReactNode } from 'react'; +import { MockedProvider } from '@apollo/client/testing'; +import { renderHook } from '@testing-library/react'; +import { RecoilRoot } from 'recoil'; + +import { useRecordOptimisticEffect } from '@/object-metadata/hooks/useRecordOptimisticEffect'; +import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock'; + +const mockRegisterOptimisticEffect = jest.fn(); + +jest.mock('@/apollo/optimistic-effect/hooks/useOptimisticEffect', () => ({ + useOptimisticEffect: jest.fn(() => ({ + registerOptimisticEffect: mockRegisterOptimisticEffect, + unregisterOptimisticEffect: jest.fn(), + })), +})); + +const Wrapper = ({ children }: { children: ReactNode }) => ( + + {children} + +); + +const mockObjectMetadataItems = getObjectMetadataItemsMock(); + +describe('useRecordOptimisticEffect', () => { + it('should work as expected', async () => { + const objectMetadataItem = mockObjectMetadataItems.find( + (item) => item.namePlural === 'people', + )!; + + renderHook(() => useRecordOptimisticEffect({ objectMetadataItem }), { + wrapper: Wrapper, + }); + + expect(mockRegisterOptimisticEffect).toHaveBeenCalled(); + }); +}); diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useRelationMetadata.test.tsx b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useRelationMetadata.test.tsx new file mode 100644 index 000000000..d377424bf --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useRelationMetadata.test.tsx @@ -0,0 +1,55 @@ +import { ReactNode } from 'react'; +import { MockedProvider } from '@apollo/client/testing'; +import { renderHook } from '@testing-library/react'; +import { RecoilRoot } from 'recoil'; + +import { useRelationMetadata } from '@/object-metadata/hooks/useRelationMetadata'; +import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; +import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock'; + +import { TestApolloMetadataClientProvider } from '../__mocks__/ApolloMetadataClientProvider'; + +const Wrapper = ({ children }: { children: ReactNode }) => ( + + + + {children} + + + +); + +describe('useRelationMetadata', () => { + it('should return correct properties', async () => { + const { result, rerender } = renderHook( + ({ fieldMetadataItem }: { fieldMetadataItem?: FieldMetadataItem }) => + useRelationMetadata({ fieldMetadataItem }), + { + wrapper: Wrapper, + initialProps: {}, + }, + ); + + const { + relationFieldMetadataItem, + relationObjectMetadataItem, + relationType, + } = result.current; + + expect(relationFieldMetadataItem).toBeUndefined(); + expect(relationObjectMetadataItem).toBeUndefined(); + expect(relationType).toBeUndefined(); + + const objectMetadataItems = getObjectMetadataItemsMock(); + const objectMetadata = objectMetadataItems.find( + (item) => item.nameSingular === 'person', + )!; + const fieldMetadataItem = objectMetadata.fields.find( + (field) => field.name === 'opportunities', + )!; + + rerender({ fieldMetadataItem }); + + expect(result.current.relationType).toBe('ONE_TO_MANY'); + }); +}); diff --git a/packages/twenty-front/src/modules/object-metadata/utils/__tests__/getFieldSlug.test.ts b/packages/twenty-front/src/modules/object-metadata/utils/__tests__/getFieldSlug.test.ts new file mode 100644 index 000000000..84a0865d1 --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/utils/__tests__/getFieldSlug.test.ts @@ -0,0 +1,8 @@ +import { getFieldSlug } from '@/object-metadata/utils/getFieldSlug'; + +describe('getFieldSlug', () => { + it('should work as expected', () => { + const res = getFieldSlug({ label: 'Pipeline Step' }); + expect(res).toBe('pipeline-step'); + }); +}); diff --git a/packages/twenty-front/src/modules/object-metadata/utils/__tests__/getObjectOrderByField.test.ts b/packages/twenty-front/src/modules/object-metadata/utils/__tests__/getObjectOrderByField.test.ts new file mode 100644 index 000000000..591711e36 --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/utils/__tests__/getObjectOrderByField.test.ts @@ -0,0 +1,16 @@ +import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock'; +import { getObjectOrderByField } from '@/object-metadata/utils/getObjectOrderByField'; + +const mockObjectMetadataItems = getObjectMetadataItemsMock(); + +describe('getObjectOrderByField', () => { + it('should work as expected', () => { + const objectMetadataItem = mockObjectMetadataItems.find( + (item) => item.nameSingular === 'person', + )!; + const res = getObjectOrderByField(objectMetadataItem); + expect(res).toEqual({ + name: { firstName: 'AscNullsLast', lastName: 'AscNullsLast' }, + }); + }); +}); diff --git a/packages/twenty-front/src/modules/object-metadata/utils/__tests__/getObjectSlug.test.ts b/packages/twenty-front/src/modules/object-metadata/utils/__tests__/getObjectSlug.test.ts new file mode 100644 index 000000000..fdd16fdb1 --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/utils/__tests__/getObjectSlug.test.ts @@ -0,0 +1,15 @@ +import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock'; +import { getObjectSlug } from '@/object-metadata/utils/getObjectSlug'; + +const mockObjectMetadataItems = getObjectMetadataItemsMock(); + +describe('getObjectSlug', () => { + it('should work as expected', () => { + const objectMetadataItem = mockObjectMetadataItems.find( + (item) => item.nameSingular === 'person', + )!; + + const res = getObjectSlug(objectMetadataItem); + expect(res).toBe('people'); + }); +}); diff --git a/packages/twenty-front/src/modules/object-metadata/utils/__tests__/isLabelIdentifierField.test.ts b/packages/twenty-front/src/modules/object-metadata/utils/__tests__/isLabelIdentifierField.test.ts new file mode 100644 index 000000000..85ad1f0eb --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/utils/__tests__/isLabelIdentifierField.test.ts @@ -0,0 +1,11 @@ +import { isLabelIdentifierField } from '@/object-metadata/utils/isLabelIdentifierField'; + +describe('isLabelIdentifierField', () => { + it('should work as expected', () => { + const res = isLabelIdentifierField({ + fieldMetadataItem: { id: 'fieldId', name: 'fieldName' }, + objectMetadataItem: {}, + }); + expect(res).toBe(false); + }); +}); diff --git a/packages/twenty-front/src/modules/object-metadata/utils/__tests__/isObjectMetadataAvailableForRelation.test.ts b/packages/twenty-front/src/modules/object-metadata/utils/__tests__/isObjectMetadataAvailableForRelation.test.ts new file mode 100644 index 000000000..e2dff5d09 --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/utils/__tests__/isObjectMetadataAvailableForRelation.test.ts @@ -0,0 +1,15 @@ +import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock'; +import { isObjectMetadataAvailableForRelation } from '@/object-metadata/utils/isObjectMetadataAvailableForRelation'; + +const mockObjectMetadataItems = getObjectMetadataItemsMock(); + +describe('isObjectMetadataAvailableForRelation', () => { + it('should work as expected', () => { + const objectMetadataItem = mockObjectMetadataItems.find( + (item) => item.nameSingular === 'person', + )!; + + const res = isObjectMetadataAvailableForRelation(objectMetadataItem); + expect(res).toBe(true); + }); +}); diff --git a/packages/twenty-front/src/modules/object-metadata/utils/__tests__/validateMetadataLabel.test.ts b/packages/twenty-front/src/modules/object-metadata/utils/__tests__/validateMetadataLabel.test.ts new file mode 100644 index 000000000..7f29a921f --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/utils/__tests__/validateMetadataLabel.test.ts @@ -0,0 +1,8 @@ +import { validateMetadataLabel } from '@/object-metadata/utils/validateMetadataLabel'; + +describe('validateMetadataLabel', () => { + it('should work as expected', () => { + const res = validateMetadataLabel('Pipeline Step'); + expect(res).toBe(true); + }); +});