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 000000000..db20611f4
--- /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 000000000..58c242c31
--- /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 000000000..3d68abe65
--- /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 000000000..7f96b1be6
--- /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 000000000..1fad18ddb
--- /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 000000000..68dcfc563
--- /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 000000000..98d3cad28
--- /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 000000000..e1d899605
--- /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 000000000..d0f574f5f
--- /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 000000000..f55efc120
--- /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 000000000..013889b93
--- /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 000000000..313ca054f
--- /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);
+ });
+});