TWNTY-3549 - Add tests for modules/object-record/field (#3572)
* Add tests for `modules/object-record/field` Co-authored-by: v1b3m <vibenjamin6@gmail.com> * Merge main Co-authored-by: v1b3m <vibenjamin6@gmail.com> * Move field definitions to separate file 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:
committed by
GitHub
parent
e0943b15c4
commit
2b6d66f1bc
@ -0,0 +1,100 @@
|
|||||||
|
import { FieldDefinition } from '@/object-record/field/types/FieldDefinition';
|
||||||
|
import {
|
||||||
|
FieldBooleanMetadata,
|
||||||
|
FieldFullNameMetadata,
|
||||||
|
FieldLinkMetadata,
|
||||||
|
FieldPhoneMetadata,
|
||||||
|
FieldRatingMetadata,
|
||||||
|
FieldRelationMetadata,
|
||||||
|
FieldSelectMetadata,
|
||||||
|
FieldTextMetadata,
|
||||||
|
} from '@/object-record/field/types/FieldMetadata';
|
||||||
|
|
||||||
|
export const fieldMetadataId = 'fieldMetadataId';
|
||||||
|
|
||||||
|
export const textfieldDefinition: FieldDefinition<FieldTextMetadata> = {
|
||||||
|
fieldMetadataId,
|
||||||
|
label: 'User Name',
|
||||||
|
iconName: 'User',
|
||||||
|
type: 'TEXT',
|
||||||
|
metadata: { placeHolder: 'John Doe', fieldName: 'userName' },
|
||||||
|
};
|
||||||
|
|
||||||
|
export const booleanFieldDefinition: FieldDefinition<FieldBooleanMetadata> = {
|
||||||
|
fieldMetadataId,
|
||||||
|
label: 'Is Active?',
|
||||||
|
iconName: 'iconName',
|
||||||
|
type: 'BOOLEAN',
|
||||||
|
metadata: {
|
||||||
|
objectMetadataNameSingular: 'person',
|
||||||
|
fieldName: 'isActive',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const relationFieldDefinition: FieldDefinition<FieldRelationMetadata> = {
|
||||||
|
fieldMetadataId,
|
||||||
|
label: 'Contact',
|
||||||
|
iconName: 'Phone',
|
||||||
|
type: 'RELATION',
|
||||||
|
metadata: {
|
||||||
|
fieldName: 'contact',
|
||||||
|
relationFieldMetadataId: 'relationFieldMetadataId',
|
||||||
|
relationObjectMetadataNamePlural: 'users',
|
||||||
|
relationObjectMetadataNameSingular: 'user',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const selectFieldDefinition: FieldDefinition<FieldSelectMetadata> = {
|
||||||
|
fieldMetadataId,
|
||||||
|
label: 'Account Owner',
|
||||||
|
iconName: 'iconName',
|
||||||
|
type: 'SELECT',
|
||||||
|
metadata: {
|
||||||
|
fieldName: 'accountOwner',
|
||||||
|
options: [{ label: 'Elon Musk', color: 'blue', value: 'userId' }],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fullNameFieldDefinition: FieldDefinition<FieldFullNameMetadata> = {
|
||||||
|
fieldMetadataId,
|
||||||
|
label: 'Display Name',
|
||||||
|
iconName: 'profile',
|
||||||
|
type: 'FULL_NAME',
|
||||||
|
metadata: {
|
||||||
|
fieldName: 'displayName',
|
||||||
|
placeHolder: 'Mr Miagi',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const linkFieldDefinition: FieldDefinition<FieldLinkMetadata> = {
|
||||||
|
fieldMetadataId,
|
||||||
|
label: 'LinkedIn URL',
|
||||||
|
iconName: 'url',
|
||||||
|
type: 'LINK',
|
||||||
|
metadata: {
|
||||||
|
fieldName: 'linkedInURL',
|
||||||
|
placeHolder: 'https://linkedin.com/user',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const phoneFieldDefinition: FieldDefinition<FieldPhoneMetadata> = {
|
||||||
|
fieldMetadataId,
|
||||||
|
label: 'Contact',
|
||||||
|
iconName: 'Phone',
|
||||||
|
type: 'TEXT',
|
||||||
|
metadata: {
|
||||||
|
objectMetadataNameSingular: 'person',
|
||||||
|
placeHolder: '(+256)-712-345-6789',
|
||||||
|
fieldName: 'phone',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ratingfieldDefinition: FieldDefinition<FieldRatingMetadata> = {
|
||||||
|
fieldMetadataId,
|
||||||
|
label: 'Rating',
|
||||||
|
iconName: 'iconName',
|
||||||
|
type: 'RATING',
|
||||||
|
metadata: {
|
||||||
|
fieldName: 'rating',
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
import { ReactNode } from 'react';
|
||||||
|
import { act, renderHook } from '@testing-library/react';
|
||||||
|
import { RecoilRoot, useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import {
|
||||||
|
fieldMetadataId,
|
||||||
|
textfieldDefinition,
|
||||||
|
} from '@/object-record/field/__mocks__/fieldDefinitions';
|
||||||
|
import { FieldContext } from '@/object-record/field/contexts/FieldContext';
|
||||||
|
import { entityFieldInitialValueFamilyState } from '@/object-record/field/states/entityFieldInitialValueFamilyState';
|
||||||
|
|
||||||
|
import { useFieldInitialValue } from '../useFieldInitialValue';
|
||||||
|
|
||||||
|
const entityId = 'entityId';
|
||||||
|
|
||||||
|
const wrapper = ({ children }: { children: ReactNode }) => (
|
||||||
|
<RecoilRoot>
|
||||||
|
<FieldContext.Provider
|
||||||
|
value={{
|
||||||
|
fieldDefinition: textfieldDefinition,
|
||||||
|
entityId,
|
||||||
|
hotkeyScope: 'hotkeyScope',
|
||||||
|
isLabelIdentifier: false,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</FieldContext.Provider>
|
||||||
|
</RecoilRoot>
|
||||||
|
);
|
||||||
|
|
||||||
|
describe('useFieldInitialValue', () => {
|
||||||
|
it('should work as expected', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const setFieldInitialValue = useSetRecoilState(
|
||||||
|
entityFieldInitialValueFamilyState({
|
||||||
|
fieldMetadataId,
|
||||||
|
entityId,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
setFieldInitialValue,
|
||||||
|
fieldInitialValue: useFieldInitialValue(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{
|
||||||
|
wrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.current.fieldInitialValue).toBeUndefined();
|
||||||
|
|
||||||
|
const initialValue = { isEmpty: false, value: 'Sheldon Cooper' };
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.setFieldInitialValue(initialValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.fieldInitialValue).toEqual(initialValue);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
import { ReactNode } from 'react';
|
||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { RecoilRoot } from 'recoil';
|
||||||
|
|
||||||
|
import {
|
||||||
|
phoneFieldDefinition,
|
||||||
|
relationFieldDefinition,
|
||||||
|
} from '@/object-record/field/__mocks__/fieldDefinitions';
|
||||||
|
import { FieldContext } from '@/object-record/field/contexts/FieldContext';
|
||||||
|
import { useGetButtonIcon } from '@/object-record/field/hooks/useGetButtonIcon';
|
||||||
|
import { FieldDefinition } from '@/object-record/field/types/FieldDefinition';
|
||||||
|
import { FieldMetadata } from '@/object-record/field/types/FieldMetadata';
|
||||||
|
import { IconPencil } from '@/ui/display/icon';
|
||||||
|
|
||||||
|
const entityId = 'entityId';
|
||||||
|
|
||||||
|
const getWrapper =
|
||||||
|
(fieldDefinition: FieldDefinition<FieldMetadata>) =>
|
||||||
|
({ children }: { children: ReactNode }) => (
|
||||||
|
<FieldContext.Provider
|
||||||
|
value={{
|
||||||
|
fieldDefinition,
|
||||||
|
entityId,
|
||||||
|
hotkeyScope: 'hotkeyScope',
|
||||||
|
isLabelIdentifier: false,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RecoilRoot>{children}</RecoilRoot>
|
||||||
|
</FieldContext.Provider>
|
||||||
|
);
|
||||||
|
|
||||||
|
const PhoneWrapper = getWrapper(phoneFieldDefinition);
|
||||||
|
const RelationWrapper = getWrapper(relationFieldDefinition);
|
||||||
|
|
||||||
|
describe('useGetButtonIcon', () => {
|
||||||
|
it('should return undefined', () => {
|
||||||
|
const { result } = renderHook(() => useGetButtonIcon());
|
||||||
|
expect(result.current).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return icon pencil', () => {
|
||||||
|
const { result } = renderHook(() => useGetButtonIcon(), {
|
||||||
|
wrapper: PhoneWrapper,
|
||||||
|
});
|
||||||
|
expect(result.current).toEqual(IconPencil);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return iconPencil for relation field', () => {
|
||||||
|
const { result } = renderHook(() => useGetButtonIcon(), {
|
||||||
|
wrapper: RelationWrapper,
|
||||||
|
});
|
||||||
|
expect(result.current).toEqual(IconPencil);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
import { ReactNode } from 'react';
|
||||||
|
import { act, renderHook } from '@testing-library/react';
|
||||||
|
import { RecoilRoot, useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { phoneFieldDefinition } from '@/object-record/field/__mocks__/fieldDefinitions';
|
||||||
|
import { FieldContext } from '@/object-record/field/contexts/FieldContext';
|
||||||
|
import { useIsFieldEditModeValueEmpty } from '@/object-record/field/hooks/useIsFieldEditModeValueEmpty';
|
||||||
|
import { entityFieldsEditModeValueFamilyState } from '@/object-record/field/states/entityFieldsEditModeValueFamilyState';
|
||||||
|
|
||||||
|
const entityId = 'entityId';
|
||||||
|
|
||||||
|
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||||
|
<FieldContext.Provider
|
||||||
|
value={{
|
||||||
|
fieldDefinition: phoneFieldDefinition,
|
||||||
|
entityId,
|
||||||
|
hotkeyScope: 'hotkeyScope',
|
||||||
|
isLabelIdentifier: false,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RecoilRoot>{children}</RecoilRoot>
|
||||||
|
</FieldContext.Provider>
|
||||||
|
);
|
||||||
|
|
||||||
|
describe('useIsFieldEditModeValueEmpty', () => {
|
||||||
|
it('should work as expected', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const setFieldEditModeValue = useSetRecoilState(
|
||||||
|
entityFieldsEditModeValueFamilyState(entityId),
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
setFieldEditModeValue,
|
||||||
|
isFieldEditModeValueEmpty: useIsFieldEditModeValueEmpty(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{
|
||||||
|
wrapper: Wrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.current.isFieldEditModeValueEmpty).toBe(true);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.setFieldEditModeValue({
|
||||||
|
phone: '+1 233223',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.isFieldEditModeValueEmpty).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
import { ReactNode } from 'react';
|
||||||
|
import { act, renderHook } from '@testing-library/react';
|
||||||
|
import { RecoilRoot, useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { phoneFieldDefinition } from '@/object-record/field/__mocks__/fieldDefinitions';
|
||||||
|
import { FieldContext } from '@/object-record/field/contexts/FieldContext';
|
||||||
|
import { useIsFieldEmpty } from '@/object-record/field/hooks/useIsFieldEmpty';
|
||||||
|
import { entityFieldsFamilyState } from '@/object-record/field/states/entityFieldsFamilyState';
|
||||||
|
|
||||||
|
const entityId = 'entityId';
|
||||||
|
|
||||||
|
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||||
|
<FieldContext.Provider
|
||||||
|
value={{
|
||||||
|
fieldDefinition: phoneFieldDefinition,
|
||||||
|
entityId,
|
||||||
|
hotkeyScope: 'hotkeyScope',
|
||||||
|
isLabelIdentifier: false,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RecoilRoot>{children}</RecoilRoot>
|
||||||
|
</FieldContext.Provider>
|
||||||
|
);
|
||||||
|
|
||||||
|
describe('useIsFieldEmpty', () => {
|
||||||
|
it('should work as expected', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const setFieldState = useSetRecoilState(
|
||||||
|
entityFieldsFamilyState(entityId),
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
setFieldState,
|
||||||
|
isFieldEditModeValueEmpty: useIsFieldEmpty(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{
|
||||||
|
wrapper: Wrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.current.isFieldEditModeValueEmpty).toBe(true);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.setFieldState({
|
||||||
|
id: 'id',
|
||||||
|
phone: '+1 233223',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.isFieldEditModeValueEmpty).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
import { ReactNode } from 'react';
|
||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { RecoilRoot } from 'recoil';
|
||||||
|
|
||||||
|
import {
|
||||||
|
phoneFieldDefinition,
|
||||||
|
ratingfieldDefinition,
|
||||||
|
} from '@/object-record/field/__mocks__/fieldDefinitions';
|
||||||
|
import { FieldContext } from '@/object-record/field/contexts/FieldContext';
|
||||||
|
import { useIsFieldInputOnly } from '@/object-record/field/hooks/useIsFieldInputOnly';
|
||||||
|
import { FieldDefinition } from '@/object-record/field/types/FieldDefinition';
|
||||||
|
import { FieldMetadata } from '@/object-record/field/types/FieldMetadata';
|
||||||
|
|
||||||
|
const entityId = 'entityId';
|
||||||
|
|
||||||
|
const getWrapper =
|
||||||
|
(fieldDefinition: FieldDefinition<FieldMetadata>) =>
|
||||||
|
({ children }: { children: ReactNode }) => (
|
||||||
|
<FieldContext.Provider
|
||||||
|
value={{
|
||||||
|
fieldDefinition,
|
||||||
|
entityId,
|
||||||
|
hotkeyScope: 'hotkeyScope',
|
||||||
|
isLabelIdentifier: false,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RecoilRoot>{children}</RecoilRoot>
|
||||||
|
</FieldContext.Provider>
|
||||||
|
);
|
||||||
|
|
||||||
|
const RatingWrapper = getWrapper(ratingfieldDefinition);
|
||||||
|
const PhoneWrapper = getWrapper(phoneFieldDefinition);
|
||||||
|
|
||||||
|
describe('useIsFieldInputOnly', () => {
|
||||||
|
it('should return true', () => {
|
||||||
|
const { result } = renderHook(() => useIsFieldInputOnly(), {
|
||||||
|
wrapper: RatingWrapper,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false', () => {
|
||||||
|
const { result } = renderHook(() => useIsFieldInputOnly(), {
|
||||||
|
wrapper: PhoneWrapper,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,159 @@
|
|||||||
|
import { ReactNode } from 'react';
|
||||||
|
import { gql } from '@apollo/client';
|
||||||
|
import { MockedProvider, MockedResponse } from '@apollo/client/testing';
|
||||||
|
import { act, renderHook, waitFor } from '@testing-library/react';
|
||||||
|
import { RecoilRoot, useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import {
|
||||||
|
phoneFieldDefinition,
|
||||||
|
relationFieldDefinition,
|
||||||
|
} from '@/object-record/field/__mocks__/fieldDefinitions';
|
||||||
|
import {
|
||||||
|
FieldContext,
|
||||||
|
RecordUpdateHook,
|
||||||
|
RecordUpdateHookParams,
|
||||||
|
} from '@/object-record/field/contexts/FieldContext';
|
||||||
|
import { usePersistField } from '@/object-record/field/hooks/usePersistField';
|
||||||
|
import { entityFieldsFamilySelector } from '@/object-record/field/states/selectors/entityFieldsFamilySelector';
|
||||||
|
import { FieldDefinition } from '@/object-record/field/types/FieldDefinition';
|
||||||
|
import { FieldMetadata } from '@/object-record/field/types/FieldMetadata';
|
||||||
|
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||||
|
|
||||||
|
jest.mock('@/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery', () => ({
|
||||||
|
useMapFieldMetadataToGraphQLQuery: () => () => '\n',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const query = gql`
|
||||||
|
mutation UpdateOneWorkspaceMember(
|
||||||
|
$idToUpdate: ID!
|
||||||
|
$input: WorkspaceMemberUpdateInput!
|
||||||
|
) {
|
||||||
|
updateWorkspaceMember(id: $idToUpdate, data: $input) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const mocks: MockedResponse[] = [
|
||||||
|
{
|
||||||
|
request: {
|
||||||
|
query,
|
||||||
|
variables: { idToUpdate: 'entityId', input: { phone: '+1 123 456' } },
|
||||||
|
},
|
||||||
|
result: jest.fn(() => ({
|
||||||
|
data: {
|
||||||
|
updateWorkspaceMember: {
|
||||||
|
id: 'entityId',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
request: {
|
||||||
|
query,
|
||||||
|
variables: {
|
||||||
|
idToUpdate: 'entityId',
|
||||||
|
input: { contactId: null, contact: { foo: 'bar' } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
result: jest.fn(() => ({
|
||||||
|
data: {
|
||||||
|
updateWorkspaceMember: {
|
||||||
|
id: 'entityId',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const entityId = 'entityId';
|
||||||
|
const fieldName = 'phone';
|
||||||
|
|
||||||
|
const getWrapper =
|
||||||
|
(fieldDefinition: FieldDefinition<FieldMetadata>) =>
|
||||||
|
({ children }: { children: ReactNode }) => {
|
||||||
|
const useUpdateOneRecordMutation: RecordUpdateHook = () => {
|
||||||
|
const { updateOneRecord } = useUpdateOneRecord({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.WorkspaceMember,
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateEntity = ({ variables }: RecordUpdateHookParams) => {
|
||||||
|
updateOneRecord?.({
|
||||||
|
idToUpdate: variables.where.id as string,
|
||||||
|
updateOneRecordInput: variables.updateOneRecordInput,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return [updateEntity, { loading: false }];
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MockedProvider mocks={mocks} addTypename={false}>
|
||||||
|
<FieldContext.Provider
|
||||||
|
value={{
|
||||||
|
fieldDefinition,
|
||||||
|
entityId,
|
||||||
|
hotkeyScope: 'hotkeyScope',
|
||||||
|
isLabelIdentifier: false,
|
||||||
|
useUpdateRecord: useUpdateOneRecordMutation,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RecoilRoot>{children}</RecoilRoot>
|
||||||
|
</FieldContext.Provider>
|
||||||
|
</MockedProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const PhoneWrapper = getWrapper(phoneFieldDefinition);
|
||||||
|
const RelationWrapper = getWrapper(relationFieldDefinition);
|
||||||
|
|
||||||
|
describe('usePersistField', () => {
|
||||||
|
it('should work as expected', async () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const entityFields = useRecoilValue(
|
||||||
|
entityFieldsFamilySelector({ entityId, fieldName }),
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
persistField: usePersistField(),
|
||||||
|
entityFields,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ wrapper: PhoneWrapper },
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.persistField('+1 123 456');
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mocks[0].result).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should persist relation field', async () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const entityFields = useRecoilValue(
|
||||||
|
entityFieldsFamilySelector({ entityId, fieldName }),
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
persistField: usePersistField(),
|
||||||
|
entityFields,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ wrapper: RelationWrapper },
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.persistField({ foo: 'bar' });
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mocks[1].result).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
import { ReactNode } from 'react';
|
||||||
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
|
import { act, renderHook } from '@testing-library/react';
|
||||||
|
import { RecoilRoot, useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { phoneFieldDefinition } from '@/object-record/field/__mocks__/fieldDefinitions';
|
||||||
|
import { FieldContext } from '@/object-record/field/contexts/FieldContext';
|
||||||
|
import { useSaveFieldEditModeValue } from '@/object-record/field/hooks/useSaveFieldEditModeValue';
|
||||||
|
import { entityFieldsEditModeValueFamilySelector } from '@/object-record/field/states/selectors/entityFieldsEditModeValueFamilySelector';
|
||||||
|
|
||||||
|
const entityId = 'entityId';
|
||||||
|
const fieldName = 'phone';
|
||||||
|
|
||||||
|
const Wrapper = ({ children }: { children: ReactNode }) => {
|
||||||
|
return (
|
||||||
|
<MockedProvider addTypename={false}>
|
||||||
|
<FieldContext.Provider
|
||||||
|
value={{
|
||||||
|
fieldDefinition: phoneFieldDefinition,
|
||||||
|
entityId,
|
||||||
|
hotkeyScope: 'hotkeyScope',
|
||||||
|
isLabelIdentifier: false,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RecoilRoot>{children}</RecoilRoot>
|
||||||
|
</FieldContext.Provider>
|
||||||
|
</MockedProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('useSaveFieldEditModeValue', () => {
|
||||||
|
it('should work as expected', () => {
|
||||||
|
const {
|
||||||
|
result: { current },
|
||||||
|
} = renderHook(
|
||||||
|
() => {
|
||||||
|
const entityFieldsEditModeValue = useRecoilValue(
|
||||||
|
entityFieldsEditModeValueFamilySelector({ entityId, fieldName }),
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
saveFieldEditModeValue: useSaveFieldEditModeValue(),
|
||||||
|
entityFieldsEditModeValue,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ wrapper: Wrapper },
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(current.entityFieldsEditModeValue).toBeUndefined();
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
current.saveFieldEditModeValue('test');
|
||||||
|
});
|
||||||
|
|
||||||
|
// We expect `current.entityFieldsEditModeValue` to be updated
|
||||||
|
// but I think it's async
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,95 @@
|
|||||||
|
import { ReactNode } from 'react';
|
||||||
|
import { gql } from '@apollo/client';
|
||||||
|
import { MockedProvider, MockedResponse } from '@apollo/client/testing';
|
||||||
|
import { act, renderHook, waitFor } from '@testing-library/react';
|
||||||
|
import { RecoilRoot } from 'recoil';
|
||||||
|
|
||||||
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { booleanFieldDefinition } from '@/object-record/field/__mocks__/fieldDefinitions';
|
||||||
|
import {
|
||||||
|
FieldContext,
|
||||||
|
RecordUpdateHook,
|
||||||
|
RecordUpdateHookParams,
|
||||||
|
} from '@/object-record/field/contexts/FieldContext';
|
||||||
|
import { useToggleEditOnlyInput } from '@/object-record/field/hooks/useToggleEditOnlyInput';
|
||||||
|
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||||
|
|
||||||
|
jest.mock('@/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery', () => ({
|
||||||
|
useMapFieldMetadataToGraphQLQuery: () => () => '\n',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const entityId = 'entityId';
|
||||||
|
|
||||||
|
const mocks: MockedResponse[] = [
|
||||||
|
{
|
||||||
|
request: {
|
||||||
|
query: gql`
|
||||||
|
mutation UpdateOnePerson($idToUpdate: ID!, $input: PersonUpdateInput!) {
|
||||||
|
updatePerson(id: $idToUpdate, data: $input) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
variables: { idToUpdate: 'entityId', input: { isActive: true } },
|
||||||
|
},
|
||||||
|
result: jest.fn(() => ({
|
||||||
|
data: {
|
||||||
|
updateWorkspaceMember: {
|
||||||
|
id: 'entityId',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const Wrapper = ({ children }: { children: ReactNode }) => {
|
||||||
|
const useUpdateOneRecordMutation: RecordUpdateHook = () => {
|
||||||
|
const { updateOneRecord } = useUpdateOneRecord({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.Person,
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateEntity = ({ variables }: RecordUpdateHookParams) => {
|
||||||
|
updateOneRecord?.({
|
||||||
|
idToUpdate: variables.where.id as string,
|
||||||
|
updateOneRecordInput: variables.updateOneRecordInput,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return [updateEntity, { loading: false }];
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MockedProvider mocks={mocks} addTypename={false}>
|
||||||
|
<FieldContext.Provider
|
||||||
|
value={{
|
||||||
|
fieldDefinition: booleanFieldDefinition,
|
||||||
|
entityId,
|
||||||
|
hotkeyScope: 'hotkeyScope',
|
||||||
|
isLabelIdentifier: false,
|
||||||
|
useUpdateRecord: useUpdateOneRecordMutation,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RecoilRoot>{children}</RecoilRoot>
|
||||||
|
</FieldContext.Provider>
|
||||||
|
</MockedProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('useToggleEditOnlyInput', () => {
|
||||||
|
it('should toggle field', async () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => ({ toggleField: useToggleEditOnlyInput() }),
|
||||||
|
{
|
||||||
|
wrapper: Wrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.toggleField();
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mocks[0].result).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,118 @@
|
|||||||
|
import {
|
||||||
|
booleanFieldDefinition,
|
||||||
|
fieldMetadataId,
|
||||||
|
fullNameFieldDefinition,
|
||||||
|
linkFieldDefinition,
|
||||||
|
relationFieldDefinition,
|
||||||
|
selectFieldDefinition,
|
||||||
|
} from '@/object-record/field/__mocks__/fieldDefinitions';
|
||||||
|
import { FieldDefinition } from '@/object-record/field/types/FieldDefinition';
|
||||||
|
import { FieldCurrencyMetadata } from '@/object-record/field/types/FieldMetadata';
|
||||||
|
|
||||||
|
import { isFieldValueEmpty } from '../isFieldValueEmpty';
|
||||||
|
|
||||||
|
describe('isFieldValueEmpty', () => {
|
||||||
|
it('should return correct value for boolean field', () => {
|
||||||
|
expect(
|
||||||
|
isFieldValueEmpty({
|
||||||
|
fieldDefinition: booleanFieldDefinition,
|
||||||
|
fieldValue: null,
|
||||||
|
}),
|
||||||
|
).toBe(true);
|
||||||
|
expect(
|
||||||
|
isFieldValueEmpty({
|
||||||
|
fieldDefinition: booleanFieldDefinition,
|
||||||
|
fieldValue: false,
|
||||||
|
}),
|
||||||
|
).toBe(false);
|
||||||
|
expect(
|
||||||
|
isFieldValueEmpty({
|
||||||
|
fieldDefinition: booleanFieldDefinition,
|
||||||
|
fieldValue: true,
|
||||||
|
}),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return correct value for relation field', () => {
|
||||||
|
expect(
|
||||||
|
isFieldValueEmpty({
|
||||||
|
fieldDefinition: relationFieldDefinition,
|
||||||
|
fieldValue: null,
|
||||||
|
}),
|
||||||
|
).toBe(true);
|
||||||
|
expect(
|
||||||
|
isFieldValueEmpty({
|
||||||
|
fieldDefinition: relationFieldDefinition,
|
||||||
|
fieldValue: { foo: 'bar' },
|
||||||
|
}),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return correct value for select field', () => {
|
||||||
|
// If the value does not match the fieldDefinition, it will always return `false`
|
||||||
|
// Should it return `false` or `true` if the fieldValue doesn't match?
|
||||||
|
expect(
|
||||||
|
isFieldValueEmpty({
|
||||||
|
fieldDefinition: selectFieldDefinition,
|
||||||
|
fieldValue: '',
|
||||||
|
}),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return correct value for currency field', () => {
|
||||||
|
const fieldDefinition: FieldDefinition<FieldCurrencyMetadata> = {
|
||||||
|
fieldMetadataId,
|
||||||
|
label: 'Annual Income',
|
||||||
|
iconName: 'cashCow',
|
||||||
|
type: 'CURRENCY',
|
||||||
|
metadata: {
|
||||||
|
fieldName: 'annualIncome',
|
||||||
|
placeHolder: '100000',
|
||||||
|
isPositive: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(
|
||||||
|
isFieldValueEmpty({
|
||||||
|
fieldDefinition,
|
||||||
|
fieldValue: { currencyCode: 'USD', amountMicros: 1000000 },
|
||||||
|
}),
|
||||||
|
).toBe(false);
|
||||||
|
expect(
|
||||||
|
isFieldValueEmpty({
|
||||||
|
fieldDefinition,
|
||||||
|
fieldValue: { currencyCode: 'USD' },
|
||||||
|
}),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return correct value for fullname field', () => {
|
||||||
|
expect(
|
||||||
|
isFieldValueEmpty({
|
||||||
|
fieldDefinition: fullNameFieldDefinition,
|
||||||
|
fieldValue: { firstName: '', lastName: '' },
|
||||||
|
}),
|
||||||
|
).toBe(true);
|
||||||
|
expect(
|
||||||
|
isFieldValueEmpty({
|
||||||
|
fieldDefinition: fullNameFieldDefinition,
|
||||||
|
fieldValue: { firstName: 'Sheldon', lastName: '' },
|
||||||
|
}),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return correct value for link field', () => {
|
||||||
|
expect(
|
||||||
|
isFieldValueEmpty({
|
||||||
|
fieldDefinition: linkFieldDefinition,
|
||||||
|
fieldValue: { url: '', label: '' },
|
||||||
|
}),
|
||||||
|
).toBe(true);
|
||||||
|
expect(
|
||||||
|
isFieldValueEmpty({
|
||||||
|
fieldDefinition: linkFieldDefinition,
|
||||||
|
fieldValue: { url: 'https://linkedin.com/user-slug', label: '' },
|
||||||
|
}),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user