Files
ComfyUI_frontend/src/renderer/extensions/minimap/minimapCanvasRenderer.test.ts
Johnpaul Chiwetelu 3946d7b5ff Road to no explicit any part 8 group 5 (#8329)
## Summary
- Add `createMockLLink` and `createMockLinks` factory functions to
handle hybrid Map/Record types
- Replace `as any` assertions with type-safe factory functions in
minimap tests
- Implement proper Pinia store mocking using `vi.hoisted()` pattern
- Remove unused `createMockSubgraph` export (shadowed by local
implementations)

## Test plan
- [x] All minimap tests pass (29 tests)
- [x] `pnpm typecheck` passes
- [x] `pnpm lint` passes
- [x] `pnpm knip` passes

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8329-Road-to-no-explicit-any-part-8-group-5-2f56d73d365081218882de81d5526220)
by [Unito](https://www.unito.io)

---------

Co-authored-by: AustinMroz <austin@comfy.org>
2026-01-27 19:25:15 +01:00

347 lines
8.9 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest'
import type { LGraph } from '@/lib/litegraph/src/litegraph'
import { LGraphEventMode } from '@/lib/litegraph/src/litegraph'
import { renderMinimapToCanvas } from '@/renderer/extensions/minimap/minimapCanvasRenderer'
import type { MinimapRenderContext } from '@/renderer/extensions/minimap/types'
import { adjustColor } from '@/utils/colorUtil'
import {
createMockLGraph,
createMockLGraphNode,
createMockLinks,
createMockLLink,
createMockNodeOutputSlot
} from '@/utils/__tests__/litegraphTestUtils'
const mockUseColorPaletteStore = vi.hoisted(() => vi.fn())
vi.mock('@/stores/workspace/colorPaletteStore', () => ({
useColorPaletteStore: mockUseColorPaletteStore
}))
vi.mock('@/utils/colorUtil', () => ({
adjustColor: vi.fn((color: string) => color + '_adjusted')
}))
describe('minimapCanvasRenderer', () => {
let mockCanvas: HTMLCanvasElement
let mockContext: CanvasRenderingContext2D
let mockGraph: LGraph
beforeEach(() => {
vi.clearAllMocks()
mockContext = {
clearRect: vi.fn(),
fillRect: vi.fn(),
strokeRect: vi.fn(),
beginPath: vi.fn(),
moveTo: vi.fn(),
lineTo: vi.fn(),
stroke: vi.fn(),
arc: vi.fn(),
fill: vi.fn(),
fillStyle: '',
strokeStyle: '',
lineWidth: 1
} as Partial<CanvasRenderingContext2D> as CanvasRenderingContext2D
mockCanvas = {
getContext: vi.fn().mockReturnValue(mockContext)
} as Partial<HTMLCanvasElement> as HTMLCanvasElement
mockGraph = createMockLGraph({
_nodes: [
createMockLGraphNode({
id: '1',
pos: [100, 100],
size: [150, 80],
bgcolor: '#FF0000',
mode: LGraphEventMode.ALWAYS,
has_errors: false,
outputs: []
}),
createMockLGraphNode({
id: '2',
pos: [300, 200],
size: [120, 60],
bgcolor: '#00FF00',
mode: LGraphEventMode.BYPASS,
has_errors: true,
outputs: []
})
],
_groups: [],
links: {} as typeof mockGraph.links,
getNodeById: vi.fn()
})
mockUseColorPaletteStore.mockReturnValue({
completedActivePalette: {
id: 'test',
name: 'Test Palette',
colors: {},
light_theme: false
}
})
})
it('should clear canvas and render nodes', () => {
const context: MinimapRenderContext = {
bounds: { minX: 0, minY: 0, width: 500, height: 400 },
scale: 0.5,
settings: {
nodeColors: true,
showLinks: false,
showGroups: false,
renderBypass: true,
renderError: true
},
width: 250,
height: 200
}
renderMinimapToCanvas(mockCanvas, mockGraph, context)
// Should clear the canvas first
expect(mockContext.clearRect).toHaveBeenCalledWith(0, 0, 250, 200)
// Should render nodes (batch by color)
expect(mockContext.fillRect).toHaveBeenCalled()
})
it('should handle empty graph', () => {
mockGraph._nodes = []
const context: MinimapRenderContext = {
bounds: { minX: 0, minY: 0, width: 500, height: 400 },
scale: 0.5,
settings: {
nodeColors: true,
showLinks: false,
showGroups: false,
renderBypass: false,
renderError: false
},
width: 250,
height: 200
}
renderMinimapToCanvas(mockCanvas, mockGraph, context)
expect(mockContext.clearRect).toHaveBeenCalledWith(0, 0, 250, 200)
expect(mockContext.fillRect).not.toHaveBeenCalled()
})
it('should batch render nodes by color', () => {
const context: MinimapRenderContext = {
bounds: { minX: 0, minY: 0, width: 500, height: 400 },
scale: 0.5,
settings: {
nodeColors: true,
showLinks: false,
showGroups: false,
renderBypass: false,
renderError: false
},
width: 250,
height: 200
}
renderMinimapToCanvas(mockCanvas, mockGraph, context)
// Should set fill style for each color group
const fillStyleCalls = []
let currentStyle = ''
mockContext.fillStyle = ''
Object.defineProperty(mockContext, 'fillStyle', {
get: () => currentStyle,
set: (value) => {
currentStyle = value
fillStyleCalls.push(value)
}
})
renderMinimapToCanvas(mockCanvas, mockGraph, context)
// Different colors for different nodes
expect(fillStyleCalls.length).toBeGreaterThan(0)
})
it('should render bypass nodes with special color', () => {
const context: MinimapRenderContext = {
bounds: { minX: 0, minY: 0, width: 500, height: 400 },
scale: 0.5,
settings: {
nodeColors: true,
showLinks: false,
showGroups: false,
renderBypass: true,
renderError: false
},
width: 250,
height: 200
}
renderMinimapToCanvas(mockCanvas, mockGraph, context)
// Node 2 is in bypass mode, should be rendered
expect(mockContext.fillRect).toHaveBeenCalled()
})
it('should render error outlines when enabled', () => {
const context: MinimapRenderContext = {
bounds: { minX: 0, minY: 0, width: 500, height: 400 },
scale: 0.5,
settings: {
nodeColors: true,
showLinks: false,
showGroups: false,
renderBypass: false,
renderError: true
},
width: 250,
height: 200
}
renderMinimapToCanvas(mockCanvas, mockGraph, context)
// Should set stroke style for errors
expect(mockContext.strokeStyle).toBe('#FF0000')
expect(mockContext.strokeRect).toHaveBeenCalled()
})
it('should render groups when enabled', () => {
mockGraph._groups = [
{
pos: [50, 50],
size: [400, 300],
color: '#0000FF'
}
] as Partial<
(typeof mockGraph._groups)[number]
>[] as typeof mockGraph._groups
const context: MinimapRenderContext = {
bounds: { minX: 0, minY: 0, width: 500, height: 400 },
scale: 0.5,
settings: {
nodeColors: true,
showLinks: false,
showGroups: true,
renderBypass: false,
renderError: false
},
width: 250,
height: 200
}
renderMinimapToCanvas(mockCanvas, mockGraph, context)
// Groups should be rendered before nodes
expect(mockContext.fillRect).toHaveBeenCalled()
})
it('should render connections when enabled', () => {
const targetNode = {
id: '2',
pos: [300, 200],
size: [120, 60]
}
mockGraph._nodes[0].outputs = [
createMockNodeOutputSlot({
name: 'output',
type: 'number',
links: [1],
boundingRect: new Float64Array([0, 0, 10, 10])
})
]
mockGraph.links = createMockLinks([
createMockLLink({ id: 1, target_id: 2, origin_slot: 0, target_slot: 0 })
])
mockGraph.getNodeById = vi.fn().mockReturnValue(targetNode)
const context: MinimapRenderContext = {
bounds: { minX: 0, minY: 0, width: 500, height: 400 },
scale: 0.5,
settings: {
nodeColors: false,
showLinks: true,
showGroups: false,
renderBypass: false,
renderError: false
},
width: 250,
height: 200
}
renderMinimapToCanvas(mockCanvas, mockGraph, context)
// Should draw connection lines
expect(mockContext.beginPath).toHaveBeenCalled()
expect(mockContext.moveTo).toHaveBeenCalled()
expect(mockContext.lineTo).toHaveBeenCalled()
expect(mockContext.stroke).toHaveBeenCalled()
// Should draw connection slots
expect(mockContext.arc).toHaveBeenCalled()
expect(mockContext.fill).toHaveBeenCalled()
})
it('should handle light theme colors', () => {
mockUseColorPaletteStore.mockReturnValue({
completedActivePalette: {
id: 'test',
name: 'Test Palette',
colors: {},
light_theme: true
}
})
const context: MinimapRenderContext = {
bounds: { minX: 0, minY: 0, width: 500, height: 400 },
scale: 0.5,
settings: {
nodeColors: true,
showLinks: false,
showGroups: false,
renderBypass: false,
renderError: false
},
width: 250,
height: 200
}
renderMinimapToCanvas(mockCanvas, mockGraph, context)
// Color adjustment should be called for light theme
expect(adjustColor).toHaveBeenCalled()
})
it('should calculate correct offsets for centering', () => {
const context: MinimapRenderContext = {
bounds: { minX: 0, minY: 0, width: 200, height: 100 },
scale: 0.5,
settings: {
nodeColors: false,
showLinks: false,
showGroups: false,
renderBypass: false,
renderError: false
},
width: 250,
height: 200
}
renderMinimapToCanvas(mockCanvas, mockGraph, context)
// With bounds 200x100 at scale 0.5 = 100x50
// Canvas is 250x200, so offset should be (250-100)/2 = 75, (200-50)/2 = 75
// This affects node positioning
expect(mockContext.fillRect).toHaveBeenCalled()
})
})