Files
ComfyUI_frontend/src/constants/essentialsDisplayNames.test.ts
Christian Byrne 54f13930a4 feat: add display name mappings for Essentials tab nodes (#9072)
## Summary

Add frontend-only display name mappings for nodes shown in the
Essentials tab, plus parse the new `essentials_category` field from the
backend.

## Changes

- **What**: Created `src/constants/essentialsDisplayNames.ts` with a
static mapping of node names to user-friendly display names (e.g.
`CLIPTextEncode` → "Text", `ImageScale` → "Resize Image"). Regular nodes
use exact name matching; blueprint nodes use prefix matching since their
filenames include model-specific suffixes. Integrated into
`NodeLibrarySidebarTab.vue`'s `renderedRoot` computed for leaf node
labels with fallback to `display_name`. Added `essentials_category`
(z.string().optional()) to the node def schema and `ComfyNodeDefImpl` to
parse the field already sent by the backend (PR #12357).

## Review Focus

Display names are resolved only in the Essentials tab tree view
(`NodeLibrarySidebarTab.vue`), not globally, to avoid side effects on
search, bookmarks, or other views. Blueprint prefix matching is ordered
longest-first so more specific prefixes (e.g. `image_inpainting_`) match
before shorter ones (e.g. `image_edit`).

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9072-feat-add-display-name-mappings-for-Essentials-tab-nodes-30f6d73d3650817c9acdc9b0315ed0be)
by [Unito](https://www.unito.io)
2026-02-22 01:03:15 -08:00

106 lines
3.5 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest'
vi.mock('@/i18n', () => ({
t: vi.fn((key: string) => key)
}))
import { resolveEssentialsDisplayName } from '@/constants/essentialsDisplayNames'
describe('resolveEssentialsDisplayName', () => {
describe('exact name matches', () => {
it.each([
['LoadImage', 'essentials.loadImage'],
['SaveImage', 'essentials.saveImage'],
['PrimitiveStringMultiline', 'essentials.text'],
['ImageScale', 'essentials.resizeImage'],
['LoraLoader', 'essentials.loadStyleLora'],
['OpenAIChatNode', 'essentials.textGenerationLLM'],
['RecraftRemoveBackgroundNode', 'essentials.removeBackground'],
['ImageCompare', 'essentials.imageCompare'],
['StabilityTextToAudio', 'essentials.musicGeneration'],
['BatchImagesNode', 'essentials.batchImage'],
['Video Slice', 'essentials.extractFrame'],
['KlingLipSyncAudioToVideoNode', 'essentials.lipsync'],
['KlingLipSyncTextToVideoNode', 'essentials.lipsync']
])('%s -> %s', (name, expected) => {
expect(resolveEssentialsDisplayName({ name })).toBe(expected)
})
})
describe('3D API node alternatives', () => {
it.each([
['TencentTextToModelNode', 'essentials.textTo3DModel'],
['MeshyTextToModelNode', 'essentials.textTo3DModel'],
['TripoTextToModelNode', 'essentials.textTo3DModel'],
['TencentImageToModelNode', 'essentials.imageTo3DModel'],
['MeshyImageToModelNode', 'essentials.imageTo3DModel'],
['TripoImageToModelNode', 'essentials.imageTo3DModel']
])('%s -> %s', (name, expected) => {
expect(resolveEssentialsDisplayName({ name })).toBe(expected)
})
})
describe('blueprint prefix matches', () => {
it.each([
[
'SubgraphBlueprint.text_to_image_flux_schnell.json',
'essentials.textToImage'
],
['SubgraphBlueprint.text_to_image_sd15.json', 'essentials.textToImage'],
[
'SubgraphBlueprint.image_edit_something.json',
'essentials.imageToImage'
],
['SubgraphBlueprint.pose_to_image_v2.json', 'essentials.poseToImage'],
[
'SubgraphBlueprint.canny_to_image_z_image_turbo.json',
'essentials.cannyToImage'
],
[
'SubgraphBlueprint.depth_to_image_z_image_turbo.json',
'essentials.depthToImage'
],
['SubgraphBlueprint.text_to_video_ltx.json', 'essentials.textToVideo'],
['SubgraphBlueprint.image_to_video_wan.json', 'essentials.imageToVideo'],
[
'SubgraphBlueprint.pose_to_video_ltx_2_0.json',
'essentials.poseToVideo'
],
[
'SubgraphBlueprint.canny_to_video_ltx_2_0.json',
'essentials.cannyToVideo'
],
[
'SubgraphBlueprint.depth_to_video_ltx_2_0.json',
'essentials.depthToVideo'
],
[
'SubgraphBlueprint.image_inpainting_qwen_image_instantx.json',
'essentials.inpaintImage'
],
[
'SubgraphBlueprint.image_outpainting_qwen_image_instantx.json',
'essentials.outpaintImage'
]
])('%s -> %s', (name, expected) => {
expect(resolveEssentialsDisplayName({ name })).toBe(expected)
})
})
describe('unmapped nodes', () => {
it('returns undefined for unknown node names', () => {
expect(resolveEssentialsDisplayName({ name: 'SomeRandomNode' })).toBe(
undefined
)
})
it('returns undefined for unknown blueprint prefixes', () => {
expect(
resolveEssentialsDisplayName({
name: 'SubgraphBlueprint.unknown_workflow.json'
})
).toBe(undefined)
})
})
})