Repository: onsi/ginkgo (go)

Issue:
Support shorter aliases for color escape codes
#1095 introduced support for configuring colors through environment variables.

In order to support simpler configuration it would be nice to support shorter versions of the escape codes. The following values would be nice to map.

For the standard 16 colors (0-15)
`red` => `\x1b[31m`
`1` => `\x1b[31m`
`bright-red` => `\x1b[31;1m`
`8` => `\x1b[31;1m`

For extended 256 colors (16-255)
`16` => `\x1b[38;5;16m`
      

Pull request description:
Color aliases for custom color support
#1095 introduced custom color support. This PR improves the implementation, so that it doesn't rely on entering entire escape codes but instead it uses the color names and for the 256 color palette  it uses their number.

Example of how this feature improves usablity when using a color scheme like solarized. Before this change the colors looks like this when using the solarized color scheme

![Screenshot 2022-12-16 at 13 13 35](https://user-images.githubusercontent.com/3807831/208097314-d372b8e0-22bc-44f1-828a-c6cf60cbd978.png)

By setting the following envvars

```bash
export GINKGO_CLI_COLOR_RED="red"
export GINKGO_CLI_COLOR_ORANGE="bright-red"
export GINKGO_CLI_COLOR_CORAL="bright-magenta"
export GINKGO_CLI_COLOR_MAGENTA="magenta"
export GINKGO_CLI_COLOR_GREEN="green"
export GINKGO_CLI_COLOR_DARK_GREEN="green"
export GINKGO_CLI_COLOR_YELLOW="yellow"
export GINKGO_CLI_COLOR_LIGHT_YELLOW="yellow"
export GINKGO_CLI_COLOR_CYAN="cyan"
export GINKGO_CLI_COLOR_GRAY="bright-blue"
export GINKGO_CLI_COLOR_LIGHT_GRAY="bright-cyan" 
export GINKGO_CLI_COLOR_BLUE="blue"
```

The output now looks like this

![Screenshot 2022-12-16 at 13 09 51](https://user-images.githubusercontent.com/3807831/208097643-0ae95702-c395-493a-91b6-43311c21b020.png)

One could also chose to use the color numbers like (which also is needed to reference the 256 color palette).

```bash
export GINKGO_CLI_COLOR_RED="1"
export GINKGO_CLI_COLOR_ORANGE="9"
export GINKGO_CLI_COLOR_CORAL="13"
export GINKGO_CLI_COLOR_MAGENTA="5"
export GINKGO_CLI_COLOR_GREEN="2"
export GINKGO_CLI_COLOR_DARK_GREEN="2"
export GINKGO_CLI_COLOR_YELLOW="3"
export GINKGO_CLI_COLOR_LIGHT_YELLOW="3"
export GINKGO_CLI_COLOR_CYAN="6"
export GINKGO_CLI_COLOR_GRAY="12"
export GINKGO_CLI_COLOR_LIGHT_GRAY="14" 
export GINKGO_CLI_COLOR_BLUE="4"
```

This moves the custom color support away from the raw ASCI escapes and instead uses words for the standard 16 terminal colors or optionally 0-255 for the extended 256 color palette.

Besides the feature improvement a missing envvar unset in the tests was added.

fixes #1098 

Patch:
diff --git a/formatter/formatter.go b/formatter/formatter.go
index da224ea..ece35d1 100644
--- a/formatter/formatter.go
+++ b/formatter/formatter.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"os"
 	"regexp"
+	"strconv"
 	"strings"
 )
 
@@ -50,13 +51,35 @@ func NewWithNoColorBool(noColor bool) Formatter {
 }
 
 func New(colorMode ColorMode) Formatter {
+	colorAliases := map[string]int{
+		"black":   0,
+		"red":     1,
+		"green":   2,
+		"yellow":  3,
+		"blue":    4,
+		"magenta": 5,
+		"cyan":    6,
+		"white":   7,
+	}
+	for colorAlias, n := range colorAliases {
+		colorAliases[fmt.Sprintf("bright-%s", colorAlias)] = n + 8
+	}
+
 	getColor := func(color, defaultEscapeCode string) string {
 		color = strings.ToUpper(strings.ReplaceAll(color, "-", "_"))
 		envVar := fmt.Sprintf("GINKGO_CLI_COLOR_%s", color)
-		if escapeCode := os.Getenv(envVar); escapeCode != "" {
-			return escapeCode
+		envVarColor := os.Getenv(envVar)
+		if envVarColor == "" {
+			return defaultEscapeCode
+		}
+		if colorCode, ok := colorAliases[envVarColor]; ok {
+			return fmt.Sprintf("\x1b[38;5;%dm", colorCode)
+		}
+		colorCode, err := strconv.Atoi(envVarColor)
+		if err != nil || colorCode < 0 || colorCode > 255 {
+			return defaultEscapeCode
 		}
-		return defaultEscapeCode
+		return fmt.Sprintf("\x1b[38;5;%dm", colorCode)
 	}
 
 	f := Formatter{


Test patch:
diff --git a/formatter/formatter_test.go b/formatter/formatter_test.go
index b8db731..17503d7 100644
--- a/formatter/formatter_test.go
+++ b/formatter/formatter_test.go
@@ -62,19 +62,46 @@ var _ = Describe("Formatter", func() {
 		})
 	})
 
-	Context("with environment overrides", func() {
-		BeforeEach(func() {
-			os.Setenv("GINKGO_CLI_COLOR_RED", "\x1b[31m")
-		})
+	DescribeTable("with environment overrides",
+		func(envVars map[string]string, input, expected string) {
+			for envVar, value := range envVars {
+				os.Setenv(envVar, value)
+			}
+			f := formatter.New(colorMode)
+			Ω(f.F(input)).Should(Equal(expected))
+			for envVar := range envVars {
+				os.Unsetenv(envVar)
+			}
+		},
 
-		AfterEach(func() {
-			os.Unsetenv("GINKGO_CLI_COLOR_RED")
-		})
+		Entry("uses default for too low codes", map[string]string{
+			"GINKGO_CLI_COLOR_RED": "-1",
+		}, "{{red}}hi there{{/}}", "\x1b[38;5;9mhi there\x1b[0m"),
 
-		It("uses the escape codes from the environment variables", func() {
-			Ω(f.F("{{red}}hi there{{/}}")).Should(Equal("\x1b[31mhi there\x1b[0m"))
-		})
-	})
+		Entry("uses default for too high codes", map[string]string{
+			"GINKGO_CLI_COLOR_RED": "256",
+		}, "{{red}}hi there{{/}}", "\x1b[38;5;9mhi there\x1b[0m"),
+
+		Entry("supports literal alias for 8bit color", map[string]string{
+			"GINKGO_CLI_COLOR_RED": "red",
+		}, "{{red}}hi there{{/}}", "\x1b[38;5;1mhi there\x1b[0m"),
+
+		Entry("supports number alias for 8bit color", map[string]string{
+			"GINKGO_CLI_COLOR_RED": "1",
+		}, "{{red}}hi there{{/}}", "\x1b[38;5;1mhi there\x1b[0m"),
+
+		Entry("supports 16bit colors (bright)", map[string]string{
+			"GINKGO_CLI_COLOR_RED": "9",
+		}, "{{red}}hi there{{/}}", "\x1b[38;5;9mhi there\x1b[0m"),
+
+		Entry("supports 16bit color literal aliases (bright)", map[string]string{
+			"GINKGO_CLI_COLOR_RED": "bright-red",
+		}, "{{red}}hi there{{/}}", "\x1b[38;5;9mhi there\x1b[0m"),
+
+		Entry("supports extended 256 colors", map[string]string{
+			"GINKGO_CLI_COLOR_RED": "16",
+		}, "{{red}}hi there{{/}}", "\x1b[38;5;16mhi there\x1b[0m"),
+	)
 
 	Describe("NewWithNoColorBool", func() {
 		Context("when the noColor bool is true", func() {