mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-19 22:09:37 +00:00
*PR Created by the Glary-Bot Agent* --- ## Summary - Replace all `as unknown as Type` assertions in 59 unit test files with type-safe `@total-typescript/shoehorn` functions - Use `fromPartial<Type>()` for partial mock objects where deep-partial type-checks (21 files) - Use `fromAny<Type>()` for fundamentally incompatible types: null, undefined, primitives, variables, class expressions, and mocks with test-specific extra properties that `PartialDeepObject` rejects (remaining files) - All explicit type parameters preserved so TypeScript return types are correct - Browser test `.spec.ts` files excluded (shoehorn unavailable in `page.evaluate` browser context) ## Verification - `pnpm typecheck` ✅ - `pnpm lint` ✅ - `pnpm format` ✅ - Pre-commit hooks passed (format + oxlint + eslint + typecheck) - Migrated test files verified passing (ran representative subset) - No test behavior changes — only type assertion syntax changed - No UI changes — screenshots not applicable ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-10761-test-migrate-as-unknown-as-to-total-typescript-shoehorn-3336d73d365081f6b8adc44db5dcc380) by [Unito](https://www.unito.io) --------- Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> Co-authored-by: Amp <amp@ampcode.com>
219 lines
6.6 KiB
TypeScript
219 lines
6.6 KiB
TypeScript
import { createTestingPinia } from '@pinia/testing'
|
|
import { fromAny } from '@total-typescript/shoehorn'
|
|
import { setActivePinia } from 'pinia'
|
|
import { beforeEach, describe, expect, it } from 'vitest'
|
|
|
|
import { LGraph, LGraphNode } from '@/lib/litegraph/src/litegraph'
|
|
import type { INumericWidget } from '@/lib/litegraph/src/types/widgets'
|
|
import { NumberWidget } from '@/lib/litegraph/src/widgets/NumberWidget'
|
|
import { useWidgetValueStore } from '@/stores/widgetValueStore'
|
|
|
|
function createTestWidget(
|
|
node: LGraphNode,
|
|
overrides: Partial<INumericWidget> = {}
|
|
): NumberWidget {
|
|
return new NumberWidget(
|
|
{
|
|
type: 'number',
|
|
name: 'testWidget',
|
|
value: 42,
|
|
options: { min: 0, max: 100 },
|
|
y: 0,
|
|
...overrides
|
|
},
|
|
node
|
|
)
|
|
}
|
|
|
|
describe('BaseWidget store integration', () => {
|
|
let graph: LGraph
|
|
let node: LGraphNode
|
|
let store: ReturnType<typeof useWidgetValueStore>
|
|
|
|
beforeEach(() => {
|
|
setActivePinia(createTestingPinia({ stubActions: false }))
|
|
store = useWidgetValueStore()
|
|
graph = new LGraph()
|
|
node = new LGraphNode('TestNode')
|
|
node.id = 1
|
|
graph.add(node)
|
|
})
|
|
|
|
describe('metadata properties before registration', () => {
|
|
it('uses internal values when not registered', () => {
|
|
const widget = createTestWidget(node, {
|
|
label: 'My Label',
|
|
hidden: true,
|
|
disabled: true,
|
|
advanced: true
|
|
})
|
|
|
|
expect(widget.label).toBe('My Label')
|
|
expect(widget.hidden).toBe(true)
|
|
expect(widget.disabled).toBe(true)
|
|
expect(widget.advanced).toBe(true)
|
|
})
|
|
|
|
it('allows setting properties without store', () => {
|
|
const widget = createTestWidget(node)
|
|
|
|
widget.label = 'New Label'
|
|
widget.hidden = true
|
|
widget.disabled = true
|
|
widget.advanced = true
|
|
|
|
expect(widget.label).toBe('New Label')
|
|
expect(widget.hidden).toBe(true)
|
|
expect(widget.disabled).toBe(true)
|
|
expect(widget.advanced).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe('metadata properties after registration', () => {
|
|
it('reads from store when registered', () => {
|
|
const widget = createTestWidget(node, {
|
|
name: 'storeWidget',
|
|
label: 'Store Label',
|
|
hidden: true,
|
|
disabled: true,
|
|
advanced: true
|
|
})
|
|
widget.setNodeId(1)
|
|
|
|
expect(widget.label).toBe('Store Label')
|
|
expect(widget.hidden).toBe(true)
|
|
expect(widget.disabled).toBe(true)
|
|
expect(widget.advanced).toBe(true)
|
|
})
|
|
|
|
it('writes to store when registered', () => {
|
|
const widget = createTestWidget(node, { name: 'writeWidget' })
|
|
widget.setNodeId(1)
|
|
|
|
widget.label = 'Updated Label'
|
|
widget.hidden = true
|
|
widget.disabled = true
|
|
widget.advanced = true
|
|
|
|
const state = store.getWidget(graph.id, 1, 'writeWidget')
|
|
expect(state?.label).toBe('Updated Label')
|
|
expect(state?.disabled).toBe(true)
|
|
|
|
expect(widget.hidden).toBe(true)
|
|
expect(widget.advanced).toBe(true)
|
|
})
|
|
|
|
it('syncs value with store', () => {
|
|
const widget = createTestWidget(node, { name: 'valueWidget', value: 42 })
|
|
widget.setNodeId(1)
|
|
|
|
widget.value = 99
|
|
expect(store.getWidget(graph.id, 1, 'valueWidget')?.value).toBe(99)
|
|
|
|
const state = store.getWidget(graph.id, 1, 'valueWidget')!
|
|
state.value = 55
|
|
expect(widget.value).toBe(55)
|
|
})
|
|
})
|
|
|
|
describe('automatic registration via setNodeId', () => {
|
|
it('registers widget with all metadata', () => {
|
|
const widget = createTestWidget(node, {
|
|
name: 'autoRegWidget',
|
|
value: 100,
|
|
label: 'Auto Label',
|
|
hidden: true,
|
|
disabled: true,
|
|
advanced: true
|
|
})
|
|
widget.setNodeId(1)
|
|
|
|
const state = store.getWidget(graph.id, 1, 'autoRegWidget')
|
|
expect(state).toBeDefined()
|
|
expect(state?.nodeId).toBe(1)
|
|
expect(state?.name).toBe('autoRegWidget')
|
|
expect(state?.type).toBe('number')
|
|
expect(state?.value).toBe(100)
|
|
expect(state?.label).toBe('Auto Label')
|
|
expect(state?.disabled).toBe(true)
|
|
expect(state?.options).toEqual({ min: 0, max: 100 })
|
|
|
|
expect(widget.hidden).toBe(true)
|
|
expect(widget.advanced).toBe(true)
|
|
})
|
|
|
|
it('registers widget with default metadata values', () => {
|
|
const widget = createTestWidget(node, { name: 'defaultsWidget' })
|
|
widget.setNodeId(1)
|
|
|
|
const state = store.getWidget(graph.id, 1, 'defaultsWidget')
|
|
expect(state).toBeDefined()
|
|
expect(state?.disabled).toBe(false)
|
|
expect(state?.label).toBeUndefined()
|
|
|
|
expect(widget.hidden).toBeUndefined()
|
|
expect(widget.advanced).toBeUndefined()
|
|
})
|
|
|
|
it('registers widget value accessible via getWidget', () => {
|
|
const widget = createTestWidget(node, { name: 'valuesWidget', value: 77 })
|
|
widget.setNodeId(1)
|
|
|
|
expect(store.getWidget(graph.id, 1, 'valuesWidget')?.value).toBe(77)
|
|
})
|
|
})
|
|
|
|
describe('DOM widget value registration', () => {
|
|
it('registers value from getter when value property is overridden', () => {
|
|
const defaultValue = 'You are an expert image-generation engine.'
|
|
const widget = createTestWidget(node, {
|
|
name: 'system_prompt',
|
|
value: fromAny<number, unknown>(undefined)
|
|
})
|
|
|
|
// Simulate what addDOMWidget does: override value with getter/setter
|
|
// that falls back to a default (like inputEl.value for textarea widgets)
|
|
Object.defineProperty(widget, 'value', {
|
|
get() {
|
|
const graphId = widget.node.graph?.rootGraph.id
|
|
if (!graphId) return defaultValue
|
|
const state = store.getWidget(graphId, node.id, 'system_prompt')
|
|
return (state?.value as string) ?? defaultValue
|
|
},
|
|
set(v: string) {
|
|
const graphId = widget.node.graph?.rootGraph.id
|
|
if (!graphId) return
|
|
const state = store.getWidget(graphId, node.id, 'system_prompt')
|
|
if (state) state.value = v
|
|
}
|
|
})
|
|
|
|
widget.setNodeId(node.id)
|
|
|
|
const state = store.getWidget(graph.id, node.id, 'system_prompt')
|
|
expect(state?.value).toBe(defaultValue)
|
|
})
|
|
})
|
|
|
|
describe('fallback behavior', () => {
|
|
it('uses internal value before registration', () => {
|
|
const widget = createTestWidget(node, {
|
|
name: 'fallbackWidget',
|
|
label: 'Internal'
|
|
})
|
|
// Widget not yet registered - should use internal value
|
|
expect(widget.label).toBe('Internal')
|
|
})
|
|
|
|
it('handles undefined values correctly', () => {
|
|
const widget = createTestWidget(node)
|
|
widget.setNodeId(1)
|
|
|
|
widget.disabled = undefined
|
|
|
|
const state = store.getWidget(graph.id, 1, 'testWidget')
|
|
expect(state?.disabled).toBe(false)
|
|
})
|
|
})
|
|
})
|