Skip to content

Commit c7817cd

Browse files
committed
Add tests on utils
1 parent 8f79d6d commit c7817cd

14 files changed

+2179
-15
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { getAgentIdFromStep } from '../getAgentIdFromStep';
2+
3+
describe('getAgentIdFromStep', () => {
4+
it('should return undefined when stepDefinition is undefined', () => {
5+
const result = getAgentIdFromStep(undefined);
6+
7+
expect(result).toBeUndefined();
8+
});
9+
10+
it('should return undefined when stepDefinition type is trigger', () => {
11+
const result = getAgentIdFromStep({
12+
type: 'trigger',
13+
definition: { type: 'DATABASE_EVENT' },
14+
});
15+
16+
expect(result).toBeUndefined();
17+
});
18+
19+
it('should return undefined when definition is undefined', () => {
20+
const result = getAgentIdFromStep({
21+
type: 'action',
22+
definition: undefined,
23+
});
24+
25+
expect(result).toBeUndefined();
26+
});
27+
28+
it('should return undefined when definition type is not AI_AGENT', () => {
29+
const result = getAgentIdFromStep({
30+
type: 'action',
31+
definition: { type: 'CREATE_RECORD' },
32+
});
33+
34+
expect(result).toBeUndefined();
35+
});
36+
37+
it('should return undefined when settings is missing', () => {
38+
const result = getAgentIdFromStep({
39+
type: 'action',
40+
definition: { type: 'AI_AGENT' },
41+
});
42+
43+
expect(result).toBeUndefined();
44+
});
45+
46+
it('should return undefined when settings.input is missing', () => {
47+
const result = getAgentIdFromStep({
48+
type: 'action',
49+
definition: {
50+
type: 'AI_AGENT',
51+
settings: {},
52+
},
53+
});
54+
55+
expect(result).toBeUndefined();
56+
});
57+
58+
it('should return undefined when settings.input.agentId is missing', () => {
59+
const result = getAgentIdFromStep({
60+
type: 'action',
61+
definition: {
62+
type: 'AI_AGENT',
63+
settings: {
64+
input: {},
65+
},
66+
},
67+
});
68+
69+
expect(result).toBeUndefined();
70+
});
71+
72+
it('should return agentId when all conditions are met', () => {
73+
const result = getAgentIdFromStep({
74+
type: 'action',
75+
definition: {
76+
type: 'AI_AGENT',
77+
settings: {
78+
input: {
79+
agentId: 'agent-123',
80+
},
81+
},
82+
},
83+
});
84+
85+
expect(result).toBe('agent-123');
86+
});
87+
});

packages/twenty-front/src/modules/workflow/workflow-steps/hooks/__tests__/useUpdateStep.test.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useUpdateStep } from '@/workflow/workflow-steps/hooks/useUpdateStep';
22
import { act, renderHook } from '@testing-library/react';
3+
import { type WorkflowAction } from '~/generated/graphql';
34

45
const mockUpdateWorkflowVersionStep = jest.fn();
56
const mockGetUpdatableWorkflowVersion = jest.fn();
@@ -69,4 +70,69 @@ describe('useUpdateStep', () => {
6970
step: mockStep,
7071
});
7172
});
73+
74+
it('should mark step for recomputation after update', async () => {
75+
const mockWorkflowVersionId = 'version-123';
76+
const mockStep = {
77+
id: 'step-1',
78+
name: 'Create Record',
79+
valid: true,
80+
type: 'CREATE_RECORD' as const,
81+
settings: {
82+
input: {
83+
objectName: 'company',
84+
},
85+
errorHandlingOptions: {
86+
retryOnFailure: { value: false },
87+
continueOnFailure: { value: false },
88+
},
89+
},
90+
};
91+
92+
mockGetUpdatableWorkflowVersion.mockResolvedValue(mockWorkflowVersionId);
93+
94+
const { result } = renderHook(() => useUpdateStep());
95+
await act(async () => {
96+
await result.current.updateStep(mockStep as WorkflowAction);
97+
});
98+
99+
expect(mockMarkStepForRecomputation).toHaveBeenCalledWith({
100+
stepId: 'step-1',
101+
workflowVersionId: mockWorkflowVersionId,
102+
});
103+
});
104+
105+
it('should mark step for recomputation for all step types', async () => {
106+
const mockWorkflowVersionId = 'version-123';
107+
const stepTypes = ['CODE', 'HTTP_REQUEST', 'CREATE_RECORD', 'SEND_EMAIL'];
108+
109+
for (const stepType of stepTypes) {
110+
mockMarkStepForRecomputation.mockClear();
111+
mockGetUpdatableWorkflowVersion.mockResolvedValue(mockWorkflowVersionId);
112+
113+
const mockStep = {
114+
id: `step-${stepType}`,
115+
name: `${stepType} Step`,
116+
valid: true,
117+
type: stepType as any,
118+
settings: {
119+
input: {},
120+
errorHandlingOptions: {
121+
retryOnFailure: { value: false },
122+
continueOnFailure: { value: false },
123+
},
124+
},
125+
};
126+
127+
const { result } = renderHook(() => useUpdateStep());
128+
await act(async () => {
129+
await result.current.updateStep(mockStep as WorkflowAction);
130+
});
131+
132+
expect(mockMarkStepForRecomputation).toHaveBeenCalledWith({
133+
stepId: `step-${stepType}`,
134+
workflowVersionId: mockWorkflowVersionId,
135+
});
136+
}
137+
});
72138
});

packages/twenty-front/src/modules/workflow/workflow-trigger/hooks/__tests__/useUpdateWorkflowVersionTrigger.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,33 @@ describe('useUpdateWorkflowVersionTrigger', () => {
6161
},
6262
});
6363
});
64+
65+
it('marks for recomputation for all trigger types', async () => {
66+
const triggerTypes = ['DATABASE_EVENT', 'MANUAL', 'CRON', 'WEBHOOK'];
67+
68+
for (const triggerType of triggerTypes) {
69+
mockMarkStepForRecomputation.mockClear();
70+
mockGetUpdatableWorkflowVersion.mockResolvedValue('version-id');
71+
72+
const testTrigger: WorkflowTrigger = {
73+
name: `${triggerType} Trigger`,
74+
type: triggerType as any,
75+
settings: {
76+
outputSchema: {},
77+
},
78+
nextStepIds: [],
79+
};
80+
81+
const { result } = renderHook(() => useUpdateWorkflowVersionTrigger());
82+
83+
await act(async () => {
84+
await result.current.updateTrigger(testTrigger);
85+
});
86+
87+
expect(mockMarkStepForRecomputation).toHaveBeenCalledWith({
88+
stepId: TRIGGER_STEP_ID,
89+
workflowVersionId: 'version-id',
90+
});
91+
}
92+
});
6493
});

packages/twenty-front/src/modules/workflow/workflow-trigger/hooks/useUpdateWorkflowVersionTrigger.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
22
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
33
import { useGetUpdatableWorkflowVersionOrThrow } from '@/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow';
44
import {
5-
type WorkflowTrigger,
6-
type WorkflowVersion,
5+
type WorkflowTrigger,
6+
type WorkflowVersion,
77
} from '@/workflow/types/Workflow';
8+
89
import { useStepsOutputSchema } from '@/workflow/workflow-variables/hooks/useStepsOutputSchema';
910
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
1011

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
2+
import { COMMAND_MENU_DEFAULT_ICON } from '@/workflow/workflow-trigger/constants/CommandMenuDefaultIcon';
3+
import { getManualTriggerDefaultSettings } from '../getManualTriggerDefaultSettings';
4+
5+
const mockObjectMetadataItems: ObjectMetadataItem[] = [
6+
{
7+
id: 'company-id',
8+
nameSingular: 'company',
9+
namePlural: 'companies',
10+
labelSingular: 'Company',
11+
labelPlural: 'Companies',
12+
icon: 'IconBuilding',
13+
fields: [],
14+
createdAt: new Date(),
15+
} as unknown as ObjectMetadataItem,
16+
];
17+
18+
describe('getManualTriggerDefaultSettings', () => {
19+
describe('GLOBAL availability', () => {
20+
it('should return correct settings for GLOBAL type', () => {
21+
const result = getManualTriggerDefaultSettings({
22+
availabilityType: 'GLOBAL',
23+
activeNonSystemObjectMetadataItems: mockObjectMetadataItems,
24+
});
25+
26+
expect(result).toEqual({
27+
objectType: undefined,
28+
availability: {
29+
type: 'GLOBAL',
30+
locations: undefined,
31+
},
32+
outputSchema: {},
33+
icon: COMMAND_MENU_DEFAULT_ICON,
34+
isPinned: false,
35+
});
36+
});
37+
38+
it('should use custom icon when provided', () => {
39+
const result = getManualTriggerDefaultSettings({
40+
availabilityType: 'GLOBAL',
41+
activeNonSystemObjectMetadataItems: mockObjectMetadataItems,
42+
icon: 'IconCustom',
43+
});
44+
45+
expect(result.icon).toBe('IconCustom');
46+
});
47+
48+
it('should use isPinned when provided', () => {
49+
const result = getManualTriggerDefaultSettings({
50+
availabilityType: 'GLOBAL',
51+
activeNonSystemObjectMetadataItems: mockObjectMetadataItems,
52+
isPinned: true,
53+
});
54+
55+
expect(result.isPinned).toBe(true);
56+
});
57+
});
58+
59+
describe('SINGLE_RECORD availability', () => {
60+
it('should return correct settings for SINGLE_RECORD type', () => {
61+
const result = getManualTriggerDefaultSettings({
62+
availabilityType: 'SINGLE_RECORD',
63+
activeNonSystemObjectMetadataItems: mockObjectMetadataItems,
64+
});
65+
66+
expect(result).toEqual({
67+
objectType: 'company',
68+
availability: {
69+
type: 'SINGLE_RECORD',
70+
objectNameSingular: 'company',
71+
},
72+
outputSchema: {},
73+
icon: COMMAND_MENU_DEFAULT_ICON,
74+
isPinned: false,
75+
});
76+
});
77+
78+
it('should use the first object metadata item', () => {
79+
const multipleObjects: ObjectMetadataItem[] = [
80+
...mockObjectMetadataItems,
81+
{
82+
id: 'person-id',
83+
nameSingular: 'person',
84+
namePlural: 'people',
85+
labelSingular: 'Person',
86+
labelPlural: 'People',
87+
icon: 'IconUser',
88+
fields: [],
89+
} as unknown as ObjectMetadataItem,
90+
];
91+
92+
const result = getManualTriggerDefaultSettings({
93+
availabilityType: 'SINGLE_RECORD',
94+
activeNonSystemObjectMetadataItems: multipleObjects,
95+
});
96+
97+
expect(result.objectType).toBe('company');
98+
expect(
99+
(result.availability as { objectNameSingular: string })
100+
.objectNameSingular,
101+
).toBe('company');
102+
});
103+
});
104+
105+
describe('BULK_RECORDS availability', () => {
106+
it('should return correct settings for BULK_RECORDS type', () => {
107+
const result = getManualTriggerDefaultSettings({
108+
availabilityType: 'BULK_RECORDS',
109+
activeNonSystemObjectMetadataItems: mockObjectMetadataItems,
110+
});
111+
112+
expect(result).toEqual({
113+
objectType: 'company',
114+
availability: {
115+
type: 'BULK_RECORDS',
116+
objectNameSingular: 'company',
117+
},
118+
outputSchema: {},
119+
icon: COMMAND_MENU_DEFAULT_ICON,
120+
isPinned: false,
121+
});
122+
});
123+
});
124+
});

0 commit comments

Comments
 (0)