fix color picker value prefix and add component tests

This commit is contained in:
bymyself
2025-09-09 21:32:34 -07:00
parent 97d00ea47d
commit e7b44acc04
2 changed files with 249 additions and 1 deletions

View File

@@ -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)
})
})
})

View File

@@ -16,7 +16,9 @@
}"
@update:model-value="onChange"
/>
<span class="text-xs">#{{ localValue }}</span>
<span class="text-xs">{{
localValue.startsWith('#') ? localValue : '#' + localValue
}}</span>
</label>
</WidgetLayoutField>
</template>