@@ -352,5 +352,111 @@ public void ChooseTextColor_LightBackground_ReturnsDarkText()
352352 string chosen = ColorUtils . ChooseTextColor ( "#FFFFFF" ) ;
353353 Assert . Equal ( "#0F172A" , chosen ) ;
354354 }
355+
356+ // ── ToHex ─────────────────────────────────────────────────────────────────
357+
358+ [ Fact ]
359+ public void ToHex_OpaqueChannels_ReturnsRrggbbFormat ( )
360+ {
361+ string result = ColorUtils . ToHex ( 0x4F , 0x81 , 0xBD ) ;
362+ Assert . Equal ( "#4F81BD" , result , ignoreCase : true ) ;
363+ Assert . Equal ( 7 , result . Length ) ;
364+ }
365+
366+ [ Fact ]
367+ public void ToHex_WithNonOpaqueAlpha_ReturnsRrggbbaaFormat ( )
368+ {
369+ string result = ColorUtils . ToHex ( 0x4F , 0x81 , 0xBD , 0xCC ) ;
370+ Assert . Equal ( "#4F81BDCC" , result , ignoreCase : true ) ;
371+ Assert . Equal ( 9 , result . Length ) ;
372+ }
373+
374+ [ Fact ]
375+ public void ToHex_OpaqueAlpha255_OmitsAlphaByte ( )
376+ {
377+ string result = ColorUtils . ToHex ( 100 , 150 , 200 , 255 ) ;
378+ Assert . Equal ( 7 , result . Length ) ; // #RRGGBB only
379+ }
380+
381+ [ Fact ]
382+ public void ToHex_OverflowChannels_ClampsTo255 ( )
383+ {
384+ // Values above 255 should clamp — not produce out-of-range hex
385+ string result = ColorUtils . ToHex ( 300 , 400 , 500 ) ;
386+ Assert . Equal ( "#FFFFFF" , result , ignoreCase : true ) ;
387+ }
388+
389+ [ Fact ]
390+ public void ToHex_NegativeChannels_ClampsToZero ( )
391+ {
392+ string result = ColorUtils . ToHex ( - 10 , - 50 , - 100 ) ;
393+ Assert . Equal ( "#000000" , result , ignoreCase : true ) ;
394+ }
395+
396+ [ Fact ]
397+ public void ToHex_MixedOutOfRangeChannels_ClampedIndividually ( )
398+ {
399+ // r=300→FF, g=0x81 stays, b=-1→00
400+ string result = ColorUtils . ToHex ( 300 , 0x81 , - 1 ) ;
401+ Assert . Equal ( "#FF8100" , result , ignoreCase : true ) ;
402+ }
403+
404+ // ── Vibrant ───────────────────────────────────────────────────────────────
405+
406+ [ Fact ]
407+ public void Vibrant_PastelInput_ProducesMoreSaturatedResult ( )
408+ {
409+ // A pastel blue with clear hue deviation across channels
410+ string pastel = "#AACCEE" ;
411+ string vibrant = ColorUtils . Vibrant ( pastel ) ;
412+ var ( pr , pg , pb ) = ColorUtils . ParseHex ( pastel ) ;
413+ var ( vr , vg , vb ) = ColorUtils . ParseHex ( vibrant ) ;
414+
415+ // The max-min spread (saturation proxy) should be larger after vibrant
416+ int pastelSpread = Math . Max ( pr , Math . Max ( pg , pb ) ) - Math . Min ( pr , Math . Min ( pg , pb ) ) ;
417+ int vibrantSpread = Math . Max ( vr , Math . Max ( vg , vb ) ) - Math . Min ( vr , Math . Min ( vg , vb ) ) ;
418+ Assert . True ( vibrantSpread > pastelSpread , $ "Expected vibrant spread ({ vibrantSpread } ) > pastel spread ({ pastelSpread } ).") ;
419+ }
420+
421+ [ Fact ]
422+ public void Vibrant_PreservesAlphaChannel ( )
423+ {
424+ string result = ColorUtils . Vibrant ( "#AACCEECC" ) ;
425+ var ( _, _, _, a ) = ColorUtils . ParseHexWithAlpha ( result ) ;
426+ Assert . Equal ( 0xCC , a ) ;
427+ Assert . Equal ( 9 , result . Length ) ; // #RRGGBBAA
428+ }
429+
430+ [ Fact ]
431+ public void Vibrant_OpaqueInput_OutputHasNoAlphaSuffix ( )
432+ {
433+ string result = ColorUtils . Vibrant ( "#AACCEE" ) ;
434+ Assert . Equal ( 7 , result . Length ) ; // #RRGGBB
435+ }
436+
437+ [ Fact ]
438+ public void Vibrant_ReturnsValidHexString ( )
439+ {
440+ string result = ColorUtils . Vibrant ( "#B3D9F2" ) ;
441+ // Should start with # and be parseable
442+ Assert . StartsWith ( "#" , result ) ;
443+ var ( r , g , b ) = ColorUtils . ParseHex ( result ) ; // must not throw
444+ Assert . InRange ( r , 0 , 255 ) ;
445+ Assert . InRange ( g , 0 , 255 ) ;
446+ Assert . InRange ( b , 0 , 255 ) ;
447+ }
448+
449+ [ Fact ]
450+ public void Vibrant_HigherAmplify_ProducesMoreIntenseResult ( )
451+ {
452+ string pastel = "#AACCEE" ;
453+ string vibrant1 = ColorUtils . Vibrant ( pastel , amplify : 2.0 ) ;
454+ string vibrant2 = ColorUtils . Vibrant ( pastel , amplify : 4.0 ) ;
455+ var ( r1 , g1 , b1 ) = ColorUtils . ParseHex ( vibrant1 ) ;
456+ var ( r2 , g2 , b2 ) = ColorUtils . ParseHex ( vibrant2 ) ;
457+ int spread1 = Math . Max ( r1 , Math . Max ( g1 , b1 ) ) - Math . Min ( r1 , Math . Min ( g1 , b1 ) ) ;
458+ int spread2 = Math . Max ( r2 , Math . Max ( g2 , b2 ) ) - Math . Min ( r2 , Math . Min ( g2 , b2 ) ) ;
459+ Assert . True ( spread2 >= spread1 , "Higher amplify should produce equal or greater channel spread." ) ;
460+ }
355461}
356462
0 commit comments