mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-24 00:09:32 +00:00
Road to No explicit any Part 8 (Group 3): Improve type safety in Group 3 test mocks (#8304)
## Summary - Eliminated all `as unknown as` type assertions from Group 3 test files - Created reusable factory functions in `litegraphTestUtils.ts` for better type safety - Improved test mock composition using `Partial` types with single `as` casts - Fixed LGraphNode tests to use proper API methods instead of direct property assignment ## Changes by Category ### New Factory Functions in `litegraphTestUtils.ts` - `createMockLGraphNodeWithArrayBoundingRect()` - Creates LGraphNode with proper boundingRect for position tests - `createMockFileList()` - Creates mock FileList with proper structure including `item()` method ### Test File Improvements **Composables:** - `useLoad3dDrag.test.ts` - Used `createMockFileList` factory - `useLoad3dViewer.test.ts` - Created local `MockSceneManager` interface with proper typing **LiteGraph Tests:** - `LGraphNode.test.ts` - Replaced direct `boundingRect` assignments with `updateArea()` calls - `LinkConnector.test.ts` - Improved mock composition with proper Partial types - `ToOutputRenderLink.test.ts` - Added `MockEvents` interface for type-safe event mocking - Updated integration and core tests to use new factory functions **Extension Tests:** - `contextMenuFilter.test.ts` - Updated menu factories to accept `(IContextMenuValue | null)[]` ## Type Safety Improvements - Zero `as unknown as` instances (was: multiple instances across 17 files) - All mocks use proper `Partial<T>` composition with single `as T` casts - Improved IntelliSense and type checking in test files - Centralized mock creation reduces duplication and improves maintainability ## Test Plan - ✅ All TypeScript type checks pass - ✅ ESLint passes with no new errors - ✅ Pre-commit hooks (format, lint, typecheck) all pass - ✅ Knip unused export check passes - ✅ No behavioral changes to actual tests (only type improvements) ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8304-Road-to-No-explicit-any-Improve-type-safety-in-Group-3-test-mocks-2f36d73d365081ab841de96e5f01306d) by [Unito](https://www.unito.io)
This commit is contained in:
committed by
GitHub
parent
ba5380395d
commit
29220f6562
@@ -14,6 +14,11 @@ import {
|
||||
} from '@/lib/litegraph/src/litegraph'
|
||||
|
||||
import { test } from './__fixtures__/testExtensions'
|
||||
import { createMockLGraphNodeWithArrayBoundingRect } from '@/utils/__tests__/litegraphTestUtils'
|
||||
|
||||
interface NodeConstructorWithSlotOffset {
|
||||
slot_start_y?: number
|
||||
}
|
||||
|
||||
function getMockISerialisedNode(
|
||||
data: Partial<ISerialisedNode>
|
||||
@@ -297,16 +302,10 @@ describe('LGraphNode', () => {
|
||||
|
||||
describe('getInputPos and getOutputPos', () => {
|
||||
test('should handle collapsed nodes correctly', () => {
|
||||
const node = new LGraphNode('TestNode') as unknown as Omit<
|
||||
LGraphNode,
|
||||
'boundingRect'
|
||||
> & { boundingRect: Float64Array }
|
||||
const node = createMockLGraphNodeWithArrayBoundingRect('TestNode')
|
||||
node.pos = [100, 100]
|
||||
node.size = [100, 100]
|
||||
node.boundingRect[0] = 100
|
||||
node.boundingRect[1] = 100
|
||||
node.boundingRect[2] = 100
|
||||
node.boundingRect[3] = 100
|
||||
node.updateArea()
|
||||
node.configure(
|
||||
getMockISerialisedNode({
|
||||
id: 1,
|
||||
@@ -366,16 +365,10 @@ describe('LGraphNode', () => {
|
||||
})
|
||||
|
||||
test('should detect input slots correctly', () => {
|
||||
const node = new LGraphNode('TestNode') as unknown as Omit<
|
||||
LGraphNode,
|
||||
'boundingRect'
|
||||
> & { boundingRect: Float64Array }
|
||||
const node = createMockLGraphNodeWithArrayBoundingRect('TestNode')
|
||||
node.pos = [100, 100]
|
||||
node.size = [100, 100]
|
||||
node.boundingRect[0] = 100
|
||||
node.boundingRect[1] = 100
|
||||
node.boundingRect[2] = 200
|
||||
node.boundingRect[3] = 200
|
||||
node.updateArea()
|
||||
node.configure(
|
||||
getMockISerialisedNode({
|
||||
id: 1,
|
||||
@@ -398,16 +391,10 @@ describe('LGraphNode', () => {
|
||||
})
|
||||
|
||||
test('should detect output slots correctly', () => {
|
||||
const node = new LGraphNode('TestNode') as unknown as Omit<
|
||||
LGraphNode,
|
||||
'boundingRect'
|
||||
> & { boundingRect: Float64Array }
|
||||
const node = createMockLGraphNodeWithArrayBoundingRect('TestNode')
|
||||
node.pos = [100, 100]
|
||||
node.size = [100, 100]
|
||||
node.boundingRect[0] = 100
|
||||
node.boundingRect[1] = 100
|
||||
node.boundingRect[2] = 200
|
||||
node.boundingRect[3] = 200
|
||||
node.updateArea()
|
||||
node.configure(
|
||||
getMockISerialisedNode({
|
||||
id: 1,
|
||||
@@ -431,16 +418,10 @@ describe('LGraphNode', () => {
|
||||
})
|
||||
|
||||
test('should prioritize input slots over output slots', () => {
|
||||
const node = new LGraphNode('TestNode') as unknown as Omit<
|
||||
LGraphNode,
|
||||
'boundingRect'
|
||||
> & { boundingRect: Float64Array }
|
||||
const node = createMockLGraphNodeWithArrayBoundingRect('TestNode')
|
||||
node.pos = [100, 100]
|
||||
node.size = [100, 100]
|
||||
node.boundingRect[0] = 100
|
||||
node.boundingRect[1] = 100
|
||||
node.boundingRect[2] = 200
|
||||
node.boundingRect[3] = 200
|
||||
node.updateArea()
|
||||
node.configure(
|
||||
getMockISerialisedNode({
|
||||
id: 1,
|
||||
@@ -632,7 +613,8 @@ describe('LGraphNode', () => {
|
||||
}
|
||||
node.inputs = [inputSlot, inputSlot2]
|
||||
const slotIndex = 0
|
||||
const nodeOffsetY = (node.constructor as any).slot_start_y || 0
|
||||
const nodeOffsetY =
|
||||
(node.constructor as NodeConstructorWithSlotOffset).slot_start_y || 0
|
||||
const expectedY =
|
||||
200 + (slotIndex + 0.7) * LiteGraph.NODE_SLOT_HEIGHT + nodeOffsetY
|
||||
const expectedX = 100 + LiteGraph.NODE_SLOT_HEIGHT * 0.5
|
||||
@@ -644,7 +626,7 @@ describe('LGraphNode', () => {
|
||||
})
|
||||
|
||||
test('should return default vertical position including slot_start_y when defined', () => {
|
||||
;(node.constructor as any).slot_start_y = 25
|
||||
;(node.constructor as NodeConstructorWithSlotOffset).slot_start_y = 25
|
||||
node.flags.collapsed = false
|
||||
node.inputs = [inputSlot]
|
||||
const slotIndex = 0
|
||||
@@ -653,7 +635,7 @@ describe('LGraphNode', () => {
|
||||
200 + (slotIndex + 0.7) * LiteGraph.NODE_SLOT_HEIGHT + nodeOffsetY
|
||||
const expectedX = 100 + LiteGraph.NODE_SLOT_HEIGHT * 0.5
|
||||
expect(node.getInputSlotPos(inputSlot)).toEqual([expectedX, expectedY])
|
||||
delete (node.constructor as any).slot_start_y
|
||||
delete (node.constructor as NodeConstructorWithSlotOffset).slot_start_y
|
||||
})
|
||||
test('should not overwrite onMouseDown prototype', () => {
|
||||
expect(Object.prototype.hasOwnProperty.call(node, 'onMouseDown')).toEqual(
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { beforeEach, describe, expect, it } from 'vitest'
|
||||
|
||||
import { LGraphNodeProperties } from '@/lib/litegraph/src/LGraphNodeProperties'
|
||||
import type { LGraph, LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
import {
|
||||
createMockLGraph,
|
||||
createMockLGraphNode
|
||||
} from '@/utils/__tests__/litegraphTestUtils'
|
||||
|
||||
describe('LGraphNodeProperties', () => {
|
||||
let mockNode: any
|
||||
let mockGraph: any
|
||||
let mockNode: LGraphNode
|
||||
let mockGraph: LGraph
|
||||
|
||||
beforeEach(() => {
|
||||
mockGraph = {
|
||||
trigger: vi.fn()
|
||||
}
|
||||
mockGraph = createMockLGraph()
|
||||
|
||||
mockNode = {
|
||||
mockNode = createMockLGraphNode({
|
||||
id: 123,
|
||||
title: 'Test Node',
|
||||
flags: {},
|
||||
graph: mockGraph
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('property tracking', () => {
|
||||
|
||||
@@ -17,6 +17,10 @@ import {
|
||||
LinkDirection
|
||||
} from '@/lib/litegraph/src/litegraph'
|
||||
import type { ConnectingLink } from '@/lib/litegraph/src/interfaces'
|
||||
import {
|
||||
createMockNodeInputSlot,
|
||||
createMockNodeOutputSlot
|
||||
} from '@/utils/__tests__/litegraphTestUtils'
|
||||
|
||||
interface TestContext {
|
||||
network: LinkNetwork & { add(node: LGraphNode): void }
|
||||
@@ -136,7 +140,7 @@ describe('LinkConnector', () => {
|
||||
connector.state.connectingTo = 'input'
|
||||
|
||||
expect(() => {
|
||||
connector.moveInputLink(network, { link: 1 } as any)
|
||||
connector.moveInputLink(network, createMockNodeInputSlot({ link: 1 }))
|
||||
}).toThrow('Already dragging links.')
|
||||
})
|
||||
})
|
||||
@@ -174,7 +178,10 @@ describe('LinkConnector', () => {
|
||||
connector.state.connectingTo = 'output'
|
||||
|
||||
expect(() => {
|
||||
connector.moveOutputLink(network, { links: [1] } as any)
|
||||
connector.moveOutputLink(
|
||||
network,
|
||||
createMockNodeOutputSlot({ links: [1] })
|
||||
)
|
||||
}).toThrow('Already dragging links.')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -12,6 +12,10 @@ import { LGraphNode, LLink, LinkConnector } from '@/lib/litegraph/src/litegraph'
|
||||
|
||||
import { test as baseTest } from '../__fixtures__/testExtensions'
|
||||
import type { ConnectingLink } from '@/lib/litegraph/src/interfaces'
|
||||
import {
|
||||
createMockCanvasPointerEvent,
|
||||
createMockCanvasRenderingContext2D
|
||||
} from '@/utils/__tests__/litegraphTestUtils'
|
||||
|
||||
interface TestContext {
|
||||
graph: LGraph
|
||||
@@ -35,9 +39,9 @@ const test = baseTest.extend<TestContext>({
|
||||
},
|
||||
|
||||
graph: async ({ reroutesComplexGraph }, use) => {
|
||||
const ctx = vi.fn(() => ({ measureText: vi.fn(() => ({ width: 10 })) }))
|
||||
const mockCtx = createMockCanvasRenderingContext2D()
|
||||
for (const node of reroutesComplexGraph.nodes) {
|
||||
node.updateArea(ctx() as unknown as CanvasRenderingContext2D)
|
||||
node.updateArea(mockCtx)
|
||||
}
|
||||
await use(reroutesComplexGraph)
|
||||
},
|
||||
@@ -186,10 +190,10 @@ const test = baseTest.extend<TestContext>({
|
||||
})
|
||||
|
||||
function mockedNodeTitleDropEvent(node: LGraphNode): CanvasPointerEvent {
|
||||
return {
|
||||
canvasX: node.pos[0] + node.size[0] / 2,
|
||||
canvasY: node.pos[1] + 16
|
||||
} as any
|
||||
return createMockCanvasPointerEvent(
|
||||
node.pos[0] + node.size[0] / 2,
|
||||
node.pos[1] + 16
|
||||
)
|
||||
}
|
||||
|
||||
function mockedInputDropEvent(
|
||||
@@ -197,10 +201,7 @@ function mockedInputDropEvent(
|
||||
slot: number
|
||||
): CanvasPointerEvent {
|
||||
const pos = node.getInputPos(slot)
|
||||
return {
|
||||
canvasX: pos[0],
|
||||
canvasY: pos[1]
|
||||
} as any
|
||||
return createMockCanvasPointerEvent(pos[0], pos[1])
|
||||
}
|
||||
|
||||
function mockedOutputDropEvent(
|
||||
@@ -208,10 +209,7 @@ function mockedOutputDropEvent(
|
||||
slot: number
|
||||
): CanvasPointerEvent {
|
||||
const pos = node.getOutputPos(slot)
|
||||
return {
|
||||
canvasX: pos[0],
|
||||
canvasY: pos[1]
|
||||
} as any
|
||||
return createMockCanvasPointerEvent(pos[0], pos[1])
|
||||
}
|
||||
|
||||
describe('LinkConnector Integration', () => {
|
||||
@@ -239,7 +237,7 @@ describe('LinkConnector Integration', () => {
|
||||
|
||||
const canvasX = disconnectedNode.pos[0] + disconnectedNode.size[0] / 2
|
||||
const canvasY = disconnectedNode.pos[1] + 16
|
||||
const dropEvent = { canvasX, canvasY } as any
|
||||
const dropEvent = createMockCanvasPointerEvent(canvasX, canvasY)
|
||||
|
||||
// Drop links, ensure reset has not been run
|
||||
connector.dropLinks(graph, dropEvent)
|
||||
@@ -281,7 +279,7 @@ describe('LinkConnector Integration', () => {
|
||||
|
||||
const canvasX = disconnectedNode.pos[0] + disconnectedNode.size[0] / 2
|
||||
const canvasY = disconnectedNode.pos[1] + 16
|
||||
const dropEvent = { canvasX, canvasY } as any
|
||||
const dropEvent = createMockCanvasPointerEvent(canvasX, canvasY)
|
||||
|
||||
connector.dropLinks(graph, dropEvent)
|
||||
connector.reset()
|
||||
@@ -422,7 +420,7 @@ describe('LinkConnector Integration', () => {
|
||||
|
||||
const canvasX = disconnectedNode.pos[0] + disconnectedNode.size[0] / 2
|
||||
const canvasY = disconnectedNode.pos[1] + 16
|
||||
const dropEvent = { canvasX, canvasY } as any
|
||||
const dropEvent = createMockCanvasPointerEvent(canvasX, canvasY)
|
||||
|
||||
connector.dropLinks(graph, dropEvent)
|
||||
connector.reset()
|
||||
@@ -473,9 +471,10 @@ describe('LinkConnector Integration', () => {
|
||||
expect(floatingLink).toBeInstanceOf(LLink)
|
||||
const floatingReroute = LLink.getReroutes(graph, floatingLink)[0]
|
||||
|
||||
const canvasX = floatingReroute.pos[0]
|
||||
const canvasY = floatingReroute.pos[1]
|
||||
const dropEvent = { canvasX, canvasY } as any
|
||||
const dropEvent = createMockCanvasPointerEvent(
|
||||
floatingReroute.pos[0],
|
||||
floatingReroute.pos[1]
|
||||
)
|
||||
|
||||
connector.dropLinks(graph, dropEvent)
|
||||
connector.reset()
|
||||
@@ -554,7 +553,10 @@ describe('LinkConnector Integration', () => {
|
||||
const manyOutputsNode = graph.getNodeById(4)!
|
||||
const canvasX = floatingReroute.pos[0]
|
||||
const canvasY = floatingReroute.pos[1]
|
||||
const floatingRerouteEvent = { canvasX, canvasY } as any
|
||||
const floatingRerouteEvent = createMockCanvasPointerEvent(
|
||||
canvasX,
|
||||
canvasY
|
||||
)
|
||||
|
||||
connector.moveOutputLink(graph, manyOutputsNode.outputs[0])
|
||||
connector.dropLinks(graph, floatingRerouteEvent)
|
||||
@@ -579,7 +581,7 @@ describe('LinkConnector Integration', () => {
|
||||
|
||||
const canvasX = reroute7.pos[0]
|
||||
const canvasY = reroute7.pos[1]
|
||||
const reroute7Event = { canvasX, canvasY } as any
|
||||
const reroute7Event = createMockCanvasPointerEvent(canvasX, canvasY)
|
||||
|
||||
const toSortedRerouteChain = (linkIds: number[]) =>
|
||||
linkIds
|
||||
@@ -698,7 +700,7 @@ describe('LinkConnector Integration', () => {
|
||||
const canvasY = disconnectedNode.pos[1]
|
||||
|
||||
connector.dragFromReroute(graph, floatingReroute)
|
||||
connector.dropLinks(graph, { canvasX, canvasY } as any)
|
||||
connector.dropLinks(graph, createMockCanvasPointerEvent(canvasX, canvasY))
|
||||
connector.reset()
|
||||
|
||||
expect(graph.floatingLinks.size).toBe(0)
|
||||
@@ -716,7 +718,7 @@ describe('LinkConnector Integration', () => {
|
||||
const canvasY = reroute8.pos[1]
|
||||
|
||||
connector.dragFromReroute(graph, floatingReroute)
|
||||
connector.dropLinks(graph, { canvasX, canvasY } as any)
|
||||
connector.dropLinks(graph, createMockCanvasPointerEvent(canvasX, canvasY))
|
||||
connector.reset()
|
||||
|
||||
expect(graph.floatingLinks.size).toBe(0)
|
||||
@@ -801,10 +803,10 @@ describe('LinkConnector Integration', () => {
|
||||
connector.moveOutputLink(graph, floatingOutNode.outputs[0])
|
||||
|
||||
const manyOutputsNode = graph.getNodeById(4)!
|
||||
const dropEvent = {
|
||||
canvasX: manyOutputsNode.pos[0],
|
||||
canvasY: manyOutputsNode.pos[1]
|
||||
} as any
|
||||
const dropEvent = createMockCanvasPointerEvent(
|
||||
manyOutputsNode.pos[0],
|
||||
manyOutputsNode.pos[1]
|
||||
)
|
||||
connector.dropLinks(graph, dropEvent)
|
||||
connector.reset()
|
||||
|
||||
@@ -818,9 +820,11 @@ describe('LinkConnector Integration', () => {
|
||||
connector.moveOutputLink(graph, manyOutputsNode.outputs[0])
|
||||
|
||||
const disconnectedNode = graph.getNodeById(9)!
|
||||
dropEvent.canvasX = disconnectedNode.pos[0]
|
||||
dropEvent.canvasY = disconnectedNode.pos[1]
|
||||
connector.dropLinks(graph, dropEvent)
|
||||
const dropEvent2 = createMockCanvasPointerEvent(
|
||||
disconnectedNode.pos[0],
|
||||
disconnectedNode.pos[1]
|
||||
)
|
||||
connector.dropLinks(graph, dropEvent2)
|
||||
connector.reset()
|
||||
|
||||
const newOutput = disconnectedNode.outputs[0]
|
||||
@@ -951,10 +955,10 @@ describe('LinkConnector Integration', () => {
|
||||
|
||||
const targetReroute = graph.reroutes.get(targetRerouteId)!
|
||||
const nextLinkIds = getNextLinkIds(targetReroute.linkIds)
|
||||
const dropEvent = {
|
||||
canvasX: targetReroute.pos[0],
|
||||
canvasY: targetReroute.pos[1]
|
||||
} as any
|
||||
const dropEvent = createMockCanvasPointerEvent(
|
||||
targetReroute.pos[0],
|
||||
targetReroute.pos[1]
|
||||
)
|
||||
|
||||
connector.dragNewFromOutput(
|
||||
graph,
|
||||
@@ -1094,10 +1098,10 @@ describe('LinkConnector Integration', () => {
|
||||
|
||||
connector.dragFromReroute(graph, fromReroute)
|
||||
|
||||
const dropEvent = {
|
||||
canvasX: toReroute.pos[0],
|
||||
canvasY: toReroute.pos[1]
|
||||
} as any
|
||||
const dropEvent = createMockCanvasPointerEvent(
|
||||
toReroute.pos[0],
|
||||
toReroute.pos[1]
|
||||
)
|
||||
connector.dropLinks(graph, dropEvent)
|
||||
connector.reset()
|
||||
|
||||
@@ -1167,10 +1171,10 @@ describe('LinkConnector Integration', () => {
|
||||
const fromReroute = graph.reroutes.get(from)!
|
||||
const toReroute = graph.reroutes.get(to)!
|
||||
|
||||
const dropEvent = {
|
||||
canvasX: toReroute.pos[0],
|
||||
canvasY: toReroute.pos[1]
|
||||
} as any
|
||||
const dropEvent = createMockCanvasPointerEvent(
|
||||
toReroute.pos[0],
|
||||
toReroute.pos[1]
|
||||
)
|
||||
|
||||
connector.dragFromReroute(graph, fromReroute)
|
||||
connector.dropLinks(graph, dropEvent)
|
||||
@@ -1204,10 +1208,10 @@ describe('LinkConnector Integration', () => {
|
||||
const node = graph.getNodeById(nodeId)!
|
||||
const input = node.inputs[0]
|
||||
const reroute = graph.getReroute(rerouteId)!
|
||||
const dropEvent = {
|
||||
canvasX: reroute.pos[0],
|
||||
canvasY: reroute.pos[1]
|
||||
} as any
|
||||
const dropEvent = createMockCanvasPointerEvent(
|
||||
reroute.pos[0],
|
||||
reroute.pos[1]
|
||||
)
|
||||
|
||||
connector.dragNewFromInput(graph, node, input)
|
||||
connector.dropLinks(graph, dropEvent)
|
||||
@@ -1234,7 +1238,7 @@ describe('LinkConnector Integration', () => {
|
||||
|
||||
const node = graph.getNodeById(nodeId)!
|
||||
const reroute = graph.getReroute(rerouteId)!
|
||||
const dropEvent = { canvasX: node.pos[0], canvasY: node.pos[1] } as any
|
||||
const dropEvent = createMockCanvasPointerEvent(node.pos[0], node.pos[1])
|
||||
|
||||
connector.dragFromReroute(graph, reroute)
|
||||
connector.dropLinks(graph, dropEvent)
|
||||
@@ -1262,10 +1266,10 @@ describe('LinkConnector Integration', () => {
|
||||
const node = graph.getNodeById(nodeId)!
|
||||
const reroute = graph.getReroute(rerouteId)!
|
||||
const inputPos = node.getInputPos(0)
|
||||
const dropOnInputEvent = {
|
||||
canvasX: inputPos[0],
|
||||
canvasY: inputPos[1]
|
||||
} as any
|
||||
const dropOnInputEvent = createMockCanvasPointerEvent(
|
||||
inputPos[0],
|
||||
inputPos[1]
|
||||
)
|
||||
|
||||
connector.dragFromReroute(graph, reroute)
|
||||
connector.dropLinks(graph, dropOnInputEvent)
|
||||
|
||||
@@ -1,23 +1,46 @@
|
||||
// TODO: Fix these tests after migration
|
||||
import { beforeEach, describe, expect, test, vi } from 'vitest'
|
||||
|
||||
import type { INodeInputSlot, LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
// We don't strictly need RenderLink interface import for the mock
|
||||
import { LinkConnector } from '@/lib/litegraph/src/litegraph'
|
||||
import {
|
||||
createMockCanvasPointerEvent,
|
||||
createMockLGraphNode,
|
||||
createMockLinkNetwork,
|
||||
createMockNodeInputSlot,
|
||||
createMockNodeOutputSlot
|
||||
} from '@/utils/__tests__/litegraphTestUtils'
|
||||
|
||||
// Mocks
|
||||
const mockSetConnectingLinks = vi.fn()
|
||||
|
||||
type RenderLinkItem = LinkConnector['renderLinks'][number]
|
||||
|
||||
// Mock a structure that has the needed method
|
||||
function mockRenderLinkImpl(canConnect: boolean) {
|
||||
return {
|
||||
canConnectToInput: vi.fn().mockReturnValue(canConnect)
|
||||
// Add other properties if they become necessary for tests
|
||||
function mockRenderLinkImpl(canConnect: boolean): RenderLinkItem {
|
||||
const partial: Partial<RenderLinkItem> = {
|
||||
toType: 'output',
|
||||
fromPos: [0, 0],
|
||||
fromSlotIndex: 0,
|
||||
fromDirection: 0,
|
||||
network: createMockLinkNetwork(),
|
||||
node: createMockLGraphNode(),
|
||||
fromSlot: createMockNodeOutputSlot(),
|
||||
dragDirection: 0,
|
||||
canConnectToInput: vi.fn().mockReturnValue(canConnect),
|
||||
canConnectToOutput: vi.fn().mockReturnValue(false),
|
||||
canConnectToReroute: vi.fn().mockReturnValue(false),
|
||||
connectToInput: vi.fn(),
|
||||
connectToOutput: vi.fn(),
|
||||
connectToSubgraphInput: vi.fn(),
|
||||
connectToRerouteOutput: vi.fn(),
|
||||
connectToSubgraphOutput: vi.fn(),
|
||||
connectToRerouteInput: vi.fn()
|
||||
}
|
||||
return partial as RenderLinkItem
|
||||
}
|
||||
|
||||
const mockNode = {} as LGraphNode
|
||||
const mockInput = {} as INodeInputSlot
|
||||
const mockNode = createMockLGraphNode()
|
||||
const mockInput = createMockNodeInputSlot()
|
||||
|
||||
describe.skip('LinkConnector', () => {
|
||||
let connector: LinkConnector
|
||||
@@ -37,8 +60,7 @@ describe.skip('LinkConnector', () => {
|
||||
test('should return true if at least one render link can connect', () => {
|
||||
const link1 = mockRenderLinkImpl(false)
|
||||
const link2 = mockRenderLinkImpl(true)
|
||||
// Cast to any to satisfy the push requirement, as we only need the canConnectToInput method
|
||||
connector.renderLinks.push(link1 as any, link2 as any)
|
||||
connector.renderLinks.push(link1, link2)
|
||||
expect(connector.isInputValidDrop(mockNode, mockInput)).toBe(true)
|
||||
expect(link1.canConnectToInput).toHaveBeenCalledWith(mockNode, mockInput)
|
||||
expect(link2.canConnectToInput).toHaveBeenCalledWith(mockNode, mockInput)
|
||||
@@ -47,7 +69,7 @@ describe.skip('LinkConnector', () => {
|
||||
test('should return false if no render links can connect', () => {
|
||||
const link1 = mockRenderLinkImpl(false)
|
||||
const link2 = mockRenderLinkImpl(false)
|
||||
connector.renderLinks.push(link1 as any, link2 as any)
|
||||
connector.renderLinks.push(link1, link2)
|
||||
expect(connector.isInputValidDrop(mockNode, mockInput)).toBe(false)
|
||||
expect(link1.canConnectToInput).toHaveBeenCalledWith(mockNode, mockInput)
|
||||
expect(link2.canConnectToInput).toHaveBeenCalledWith(mockNode, mockInput)
|
||||
@@ -57,7 +79,7 @@ describe.skip('LinkConnector', () => {
|
||||
const link1 = mockRenderLinkImpl(false)
|
||||
const link2 = mockRenderLinkImpl(true) // This one can connect
|
||||
const link3 = mockRenderLinkImpl(false)
|
||||
connector.renderLinks.push(link1 as any, link2 as any, link3 as any)
|
||||
connector.renderLinks.push(link1, link2, link3)
|
||||
|
||||
expect(connector.isInputValidDrop(mockNode, mockInput)).toBe(true)
|
||||
|
||||
@@ -88,7 +110,10 @@ describe.skip('LinkConnector', () => {
|
||||
|
||||
test('should call the listener when the event is dispatched before reset', () => {
|
||||
const listener = vi.fn()
|
||||
const eventData = { renderLinks: [], event: {} as any } // Mock event data
|
||||
const eventData = {
|
||||
renderLinks: [],
|
||||
event: createMockCanvasPointerEvent(0, 0)
|
||||
}
|
||||
connector.listenUntilReset('before-drop-links', listener)
|
||||
|
||||
connector.events.dispatch('before-drop-links', eventData)
|
||||
@@ -120,7 +145,10 @@ describe.skip('LinkConnector', () => {
|
||||
|
||||
test('should not call the listener after reset is dispatched', () => {
|
||||
const listener = vi.fn()
|
||||
const eventData = { renderLinks: [], event: {} as any }
|
||||
const eventData = {
|
||||
renderLinks: [],
|
||||
event: createMockCanvasPointerEvent(0, 0)
|
||||
}
|
||||
connector.listenUntilReset('before-drop-links', listener)
|
||||
|
||||
// Dispatch reset first
|
||||
|
||||
@@ -9,10 +9,20 @@ import {
|
||||
LLink
|
||||
} from '@/lib/litegraph/src/litegraph'
|
||||
import { ToInputFromIoNodeLink } from '@/lib/litegraph/src/canvas/ToInputFromIoNodeLink'
|
||||
import type { NodeInputSlot } from '@/lib/litegraph/src/litegraph'
|
||||
import type {
|
||||
CanvasPointerEvent,
|
||||
NodeInputSlot
|
||||
} from '@/lib/litegraph/src/litegraph'
|
||||
import { LinkDirection } from '@/lib/litegraph/src/types/globalEnums'
|
||||
|
||||
import { createTestSubgraph } from '../subgraph/__fixtures__/subgraphHelpers'
|
||||
import {
|
||||
createMockCanvasPointerEvent,
|
||||
createMockNodeInputSlot
|
||||
} from '@/utils/__tests__/litegraphTestUtils'
|
||||
|
||||
type MockPointerEvent = CanvasPointerEvent
|
||||
type MockRenderLink = ToOutputRenderLink
|
||||
|
||||
describe('LinkConnector SubgraphInput connection validation', () => {
|
||||
let connector: LinkConnector
|
||||
@@ -206,10 +216,7 @@ describe('LinkConnector SubgraphInput connection validation', () => {
|
||||
connector.state.connectingTo = 'output'
|
||||
|
||||
// Create mock event
|
||||
const mockEvent = {
|
||||
canvasX: 100,
|
||||
canvasY: 100
|
||||
} as any
|
||||
const mockEvent: MockPointerEvent = createMockCanvasPointerEvent(100, 100)
|
||||
|
||||
// Mock the getSlotInPosition to return the subgraph input
|
||||
const mockGetSlotInPosition = vi.fn().mockReturnValue(subgraph.inputs[0])
|
||||
@@ -256,10 +263,7 @@ describe('LinkConnector SubgraphInput connection validation', () => {
|
||||
connector.state.connectingTo = 'output'
|
||||
|
||||
// Create mock event
|
||||
const mockEvent = {
|
||||
canvasX: 100,
|
||||
canvasY: 100
|
||||
} as any
|
||||
const mockEvent: MockPointerEvent = createMockCanvasPointerEvent(100, 100)
|
||||
|
||||
// Mock the getSlotInPosition to return the subgraph input
|
||||
const mockGetSlotInPosition = vi.fn().mockReturnValue(subgraph.inputs[0])
|
||||
@@ -342,12 +346,12 @@ describe('LinkConnector SubgraphInput connection validation', () => {
|
||||
})
|
||||
|
||||
// Create a mock render link without the method
|
||||
const mockLink = {
|
||||
fromSlot: { type: 'number' }
|
||||
const mockLink: Partial<MockRenderLink> = {
|
||||
fromSlot: createMockNodeInputSlot({ type: 'number' })
|
||||
// No canConnectToSubgraphInput method
|
||||
} as any
|
||||
}
|
||||
|
||||
connector.renderLinks.push(mockLink)
|
||||
connector.renderLinks.push(mockLink as MockRenderLink)
|
||||
|
||||
const subgraphInput = subgraph.inputs[0]
|
||||
|
||||
|
||||
@@ -4,23 +4,30 @@ import {
|
||||
LinkDirection,
|
||||
ToOutputRenderLink
|
||||
} from '@/lib/litegraph/src/litegraph'
|
||||
import type { CustomEventTarget } from '@/lib/litegraph/src/infrastructure/CustomEventTarget'
|
||||
import type { LinkConnectorEventMap } from '@/lib/litegraph/src/infrastructure/LinkConnectorEventMap'
|
||||
import {
|
||||
createMockLGraphNode,
|
||||
createMockLinkNetwork,
|
||||
createMockNodeInputSlot,
|
||||
createMockNodeOutputSlot
|
||||
} from '@/utils/__tests__/litegraphTestUtils'
|
||||
|
||||
describe('ToOutputRenderLink', () => {
|
||||
describe('connectToOutput', () => {
|
||||
it('should return early if inputNode is null', () => {
|
||||
// Setup
|
||||
const mockNetwork = {}
|
||||
const mockFromSlot = {}
|
||||
const mockNode = {
|
||||
id: 'test-id',
|
||||
const mockNetwork = createMockLinkNetwork()
|
||||
const mockFromSlot = createMockNodeInputSlot()
|
||||
const mockNode = createMockLGraphNode({
|
||||
inputs: [mockFromSlot],
|
||||
getInputPos: vi.fn().mockReturnValue([0, 0])
|
||||
}
|
||||
})
|
||||
|
||||
const renderLink = new ToOutputRenderLink(
|
||||
mockNetwork as any,
|
||||
mockNode as any,
|
||||
mockFromSlot as any,
|
||||
mockNetwork,
|
||||
mockNode,
|
||||
mockFromSlot,
|
||||
undefined,
|
||||
LinkDirection.CENTER
|
||||
)
|
||||
@@ -30,18 +37,21 @@ describe('ToOutputRenderLink', () => {
|
||||
value: null
|
||||
})
|
||||
|
||||
const mockTargetNode = {
|
||||
const mockTargetNode = createMockLGraphNode({
|
||||
connectSlots: vi.fn()
|
||||
}
|
||||
const mockEvents = {
|
||||
})
|
||||
const mockEvents: Partial<CustomEventTarget<LinkConnectorEventMap>> = {
|
||||
addEventListener: vi.fn(),
|
||||
removeEventListener: vi.fn(),
|
||||
dispatchEvent: vi.fn(),
|
||||
dispatch: vi.fn()
|
||||
}
|
||||
|
||||
// Act
|
||||
renderLink.connectToOutput(
|
||||
mockTargetNode as any,
|
||||
{} as any,
|
||||
mockEvents as any
|
||||
mockTargetNode,
|
||||
createMockNodeOutputSlot(),
|
||||
mockEvents as CustomEventTarget<LinkConnectorEventMap>
|
||||
)
|
||||
|
||||
// Assert
|
||||
@@ -51,35 +61,37 @@ describe('ToOutputRenderLink', () => {
|
||||
|
||||
it('should create connection and dispatch event when inputNode exists', () => {
|
||||
// Setup
|
||||
const mockNetwork = {}
|
||||
const mockFromSlot = {}
|
||||
const mockNode = {
|
||||
id: 'test-id',
|
||||
const mockNetwork = createMockLinkNetwork()
|
||||
const mockFromSlot = createMockNodeInputSlot()
|
||||
const mockNode = createMockLGraphNode({
|
||||
inputs: [mockFromSlot],
|
||||
getInputPos: vi.fn().mockReturnValue([0, 0])
|
||||
}
|
||||
})
|
||||
|
||||
const renderLink = new ToOutputRenderLink(
|
||||
mockNetwork as any,
|
||||
mockNode as any,
|
||||
mockFromSlot as any,
|
||||
mockNetwork,
|
||||
mockNode,
|
||||
mockFromSlot,
|
||||
undefined,
|
||||
LinkDirection.CENTER
|
||||
)
|
||||
|
||||
const mockNewLink = { id: 'new-link' }
|
||||
const mockTargetNode = {
|
||||
const mockTargetNode = createMockLGraphNode({
|
||||
connectSlots: vi.fn().mockReturnValue(mockNewLink)
|
||||
}
|
||||
const mockEvents = {
|
||||
})
|
||||
const mockEvents: Partial<CustomEventTarget<LinkConnectorEventMap>> = {
|
||||
addEventListener: vi.fn(),
|
||||
removeEventListener: vi.fn(),
|
||||
dispatchEvent: vi.fn(),
|
||||
dispatch: vi.fn()
|
||||
}
|
||||
|
||||
// Act
|
||||
renderLink.connectToOutput(
|
||||
mockTargetNode as any,
|
||||
{} as any,
|
||||
mockEvents as any
|
||||
mockTargetNode,
|
||||
createMockNodeOutputSlot(),
|
||||
mockEvents as CustomEventTarget<LinkConnectorEventMap>
|
||||
)
|
||||
|
||||
// Assert
|
||||
|
||||
@@ -3,6 +3,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { legacyMenuCompat } from '@/lib/litegraph/src/contextMenuCompat'
|
||||
import type { IContextMenuValue } from '@/lib/litegraph/src/litegraph'
|
||||
import { LGraphCanvas } from '@/lib/litegraph/src/litegraph'
|
||||
import { createMockCanvas } from '@/utils/__tests__/litegraphTestUtils'
|
||||
|
||||
describe('contextMenuCompat', () => {
|
||||
let originalGetCanvasMenuOptions: typeof LGraphCanvas.prototype.getCanvasMenuOptions
|
||||
@@ -13,11 +14,11 @@ describe('contextMenuCompat', () => {
|
||||
originalGetCanvasMenuOptions = LGraphCanvas.prototype.getCanvasMenuOptions
|
||||
|
||||
// Create mock canvas
|
||||
mockCanvas = {
|
||||
mockCanvas = createMockCanvas({
|
||||
constructor: {
|
||||
prototype: LGraphCanvas.prototype
|
||||
}
|
||||
} as unknown as LGraphCanvas
|
||||
} as typeof LGraphCanvas
|
||||
} as Partial<LGraphCanvas>)
|
||||
|
||||
// Clear console warnings
|
||||
vi.spyOn(console, 'warn').mockImplementation(() => {})
|
||||
@@ -54,11 +55,12 @@ describe('contextMenuCompat', () => {
|
||||
|
||||
// Simulate extension monkey-patching
|
||||
const original = LGraphCanvas.prototype.getCanvasMenuOptions
|
||||
LGraphCanvas.prototype.getCanvasMenuOptions = function (...args: any[]) {
|
||||
const items = (original as any).apply(this, args)
|
||||
items.push({ content: 'Custom Item', callback: () => {} })
|
||||
return items
|
||||
}
|
||||
LGraphCanvas.prototype.getCanvasMenuOptions =
|
||||
function (): (IContextMenuValue | null)[] {
|
||||
const items = original.call(this)
|
||||
items.push({ content: 'Custom Item', callback: () => {} })
|
||||
return items
|
||||
}
|
||||
|
||||
// Should have logged a warning with extension name
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
@@ -83,8 +85,10 @@ describe('contextMenuCompat', () => {
|
||||
legacyMenuCompat.install(LGraphCanvas.prototype, methodName)
|
||||
legacyMenuCompat.setCurrentExtension('test.extension')
|
||||
|
||||
const patchFunction = function (this: LGraphCanvas, ...args: any[]) {
|
||||
const items = (originalGetCanvasMenuOptions as any).apply(this, args)
|
||||
const patchFunction = function (
|
||||
this: LGraphCanvas
|
||||
): (IContextMenuValue | null)[] {
|
||||
const items = originalGetCanvasMenuOptions.call(this)
|
||||
items.push({ content: 'Custom', callback: () => {} })
|
||||
return items
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user