Files
ComfyUI_frontend/src/lib/litegraph/src/widgets/BaseWidget.test.ts
Alexander Brown 661e3d7949 test: migrate as unknown as to @total-typescript/shoehorn (#10761)
*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>
2026-03-30 19:20:18 -07:00

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)
})
})
})