mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-12 00:20:15 +00:00
## Summary Add support for displaying custom on/off labels for boolean toggle widgets, matching the behavior in litegraph mode. ## Screenshots before - litegraph <img width="1232" height="600" alt="image" src="https://github.com/user-attachments/assets/aae91acd-4b6b-4a89-aded-c5445e352006" /> before - vueNodes <img width="869" height="584" alt="image" src="https://github.com/user-attachments/assets/a69dc71e-45f7-4941-911f-f037a2b1c5c2" /> after - vueNodes <img width="1156" height="608" alt="image" src="https://github.com/user-attachments/assets/818164a6-826b-4545-bc20-e01625f11d7d" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7894-feat-display-label_on-label_off-for-boolean-widgets-in-vueNodes-mode-2e26d73d365081a3b938c87dd4cf23aa) by [Unito](https://www.unito.io)
196 lines
6.2 KiB
TypeScript
196 lines
6.2 KiB
TypeScript
import { mount } from '@vue/test-utils'
|
|
import PrimeVue from 'primevue/config'
|
|
import ToggleSwitch from 'primevue/toggleswitch'
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
import type { IWidgetOptions } from '@/lib/litegraph/src/types/widgets'
|
|
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
|
|
|
import WidgetToggleSwitch from './WidgetToggleSwitch.vue'
|
|
|
|
describe('WidgetToggleSwitch Value Binding', () => {
|
|
const createMockWidget = (
|
|
value: boolean = false,
|
|
options: IWidgetOptions = {},
|
|
callback?: (value: boolean) => void
|
|
): SimplifiedWidget<boolean, IWidgetOptions> => ({
|
|
name: 'test_toggle',
|
|
type: 'boolean',
|
|
value,
|
|
options,
|
|
callback
|
|
})
|
|
|
|
const mountComponent = (
|
|
widget: SimplifiedWidget<boolean>,
|
|
modelValue: boolean,
|
|
readonly = false
|
|
) => {
|
|
return mount(WidgetToggleSwitch, {
|
|
props: {
|
|
widget,
|
|
modelValue,
|
|
readonly
|
|
},
|
|
global: {
|
|
plugins: [PrimeVue],
|
|
components: { ToggleSwitch }
|
|
}
|
|
})
|
|
}
|
|
|
|
describe('Vue Event Emission', () => {
|
|
it('emits Vue event when toggled from false to true', async () => {
|
|
const widget = createMockWidget(false)
|
|
const wrapper = mountComponent(widget, false)
|
|
|
|
const toggle = wrapper.findComponent({ name: 'ToggleSwitch' })
|
|
await toggle.setValue(true)
|
|
|
|
const emitted = wrapper.emitted('update:modelValue')
|
|
expect(emitted).toBeDefined()
|
|
expect(emitted![0]).toContain(true)
|
|
})
|
|
|
|
it('emits Vue event when toggled from true to false', async () => {
|
|
const widget = createMockWidget(true)
|
|
const wrapper = mountComponent(widget, true)
|
|
|
|
const toggle = wrapper.findComponent({ name: 'ToggleSwitch' })
|
|
await toggle.setValue(false)
|
|
|
|
const emitted = wrapper.emitted('update:modelValue')
|
|
expect(emitted).toBeDefined()
|
|
expect(emitted![0]).toContain(false)
|
|
})
|
|
|
|
it('handles value changes gracefully', async () => {
|
|
const widget = createMockWidget(false)
|
|
const wrapper = mountComponent(widget, false)
|
|
|
|
// Should not throw when changing values
|
|
const toggle = wrapper.findComponent({ name: 'ToggleSwitch' })
|
|
await toggle.setValue(true)
|
|
await toggle.setValue(false)
|
|
|
|
// Should emit events for all changes
|
|
const emitted = wrapper.emitted('update:modelValue')
|
|
expect(emitted).toHaveLength(2)
|
|
expect(emitted![0]).toContain(true)
|
|
expect(emitted![1]).toContain(false)
|
|
})
|
|
})
|
|
|
|
describe('Component Rendering', () => {
|
|
it('renders toggle switch component', () => {
|
|
const widget = createMockWidget(false)
|
|
const wrapper = mountComponent(widget, false)
|
|
|
|
expect(wrapper.findComponent({ name: 'ToggleSwitch' }).exists()).toBe(
|
|
true
|
|
)
|
|
})
|
|
|
|
it('displays correct initial state for false', () => {
|
|
const widget = createMockWidget(false)
|
|
const wrapper = mountComponent(widget, false)
|
|
|
|
const toggle = wrapper.findComponent({ name: 'ToggleSwitch' })
|
|
expect(toggle.props('modelValue')).toBe(false)
|
|
})
|
|
|
|
it('displays correct initial state for true', () => {
|
|
const widget = createMockWidget(true)
|
|
const wrapper = mountComponent(widget, true)
|
|
|
|
const toggle = wrapper.findComponent({ name: 'ToggleSwitch' })
|
|
expect(toggle.props('modelValue')).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe('Multiple Value Changes', () => {
|
|
it('handles rapid toggling correctly', async () => {
|
|
const widget = createMockWidget(false)
|
|
const wrapper = mountComponent(widget, false)
|
|
|
|
const toggle = wrapper.findComponent({ name: 'ToggleSwitch' })
|
|
|
|
// Rapid toggle sequence
|
|
await toggle.setValue(true)
|
|
await toggle.setValue(false)
|
|
await toggle.setValue(true)
|
|
|
|
// Should have emitted 3 Vue events with correct values
|
|
const emitted = wrapper.emitted('update:modelValue')
|
|
expect(emitted).toHaveLength(3)
|
|
expect(emitted![0]).toContain(true)
|
|
expect(emitted![1]).toContain(false)
|
|
expect(emitted![2]).toContain(true)
|
|
})
|
|
|
|
it('maintains state consistency during multiple changes', async () => {
|
|
const widget = createMockWidget(false)
|
|
const wrapper = mountComponent(widget, false)
|
|
|
|
const toggle = wrapper.findComponent({ name: 'ToggleSwitch' })
|
|
|
|
// Multiple state changes
|
|
await toggle.setValue(true)
|
|
await toggle.setValue(false)
|
|
await toggle.setValue(true)
|
|
await toggle.setValue(false)
|
|
|
|
const emitted = wrapper.emitted('update:modelValue')
|
|
expect(emitted).toHaveLength(4)
|
|
// Verify alternating pattern
|
|
expect(emitted![0]).toContain(true)
|
|
expect(emitted![1]).toContain(false)
|
|
expect(emitted![2]).toContain(true)
|
|
expect(emitted![3]).toContain(false)
|
|
})
|
|
})
|
|
|
|
describe('Label Display (label_on/label_off)', () => {
|
|
it('displays label_on when value is true', () => {
|
|
const widget = createMockWidget(true, { on: 'inside', off: 'outside' })
|
|
const wrapper = mountComponent(widget, true)
|
|
|
|
expect(wrapper.text()).toContain('inside')
|
|
})
|
|
|
|
it('displays label_off when value is false', () => {
|
|
const widget = createMockWidget(false, { on: 'inside', off: 'outside' })
|
|
const wrapper = mountComponent(widget, false)
|
|
|
|
expect(wrapper.text()).toContain('outside')
|
|
})
|
|
|
|
it('does not display label when no on/off options provided', () => {
|
|
const widget = createMockWidget(false, {})
|
|
const wrapper = mountComponent(widget, false)
|
|
|
|
expect(wrapper.find('span').exists()).toBe(false)
|
|
})
|
|
|
|
it('updates label when value changes', async () => {
|
|
const widget = createMockWidget(false, { on: 'enabled', off: 'disabled' })
|
|
const wrapper = mountComponent(widget, false)
|
|
|
|
expect(wrapper.text()).toContain('disabled')
|
|
|
|
await wrapper.setProps({ modelValue: true })
|
|
expect(wrapper.text()).toContain('enabled')
|
|
})
|
|
|
|
it('falls back to true/false when only partial options provided', () => {
|
|
const widgetOnOnly = createMockWidget(true, { on: 'active' })
|
|
const wrapperOn = mountComponent(widgetOnOnly, true)
|
|
expect(wrapperOn.text()).toContain('active')
|
|
|
|
const widgetOffOnly = createMockWidget(false, { off: 'inactive' })
|
|
const wrapperOff = mountComponent(widgetOffOnly, false)
|
|
expect(wrapperOff.text()).toContain('inactive')
|
|
})
|
|
})
|
|
})
|