Add tests for modules/object-metadata/hooks (#3485)

* Add tests for `modules/object-metadata/hooks`

Co-authored-by: v1b3m <vibenjamin6@gmail.com>

* Merge main

Co-authored-by: v1b3m <vibenjamin6@gmail.com>

* Refactor according to self review

Co-authored-by: v1b3m <vibenjamin6@gmail.com>

---------

Co-authored-by: gitstart-twenty <gitstart-twenty@users.noreply.github.com>
Co-authored-by: v1b3m <vibenjamin6@gmail.com>
This commit is contained in:
gitstart-twenty
2024-01-17 15:57:31 +01:00
committed by GitHub
parent ba038a4a83
commit 6af2513528
30 changed files with 1624 additions and 0 deletions

View File

@ -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<NormalizedCacheObject>;
return (
<ApolloMetadataClientContext.Provider value={client}>
{client ? children : ''}
</ApolloMetadataClientContext.Provider>
);
};

View File

@ -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: '',
};

View File

@ -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: '',
};

View File

@ -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: '',
};

View File

@ -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: [],
},
};

View File

@ -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: [] },
};

View File

@ -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: '',
};

View File

@ -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<ObjectMetadataItem>) => {
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<ObjectMetadataItem>) => {
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<ObjectMetadataItem>) => {
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');
});
});

View File

@ -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 }) => (
<RecoilRoot>
<MockedProvider mocks={mocks} addTypename={false}>
<TestApolloMetadataClientProvider>
{children}
</TestApolloMetadataClientProvider>
</MockedProvider>
</RecoilRoot>
);
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 });
});
});
});

View File

@ -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 }) => (
<RecoilRoot>
<MockedProvider mocks={mocks} addTypename={false}>
<TestApolloMetadataClientProvider>
{children}
</TestApolloMetadataClientProvider>
</MockedProvider>
</RecoilRoot>
);
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 });
});
});
});

View File

@ -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 }) => (
<RecoilRoot>
<MockedProvider mocks={mocks} addTypename={false}>
<TestApolloMetadataClientProvider>
{children}
</TestApolloMetadataClientProvider>
</MockedProvider>
</RecoilRoot>
);
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 });
});
});
});

View File

@ -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 }) => (
<RecoilRoot>
<MockedProvider mocks={mocks} addTypename={false}>
<TestApolloMetadataClientProvider>
{children}
</TestApolloMetadataClientProvider>
</MockedProvider>
</RecoilRoot>
);
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,
});
});
});
});

View File

@ -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);
});
});

View File

@ -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 }) => (
<RecoilRoot>
<MockedProvider mocks={mocks} addTypename={false}>
<TestApolloMetadataClientProvider>
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
{children}
</SnackBarProviderScope>
</TestApolloMetadataClientProvider>
</MockedProvider>
</RecoilRoot>
);
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([]);
});
});

View File

@ -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' },
});
});
});

View File

@ -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');
});
});

View File

@ -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);
});
});

View File

@ -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',
});
});
});

View File

@ -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 }) => (
<RecoilRoot>
<MockedProvider addTypename={false}>
<TestApolloMetadataClientProvider>
{children}
</TestApolloMetadataClientProvider>
</MockedProvider>
</RecoilRoot>
);
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');
});
});

View File

@ -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 }) => (
<RecoilRoot>
<MockedProvider mocks={mocks} addTypename={false}>
<TestApolloMetadataClientProvider>
{children}
</TestApolloMetadataClientProvider>
</MockedProvider>
</RecoilRoot>
);
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 });
});
});
});

View File

@ -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');
});
});

View File

@ -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');
});
});

View File

@ -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 }) => (
<RecoilRoot>
<MockedProvider addTypename={false}>{children}</MockedProvider>
</RecoilRoot>
);
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();
});
});

View File

@ -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 }) => (
<RecoilRoot>
<MockedProvider addTypename={false}>
<TestApolloMetadataClientProvider>
{children}
</TestApolloMetadataClientProvider>
</MockedProvider>
</RecoilRoot>
);
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');
});
});

View File

@ -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');
});
});

View File

@ -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' },
});
});
});

View File

@ -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');
});
});

View File

@ -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);
});
});

View File

@ -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);
});
});

View File

@ -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);
});
});