Fix last batch of jest tests (#6582)

As title
This commit is contained in:
Thomas Trompette
2024-08-08 15:53:17 +02:00
committed by GitHub
parent 774cb554f4
commit a0e5ca44ba
5 changed files with 188 additions and 355 deletions

View File

@ -1,14 +1,17 @@
import { gql } from '@apollo/client'; import { renderHook } from '@testing-library/react';
import { MockedProvider, MockedResponse } from '@apollo/client/testing';
import { act, renderHook, waitFor } from '@testing-library/react';
import { ReactNode } from 'react'; import { ReactNode } from 'react';
import { RecoilRoot, useSetRecoilState } from 'recoil'; import { RecoilRoot } from 'recoil';
import { useActivities } from '@/activities/hooks/useActivities'; import { useActivities } from '@/activities/hooks/useActivities';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
import { mockWorkspaceMembers } from '~/testing/mock-data/workspace-members'; jest.mock('@/activities/hooks/useActivityTargetsForTargetableObjects', () => ({
useActivityTargetsForTargetableObjects: jest.fn(),
}));
jest.mock('@/object-record/hooks/useFindManyRecords', () => ({
useFindManyRecords: jest.fn(),
}));
const mockActivityTarget = { const mockActivityTarget = {
__typename: 'ActivityTarget', __typename: 'ActivityTarget',
@ -22,6 +25,7 @@ const mockActivityTarget = {
const mockActivity = { const mockActivity = {
__typename: 'Task', __typename: 'Task',
companyId: '123',
updatedAt: '2021-08-03T19:20:06.000Z', updatedAt: '2021-08-03T19:20:06.000Z',
createdAt: '2021-08-03T19:20:06.000Z', createdAt: '2021-08-03T19:20:06.000Z',
status: 'DONE', status: 'DONE',
@ -33,168 +37,35 @@ const mockActivity = {
id: '234', id: '234',
}; };
const defaultResponseData = {
pageInfo: {
hasNextPage: false,
startCursor: '',
endCursor: '',
},
totalCount: 1,
};
const mocks: MockedResponse[] = [
{
request: {
query: gql`
query FindManyActivityTargets(
$filter: ActivityTargetFilterInput
$orderBy: [ActivityTargetOrderByInput]
$lastCursor: String
$limit: Int
) {
activityTargets(
filter: $filter
orderBy: $orderBy
first: $limit
after: $lastCursor
) {
edges {
node {
__typename
updatedAt
createdAt
activityId
id
}
cursor
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
totalCount
}
}
`,
variables: {
filter: { companyId: { eq: '123' } },
limit: undefined,
orderBy: undefined,
},
},
result: jest.fn(() => ({
data: {
activityTargets: {
...defaultResponseData,
edges: [
{
node: mockActivityTarget,
cursor: '1',
},
],
},
},
})),
},
{
request: {
query: gql`
query FindManyTasks(
$filter: TaskFilterInput
$orderBy: [TaskOrderByInput]
$lastCursor: String
$limit: Int
) {
tasks(
filter: $filter
orderBy: $orderBy
first: $limit
after: $lastCursor
) {
edges {
node {
__typename
title
id
updatedAt
createdAt
body
status
dueAt
}
cursor
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
totalCount
}
}
`,
variables: {
filter: { id: { in: ['234'] } },
limit: undefined,
orderBy: [{}],
},
},
result: jest.fn(() => ({
data: {
activities: {
...defaultResponseData,
edges: [
{
node: mockActivity,
cursor: '1',
},
],
},
},
})),
},
];
const Wrapper = ({ children }: { children: ReactNode }) => ( const Wrapper = ({ children }: { children: ReactNode }) => (
<RecoilRoot> <RecoilRoot>{children}</RecoilRoot>
<MockedProvider mocks={mocks} addTypename={false}>
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
{children}
</SnackBarProviderScope>
</MockedProvider>
</RecoilRoot>
); );
describe('useActivities', () => { describe('useActivities', () => {
it('returns default response', () => { afterEach(() => {
const { result } = renderHook( jest.clearAllMocks();
() =>
useActivities({
objectNameSingular: CoreObjectNameSingular.Task,
targetableObjects: [],
activitiesFilters: {},
activitiesOrderByVariables: [{}],
skip: false,
}),
{ wrapper: Wrapper },
);
expect(result.current).toEqual({
activities: [],
loading: false,
});
}); });
it('fetches activities', async () => { it('fetches activities', async () => {
const useActivityTargetsForTargetableObjectsMock = jest.requireMock(
'@/activities/hooks/useActivityTargetsForTargetableObjects',
);
useActivityTargetsForTargetableObjectsMock.useActivityTargetsForTargetableObjects.mockReturnValue(
{
activityTargets: [mockActivityTarget],
loadingActivityTargets: false,
},
);
const useFindManyRecordsMock = jest.requireMock(
'@/object-record/hooks/useFindManyRecords',
);
useFindManyRecordsMock.useFindManyRecords.mockReturnValue({
records: [mockActivity],
});
const { result } = renderHook( const { result } = renderHook(
() => { () => {
const setCurrentWorkspaceMember = useSetRecoilState(
currentWorkspaceMemberState,
);
const activities = useActivities({ const activities = useActivities({
objectNameSingular: CoreObjectNameSingular.Task, objectNameSingular: CoreObjectNameSingular.Task,
targetableObjects: [ targetableObjects: [
@ -204,21 +75,11 @@ describe('useActivities', () => {
activitiesOrderByVariables: [{}], activitiesOrderByVariables: [{}],
skip: false, skip: false,
}); });
return { activities, setCurrentWorkspaceMember }; return activities;
}, },
{ wrapper: Wrapper }, { wrapper: Wrapper },
); );
act(() => { expect(result.current.activities).toEqual([mockActivity]);
result.current.setCurrentWorkspaceMember(mockWorkspaceMembers[0]);
});
await waitFor(() => {
expect(result.current.activities.loading).toBe(false);
});
const { activities } = result.current;
expect(activities.activities).toEqual([mockActivity]);
}); });
}); });

View File

@ -1,11 +1,17 @@
import { renderHook } from '@testing-library/react'; import { renderHook } from '@testing-library/react';
import { useTimelineActivities } from '@/activities/timelineActivities/hooks/useTimelineActivities'; import { useTimelineActivities } from '@/activities/timelineActivities/hooks/useTimelineActivities';
import { ReactNode } from 'react';
import { RecoilRoot } from 'recoil';
jest.mock('@/object-record/hooks/useFindManyRecords', () => ({ jest.mock('@/object-record/hooks/useFindManyRecords', () => ({
useFindManyRecords: jest.fn(), useFindManyRecords: jest.fn(),
})); }));
const Wrapper = ({ children }: { children: ReactNode }) => (
<RecoilRoot>{children}</RecoilRoot>
);
describe('useTimelineActivities', () => { describe('useTimelineActivities', () => {
afterEach(() => { afterEach(() => {
jest.clearAllMocks(); jest.clearAllMocks();
@ -52,8 +58,9 @@ describe('useTimelineActivities', () => {
records: mockedTimelineActivities, records: mockedTimelineActivities,
}); });
const { result } = renderHook(() => const { result } = renderHook(
useTimelineActivities(mockTargetableObject), () => useTimelineActivities(mockTargetableObject),
{ wrapper: Wrapper },
); );
expect(result.current.timelineActivities).toEqual(mockedTimelineActivities); expect(result.current.timelineActivities).toEqual(mockedTimelineActivities);

View File

@ -1,12 +1,20 @@
import { TimelineActivity } from '@/activities/timelineActivities/types/TimelineActivity'; import { TimelineActivity } from '@/activities/timelineActivities/types/TimelineActivity';
import { filterOutInvalidTimelineActivities } from '@/activities/timelineActivities/utils/filterOutInvalidTimelineActivities'; import { filterOutInvalidTimelineActivities } from '@/activities/timelineActivities/utils/filterOutInvalidTimelineActivities';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
const noteObjectMetadataItem = {
nameSingular: CoreObjectNameSingular.Note,
namePlural: 'notes',
fields: [{ name: 'field1' }, { name: 'field2' }, { name: 'field3' }],
} as ObjectMetadataItem;
describe('filterOutInvalidTimelineActivities', () => { describe('filterOutInvalidTimelineActivities', () => {
it('should filter out TimelineActivities with deleted fields from the properties diff', () => { it('should filter out TimelineActivities with deleted fields from the properties diff', () => {
const events = [ const events = [
{ {
id: '1', id: '1',
name: 'event1',
properties: { properties: {
diff: { diff: {
field1: { before: 'value1', after: 'value2' }, field1: { before: 'value1', after: 'value2' },
@ -17,6 +25,7 @@ describe('filterOutInvalidTimelineActivities', () => {
}, },
{ {
id: '2', id: '2',
name: 'event2',
properties: { properties: {
diff: { diff: {
field1: { before: 'value7', after: 'value8' }, field1: { before: 'value7', after: 'value8' },
@ -36,12 +45,13 @@ describe('filterOutInvalidTimelineActivities', () => {
const filteredEvents = filterOutInvalidTimelineActivities( const filteredEvents = filterOutInvalidTimelineActivities(
events, events,
'objectNameSingular', 'objectNameSingular',
[mainObjectMetadataItem], [mainObjectMetadataItem, noteObjectMetadataItem],
); );
expect(filteredEvents).toEqual([ expect(filteredEvents).toEqual([
{ {
id: '1', id: '1',
name: 'event1',
properties: { properties: {
diff: { diff: {
field1: { before: 'value1', after: 'value2' }, field1: { before: 'value1', after: 'value2' },
@ -52,6 +62,7 @@ describe('filterOutInvalidTimelineActivities', () => {
}, },
{ {
id: '2', id: '2',
name: 'event2',
properties: { properties: {
diff: { diff: {
field1: { before: 'value7', after: 'value8' }, field1: { before: 'value7', after: 'value8' },
@ -66,6 +77,7 @@ describe('filterOutInvalidTimelineActivities', () => {
const events = [ const events = [
{ {
id: '1', id: '1',
name: 'event1',
properties: { properties: {
diff: { diff: {
field3: { before: 'value5', after: 'value6' }, field3: { before: 'value5', after: 'value6' },
@ -74,6 +86,7 @@ describe('filterOutInvalidTimelineActivities', () => {
}, },
{ {
id: '2', id: '2',
name: 'event2',
properties: { properties: {
diff: { diff: {
field4: { before: 'value11', after: 'value12' }, field4: { before: 'value11', after: 'value12' },
@ -83,13 +96,15 @@ describe('filterOutInvalidTimelineActivities', () => {
] as TimelineActivity[]; ] as TimelineActivity[];
const mainObjectMetadataItem = { const mainObjectMetadataItem = {
nameSingular: 'objectNameSingular',
namePlural: 'objectNamePlural',
fields: [{ name: 'field1' }, { name: 'field2' }], fields: [{ name: 'field1' }, { name: 'field2' }],
} as ObjectMetadataItem; } as ObjectMetadataItem;
const filteredEvents = filterOutInvalidTimelineActivities( const filteredEvents = filterOutInvalidTimelineActivities(
events, events,
'objectNameSingular', 'objectNameSingular',
[mainObjectMetadataItem], [mainObjectMetadataItem, noteObjectMetadataItem],
); );
expect(filteredEvents).toEqual([]); expect(filteredEvents).toEqual([]);
@ -99,22 +114,26 @@ describe('filterOutInvalidTimelineActivities', () => {
const events = [ const events = [
{ {
id: '1', id: '1',
name: 'event1',
properties: {}, properties: {},
}, },
{ {
id: '2', id: '2',
name: 'event2',
properties: {}, properties: {},
}, },
] as TimelineActivity[]; ] as TimelineActivity[];
const mainObjectMetadataItem = { const mainObjectMetadataItem = {
nameSingular: 'objectNameSingular',
namePlural: 'objectNamePlural',
fields: [{ name: 'field1' }, { name: 'field2' }], fields: [{ name: 'field1' }, { name: 'field2' }],
} as ObjectMetadataItem; } as ObjectMetadataItem;
const filteredEvents = filterOutInvalidTimelineActivities( const filteredEvents = filterOutInvalidTimelineActivities(
events, events,
'objectNameSingular', 'objectNameSingular',
[mainObjectMetadataItem], [mainObjectMetadataItem, noteObjectMetadataItem],
); );
expect(filteredEvents).toEqual(events); expect(filteredEvents).toEqual(events);

View File

@ -43,7 +43,8 @@ export const filterOutInvalidTimelineActivities = (
const validDiffEntries = Object.entries(diff).filter(([diffKey]) => const validDiffEntries = Object.entries(diff).filter(([diffKey]) =>
isNoteOrTask isNoteOrTask
? noteFieldMetadataItemMap.has(diffKey) ? // Note and Task objects have the same field metadata
noteFieldMetadataItemMap.has(diffKey)
: fieldMetadataItemMap.has(diffKey), : fieldMetadataItemMap.has(diffKey),
); );

View File

@ -1,194 +1,139 @@
import { gql } from '@apollo/client'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { MockedProvider } from '@apollo/client/testing';
import { act, renderHook, waitFor } from '@testing-library/react';
import { RecoilRoot, useSetRecoilState } from 'recoil';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useMultiObjectSearch } from '@/object-record/relation-picker/hooks/useMultiObjectSearch'; import {
MultiObjectSearch,
ObjectRecordForSelect,
SelectedObjectRecordId,
useMultiObjectSearch,
} from '@/object-record/relation-picker/hooks/useMultiObjectSearch';
import { useMultiObjectSearchMatchesSearchFilterAndSelectedItemsQuery } from '@/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterAndSelectedItemsQuery';
import { useMultiObjectSearchMatchesSearchFilterAndToSelectQuery } from '@/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterAndToSelectQuery';
import { useMultiObjectSearchSelectedItemsQuery } from '@/object-record/relation-picker/hooks/useMultiObjectSearchSelectedItemsQuery';
import { renderHook } from '@testing-library/react';
import { FieldMetadataType } from '~/generated/graphql'; import { FieldMetadataType } from '~/generated/graphql';
const query = gql` jest.mock(
query CombinedFindManyRecords( '@/object-record/relation-picker/hooks/useMultiObjectSearchSelectedItemsQuery',
$filterNameSingular: NameSingularFilterInput );
$orderByNameSingular: [NameSingularOrderByInput] jest.mock(
$lastCursorNameSingular: String '@/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterAndSelectedItemsQuery',
$limitNameSingular: Int );
) { jest.mock(
namePlural( '@/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterAndToSelectQuery',
filter: $filterNameSingular );
orderBy: $orderByNameSingular
first: $limitNameSingular
after: $lastCursorNameSingular
) {
edges {
node {
__typename
id
}
cursor
}
pageInfo {
hasNextPage
startCursor
endCursor
}
totalCount
}
}
`;
const response = {
namePlural: {
edges: [{ node: { __typename: 'Custom', id: 'nodeId' }, cursor: 'cursor' }],
pageInfo: { startCursor: '', hasNextPage: '', endCursor: '' },
},
};
const mocks = [ const objectData: ObjectMetadataItem[] = [
{ {
request: { createdAt: 'createdAt',
query, id: 'id',
variables: { isActive: true,
filterNameSingular: { id: { in: ['1'] } }, isCustom: true,
orderByNameSingular: [{ createdAt: 'DescNullsLast' }], isSystem: false,
limitNameSingular: 60, isRemote: false,
labelPlural: 'labelPlural',
labelSingular: 'labelSingular',
namePlural: 'namePlural',
nameSingular: 'nameSingular',
updatedAt: 'updatedAt',
fields: [
{
id: 'f6a0a73a-5ee6-442e-b764-39b682471240',
name: 'id',
label: 'id',
type: FieldMetadataType.Uuid,
createdAt: '2024-01-01T00:00:00.000Z',
updatedAt: '2024-01-01T00:00:00.000Z',
isActive: true,
}, },
}, ],
result: jest.fn(() => ({
data: response,
})),
},
{
request: {
query,
variables: {
filterNameSingular: { and: [{}, { id: { in: ['1'] } }] },
orderByNameSingular: [{ createdAt: 'DescNullsLast' }],
limitNameSingular: 60,
},
},
result: jest.fn(() => ({
data: response,
})),
},
{
request: {
query,
variables: {
limitNameSingular: 60,
filterNameSingular: { not: { id: { in: ['1'] } } },
orderByNameSingular: [{ createdAt: 'DescNullsLast' }],
},
},
result: jest.fn(() => ({
data: response,
})),
}, },
]; ];
const Wrapper = ({ children }: { children: React.ReactNode }) => (
<MockedProvider mocks={mocks} addTypename={false}>
<RecoilRoot>{children}</RecoilRoot>
</MockedProvider>
);
describe('useMultiObjectSearch', () => { describe('useMultiObjectSearch', () => {
it('should return object formatted from objectMetadataItemsState', async () => { const selectedObjectRecordIds: SelectedObjectRecordId[] = [
const { result } = renderHook( { objectNameSingular: 'object1', id: '1' },
() => ({ { objectNameSingular: 'object2', id: '2' },
multiObjects: useMultiObjectSearch({ ];
searchFilterValue: '', const searchFilterValue = 'searchValue';
selectedObjectRecordIds: [ const limit = 5;
{ const excludedObjectRecordIds: SelectedObjectRecordId[] = [
objectNameSingular: 'nameSingular', { objectNameSingular: 'object3', id: '3' },
id: '1', { objectNameSingular: 'object4', id: '4' },
}, ];
], const excludedObjects: CoreObjectNameSingular[] = [];
}),
setObjectMetadata: useSetRecoilState(objectMetadataItemsState), const selectedObjectRecords: ObjectRecordForSelect[] = [
}), {
{ objectMetadataItem: objectData[0],
wrapper: Wrapper, record: {
}, __typename: 'ObjectRecord',
); id: '1',
const objectData: ObjectMetadataItem[] = [
{
createdAt: 'createdAt', createdAt: 'createdAt',
id: 'id',
isActive: true,
isCustom: true,
isSystem: false,
isRemote: false,
labelPlural: 'labelPlural',
labelSingular: 'labelSingular',
namePlural: 'namePlural',
nameSingular: 'nameSingular',
updatedAt: 'updatedAt', updatedAt: 'updatedAt',
fields: [
{
id: 'f6a0a73a-5ee6-442e-b764-39b682471240',
name: 'id',
label: 'id',
type: FieldMetadataType.Uuid,
createdAt: '2024-01-01T00:00:00.000Z',
updatedAt: '2024-01-01T00:00:00.000Z',
isActive: true,
},
],
}, },
]; recordIdentifier: {
act(() => { id: '1',
result.current.setObjectMetadata(objectData); name: 'name',
});
await waitFor(() => {
expect(mocks[0].result).toHaveBeenCalled();
// expect(mocks[1].result).toHaveBeenCalled();
expect(mocks[2].result).toHaveBeenCalled();
});
const expectedData = [
{
objectMetadataItem: {
createdAt: 'createdAt',
id: 'id',
isActive: true,
isCustom: true,
isSystem: false,
isRemote: false,
labelPlural: 'labelPlural',
labelSingular: 'labelSingular',
namePlural: 'namePlural',
nameSingular: 'nameSingular',
updatedAt: 'updatedAt',
fields: [
{
id: 'f6a0a73a-5ee6-442e-b764-39b682471240',
name: 'id',
label: 'id',
isActive: true,
type: FieldMetadataType.Uuid,
createdAt: '2024-01-01T00:00:00.000Z',
updatedAt: '2024-01-01T00:00:00.000Z',
},
],
},
record: { id: 'nodeId', __typename: 'Custom' },
recordIdentifier: {
id: 'nodeId',
name: '',
avatarUrl: '',
avatarType: 'rounded',
linkToShowPage: '/object/nameSingular/nodeId',
},
}, },
]; },
expect(result.current.multiObjects.selectedObjectRecords).toStrictEqual( ];
expectedData, const selectedObjectRecordsLoading = false;
);
expect( const selectedAndMatchesSearchFilterObjectRecords: ObjectRecordForSelect[] =
result.current.multiObjects.filteredSelectedObjectRecords, [];
).toStrictEqual(expectedData); const selectedAndMatchesSearchFilterObjectRecordsLoading = false;
expect(result.current.multiObjects.objectRecordsToSelect).toStrictEqual(
expectedData, const toSelectAndMatchesSearchFilterObjectRecords: ObjectRecordForSelect[] =
[];
const toSelectAndMatchesSearchFilterObjectRecordsLoading = false;
beforeEach(() => {
(useMultiObjectSearchSelectedItemsQuery as jest.Mock).mockReturnValue({
selectedObjectRecords,
selectedObjectRecordsLoading,
});
(
useMultiObjectSearchMatchesSearchFilterAndSelectedItemsQuery as jest.Mock
).mockReturnValue({
selectedAndMatchesSearchFilterObjectRecords,
selectedAndMatchesSearchFilterObjectRecordsLoading,
});
(
useMultiObjectSearchMatchesSearchFilterAndToSelectQuery as jest.Mock
).mockReturnValue({
toSelectAndMatchesSearchFilterObjectRecords,
toSelectAndMatchesSearchFilterObjectRecordsLoading,
});
});
afterEach(() => {
jest.resetAllMocks();
});
it('should return the correct object records and loading state', () => {
const { result } = renderHook(() =>
useMultiObjectSearch({
searchFilterValue,
selectedObjectRecordIds,
limit,
excludedObjectRecordIds,
excludedObjects,
}),
); );
const expected: MultiObjectSearch = {
selectedObjectRecords,
filteredSelectedObjectRecords:
selectedAndMatchesSearchFilterObjectRecords,
objectRecordsToSelect: toSelectAndMatchesSearchFilterObjectRecords,
loading:
selectedAndMatchesSearchFilterObjectRecordsLoading ||
toSelectAndMatchesSearchFilterObjectRecordsLoading ||
selectedObjectRecordsLoading,
};
expect(result.current).toEqual(expected);
}); });
}); });