Add tests for modules/people, modules/pipeline, modules/search and modules/settings (#3395)

Co-authored-by: gitstart-twenty <gitstart-twenty@users.noreply.github.com>
Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
This commit is contained in:
gitstart-twenty 2024-01-12 18:05:56 +01:00 committed by GitHub
parent 284fabf17c
commit d05d7ec1d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 830 additions and 1 deletions

View File

@ -8,7 +8,8 @@ export default {
'~/(.+)': '<rootDir>/src/$1',
'@/(.+)': '<rootDir>/src/modules/$1',
'@testing/(.+)': '<rootDir>/src/testing/$1',
'\\.(jpg|jpeg|png|gif|webp|svg)$': '<rootDir>/__mocks__/imageMock.js',
'\\.(jpg|jpeg|png|gif|webp|svg|svg\\?react)$':
'<rootDir>/__mocks__/imageMock.js',
},
extensionsToTreatAsEsm: ['.ts', '.tsx'],
coverageThreshold: {

View File

@ -0,0 +1,124 @@
import { gql } from '@apollo/client';
export const query = gql`
mutation CreatePeople($data: [PersonCreateInput!]!) {
createPeople(data: $data) {
id
opportunities {
edges {
node {
id
}
}
}
xLink {
label
url
}
id
pointOfContactForOpportunities {
edges {
node {
id
}
}
}
createdAt
company {
id
}
city
email
activityTargets {
edges {
node {
id
}
}
}
jobTitle
favorites {
edges {
node {
id
}
}
}
attachments {
edges {
node {
id
}
}
}
name {
firstName
lastName
}
phone
linkedinLink {
label
url
}
updatedAt
avatarUrl
companyId
}
}
`;
export const personId = 'cb2e9f4b-20c3-4759-9315-4ffeecfaf71a';
export const variables = {
data: [
{
id: personId,
name: { firstName: 'Sheldon', lastName: ' Cooper' },
email: undefined,
jobTitle: undefined,
phone: undefined,
city: undefined,
},
],
};
export const responseData = [
{
opportunities: {
edges: [],
},
xLink: {
label: '',
url: '',
},
pointOfContactForOpportunities: {
edges: [],
},
createdAt: '',
company: {
id: '',
},
city: '',
email: '',
activityTargets: {
edges: [],
},
jobTitle: '',
favorites: {
edges: [],
},
attachments: {
edges: [],
},
name: variables.data[0].name,
phone: '',
linkedinLink: {
label: '',
url: '',
},
updatedAt: '',
avatarUrl: '',
companyId: '',
id: personId,
},
];

View File

@ -0,0 +1,107 @@
import { ReactNode } from 'react';
import { MockedProvider } from '@apollo/client/testing';
import { act, renderHook, waitFor } from '@testing-library/react';
import { RecoilRoot, useRecoilValue } from 'recoil';
import { spreadsheetImportState } from '@/spreadsheet-import/states/spreadsheetImportState';
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
import {
personId,
query,
responseData,
variables,
} from '../__mocks__/useSpreadsheetPersonImport';
import { useSpreadsheetPersonImport } from '../useSpreadsheetPersonImport';
jest.mock('uuid', () => ({
v4: jest.fn(() => personId),
}));
const mocks = [
{
request: {
query,
variables,
},
result: jest.fn(() => ({
data: {
createPeople: responseData,
},
})),
},
];
const Wrapper = ({ children }: { children: ReactNode }) => (
<RecoilRoot>
<MockedProvider mocks={mocks} addTypename={false}>
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
{children}
</SnackBarProviderScope>
</MockedProvider>
</RecoilRoot>
);
const fakeCsv = () => {
const csvContent = 'firstname, lastname\nSheldon, Cooper';
const blob = new Blob([csvContent], { type: 'text/csv' });
return new File([blob], 'fakeData.csv', { type: 'text/csv' });
};
describe('useSpreadsheetPersonImport', () => {
it('should work as expected', async () => {
const { result } = renderHook(
() => {
const spreadsheetImport = useRecoilValue(spreadsheetImportState);
const { openPersonSpreadsheetImport } = useSpreadsheetPersonImport();
return { openPersonSpreadsheetImport, spreadsheetImport };
},
{
wrapper: Wrapper,
},
);
const { spreadsheetImport, openPersonSpreadsheetImport } = result.current;
expect(spreadsheetImport.isOpen).toBe(false);
expect(spreadsheetImport.options).toBeNull();
await act(async () => {
openPersonSpreadsheetImport();
});
const { spreadsheetImport: updatedImport } = result.current;
expect(updatedImport.isOpen).toBe(true);
expect(updatedImport.options).toHaveProperty('onSubmit');
expect(updatedImport.options?.onSubmit).toBeInstanceOf(Function);
expect(updatedImport.options).toHaveProperty('fields');
expect(Array.isArray(updatedImport.options?.fields)).toBe(true);
act(() => {
updatedImport.options?.onSubmit(
{
validData: [
{
firstName: 'Sheldon',
lastName: ' Cooper',
},
],
invalidData: [],
all: [
{
firstName: 'Sheldon',
lastName: ' Cooper',
__index: 'cbc3985f-dde9-46d1-bae2-c124141700ac',
},
],
},
fakeCsv(),
);
});
await waitFor(() => {
expect(mocks[0].result).toHaveBeenCalled();
});
});
});

View File

@ -0,0 +1,66 @@
import { gql } from '@apollo/client';
export const query = gql`
mutation CreateOnePipelineStep($input: PipelineStepCreateInput!) {
createPipelineStep(data: $input) {
id
name
id
createdAt
opportunities {
edges {
node {
id
}
}
}
position
color
updatedAt
}
}
`;
export const deleteQuery = gql`
mutation DeleteOnePipelineStep($idToDelete: ID!) {
deletePipelineStep(id: $idToDelete) {
id
}
}
`;
export const mockId = '8f3b2121-f194-4ba4-9fbf-2d5a37126806';
export const currentPipelineId = 'f088c8c9-05d2-4276-b065-b863cc7d0b33';
const data = {
color: 'yellow',
id: 'columnId',
position: 1,
name: 'Column Title',
pipeline: { connect: { id: currentPipelineId } },
type: 'ongoing',
};
export const variables = {
input: {
id: mockId,
variables: {
data,
},
},
};
export const deleteVariables = { idToDelete: 'columnId' };
export const responseData = {
...data,
createdAt: '',
opportunities: {
edges: [],
},
updatedAt: '',
};
export const deleteResponseData = {
id: 'columnId',
};

View File

@ -0,0 +1,104 @@
import { ReactNode } from 'react';
import { act } from 'react-dom/test-utils';
import { MockedProvider } from '@apollo/client/testing';
import { renderHook } from '@testing-library/react';
import { RecoilRoot, useSetRecoilState } from 'recoil';
import { BoardColumnDefinition } from '@/object-record/record-board/types/BoardColumnDefinition';
import { currentPipelineState } from '@/pipeline/states/currentPipelineState';
import {
currentPipelineId,
deleteQuery,
deleteResponseData,
deleteVariables,
mockId,
query,
responseData,
variables,
} from '../__mocks__/usePipelineSteps';
import { usePipelineSteps } from '../usePipelineSteps';
const mocks = [
{
request: {
query,
variables,
},
result: jest.fn(() => ({
data: {
createPipelineStep: responseData,
},
})),
},
{
request: {
query: deleteQuery,
variables: deleteVariables,
},
result: jest.fn(() => ({
data: {
deletePipelineStep: deleteResponseData,
},
})),
},
];
const Wrapper = ({ children }: { children: ReactNode }) => (
<RecoilRoot>
<MockedProvider mocks={mocks} addTypename={false}>
{children}
</MockedProvider>
</RecoilRoot>
);
jest.mock('uuid', () => ({
v4: jest.fn(() => mockId),
}));
describe('usePipelineSteps', () => {
it('should handlePipelineStepAdd successfully', async () => {
const { result } = renderHook(
() => {
const setCurrentPipeline = useSetRecoilState(currentPipelineState);
setCurrentPipeline({ id: currentPipelineId });
return usePipelineSteps();
},
{
wrapper: Wrapper,
},
);
const boardColumn: BoardColumnDefinition = {
id: 'columnId',
title: 'Column Title',
colorCode: 'yellow',
position: 1,
};
await act(async () => {
const res = await result.current.handlePipelineStepAdd(boardColumn);
expect(res).toEqual(responseData);
});
});
it('should handlePipelineStepDelete successfully', async () => {
const { result } = renderHook(
() => {
const setCurrentPipeline = useSetRecoilState(currentPipelineState);
setCurrentPipeline({ id: currentPipelineId });
return usePipelineSteps();
},
{
wrapper: Wrapper,
},
);
const boardColumnId = 'columnId';
await act(async () => {
const res = await result.current.handlePipelineStepDelete(boardColumnId);
expect(res).toEqual(deleteResponseData);
});
});
});

View File

@ -0,0 +1,190 @@
import { gql } from '@apollo/client';
export const query = gql`
query FindManyPeople(
$filter: PersonFilterInput
$orderBy: PersonOrderByInput
$lastCursor: String
$limit: Float = 30
) {
people(
filter: $filter
orderBy: $orderBy
first: $limit
after: $lastCursor
) {
edges {
node {
id
opportunities {
edges {
node {
id
personId
pointOfContactId
updatedAt
companyId
pipelineStepId
probability
closeDate
amount {
amountMicros
currencyCode
}
id
createdAt
}
}
}
xLink {
label
url
}
id
pointOfContactForOpportunities {
edges {
node {
id
personId
pointOfContactId
updatedAt
companyId
pipelineStepId
probability
closeDate
amount {
amountMicros
currencyCode
}
id
createdAt
}
}
}
createdAt
company {
id
xLink {
label
url
}
linkedinLink {
label
url
}
domainName
annualRecurringRevenue {
amountMicros
currencyCode
}
createdAt
address
updatedAt
name
accountOwnerId
employees
id
idealCustomerProfile
}
city
email
activityTargets {
edges {
node {
id
updatedAt
createdAt
personId
activityId
companyId
id
}
}
}
jobTitle
favorites {
edges {
node {
id
id
companyId
createdAt
personId
position
workspaceMemberId
updatedAt
}
}
}
attachments {
edges {
node {
id
updatedAt
createdAt
name
personId
activityId
companyId
id
authorId
type
fullPath
}
}
}
name {
firstName
lastName
}
phone
linkedinLink {
label
url
}
updatedAt
avatarUrl
companyId
}
cursor
}
pageInfo {
hasNextPage
startCursor
endCursor
}
}
}
`;
export const variables = {
entitiesToSelect: {
limit: 10,
filter: {
and: [
{ and: [{ or: [{ name: { ilike: '%Entity%' } }] }] },
{ not: { id: { in: ['1', '2'] } } },
],
},
orderBy: { name: 'AscNullsLast' },
},
filteredSelectedEntities: {
limit: 60,
filter: {
and: [
{ and: [{ or: [{ name: { ilike: '%Entity%' } }] }] },
{ id: { in: ['1'] } },
],
},
orderBy: { name: 'AscNullsLast' },
},
selectedEntities: {
limit: 60,
filter: { id: { in: ['1'] } },
orderBy: { name: 'AscNullsLast' },
},
};
export const responseData = {
edges: [],
};

View File

@ -0,0 +1,109 @@
import { ReactNode } from 'react';
import { MockedProvider } from '@apollo/client/testing';
import { renderHook } from '@testing-library/react';
import { RecoilRoot, useSetRecoilState } from 'recoil';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
import { EntitiesForMultipleEntitySelect } from '@/object-record/relation-picker/types/EntitiesForMultipleEntitySelect';
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
import {
query,
responseData,
variables,
} from '../__mocks__/useFilteredSearchEntityQuery';
import { useFilteredSearchEntityQuery } from '../useFilteredSearchEntityQuery';
const mocks = [
{
request: {
query,
variables: variables.entitiesToSelect,
},
result: jest.fn(() => ({
data: {
people: responseData,
},
})),
},
{
request: {
query,
variables: variables.filteredSelectedEntities,
},
result: jest.fn(() => ({
data: {
people: responseData,
},
})),
},
{
request: {
query,
variables: variables.selectedEntities,
},
result: jest.fn(() => ({
data: {
people: responseData,
},
})),
},
];
const Wrapper = ({ children }: { children: ReactNode }) => (
<RecoilRoot>
<MockedProvider mocks={mocks} addTypename={false}>
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
{children}
</SnackBarProviderScope>
</MockedProvider>
</RecoilRoot>
);
describe('useFilteredSearchEntityQuery', () => {
it('returns the correct result when everything is provided', async () => {
const { result } = renderHook(
() => {
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
setCurrentWorkspace({
id: '32219445-f587-4c40-b2b1-6d3205ed96da',
displayName: 'cool-workspace',
allowImpersonation: false,
subscriptionStatus: 'incomplete',
});
const mockObjectMetadataItems = getObjectMetadataItemsMock();
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
setMetadataItems(mockObjectMetadataItems);
return useFilteredSearchEntityQuery({
orderByField: 'name',
filters: [{ fieldNames: ['name'], filter: 'Entity' }],
sortOrder: 'AscNullsLast',
selectedIds: ['1'],
mappingFunction: (entity): any => ({
value: entity.id,
label: entity.name,
}),
limit: 10,
excludeEntityIds: ['2'],
objectNameSingular: 'person',
});
},
{ wrapper: Wrapper },
);
const expectedResult: EntitiesForMultipleEntitySelect<any> = {
selectedEntities: [],
filteredSelectedEntities: [],
entitiesToSelect: [],
loading: true,
};
expect(result.current).toEqual(expectedResult);
});
});

View File

@ -0,0 +1,51 @@
import { act, renderHook } from '@testing-library/react';
import { useFieldMetadataForm } from '../useFieldMetadataForm';
describe('useFieldMetadataForm', () => {
it('should initialize with default values', () => {
const { result } = renderHook(() => useFieldMetadataForm());
expect(result.current.isInitialized).toBe(false);
act(() => {
result.current.initForm({});
});
expect(result.current.isInitialized).toBe(true);
expect(result.current.formValues).toEqual({
icon: 'IconUsers',
label: '',
type: 'TEXT',
currency: { currencyCode: 'USD' },
relation: {
type: 'ONE_TO_MANY',
objectMetadataId: '',
field: { label: '' },
},
select: [
{ color: 'green', label: 'Option 1', value: expect.any(String) },
],
});
});
it('should handle form changes', () => {
const { result } = renderHook(() => useFieldMetadataForm());
act(() => {
result.current.initForm({});
});
expect(result.current.hasFieldFormChanged).toBe(false);
expect(result.current.hasRelationFormChanged).toBe(false);
expect(result.current.hasSelectFormChanged).toBe(false);
act(() => {
result.current.handleFormChange({ label: 'New Label' });
});
expect(result.current.hasFieldFormChanged).toBe(true);
expect(result.current.hasRelationFormChanged).toBe(false);
expect(result.current.hasSelectFormChanged).toBe(false);
});
});

View File

@ -0,0 +1,50 @@
import { ReactNode } from 'react';
import { MockedProvider } from '@apollo/client/testing';
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 { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
import { useFieldPreview } from '../useFieldPreview';
const Wrapper = ({ children }: { children: ReactNode }) => (
<RecoilRoot>
<MockedProvider addTypename={false}>
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
{children}
</SnackBarProviderScope>
</MockedProvider>
</RecoilRoot>
);
const mockObjectMetadataItems = getObjectMetadataItemsMock();
describe('useFieldPreview', () => {
it('returns default values', () => {
const objectMetadataItem = mockObjectMetadataItems[1];
const fieldMetadata = objectMetadataItem.fields[0];
const { result } = renderHook(
() => {
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
setMetadataItems(mockObjectMetadataItems);
return useFieldPreview({
objectMetadataId: objectMetadataItem.id,
fieldMetadata,
});
},
{ wrapper: Wrapper },
);
expect(result.current.entityId).toBe(`${objectMetadataItem.id}-field-form`);
expect(result.current.FieldIcon).toBeDefined();
expect(result.current.fieldName).toBe(fieldMetadata.name);
expect(result.current.ObjectIcon).toBeDefined();
expect(result.current.fieldName).toBe(fieldMetadata.name);
expect(result.current.objectMetadataItem?.id).toBe(objectMetadataItem.id);
expect(result.current.relationObjectMetadataItem).toBeUndefined();
expect(result.current.value).toBeDefined();
});
});

View File

@ -0,0 +1,27 @@
import { act, renderHook } from '@testing-library/react';
import { RecoilRoot, RecoilState } from 'recoil';
import { generatedApiKeyFamilyState } from '@/settings/developers/states/generatedApiKeyFamilyState';
import { useGeneratedApiKeys } from '../useGeneratedApiKeys';
describe('useGeneratedApiKeys', () => {
test('should set generatedApiKeyFamilyState correctly', () => {
const { result } = renderHook(() => useGeneratedApiKeys(), {
wrapper: RecoilRoot,
});
const apiKeyId = 'someId';
const apiKey = 'someKey';
act(() => {
result.current(apiKeyId, apiKey);
});
const recoilState: RecoilState<string | null | undefined> =
generatedApiKeyFamilyState(apiKeyId);
const stateValue = recoilState.key;
expect(stateValue).toContain(apiKeyId);
});
});