diff --git a/src/lib/litegraph/src/LGraph.ts b/src/lib/litegraph/src/LGraph.ts index 49ad351010..95bd5b3ad8 100644 --- a/src/lib/litegraph/src/LGraph.ts +++ b/src/lib/litegraph/src/LGraph.ts @@ -2603,6 +2603,10 @@ export class Subgraph } addInput(name: string, type: string): SubgraphInput { + if (name === null || type === null) { + throw new Error('Name and type are required for subgraph input') + } + this.events.dispatch('adding-input', { name, type }) const input = new SubgraphInput( @@ -2621,6 +2625,10 @@ export class Subgraph } addOutput(name: string, type: string): SubgraphOutput { + if (name === null || type === null) { + throw new Error('Name and type are required for subgraph output') + } + this.events.dispatch('adding-output', { name, type }) const output = new SubgraphOutput( diff --git a/src/lib/litegraph/src/interfaces.ts b/src/lib/litegraph/src/interfaces.ts index aad3dff3fa..9df7339af1 100644 --- a/src/lib/litegraph/src/interfaces.ts +++ b/src/lib/litegraph/src/interfaces.ts @@ -254,7 +254,10 @@ type KeysOfType = Exclude< > /** The names of all (optional) methods and functions in T */ -export type MethodNames = KeysOfType any) | undefined> +export type MethodNames = KeysOfType< + T, + ((...args: unknown[]) => unknown) | undefined +> export interface NewNodePosition { node: LGraphNode newPos: { @@ -459,28 +462,6 @@ export interface ISubgraphInput extends INodeInputSlot { _subgraphSlot: SubgraphInput } -/** - * Shorthand for {@link Parameters} of optional callbacks. - * @example - * ```ts - * const { onClick } = CustomClass.prototype - * CustomClass.prototype.onClick = function (...args: CallbackParams) { - * const r = onClick?.apply(this, args) - * // ... - * return r - * } - * ``` - */ -export type CallbackParams any) | undefined> = - Parameters> - -/** - * Shorthand for {@link ReturnType} of optional callbacks. - * @see {@link CallbackParams} - */ -export type CallbackReturn any) | undefined> = - ReturnType> - /** * An object that can be hovered over. */ diff --git a/src/lib/litegraph/src/subgraph/ExecutableNodeDTO.ts b/src/lib/litegraph/src/subgraph/ExecutableNodeDTO.ts index 9e9454a817..366dbb96d3 100644 --- a/src/lib/litegraph/src/subgraph/ExecutableNodeDTO.ts +++ b/src/lib/litegraph/src/subgraph/ExecutableNodeDTO.ts @@ -4,11 +4,7 @@ import { InvalidLinkError } from '@/lib/litegraph/src/infrastructure/InvalidLink import { NullGraphError } from '@/lib/litegraph/src/infrastructure/NullGraphError' import { RecursionError } from '@/lib/litegraph/src/infrastructure/RecursionError' import { SlotIndexError } from '@/lib/litegraph/src/infrastructure/SlotIndexError' -import type { - CallbackParams, - CallbackReturn, - ISlotType -} from '@/lib/litegraph/src/interfaces' +import type { ISlotType } from '@/lib/litegraph/src/interfaces' import { LGraphEventMode, LiteGraph } from '@/lib/litegraph/src/litegraph' import type { Subgraph } from './Subgraph' @@ -45,8 +41,8 @@ type ResolvedInput = { */ export class ExecutableNodeDTO implements ExecutableLGraphNode { applyToGraph?( - ...args: CallbackParams - ): CallbackReturn + ...args: Parameters> + ): ReturnType> /** The graph that this node is a part of. */ readonly graph: LGraph | Subgraph diff --git a/src/lib/litegraph/src/subgraph/SubgraphEdgeCases.test.ts b/src/lib/litegraph/src/subgraph/SubgraphEdgeCases.test.ts index b161467e28..5a5ba1745f 100644 --- a/src/lib/litegraph/src/subgraph/SubgraphEdgeCases.test.ts +++ b/src/lib/litegraph/src/subgraph/SubgraphEdgeCases.test.ts @@ -83,7 +83,9 @@ describe.skip('SubgraphEdgeCases - Invalid States', () => { name: 'fake', type: 'number', disconnect: () => {} - } as any + } as Partial[0]> as Parameters< + typeof subgraph.removeInput + >[0] // Should throw appropriate error for non-existent input expect(() => { @@ -97,41 +99,43 @@ describe.skip('SubgraphEdgeCases - Invalid States', () => { name: 'fake', type: 'number', disconnect: () => {} - } as any + } as Partial[0]> as Parameters< + typeof subgraph.removeOutput + >[0] expect(() => { subgraph.removeOutput(fakeOutput) }).toThrow(/Output not found/) // Expected error }) - it('should handle null/undefined input names', () => { + it('should throw error for null/undefined input names', () => { const subgraph = createTestSubgraph() - // ISSUE: Current implementation allows null/undefined names which may cause runtime errors - // TODO: Consider adding validation to prevent null/undefined names - // This test documents the current permissive behavior + const nullString: string = null! + const undefinedString: string = undefined! + expect(() => { - subgraph.addInput(null as any, 'number') - }).not.toThrow() // Current behavior: allows null + subgraph.addInput(nullString, 'number') + }).toThrow() expect(() => { - subgraph.addInput(undefined as any, 'number') - }).not.toThrow() // Current behavior: allows undefined + subgraph.addInput(undefinedString, 'number') + }).toThrow() }) it('should handle null/undefined output names', () => { const subgraph = createTestSubgraph() - // ISSUE: Current implementation allows null/undefined names which may cause runtime errors - // TODO: Consider adding validation to prevent null/undefined names - // This test documents the current permissive behavior + const nullString: string = null! + const undefinedString: string = undefined! + expect(() => { - subgraph.addOutput(null as any, 'number') - }).not.toThrow() // Current behavior: allows null + subgraph.addOutput(nullString, 'number') + }).toThrow() expect(() => { - subgraph.addOutput(undefined as any, 'number') - }).not.toThrow() // Current behavior: allows undefined + subgraph.addOutput(undefinedString, 'number') + }).toThrow() }) it('should handle empty string names', () => { @@ -151,14 +155,16 @@ describe.skip('SubgraphEdgeCases - Invalid States', () => { it('should handle undefined types gracefully', () => { const subgraph = createTestSubgraph() - // Undefined type should not crash but may have default behavior + const undefinedString: string = undefined! + + // Undefined type should throw error expect(() => { - subgraph.addInput('test', undefined as any) - }).not.toThrow() + subgraph.addInput('test', undefinedString) + }).toThrow() expect(() => { - subgraph.addOutput('test', undefined as any) - }).not.toThrow() + subgraph.addOutput('test', undefinedString) + }).toThrow() }) it('should handle duplicate slot names', () => { diff --git a/src/lib/litegraph/src/subgraph/SubgraphMemory.test.ts b/src/lib/litegraph/src/subgraph/SubgraphMemory.test.ts index 7b936392a6..92699283e9 100644 --- a/src/lib/litegraph/src/subgraph/SubgraphMemory.test.ts +++ b/src/lib/litegraph/src/subgraph/SubgraphMemory.test.ts @@ -10,6 +10,12 @@ import { createTestSubgraphNode } from './__fixtures__/subgraphHelpers' +type InputWithWidget = { + _widget?: IWidget | { type: string; value: unknown; name: string } + _connection?: { id: number; type: string } + _listenerController?: AbortController +} + describe.skip('SubgraphNode Memory Management', () => { describe.skip('Event Listener Cleanup', () => { it('should register event listeners on construction', () => { @@ -308,14 +314,14 @@ describe.skip('SubgraphMemory - Widget Reference Management', () => { // Set widget reference if (input && '_widget' in input) { - ;(input as any)._widget = mockWidget - expect((input as any)._widget).toBe(mockWidget) + ;(input as InputWithWidget)._widget = mockWidget + expect((input as InputWithWidget)._widget).toBe(mockWidget) } // Clear widget reference if (input && '_widget' in input) { - ;(input as any)._widget = undefined - expect((input as any)._widget).toBeUndefined() + ;(input as InputWithWidget)._widget = undefined + expect((input as InputWithWidget)._widget).toBeUndefined() } } ) @@ -360,30 +366,34 @@ describe.skip('SubgraphMemory - Widget Reference Management', () => { // Set up references that should be cleaned up const mockReferences = { - widget: { type: 'number', value: 42 }, + widget: { type: 'number', value: 42, name: 'mock_widget' }, connection: { id: 1, type: 'number' }, listener: vi.fn() } // Set references if (input) { - ;(input as any)._widget = mockReferences.widget - ;(input as any)._connection = mockReferences.connection + ;(input as InputWithWidget)._widget = mockReferences.widget + ;(input as InputWithWidget)._connection = mockReferences.connection } if (output) { - ;(input as any)._connection = mockReferences.connection + ;(input as InputWithWidget)._connection = mockReferences.connection } // Verify references are set - expect((input as any)?._widget).toBe(mockReferences.widget) - expect((input as any)?._connection).toBe(mockReferences.connection) + expect((input as InputWithWidget)?._widget).toBe(mockReferences.widget) + expect((input as InputWithWidget)?._connection).toBe( + mockReferences.connection + ) // Simulate proper cleanup (what onRemoved should do) subgraphNode.onRemoved() // Input-specific listeners should be cleaned up (this works) if (input && '_listenerController' in input) { - expect((input as any)._listenerController?.signal.aborted).toBe(true) + expect( + (input as InputWithWidget)._listenerController?.signal.aborted + ).toBe(true) } } ) diff --git a/src/lib/litegraph/src/subgraph/SubgraphNode.test.ts b/src/lib/litegraph/src/subgraph/SubgraphNode.test.ts index 96e2e5dd38..10493cfd44 100644 --- a/src/lib/litegraph/src/subgraph/SubgraphNode.test.ts +++ b/src/lib/litegraph/src/subgraph/SubgraphNode.test.ts @@ -9,6 +9,7 @@ import { describe, expect, it, vi } from 'vitest' import type { SubgraphNode } from '@/lib/litegraph/src/litegraph' import { LGraph, Subgraph } from '@/lib/litegraph/src/litegraph' +import type { SubgraphInput } from '@/lib/litegraph/src/subgraph/SubgraphInput' import { subgraphTest } from './__fixtures__/subgraphFixtures' import { @@ -531,7 +532,7 @@ describe.skip('SubgraphNode Cleanup', () => { // Now trigger an event - only node1 should respond subgraph.events.dispatch('input-added', { - input: { name: 'test', type: 'number', id: 'test-id' } as any + input: { name: 'test', type: 'number', id: 'test-id' } as SubgraphInput }) // Only node1 should have added an input @@ -558,7 +559,7 @@ describe.skip('SubgraphNode Cleanup', () => { // Trigger an event - no nodes should respond subgraph.events.dispatch('input-added', { - input: { name: 'test', type: 'number', id: 'test-id' } as any + input: { name: 'test', type: 'number', id: 'test-id' } as SubgraphInput }) // Without cleanup: all 3 removed nodes would have added an input diff --git a/src/lib/litegraph/src/subgraph/SubgraphNode.titleButton.test.ts b/src/lib/litegraph/src/subgraph/SubgraphNode.titleButton.test.ts index c76e9d5ee0..feb6b1d50c 100644 --- a/src/lib/litegraph/src/subgraph/SubgraphNode.titleButton.test.ts +++ b/src/lib/litegraph/src/subgraph/SubgraphNode.titleButton.test.ts @@ -3,12 +3,18 @@ import { describe, expect, it, vi } from 'vitest' import { LGraphButton } from '@/lib/litegraph/src/litegraph' import type { LGraphCanvas } from '@/lib/litegraph/src/litegraph' +import type { CanvasPointerEvent } from '@/lib/litegraph/src/types/events' import { createTestSubgraph, createTestSubgraphNode } from './__fixtures__/subgraphHelpers' +interface MockPointerEvent { + canvasX: number + canvasY: number +} + describe.skip('SubgraphNode Title Button', () => { describe.skip('Constructor', () => { it('should automatically add enter_subgraph button', () => { @@ -58,7 +64,7 @@ describe.skip('SubgraphNode Title Button', () => { const canvas = { openSubgraph: vi.fn(), dispatch: vi.fn() - } as unknown as LGraphCanvas + } as Partial as LGraphCanvas subgraphNode.onTitleButtonClick(enterButton, canvas) @@ -78,7 +84,7 @@ describe.skip('SubgraphNode Title Button', () => { const canvas = { openSubgraph: vi.fn(), dispatch: vi.fn() - } as unknown as LGraphCanvas + } as Partial as LGraphCanvas subgraphNode.onTitleButtonClick(customButton, canvas) @@ -119,16 +125,16 @@ describe.skip('SubgraphNode Title Button', () => { const canvas = { ctx: { measureText: vi.fn().mockReturnValue({ width: 25 }) - } as unknown as CanvasRenderingContext2D, + } as Partial as CanvasRenderingContext2D, openSubgraph: vi.fn(), dispatch: vi.fn() - } as unknown as LGraphCanvas + } as Partial as LGraphCanvas // Simulate click on the enter button - const event = { + const event: MockPointerEvent = { canvasX: 275, // Near right edge where button should be canvasY: 80 // In title area - } as any + } // Calculate node-relative position const clickPosRelativeToNode: [number, number] = [ @@ -138,7 +144,7 @@ describe.skip('SubgraphNode Title Button', () => { // @ts-expect-error onMouseDown possibly undefined const handled = subgraphNode.onMouseDown( - event, + event as Partial as CanvasPointerEvent, clickPosRelativeToNode, canvas ) @@ -156,16 +162,16 @@ describe.skip('SubgraphNode Title Button', () => { const canvas = { ctx: { measureText: vi.fn().mockReturnValue({ width: 25 }) - } as unknown as CanvasRenderingContext2D, + } as Partial as CanvasRenderingContext2D, openSubgraph: vi.fn(), dispatch: vi.fn() - } as unknown as LGraphCanvas + } as Partial as LGraphCanvas // Click in the body of the node, not on button - const event = { + const event: MockPointerEvent = { canvasX: 200, // Middle of node canvasY: 150 // Body area - } as any + } // Calculate node-relative position const clickPosRelativeToNode: [number, number] = [ @@ -173,9 +179,8 @@ describe.skip('SubgraphNode Title Button', () => { 150 - subgraphNode.pos[1] // 150 - 100 = 50 ] - // @ts-expect-error onMouseDown possibly undefined - const handled = subgraphNode.onMouseDown( - event, + const handled = subgraphNode.onMouseDown!( + event as Partial as CanvasPointerEvent, clickPosRelativeToNode, canvas ) @@ -204,25 +209,24 @@ describe.skip('SubgraphNode Title Button', () => { const canvas = { ctx: { measureText: vi.fn().mockReturnValue({ width: 25 }) - } as unknown as CanvasRenderingContext2D, + } as Partial as CanvasRenderingContext2D, openSubgraph: vi.fn(), dispatch: vi.fn() - } as unknown as LGraphCanvas + } as Partial as LGraphCanvas // Try to click on where the button would be - const event = { + const event: MockPointerEvent = { canvasX: 275, canvasY: 80 - } as any + } const clickPosRelativeToNode: [number, number] = [ 275 - subgraphNode.pos[0], // 175 80 - subgraphNode.pos[1] // -20 ] - // @ts-expect-error onMouseDown possibly undefined - const handled = subgraphNode.onMouseDown( - event, + const handled = subgraphNode.onMouseDown!( + event as Partial as CanvasPointerEvent, clickPosRelativeToNode, canvas ) diff --git a/src/lib/litegraph/src/subgraph/SubgraphSlotVisualFeedback.test.ts b/src/lib/litegraph/src/subgraph/SubgraphSlotVisualFeedback.test.ts index baf842812f..9a85ddb185 100644 --- a/src/lib/litegraph/src/subgraph/SubgraphSlotVisualFeedback.test.ts +++ b/src/lib/litegraph/src/subgraph/SubgraphSlotVisualFeedback.test.ts @@ -1,13 +1,21 @@ // TODO: Fix these tests after migration import { beforeEach, describe, expect, it, vi } from 'vitest' +import type { DefaultConnectionColors } from '@/lib/litegraph/src/interfaces' import { LGraphNode } from '@/lib/litegraph/src/litegraph' import { createTestSubgraph } from './__fixtures__/subgraphHelpers' +interface MockColorContext { + defaultInputColor: string + defaultOutputColor: string + getConnectedColor: ReturnType + getDisconnectedColor: ReturnType +} + describe.skip('SubgraphSlot visual feedback', () => { let mockCtx: CanvasRenderingContext2D - let mockColorContext: any + let mockColorContext: MockColorContext let globalAlphaValues: number[] beforeEach(() => { @@ -34,7 +42,8 @@ describe.skip('SubgraphSlot visual feedback', () => { rect: vi.fn(), fillText: vi.fn() } - mockCtx = mockContext as unknown as CanvasRenderingContext2D + mockCtx = + mockContext as Partial as CanvasRenderingContext2D // Create a mock color context mockColorContext = { @@ -42,7 +51,7 @@ describe.skip('SubgraphSlot visual feedback', () => { defaultOutputColor: '#00FF00', getConnectedColor: vi.fn().mockReturnValue('#0000FF'), getDisconnectedColor: vi.fn().mockReturnValue('#AAAAAA') - } + } as Partial as MockColorContext }) it('should render SubgraphInput slots with full opacity when dragging from compatible slot', () => { @@ -60,7 +69,8 @@ describe.skip('SubgraphSlot visual feedback', () => { // Draw the slot with a compatible fromSlot subgraphInput.draw({ ctx: mockCtx, - colorContext: mockColorContext, + colorContext: + mockColorContext as Partial as DefaultConnectionColors, fromSlot: nodeInput, editorAlpha: 1 }) @@ -80,7 +90,8 @@ describe.skip('SubgraphSlot visual feedback', () => { // Draw subgraphInput2 while dragging from subgraphInput1 (incompatible - both are outputs inside subgraph) subgraphInput2.draw({ ctx: mockCtx, - colorContext: mockColorContext, + colorContext: + mockColorContext as Partial as DefaultConnectionColors, fromSlot: subgraphInput1, editorAlpha: 1 }) @@ -105,7 +116,8 @@ describe.skip('SubgraphSlot visual feedback', () => { // Draw the slot with a compatible fromSlot subgraphOutput.draw({ ctx: mockCtx, - colorContext: mockColorContext, + colorContext: + mockColorContext as Partial as DefaultConnectionColors, fromSlot: nodeOutput, editorAlpha: 1 }) @@ -125,7 +137,8 @@ describe.skip('SubgraphSlot visual feedback', () => { // Draw subgraphOutput2 while dragging from subgraphOutput1 (incompatible - both are inputs inside subgraph) subgraphOutput2.draw({ ctx: mockCtx, - colorContext: mockColorContext, + colorContext: + mockColorContext as Partial as DefaultConnectionColors, fromSlot: subgraphOutput1, editorAlpha: 1 }) @@ -170,7 +183,8 @@ describe.skip('SubgraphSlot visual feedback', () => { // Draw the SubgraphOutput slot while dragging from a node output with incompatible type subgraphOutput.draw({ ctx: mockCtx, - colorContext: mockColorContext, + colorContext: + mockColorContext as Partial as DefaultConnectionColors, fromSlot: nodeStringOutput, editorAlpha: 1 }) diff --git a/src/lib/litegraph/src/subgraph/SubgraphWidgetPromotion.test.ts b/src/lib/litegraph/src/subgraph/SubgraphWidgetPromotion.test.ts index d47ca186d9..1d609fe017 100644 --- a/src/lib/litegraph/src/subgraph/SubgraphWidgetPromotion.test.ts +++ b/src/lib/litegraph/src/subgraph/SubgraphWidgetPromotion.test.ts @@ -18,7 +18,7 @@ import { function createNodeWithWidget( title: string, widgetType: TWidgetType = 'number', - widgetValue: any = 42, + widgetValue: unknown = 42, slotType: ISlotType = 'number', tooltip?: string ) { diff --git a/src/lib/litegraph/src/utils/textUtils.test.ts b/src/lib/litegraph/src/utils/textUtils.test.ts index 8ca101f610..4d2bbbca0b 100644 --- a/src/lib/litegraph/src/utils/textUtils.test.ts +++ b/src/lib/litegraph/src/utils/textUtils.test.ts @@ -1,3 +1,4 @@ +import type { Mock } from 'vitest' import { describe, expect, it, vi } from 'vitest' import { truncateText } from '@/lib/litegraph/src/litegraph' @@ -5,8 +6,13 @@ import { truncateText } from '@/lib/litegraph/src/litegraph' describe('truncateText', () => { const createMockContext = (charWidth: number = 10) => { return { - measureText: vi.fn((text: string) => ({ width: text.length * charWidth })) - } as unknown as CanvasRenderingContext2D + measureText: vi.fn( + (text: string) => + ({ + width: text.length * charWidth + }) as TextMetrics + ) + } as Partial as CanvasRenderingContext2D } it('should return original text if it fits within maxWidth', () => { @@ -57,7 +63,7 @@ describe('truncateText', () => { // Verify binary search efficiency - should not measure every possible substring // Binary search for 100 chars should take around log2(100) ≈ 7 iterations // Plus a few extra calls for measuring the full text and ellipsis - const callCount = (ctx.measureText as any).mock.calls.length + const callCount = (ctx.measureText as Mock).mock.calls.length expect(callCount).toBeLessThan(20) expect(callCount).toBeGreaterThan(5) }) diff --git a/src/lib/litegraph/src/widgets/ComboWidget.test.ts b/src/lib/litegraph/src/widgets/ComboWidget.test.ts index f080dbab70..e58a61cc8b 100644 --- a/src/lib/litegraph/src/widgets/ComboWidget.test.ts +++ b/src/lib/litegraph/src/widgets/ComboWidget.test.ts @@ -389,8 +389,9 @@ describe('ComboWidget', () => { node.size = [200, 30] const mockContextMenu = vi.fn() - LiteGraph.ContextMenu = - mockContextMenu as unknown as typeof LiteGraph.ContextMenu + LiteGraph.ContextMenu = mockContextMenu as Partial< + typeof LiteGraph.ContextMenu + > as typeof LiteGraph.ContextMenu widget.onClick({ e: mockEvent, node, canvas: mockCanvas }) @@ -426,8 +427,9 @@ describe('ComboWidget', () => { node.size = [200, 30] const mockContextMenu = vi.fn() - LiteGraph.ContextMenu = - mockContextMenu as unknown as typeof LiteGraph.ContextMenu + LiteGraph.ContextMenu = mockContextMenu as Partial< + typeof LiteGraph.ContextMenu + > as typeof LiteGraph.ContextMenu widget.onClick({ e: mockEvent, node, canvas: mockCanvas }) @@ -463,8 +465,9 @@ describe('ComboWidget', () => { .mockImplementation(function (_values, options) { capturedCallback = options.callback }) - LiteGraph.ContextMenu = - mockContextMenu as unknown as typeof LiteGraph.ContextMenu + LiteGraph.ContextMenu = mockContextMenu as Partial< + typeof LiteGraph.ContextMenu + > as typeof LiteGraph.ContextMenu const setValueSpy = vi.spyOn(widget, 'setValue') widget.onClick({ e: mockEvent, node, canvas: mockCanvas }) @@ -506,8 +509,9 @@ describe('ComboWidget', () => { .mockImplementation(function (_values, options) { capturedCallback = options.callback }) - LiteGraph.ContextMenu = - mockContextMenu as unknown as typeof LiteGraph.ContextMenu + LiteGraph.ContextMenu = mockContextMenu as Partial< + typeof LiteGraph.ContextMenu + > as typeof LiteGraph.ContextMenu const setValueSpy = vi.spyOn(widget, 'setValue') widget.onClick({ e: mockEvent, node, canvas: mockCanvas }) @@ -538,8 +542,9 @@ describe('ComboWidget', () => { node.size = [200, 30] const mockContextMenu = vi.fn() - LiteGraph.ContextMenu = - mockContextMenu as unknown as typeof LiteGraph.ContextMenu + LiteGraph.ContextMenu = mockContextMenu as Partial< + typeof LiteGraph.ContextMenu + > as typeof LiteGraph.ContextMenu widget.onClick({ e: mockEvent, node, canvas: mockCanvas }) @@ -572,8 +577,9 @@ describe('ComboWidget', () => { node.size = [200, 30] const mockContextMenu = vi.fn() - LiteGraph.ContextMenu = - mockContextMenu as unknown as typeof LiteGraph.ContextMenu + LiteGraph.ContextMenu = mockContextMenu as Partial< + typeof LiteGraph.ContextMenu + > as typeof LiteGraph.ContextMenu widget.onClick({ e: mockEvent, node, canvas: mockCanvas }) @@ -768,8 +774,9 @@ describe('ComboWidget', () => { .mockImplementation(function () { this.addItem = mockAddItem }) - LiteGraph.ContextMenu = - mockContextMenu as unknown as typeof LiteGraph.ContextMenu + LiteGraph.ContextMenu = mockContextMenu as Partial< + typeof LiteGraph.ContextMenu + > as typeof LiteGraph.ContextMenu widget.onClick({ e: mockEvent, node, canvas: mockCanvas }) // Should show formatted labels in dropdown @@ -831,8 +838,9 @@ describe('ComboWidget', () => { capturedCallback = options.callback this.addItem = mockAddItem }) - LiteGraph.ContextMenu = - mockContextMenu as unknown as typeof LiteGraph.ContextMenu + LiteGraph.ContextMenu = mockContextMenu as Partial< + typeof LiteGraph.ContextMenu + > as typeof LiteGraph.ContextMenu const setValueSpy = vi.spyOn(widget, 'setValue') widget.onClick({ e: mockEvent, node, canvas: mockCanvas }) @@ -885,8 +893,9 @@ describe('ComboWidget', () => { capturedCallback = options.callback this.addItem = mockAddItem }) - LiteGraph.ContextMenu = - mockContextMenu as unknown as typeof LiteGraph.ContextMenu + LiteGraph.ContextMenu = mockContextMenu as Partial< + typeof LiteGraph.ContextMenu + > as typeof LiteGraph.ContextMenu widget.onClick({ e: mockEvent, node, canvas: mockCanvas }) @@ -964,8 +973,9 @@ describe('ComboWidget', () => { .mockImplementation(function () { this.addItem = mockAddItem }) - LiteGraph.ContextMenu = - mockContextMenu as unknown as typeof LiteGraph.ContextMenu + LiteGraph.ContextMenu = mockContextMenu as Partial< + typeof LiteGraph.ContextMenu + > as typeof LiteGraph.ContextMenu widget.onClick({ e: mockEvent, node, canvas: mockCanvas }) @@ -1012,8 +1022,9 @@ describe('ComboWidget', () => { node.size = [200, 30] const mockContextMenu = vi.fn() - LiteGraph.ContextMenu = - mockContextMenu as unknown as typeof LiteGraph.ContextMenu + LiteGraph.ContextMenu = mockContextMenu as Partial< + typeof LiteGraph.ContextMenu + > as typeof LiteGraph.ContextMenu widget.onClick({ e: mockEvent, node, canvas: mockCanvas }) @@ -1051,7 +1062,8 @@ describe('ComboWidget', () => { createMockWidgetConfig({ name: 'mode', value: 'test', - options: { values: null as any } + // @ts-expect-error - Testing with intentionally invalid null value + options: { values: null } }), node ) diff --git a/src/platform/assets/services/assetService.test.ts b/src/platform/assets/services/assetService.test.ts index e126fd66c5..7541a9eecd 100644 --- a/src/platform/assets/services/assetService.test.ts +++ b/src/platform/assets/services/assetService.test.ts @@ -67,7 +67,7 @@ const MOCK_ASSETS = { } as const // Helper functions -function mockApiResponse(assets: any[], options = {}) { +function mockApiResponse(assets: unknown[], options = {}) { const response = { assets, total: assets.length, diff --git a/src/platform/settings/composables/useSettingSearch.test.ts b/src/platform/settings/composables/useSettingSearch.test.ts index ba63256e8d..799eebed5c 100644 --- a/src/platform/settings/composables/useSettingSearch.test.ts +++ b/src/platform/settings/composables/useSettingSearch.test.ts @@ -8,6 +8,17 @@ import { getSettingInfo, useSettingStore } from '@/platform/settings/settingStore' +import type { SettingTreeNode } from '@/platform/settings/settingStore' + +// Test-specific type for mock settings +interface MockSettingParams { + id: string + name: string + type: string + defaultValue: unknown + category?: string[] + deprecated?: boolean +} // Mock dependencies vi.mock('@/i18n', () => ({ @@ -20,8 +31,8 @@ vi.mock('@/platform/settings/settingStore', () => ({ })) describe('useSettingSearch', () => { - let mockSettingStore: any - let mockSettings: any + let mockSettingStore: ReturnType + let mockSettings: Record beforeEach(() => { setActivePinia(createPinia()) @@ -70,11 +81,11 @@ describe('useSettingSearch', () => { // Mock setting store mockSettingStore = { settingsById: mockSettings - } + } as ReturnType vi.mocked(useSettingStore).mockReturnValue(mockSettingStore) // Mock getSettingInfo function - vi.mocked(getSettingInfo).mockImplementation((setting: any) => { + vi.mocked(getSettingInfo).mockImplementation((setting) => { const parts = setting.category || setting.id.split('.') return { category: parts[0] ?? 'Other', @@ -301,8 +312,8 @@ describe('useSettingSearch', () => { const search = useSettingSearch() search.filteredSettingIds.value = ['Category.Setting1', 'Other.Setting3'] - const activeCategory = { label: 'Category' } as any - const results = search.getSearchResults(activeCategory) + const activeCategory: Partial = { label: 'Category' } + const results = search.getSearchResults(activeCategory as SettingTreeNode) expect(results).toEqual([ { diff --git a/src/platform/settings/settingStore.test.ts b/src/platform/settings/settingStore.test.ts index 60d7acd1c4..4f800463c7 100644 --- a/src/platform/settings/settingStore.test.ts +++ b/src/platform/settings/settingStore.test.ts @@ -6,6 +6,7 @@ import { useSettingStore } from '@/platform/settings/settingStore' import type { SettingParams } from '@/platform/settings/types' +import type { Settings } from '@/schemas/apiSchema' import { api } from '@/scripts/api' import { app } from '@/scripts/app' @@ -45,7 +46,9 @@ describe('useSettingStore', () => { describe('loadSettingValues', () => { it('should load settings from API', async () => { const mockSettings = { 'test.setting': 'value' } - vi.mocked(api.getSettings).mockResolvedValue(mockSettings as any) + vi.mocked(api.getSettings).mockResolvedValue( + mockSettings as Partial as Settings + ) await store.loadSettingValues() diff --git a/src/platform/telemetry/types.ts b/src/platform/telemetry/types.ts index 07daf3874f..2ce9c7f0f9 100644 --- a/src/platform/telemetry/types.ts +++ b/src/platform/telemetry/types.ts @@ -37,6 +37,13 @@ export interface SurveyResponses { making?: string[] } +export interface SurveyResponsesNormalized extends SurveyResponses { + industry_normalized?: string + industry_raw?: string + useCase_normalized?: string + useCase_raw?: string +} + /** * Run button tracking properties */ diff --git a/src/platform/telemetry/utils/__tests__/surveyNormalization.test.ts b/src/platform/telemetry/utils/__tests__/surveyNormalization.test.ts index ea85bd6eba..059f7d67db 100644 --- a/src/platform/telemetry/utils/__tests__/surveyNormalization.test.ts +++ b/src/platform/telemetry/utils/__tests__/surveyNormalization.test.ts @@ -328,9 +328,9 @@ describe('normalizeIndustry', () => { }) it('should handle null and invalid inputs', () => { - expect(normalizeIndustry(null as any)).toBe('Other / Undefined') - expect(normalizeIndustry(undefined as any)).toBe('Other / Undefined') - expect(normalizeIndustry(123 as any)).toBe('Other / Undefined') + expect(normalizeIndustry(null)).toBe('Other / Undefined') + expect(normalizeIndustry(undefined)).toBe('Other / Undefined') + expect(normalizeIndustry(123)).toBe('Other / Undefined') }) }) @@ -508,7 +508,7 @@ describe('normalizeUseCase', () => { expect(normalizeUseCase('none')).toBe('Other / Undefined') expect(normalizeUseCase('undefined')).toBe('Other / Undefined') expect(normalizeUseCase('')).toBe('Other / Undefined') - expect(normalizeUseCase(null as any)).toBe('Other / Undefined') + expect(normalizeUseCase(null)).toBe('Other / Undefined') }) }) diff --git a/src/platform/telemetry/utils/surveyNormalization.ts b/src/platform/telemetry/utils/surveyNormalization.ts index fdffd62c7b..ba126f9056 100644 --- a/src/platform/telemetry/utils/surveyNormalization.ts +++ b/src/platform/telemetry/utils/surveyNormalization.ts @@ -6,6 +6,7 @@ * Uses Fuse.js for fuzzy matching against category keywords. */ import Fuse from 'fuse.js' +import type { SurveyResponses, SurveyResponsesNormalized } from '../types' interface CategoryMapping { name: string @@ -583,21 +584,19 @@ export function normalizeUseCase(rawUseCase: unknown): string { * Apply normalization to survey responses * Creates both normalized and raw versions of responses */ -export function normalizeSurveyResponses(responses: { - industry?: string - useCase?: string - [key: string]: any -}) { - const normalized = { ...responses } +export function normalizeSurveyResponses( + responses: SurveyResponses +): SurveyResponsesNormalized { + const normalized: SurveyResponsesNormalized = { ...responses } // Normalize industry - if (responses.industry) { + if (typeof responses.industry === 'string') { normalized.industry_normalized = normalizeIndustry(responses.industry) normalized.industry_raw = responses.industry } // Normalize use case - if (responses.useCase) { + if (typeof responses.useCase === 'string') { normalized.useCase_normalized = normalizeUseCase(responses.useCase) normalized.useCase_raw = responses.useCase }