CodeRabbit Generated Unit Tests: Add injectionKeys and boundsCalculator unit tests

This commit is contained in:
coderabbitai[bot]
2025-11-21 22:32:26 +00:00
committed by GitHub
parent 8b58943229
commit c4f4a452a0
4 changed files with 280 additions and 0 deletions

View File

@@ -349,3 +349,49 @@ describe('useTransformState', () => {
})
})
})
describe('useTransformState - Non-Shared Behavior', () => {
it('should create independent instances for multiple calls', () => {
const state1 = useTransformState()
const state2 = useTransformState()
// Modify state1
const mockCanvas1 = {
ds: { offset: [100, 200], scale: 1.5 }
} as any
state1.syncWithCanvas(mockCanvas1)
// Modify state2 differently
const mockCanvas2 = {
ds: { offset: [300, 400], scale: 0.5 }
} as any
state2.syncWithCanvas(mockCanvas2)
// Each instance should have its own camera state
expect(state1.camera.x).toBe(100)
expect(state1.camera.z).toBe(1.5)
expect(state2.camera.x).toBe(300)
expect(state2.camera.z).toBe(0.5)
})
it('should handle ArrayLike<number> parameters correctly', () => {
const { isNodeInViewport } = useTransformState()
const viewport = { width: 800, height: 600 }
// Test with regular arrays
const arrayPos: number[] = [100, 200]
const arraySize: number[] = [150, 100]
expect(() => isNodeInViewport(arrayPos, arraySize, viewport)).not.toThrow()
// Test with TypedArrays
const typedPos = new Float32Array([100, 200])
const typedSize = new Float64Array([150, 100])
expect(() => isNodeInViewport(typedPos, typedSize, viewport)).not.toThrow()
// Test with tuples
const tuplePos: [number, number] = [100, 200]
const tupleSize: [number, number] = [150, 100]
expect(() => isNodeInViewport(tuplePos, tupleSize, viewport)).not.toThrow()
})
})

View File

@@ -0,0 +1,59 @@
import { describe, expect, it } from 'vitest'
import { inject, provide } from 'vue'
import {
TransformStateKey,
type TransformState
} from '@/renderer/core/layout/injectionKeys'
describe('injectionKeys', () => {
describe('TransformStateKey', () => {
it('should be a valid Symbol injection key', () => {
expect(TransformStateKey).toBeDefined()
expect(typeof TransformStateKey).toBe('symbol')
expect(TransformStateKey.toString()).toContain('transformState')
})
it('should work with Vue provide/inject', () => {
const mockTransformState: TransformState = {
camera: { x: 0, y: 0, z: 1 },
screenToCanvas: () => ({ x: 0, y: 0 }),
canvasToScreen: () => ({ x: 0, y: 0 }),
isNodeInViewport: () => true
}
// Simulate provide
provide(TransformStateKey, mockTransformState)
// Simulate inject
const injected = inject(TransformStateKey)
expect(injected).toBe(mockTransformState)
})
it('should enforce correct TransformState interface structure', () => {
const validState: TransformState = {
camera: { x: 10, y: 20, z: 1.5 },
screenToCanvas: (point: { x: number; y: number }) => ({
x: point.x / 1.5 - 10,
y: point.y / 1.5 - 20
}),
canvasToScreen: (point: { x: number; y: number }) => ({
x: (point.x + 10) * 1.5,
y: (point.y + 20) * 1.5
}),
isNodeInViewport: (
nodePos: ArrayLike<number>,
nodeSize: ArrayLike<number>,
viewport: { width: number; height: number },
margin?: number
) => {
return true
}
}
// Type check - should compile without errors
const _check: TransformState = validState
expect(_check).toBeDefined()
})
})
})

View File

@@ -0,0 +1,95 @@
import { describe, expect, it } from 'vitest'
import { calculateNodeBounds } from '@/renderer/core/spatial/boundsCalculator'
describe('boundsCalculator', () => {
describe('calculateNodeBounds', () => {
it('should calculate bounds for single node', () => {
const nodes = [{ pos: [100, 200], size: [150, 100] }]
const bounds = calculateNodeBounds(nodes)
expect(bounds).toEqual({
minX: 100,
minY: 200,
maxX: 250,
maxY: 300,
width: 150,
height: 100
})
})
it('should calculate bounds for multiple nodes', () => {
const nodes = [
{ pos: [100, 200], size: [150, 100] },
{ pos: [300, 400], size: [200, 150] },
{ pos: [50, 100], size: [100, 80] }
]
const bounds = calculateNodeBounds(nodes)
expect(bounds).toEqual({
minX: 50,
minY: 100,
maxX: 500,
maxY: 550,
width: 450,
height: 450
})
})
it('should return null for empty array', () => {
const nodes: Array<{ pos: ArrayLike<number>; size: ArrayLike<number> }> =
[]
const bounds = calculateNodeBounds(nodes)
expect(bounds).toBeNull()
})
it('should handle nodes with negative coordinates', () => {
const nodes = [
{ pos: [-100, -200], size: [150, 100] },
{ pos: [50, 100], size: [100, 80] }
]
const bounds = calculateNodeBounds(nodes)
expect(bounds).toEqual({
minX: -100,
minY: -200,
maxX: 150,
maxY: 180,
width: 250,
height: 380
})
})
it('should accept TypedArray for position and size', () => {
const nodes = [
{ pos: new Float32Array([100, 200]), size: [150, 100] },
{ pos: new Float64Array([300, 400]), size: new Float32Array([200, 150]) }
]
const bounds = calculateNodeBounds(nodes)
expect(bounds).not.toBeNull()
expect(bounds!.minX).toBe(100)
expect(bounds!.minY).toBe(200)
})
it('should handle large number of nodes efficiently', () => {
const nodes = Array.from({ length: 10000 }, (_, i) => ({
pos: [i * 10, i * 10],
size: [100, 100]
}))
const start = performance.now()
const bounds = calculateNodeBounds(nodes)
const end = performance.now()
expect(bounds).not.toBeNull()
expect(end - start).toBeLessThan(100)
})
})
})

View File

@@ -399,3 +399,83 @@ describe('useNodeEventHandlers', () => {
})
})
})
describe('useNodeEventHandlers - New Batch Operations', () => {
describe('selectNodes', () => {
it('should select multiple nodes at once', () => {
const { selectNodes } = useNodeEventHandlers()
const mockNodes = [
{ id: 'node1', selected: false },
{ id: 'node2', selected: false },
{ id: 'node3', selected: false }
]
mockNodeManager.value!.getNode = vi.fn((id: string) =>
mockNodes.find((n) => n.id === id)
) as any
const canvasStore = useCanvasStore()
canvasStore.canvas!.select = vi.fn()
canvasStore.canvas!.deselectAll = vi.fn()
selectNodes(['node1', 'node2', 'node3'], false)
expect(canvasStore.canvas!.deselectAll).toHaveBeenCalled()
expect(canvasStore.canvas!.select).toHaveBeenCalledTimes(3)
})
it('should skip non-existent nodes', () => {
const { selectNodes } = useNodeEventHandlers()
mockNodeManager.value!.getNode = vi.fn(() => null)
const canvasStore = useCanvasStore()
canvasStore.canvas!.select = vi.fn()
selectNodes(['missing-node'], false)
expect(canvasStore.canvas!.select).not.toHaveBeenCalled()
})
})
describe('deselectNodes', () => {
it('should deselect multiple nodes', () => {
const { deselectNodes } = useNodeEventHandlers()
const mockNodes = [
{ id: 'node1', selected: true },
{ id: 'node2', selected: true }
]
mockNodeManager.value!.getNode = vi.fn((id: string) =>
mockNodes.find((n) => n.id === id)
) as any
const canvasStore = useCanvasStore()
canvasStore.canvas!.deselect = vi.fn()
deselectNodes(['node1', 'node2'])
expect(canvasStore.canvas!.deselect).toHaveBeenCalledTimes(2)
})
})
describe('deselectNode', () => {
it('should deselect a single node', () => {
const { deselectNode } = useNodeEventHandlers()
const mockNodeToDeselect = { id: 'test-node', selected: true }
mockNodeManager.value!.getNode = vi.fn(() => mockNodeToDeselect as any)
const canvasStore = useCanvasStore()
canvasStore.canvas!.deselect = vi.fn()
deselectNode('test-node')
expect(canvasStore.canvas!.deselect).toHaveBeenCalledWith(
mockNodeToDeselect
)
})
})
})