1package termenv
2
3import (
4	"bytes"
5	"fmt"
6	"image/color"
7	"os"
8	"strings"
9	"testing"
10	"text/template"
11)
12
13func TestTermEnv(t *testing.T) {
14	p := ColorProfile()
15	if p != TrueColor && p != Ascii {
16		t.Errorf("Expected %d, got %d", TrueColor, p)
17	}
18
19	fg := ForegroundColor()
20	fgseq := fg.Sequence(false)
21	fgexp := "97"
22	if fgseq != fgexp && fgseq != "" {
23		t.Errorf("Expected %s, got %s", fgexp, fgseq)
24	}
25
26	bg := BackgroundColor()
27	bgseq := bg.Sequence(true)
28	bgexp := "48;2;0;0;0"
29	if bgseq != bgexp && bgseq != "" {
30		t.Errorf("Expected %s, got %s", bgexp, bgseq)
31	}
32
33	_ = HasDarkBackground()
34}
35
36func TestRendering(t *testing.T) {
37	out := String("foobar")
38	if out.String() != "foobar" {
39		t.Errorf("Unstyled strings should be returned as plain text")
40	}
41
42	out = out.Foreground(TrueColor.Color("#abcdef"))
43	out = out.Background(TrueColor.Color("69"))
44	out = out.Bold()
45	out = out.Italic()
46	out = out.Faint()
47	out = out.Underline()
48	out = out.Blink()
49
50	exp := "\x1b[38;2;171;205;239;48;5;69;1;3;2;4;5mfoobar\x1b[0m"
51	if out.String() != exp {
52		t.Errorf("Expected %s, got %s", exp, out.String())
53	}
54
55	exp = "foobar"
56	mono := String(exp)
57	mono = mono.Foreground(Ascii.Color("#abcdef"))
58	if mono.String() != exp {
59		t.Errorf("Ascii profile should not apply color styles")
60	}
61}
62
63func TestColorConversion(t *testing.T) {
64	// ANSI color
65	a := ANSI.Color("7")
66	c := ConvertToRGB(a)
67
68	exp := "#c0c0c0"
69	if c.Hex() != exp {
70		t.Errorf("Expected %s, got %s", exp, c.Hex())
71	}
72
73	// ANSI-256 color
74	a256 := ANSI256.Color("91")
75	c = ConvertToRGB(a256)
76
77	exp = "#8700af"
78	if c.Hex() != exp {
79		t.Errorf("Expected %s, got %s", exp, c.Hex())
80	}
81
82	// hex color
83	hex := "#abcdef"
84	argb := TrueColor.Color(hex)
85	c = ConvertToRGB(argb)
86
87	if c.Hex() != hex {
88		t.Errorf("Expected %s, got %s", exp, c.Hex())
89	}
90}
91
92func TestFromColor(t *testing.T) {
93	// color.Color interface
94	c := TrueColor.FromColor(color.RGBA{255, 128, 0, 255})
95	exp := "38;2;255;128;0"
96	if c.Sequence(false) != exp {
97		t.Errorf("Expected %s, got %s", exp, c.Sequence(false))
98	}
99}
100
101func TestAscii(t *testing.T) {
102	c := Ascii.Color("#abcdef")
103	if c.Sequence(false) != "" {
104		t.Errorf("Expected empty sequence, got %s", c.Sequence(false))
105	}
106}
107
108func TestANSIProfile(t *testing.T) {
109	p := ANSI
110
111	c := p.Color("#e88388")
112	exp := "91"
113	if c.Sequence(false) != exp {
114		t.Errorf("Expected %s, got %s", exp, c.Sequence(false))
115	}
116	if _, ok := c.(ANSIColor); !ok {
117		t.Errorf("Expected type termenv.ANSIColor, got %T", c)
118	}
119
120	c = p.Color("82")
121	exp = "92"
122	if c.Sequence(false) != exp {
123		t.Errorf("Expected %s, got %s", exp, c.Sequence(false))
124	}
125	if _, ok := c.(ANSIColor); !ok {
126		t.Errorf("Expected type termenv.ANSIColor, got %T", c)
127	}
128
129	c = p.Color("2")
130	exp = "32"
131	if c.Sequence(false) != exp {
132		t.Errorf("Expected %s, got %s", exp, c.Sequence(false))
133	}
134	if _, ok := c.(ANSIColor); !ok {
135		t.Errorf("Expected type termenv.ANSIColor, got %T", c)
136	}
137}
138
139func TestANSI256Profile(t *testing.T) {
140	p := ANSI256
141
142	c := p.Color("#abcdef")
143	exp := "38;5;153"
144	if c.Sequence(false) != exp {
145		t.Errorf("Expected %s, got %s", exp, c.Sequence(false))
146	}
147	if _, ok := c.(ANSI256Color); !ok {
148		t.Errorf("Expected type termenv.ANSI256Color, got %T", c)
149	}
150
151	c = p.Color("139")
152	exp = "38;5;139"
153	if c.Sequence(false) != exp {
154		t.Errorf("Expected %s, got %s", exp, c.Sequence(false))
155	}
156	if _, ok := c.(ANSI256Color); !ok {
157		t.Errorf("Expected type termenv.ANSI256Color, got %T", c)
158	}
159
160	c = p.Color("2")
161	exp = "32"
162	if c.Sequence(false) != exp {
163		t.Errorf("Expected %s, got %s", exp, c.Sequence(false))
164	}
165	if _, ok := c.(ANSIColor); !ok {
166		t.Errorf("Expected type termenv.ANSIColor, got %T", c)
167	}
168}
169
170func TestTrueColorProfile(t *testing.T) {
171	p := TrueColor
172
173	c := p.Color("#abcdef")
174	exp := "38;2;171;205;239"
175	if c.Sequence(false) != exp {
176		t.Errorf("Expected %s, got %s", exp, c.Sequence(false))
177	}
178	if _, ok := c.(RGBColor); !ok {
179		t.Errorf("Expected type termenv.HexColor, got %T", c)
180	}
181
182	c = p.Color("139")
183	exp = "38;5;139"
184	if c.Sequence(false) != exp {
185		t.Errorf("Expected %s, got %s", exp, c.Sequence(false))
186	}
187	if _, ok := c.(ANSI256Color); !ok {
188		t.Errorf("Expected type termenv.ANSI256Color, got %T", c)
189	}
190
191	c = p.Color("2")
192	exp = "32"
193	if c.Sequence(false) != exp {
194		t.Errorf("Expected %s, got %s", exp, c.Sequence(false))
195	}
196	if _, ok := c.(ANSIColor); !ok {
197		t.Errorf("Expected type termenv.ANSIColor, got %T", c)
198	}
199}
200
201func TestStyles(t *testing.T) {
202	s := String("foobar").Foreground(TrueColor.Color("2"))
203
204	exp := "\x1b[32mfoobar\x1b[0m"
205	if s.String() != exp {
206		t.Errorf("Expected %s, got %s", exp, s.String())
207	}
208}
209
210func TestTemplateHelpers(t *testing.T) {
211	p := TrueColor
212
213	exp := String("Hello World")
214	basetpl := `{{ %s "Hello World" }}`
215	wraptpl := `{{ %s (%s "Hello World") }}`
216
217	tt := []struct {
218		Template string
219		Expected string
220	}{
221		{
222			Template: fmt.Sprintf(basetpl, "Bold"),
223			Expected: exp.Bold().String(),
224		},
225		{
226			Template: fmt.Sprintf(basetpl, "Faint"),
227			Expected: exp.Faint().String(),
228		},
229		{
230			Template: fmt.Sprintf(basetpl, "Italic"),
231			Expected: exp.Italic().String(),
232		},
233		{
234			Template: fmt.Sprintf(basetpl, "Underline"),
235			Expected: exp.Underline().String(),
236		},
237		{
238			Template: fmt.Sprintf(basetpl, "Overline"),
239			Expected: exp.Overline().String(),
240		},
241		{
242			Template: fmt.Sprintf(basetpl, "Blink"),
243			Expected: exp.Blink().String(),
244		},
245		{
246			Template: fmt.Sprintf(basetpl, "Reverse"),
247			Expected: exp.Reverse().String(),
248		},
249		{
250			Template: fmt.Sprintf(basetpl, "CrossOut"),
251			Expected: exp.CrossOut().String(),
252		},
253		{
254			Template: fmt.Sprintf(wraptpl, "Underline", "Bold"),
255			Expected: String(exp.Bold().String()).Underline().String(),
256		},
257		{
258			Template: `{{ Color "#ff0000" "foobar" }}`,
259			Expected: String("foobar").Foreground(p.Color("#ff0000")).String(),
260		},
261		{
262			Template: `{{ Color "#ff0000" "#0000ff" "foobar" }}`,
263			Expected: String("foobar").
264				Foreground(p.Color("#ff0000")).
265				Background(p.Color("#0000ff")).
266				String(),
267		},
268		{
269			Template: `{{ Foreground "#ff0000" "foobar" }}`,
270			Expected: String("foobar").Foreground(p.Color("#ff0000")).String(),
271		},
272		{
273			Template: `{{ Background "#ff0000" "foobar" }}`,
274			Expected: String("foobar").Background(p.Color("#ff0000")).String(),
275		},
276	}
277
278	for i, v := range tt {
279		tpl, err := template.New(fmt.Sprintf("test_%d", i)).Funcs(TemplateFuncs(p)).Parse(v.Template)
280		if err != nil {
281			t.Error(err)
282		}
283
284		var buf bytes.Buffer
285		err = tpl.Execute(&buf, nil)
286		if err != nil {
287			t.Error(err)
288		}
289
290		if buf.String() != v.Expected {
291			v1 := strings.Replace(v.Expected, "\x1b", "", -1)
292			v2 := strings.Replace(buf.String(), "\x1b", "", -1)
293			t.Errorf("Expected %s, got %s", v1, v2)
294		}
295	}
296}
297
298func TestEnvNoColor(t *testing.T) {
299	tests := []struct {
300		name     string
301		environ  []string
302		expected bool
303	}{
304		{"no env", nil, false},
305		{"no_color", []string{"NO_COLOR", "Y"}, true},
306		{"no_color+clicolor=1", []string{"NO_COLOR", "Y", "CLICOLOR", "1"}, true},
307		{"no_color+clicolor_force=1", []string{"NO_COLOR", "Y", "CLICOLOR_FORCE", "1"}, true},
308		{"clicolor=0", []string{"CLICOLOR", "0"}, true},
309		{"clicolor=1", []string{"CLICOLOR", "1"}, false},
310		{"clicolor_force=1", []string{"CLICOLOR_FORCE", "0"}, false},
311		{"clicolor_force=0", []string{"CLICOLOR_FORCE", "1"}, false},
312		{"clicolor=0+clicolor_force=1", []string{"CLICOLOR", "0", "CLICOLOR_FORCE", "1"}, false},
313		{"clicolor=1+clicolor_force=1", []string{"CLICOLOR", "1", "CLICOLOR_FORCE", "1"}, false},
314		{"clicolor=0+clicolor_force=0", []string{"CLICOLOR", "0", "CLICOLOR_FORCE", "0"}, true},
315		{"clicolor=1+clicolor_force=0", []string{"CLICOLOR", "1", "CLICOLOR_FORCE", "0"}, false},
316	}
317	for _, test := range tests {
318		t.Run(test.name, func(t *testing.T) {
319			defer func() {
320				os.Unsetenv("NO_COLOR")
321				os.Unsetenv("CLICOLOR")
322				os.Unsetenv("CLICOLOR_FORCE")
323			}()
324			for i := 0; i < len(test.environ); i += 2 {
325				os.Setenv(test.environ[i], test.environ[i+1])
326			}
327			actual := EnvNoColor()
328			if test.expected != actual {
329				t.Errorf("expected %t but was %t", test.expected, actual)
330			}
331		})
332	}
333}
334