Skip to content

Commit a40ceb4

Browse files
committed
added tests
1 parent ad149c3 commit a40ceb4

3 files changed

Lines changed: 607 additions & 20 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,388 @@
1+
import { beforeEach, describe, expect, it, vi } from 'vitest'
2+
3+
// Mock hooks
4+
const mockCollaborativeUpdates = {
5+
collaborativeUpdateLoopType: vi.fn(),
6+
collaborativeUpdateParallelType: vi.fn(),
7+
collaborativeUpdateIterationCount: vi.fn(),
8+
collaborativeUpdateIterationCollection: vi.fn(),
9+
}
10+
11+
const mockStoreData = {
12+
loops: {},
13+
parallels: {},
14+
}
15+
16+
vi.mock('@/hooks/use-collaborative-workflow', () => ({
17+
useCollaborativeWorkflow: () => mockCollaborativeUpdates,
18+
}))
19+
20+
vi.mock('@/stores/workflows/workflow/store', () => ({
21+
useWorkflowStore: () => mockStoreData,
22+
}))
23+
24+
vi.mock('@/components/ui/badge', () => ({
25+
Badge: ({ children, ...props }: any) => (
26+
<div data-testid='badge' {...props}>
27+
{children}
28+
</div>
29+
),
30+
}))
31+
32+
vi.mock('@/components/ui/input', () => ({
33+
Input: (props: any) => <input data-testid='input' {...props} />,
34+
}))
35+
36+
vi.mock('@/components/ui/popover', () => ({
37+
Popover: ({ children }: any) => <div data-testid='popover'>{children}</div>,
38+
PopoverContent: ({ children }: any) => <div data-testid='popover-content'>{children}</div>,
39+
PopoverTrigger: ({ children }: any) => <div data-testid='popover-trigger'>{children}</div>,
40+
}))
41+
42+
vi.mock('@/components/ui/tag-dropdown', () => ({
43+
checkTagTrigger: vi.fn(() => ({ show: false })),
44+
TagDropdown: ({ children }: any) => <div data-testid='tag-dropdown'>{children}</div>,
45+
}))
46+
47+
vi.mock('react-simple-code-editor', () => ({
48+
default: (props: any) => <textarea data-testid='code-editor' {...props} />,
49+
}))
50+
51+
describe('IterationBadges', () => {
52+
const defaultProps = {
53+
nodeId: 'test-node-1',
54+
data: {
55+
width: 500,
56+
height: 300,
57+
isPreview: false,
58+
},
59+
iterationType: 'loop' as const,
60+
}
61+
62+
beforeEach(() => {
63+
vi.clearAllMocks()
64+
mockStoreData.loops = {}
65+
mockStoreData.parallels = {}
66+
})
67+
68+
describe('Component Interface', () => {
69+
it.concurrent('should accept required props', () => {
70+
expect(defaultProps.nodeId).toBeDefined()
71+
expect(defaultProps.data).toBeDefined()
72+
expect(defaultProps.iterationType).toBeDefined()
73+
})
74+
75+
it.concurrent('should handle loop iteration type prop', () => {
76+
const loopProps = { ...defaultProps, iterationType: 'loop' as const }
77+
expect(loopProps.iterationType).toBe('loop')
78+
})
79+
80+
it.concurrent('should handle parallel iteration type prop', () => {
81+
const parallelProps = { ...defaultProps, iterationType: 'parallel' as const }
82+
expect(parallelProps.iterationType).toBe('parallel')
83+
})
84+
})
85+
86+
describe('Configuration System', () => {
87+
it.concurrent('should use correct config for loop type', () => {
88+
const CONFIG = {
89+
loop: {
90+
typeLabels: { for: 'For Loop', forEach: 'For Each' },
91+
typeKey: 'loopType' as const,
92+
storeKey: 'loops' as const,
93+
maxIterations: 100,
94+
configKeys: {
95+
iterations: 'iterations' as const,
96+
items: 'forEachItems' as const,
97+
},
98+
},
99+
}
100+
101+
expect(CONFIG.loop.typeLabels.for).toBe('For Loop')
102+
expect(CONFIG.loop.typeLabels.forEach).toBe('For Each')
103+
expect(CONFIG.loop.maxIterations).toBe(100)
104+
expect(CONFIG.loop.storeKey).toBe('loops')
105+
})
106+
107+
it.concurrent('should use correct config for parallel type', () => {
108+
const CONFIG = {
109+
parallel: {
110+
typeLabels: { count: 'Parallel Count', collection: 'Parallel Each' },
111+
typeKey: 'parallelType' as const,
112+
storeKey: 'parallels' as const,
113+
maxIterations: 20,
114+
configKeys: {
115+
iterations: 'count' as const,
116+
items: 'distribution' as const,
117+
},
118+
},
119+
}
120+
121+
expect(CONFIG.parallel.typeLabels.count).toBe('Parallel Count')
122+
expect(CONFIG.parallel.typeLabels.collection).toBe('Parallel Each')
123+
expect(CONFIG.parallel.maxIterations).toBe(20)
124+
expect(CONFIG.parallel.storeKey).toBe('parallels')
125+
})
126+
})
127+
128+
describe('Type Determination Logic', () => {
129+
it.concurrent('should default to "for" for loop type', () => {
130+
type IterationType = 'loop' | 'parallel'
131+
const determineDefaultType = (iterationType: IterationType) => {
132+
return iterationType === 'loop' ? 'for' : 'count'
133+
}
134+
135+
const currentType = determineDefaultType('loop')
136+
expect(currentType).toBe('for')
137+
})
138+
139+
it.concurrent('should default to "count" for parallel type', () => {
140+
type IterationType = 'loop' | 'parallel'
141+
const determineDefaultType = (iterationType: IterationType) => {
142+
return iterationType === 'loop' ? 'for' : 'count'
143+
}
144+
145+
const currentType = determineDefaultType('parallel')
146+
expect(currentType).toBe('count')
147+
})
148+
149+
it.concurrent('should use explicit loopType when provided', () => {
150+
type IterationType = 'loop' | 'parallel'
151+
const determineType = (explicitType: string | undefined, iterationType: IterationType) => {
152+
return explicitType || (iterationType === 'loop' ? 'for' : 'count')
153+
}
154+
155+
const currentType = determineType('forEach', 'loop')
156+
expect(currentType).toBe('forEach')
157+
})
158+
159+
it.concurrent('should use explicit parallelType when provided', () => {
160+
type IterationType = 'loop' | 'parallel'
161+
const determineType = (explicitType: string | undefined, iterationType: IterationType) => {
162+
return explicitType || (iterationType === 'loop' ? 'for' : 'count')
163+
}
164+
165+
const currentType = determineType('collection', 'parallel')
166+
expect(currentType).toBe('collection')
167+
})
168+
})
169+
170+
describe('Count Mode Detection', () => {
171+
it.concurrent('should be in count mode for loop + for combination', () => {
172+
type IterationType = 'loop' | 'parallel'
173+
type LoopType = 'for' | 'forEach'
174+
type ParallelType = 'count' | 'collection'
175+
176+
const iterationType: IterationType = 'loop'
177+
const currentType: LoopType = 'for'
178+
const isCountMode = iterationType === 'loop' && currentType === 'for'
179+
180+
expect(isCountMode).toBe(true)
181+
})
182+
183+
it.concurrent('should be in count mode for parallel + count combination', () => {
184+
type IterationType = 'loop' | 'parallel'
185+
type ParallelType = 'count' | 'collection'
186+
187+
const iterationType: IterationType = 'parallel'
188+
const currentType: ParallelType = 'count'
189+
const isCountMode = iterationType === 'parallel' && currentType === 'count'
190+
191+
expect(isCountMode).toBe(true)
192+
})
193+
194+
it.concurrent('should not be in count mode for loop + forEach combination', () => {
195+
type IterationType = 'loop' | 'parallel'
196+
197+
const testCountMode = (iterationType: IterationType, currentType: string) => {
198+
return iterationType === 'loop' && currentType === 'for'
199+
}
200+
201+
const isCountMode = testCountMode('loop', 'forEach')
202+
expect(isCountMode).toBe(false)
203+
})
204+
205+
it.concurrent('should not be in count mode for parallel + collection combination', () => {
206+
type IterationType = 'loop' | 'parallel'
207+
208+
const testCountMode = (iterationType: IterationType, currentType: string) => {
209+
return iterationType === 'parallel' && currentType === 'count'
210+
}
211+
212+
const isCountMode = testCountMode('parallel', 'collection')
213+
expect(isCountMode).toBe(false)
214+
})
215+
})
216+
217+
describe('Configuration Values', () => {
218+
it.concurrent('should handle default iteration count', () => {
219+
const data = { count: undefined }
220+
const configIterations = data.count ?? 5
221+
expect(configIterations).toBe(5)
222+
})
223+
224+
it.concurrent('should use provided iteration count', () => {
225+
const data = { count: 10 }
226+
const configIterations = data.count ?? 5
227+
expect(configIterations).toBe(10)
228+
})
229+
230+
it.concurrent('should handle string collection', () => {
231+
const collection = '[1, 2, 3, 4, 5]'
232+
const collectionString =
233+
typeof collection === 'string' ? collection : JSON.stringify(collection) || ''
234+
expect(collectionString).toBe('[1, 2, 3, 4, 5]')
235+
})
236+
237+
it.concurrent('should handle object collection', () => {
238+
const collection = { items: [1, 2, 3] }
239+
const collectionString =
240+
typeof collection === 'string' ? collection : JSON.stringify(collection) || ''
241+
expect(collectionString).toBe('{"items":[1,2,3]}')
242+
})
243+
244+
it.concurrent('should handle array collection', () => {
245+
const collection = [1, 2, 3, 4, 5]
246+
const collectionString =
247+
typeof collection === 'string' ? collection : JSON.stringify(collection) || ''
248+
expect(collectionString).toBe('[1,2,3,4,5]')
249+
})
250+
})
251+
252+
describe('Preview Mode Handling', () => {
253+
it.concurrent('should handle preview mode for loops', () => {
254+
const previewProps = {
255+
...defaultProps,
256+
data: { ...defaultProps.data, isPreview: true },
257+
iterationType: 'loop' as const,
258+
}
259+
260+
expect(previewProps.data.isPreview).toBe(true)
261+
// In preview mode, collaborative functions shouldn't be called
262+
expect(mockCollaborativeUpdates.collaborativeUpdateLoopType).not.toHaveBeenCalled()
263+
})
264+
265+
it.concurrent('should handle preview mode for parallels', () => {
266+
const previewProps = {
267+
...defaultProps,
268+
data: { ...defaultProps.data, isPreview: true },
269+
iterationType: 'parallel' as const,
270+
}
271+
272+
expect(previewProps.data.isPreview).toBe(true)
273+
// In preview mode, collaborative functions shouldn't be called
274+
expect(mockCollaborativeUpdates.collaborativeUpdateParallelType).not.toHaveBeenCalled()
275+
})
276+
})
277+
278+
describe('Store Integration', () => {
279+
it.concurrent('should access loops store for loop iteration type', () => {
280+
const nodeId = 'loop-node-1'
281+
;(mockStoreData.loops as any)[nodeId] = { iterations: 10 }
282+
283+
const nodeConfig = (mockStoreData.loops as any)[nodeId]
284+
expect(nodeConfig).toBeDefined()
285+
expect(nodeConfig.iterations).toBe(10)
286+
})
287+
288+
it.concurrent('should access parallels store for parallel iteration type', () => {
289+
const nodeId = 'parallel-node-1'
290+
;(mockStoreData.parallels as any)[nodeId] = { count: 5 }
291+
292+
const nodeConfig = (mockStoreData.parallels as any)[nodeId]
293+
expect(nodeConfig).toBeDefined()
294+
expect(nodeConfig.count).toBe(5)
295+
})
296+
297+
it.concurrent('should handle missing node configuration gracefully', () => {
298+
const nodeId = 'missing-node'
299+
const nodeConfig = (mockStoreData.loops as any)[nodeId]
300+
expect(nodeConfig).toBeUndefined()
301+
})
302+
})
303+
304+
describe('Max Iterations Limits', () => {
305+
it.concurrent('should enforce max iterations for loops (100)', () => {
306+
const maxIterations = 100
307+
const testValue = 150
308+
const clampedValue = Math.min(maxIterations, testValue)
309+
expect(clampedValue).toBe(100)
310+
})
311+
312+
it.concurrent('should enforce max iterations for parallels (20)', () => {
313+
const maxIterations = 20
314+
const testValue = 50
315+
const clampedValue = Math.min(maxIterations, testValue)
316+
expect(clampedValue).toBe(20)
317+
})
318+
319+
it.concurrent('should allow values within limits', () => {
320+
const loopMaxIterations = 100
321+
const parallelMaxIterations = 20
322+
323+
expect(Math.min(loopMaxIterations, 50)).toBe(50)
324+
expect(Math.min(parallelMaxIterations, 10)).toBe(10)
325+
})
326+
})
327+
328+
describe('Collaborative Update Functions', () => {
329+
it.concurrent('should have the correct collaborative functions available', () => {
330+
expect(mockCollaborativeUpdates.collaborativeUpdateLoopType).toBeDefined()
331+
expect(mockCollaborativeUpdates.collaborativeUpdateParallelType).toBeDefined()
332+
expect(mockCollaborativeUpdates.collaborativeUpdateIterationCount).toBeDefined()
333+
expect(mockCollaborativeUpdates.collaborativeUpdateIterationCollection).toBeDefined()
334+
})
335+
336+
it.concurrent('should call correct function for loop type updates', () => {
337+
const handleTypeChange = (newType: string, iterationType: string, nodeId: string) => {
338+
if (iterationType === 'loop') {
339+
mockCollaborativeUpdates.collaborativeUpdateLoopType(nodeId, newType)
340+
} else {
341+
mockCollaborativeUpdates.collaborativeUpdateParallelType(nodeId, newType)
342+
}
343+
}
344+
345+
handleTypeChange('forEach', 'loop', 'test-node')
346+
expect(mockCollaborativeUpdates.collaborativeUpdateLoopType).toHaveBeenCalledWith(
347+
'test-node',
348+
'forEach'
349+
)
350+
})
351+
352+
it.concurrent('should call correct function for parallel type updates', () => {
353+
const handleTypeChange = (newType: string, iterationType: string, nodeId: string) => {
354+
if (iterationType === 'loop') {
355+
mockCollaborativeUpdates.collaborativeUpdateLoopType(nodeId, newType)
356+
} else {
357+
mockCollaborativeUpdates.collaborativeUpdateParallelType(nodeId, newType)
358+
}
359+
}
360+
361+
handleTypeChange('collection', 'parallel', 'test-node')
362+
expect(mockCollaborativeUpdates.collaborativeUpdateParallelType).toHaveBeenCalledWith(
363+
'test-node',
364+
'collection'
365+
)
366+
})
367+
})
368+
369+
describe('Input Sanitization', () => {
370+
it.concurrent('should sanitize numeric input by removing non-digits', () => {
371+
const testInput = 'abc123def456'
372+
const sanitized = testInput.replace(/[^0-9]/g, '')
373+
expect(sanitized).toBe('123456')
374+
})
375+
376+
it.concurrent('should handle empty input', () => {
377+
const testInput = ''
378+
const sanitized = testInput.replace(/[^0-9]/g, '')
379+
expect(sanitized).toBe('')
380+
})
381+
382+
it.concurrent('should preserve valid numeric input', () => {
383+
const testInput = '42'
384+
const sanitized = testInput.replace(/[^0-9]/g, '')
385+
expect(sanitized).toBe('42')
386+
})
387+
})
388+
})

0 commit comments

Comments
 (0)