mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-27 17:52:16 +00:00
[refactor] Move pure functions from layout store to separate modules so they can be tested (and add tests) (#5462)
* refactor layout store utils * [refactor] use nullish coalescing in getOr helper - addresses @DrJKL's suggestion Replaces manual undefined/null checks with more concise ?? operator for cleaner code that achieves the same functionality. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * [refactor] improve Y.Map typing for better type safety - addresses @DrJKL's typing suggestions - Use Y.Map<NodeLayout[keyof NodeLayout]> instead of Y.Map<unknown> - Provides compile-time type safety for stored values - Improves IntelliSense and prevents type mismatches - Updates mappers, store, tests, and helper functions consistently - No runtime changes, pure TypeScript improvement 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * [refactor] address @arjansingh code quality feedback - Remove AI-generated refactoring comment that adds no value - Reorganize tests with nested describe blocks for better readability - Group related test cases by function for easier scanning 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * [refactor] move makeLinkSegmentKey to layoutUtils - addresses @arjansingh's file organization feedback - Move string concatenation function from layoutMath.ts to new layoutUtils.ts - Keep layoutMath.ts focused on pure geometric calculations - Create dedicated layoutUtils.ts for general layout utilities - Update imports in store and create separate test file - Improves module cohesion and clarity of purpose 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * [cleanup] remove leftover AI refactoring comments - Remove "Constants moved to utils" and "Node layout mapping moved to utils" - Clean up extra blank lines from previous refactoring - Keep meaningful organizational comments like "Helper methods" 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * [cleanup] remove unnecessary import aliases Remove pointInBoundsUtil/boundsIntersectUtil aliases as there are no naming conflicts. Use direct function names for cleaner code. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * [refactor] improve Y.Map typing with named NodeLayoutMap type - addresses @DrJKL's performance and type safety suggestions - Create named NodeLayoutMap type for TypeScript performance optimization - Improve getOr function with proper key constraints and type safety - Update all Y.Map<NodeLayout[keyof NodeLayout]> usages to use NodeLayoutMap - Remove manual type assertions in favor of generic key constraints - Clean up unused imports and fix formatting issues * [cleanup] remove explanatory comment per @DrJKL's preference * don't wait for dialog close button to be stable --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
49
tests-ui/tests/renderer/core/layout/utils/layoutMath.test.ts
Normal file
49
tests-ui/tests/renderer/core/layout/utils/layoutMath.test.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import {
|
||||
REROUTE_RADIUS,
|
||||
boundsIntersect,
|
||||
pointInBounds
|
||||
} from '@/renderer/core/layout/utils/layoutMath'
|
||||
|
||||
describe('layoutMath utils', () => {
|
||||
describe('pointInBounds', () => {
|
||||
it('detects inclusion correctly', () => {
|
||||
const bounds = { x: 10, y: 10, width: 100, height: 50 }
|
||||
expect(pointInBounds({ x: 10, y: 10 }, bounds)).toBe(true)
|
||||
expect(pointInBounds({ x: 110, y: 60 }, bounds)).toBe(true)
|
||||
expect(pointInBounds({ x: 9, y: 10 }, bounds)).toBe(false)
|
||||
expect(pointInBounds({ x: 111, y: 10 }, bounds)).toBe(false)
|
||||
expect(pointInBounds({ x: 10, y: 61 }, bounds)).toBe(false)
|
||||
})
|
||||
|
||||
it('works with zero-size bounds', () => {
|
||||
const zero = { x: 10, y: 20, width: 0, height: 0 }
|
||||
expect(pointInBounds({ x: 10, y: 20 }, zero)).toBe(true)
|
||||
expect(pointInBounds({ x: 10, y: 21 }, zero)).toBe(false)
|
||||
expect(pointInBounds({ x: 9, y: 20 }, zero)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('boundsIntersect', () => {
|
||||
it('detects intersection correctly', () => {
|
||||
const a = { x: 0, y: 0, width: 10, height: 10 }
|
||||
const b = { x: 5, y: 5, width: 10, height: 10 }
|
||||
const c = { x: 11, y: 0, width: 5, height: 5 }
|
||||
expect(boundsIntersect(a, b)).toBe(true)
|
||||
expect(boundsIntersect(a, c)).toBe(false)
|
||||
})
|
||||
|
||||
it('treats touching edges as intersecting', () => {
|
||||
const a = { x: 0, y: 0, width: 10, height: 10 }
|
||||
const d = { x: 10, y: 0, width: 5, height: 5 } // touches at right edge
|
||||
expect(boundsIntersect(a, d)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('REROUTE_RADIUS', () => {
|
||||
it('exports a sensible reroute radius', () => {
|
||||
expect(REROUTE_RADIUS).toBeGreaterThan(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,18 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { makeLinkSegmentKey } from '@/renderer/core/layout/utils/layoutUtils'
|
||||
|
||||
describe('layoutUtils', () => {
|
||||
describe('makeLinkSegmentKey', () => {
|
||||
it('creates stable keys for null reroute', () => {
|
||||
expect(makeLinkSegmentKey(10, null)).toBe('10:final')
|
||||
expect(makeLinkSegmentKey(42, null)).toBe('42:final')
|
||||
})
|
||||
|
||||
it('creates stable keys for numeric reroute ids', () => {
|
||||
expect(makeLinkSegmentKey(10, 3)).toBe('10:3')
|
||||
expect(makeLinkSegmentKey(42, 0)).toBe('42:0')
|
||||
expect(makeLinkSegmentKey(42, 7)).toBe('42:7')
|
||||
})
|
||||
})
|
||||
})
|
||||
47
tests-ui/tests/renderer/core/layout/utils/mappers.test.ts
Normal file
47
tests-ui/tests/renderer/core/layout/utils/mappers.test.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import * as Y from 'yjs'
|
||||
|
||||
import {
|
||||
NODE_LAYOUT_DEFAULTS,
|
||||
type NodeLayoutMap,
|
||||
yNodeToLayout
|
||||
} from '@/renderer/core/layout/utils/mappers'
|
||||
|
||||
describe('mappers', () => {
|
||||
it('yNodeToLayout reads from Yjs-attached map', () => {
|
||||
const layout = {
|
||||
id: 'node-1',
|
||||
position: { x: 12, y: 34 },
|
||||
size: { width: 111, height: 222 },
|
||||
zIndex: 5,
|
||||
visible: true,
|
||||
bounds: { x: 12, y: 34, width: 111, height: 222 }
|
||||
}
|
||||
|
||||
const doc = new Y.Doc()
|
||||
const ynode = doc.getMap('node') as NodeLayoutMap
|
||||
ynode.set('id', layout.id)
|
||||
ynode.set('position', layout.position)
|
||||
ynode.set('size', layout.size)
|
||||
ynode.set('zIndex', layout.zIndex)
|
||||
ynode.set('visible', layout.visible)
|
||||
ynode.set('bounds', layout.bounds)
|
||||
|
||||
const back = yNodeToLayout(ynode)
|
||||
expect(back).toEqual(layout)
|
||||
})
|
||||
|
||||
it('yNodeToLayout applies defaults for missing fields', () => {
|
||||
const doc = new Y.Doc()
|
||||
const ynode = doc.getMap('node') as NodeLayoutMap
|
||||
// Don't set any fields - they should all use defaults
|
||||
|
||||
const back = yNodeToLayout(ynode)
|
||||
expect(back.id).toBe(NODE_LAYOUT_DEFAULTS.id)
|
||||
expect(back.position).toEqual(NODE_LAYOUT_DEFAULTS.position)
|
||||
expect(back.size).toEqual(NODE_LAYOUT_DEFAULTS.size)
|
||||
expect(back.zIndex).toEqual(NODE_LAYOUT_DEFAULTS.zIndex)
|
||||
expect(back.visible).toEqual(NODE_LAYOUT_DEFAULTS.visible)
|
||||
expect(back.bounds).toEqual(NODE_LAYOUT_DEFAULTS.bounds)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user