mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-28 18:22:40 +00:00
fix color picker value prefix and add component tests
This commit is contained in:
@@ -0,0 +1,246 @@
|
|||||||
|
import { mount } from '@vue/test-utils'
|
||||||
|
import ColorPicker from 'primevue/colorpicker'
|
||||||
|
import type { ColorPickerProps } from 'primevue/colorpicker'
|
||||||
|
import PrimeVue from 'primevue/config'
|
||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
|
||||||
|
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
||||||
|
|
||||||
|
import WidgetColorPicker from './WidgetColorPicker.vue'
|
||||||
|
import WidgetLayoutField from './layout/WidgetLayoutField.vue'
|
||||||
|
|
||||||
|
describe('WidgetColorPicker Value Binding', () => {
|
||||||
|
const createMockWidget = (
|
||||||
|
value: string = '#000000',
|
||||||
|
options: Partial<ColorPickerProps> = {},
|
||||||
|
callback?: (value: string) => void
|
||||||
|
): SimplifiedWidget<string> => ({
|
||||||
|
name: 'test_color_picker',
|
||||||
|
type: 'color',
|
||||||
|
value,
|
||||||
|
options,
|
||||||
|
callback
|
||||||
|
})
|
||||||
|
|
||||||
|
const mountComponent = (
|
||||||
|
widget: SimplifiedWidget<string>,
|
||||||
|
modelValue: string,
|
||||||
|
readonly = false
|
||||||
|
) => {
|
||||||
|
return mount(WidgetColorPicker, {
|
||||||
|
global: {
|
||||||
|
plugins: [PrimeVue],
|
||||||
|
components: {
|
||||||
|
ColorPicker,
|
||||||
|
WidgetLayoutField
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
widget,
|
||||||
|
modelValue,
|
||||||
|
readonly
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const setColorPickerValue = async (
|
||||||
|
wrapper: ReturnType<typeof mount>,
|
||||||
|
value: string
|
||||||
|
) => {
|
||||||
|
const colorPicker = wrapper.findComponent({ name: 'ColorPicker' })
|
||||||
|
await colorPicker.setValue(value)
|
||||||
|
return wrapper.emitted('update:modelValue')
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Vue Event Emission', () => {
|
||||||
|
it('emits Vue event when color changes', async () => {
|
||||||
|
const widget = createMockWidget('#ff0000')
|
||||||
|
const wrapper = mountComponent(widget, '#ff0000')
|
||||||
|
|
||||||
|
const emitted = await setColorPickerValue(wrapper, '#00ff00')
|
||||||
|
|
||||||
|
expect(emitted).toBeDefined()
|
||||||
|
expect(emitted![0]).toContain('#00ff00')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('handles different color formats', async () => {
|
||||||
|
const widget = createMockWidget('#ffffff')
|
||||||
|
const wrapper = mountComponent(widget, '#ffffff')
|
||||||
|
|
||||||
|
const emitted = await setColorPickerValue(wrapper, '#123abc')
|
||||||
|
|
||||||
|
expect(emitted).toBeDefined()
|
||||||
|
expect(emitted![0]).toContain('#123abc')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('handles missing callback gracefully', async () => {
|
||||||
|
const widget = createMockWidget('#000000', {}, undefined)
|
||||||
|
const wrapper = mountComponent(widget, '#000000')
|
||||||
|
|
||||||
|
const emitted = await setColorPickerValue(wrapper, '#ff00ff')
|
||||||
|
|
||||||
|
// Should still emit Vue event
|
||||||
|
expect(emitted).toBeDefined()
|
||||||
|
expect(emitted![0]).toContain('#ff00ff')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Component Rendering', () => {
|
||||||
|
it('renders color picker component', () => {
|
||||||
|
const widget = createMockWidget('#ff0000')
|
||||||
|
const wrapper = mountComponent(widget, '#ff0000')
|
||||||
|
|
||||||
|
const colorPicker = wrapper.findComponent({ name: 'ColorPicker' })
|
||||||
|
expect(colorPicker.exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders layout field wrapper', () => {
|
||||||
|
const widget = createMockWidget('#ff0000')
|
||||||
|
const wrapper = mountComponent(widget, '#ff0000')
|
||||||
|
|
||||||
|
const layoutField = wrapper.findComponent({ name: 'WidgetLayoutField' })
|
||||||
|
expect(layoutField.exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('displays current color value as text', () => {
|
||||||
|
const widget = createMockWidget('#ff0000')
|
||||||
|
const wrapper = mountComponent(widget, '#ff0000')
|
||||||
|
|
||||||
|
const colorText = wrapper.find('span')
|
||||||
|
expect(colorText.text()).toBe('#ff0000')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('updates color text when value changes', async () => {
|
||||||
|
const widget = createMockWidget('#ff0000')
|
||||||
|
const wrapper = mountComponent(widget, '#ff0000')
|
||||||
|
|
||||||
|
await setColorPickerValue(wrapper, '#00ff00')
|
||||||
|
|
||||||
|
// Need to check the local state update
|
||||||
|
const colorText = wrapper.find('span')
|
||||||
|
// Note: The actual text update depends on the component's reactive state
|
||||||
|
expect(colorText.exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('uses default color when no value provided', () => {
|
||||||
|
const widget = createMockWidget('')
|
||||||
|
const wrapper = mountComponent(widget, '')
|
||||||
|
|
||||||
|
const colorPicker = wrapper.findComponent({ name: 'ColorPicker' })
|
||||||
|
// Should use the default value from the composable
|
||||||
|
expect(colorPicker.exists()).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Readonly Mode', () => {
|
||||||
|
it('disables color picker when readonly', () => {
|
||||||
|
const widget = createMockWidget('#ff0000')
|
||||||
|
const wrapper = mountComponent(widget, '#ff0000', true)
|
||||||
|
|
||||||
|
const colorPicker = wrapper.findComponent({ name: 'ColorPicker' })
|
||||||
|
expect(colorPicker.props('disabled')).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('enables color picker when not readonly', () => {
|
||||||
|
const widget = createMockWidget('#ff0000')
|
||||||
|
const wrapper = mountComponent(widget, '#ff0000', false)
|
||||||
|
|
||||||
|
const colorPicker = wrapper.findComponent({ name: 'ColorPicker' })
|
||||||
|
expect(colorPicker.props('disabled')).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Color Formats', () => {
|
||||||
|
it('handles valid hex colors', async () => {
|
||||||
|
const validHexColors = [
|
||||||
|
'#000000',
|
||||||
|
'#ffffff',
|
||||||
|
'#ff0000',
|
||||||
|
'#00ff00',
|
||||||
|
'#0000ff',
|
||||||
|
'#123abc'
|
||||||
|
]
|
||||||
|
|
||||||
|
for (const color of validHexColors) {
|
||||||
|
const widget = createMockWidget(color)
|
||||||
|
const wrapper = mountComponent(widget, color)
|
||||||
|
|
||||||
|
const colorText = wrapper.find('span')
|
||||||
|
expect(colorText.text()).toBe(color)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('handles short hex colors', () => {
|
||||||
|
const widget = createMockWidget('#fff')
|
||||||
|
const wrapper = mountComponent(widget, '#fff')
|
||||||
|
|
||||||
|
const colorText = wrapper.find('span')
|
||||||
|
expect(colorText.text()).toBe('#fff')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('passes widget options to color picker', () => {
|
||||||
|
const colorOptions = {
|
||||||
|
format: 'hex' as const,
|
||||||
|
inline: true
|
||||||
|
}
|
||||||
|
const widget = createMockWidget('#ff0000', colorOptions)
|
||||||
|
const wrapper = mountComponent(widget, '#ff0000')
|
||||||
|
|
||||||
|
const colorPicker = wrapper.findComponent({ name: 'ColorPicker' })
|
||||||
|
expect(colorPicker.props('format')).toBe('hex')
|
||||||
|
expect(colorPicker.props('inline')).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Widget Layout Integration', () => {
|
||||||
|
it('passes widget to layout field', () => {
|
||||||
|
const widget = createMockWidget('#ff0000')
|
||||||
|
const wrapper = mountComponent(widget, '#ff0000')
|
||||||
|
|
||||||
|
const layoutField = wrapper.findComponent({ name: 'WidgetLayoutField' })
|
||||||
|
expect(layoutField.props('widget')).toEqual(widget)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('maintains proper component structure', () => {
|
||||||
|
const widget = createMockWidget('#ff0000')
|
||||||
|
const wrapper = mountComponent(widget, '#ff0000')
|
||||||
|
|
||||||
|
// Should have layout field containing label with color picker and text
|
||||||
|
const layoutField = wrapper.findComponent({ name: 'WidgetLayoutField' })
|
||||||
|
const label = wrapper.find('label')
|
||||||
|
const colorPicker = wrapper.findComponent({ name: 'ColorPicker' })
|
||||||
|
const colorText = wrapper.find('span')
|
||||||
|
|
||||||
|
expect(layoutField.exists()).toBe(true)
|
||||||
|
expect(label.exists()).toBe(true)
|
||||||
|
expect(colorPicker.exists()).toBe(true)
|
||||||
|
expect(colorText.exists()).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Edge Cases', () => {
|
||||||
|
it('handles empty color value', () => {
|
||||||
|
const widget = createMockWidget('')
|
||||||
|
const wrapper = mountComponent(widget, '')
|
||||||
|
|
||||||
|
const colorPicker = wrapper.findComponent({ name: 'ColorPicker' })
|
||||||
|
expect(colorPicker.exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('handles invalid color formats gracefully', () => {
|
||||||
|
const widget = createMockWidget('invalid-color')
|
||||||
|
const wrapper = mountComponent(widget, 'invalid-color')
|
||||||
|
|
||||||
|
const colorText = wrapper.find('span')
|
||||||
|
expect(colorText.text()).toBe('#invalid-color')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('handles widget with no options', () => {
|
||||||
|
const widget = createMockWidget('#ff0000')
|
||||||
|
const wrapper = mountComponent(widget, '#ff0000')
|
||||||
|
|
||||||
|
const colorPicker = wrapper.findComponent({ name: 'ColorPicker' })
|
||||||
|
expect(colorPicker.exists()).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -16,7 +16,9 @@
|
|||||||
}"
|
}"
|
||||||
@update:model-value="onChange"
|
@update:model-value="onChange"
|
||||||
/>
|
/>
|
||||||
<span class="text-xs">#{{ localValue }}</span>
|
<span class="text-xs">{{
|
||||||
|
localValue.startsWith('#') ? localValue : '#' + localValue
|
||||||
|
}}</span>
|
||||||
</label>
|
</label>
|
||||||
</WidgetLayoutField>
|
</WidgetLayoutField>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user