Files
ComfyUI_frontend/browser_tests/helpers/promotedWidgets.ts
Alexander Brown 34a77e5016 test: harden subgraph test coverage and remove low-value tests (#9967)
## Summary

Harden subgraph test coverage: remove low-value change-detector tests,
consolidate fixtures, add behavioral coverage, and fix test
infrastructure issues. Includes minor production code corrections
discovered during test hardening.

## Changes

- **What**: Comprehensive subgraph test suite overhaul across 6 phases
  - Removed change-detector tests and redundant assertions
- Consolidated fixture helpers into `subgraphHelpers.ts` /
`subgraphFixtures.ts`
  - Added Pinia initialization and fixture reset to all test files
  - Fixed barrel import violations (circular dependency prevention)
  - Added behavioral coverage for slot connections, events, edge cases
  - Added E2E helper and smoke test for subgraph promotion
  - Exported `SubgraphSlotBase` from litegraph barrel for test access
- **Production code changes** (minor correctness fixes found during
testing):
- `resolveSubgraphInputLink.ts`: iterate forward (first-connected-wins)
to match `_resolveLinkedPromotionBySubgraphInput`
- `promotionSchema.ts`: return `[]` instead of throwing on invalid
`proxyWidgets`; console.warn always (not DEV-only)
  - `LGraph.ts`: disconnect-after-veto ordering fix
  - `litegraph.ts`: barrel export swap for `SubgraphSlotBase`
- **Stats**: 349 tests passing, 0 skipped across 26 test files

## Review Focus

- Tests that merely asserted default property values were deleted
(change detectors)
- Fixture state is now reset via `resetSubgraphFixtureState()` in
`beforeEach`
- All imports use `@/lib/litegraph/src/litegraph` barrel to avoid
circular deps
- Production changes are small and directly motivated by test findings

---------

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: bymyself <cbyrne@comfy.org>
2026-03-17 19:03:18 +00:00

92 lines
2.4 KiB
TypeScript

import type { ComfyPage } from '../fixtures/ComfyPage'
export type PromotedWidgetEntry = [string, string]
export interface PromotedWidgetSnapshot {
proxyWidgets: PromotedWidgetEntry[]
widgetNames: string[]
}
export function isPromotedWidgetEntry(
entry: unknown
): entry is PromotedWidgetEntry {
return (
Array.isArray(entry) &&
entry.length === 2 &&
typeof entry[0] === 'string' &&
typeof entry[1] === 'string'
)
}
export function normalizePromotedWidgets(
value: unknown
): PromotedWidgetEntry[] {
if (!Array.isArray(value)) return []
return value.filter(isPromotedWidgetEntry)
}
export async function getPromotedWidgets(
comfyPage: ComfyPage,
nodeId: string
): Promise<PromotedWidgetEntry[]> {
const raw = await comfyPage.page.evaluate((id) => {
const node = window.app!.canvas.graph!.getNodeById(id)
return node?.properties?.proxyWidgets ?? []
}, nodeId)
return normalizePromotedWidgets(raw)
}
export async function getPromotedWidgetSnapshot(
comfyPage: ComfyPage,
nodeId: string
): Promise<PromotedWidgetSnapshot> {
const raw = await comfyPage.page.evaluate((id) => {
const node = window.app!.canvas.graph!.getNodeById(id)
return {
proxyWidgets: node?.properties?.proxyWidgets ?? [],
widgetNames: (node?.widgets ?? []).map((widget) => widget.name)
}
}, nodeId)
return {
proxyWidgets: normalizePromotedWidgets(raw.proxyWidgets),
widgetNames: Array.isArray(raw.widgetNames)
? raw.widgetNames.filter(
(name): name is string => typeof name === 'string'
)
: []
}
}
export async function getPromotedWidgetNames(
comfyPage: ComfyPage,
nodeId: string
): Promise<string[]> {
const promotedWidgets = await getPromotedWidgets(comfyPage, nodeId)
return promotedWidgets.map(([, widgetName]) => widgetName)
}
export async function getPromotedWidgetCount(
comfyPage: ComfyPage,
nodeId: string
): Promise<number> {
const promotedWidgets = await getPromotedWidgets(comfyPage, nodeId)
return promotedWidgets.length
}
export async function getPromotedWidgetCountByName(
comfyPage: ComfyPage,
nodeId: string,
widgetName: string
): Promise<number> {
return comfyPage.page.evaluate(
([id, name]) => {
const node = window.app!.canvas.graph!.getNodeById(id)
const widgets = node?.widgets ?? []
return widgets.filter((widget) => widget.name === name).length
},
[nodeId, widgetName] as const
)
}