Skip to content

Commit 12b1d60

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

File tree

9 files changed

+1356
-15
lines changed

9 files changed

+1356
-15
lines changed

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: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
2+
import {
3+
computeStepOutputSchema,
4+
shouldComputeOutputSchemaOnFrontend,
5+
} from '@/workflow/workflow-variables/utils/generate/computeStepOutputSchema';
6+
import { FieldMetadataType } from 'twenty-shared/types';
7+
8+
const mockCompanyObjectMetadataItem: ObjectMetadataItem = {
9+
id: 'company-metadata-id',
10+
nameSingular: 'company',
11+
namePlural: 'companies',
12+
labelSingular: 'Company',
13+
labelPlural: 'Companies',
14+
icon: 'IconBuildingSkyscraper',
15+
fields: [
16+
{
17+
id: 'name-field-id',
18+
name: 'name',
19+
label: 'Name',
20+
type: FieldMetadataType.TEXT,
21+
isActive: true,
22+
isSystem: false,
23+
},
24+
],
25+
} as ObjectMetadataItem;
26+
27+
describe('computeStepOutputSchema', () => {
28+
describe('PERSISTED_OUTPUT_SCHEMA_TYPES', () => {
29+
it('should return undefined for CODE step type', () => {
30+
const result = computeStepOutputSchema({
31+
step: { type: 'CODE', settings: {} } as any,
32+
objectMetadataItems: [],
33+
});
34+
35+
expect(result).toBeUndefined();
36+
});
37+
38+
it('should return undefined for HTTP_REQUEST step type', () => {
39+
const result = computeStepOutputSchema({
40+
step: { type: 'HTTP_REQUEST', settings: {} } as any,
41+
objectMetadataItems: [],
42+
});
43+
44+
expect(result).toBeUndefined();
45+
});
46+
47+
it('should return undefined for AI_AGENT step type', () => {
48+
const result = computeStepOutputSchema({
49+
step: { type: 'AI_AGENT', settings: {} } as any,
50+
objectMetadataItems: [],
51+
});
52+
53+
expect(result).toBeUndefined();
54+
});
55+
56+
it('should return undefined for WEBHOOK step type', () => {
57+
const result = computeStepOutputSchema({
58+
step: { type: 'WEBHOOK', settings: {} } as any,
59+
objectMetadataItems: [],
60+
});
61+
62+
expect(result).toBeUndefined();
63+
});
64+
65+
it('should return undefined for ITERATOR step type', () => {
66+
const result = computeStepOutputSchema({
67+
step: { type: 'ITERATOR', settings: {} } as any,
68+
objectMetadataItems: [],
69+
});
70+
71+
expect(result).toBeUndefined();
72+
});
73+
});
74+
75+
describe('DATABASE_EVENT trigger', () => {
76+
it('should return empty object when eventName is not defined', () => {
77+
const result = computeStepOutputSchema({
78+
step: { type: 'DATABASE_EVENT', settings: {} } as any,
79+
objectMetadataItems: [mockCompanyObjectMetadataItem],
80+
});
81+
82+
expect(result).toEqual({});
83+
});
84+
85+
it('should return empty object when eventName cannot be parsed', () => {
86+
const result = computeStepOutputSchema({
87+
step: {
88+
type: 'DATABASE_EVENT',
89+
settings: { eventName: 'invalid' },
90+
} as any,
91+
objectMetadataItems: [mockCompanyObjectMetadataItem],
92+
});
93+
94+
expect(result).toEqual({});
95+
});
96+
97+
it('should return empty object when object metadata is not found', () => {
98+
const result = computeStepOutputSchema({
99+
step: {
100+
type: 'DATABASE_EVENT',
101+
settings: { eventName: 'unknownObject.created' },
102+
} as any,
103+
objectMetadataItems: [mockCompanyObjectMetadataItem],
104+
});
105+
106+
expect(result).toEqual({});
107+
});
108+
109+
it('should return record event output schema for valid eventName', () => {
110+
const result = computeStepOutputSchema({
111+
step: {
112+
type: 'DATABASE_EVENT',
113+
settings: { eventName: 'company.created' },
114+
} as any,
115+
objectMetadataItems: [mockCompanyObjectMetadataItem],
116+
});
117+
118+
expect(result).toHaveProperty('_outputSchemaType', 'RECORD');
119+
expect(result).toHaveProperty('object');
120+
expect(result).toHaveProperty('fields');
121+
});
122+
});
123+
124+
describe('MANUAL trigger', () => {
125+
it('should return empty object when availability is not defined', () => {
126+
const result = computeStepOutputSchema({
127+
step: { type: 'MANUAL', settings: {} } as any,
128+
objectMetadataItems: [mockCompanyObjectMetadataItem],
129+
});
130+
131+
expect(result).toEqual({});
132+
});
133+
134+
it('should return empty object for GLOBAL availability', () => {
135+
const result = computeStepOutputSchema({
136+
step: {
137+
type: 'MANUAL',
138+
settings: { availability: { type: 'GLOBAL' } },
139+
} as any,
140+
objectMetadataItems: [mockCompanyObjectMetadataItem],
141+
});
142+
143+
expect(result).toEqual({});
144+
});
145+
146+
it('should return record output schema for SINGLE_RECORD availability', () => {
147+
const result = computeStepOutputSchema({
148+
step: {
149+
type: 'MANUAL',
150+
settings: {
151+
availability: {
152+
type: 'SINGLE_RECORD',
153+
objectNameSingular: 'company',
154+
},
155+
},
156+
} as any,
157+
objectMetadataItems: [mockCompanyObjectMetadataItem],
158+
});
159+
160+
expect(result).toHaveProperty('_outputSchemaType', 'RECORD');
161+
expect(result).toHaveProperty('object');
162+
});
163+
164+
it('should return array indicator for BULK_RECORDS availability', () => {
165+
const result = computeStepOutputSchema({
166+
step: {
167+
type: 'MANUAL',
168+
settings: {
169+
availability: {
170+
type: 'BULK_RECORDS',
171+
objectNameSingular: 'company',
172+
},
173+
},
174+
} as any,
175+
objectMetadataItems: [mockCompanyObjectMetadataItem],
176+
});
177+
178+
expect(result).toHaveProperty('companies');
179+
expect((result as any).companies).toMatchObject({
180+
isLeaf: true,
181+
label: 'Companies',
182+
type: 'array',
183+
});
184+
});
185+
});
186+
187+
describe('CRON trigger', () => {
188+
it('should return empty object', () => {
189+
const result = computeStepOutputSchema({
190+
step: { type: 'CRON', settings: {} } as any,
191+
objectMetadataItems: [],
192+
});
193+
194+
expect(result).toEqual({});
195+
});
196+
});
197+
198+
describe('Record action steps', () => {
199+
it.each([
200+
'CREATE_RECORD',
201+
'UPDATE_RECORD',
202+
'DELETE_RECORD',
203+
'UPSERT_RECORD',
204+
])(
205+
'should return record output schema for %s with valid objectName',
206+
(stepType) => {
207+
const result = computeStepOutputSchema({
208+
step: {
209+
type: stepType,
210+
settings: { input: { objectName: 'company' } },
211+
} as any,
212+
objectMetadataItems: [mockCompanyObjectMetadataItem],
213+
});
214+
215+
expect(result).toHaveProperty('_outputSchemaType', 'RECORD');
216+
expect(result).toHaveProperty('object');
217+
},
218+
);
219+
});
220+
221+
describe('FIND_RECORDS step', () => {
222+
it('should return empty object when objectName is not defined', () => {
223+
const result = computeStepOutputSchema({
224+
step: { type: 'FIND_RECORDS', settings: { input: {} } } as any,
225+
objectMetadataItems: [mockCompanyObjectMetadataItem],
226+
});
227+
228+
expect(result).toEqual({});
229+
});
230+
231+
it('should return find records output schema with valid objectName', () => {
232+
const result = computeStepOutputSchema({
233+
step: {
234+
type: 'FIND_RECORDS',
235+
settings: { input: { objectName: 'company' } },
236+
} as any,
237+
objectMetadataItems: [mockCompanyObjectMetadataItem],
238+
});
239+
240+
expect(result).toHaveProperty('first');
241+
expect(result).toHaveProperty('all');
242+
expect(result).toHaveProperty('totalCount');
243+
});
244+
});
245+
246+
describe('SEND_EMAIL step', () => {
247+
it('should return success boolean schema', () => {
248+
const result = computeStepOutputSchema({
249+
step: { type: 'SEND_EMAIL', settings: {} } as any,
250+
objectMetadataItems: [],
251+
});
252+
253+
expect(result).toEqual({
254+
success: {
255+
isLeaf: true,
256+
type: FieldMetadataType.BOOLEAN,
257+
label: 'Success',
258+
value: true,
259+
},
260+
});
261+
});
262+
});
263+
264+
describe('Empty output schema steps', () => {
265+
it.each(['FILTER', 'DELAY', 'EMPTY'])(
266+
'should return empty object for %s step type',
267+
(stepType) => {
268+
const result = computeStepOutputSchema({
269+
step: { type: stepType, settings: {} } as any,
270+
objectMetadataItems: [],
271+
});
272+
273+
expect(result).toEqual({});
274+
},
275+
);
276+
});
277+
278+
describe('Unknown step type', () => {
279+
it('should return empty object for unknown step type', () => {
280+
const result = computeStepOutputSchema({
281+
step: { type: 'UNKNOWN_TYPE', settings: {} } as any,
282+
objectMetadataItems: [],
283+
});
284+
285+
expect(result).toEqual({});
286+
});
287+
});
288+
});
289+
290+
describe('shouldComputeOutputSchemaOnFrontend', () => {
291+
it('should return false for CODE', () => {
292+
expect(shouldComputeOutputSchemaOnFrontend('CODE')).toBe(false);
293+
});
294+
295+
it('should return false for HTTP_REQUEST', () => {
296+
expect(shouldComputeOutputSchemaOnFrontend('HTTP_REQUEST')).toBe(false);
297+
});
298+
299+
it('should return false for AI_AGENT', () => {
300+
expect(shouldComputeOutputSchemaOnFrontend('AI_AGENT')).toBe(false);
301+
});
302+
303+
it('should return false for WEBHOOK', () => {
304+
expect(shouldComputeOutputSchemaOnFrontend('WEBHOOK')).toBe(false);
305+
});
306+
307+
it('should return false for ITERATOR', () => {
308+
expect(shouldComputeOutputSchemaOnFrontend('ITERATOR')).toBe(false);
309+
});
310+
311+
it('should return true for DATABASE_EVENT', () => {
312+
expect(shouldComputeOutputSchemaOnFrontend('DATABASE_EVENT')).toBe(true);
313+
});
314+
315+
it('should return true for CREATE_RECORD', () => {
316+
expect(shouldComputeOutputSchemaOnFrontend('CREATE_RECORD')).toBe(true);
317+
});
318+
319+
it('should return true for FIND_RECORDS', () => {
320+
expect(shouldComputeOutputSchemaOnFrontend('FIND_RECORDS')).toBe(true);
321+
});
322+
323+
it('should return true for SEND_EMAIL', () => {
324+
expect(shouldComputeOutputSchemaOnFrontend('SEND_EMAIL')).toBe(true);
325+
});
326+
});

0 commit comments

Comments
 (0)