diff --git a/README.md b/README.md index d5134a7..5555423 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,12 @@ composer require macocci7/bash-colorizer ->echo("Hi, there!", PHP_EOL); ``` + by hex code: + ```php + Colorizer::foreground('#ffcc00') // or #fc0 + ->echo("Hi, there!", PHP_EOL); + ``` + by number [0 - 255] (256 colors): ```php Colorizer::foreground(2) @@ -145,6 +151,12 @@ composer require macocci7/bash-colorizer ->echo("Hi, there!", PHP_EOL); ``` + by hex code: + ```php + Colorizer::background("#ffcc00") // or #fc0 + ->echo("Hi, there!", PHP_EOL); + ``` + by number [0 - 255] (256 colors): ```php Colorizer::background(1) @@ -168,6 +180,28 @@ composer require macocci7/bash-colorizer ->echo("Hi, there!", PHP_EOL); ``` +- Setting underline color: + + by hex code: + ```php + Colorizer::underline("#ffcc00") // or #fc0 + ->echo("Hi, there!", PHP_EOL); + ``` + + by number [0 - 255] (256 colors): + ```php + Colorizer::underline(1) + ->echo("Hi, there!", PHP_EOL); + ``` + + by (RGB) array (24bit 16777216 colors): + ```php + Colorizer::underline([255, 0, 0]) + ->echo("Hi, there!", PHP_EOL); + ``` + + See more: [Available Colors](#63-available-colors) + - Returning colorized string: As an argument of echo: @@ -223,22 +257,34 @@ composer require macocci7/bash-colorizer |`no-italic`|〇|〇| |`no-underline`|〇|〇| |`no-blink`|ー
*3|〇
*4| +|`proportional-spacing`|ー|ー| |`no-reverse`|〇|〇| |`no-conceal`|〇|〇| |`no-strike`|〇|〇| +|`no-proportional-spacing`|ー|ー| +|`framed`|❌|❌| +|`encircled`|❌|❌| +|`overlined`|〇|〇| +|`no-framed-no-encircled`|ー
*3|ー
*3| +|`no-overlined`|〇|〇| +|`underline-color`|〇|▲
*5| > *1: No effect with `faint`
> *2: Not `fast` (blinks at the same rate as `blink`)
> -*3: Unknown because `blink` has no effect
+*3: Unknown because the corresponding attribute has no effect
> *4: Also effective against `fast-blink`
+> +*5: Partially effective
e.g.) on VSCode Terminal - + + +See more: [Select Graphic Rendition parameters | ANSI escape code | Wikipedia](https://en.wikipedia.org/wiki/ANSI_escape_code#Select_Graphic_Rendition_parameters) ### 6.3. Available Colors @@ -261,7 +307,7 @@ e.g.) on VSCode Terminal |---|---| ||| -- 256 colors [ 0 - 255 ]: +- 256 colors [ 0 - 255 ]: `foreground`/`background`/`underline` e.g.) foreground colors on VSCode Terminal: @@ -271,6 +317,10 @@ e.g.) on VSCode Terminal + e.g.) underline colors on VSCode Terminal: + + + - 24bit (16777216) colors: e.g.) foreground colors on VSCode Terminal: @@ -281,6 +331,10 @@ e.g.) on VSCode Terminal + e.g.) underline colors on VSCode Terminal: + + + ## 7. Examples Example codes are in [playground](playground/) directory. @@ -293,6 +347,8 @@ Example codes are in [playground](playground/) directory. - [foreground_24bitcolors.php](playground/foreground_24bitcolors.php) - [background_256colors.php](playground/background_256colors.php) - [background_24bitcolors.php](playground/background_24bitcolors.php) +- [underline_256colors.php](playground/underline_256colors.php) +- [underline_24bitcolors.php](playground/underline_24bitcolors.php) - [readable.php](playground/readable.php) ## 8. LICENSE diff --git a/arts/available_attributes.png b/arts/available_attributes.png index 84ba2ad..a456c90 100644 Binary files a/arts/available_attributes.png and b/arts/available_attributes.png differ diff --git a/arts/underline_24bitcolors.png b/arts/underline_24bitcolors.png new file mode 100644 index 0000000..f634c23 Binary files /dev/null and b/arts/underline_24bitcolors.png differ diff --git a/arts/underline_256colors.png b/arts/underline_256colors.png new file mode 100644 index 0000000..b0fb1a3 Binary files /dev/null and b/arts/underline_256colors.png differ diff --git a/playground/colorizer.php b/playground/colorizer.php index d83153f..105cc86 100644 --- a/playground/colorizer.php +++ b/playground/colorizer.php @@ -7,6 +7,7 @@ $greeting = "Let's make your bash terminal full of colors!"; Colorizer::attributes(["bold"]) + ->echo(PHP_EOL) ->foreground("red")->echo(" B ") ->foreground("yellow")->echo("A ") ->foreground("white")->echo("S ") @@ -19,7 +20,8 @@ ->foreground("yellow")->echo("I ") ->foreground("white")->echo("Z ") ->foreground("green")->echo("E ") - ->foreground("cyan")->echo("R", "\n\n") + ->foreground("cyan")->echo("R", PHP_EOL) + ->echo(PHP_EOL) ; Colorizer::attributes(["reset"]) ->foreground("white")->background("black")->echo($greeting, PHP_EOL) @@ -30,4 +32,5 @@ ->foreground("green")->background("cyan")->echo($greeting, PHP_EOL) ->foreground("green")->background("blue")->echo($greeting, PHP_EOL) ->foreground("green")->background("magenta")->echo($greeting, PHP_EOL) + ->echo('', PHP_EOL) ; diff --git a/playground/underline_24bitcolors.php b/playground/underline_24bitcolors.php new file mode 100644 index 0000000..468e532 --- /dev/null +++ b/playground/underline_24bitcolors.php @@ -0,0 +1,23 @@ + 255 ? 255 : $r); + $gg = $g < 0 ? 0 : ($g > 255 ? 255 : $g); + $bb = $b < 0 ? 0 : ($b > 255 ? 255 : $b); + $s = sprintf(' [%3d;%3d;%3d] ', $rr, $gg, $bb); + Colorizer::underline([$rr, $gg, $bb]) + ->echo($s, !$i ? PHP_EOL : ''); + } + } +} diff --git a/playground/underline_256colors.php b/playground/underline_256colors.php new file mode 100644 index 0000000..ae85ef0 --- /dev/null +++ b/playground/underline_256colors.php @@ -0,0 +1,12 @@ +echo($s, $eol); +} diff --git a/src/Colorizer.php b/src/Colorizer.php index e197349..fd999a6 100644 --- a/src/Colorizer.php +++ b/src/Colorizer.php @@ -2,6 +2,7 @@ namespace Macocci7\BashColorizer; +use Macocci7\BashColorizer\Converter; use Macocci7\BashColorizer\Enums\Attribute; use Macocci7\BashColorizer\Enums\Foreground; use Macocci7\BashColorizer\Enums\Background; @@ -9,6 +10,8 @@ class Colorizer { + use Traits\ValidationTrait; + protected const ESC = "\e"; /** @@ -25,6 +28,8 @@ public function __construct(array $config = []) } /** + * sets or returns attributes + * * @param string[]|Attribute[]|null $attributes = [] * @return string[]|null|self */ @@ -40,6 +45,8 @@ public static function attributes(array|null $attributes = null): array|null|sel } /** + * sets or returns foreground color + * * @param string|Foreground|int|int[]|null $foreground = null * @return string|Foreground|int|int[]|null|self */ @@ -54,6 +61,8 @@ public static function foreground( } /** + * sets or returns background color + * * @param string|Background|int|int[]|null $background = null * @return string|Background|int|int[]|null|self */ @@ -67,6 +76,22 @@ public static function background( return new self(static::$config); } + /** + * sets or returns underline color + * + * @param string|int|int[]|null $underline = null + * @return string|int|int[]|null|self + */ + public static function underline( + string|int|array|null $underline = null + ): string|int|array|null|self { + if (is_null($underline)) { + return self::$config['underline'] ?? null; + } + static::$config['underline'] = $underline; + return new self(static::$config); + } + /** * @param array $config * @return array|self @@ -80,19 +105,34 @@ public static function config(array|null $config = null): array|self return new self($config); } + public static function hasAttribute(string|Attribute $attribute): bool + { + $attributes = static::attributes(); + if (empty($attributes)) { + return false; + } + if (is_string($attribute)) { + return is_null(Attribute::tryFrom($attribute)) + ? false + : in_array($attribute, $attributes) + || in_array(Attribute::from($attribute), $attributes); + } + return in_array($attribute->value, $attributes) + || in_array($attribute, $attributes); + } + /** - * @param array $config = [] * @return int[] */ - protected static function getAttributeCodes(array $config = []): array + protected static function getAttributeCodes(): array { - if (!isset($config['attributes'])) { + if (!isset(static::$config['attributes'])) { return []; } $codes = []; - foreach ($config['attributes'] as $attribute) { + foreach (static::$config['attributes'] as $attribute) { if (is_string($attribute)) { $codes[] = Attribute::tryFrom($attribute)?->code(); } elseif ($attribute instanceof Attribute) { @@ -100,20 +140,19 @@ protected static function getAttributeCodes(array $config = []): array } } - return $codes; + return array_unique($codes); } /** - * @param array $config = [] * @return int[] */ - protected static function getForegroundCode(array $config = []): array + protected static function getForegroundCode(): array { - if (!isset($config['foreground'])) { + if (!isset(static::$config['foreground'])) { return []; } - $foreground = $config['foreground']; + $foreground = static::$config['foreground']; if (is_int($foreground) || is_array($foreground)) { return static::extendedCodes(Foreground::Extended, $foreground); @@ -121,6 +160,13 @@ protected static function getForegroundCode(array $config = []): array $code = null; if (is_string($foreground)) { + if (static::isHex($foreground)) { + return [ + Foreground::Extended->code(), + 2, + ...Converter::decimal($foreground), + ]; + } $code = Foreground::tryFrom($foreground)?->code(); } elseif ($foreground instanceof Foreground) { $code = $foreground->code(); @@ -130,16 +176,15 @@ protected static function getForegroundCode(array $config = []): array } /** - * @param array $config = [] * @return int[] */ - protected static function getBackgroundCode(array $config = []): array + protected static function getBackgroundCode(): array { - if (!isset($config['background'])) { + if (!isset(static::$config['background'])) { return []; } - $background = $config['background']; + $background = static::$config['background']; if (is_int($background) || is_array($background)) { return static::extendedCodes(Background::Extended, $background); @@ -148,6 +193,13 @@ protected static function getBackgroundCode(array $config = []): array $code = null; if (is_string($background)) { + if (static::isHex($background)) { + return [ + Background::Extended->code(), + 2, + ...Converter::decimal($background), + ]; + } $code = Background::tryFrom($background)?->code(); } elseif ($background instanceof Background) { $code = $background->code(); @@ -156,6 +208,52 @@ protected static function getBackgroundCode(array $config = []): array return strlen($code ?? '') ? [$code] : []; } + /** + * @return int[] + */ + protected static function getUnderlineCode(): array + { + if (!isset(static::$config['underline'])) { + return []; + } + + $underline = static::$config['underline']; + + $attributeUnderline = static::hasAttribute('underline') + ? [] + : [Attribute::Underline->code()]; + if (is_int($underline)) { + return [ + ...$attributeUnderline, + Attribute::UnderlineColor->code(), + 5, + Filter::number($underline), + ]; + } + + if (is_array($underline)) { + return [ + ...$attributeUnderline, + Attribute::UnderlineColor->code(), + 2, + ...Filter::rgb($underline), + ]; + } + + if (is_string($underline)) { + if (static::isHex($underline)) { + return [ + ...$attributeUnderline, + Attribute::UnderlineColor->code(), + 2, + ...Converter::decimal($underline), + ]; + } + } + + return []; + } + /** * @param Foreground|Background $enum * @param int|int[] $color @@ -182,21 +280,19 @@ protected static function extendedCodes( ]; } - /** - * @param array $config = [] - */ - public static function codes(array $config = []): string + public static function codes(): string { return implode(';', array_merge( - self::getAttributeCodes($config), - self::getForegroundCode($config), - self::getBackgroundCode($config), + self::getAttributeCodes(), + self::getForegroundCode(), + self::getBackgroundCode(), + self::getUnderlineCode(), )); } public static function encode(string $string, string $eol = ''): string { - $codes = static::codes(static::$config); + $codes = static::codes(); return empty($codes) ? $string : sprintf( '%s[%sm%s%s[m%s', static::ESC, diff --git a/src/Converter.php b/src/Converter.php new file mode 100644 index 0000000..d843757 --- /dev/null +++ b/src/Converter.php @@ -0,0 +1,31 @@ + 7, static::Conceal => 8, static::Strike => 9, + //static::PrimaryFont => 10, + //static::AlternativeFont1 => 11, + //static::AlternativeFont2 => 12, + //static::AlternativeFont3 => 13, + //static::AlternativeFont4 => 14, + //static::AlternativeFont5 => 15, + //static::AlternativeFont6 => 16, + //static::AlternativeFont7 => 17, + //static::AlternativeFont8 => 18, + //static::AlternativeFont9 => 19, static::Gothic => 20, static::DoubleUnderline => 21, static::Normal => 22, static::NoItalic => 23, static::NoUnderline => 24, static::NoBlink => 25, + static::ProportionalSpacing => 26, static::NoReverse => 27, static::NoConceal => 28, static::NoStrike => 29, + static::NoProportionalSpacing => 50, + static::Framed => 51, + static::Encircled => 52, + static::Overlined => 53, + static::NoFramedNoEncircled => 54, + static::NoOverlined => 55, + static::UnderlineColor => 58, }; } } diff --git a/src/Traits/ValidationTrait.php b/src/Traits/ValidationTrait.php new file mode 100644 index 0000000..4d13ebe --- /dev/null +++ b/src/Traits/ValidationTrait.php @@ -0,0 +1,23 @@ + ["bold", "italic"], 'forground' => "yellow", 'background' => "blue", + 'underline' => "#ff9900", + ], + ], + 'case 8' => [ + 'config' => [ + 'attributes' => ["bold", "italic"], + 'forground' => "yellow", + 'background' => "blue", + 'underline' => "#ff9900", 'unnecessary' => "hello", ], ], @@ -116,6 +125,101 @@ public function test_background_can_set_background_color_correctly(string|Backgr $this->assertSame($background, Colorizer::config()['background']); } + public static function provide_underline_can_set_underline_color_correctly(): array + { + return [ + '""' => ['underline' => ""], + '"#ff9900"' => ['underline' => "#ff9900"], + '128' => ['underline' => 128], + '[0, 128, 255]' => ['underline' => [0, 128, 255]], + ]; + } + + #[DataProvider('provide_underline_can_set_underline_color_correctly')] + public function test_underline_can_set_underline_color_correctly(string|int|array $underline): void + { + Colorizer::underline($underline); + $this->assertSame($underline, Colorizer::underline()); + $this->assertSame($underline, Colorizer::config()['underline']); + } + + public static function provide_hasAttribute_can_return_correct_bool(): array + { + return [ + 'case 1' => [ + 'attributes' => null, + 'attribute' => 'bold', + 'expected' => false, + ], + 'case 2' => [ + 'attributes' => [], + 'attribute' => '', + 'expected' => false, + ], + 'case 3' => [ + 'attributes' => [], + 'attribute' => 'bold', + 'expected' => false, + ], + 'case 4' => [ + 'attributes' => ['italic'], + 'attribute' => 'bold', + 'expected' => false, + ], + 'case 5' => [ + 'attributes' => ['bold'], + 'attribute' => 'bold', + 'expected' => true, + ], + 'case 6' => [ + 'attributes' => ['bold'], + 'attribute' => Attribute::Bold, + 'expected' => true, + ], + 'case 7' => [ + 'attributes' => [Attribute::Bold], + 'attribute' => Attribute::Bold, + 'expected' => true, + ], + 'case 8' => [ + 'attributes' => [Attribute::Bold], + 'attribute' => 'bold', + 'expected' => true, + ], + 'case 9' => [ + 'attributes' => ['italic', 'bold'], + 'attribute' => 'bold', + 'expected' => true, + ], + 'case 10' => [ + 'attributes' => ['italic', Attribute::Bold], + 'attribute' => 'bold', + 'expected' => true, + ], + 'case 11' => [ + 'attributes' => ['italic', 'bold'], + 'attribute' => Attribute::Bold, + 'expected' => true, + ], + 'case 12' => [ + 'attributes' => ['italic', Attribute::Bold], + 'attribute' => Attribute::Bold, + 'expected' => true, + ], + ]; + } + + #[DataProvider('provide_hasAttribute_can_return_correct_bool')] + public function test_hasAttribute_can_return_correct_bool(array|null $attributes, string|Attribute $attribute, bool $expected): void + { + if (is_null($attributes)) { + Colorizer::config([]); + } else { + Colorizer::attributes($attributes); + } + $this->assertSame($expected, Colorizer::hasAttribute($attribute)); + } + public static function provide_codes_can_return_codes_correctly(): array { return [ @@ -302,6 +406,12 @@ public static function provide_codes_can_return_codes_correctly(): array ], 'expected' => '33', ], + '#ff9900 as foreground' => [ + 'config' => [ + 'foreground' => '#ff9900', + ], + 'expected' => '38;2;255;153;0', + ], 'Yellow as foreground' => [ 'config' => [ 'foreground' => Foreground::Yellow, @@ -442,6 +552,12 @@ public static function provide_codes_can_return_codes_correctly(): array ], 'expected' => '42', ], + '#0099ff as background' => [ + 'config' => [ + 'background' => '#0099ff', + ], + 'expected' => '48;2;0;153;255', + ], 'Green as background' => [ 'config' => [ 'background' => Background::Green, @@ -515,94 +631,275 @@ public static function provide_codes_can_return_codes_correctly(): array 'expected' => '48;2;64;128;192', ], + // cases for underline + 'empty as underline' => [ + 'config' => [ + 'underline' => '', + ], + 'expected' => '', + ], + 'null as underline' => [ + 'config' => [ + 'underline' => null, + ], + 'expected' => '', + ], + 'invalid as underline' => [ + 'config' => [ + 'underline' => 'invalid', + ], + 'expected' => '', + ], + 'true as underline' => [ + 'config' => [ + 'underline' => true, + ], + 'expected' => '', + ], + 'false as underline' => [ + 'config' => [ + 'underline' => false, + ], + 'expected' => '', + ], + 'float as underline' => [ + 'config' => [ + 'underline' => 1.5, + ], + 'expected' => '', + ], + 'object as underline' => [ + 'config' => [ + 'underline' => new \stdClass(), + ], + 'expected' => '', + ], + 'closure as underline' => [ + 'config' => [ + 'underline' => fn () => "magenta", + ], + 'expected' => '', + ], + 'Attribute enum as underline' => [ + 'config' => [ + 'underline' => Attribute::Italic, + ], + 'expected' => '', + ], + 'Foregound enum as underline' => [ + 'config' => [ + 'underline' => Foreground::Cyan, + ], + 'expected' => '', + ], + 'Background enum as underline' => [ + 'config' => [ + 'underline' => Background::Cyan, + ], + 'expected' => '', + ], + 'green as underline' => [ + 'config' => [ + 'underline' => 'green', + ], + 'expected' => '', + ], + '#f90 as underline' => [ + 'config' => [ + 'underline' => '#0099ff', + ], + 'expected' => '4;58;2;0;153;255', + ], + '#0099ff as underline' => [ + 'config' => [ + 'underline' => '#0099ff', + ], + 'expected' => '4;58;2;0;153;255', + ], + 'int -1 as underline' => [ + 'config' => [ + 'underline' => -1, + ], + 'expected' => '4;58;5;0', + ], + 'int 0 as underline' => [ + 'config' => [ + 'underline' => 0, + ], + 'expected' => '4;58;5;0', + ], + 'int 1 as underline' => [ + 'config' => [ + 'underline' => 1, + ], + 'expected' => '4;58;5;1', + ], + 'int 254 as underline' => [ + 'config' => [ + 'underline' => 254, + ], + 'expected' => '4;58;5;254', + ], + 'int 255 as underline' => [ + 'config' => [ + 'underline' => 255, + ], + 'expected' => '4;58;5;255', + ], + 'int 256 as underline' => [ + 'config' => [ + 'underline' => 256, + ], + 'expected' => '4;58;5;255', + ], + '[null] as underline' => [ + 'config' => [ + 'underline' => [null], + ], + 'expected' => '4;58;2;0;0;0', + ], + '[64] as underline' => [ + 'config' => [ + 'underline' => [64], + ], + 'expected' => '4;58;2;64;0;0', + ], + '[64, 128] as underline' => [ + 'config' => [ + 'underline' => [64, 128], + ], + 'expected' => '4;58;2;64;128;0', + ], + '[64, 128, 192] as underline' => [ + 'config' => [ + 'underline' => [64, 128, 192], + ], + 'expected' => '4;58;2;64;128;192', + ], + '[64, 128, 192, 255] as underline' => [ + 'config' => [ + 'underline' => [64, 128, 192, 255], + ], + 'expected' => '4;58;2;64;128;192', + ], + // combinations - 'empty attributes, foreground and background' => [ + 'empty attributes, foreground, background and underline' => [ 'config' => [ 'attributes' => [], 'foreground' => '', 'background' => '', + 'underline' => '', ], 'expected' => '', ], - 'empty as attributes, empty foreground and background' => [ + 'empty as attributes, empty foreground, background and underline' => [ 'config' => [ 'attributes' => [''], 'foreground' => '', 'background' => '', + 'underline' => '', ], 'expected' => '', ], - 'null as attributes, empty foreground and background' => [ + 'null as attributes, empty foreground, background and underline' => [ 'config' => [ 'attributes' => [null], 'foreground' => '', 'background' => '', + 'underline' => '', ], 'expected' => '', ], - 'null as attributes, foreground and background' => [ + 'null as attributes, foreground, background and underline' => [ 'config' => [ 'attributes' => [null], 'foreground' => null, 'background' => null, + 'underline' => null, ], 'expected' => '', ], - 'invalid as attributes, empty foreground and background' => [ + 'invalid as attributes, empty foreground, background and underline' => [ 'config' => [ 'attributes' => ['invalid'], 'foreground' => '', 'background' => '', + 'underline' => '', ], 'expected' => '', ], - 'invalid as attributes, foreground and background' => [ + 'invalid as attributes, foreground, background and underline' => [ 'config' => [ 'attributes' => ['invalid'], 'foreground' => 'invalid', 'background' => 'invalid', + 'underline' => 'invalid', ], 'expected' => '', ], - 'bold, empty foreground and background' => [ + 'bold, empty foreground, background and underline' => [ 'config' => [ 'attributes' => ['bold'], 'foreground' => '', 'background' => '', + 'underline' => '', ], 'expected' => '1', ], - 'bold, red foreground and empty background' => [ + 'bold, red foreground, empty background and underline' => [ 'config' => [ 'attributes' => ['bold'], 'foreground' => 'red', 'background' => '', + 'underline' => '', ], 'expected' => '1;31', ], - 'bold, red foreground and yellow background' => [ + 'bold, red foreground, yellow background and empty underline' => [ 'config' => [ 'attributes' => ['bold'], 'foreground' => 'red', 'background' => 'yellow', + 'underline' => '', ], 'expected' => '1;31;43', ], - 'bold, empty foreground and yellow background' => [ + 'bold, red foreground, yellow background and #0099ff underline' => [ + 'config' => [ + 'attributes' => ['bold'], + 'foreground' => 'red', + 'background' => 'yellow', + 'underline' => '#0099ff', + ], + 'expected' => '1;31;43;4;58;2;0;153;255', + ], + 'bold, empty foreground, yellow background and #0099ff underline' => [ 'config' => [ 'attributes' => ['bold'], 'foreground' => '', 'background' => 'yellow', + 'underline' => '#0099ff', ], - 'expected' => '1;43', + 'expected' => '1;43;4;58;2;0;153;255', ], - 'bold, italic, red foreground and yellow background' => [ + 'bold, italic, red foreground, yellow background and #0099ff underline' => [ 'config' => [ 'attributes' => ['bold', 'italic'], 'foreground' => 'red', 'background' => 'yellow', + 'underline' => '#0099ff', ], - 'expected' => '1;3;31;43', + 'expected' => '1;3;31;43;4;58;2;0;153;255', + ], + 'bold, underline, italic, red foreground, yellow background and #0099ff underline' => [ + 'config' => [ + 'attributes' => ['bold', 'underline', 'italic'], + 'foreground' => 'red', + 'background' => 'yellow', + 'underline' => '#0099ff', + ], + 'expected' => '1;4;3;31;43;58;2;0;153;255', ], ]; } @@ -610,7 +907,7 @@ public static function provide_codes_can_return_codes_correctly(): array #[DataProvider('provide_codes_can_return_codes_correctly')] public function test_codes_can_return_codes_correctly(array $config, string $expected): void { - $this->assertSame($expected, Colorizer::codes($config)); + $this->assertSame($expected, Colorizer::config($config)->codes()); } public static function provide_encode_can_return_encoded_value_correctly(): array @@ -642,6 +939,13 @@ public static function provide_encode_can_return_encoded_value_correctly(): arra 'message' => 'Hi, there!', 'expected' => "\e[31mHi, there!\e[m", ], + 'foreground:#ff9900' => [ + 'config' => [ + 'foreground' => "#ff9900", + ], + 'message' => 'Hi, there!', + 'expected' => "\e[38;2;255;153;0mHi, there!\e[m", + ], 'foreground:128' => [ 'config' => [ 'foreground' => 128, @@ -663,6 +967,13 @@ public static function provide_encode_can_return_encoded_value_correctly(): arra 'message' => 'Hi, there!', 'expected' => "\e[41mHi, there!\e[m", ], + 'background:#0099ff' => [ + 'config' => [ + 'background' => "#0099ff", + ], + 'message' => 'Hi, there!', + 'expected' => "\e[48;2;0;153;255mHi, there!\e[m", + ], 'background:128' => [ 'config' => [ 'background' => 128, @@ -709,6 +1020,49 @@ public function test_encode_can_return_encoded_value_correctly(array $config, st $this->assertSame($expected, Colorizer::config($config)->encode($message)); } + public static function provide_echo_can_output_strings_correctly(): array + { + return [ + 'empty' => [ + 'config' => [], + 'string' => '', + 'expected' => '', + ], + 'empty config' => [ + 'config' => [], + 'string' => 'Hello', + 'expected' => 'Hello', + ], + 'invalid config' => [ + 'config' => ['bold'], + 'string' => 'Hello', + 'expected' => 'Hello', + ], + 'bold' => [ + 'config' => ['attributes' => ['bold']], + 'string' => 'Hello', + 'expected' => "\e[1mHello\e[m", + ], + 'bold and italic' => [ + 'config' => ['attributes' => ['bold', 'italic']], + 'string' => 'Hello', + 'expected' => "\e[1;3mHello\e[m", + ], + 'bold, italic and foreground red' => [ + 'config' => ['attributes' => ['bold', 'italic'], 'foreground' => 'red'], + 'string' => 'Hello', + 'expected' => "\e[1;3;31mHello\e[m", + ], + ]; + } + + #[DataProvider('provide_echo_can_output_strings_correctly')] + public function test_echo_can_output_strings_correctly(array $config, string $string, string $expected): void + { + $this->expectOutputString($expected); + Colorizer::config($config)->echo($string); + } + public static function provide_readable_can_return_readable_value_correctly(): array { return [ @@ -738,6 +1092,13 @@ public static function provide_readable_can_return_readable_value_correctly(): a 'message' => 'Hi, there!', 'expected' => "\\033[31mHi, there!\\033[m", ], + 'foreground:#0099ff' => [ + 'config' => [ + 'foreground' => "#0099ff", + ], + 'message' => 'Hi, there!', + 'expected' => "\\033[38;2;0;153;255mHi, there!\\033[m", + ], 'foreground:128' => [ 'config' => [ 'foreground' => 128, @@ -759,6 +1120,13 @@ public static function provide_readable_can_return_readable_value_correctly(): a 'message' => 'Hi, there!', 'expected' => "\\033[41mHi, there!\\033[m", ], + 'background:#ff9900' => [ + 'config' => [ + 'background' => "#ff9900", + ], + 'message' => 'Hi, there!', + 'expected' => "\\033[48;2;255;153;0mHi, there!\\033[m", + ], 'background:128' => [ 'config' => [ 'background' => 128, diff --git a/tests/ConverterTest.php b/tests/ConverterTest.php new file mode 100644 index 0000000..4096330 --- /dev/null +++ b/tests/ConverterTest.php @@ -0,0 +1,42 @@ + ['rgb' => '#000', 'expected' => [0, 0, 0]], + '#123' => ['rgb' => '#123', 'expected' => [17, 34, 51]], + '#fed' => ['rgb' => '#fed', 'expected' => [255, 238, 221]], + '#000000' => ['rgb' => '#000000', 'expected' => [0, 0, 0]], + '#123456' => ['rgb' => '#123456', 'expected' => [18, 52, 86]], + '#ffeedd' => ['rgb' => '#ffeedd', 'expected' => [255, 238, 221]], + + 'fff' => ['rgb' => 'fff', 'expected' => []], + 'fffffff' => ['rgb' => 'fffffff', 'expected' => []], + '#' => ['rgb' => '#', 'expected' => []], + '#1' => ['rgb' => '#1', 'expected' => []], + '#12' => ['rgb' => '#12', 'expected' => []], + '#efg' => ['rgb' => '#efg', 'expected' => []], + '#1234' => ['rgb' => '#1234', 'expected' => []], + '#12345' => ['rgb' => '#12345', 'expected' => []], + '#bcdefg' => ['rgb' => '#bcdefg', 'expected' => []], + '#1234567' => ['rgb' => '#1234567', 'expected' => []], + '#abcdefg' => ['rgb' => '#abcdefg', 'expected' => []], + ]; + } + + #[DataProvider('provide_decimal_can_return_correct_array')] + public function test_decimal_can_return_correct_array(string $rgb, array $expected): void + { + $this->assertSame($expected, Converter::decimal($rgb)); + } +} diff --git a/tests/Enums/AttributeTest.php b/tests/Enums/AttributeTest.php index 1cfadc8..25a590d 100644 --- a/tests/Enums/AttributeTest.php +++ b/tests/Enums/AttributeTest.php @@ -30,9 +30,16 @@ public static function provide_code_can_return_correct_code(): array 'attribute:NoItalic' => ['enum' => Attribute::NoItalic, 'expected' => 23], 'attribute:NoUnderline' => ['enum' => Attribute::NoUnderline, 'expected' => 24], 'attribute:NoBlink' => ['enum' => Attribute::NoBlink, 'expected' => 25], + 'attribute:ProportionalSpacing' => ['enum' => Attribute::ProportionalSpacing, 'expected' => 26], 'attribute:NoReverse' => ['enum' => Attribute::NoReverse, 'expected' => 27], 'attribute:NoConceal' => ['enum' => Attribute::NoConceal, 'expected' => 28], - 'attribute:NoStrike' => ['enum' => Attribute::NoStrike, 'expected' => 29], + 'attribute:NoProportionalSpacing' => ['enum' => Attribute::NoProportionalSpacing, 'expected' => 50], + 'attribute:Framed' => ['enum' => Attribute::Framed, 'expected' => 51], + 'attribute:Encircled' => ['enum' => Attribute::Encircled, 'expected' => 52], + 'attribute:Overlined' => ['enum' => Attribute::Overlined, 'expected' => 53], + 'attribute:NoFramedNoEncircled' => ['enum' => Attribute::NoFramedNoEncircled, 'expected' => 54], + 'attribute:NoOverlined' => ['enum' => Attribute::NoOverlined, 'expected' => 55], + 'attribute:UnderlineColor' => ['enum' => Attribute::UnderlineColor, 'expected' => 58], ]; } @@ -61,9 +68,17 @@ public function test_codes_can_return_correct_codes(): void 'no-italic' => 23, 'no-underline' => 24, 'no-blink' => 25, + 'proportional-spacing' => 26, 'no-reverse' => 27, 'no-conceal' => 28, 'no-strike' => 29, + 'no-proportional-spacing' => 50, + 'framed' => 51, + 'encircled' => 52, + 'overlined' => 53, + 'no-framed-no-encircled' => 54, + 'no-overlined' => 55, + 'underline-color' => 58, ]; $this->assertSame($codes, Attribute::codes()); } diff --git a/tests/Traits/ValidationTraitTest.php b/tests/Traits/ValidationTraitTest.php new file mode 100644 index 0000000..2c4c1c9 --- /dev/null +++ b/tests/Traits/ValidationTraitTest.php @@ -0,0 +1,106 @@ + ['rgb' => '', 'expected' => false], + '123' => ['rgb' => '123', 'expected' => false], + '123456' => ['rgb' => '123456', 'expected' => false], + '#' => ['rgb' => '#', 'expected' => false], + '#1' => ['rgb' => '#1', 'expected' => false], + '#12' => ['rgb' => '#12', 'expected' => false], + '#123' => ['rgb' => '#123', 'expected' => true], + '#1234' => ['rgb' => '#1234', 'expected' => false], + '#12345' => ['rgb' => '#12345', 'expected' => false], + '#123456' => ['rgb' => '#123456', 'expected' => false], + '#1234567' => ['rgb' => '#1234567', 'expected' => false], + '#efg' => ['rgb' => '#efg', 'expected' => false], + '#def' => ['rgb' => '#def', 'expected' => true], + '#000' => ['rgb' => '#000', 'expected' => true], + '#1af' => ['rgb' => '#1af', 'expected' => true], + ]; + } + + #[DataProvider('provide_isShortHex_can_return_correct_bool')] + public function test_isShortHex_can_return_correct_bool(string $rgb, bool $expected): void + { + $validator = new class { + use ValidationTrait; + }; + $this->assertSame($expected, $validator::isShortHex($rgb)); + } + + public static function provide_isLongHex_can_return_correct_bool(): array + { + return [ + 'empty' => ['rgb' => '', 'expected' => false], + 'fff' => ['rgb' => 'fff', 'expected' => false], + 'ffffff' => ['rgb' => 'ffffff', 'expected' => false], + '#' => ['rgb' => '#', 'expected' => false], + '#1' => ['rgb' => '#1', 'expected' => false], + '#12' => ['rgb' => '#12', 'expected' => false], + '#123' => ['rgb' => '#123', 'expected' => false], + '#1234' => ['rgb' => '#1234', 'expected' => false], + '#12345' => ['rgb' => '#12345', 'expected' => false], + '#123456' => ['rgb' => '#123456', 'expected' => true], + '#1234567' => ['rgb' => '#1234567', 'expected' => false], + '#000000' => ['rgb' => '#000000', 'expected' => true], + '#abcdef' => ['rgb' => '#abcdef', 'expected' => true], + '#bcdefg' => ['rgb' => '#bcdefg', 'expected' => false], + '#123def' => ['rgb' => '#123def', 'expected' => true], + ]; + } + + #[DataProvider('provide_isLongHex_can_return_correct_bool')] + public function test_isLongHex_can_return_correct_bool(string $rgb, bool $expected): void + { + $validator = new class { + use ValidationTrait; + }; + $this->assertSame($expected, $validator::isLongHex($rgb)); + } + + public static function provide_isHex_can_return_correct_bool(): array + { + return [ + 'empty' => ['rgb' => '', 'expected' => false], + '123' => ['rgb' => '123', 'expected' => false], + '123456' => ['rgb' => '123456', 'expected' => false], + '#' => ['rgb' => '#', 'expected' => false], + '#1' => ['rgb' => '#1', 'expected' => false], + '#12' => ['rgb' => '#12', 'expected' => false], + '#123' => ['rgb' => '#123', 'expected' => true], + '#1234' => ['rgb' => '#1234', 'expected' => false], + '#12345' => ['rgb' => '#12345', 'expected' => false], + '#123456' => ['rgb' => '#123456', 'expected' => true], + '#1234567' => ['rgb' => '#1234567', 'expected' => false], + '#000' => ['rgb' => '#000', 'expected' => true], + '#def' => ['rgb' => '#def', 'expected' => true], + '#efg' => ['rgb' => '#efg', 'expected' => false], + '#09f' => ['rgb' => '#09f', 'expected' => true], + '#000000' => ['rgb' => '#000000', 'expected' => true], + '#abcdef' => ['rgb' => '#abcdef', 'expected' => true], + '#bcdefg' => ['rgb' => '#bcdefg', 'expected' => false], + '#123def' => ['rgb' => '#123def', 'expected' => true], + ]; + } + + #[DataProvider('provide_isHex_can_return_correct_bool')] + public function test_isHex_can_return_correct_bool(string $rgb, bool $expected): void + { + $validator = new class { + use ValidationTrait; + }; + $this->assertSame($expected, $validator::isHex($rgb)); + } +}