mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-26 19:09:52 +00:00
No I am not proud of the new placeholder arguments. ## Summary Small change to unify the two composables before updating the logic. Edit: Now unifies them both into the **void**. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-5742-cleanup-unify-useCanvasTransformSync-composables-2776d73d36508147ad39d11de8588b2e) by [Unito](https://www.unito.io)
290 lines
8.3 KiB
TypeScript
290 lines
8.3 KiB
TypeScript
import { useRafFn } from '@vueuse/core'
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import { ref } from 'vue'
|
|
|
|
import type { LGraph } from '@/lib/litegraph/src/litegraph'
|
|
import { useMinimapViewport } from '@/renderer/extensions/minimap/composables/useMinimapViewport'
|
|
import type { MinimapCanvas } from '@/renderer/extensions/minimap/types'
|
|
|
|
vi.mock('@vueuse/core')
|
|
vi.mock('@/renderer/core/spatial/boundsCalculator', () => ({
|
|
calculateNodeBounds: vi.fn(),
|
|
calculateMinimapScale: vi.fn(),
|
|
enforceMinimumBounds: vi.fn()
|
|
}))
|
|
|
|
describe('useMinimapViewport', () => {
|
|
let mockCanvas: MinimapCanvas
|
|
let mockGraph: LGraph
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
|
|
mockCanvas = {
|
|
canvas: {
|
|
clientWidth: 800,
|
|
clientHeight: 600,
|
|
width: 1600,
|
|
height: 1200
|
|
} as HTMLCanvasElement,
|
|
ds: {
|
|
scale: 1,
|
|
offset: [0, 0]
|
|
},
|
|
setDirty: vi.fn()
|
|
}
|
|
|
|
mockGraph = {
|
|
_nodes: [
|
|
{ pos: [100, 100], size: [150, 80] },
|
|
{ pos: [300, 200], size: [120, 60] }
|
|
]
|
|
} as any
|
|
|
|
vi.mocked(useRafFn, { partial: true }).mockReturnValue({
|
|
resume: vi.fn(),
|
|
pause: vi.fn()
|
|
})
|
|
})
|
|
|
|
it('should initialize with default bounds', () => {
|
|
const canvasRef = ref(mockCanvas as any)
|
|
const graphRef = ref(mockGraph as any)
|
|
|
|
const viewport = useMinimapViewport(canvasRef, graphRef, 250, 200)
|
|
|
|
expect(viewport.bounds.value).toEqual({
|
|
minX: 0,
|
|
minY: 0,
|
|
maxX: 0,
|
|
maxY: 0,
|
|
width: 0,
|
|
height: 0
|
|
})
|
|
|
|
expect(viewport.scale.value).toBe(1)
|
|
})
|
|
|
|
it('should calculate graph bounds from nodes', async () => {
|
|
const { calculateNodeBounds, enforceMinimumBounds } = await import(
|
|
'@/renderer/core/spatial/boundsCalculator'
|
|
)
|
|
|
|
vi.mocked(calculateNodeBounds).mockReturnValue({
|
|
minX: 100,
|
|
minY: 100,
|
|
maxX: 420,
|
|
maxY: 260,
|
|
width: 320,
|
|
height: 160
|
|
})
|
|
|
|
vi.mocked(enforceMinimumBounds).mockImplementation((bounds) => bounds)
|
|
|
|
const canvasRef = ref(mockCanvas as any)
|
|
const graphRef = ref(mockGraph as any)
|
|
|
|
const viewport = useMinimapViewport(canvasRef, graphRef, 250, 200)
|
|
|
|
viewport.updateBounds()
|
|
|
|
expect(calculateNodeBounds).toHaveBeenCalledWith(mockGraph._nodes)
|
|
expect(enforceMinimumBounds).toHaveBeenCalled()
|
|
})
|
|
|
|
it('should handle empty graph', async () => {
|
|
const { calculateNodeBounds } = await import(
|
|
'@/renderer/core/spatial/boundsCalculator'
|
|
)
|
|
|
|
vi.mocked(calculateNodeBounds).mockReturnValue(null)
|
|
|
|
const canvasRef = ref(mockCanvas as any)
|
|
const graphRef = ref({ _nodes: [] } as any)
|
|
|
|
const viewport = useMinimapViewport(canvasRef, graphRef, 250, 200)
|
|
|
|
viewport.updateBounds()
|
|
|
|
expect(viewport.bounds.value).toEqual({
|
|
minX: 0,
|
|
minY: 0,
|
|
maxX: 100,
|
|
maxY: 100,
|
|
width: 100,
|
|
height: 100
|
|
})
|
|
})
|
|
|
|
it('should update canvas dimensions', () => {
|
|
const canvasRef = ref(mockCanvas as any)
|
|
const graphRef = ref(mockGraph as any)
|
|
|
|
const viewport = useMinimapViewport(canvasRef, graphRef, 250, 200)
|
|
|
|
viewport.updateCanvasDimensions()
|
|
|
|
expect(viewport.canvasDimensions.value).toEqual({
|
|
width: 800,
|
|
height: 600
|
|
})
|
|
})
|
|
|
|
it('should calculate viewport transform', async () => {
|
|
const { calculateNodeBounds, enforceMinimumBounds, calculateMinimapScale } =
|
|
await import('@/renderer/core/spatial/boundsCalculator')
|
|
|
|
// Mock the bounds calculation
|
|
vi.mocked(calculateNodeBounds).mockReturnValue({
|
|
minX: 0,
|
|
minY: 0,
|
|
maxX: 500,
|
|
maxY: 400,
|
|
width: 500,
|
|
height: 400
|
|
})
|
|
|
|
vi.mocked(enforceMinimumBounds).mockImplementation((bounds) => bounds)
|
|
vi.mocked(calculateMinimapScale).mockReturnValue(0.5)
|
|
|
|
const canvasRef = ref(mockCanvas as any)
|
|
const graphRef = ref(mockGraph as any)
|
|
|
|
const viewport = useMinimapViewport(canvasRef, graphRef, 250, 200)
|
|
|
|
// Set canvas transform
|
|
mockCanvas.ds.scale = 2
|
|
mockCanvas.ds.offset = [-100, -50]
|
|
|
|
// Update bounds and viewport
|
|
viewport.updateBounds()
|
|
viewport.updateCanvasDimensions()
|
|
viewport.updateViewport()
|
|
|
|
const transform = viewport.viewportTransform.value
|
|
|
|
// World coordinates
|
|
const worldX = -(-100) // -offset[0] = 100
|
|
const worldY = -(-50) // -offset[1] = 50
|
|
|
|
// Viewport size in world coordinates
|
|
const viewportWidth = 800 / 2 // canvasWidth / scale = 400
|
|
const viewportHeight = 600 / 2 // canvasHeight / scale = 300
|
|
|
|
// Center offsets
|
|
const centerOffsetX = (250 - 500 * 0.5) / 2 // (250 - 250) / 2 = 0
|
|
const centerOffsetY = (200 - 400 * 0.5) / 2 // (200 - 200) / 2 = 0
|
|
|
|
// Expected values based on implementation: (worldX - bounds.minX) * scale + centerOffsetX
|
|
expect(transform.x).toBeCloseTo((worldX - 0) * 0.5 + centerOffsetX) // (100 - 0) * 0.5 + 0 = 50
|
|
expect(transform.y).toBeCloseTo((worldY - 0) * 0.5 + centerOffsetY) // (50 - 0) * 0.5 + 0 = 25
|
|
expect(transform.width).toBeCloseTo(viewportWidth * 0.5) // 400 * 0.5 = 200
|
|
expect(transform.height).toBeCloseTo(viewportHeight * 0.5) // 300 * 0.5 = 150
|
|
})
|
|
|
|
it('should center view on world coordinates', () => {
|
|
const canvasRef = ref(mockCanvas as any)
|
|
const graphRef = ref(mockGraph as any)
|
|
|
|
const viewport = useMinimapViewport(canvasRef, graphRef, 250, 200)
|
|
|
|
viewport.updateCanvasDimensions()
|
|
mockCanvas.ds.scale = 2
|
|
|
|
viewport.centerViewOn(300, 200)
|
|
|
|
// Should update canvas offset to center on the given world coordinates
|
|
const expectedOffsetX = -(300 - 800 / 2 / 2) // -(worldX - viewportWidth/2)
|
|
const expectedOffsetY = -(200 - 600 / 2 / 2) // -(worldY - viewportHeight/2)
|
|
|
|
expect(mockCanvas.ds.offset[0]).toBe(expectedOffsetX)
|
|
expect(mockCanvas.ds.offset[1]).toBe(expectedOffsetY)
|
|
expect(mockCanvas.setDirty).toHaveBeenCalledWith(true, true)
|
|
})
|
|
|
|
it('should start and stop viewport sync', () => {
|
|
const startSyncMock = vi.fn()
|
|
const stopSyncMock = vi.fn()
|
|
|
|
vi.mocked(useRafFn, { partial: true }).mockReturnValue({
|
|
resume: startSyncMock,
|
|
pause: stopSyncMock
|
|
})
|
|
|
|
const canvasRef = ref(mockCanvas as any)
|
|
const graphRef = ref(mockGraph as any)
|
|
|
|
const viewport = useMinimapViewport(canvasRef, graphRef, 250, 200)
|
|
|
|
viewport.startViewportSync()
|
|
expect(startSyncMock).toHaveBeenCalled()
|
|
|
|
viewport.stopViewportSync()
|
|
expect(stopSyncMock).toHaveBeenCalled()
|
|
})
|
|
|
|
it('should handle null canvas gracefully', () => {
|
|
const canvasRef = ref(null as any)
|
|
const graphRef = ref(mockGraph as any)
|
|
|
|
const viewport = useMinimapViewport(canvasRef, graphRef, 250, 200)
|
|
|
|
// Should not throw
|
|
expect(() => viewport.updateCanvasDimensions()).not.toThrow()
|
|
expect(() => viewport.updateViewport()).not.toThrow()
|
|
expect(() => viewport.centerViewOn(100, 100)).not.toThrow()
|
|
})
|
|
|
|
it('should calculate scale correctly', async () => {
|
|
const { calculateMinimapScale, calculateNodeBounds, enforceMinimumBounds } =
|
|
await import('@/renderer/core/spatial/boundsCalculator')
|
|
|
|
const testBounds = {
|
|
minX: 0,
|
|
minY: 0,
|
|
maxX: 500,
|
|
maxY: 400,
|
|
width: 500,
|
|
height: 400
|
|
}
|
|
|
|
vi.mocked(calculateNodeBounds).mockReturnValue(testBounds)
|
|
vi.mocked(enforceMinimumBounds).mockImplementation((bounds) => bounds)
|
|
vi.mocked(calculateMinimapScale).mockReturnValue(0.4)
|
|
|
|
const canvasRef = ref(mockCanvas as any)
|
|
const graphRef = ref(mockGraph as any)
|
|
|
|
const viewport = useMinimapViewport(canvasRef, graphRef, 250, 200)
|
|
|
|
viewport.updateBounds()
|
|
|
|
expect(calculateMinimapScale).toHaveBeenCalledWith(testBounds, 250, 200)
|
|
expect(viewport.scale.value).toBe(0.4)
|
|
})
|
|
|
|
it('should handle device pixel ratio', () => {
|
|
const originalDPR = window.devicePixelRatio
|
|
Object.defineProperty(window, 'devicePixelRatio', {
|
|
value: 2,
|
|
configurable: true
|
|
})
|
|
|
|
const canvasRef = ref(mockCanvas as any)
|
|
const graphRef = ref(mockGraph as any)
|
|
|
|
const viewport = useMinimapViewport(canvasRef, graphRef, 250, 200)
|
|
|
|
viewport.updateCanvasDimensions()
|
|
|
|
// Should use client dimensions or calculate from canvas dimensions / dpr
|
|
expect(viewport.canvasDimensions.value.width).toBe(800)
|
|
expect(viewport.canvasDimensions.value.height).toBe(600)
|
|
|
|
Object.defineProperty(window, 'devicePixelRatio', {
|
|
value: originalDPR,
|
|
configurable: true
|
|
})
|
|
})
|
|
})
|