Fix all broken CIs (#7439)

Fix all the broken CIs :p

This includes an ongoing effort to simplify test maintenance by having 1
unique source of truth about metadata and data mocks (that will later be
generated from a unique source of seeds: dev = demo = test)

Regressions:
- Unit line coverage: 60 > 55
- Storybook Pages branch coverage: 40 > 35
We will need to write tests to increase those coverage
- RelationFieldDisplay perf: 0.2ms to 0.22ms > We might have a
regression here
- Removed perf story about RawJSON > We will need to re-add it
This commit is contained in:
Charles Bochet
2024-10-05 00:22:38 +02:00
committed by Charles Bochet
parent bd305c8432
commit d8c4af9279
148 changed files with 4357 additions and 2536 deletions

View File

@ -1,56 +1,410 @@
import { MockedProvider } from '@apollo/client/testing';
import { renderHook } from '@testing-library/react';
import { RecoilRoot } from 'recoil';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
import { renderHook, waitFor } from '@testing-library/react';
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
import gql from 'graphql-tag';
import { generateEmptyJestRecordNode } from '~/testing/jest/generateEmptyJestRecordNode';
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
import { useRightDrawerEmailThread } from '../useRightDrawerEmailThread';
jest.mock('@/object-record/hooks/useFindOneRecord', () => ({
__esModule: true,
useFindOneRecord: jest.fn(),
}));
const mocks = [
{
request: {
query: gql`
query FindOneMessageThread($objectRecordId: ID!) {
messageThread(filter: { id: { eq: $objectRecordId } }) {
__typename
id
}
}
`,
variables: { objectRecordId: '1' },
},
result: jest.fn(() => ({
data: {
messageThread: {
id: '1',
__typename: 'MessageThread',
},
},
})),
},
{
request: {
query: gql`
query FindManyMessages(
$filter: MessageFilterInput
$orderBy: [MessageOrderByInput]
$lastCursor: String
$limit: Int
) {
messages(
filter: $filter
orderBy: $orderBy
first: $limit
after: $lastCursor
) {
edges {
node {
__typename
createdAt
headerMessageId
id
messageParticipants {
edges {
node {
__typename
displayName
handle
id
person {
__typename
avatarUrl
city
companyId
createdAt
createdBy {
source
workspaceMemberId
name
}
deletedAt
emails {
primaryEmail
additionalEmails
}
id
intro
jobTitle
linkedinLink {
primaryLinkUrl
primaryLinkLabel
secondaryLinks
}
name {
firstName
lastName
}
performanceRating
phones {
primaryPhoneNumber
primaryPhoneCountryCode
additionalPhones
}
position
updatedAt
whatsapp {
primaryPhoneNumber
primaryPhoneCountryCode
additionalPhones
}
workPreference
xLink {
primaryLinkUrl
primaryLinkLabel
secondaryLinks
}
}
role
workspaceMember {
__typename
avatarUrl
colorScheme
createdAt
dateFormat
deletedAt
id
locale
name {
firstName
lastName
}
timeFormat
timeZone
updatedAt
userEmail
userId
}
}
}
}
messageThread {
__typename
id
}
receivedAt
subject
text
}
cursor
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
totalCount
}
}
`,
variables: {
filter: { messageThreadId: { eq: '1' } },
orderBy: [{ receivedAt: 'AscNullsLast' }],
lastCursor: undefined,
limit: 10,
},
},
result: jest.fn(() => ({
data: {
messages: {
edges: [
{
node: generateEmptyJestRecordNode({
objectNameSingular: 'message',
input: {
id: '1',
text: 'Message 1',
createdAt: '2024-10-03T10:20:10.145Z',
},
}),
cursor: '1',
},
{
node: generateEmptyJestRecordNode({
objectNameSingular: 'message',
input: {
id: '2',
text: 'Message 2',
createdAt: '2024-10-03T10:20:10.145Z',
},
}),
cursor: '2',
},
],
totalCount: 2,
pageInfo: {
hasNextPage: false,
hasPreviousPage: false,
startCursor: '1',
endCursor: '2',
},
},
},
})),
},
{
request: {
query: gql`
query FindManyMessageParticipants(
$filter: MessageParticipantFilterInput
$orderBy: [MessageParticipantOrderByInput]
$lastCursor: String
$limit: Int
) {
messageParticipants(
filter: $filter
orderBy: $orderBy
first: $limit
after: $lastCursor
) {
edges {
node {
__typename
displayName
handle
id
messageId
person {
__typename
avatarUrl
city
companyId
createdAt
createdBy {
source
workspaceMemberId
name
}
deletedAt
emails {
primaryEmail
additionalEmails
}
id
intro
jobTitle
linkedinLink {
primaryLinkUrl
primaryLinkLabel
secondaryLinks
}
name {
firstName
lastName
}
performanceRating
phones {
primaryPhoneNumber
primaryPhoneCountryCode
additionalPhones
}
position
updatedAt
whatsapp {
primaryPhoneNumber
primaryPhoneCountryCode
additionalPhones
}
workPreference
xLink {
primaryLinkUrl
primaryLinkLabel
secondaryLinks
}
}
role
workspaceMember {
__typename
avatarUrl
colorScheme
createdAt
dateFormat
deletedAt
id
locale
name {
firstName
lastName
}
timeFormat
timeZone
updatedAt
userEmail
userId
}
}
cursor
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
totalCount
}
}
`,
variables: {
filter: { messageId: { in: ['1', '2'] }, role: { eq: 'from' } },
orderBy: undefined,
lastCursor: undefined,
limit: undefined,
},
},
result: jest.fn(() => ({
data: {
messageParticipants: {
edges: [
{
node: generateEmptyJestRecordNode({
objectNameSingular: 'messageParticipant',
input: {
id: 'messageParticipant-1',
role: 'from',
messageId: '1',
},
}),
cursor: '1',
},
{
node: generateEmptyJestRecordNode({
objectNameSingular: 'messageParticipant',
input: {
id: 'messageParticipant-2',
role: 'from',
messageId: '2',
},
}),
cursor: '2',
},
],
totalCount: 2,
pageInfo: {
hasNextPage: false,
hasPreviousPage: false,
startCursor: '1',
endCursor: '2',
},
},
},
})),
},
];
jest.mock('@/object-record/hooks/useFindManyRecords', () => ({
__esModule: true,
useFindManyRecords: jest.fn(),
}));
const Wrapper = getJestMetadataAndApolloMocksWrapper({
apolloMocks: mocks,
onInitializeRecoilSnapshot: ({ set }) => {
set(viewableRecordIdState, '1');
},
});
describe('useRightDrawerEmailThread', () => {
it('should return correct values', async () => {
const mockThread = { id: '1' };
const mockMessages = [
{ id: '1', text: 'Message 1' },
{ id: '2', text: 'Message 2' },
{
__typename: 'Message',
createdAt: '2024-10-03T10:20:10.145Z',
headerMessageId: '',
id: '1',
messageParticipants: [],
messageThread: null,
receivedAt: null,
sender: {
__typename: 'MessageParticipant',
displayName: '',
handle: '',
id: 'messageParticipant-1',
messageId: '1',
person: null,
role: 'from',
workspaceMember: null,
},
subject: '',
text: 'Message 1',
},
{
__typename: 'Message',
createdAt: '2024-10-03T10:20:10.145Z',
headerMessageId: '',
id: '2',
messageParticipants: [],
messageThread: null,
receivedAt: null,
sender: {
__typename: 'MessageParticipant',
displayName: '',
handle: '',
id: 'messageParticipant-2',
messageId: '2',
person: null,
role: 'from',
workspaceMember: null,
},
subject: '',
text: 'Message 2',
},
];
const mockFetchMoreRecords = jest.fn();
(useFindOneRecord as jest.Mock).mockReturnValue({
record: mockThread,
loading: false,
fetchMoreRecords: mockFetchMoreRecords,
});
(useFindManyRecords as jest.Mock).mockReturnValue({
records: mockMessages,
loading: false,
fetchMoreRecords: mockFetchMoreRecords,
});
const { result } = renderHook(() => useRightDrawerEmailThread(), {
wrapper: ({ children }) => (
<MockedProvider mocks={[]} addTypename={false}>
<RecoilRoot>{children}</RecoilRoot>
</MockedProvider>
),
wrapper: Wrapper,
});
expect(result.current.thread).toBeDefined();
expect(result.current.messages).toEqual(mockMessages);
expect(result.current.threadLoading).toBeFalsy();
expect(result.current.fetchMoreMessages).toBeInstanceOf(Function);
await waitFor(() => {
expect(result.current.thread).toBeDefined();
expect(result.current.messages).toEqual(mockMessages);
expect(result.current.threadLoading).toBeFalsy();
expect(result.current.fetchMoreMessages).toBeInstanceOf(Function);
});
});
});

View File

@ -9,7 +9,7 @@ import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMembe
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
import { mockWorkspaceMembers } from '~/testing/mock-data/workspace-members';
const cache = new InMemoryCache();

View File

@ -1,13 +1,11 @@
import { MockedProvider, MockedResponse } from '@apollo/client/testing';
import { MockedResponse } from '@apollo/client/testing';
import { act, renderHook } from '@testing-library/react';
import gql from 'graphql-tag';
import pick from 'lodash.pick';
import { ReactNode } from 'react';
import { RecoilRoot } from 'recoil';
import { useCreateActivityInDB } from '@/activities/hooks/useCreateActivityInDB';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
import { mockedTasks } from '~/testing/mock-data/tasks';
const mockedDate = '2024-03-15T12:00:00.000Z';
@ -26,14 +24,44 @@ const mocks: MockedResponse[] = [
mutation CreateOneTask($input: TaskCreateInput!) {
createTask(data: $input) {
__typename
updatedAt
assignee {
__typename
id
name {
firstName
lastName
}
}
assigneeId
attachments {
edges {
node {
__typename
activityId
authorId
companyId
createdAt
deletedAt
fullPath
id
name
noteId
opportunityId
personId
rocketId
taskId
type
updatedAt
}
}
}
body
createdAt
dueAt
id
status
body
assigneeId
title
updatedAt
}
}
`,
@ -56,15 +84,9 @@ const mocks: MockedResponse[] = [
},
];
const Wrapper = ({ children }: { children: ReactNode }) => (
<RecoilRoot>
<MockedProvider mocks={mocks} addTypename={false}>
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
{children}
</SnackBarProviderScope>
</MockedProvider>
</RecoilRoot>
);
const Wrapper = getJestMetadataAndApolloMocksWrapper({
apolloMocks: mocks,
});
describe('useCreateActivityInDB', () => {
it('Should create activity in DB', async () => {

View File

@ -1,7 +1,6 @@
import { MockedProvider, MockedResponse } from '@apollo/client/testing';
import { MockedResponse } from '@apollo/client/testing';
import { act, renderHook } from '@testing-library/react';
import { ReactNode } from 'react';
import { RecoilRoot, useRecoilValue, useSetRecoilState } from 'recoil';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
@ -9,7 +8,8 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
import gql from 'graphql-tag';
import pick from 'lodash.pick';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
import { mockedTasks } from '~/testing/mock-data/tasks';
const mockedDate = '2024-03-15T12:00:00.000Z';
@ -61,13 +61,9 @@ const mocks: MockedResponse[] = [
},
];
const Wrapper = ({ children }: { children: ReactNode }) => (
<RecoilRoot>
<MockedProvider addTypename={false} mocks={mocks}>
{children}
</MockedProvider>
</RecoilRoot>
);
const Wrapper = getJestMetadataAndApolloMocksWrapper({
apolloMocks: mocks,
});
const mockObjectMetadataItems = generatedMockObjectMetadataItems;

View File

@ -42,5 +42,8 @@ export const WithTasks: Story = {
},
parameters: {
msw: graphqlMocks,
container: {
width: '500px',
},
},
};

View File

@ -3,6 +3,7 @@ import { ComponentDecorator } from 'twenty-ui';
import { TaskList } from '@/activities/tasks/components/TaskList';
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks';
import { mockedTasks } from '~/testing/mock-data/tasks';
@ -10,13 +11,21 @@ import { mockedTasks } from '~/testing/mock-data/tasks';
const meta: Meta<typeof TaskList> = {
title: 'Modules/Activity/TaskList',
component: TaskList,
decorators: [MemoryRouterDecorator, ComponentDecorator, SnackBarDecorator],
decorators: [
ComponentDecorator,
MemoryRouterDecorator,
ObjectMetadataItemsDecorator,
SnackBarDecorator,
],
args: {
title: 'Tasks',
tasks: mockedTasks,
},
parameters: {
msw: graphqlMocks,
container: {
width: '500px',
},
},
};

View File

@ -27,6 +27,7 @@ import { TaskList } from './TaskList';
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
width: 100%;
`;
type TaskGroupsProps = {

View File

@ -12,6 +12,7 @@ type TaskListProps = {
const StyledContainer = styled.div`
align-items: flex-start;
width: 100%;
align-self: stretch;
display: flex;
flex-direction: column;

View File

@ -1,11 +1,10 @@
import { MockedProvider, MockedResponse } from '@apollo/client/testing';
import { MockedResponse } from '@apollo/client/testing';
import { act, renderHook } from '@testing-library/react';
import gql from 'graphql-tag';
import { ReactNode } from 'react';
import { RecoilRoot } from 'recoil';
import { useCompleteTask } from '@/activities/tasks/hooks/useCompleteTask';
import { Task } from '@/activities/types/Task';
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
const task: Task = {
id: '123',
@ -28,21 +27,123 @@ const mocks: MockedResponse[] = [
mutation UpdateOneTask($idToUpdate: ID!, $input: TaskUpdateInput!) {
updateTask(id: $idToUpdate, data: $input) {
__typename
updatedAt
createdAt
deletedAt
dueAt
id
status
assignee {
__typename
avatarUrl
colorScheme
createdAt
dateFormat
deletedAt
id
locale
name {
firstName
lastName
}
timeFormat
timeZone
updatedAt
userEmail
userId
}
assigneeId
attachments {
edges {
node {
__typename
activityId
authorId
companyId
createdAt
deletedAt
fullPath
id
name
noteId
opportunityId
personId
rocketId
taskId
type
updatedAt
}
}
}
body
createdAt
createdBy {
source
workspaceMemberId
name
}
assigneeId
deletedAt
dueAt
favorites {
edges {
node {
__typename
companyId
createdAt
deletedAt
id
noteId
opportunityId
personId
position
rocketId
taskId
updatedAt
viewId
workflowId
workspaceMemberId
}
}
}
id
position
status
taskTargets {
edges {
node {
__typename
companyId
createdAt
deletedAt
id
opportunityId
personId
rocketId
taskId
updatedAt
}
}
}
timelineActivities {
edges {
node {
__typename
companyId
createdAt
deletedAt
happensAt
id
linkedObjectMetadataId
linkedRecordCachedName
linkedRecordId
name
noteId
opportunityId
personId
properties
rocketId
taskId
updatedAt
workspaceMemberId
}
}
}
title
updatedAt
}
}
`,
@ -72,13 +173,9 @@ const mocks: MockedResponse[] = [
},
];
const Wrapper = ({ children }: { children: ReactNode }) => (
<RecoilRoot>
<MockedProvider mocks={mocks} addTypename={false}>
{children}
</MockedProvider>
</RecoilRoot>
);
const Wrapper = getJestMetadataAndApolloMocksWrapper({
apolloMocks: mocks,
});
describe('useCompleteTask', () => {
it('should complete task', async () => {

View File

@ -1,21 +1,16 @@
import { renderHook } from '@testing-library/react';
import { useTimelineActivities } from '@/activities/timelineActivities/hooks/useTimelineActivities';
import { ReactNode } from 'react';
import { getJestHookWrapper } from '~/testing/jest/getJestHookWrapper';
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
jest.mock('@/object-record/hooks/useFindManyRecords', () => ({
useFindManyRecords: jest.fn(),
}));
const Wrappers = getJestHookWrapper({
const Wrapper = getJestMetadataAndApolloMocksWrapper({
apolloMocks: [],
});
const Wrapper = ({ children }: { children: ReactNode }) => (
<Wrappers>{children}</Wrappers>
);
describe('useTimelineActivities', () => {
afterEach(() => {
jest.clearAllMocks();

View File

@ -5,7 +5,7 @@ import { EventRowMainObjectUpdated } from '@/activities/timelineActivities/rows/
import { TimelineActivity } from '@/activities/timelineActivities/types/TimelineActivity';
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { mockedPersonObjectMetadataItem } from '~/testing/mock-data/metadata';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
const meta: Meta<typeof EventRowMainObjectUpdated> = {
title: 'Modules/TimelineActivities/Rows/MainObject/EventRowMainObjectUpdated',
@ -35,7 +35,9 @@ const meta: Meta<typeof EventRowMainObjectUpdated> = {
},
},
} as TimelineActivity,
mainObjectMetadataItem: mockedPersonObjectMetadataItem,
mainObjectMetadataItem: generatedMockObjectMetadataItems.find(
(item) => item.namePlural === 'person',
),
},
decorators: [
ComponentDecorator,