mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-03 12:42:01 +00:00
## Summary Adds custom status messages that are shown under the previews in order to provide additional progress feedback to the user Nodes matching the words: Save, Preview -> Saving Load, Loader -> Loading Encode -> Encoding Decode -> Decoding Compile, Conditioning, Merge, -> Processing Upscale, Resize -> Resizing ToVideo -> Generating video Specific nodes: KSampler, KSamplerAdvanced, SamplerCustom, SamplerCustomAdvanced -> Generating Video Slice, GetVideoComponents, CreateVideo -> Processing video TrainLoraNode -> Training ## Changes - **What**: - add specific node lookups for non-easily matchable patterns - add regex based matching for common patterns - show on both latent preview & skeleton preview - allow app mode workflow authors to override status with custom property `Execution Message` (no UI for doing this) ## Review Focus This is purely pattern/lookup based, in future we could update the backend node schema to allow nodes to define their own status key. ## Screenshots (if applicable) <img width="757" height="461" alt="image" src="https://github.com/user-attachments/assets/2b32cc54-c4e7-4aeb-912d-b39ac8428be7" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-10369-feat-App-mode-add-execution-status-messages-32a6d73d3650814e8ca2da5eb33f3b65) by [Unito](https://www.unito.io) --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
101 lines
3.1 KiB
TypeScript
101 lines
3.1 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
|
|
import type { ComfyNodeDefImpl } from '@/stores/nodeDefStore'
|
|
|
|
import { getExecutionStatusMessage } from './getExecutionStatusMessage'
|
|
|
|
// Pass-through t so we can assert the i18n key
|
|
const t = (key: string) => key
|
|
|
|
describe('getExecutionStatusMessage', () => {
|
|
describe('custom messages', () => {
|
|
it('returns custom message from properties when set', () => {
|
|
expect(
|
|
getExecutionStatusMessage(t, 'KSampler', null, {
|
|
'Execution Message': 'custom status'
|
|
})
|
|
).toBe('custom status')
|
|
})
|
|
|
|
it('ignores empty or whitespace-only custom message', () => {
|
|
expect(
|
|
getExecutionStatusMessage(t, 'KSampler', null, {
|
|
'Execution Message': ' '
|
|
})
|
|
).toBe('execution.generating')
|
|
})
|
|
})
|
|
|
|
describe('API nodes', () => {
|
|
it('returns processing for API nodes', () => {
|
|
const apiDef = { api_node: true } as ComfyNodeDefImpl
|
|
expect(getExecutionStatusMessage(t, 'SomeApiNode', apiDef)).toBe(
|
|
'execution.processing'
|
|
)
|
|
})
|
|
|
|
it('statusMap takes precedence over api_node flag', () => {
|
|
const apiDef = { api_node: true } as ComfyNodeDefImpl
|
|
expect(getExecutionStatusMessage(t, 'KSampler', apiDef)).toBe(
|
|
'execution.generating'
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('Node type matching', () => {
|
|
it('does not match partial PascalCase words', () => {
|
|
expect(getExecutionStatusMessage(t, 'Loads')).toBeNull()
|
|
})
|
|
|
|
it('matches identifier mid-string at PascalCase boundary', () => {
|
|
expect(getExecutionStatusMessage(t, 'CompositeSaveImage')).toBe(
|
|
'execution.saving'
|
|
)
|
|
})
|
|
|
|
it('matches identifier followed by non-letter characters', () => {
|
|
expect(getExecutionStatusMessage(t, 'Save_V2')).toBe('execution.saving')
|
|
expect(getExecutionStatusMessage(t, 'LoadImage🐍')).toBe(
|
|
'execution.loading'
|
|
)
|
|
})
|
|
|
|
const testNodeTypes: [string, string[]][] = [
|
|
['generating', ['KSampler', 'SamplerCustomAdvanced']],
|
|
[
|
|
'saving',
|
|
['SaveImage', 'SaveAnimatedWEBP', 'PreviewImage', 'MaskPreview']
|
|
],
|
|
['loading', ['LoadImage', 'VAELoader', 'CheckpointLoaderSimple']],
|
|
[
|
|
'encoding',
|
|
['VAEEncode', 'StableCascade_StageC_VAEEncode', 'CLIPTextEncode']
|
|
],
|
|
['decoding', ['VAEDecode', 'VAEDecodeHunyuan3D']],
|
|
[
|
|
'resizing',
|
|
['ImageUpscaleWithModel', 'LatentUpscale', 'ResizeImageMaskNode']
|
|
],
|
|
[
|
|
'processing',
|
|
['TorchCompileModel', 'SVD_img2vid_Conditioning', 'ModelMergeSimple']
|
|
],
|
|
['generatingVideo', ['WanImageToVideo', 'WanFunControlToVideo']],
|
|
['processingVideo', ['Video Slice', 'CreateVideo']],
|
|
['training', ['TrainLoraNode']]
|
|
]
|
|
|
|
it.for(
|
|
testNodeTypes.flatMap(([status, nodes]) =>
|
|
nodes.map((node) => [status, node] as const)
|
|
)
|
|
)('%s ← %s', ([status, nodeType]) => {
|
|
expect(getExecutionStatusMessage(t, nodeType)).toBe(`execution.${status}`)
|
|
})
|
|
})
|
|
|
|
it('returns null for nodes matching no pattern', () => {
|
|
expect(getExecutionStatusMessage(t, 'PrimitiveString')).toBeNull()
|
|
})
|
|
})
|