mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-26 19:09:52 +00:00
Continuation of #6034 with - Updated synchronization for seed - Properly truncates the displayed widget value for the button - Synchronizes control after generate state with litegraph and allows for serialization Several issues from original PR have not (yet) been addressed, but are likely better moved to future PR - fix step value being 10 (legacy system) - ensure it works with COMBO (Fixed in #7095) - ensure it works with FLOAT (Fixed in #7095) - either implement or remove the config button functionality - think it should open settings? <img width="280" height="694" alt="image" src="https://github.com/user-attachments/assets/f36f1cb0-237d-4bfc-bff1-e4976775cf98" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6985-Support-control-after-generate-in-vue-2b86d73d365081d8b01ce489d887ff00) by [Unito](https://www.unito.io) --------- Co-authored-by: bymyself <cbyrne@comfy.org> Co-authored-by: github-actions <github-actions@github.com>
192 lines
6.5 KiB
TypeScript
192 lines
6.5 KiB
TypeScript
import { setActivePinia } from 'pinia'
|
|
import { createTestingPinia } from '@pinia/testing'
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
import {
|
|
getComponent,
|
|
isEssential,
|
|
shouldRenderAsVue,
|
|
FOR_TESTING
|
|
} from '@/renderer/extensions/vueNodes/widgets/registry/widgetRegistry'
|
|
import type { SafeWidgetData } from '@/composables/graph/useGraphNodeManager'
|
|
|
|
const {
|
|
WidgetAudioUI,
|
|
WidgetButton,
|
|
WidgetColorPicker,
|
|
WidgetInputNumber,
|
|
WidgetInputText,
|
|
WidgetMarkdown,
|
|
WidgetSelect,
|
|
WidgetTextarea,
|
|
WidgetToggleSwitch
|
|
} = FOR_TESTING
|
|
|
|
vi.mock('@/stores/queueStore', () => ({
|
|
useQueueStore: vi.fn(() => ({
|
|
historyTasks: []
|
|
}))
|
|
}))
|
|
|
|
// Mock the settings store for components that might use it
|
|
vi.mock('@/platform/settings/settingStore', () => ({
|
|
useSettingStore: () => ({
|
|
get: vi.fn(() => 'before')
|
|
})
|
|
}))
|
|
|
|
describe('widgetRegistry', () => {
|
|
beforeEach(() => {
|
|
setActivePinia(createTestingPinia())
|
|
vi.clearAllMocks()
|
|
})
|
|
describe('getComponent', () => {
|
|
// Test number type mappings
|
|
describe('number types', () => {
|
|
it('should map int types to slider widget', () => {
|
|
expect(getComponent('int', 'bar')).toBe(WidgetInputNumber)
|
|
expect(getComponent('INT', 'bar')).toBe(WidgetInputNumber)
|
|
})
|
|
|
|
it('should map float types to slider widget', () => {
|
|
expect(getComponent('float', 'cfg')).toBe(WidgetInputNumber)
|
|
expect(getComponent('FLOAT', 'cfg')).toBe(WidgetInputNumber)
|
|
expect(getComponent('number', 'cfg')).toBe(WidgetInputNumber)
|
|
expect(getComponent('slider', 'cfg')).toBe(WidgetInputNumber)
|
|
})
|
|
})
|
|
|
|
// Test text type mappings
|
|
describe('text types', () => {
|
|
it('should map text variations to input text widget', () => {
|
|
expect(getComponent('text', 'text')).toBe(WidgetInputText)
|
|
expect(getComponent('string', 'text')).toBe(WidgetInputText)
|
|
expect(getComponent('STRING', 'text')).toBe(WidgetInputText)
|
|
})
|
|
|
|
it('should map multiline text types to textarea widget', () => {
|
|
expect(getComponent('multiline', 'text')).toBe(WidgetTextarea)
|
|
expect(getComponent('textarea', 'text')).toBe(WidgetTextarea)
|
|
expect(getComponent('TEXTAREA', 'text')).toBe(WidgetTextarea)
|
|
expect(getComponent('customtext', 'text')).toBe(WidgetTextarea)
|
|
})
|
|
|
|
it('should map markdown to markdown widget', () => {
|
|
expect(getComponent('MARKDOWN', 'text')).toBe(WidgetMarkdown)
|
|
expect(getComponent('markdown', 'text')).toBe(WidgetMarkdown)
|
|
})
|
|
})
|
|
|
|
// Test selection type mappings
|
|
describe('selection types', () => {
|
|
it('should map combo types to select widget', () => {
|
|
expect(getComponent('combo', 'image')).toBe(WidgetSelect)
|
|
expect(getComponent('COMBO', 'video')).toBe(WidgetSelect)
|
|
})
|
|
})
|
|
|
|
// Test boolean type mappings
|
|
describe('boolean types', () => {
|
|
it('should map boolean types to toggle switch widget', () => {
|
|
expect(getComponent('toggle', 'image')).toBe(WidgetToggleSwitch)
|
|
expect(getComponent('boolean', 'image')).toBe(WidgetToggleSwitch)
|
|
expect(getComponent('BOOLEAN', 'image')).toBe(WidgetToggleSwitch)
|
|
})
|
|
})
|
|
|
|
// Test advanced widget mappings
|
|
describe('advanced widgets', () => {
|
|
it('should map color types to color picker widget', () => {
|
|
expect(getComponent('color', 'color')).toBe(WidgetColorPicker)
|
|
expect(getComponent('COLOR', 'color')).toBe(WidgetColorPicker)
|
|
})
|
|
|
|
it('should map button types to button widget', () => {
|
|
expect(getComponent('button', '')).toBe(WidgetButton)
|
|
expect(getComponent('BUTTON', '')).toBe(WidgetButton)
|
|
})
|
|
})
|
|
|
|
// Test fallback behavior
|
|
describe('fallback behavior', () => {
|
|
it('should return null for unknown types', () => {
|
|
expect(getComponent('unknown', 'unknown')).toBe(null)
|
|
expect(getComponent('custom_widget', 'custom_widget')).toBe(null)
|
|
expect(getComponent('', '')).toBe(null)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('shouldRenderAsVue', () => {
|
|
it('should return false for widgets marked as canvas-only', () => {
|
|
const widget = { type: 'text', options: { canvasOnly: true } }
|
|
expect(shouldRenderAsVue(widget)).toBe(false)
|
|
})
|
|
|
|
it('should return false for widgets without a type', () => {
|
|
const widget = { options: {} }
|
|
expect(shouldRenderAsVue(widget)).toBe(false)
|
|
})
|
|
|
|
it('should return true for widgets with mapped types', () => {
|
|
expect(shouldRenderAsVue({ type: 'text' })).toBe(true)
|
|
expect(shouldRenderAsVue({ type: 'int' })).toBe(true)
|
|
expect(shouldRenderAsVue({ type: 'combo' })).toBe(true)
|
|
})
|
|
|
|
it('should respect options while checking type', () => {
|
|
const widget: Partial<SafeWidgetData> = {
|
|
type: 'text',
|
|
options: { precision: 5 }
|
|
}
|
|
expect(shouldRenderAsVue(widget)).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe('isEssential', () => {
|
|
it('should identify essential widget types', () => {
|
|
expect(isEssential('int')).toBe(true)
|
|
expect(isEssential('INT')).toBe(true)
|
|
expect(isEssential('float')).toBe(true)
|
|
expect(isEssential('FLOAT')).toBe(true)
|
|
expect(isEssential('boolean')).toBe(true)
|
|
expect(isEssential('BOOLEAN')).toBe(true)
|
|
expect(isEssential('combo')).toBe(true)
|
|
expect(isEssential('COMBO')).toBe(true)
|
|
})
|
|
|
|
it('should identify non-essential widget types', () => {
|
|
expect(isEssential('button')).toBe(false)
|
|
expect(isEssential('color')).toBe(false)
|
|
expect(isEssential('chart')).toBe(false)
|
|
expect(isEssential('fileupload')).toBe(false)
|
|
})
|
|
|
|
it('should return false for unknown types', () => {
|
|
expect(isEssential('unknown')).toBe(false)
|
|
expect(isEssential('')).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('edge cases', () => {
|
|
it('should handle widgets with empty options', () => {
|
|
const widget = { type: 'text', options: {} }
|
|
expect(shouldRenderAsVue(widget)).toBe(true)
|
|
})
|
|
|
|
it('should handle case sensitivity correctly through aliases', () => {
|
|
// Test that both lowercase and uppercase work
|
|
expect(getComponent('string', '')).toBe(WidgetInputText)
|
|
expect(getComponent('STRING', '')).toBe(WidgetInputText)
|
|
expect(getComponent('combo', '')).toBe(WidgetSelect)
|
|
expect(getComponent('COMBO', '')).toBe(WidgetSelect)
|
|
})
|
|
|
|
it('should handle combo additional widgets', () => {
|
|
// Test that both lowercase and uppercase work
|
|
expect(getComponent('combo', 'audio')).toBe(WidgetAudioUI)
|
|
expect(getComponent('combo', 'image')).toBe(WidgetSelect)
|
|
})
|
|
})
|
|
})
|