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