Fix ColorPicker, further test cleanup

This commit is contained in:
Austin Mroz
2025-12-03 10:43:43 -08:00
parent 1c75a49978
commit c4efedfa1d
5 changed files with 48 additions and 84 deletions

View File

@@ -17,7 +17,7 @@ describe('WidgetColorPicker Value Binding', () => {
callback?: (value: string) => void
): SimplifiedWidget<string> => {
const valueRef = ref(value)
if (callback) watch(valueRef, callback)
if (callback) watch(valueRef, (v) => callback(v))
return {
name: 'test_color_picker',
type: 'color',
@@ -53,80 +53,61 @@ describe('WidgetColorPicker Value Binding', () => {
) => {
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')
describe('Value Binding', () => {
it('triggers callback when color changes', async () => {
const callback = vi.fn()
const widget = createMockWidget('#ff0000', {}, callback)
const wrapper = mountComponent(widget, '#ff0000')
const emitted = await setColorPickerValue(wrapper, '#00ff00')
expect(emitted).toBeDefined()
expect(emitted![0]).toContain('#00ff00')
await setColorPickerValue(wrapper, '#00ff00')
expect(callback).toHaveBeenCalledExactlyOnceWith('#00ff00')
})
it('handles different color formats', async () => {
const widget = createMockWidget('#ffffff')
const callback = vi.fn()
const widget = createMockWidget('#ffffff', {}, callback)
const wrapper = mountComponent(widget, '#ffffff')
const emitted = await setColorPickerValue(wrapper, '#123abc')
expect(emitted).toBeDefined()
expect(emitted![0]).toContain('#123abc')
await setColorPickerValue(wrapper, '#123abc')
expect(callback).toHaveBeenCalledExactlyOnceWith('#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')
})
it('normalizes bare hex without # to #hex on emit', async () => {
const widget = createMockWidget('ff0000')
it('normalizes bare hex without # to #hex', async () => {
const callback = vi.fn()
const widget = createMockWidget('ff0000', {}, callback)
const wrapper = mountComponent(widget, 'ff0000')
const emitted = await setColorPickerValue(wrapper, '00ff00')
expect(emitted).toBeDefined()
expect(emitted![0]).toContain('#00ff00')
await setColorPickerValue(wrapper, '00ff00')
expect(callback).toHaveBeenCalledExactlyOnceWith('#00ff00')
})
it('normalizes rgb() strings to #hex on emit', async (context) => {
context.skip('needs diagnosis')
const widget = createMockWidget('#000000')
it('normalizes rgb() strings to #hex', async () => {
const callback = vi.fn()
const widget = createMockWidget('#000000', { format: 'rgb' }, callback)
const wrapper = mountComponent(widget, '#000000')
const emitted = await setColorPickerValue(wrapper, 'rgb(255, 0, 0)')
expect(emitted).toBeDefined()
expect(emitted![0]).toContain('#ff0000')
await setColorPickerValue(wrapper, 'rgb(255, 0, 0)')
expect(callback).toHaveBeenCalledExactlyOnceWith('#ff0000')
})
it('normalizes hsb() strings to #hex on emit', async () => {
const widget = createMockWidget('#000000', { format: 'hsb' })
it('normalizes hsb() strings to #hex', async () => {
const callback = vi.fn()
const widget = createMockWidget('#000000', { format: 'hsb' }, callback)
const wrapper = mountComponent(widget, '#000000')
const emitted = await setColorPickerValue(wrapper, 'hsb(120, 100, 100)')
expect(emitted).toBeDefined()
expect(emitted![0]).toContain('#00ff00')
await setColorPickerValue(wrapper, 'hsb(120, 100, 100)')
expect(callback).toHaveBeenCalledExactlyOnceWith('#00ff00')
})
it('normalizes HSB object values to #hex on emit', async () => {
const widget = createMockWidget('#000000', { format: 'hsb' })
it('normalizes HSB object values to #hex', async () => {
const callback = vi.fn()
const widget = createMockWidget('#000000', { format: 'hsb' }, callback)
const wrapper = mountComponent(widget, '#000000')
const emitted = await setColorPickerValue(wrapper, {
h: 240,
s: 100,
b: 100
})
expect(emitted).toBeDefined()
expect(emitted![0]).toContain('#0000ff')
await setColorPickerValue(wrapper, { h: 240, s: 100, b: 100 })
expect(callback).toHaveBeenCalledExactlyOnceWith('#0000ff')
})
})
@@ -269,15 +250,15 @@ describe('WidgetColorPicker Value Binding', () => {
})
it('handles invalid color formats gracefully', async () => {
const widget = createMockWidget('invalid-color')
const callback = vi.fn()
const widget = createMockWidget('invalid-color', {}, callback)
const wrapper = mountComponent(widget, 'invalid-color')
const colorText = wrapper.find('[data-testid="widget-color-text"]')
expect(colorText.text()).toBe('#000000')
const emitted = await setColorPickerValue(wrapper, 'invalid-color')
expect(emitted).toBeDefined()
expect(emitted![0]).toContain('#000000')
await setColorPickerValue(wrapper, 'invalid-color')
expect(callback).toHaveBeenCalledExactlyOnceWith('#000000')
})
it('handles widget with no options', () => {

View File

@@ -14,7 +14,6 @@
:pt="{
preview: '!w-full !h-full !border-none'
}"
@update:model-value="onPickerUpdate"
/>
<span
class="text-xs truncate min-w-[4ch]"
@@ -27,11 +26,11 @@
<script setup lang="ts">
import ColorPicker from 'primevue/colorpicker'
import { computed, ref, watch } from 'vue'
import { computed } from 'vue'
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
import { isColorFormat, toHexFromFormat } from '@/utils/colorUtil'
import type { ColorFormat, HSB } from '@/utils/colorUtil'
import type { ColorFormat } from '@/utils/colorUtil'
import { cn } from '@/utils/tailwindUtil'
import {
PANEL_EXCLUDED_PROPS,
@@ -45,39 +44,23 @@ type WidgetOptions = { format?: ColorFormat } & Record<string, unknown>
const props = defineProps<{
widget: SimplifiedWidget<string, WidgetOptions>
modelValue: string
}>()
const emit = defineEmits<{
'update:modelValue': [value: string]
}>()
const modelValue = props.widget.value()
const format = computed<ColorFormat>(() => {
const optionFormat = props.widget.options?.format
return isColorFormat(optionFormat) ? optionFormat : 'hex'
})
type PickerValue = string | HSB
const localValue = ref<PickerValue>(
toHexFromFormat(
props.modelValue || '#000000',
isColorFormat(props.widget.options?.format)
? props.widget.options.format
: 'hex'
)
)
watch(
() => props.modelValue,
(newVal) => {
localValue.value = toHexFromFormat(newVal || '#000000', format.value)
const localValue = computed({
get() {
return toHexFromFormat(modelValue.value || '#000000', format.value)
},
set(v) {
modelValue.value = toHexFromFormat(v, format.value)
}
)
function onPickerUpdate(val: unknown) {
localValue.value = val as PickerValue
emit('update:modelValue', toHexFromFormat(val, format.value))
}
})
// ColorPicker specific excluded props include panel/overlay classes
const COLOR_PICKER_EXCLUDED_PROPS = [...PANEL_EXCLUDED_PROPS] as const

View File

@@ -51,7 +51,7 @@ describe('WidgetInputNumberInput Value Binding', () => {
expect(input.value).toBe('42')
})
it('emits update:modelValue when value changes', async () => {
it('triggers callback when value changes', async () => {
const callback = vi.fn()
const widget = createMockWidget(10, 'int', {}, callback)
const wrapper = mountComponent(widget, 10)

View File

@@ -212,7 +212,7 @@ describe('WidgetMarkdown Dual Mode Display', () => {
})
describe('Value Updates', () => {
it('emits update:modelValue when textarea content changes', async () => {
it('triggers callback when textarea content changes', async () => {
const callback = vi.fn()
const widget = createMockWidget('# Original', {}, callback)
const wrapper = mountComponent(widget, '# Original')

View File

@@ -61,7 +61,7 @@ async function setTextareaValueAndTrigger(
describe('WidgetTextarea Value Binding', () => {
describe('Widget Value Callbacks', () => {
it('emits Vue event when textarea value changes on input', async () => {
it('triggers callback when textarea value changes on input', async () => {
const callback = vi.fn()
const widget = createMockWidget('initial', {}, callback)
const wrapper = mountComponent(widget, 'initial')