From d10efb15d594d949e08e57f8a977c29db49ff5ef Mon Sep 17 00:00:00 2001 From: gitstart-twenty <140154534+gitstart-twenty@users.noreply.github.com> Date: Tue, 7 May 2024 20:04:55 +0800 Subject: [PATCH] Add unit tests on object record mutation and query hooks (#5014) ### Description Add unit tests on object record mutation and query hooks ### Refs #4884 ### Demo ![Screenshot 2024-04-18 at 15 16 19](https://github.com/twentyhq/twenty/assets/140154534/c75f716a-725e-43eb-a703-3db29065fb18) Fixes #4884 --------- Co-authored-by: gitstart-twenty Co-authored-by: v1b3m Co-authored-by: Thiago Nascimbeni Co-authored-by: Toledodev Co-authored-by: Lucas Bordeau --- .../__mocks__/useFindDuplicateRecords.ts | 67 +++++++++++++++++ .../useCreateManyRecordsMutation.test.tsx | 63 ++++++++++++++++ .../useCreateOneRecordMutation.test.tsx | 63 ++++++++++++++++ .../useDeleteManyRecordsMutation.test.tsx | 40 ++++++++++ .../useDeleteOneRecordMutation.test.tsx | 40 ++++++++++ ...uteQuickActionOnOneRecordMutation.test.tsx | 60 +++++++++++++++ .../useFindDuplicateRecords.test.tsx | 62 ++++++++++++++++ .../useFindDuplicateRecordsQuery.test.tsx | 74 +++++++++++++++++++ .../useFindManyRecordsQuery.test.tsx | 73 ++++++++++++++++++ .../__tests__/useFindOneRecordQuery.test.tsx | 60 +++++++++++++++ .../__tests__/useLazyFindOneRecord.test.tsx | 62 ++++++++++++++++ .../useUpdateOneRecordMutation.test.tsx | 63 ++++++++++++++++ 12 files changed, 727 insertions(+) create mode 100644 packages/twenty-front/src/modules/object-record/hooks/__mocks__/useFindDuplicateRecords.ts create mode 100644 packages/twenty-front/src/modules/object-record/hooks/__tests__/useCreateManyRecordsMutation.test.tsx create mode 100644 packages/twenty-front/src/modules/object-record/hooks/__tests__/useCreateOneRecordMutation.test.tsx create mode 100644 packages/twenty-front/src/modules/object-record/hooks/__tests__/useDeleteManyRecordsMutation.test.tsx create mode 100644 packages/twenty-front/src/modules/object-record/hooks/__tests__/useDeleteOneRecordMutation.test.tsx create mode 100644 packages/twenty-front/src/modules/object-record/hooks/__tests__/useExecuteQuickActionOnOneRecordMutation.test.tsx create mode 100644 packages/twenty-front/src/modules/object-record/hooks/__tests__/useFindDuplicateRecords.test.tsx create mode 100644 packages/twenty-front/src/modules/object-record/hooks/__tests__/useFindDuplicateRecordsQuery.test.tsx create mode 100644 packages/twenty-front/src/modules/object-record/hooks/__tests__/useFindManyRecordsQuery.test.tsx create mode 100644 packages/twenty-front/src/modules/object-record/hooks/__tests__/useFindOneRecordQuery.test.tsx create mode 100644 packages/twenty-front/src/modules/object-record/hooks/__tests__/useLazyFindOneRecord.test.tsx create mode 100644 packages/twenty-front/src/modules/object-record/hooks/__tests__/useUpdateOneRecordMutation.test.tsx diff --git a/packages/twenty-front/src/modules/object-record/hooks/__mocks__/useFindDuplicateRecords.ts b/packages/twenty-front/src/modules/object-record/hooks/__mocks__/useFindDuplicateRecords.ts new file mode 100644 index 0000000000..db20611f42 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/hooks/__mocks__/useFindDuplicateRecords.ts @@ -0,0 +1,67 @@ +import { gql } from '@apollo/client'; +import { mockedPeopleData } from '~/testing/mock-data/people'; + +export const query = gql` + query FindDuplicatePerson($id: UUID) { + personDuplicates(id: $id) { + edges { + node { + __typename + xLink { + label + url + } + id + createdAt + city + email + jobTitle + name { + firstName + lastName + } + phone + linkedinLink { + label + url + } + updatedAt + avatarUrl + companyId + } + cursor + } + pageInfo { + hasNextPage + startCursor + endCursor + } + totalCount + } + } +`; + +export const variables = { + id: '6205681e-7c11-40b4-9e32-f523dbe54590', +}; + +export const responseData = { + personDuplicates: { + edges: [ + { + node: { __typename: 'Person', ...mockedPeopleData[0], updatedAt: '' }, + cursor: 'cursor1', + }, + { + node: { __typename: 'Person', ...mockedPeopleData[1], updatedAt: '' }, + cursor: 'cursor2', + }, + ], + pageInfo: { + hasNextPage: false, + startCursor: 'cursor1', + endCursor: 'cursor2', + }, + totalCount: 2, + }, +}; diff --git a/packages/twenty-front/src/modules/object-record/hooks/__tests__/useCreateManyRecordsMutation.test.tsx b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useCreateManyRecordsMutation.test.tsx new file mode 100644 index 0000000000..58c242c31f --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useCreateManyRecordsMutation.test.tsx @@ -0,0 +1,63 @@ +import { renderHook } from '@testing-library/react'; +import { print } from 'graphql'; +import { RecoilRoot } from 'recoil'; + +import { useCreateManyRecordsMutation } from '@/object-record/hooks/useCreateManyRecordsMutation'; + +const expectedQueryTemplate = ` + mutation CreatePeople($data: [PersonCreateInput!]!) { + createPeople(data: $data) { + __typename + xLink { + label + url + } + id + createdAt + city + email + jobTitle + name { + firstName + lastName + } + phone + linkedinLink { + label + url + } + updatedAt + avatarUrl + companyId + } + } +`.replace(/\s/g, ''); + +describe('useCreateManyRecordsMutation', () => { + it('should return a valid createManyRecordsMutation', () => { + const objectNameSingular = 'person'; + const depth = 2; + + const { result } = renderHook( + () => + useCreateManyRecordsMutation({ + objectNameSingular, + depth, + }), + { + wrapper: RecoilRoot, + }, + ); + + const { createManyRecordsMutation } = result.current; + + expect(createManyRecordsMutation).toBeDefined(); + + const printedReceivedQuery = print(createManyRecordsMutation).replace( + /\s/g, + '', + ); + + expect(printedReceivedQuery).toEqual(expectedQueryTemplate); + }); +}); diff --git a/packages/twenty-front/src/modules/object-record/hooks/__tests__/useCreateOneRecordMutation.test.tsx b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useCreateOneRecordMutation.test.tsx new file mode 100644 index 0000000000..3d68abe65e --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useCreateOneRecordMutation.test.tsx @@ -0,0 +1,63 @@ +import { renderHook } from '@testing-library/react'; +import { print } from 'graphql'; +import { RecoilRoot } from 'recoil'; + +import { useCreateOneRecordMutation } from '@/object-record/hooks/useCreateOneRecordMutation'; + +const expectedQueryTemplate = ` + mutation CreateOnePerson($input: PersonCreateInput!) { + createPerson(data: $input) { + __typename + xLink { + label + url + } + id + createdAt + city + email + jobTitle + name { + firstName + lastName + } + phone + linkedinLink { + label + url + } + updatedAt + avatarUrl + companyId + } + } +`.replace(/\s/g, ''); + +describe('useCreateOneRecordMutation', () => { + it('should return a valid createOneRecordMutation', () => { + const objectNameSingular = 'person'; + const depth = 2; + + const { result } = renderHook( + () => + useCreateOneRecordMutation({ + objectNameSingular, + depth, + }), + { + wrapper: RecoilRoot, + }, + ); + + const { createOneRecordMutation } = result.current; + + expect(createOneRecordMutation).toBeDefined(); + + const printedReceivedQuery = print(createOneRecordMutation).replace( + /\s/g, + '', + ); + + expect(printedReceivedQuery).toEqual(expectedQueryTemplate); + }); +}); diff --git a/packages/twenty-front/src/modules/object-record/hooks/__tests__/useDeleteManyRecordsMutation.test.tsx b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useDeleteManyRecordsMutation.test.tsx new file mode 100644 index 0000000000..7f96b1be61 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useDeleteManyRecordsMutation.test.tsx @@ -0,0 +1,40 @@ +import { renderHook } from '@testing-library/react'; +import { print } from 'graphql'; +import { RecoilRoot } from 'recoil'; + +import { useDeleteManyRecordsMutation } from '@/object-record/hooks/useDeleteManyRecordsMutation'; + +const expectedQueryTemplate = ` + mutation DeleteManyPeople($filter: PersonFilterInput!) { + deletePeople(filter: $filter) { + id + } + } +`.replace(/\s/g, ''); + +describe('useDeleteManyRecordsMutation', () => { + it('should return a valid deleteManyRecordsMutation', () => { + const objectNameSingular = 'person'; + + const { result } = renderHook( + () => + useDeleteManyRecordsMutation({ + objectNameSingular, + }), + { + wrapper: RecoilRoot, + }, + ); + + const { deleteManyRecordsMutation } = result.current; + + expect(deleteManyRecordsMutation).toBeDefined(); + + const printedReceivedQuery = print(deleteManyRecordsMutation).replace( + /\s/g, + '', + ); + + expect(printedReceivedQuery).toEqual(expectedQueryTemplate); + }); +}); diff --git a/packages/twenty-front/src/modules/object-record/hooks/__tests__/useDeleteOneRecordMutation.test.tsx b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useDeleteOneRecordMutation.test.tsx new file mode 100644 index 0000000000..1fad18ddb0 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useDeleteOneRecordMutation.test.tsx @@ -0,0 +1,40 @@ +import { renderHook } from '@testing-library/react'; +import { print } from 'graphql'; +import { RecoilRoot } from 'recoil'; + +import { useDeleteOneRecordMutation } from '@/object-record/hooks/useDeleteOneRecordMutation'; + +const expectedQueryTemplate = ` + mutation DeleteOnePerson($idToDelete: UUID!) { + deletePerson(id: $idToDelete) { + id + } + } +`.replace(/\s/g, ''); + +describe('useDeleteOneRecordMutation', () => { + it('should return a valid deleteOneRecordMutation', () => { + const objectNameSingular = 'person'; + + const { result } = renderHook( + () => + useDeleteOneRecordMutation({ + objectNameSingular, + }), + { + wrapper: RecoilRoot, + }, + ); + + const { deleteOneRecordMutation } = result.current; + + expect(deleteOneRecordMutation).toBeDefined(); + + const printedReceivedQuery = print(deleteOneRecordMutation).replace( + /\s/g, + '', + ); + + expect(printedReceivedQuery).toEqual(expectedQueryTemplate); + }); +}); diff --git a/packages/twenty-front/src/modules/object-record/hooks/__tests__/useExecuteQuickActionOnOneRecordMutation.test.tsx b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useExecuteQuickActionOnOneRecordMutation.test.tsx new file mode 100644 index 0000000000..68dcfc563e --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useExecuteQuickActionOnOneRecordMutation.test.tsx @@ -0,0 +1,60 @@ +import { renderHook } from '@testing-library/react'; +import { print } from 'graphql'; +import { RecoilRoot } from 'recoil'; + +import { useExecuteQuickActionOnOneRecordMutation } from '@/object-record/hooks/useExecuteQuickActionOnOneRecordMutation'; + +const expectedQueryTemplate = ` + mutation ExecuteQuickActionOnOnePerson($idToExecuteQuickActionOn: UUID!) { + executeQuickActionOnPerson(id: $idToExecuteQuickActionOn) { + __typename + xLink { + label + url + } + id + createdAt + city + email + jobTitle + name { + firstName + lastName + } + phone + linkedinLink { + label + url + } + updatedAt + avatarUrl + companyId + } + } +`.replace(/\s/g, ''); + +describe('useExecuteQuickActionOnOneRecordMutation', () => { + it('should return a valid executeQuickActionOnOneRecordMutation', () => { + const objectNameSingular = 'person'; + + const { result } = renderHook( + () => + useExecuteQuickActionOnOneRecordMutation({ + objectNameSingular, + }), + { + wrapper: RecoilRoot, + }, + ); + + const { executeQuickActionOnOneRecordMutation } = result.current; + + expect(executeQuickActionOnOneRecordMutation).toBeDefined(); + + const printedReceivedQuery = print( + executeQuickActionOnOneRecordMutation, + ).replace(/\s/g, ''); + + expect(printedReceivedQuery).toEqual(expectedQueryTemplate); + }); +}); diff --git a/packages/twenty-front/src/modules/object-record/hooks/__tests__/useFindDuplicateRecords.test.tsx b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useFindDuplicateRecords.test.tsx new file mode 100644 index 0000000000..98d3cad285 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useFindDuplicateRecords.test.tsx @@ -0,0 +1,62 @@ +import { ReactNode } from 'react'; +import { MockedProvider } from '@apollo/client/testing'; +import { renderHook, waitFor } from '@testing-library/react'; +import { RecoilRoot } from 'recoil'; + +import { useFindDuplicateRecords } from '@/object-record/hooks/useFindDuplicateRecords'; +import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope'; + +import { + query, + responseData, + variables, +} from '../__mocks__/useFindDuplicateRecords'; + +const mocks = [ + { + request: { + query, + variables, + }, + result: jest.fn(() => ({ + data: responseData, + })), + }, +]; + +const Wrapper = ({ children }: { children: ReactNode }) => ( + + + + {children} + + + +); + +describe('useFindDuplicateRecords', () => { + it('should fetch duplicate records and return the correct data', async () => { + const objectRecordId = '6205681e-7c11-40b4-9e32-f523dbe54590'; + const objectNameSingular = 'person'; + + const { result } = renderHook( + () => + useFindDuplicateRecords({ + objectRecordId, + objectNameSingular, + }), + { + wrapper: Wrapper, + }, + ); + + expect(result.current.loading).toBe(true); + + await waitFor(() => { + expect(result.current.loading).toBe(false); + expect(result.current.records).toBeDefined(); + }); + + expect(mocks[0].result).toHaveBeenCalled(); + }); +}); diff --git a/packages/twenty-front/src/modules/object-record/hooks/__tests__/useFindDuplicateRecordsQuery.test.tsx b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useFindDuplicateRecordsQuery.test.tsx new file mode 100644 index 0000000000..e1d899605d --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useFindDuplicateRecordsQuery.test.tsx @@ -0,0 +1,74 @@ +import { renderHook } from '@testing-library/react'; +import { print } from 'graphql'; +import { RecoilRoot } from 'recoil'; + +import { useFindDuplicateRecordsQuery } from '@/object-record/hooks/useFindDuplicatesRecordsQuery'; + +const expectedQueryTemplate = ` + query FindDuplicatePerson($id: UUID) { + personDuplicates(id: $id) { + edges { + node { + __typename + xLink { + label + url + } + id + createdAt + city + email + jobTitle + name { + firstName + lastName + } + phone + linkedinLink { + label + url + } + updatedAt + avatarUrl + companyId + } + cursor + } + pageInfo { + hasNextPage + startCursor + endCursor + } + totalCount + } + } +`.replace(/\s/g, ''); + +describe('useFindDuplicateRecordsQuery', () => { + it('should return a valid findDuplicateRecordsQuery', () => { + const objectNameSingular = 'person'; + const depth = 2; + + const { result } = renderHook( + () => + useFindDuplicateRecordsQuery({ + objectNameSingular, + depth, + }), + { + wrapper: RecoilRoot, + }, + ); + + const { findDuplicateRecordsQuery } = result.current; + + expect(findDuplicateRecordsQuery).toBeDefined(); + + const printedReceivedQuery = print(findDuplicateRecordsQuery).replace( + /\s/g, + '', + ); + + expect(printedReceivedQuery).toEqual(expectedQueryTemplate); + }); +}); diff --git a/packages/twenty-front/src/modules/object-record/hooks/__tests__/useFindManyRecordsQuery.test.tsx b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useFindManyRecordsQuery.test.tsx new file mode 100644 index 0000000000..d0f574f5f5 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useFindManyRecordsQuery.test.tsx @@ -0,0 +1,73 @@ +import { renderHook } from '@testing-library/react'; +import { print } from 'graphql'; +import { RecoilRoot } from 'recoil'; + +import { useFindManyRecordsQuery } from '@/object-record/hooks/useFindManyRecordsQuery'; + +const expectedQueryTemplate = ` + query FindManyPeople($filter: PersonFilterInput, $orderBy: PersonOrderByInput, $lastCursor: String, $limit: Float) { + people(filter: $filter, orderBy: $orderBy, first: $limit, after: $lastCursor) { + edges { + node { + __typename + xLink { + label + url + } + id + createdAt + city + email + jobTitle + name { + firstName + lastName + } + phone + linkedinLink { + label + url + } + updatedAt + avatarUrl + companyId + } + cursor + } + pageInfo { + hasNextPage + startCursor + endCursor + } + totalCount + } + } +`.replace(/\s/g, ''); + +describe('useFindManyRecordsQuery', () => { + it('should return a valid findManyRecordsQuery', () => { + const objectNameSingular = 'person'; + const depth = 2; + const computeReferences = true; + + const { result } = renderHook( + () => + useFindManyRecordsQuery({ + objectNameSingular, + depth, + computeReferences, + }), + { + wrapper: RecoilRoot, + }, + ); + + const { findManyRecordsQuery } = result.current; + + expect(findManyRecordsQuery).toBeDefined(); + + const printedReceivedQuery = print(findManyRecordsQuery).replace(/\s/g, ''); + + expect(printedReceivedQuery).toEqual(expectedQueryTemplate); + }); +}); diff --git a/packages/twenty-front/src/modules/object-record/hooks/__tests__/useFindOneRecordQuery.test.tsx b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useFindOneRecordQuery.test.tsx new file mode 100644 index 0000000000..f55efc120a --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useFindOneRecordQuery.test.tsx @@ -0,0 +1,60 @@ +import { renderHook } from '@testing-library/react'; +import { print } from 'graphql'; +import { RecoilRoot } from 'recoil'; + +import { useFindOneRecordQuery } from '@/object-record/hooks/useFindOneRecordQuery'; + +const expectedQueryTemplate = ` +query FindOnePerson($objectRecordId: UUID!) { + person(filter: { id: { eq: $objectRecordId } }) { + __typename + xLink { + label + url + } + id + createdAt + city + email + jobTitle + name { + firstName + lastName + } + phone + linkedinLink { + label + url + } + updatedAt + avatarUrl + companyId + } +} +`.replace(/\s/g, ''); + +describe('useFindOneRecordQuery', () => { + it('should return a valid findOneRecordQuery', () => { + const objectNameSingular = 'person'; + const depth = 2; + + const { result } = renderHook( + () => + useFindOneRecordQuery({ + objectNameSingular, + depth, + }), + { + wrapper: RecoilRoot, + }, + ); + + const { findOneRecordQuery } = result.current; + + expect(findOneRecordQuery).toBeDefined(); + + const printedReceivedQuery = print(findOneRecordQuery).replace(/\s/g, ''); + + expect(printedReceivedQuery).toEqual(expectedQueryTemplate); + }); +}); diff --git a/packages/twenty-front/src/modules/object-record/hooks/__tests__/useLazyFindOneRecord.test.tsx b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useLazyFindOneRecord.test.tsx new file mode 100644 index 0000000000..013889b934 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useLazyFindOneRecord.test.tsx @@ -0,0 +1,62 @@ +import { ReactNode } from 'react'; +import { MockedProvider } from '@apollo/client/testing'; +import { act, renderHook, waitFor } from '@testing-library/react'; +import { RecoilRoot } from 'recoil'; + +import { + query, + responseData, + variables, +} from '@/object-record/hooks/__mocks__/useFindOneRecord'; +import { useLazyFindOneRecord } from '@/object-record/hooks/useLazyFindOneRecord'; +import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope'; + +const mocks = [ + { + request: { + query, + variables, + }, + result: jest.fn(() => ({ + data: { + person: responseData, + }, + })), + }, +]; + +const Wrapper = ({ children }: { children: ReactNode }) => ( + + + + {children} + + + +); + +const objectRecordId = '6205681e-7c11-40b4-9e32-f523dbe54590'; + +describe('useLazyFindOneRecord', () => { + it('fetches record data when called', async () => { + const { result } = renderHook( + () => useLazyFindOneRecord({ objectNameSingular: 'person' }), + { + wrapper: Wrapper, + }, + ); + + act(() => { + result.current.findOneRecord({ objectRecordId: objectRecordId }); + }); + + expect(result.current.loading).toBe(true); + + await waitFor(() => { + expect(result.current.loading).toBe(false); + expect(result.current.record).toBeDefined(); + }); + + expect(mocks[0].result).toHaveBeenCalled(); + }); +}); diff --git a/packages/twenty-front/src/modules/object-record/hooks/__tests__/useUpdateOneRecordMutation.test.tsx b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useUpdateOneRecordMutation.test.tsx new file mode 100644 index 0000000000..313ca054f3 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useUpdateOneRecordMutation.test.tsx @@ -0,0 +1,63 @@ +import { renderHook } from '@testing-library/react'; +import { print } from 'graphql'; +import { RecoilRoot } from 'recoil'; + +import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRecordMutation'; + +const expectedQueryTemplate = ` +mutation UpdateOnePerson($idToUpdate: UUID!, $input: PersonUpdateInput!) { + updatePerson(id: $idToUpdate, data: $input) { + __typename + xLink { + label + url + } + id + createdAt + city + email + jobTitle + name { + firstName + lastName + } + phone + linkedinLink { + label + url + } + updatedAt + avatarUrl + companyId + } +} +`.replace(/\s/g, ''); + +describe('useUpdateOneRecordMutation', () => { + it('should return a valid createManyRecordsMutation', () => { + const objectNameSingular = 'person'; + const depth = 2; + + const { result } = renderHook( + () => + useUpdateOneRecordMutation({ + objectNameSingular, + depth, + }), + { + wrapper: RecoilRoot, + }, + ); + + const { updateOneRecordMutation } = result.current; + + expect(updateOneRecordMutation).toBeDefined(); + + const printedReceivedQuery = print(updateOneRecordMutation).replace( + /\s/g, + '', + ); + + expect(printedReceivedQuery).toEqual(expectedQueryTemplate); + }); +});