mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 14:30:41 +00:00
Compare commits
9 Commits
sno-qa-714
...
vue-nodes/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
965bc83b11 | ||
|
|
df2fff1662 | ||
|
|
e4371a7770 | ||
|
|
5c7a2042f0 | ||
|
|
bec6fd81ab | ||
|
|
8ca9a284c4 | ||
|
|
714bf4ca4e | ||
|
|
9520ecd46b | ||
|
|
e7b44acc04 |
@@ -0,0 +1,300 @@
|
||||
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')
|
||||
})
|
||||
|
||||
it('normalizes bare hex without # to #hex on emit', 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('normalizes rgb() strings to #hex on emit', async () => {
|
||||
const widget = createMockWidget('#000000')
|
||||
const wrapper = mountComponent(widget, '#000000')
|
||||
|
||||
const emitted = await setColorPickerValue(wrapper, 'rgb(255, 0, 0)')
|
||||
expect(emitted).toBeDefined()
|
||||
expect(emitted![0]).toContain('#ff0000')
|
||||
})
|
||||
|
||||
it('normalizes hsb() strings to #hex on emit', async () => {
|
||||
const widget = createMockWidget('#000000', { format: 'hsb' })
|
||||
const wrapper = mountComponent(widget, '#000000')
|
||||
|
||||
const emitted = await setColorPickerValue(wrapper, 'hsb(120, 100, 100)')
|
||||
expect(emitted).toBeDefined()
|
||||
expect(emitted![0]).toContain('#00ff00')
|
||||
})
|
||||
|
||||
it('normalizes HSB object values to #hex on emit', async () => {
|
||||
const widget = createMockWidget('#000000', { format: 'hsb' })
|
||||
const wrapper = mountComponent(widget, '#000000')
|
||||
|
||||
const emitted = await setColorPickerValue(wrapper, {
|
||||
h: 240,
|
||||
s: 100,
|
||||
b: 100
|
||||
} as any)
|
||||
expect(emitted).toBeDefined()
|
||||
expect(emitted![0]).toContain('#0000ff')
|
||||
})
|
||||
})
|
||||
|
||||
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('normalizes display to a single leading #', () => {
|
||||
// Case 1: model value already includes '#'
|
||||
let widget = createMockWidget('#ff0000')
|
||||
let wrapper = mountComponent(widget, '#ff0000')
|
||||
let colorText = wrapper.find('[data-testid="widget-color-text"]')
|
||||
expect.soft(colorText.text()).toBe('#ff0000')
|
||||
|
||||
// Case 2: model value missing '#'
|
||||
widget = createMockWidget('ff0000')
|
||||
wrapper = mountComponent(widget, 'ff0000')
|
||||
colorText = wrapper.find('[data-testid="widget-color-text"]')
|
||||
expect.soft(colorText.text()).toBe('#ff0000')
|
||||
})
|
||||
|
||||
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('[data-testid="widget-color-text"]')
|
||||
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('[data-testid="widget-color-text"]')
|
||||
// Be specific about the displayed value including the leading '#'
|
||||
expect.soft(colorText.text()).toBe('#00ff00')
|
||||
})
|
||||
|
||||
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.soft(colorText.text()).toBe(color)
|
||||
}
|
||||
})
|
||||
|
||||
it('handles short hex colors', () => {
|
||||
const widget = createMockWidget('#fff')
|
||||
const wrapper = mountComponent(widget, '#fff')
|
||||
|
||||
const colorText = wrapper.find('[data-testid="widget-color-text"]')
|
||||
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"
|
||||
/>
|
||||
<span class="text-xs">#{{ localValue }}</span>
|
||||
<span class="text-xs" data-testid="widget-color-text">{{
|
||||
localValue.startsWith('#') ? localValue : '#' + localValue
|
||||
}}</span>
|
||||
</label>
|
||||
</WidgetLayoutField>
|
||||
</template>
|
||||
@@ -27,6 +29,7 @@ import { computed } from 'vue'
|
||||
|
||||
import { useWidgetValue } from '@/composables/graph/useWidgetValue'
|
||||
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
||||
import { normalizeColorToHex } from '@/utils/colorUtil'
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
import {
|
||||
PANEL_EXCLUDED_PROPS,
|
||||
@@ -46,12 +49,13 @@ const emit = defineEmits<{
|
||||
'update:modelValue': [value: string]
|
||||
}>()
|
||||
|
||||
// Use the composable for consistent widget value handling
|
||||
const { localValue, onChange } = useWidgetValue({
|
||||
widget: props.widget,
|
||||
modelValue: props.modelValue,
|
||||
// Normalize initial model value to ensure leading '#'
|
||||
modelValue: normalizeColorToHex(props.modelValue),
|
||||
defaultValue: '#000000',
|
||||
emit
|
||||
emit,
|
||||
transform: (val: unknown) => normalizeColorToHex(val)
|
||||
})
|
||||
|
||||
// ColorPicker specific excluded props include panel/overlay classes
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { memoize } from 'es-toolkit/compat'
|
||||
|
||||
type RGB = { r: number; g: number; b: number }
|
||||
type HSB = { h: number; s: number; b: number }
|
||||
type HSL = { h: number; s: number; l: number }
|
||||
type HSLA = { h: number; s: number; l: number; a: number }
|
||||
type ColorFormat = 'hex' | 'rgb' | 'rgba' | 'hsl' | 'hsla'
|
||||
@@ -59,6 +60,128 @@ export function hexToRgb(hex: string): RGB {
|
||||
return { r, g, b }
|
||||
}
|
||||
|
||||
export function rgbToHex({ r, g, b }: RGB): string {
|
||||
const toHex = (n: number) =>
|
||||
Math.max(0, Math.min(255, Math.round(n)))
|
||||
.toString(16)
|
||||
.padStart(2, '0')
|
||||
return `#${toHex(r)}${toHex(g)}${toHex(b)}`
|
||||
}
|
||||
|
||||
export function hsbToRgb({ h, s, b }: HSB): RGB {
|
||||
// Normalize
|
||||
const hh = ((h % 360) + 360) % 360
|
||||
const ss = Math.max(0, Math.min(100, s)) / 100
|
||||
const vv = Math.max(0, Math.min(100, b)) / 100
|
||||
|
||||
const c = vv * ss
|
||||
const x = c * (1 - Math.abs(((hh / 60) % 2) - 1))
|
||||
const m = vv - c
|
||||
|
||||
let rp = 0,
|
||||
gp = 0,
|
||||
bp = 0
|
||||
|
||||
if (hh < 60) {
|
||||
rp = c
|
||||
gp = x
|
||||
bp = 0
|
||||
} else if (hh < 120) {
|
||||
rp = x
|
||||
gp = c
|
||||
bp = 0
|
||||
} else if (hh < 180) {
|
||||
rp = 0
|
||||
gp = c
|
||||
bp = x
|
||||
} else if (hh < 240) {
|
||||
rp = 0
|
||||
gp = x
|
||||
bp = c
|
||||
} else if (hh < 300) {
|
||||
rp = x
|
||||
gp = 0
|
||||
bp = c
|
||||
} else {
|
||||
rp = c
|
||||
gp = 0
|
||||
bp = x
|
||||
}
|
||||
|
||||
return {
|
||||
r: Math.round((rp + m) * 255),
|
||||
g: Math.round((gp + m) * 255),
|
||||
b: Math.round((bp + m) * 255)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize various color inputs (hex, rgb/rgba, hsl/hsla, hsb string/object)
|
||||
* into lowercase #rrggbb. Falls back to #000000 on invalid inputs.
|
||||
*/
|
||||
export function normalizeColorToHex(value: unknown): string {
|
||||
// String inputs
|
||||
if (typeof value === 'string') {
|
||||
const raw = value.trim()
|
||||
if (!raw) return '#000000'
|
||||
|
||||
// Bare hex without '#'
|
||||
if (/^[0-9a-fA-F]{3}$/.test(raw) || /^[0-9a-fA-F]{6}$/.test(raw)) {
|
||||
return `#${raw.toLowerCase()}`
|
||||
}
|
||||
|
||||
// Starts with '#': only accept #rgb or #rrggbb
|
||||
if (raw.startsWith('#')) {
|
||||
const hex = raw.toLowerCase()
|
||||
if (hex.length === 4 || hex.length === 7) return hex
|
||||
return '#000000'
|
||||
}
|
||||
|
||||
// rgb(), rgba(), hsl(), hsla()
|
||||
if (/^(rgb|rgba|hsl|hsla)\(/i.test(raw)) {
|
||||
const rgb = parseToRgb(raw)
|
||||
return rgbToHex(rgb).toLowerCase()
|
||||
}
|
||||
|
||||
// hsb(h,s,b)
|
||||
if (/^hsb\(/i.test(raw)) {
|
||||
const nums = raw.match(/\d+(?:\.\d+)?/g)?.map(Number) || []
|
||||
if (nums.length >= 3) {
|
||||
const [h, s, b] = nums
|
||||
if ([h, s, b].every((n) => Number.isFinite(n))) {
|
||||
const rgb = hsbToRgb({ h, s, b })
|
||||
return rgbToHex(rgb).toLowerCase()
|
||||
}
|
||||
}
|
||||
return '#000000'
|
||||
}
|
||||
|
||||
// Unknown string format -> default
|
||||
return '#000000'
|
||||
}
|
||||
|
||||
// HSB object (PrimeVue)
|
||||
if (
|
||||
value &&
|
||||
typeof value === 'object' &&
|
||||
'h' in (value as any) &&
|
||||
's' in (value as any) &&
|
||||
('b' in (value as any) || 'v' in (value as any))
|
||||
) {
|
||||
const h = Number((value as any).h)
|
||||
const s = Number((value as any).s)
|
||||
const b = Number((value as any).b ?? (value as any).v)
|
||||
if ([h, s, b].every((n) => Number.isFinite(n))) {
|
||||
const rgb = hsbToRgb({ h, s, b })
|
||||
return rgbToHex(rgb).toLowerCase()
|
||||
}
|
||||
return '#000000'
|
||||
}
|
||||
|
||||
// Fallback
|
||||
return '#000000'
|
||||
}
|
||||
|
||||
export function parseToRgb(color: string): RGB {
|
||||
const format = identifyColorFormat(color)
|
||||
if (!format) return { r: 0, g: 0, b: 0 }
|
||||
|
||||
105
tests-ui/utils/colorUtil.test.ts
Normal file
105
tests-ui/utils/colorUtil.test.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import {
|
||||
hexToRgb,
|
||||
hsbToRgb,
|
||||
normalizeColorToHex,
|
||||
parseToRgb,
|
||||
rgbToHex
|
||||
} from '@/utils/colorUtil'
|
||||
|
||||
describe('colorUtil conversions', () => {
|
||||
describe('hexToRgb / rgbToHex', () => {
|
||||
it('converts 6-digit hex to RGB', () => {
|
||||
expect(hexToRgb('#ff0000')).toEqual({ r: 255, g: 0, b: 0 })
|
||||
expect(hexToRgb('#00ff00')).toEqual({ r: 0, g: 255, b: 0 })
|
||||
expect(hexToRgb('#0000ff')).toEqual({ r: 0, g: 0, b: 255 })
|
||||
})
|
||||
|
||||
it('converts 3-digit hex to RGB', () => {
|
||||
expect(hexToRgb('#f00')).toEqual({ r: 255, g: 0, b: 0 })
|
||||
expect(hexToRgb('#0f0')).toEqual({ r: 0, g: 255, b: 0 })
|
||||
expect(hexToRgb('#00f')).toEqual({ r: 0, g: 0, b: 255 })
|
||||
})
|
||||
|
||||
it('converts RGB to lowercase #hex and clamps values', () => {
|
||||
expect(rgbToHex({ r: 255, g: 0, b: 0 })).toBe('#ff0000')
|
||||
expect(rgbToHex({ r: 0, g: 255, b: 0 })).toBe('#00ff00')
|
||||
expect(rgbToHex({ r: 0, g: 0, b: 255 })).toBe('#0000ff')
|
||||
// out-of-range should clamp
|
||||
expect(rgbToHex({ r: -10, g: 300, b: 16 })).toBe('#00ff10')
|
||||
})
|
||||
|
||||
it('round-trips #hex -> rgb -> #hex', () => {
|
||||
const hex = '#123abc'
|
||||
expect(rgbToHex(hexToRgb(hex))).toBe('#123abc')
|
||||
})
|
||||
})
|
||||
|
||||
describe('parseToRgb', () => {
|
||||
it('parses #hex', () => {
|
||||
expect(parseToRgb('#ff0000')).toEqual({ r: 255, g: 0, b: 0 })
|
||||
})
|
||||
|
||||
it('parses rgb()/rgba()', () => {
|
||||
expect(parseToRgb('rgb(255, 0, 0)')).toEqual({ r: 255, g: 0, b: 0 })
|
||||
expect(parseToRgb('rgba(255,0,0,0.5)')).toEqual({ r: 255, g: 0, b: 0 })
|
||||
})
|
||||
|
||||
it('parses hsl()/hsla()', () => {
|
||||
expect(parseToRgb('hsl(0, 100%, 50%)')).toEqual({ r: 255, g: 0, b: 0 })
|
||||
const green = parseToRgb('hsla(120, 100%, 50%, 0.7)')
|
||||
expect(green.r).toBe(0)
|
||||
expect(green.g).toBe(255)
|
||||
expect(green.b).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('hsbToRgb', () => {
|
||||
it('converts HSB to primary RGB colors', () => {
|
||||
expect(hsbToRgb({ h: 0, s: 100, b: 100 })).toEqual({ r: 255, g: 0, b: 0 })
|
||||
expect(hsbToRgb({ h: 120, s: 100, b: 100 })).toEqual({
|
||||
r: 0,
|
||||
g: 255,
|
||||
b: 0
|
||||
})
|
||||
expect(hsbToRgb({ h: 240, s: 100, b: 100 })).toEqual({
|
||||
r: 0,
|
||||
g: 0,
|
||||
b: 255
|
||||
})
|
||||
})
|
||||
|
||||
it('handles non-100 brightness and clamps/normalizes input', () => {
|
||||
const rgb = hsbToRgb({ h: 360, s: 150, b: 50 })
|
||||
expect(rgbToHex(rgb)).toBe('#7f0000')
|
||||
})
|
||||
})
|
||||
|
||||
describe('normalizeColorToHex (guard rails)', () => {
|
||||
it('returns #hex for common inputs and falls back to black', () => {
|
||||
expect(normalizeColorToHex('#FFaa00')).toBe('#ffaa00')
|
||||
expect(normalizeColorToHex('ffaa00')).toBe('#ffaa00')
|
||||
expect(normalizeColorToHex('rgb(300, -5, 16)')).toBe('#ff0010')
|
||||
expect(normalizeColorToHex('hsl(0, 100%, 50%)')).toBe('#ff0000')
|
||||
expect(normalizeColorToHex('hsb(120, 100, 100)')).toBe('#00ff00')
|
||||
|
||||
// invalid strings
|
||||
expect(normalizeColorToHex('')).toBe('#000000')
|
||||
expect(normalizeColorToHex(' ')).toBe('#000000')
|
||||
expect(normalizeColorToHex('#12')).toBe('#000000')
|
||||
expect(normalizeColorToHex('#zzzzzz')).toBe('#000000')
|
||||
expect(normalizeColorToHex('not-a-color')).toBe('#000000')
|
||||
|
||||
// HSB object inputs
|
||||
expect(normalizeColorToHex({ h: 240, s: 100, b: 100 } as any)).toBe(
|
||||
'#0000ff'
|
||||
)
|
||||
expect(normalizeColorToHex({ h: NaN, s: 100, b: 100 } as any)).toBe(
|
||||
'#000000'
|
||||
)
|
||||
expect(normalizeColorToHex(null)).toBe('#000000')
|
||||
expect(normalizeColorToHex(undefined)).toBe('#000000')
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user