[READ-ONLY] a fast, modern browser for the npm registry

test: add color util tests (#889)

Co-authored-by: Daniel Roe <daniel@roe.dev>

authored by

James Garbutt
Daniel Roe
and committed by
GitHub
d54ac07d 27eba701

+136
+136
test/unit/app/utils/colors.spec.ts
··· 1 + import { describe, expect, it } from 'vitest' 2 + import { lightenHex, oklchToHex, transparentizeOklch } from '../../../../app/utils/colors' 3 + 4 + describe('lightenHex', () => { 5 + it('lightens a color by the default factor (0.5)', () => { 6 + expect(lightenHex('#000000')).toBe('#808080') 7 + }) 8 + 9 + it('lightens a color by a custom factor', () => { 10 + expect(lightenHex('#000000', 0.25)).toBe('#404040') 11 + expect(lightenHex('#000000', 0.75)).toBe('#bfbfbf') 12 + expect(lightenHex('#000000', 1)).toBe('#ffffff') 13 + }) 14 + 15 + it('returns white when factor is 1', () => { 16 + expect(lightenHex('#ff0000', 1)).toBe('#ffffff') 17 + expect(lightenHex('#123456', 1)).toBe('#ffffff') 18 + }) 19 + 20 + it('returns the original color when factor is 0', () => { 21 + expect(lightenHex('#ff0000', 0)).toBe('#ff0000') 22 + expect(lightenHex('#123456', 0)).toBe('#123456') 23 + }) 24 + 25 + it('lightens each channel independently', () => { 26 + // #ff0000 at 0.5 -> r: 255+(0)*0.5=255, g: 0+(255)*0.5=128, b: 0+(255)*0.5=128 27 + expect(lightenHex('#ff0000', 0.5)).toBe('#ff8080') 28 + }) 29 + 30 + it('returns the input unchanged for an invalid hex', () => { 31 + expect(lightenHex('not-a-color')).toBe('not-a-color') 32 + }) 33 + 34 + it('handles hex without leading #', () => { 35 + expect(lightenHex('000000', 0.5)).toBe('#808080') 36 + }) 37 + }) 38 + 39 + describe('oklchToHex', () => { 40 + it('converts pure black', () => { 41 + expect(oklchToHex('oklch(0 0 0)')).toMatchInlineSnapshot(`"#000000"`) 42 + }) 43 + 44 + it('converts pure white', () => { 45 + expect(oklchToHex('oklch(1 0 0)')).toMatchInlineSnapshot(`"#ffffff"`) 46 + }) 47 + 48 + it('converts a saturated red', () => { 49 + expect(oklchToHex('oklch(0.628 0.258 29.23)')).toMatchInlineSnapshot(`"#ff0000"`) 50 + }) 51 + 52 + it('converts a saturated green', () => { 53 + expect(oklchToHex('oklch(0.866 0.295 142.5)')).toMatchInlineSnapshot(`"#00ff00"`) 54 + }) 55 + 56 + it('converts a saturated blue', () => { 57 + expect(oklchToHex('oklch(0.452 0.313 264.05)')).toMatchInlineSnapshot(`"#0000ff"`) 58 + }) 59 + 60 + it('converts the neutral fallback color', () => { 61 + expect(oklchToHex('oklch(0.633 0 0)')).toMatchInlineSnapshot(`"#8a8a8a"`) 62 + }) 63 + 64 + it('returns null for null input', () => { 65 + expect(oklchToHex(null)).toBeNull() 66 + }) 67 + 68 + it('returns undefined for undefined input', () => { 69 + expect(oklchToHex(undefined)).toBeUndefined() 70 + }) 71 + 72 + it('throws on invalid format', () => { 73 + expect(() => oklchToHex('not-a-color')).toThrow('Invalid OKLCH color format') 74 + }) 75 + }) 76 + 77 + describe('transparentizeOklch', () => { 78 + it('makes a color 50% transparent', () => { 79 + expect(transparentizeOklch('oklch(0.5 0.2 120)', 0.5)).toMatchInlineSnapshot( 80 + `"oklch(0.5 0.2 120 / 0.5)"`, 81 + ) 82 + }) 83 + 84 + it('makes a color fully transparent (factor 1)', () => { 85 + expect(transparentizeOklch('oklch(0.5 0.2 120)', 1)).toMatchInlineSnapshot( 86 + `"oklch(0.5 0.2 120 / 0)"`, 87 + ) 88 + }) 89 + 90 + it('keeps a color fully opaque (factor 0)', () => { 91 + expect(transparentizeOklch('oklch(0.5 0.2 120)', 0)).toMatchInlineSnapshot( 92 + `"oklch(0.5 0.2 120 / 1)"`, 93 + ) 94 + }) 95 + 96 + it('reduces existing alpha', () => { 97 + expect(transparentizeOklch('oklch(0.5 0.2 120 / 0.8)', 0.5)).toMatchInlineSnapshot( 98 + `"oklch(0.5 0.2 120 / 0.4)"`, 99 + ) 100 + }) 101 + 102 + it('handles percentage lightness and alpha', () => { 103 + expect(transparentizeOklch('oklch(50% 0.2 120 / 80%)', 0.5)).toMatchInlineSnapshot( 104 + `"oklch(0.5 0.2 120 / 0.4)"`, 105 + ) 106 + }) 107 + 108 + it('clamps factor to [0, 1]', () => { 109 + expect(transparentizeOklch('oklch(0.5 0.2 120)', 2)).toMatchInlineSnapshot( 110 + `"oklch(0.5 0.2 120 / 0)"`, 111 + ) 112 + expect(transparentizeOklch('oklch(0.5 0.2 120)', -1)).toMatchInlineSnapshot( 113 + `"oklch(0.5 0.2 120 / 1)"`, 114 + ) 115 + }) 116 + 117 + it('returns fallback for null input', () => { 118 + expect(transparentizeOklch(null, 0.5)).toBe('oklch(0 0 0 / 0)') 119 + }) 120 + 121 + it('returns fallback for undefined input', () => { 122 + expect(transparentizeOklch(undefined, 0.5)).toBe('oklch(0 0 0 / 0)') 123 + }) 124 + 125 + it('returns fallback for empty string', () => { 126 + expect(transparentizeOklch('', 0.5)).toBe('oklch(0 0 0 / 0)') 127 + }) 128 + 129 + it('returns fallback for invalid format', () => { 130 + expect(transparentizeOklch('not-a-color', 0.5)).toBe('oklch(0 0 0 / 0)') 131 + }) 132 + 133 + it('uses custom fallback', () => { 134 + expect(transparentizeOklch(null, 0.5, 'oklch(1 0 0 / 0)')).toBe('oklch(1 0 0 / 0)') 135 + }) 136 + })