okami.mom/ultraviolet

git clone https://git.nkp.pet/okami.mom/ultraviolet

Ayman Bagabaschore: tests: add StyleDiff unit tests99ea455

upstream
16.2 KiB749 linesraw
1package uv
2
3import (
4	"image/color"
5	"reflect"
6	"testing"
7
8	"github.com/charmbracelet/colorprofile"
9)
10
11func TestConvertStyle(t *testing.T) {
12	s := Style{
13		Fg:             color.Black,
14		Bg:             color.White,
15		UnderlineColor: color.Black,
16	}
17	tests := []struct {
18		name    string
19		profile colorprofile.Profile
20		want    Style
21	}{
22		{
23			name:    "True Color",
24			profile: colorprofile.TrueColor,
25			want:    s,
26		},
27		{
28			name:    "256 Color",
29			profile: colorprofile.ANSI256,
30			want: Style{
31				Fg:             colorprofile.ANSI256.Convert(color.Black),
32				Bg:             colorprofile.ANSI256.Convert(color.White),
33				UnderlineColor: colorprofile.ANSI256.Convert(color.Black),
34			},
35		},
36		{
37			name:    "16 Color",
38			profile: colorprofile.ANSI,
39			want: Style{
40				Fg:             colorprofile.ANSI.Convert(color.Black),
41				Bg:             colorprofile.ANSI.Convert(color.White),
42				UnderlineColor: colorprofile.ANSI.Convert(color.Black),
43			},
44		},
45		{
46			name:    "Grayscale",
47			profile: colorprofile.Ascii,
48			want: Style{
49				Fg:             nil,
50				Bg:             nil,
51				UnderlineColor: nil,
52			},
53		},
54		{
55			name:    "No Profile",
56			profile: colorprofile.NoTTY,
57			want:    Style{},
58		},
59	}
60	for _, tt := range tests {
61		t.Run(tt.name, func(t *testing.T) {
62			if got := ConvertStyle(s, tt.profile); !reflect.DeepEqual(got, tt.want) {
63				t.Errorf("ConvertStyle() = %+v, want %+v", got, tt.want)
64			}
65		})
66	}
67}
68
69func TestConvertLink(t *testing.T) {
70	l := Link{
71		URL:    "https://example.com",
72		Params: "id=1",
73	}
74	tests := []struct {
75		name    string
76		profile colorprofile.Profile
77		want    Link
78	}{
79		{
80			name:    "True Color",
81			profile: colorprofile.TrueColor,
82			want:    l,
83		},
84		{
85			name:    "No TTY",
86			profile: colorprofile.NoTTY,
87			want:    Link{},
88		},
89	}
90	for _, tt := range tests {
91		t.Run(tt.name, func(t *testing.T) {
92			if got := ConvertLink(l, tt.profile); !reflect.DeepEqual(got, tt.want) {
93				t.Errorf("ConvertLink() = %+v, want %+v", got, tt.want)
94			}
95		})
96	}
97}
98
99func TestStyleDiff(t *testing.T) {
100	red := color.RGBA{255, 0, 0, 255}
101	blue := color.RGBA{0, 0, 255, 255}
102	green := color.RGBA{0, 255, 0, 255}
103	yellow := color.RGBA{255, 255, 0, 255}
104	cyan := color.RGBA{0, 255, 255, 255}
105	magenta := color.RGBA{255, 0, 255, 255}
106
107	tests := []struct {
108		name string
109		from *Style
110		to   *Style
111		want string
112	}{
113		// Nil and zero cases
114		{
115			name: "both nil",
116			from: nil,
117			to:   nil,
118			want: "",
119		},
120		{
121			name: "from nil to zero",
122			from: nil,
123			to:   &Style{},
124			want: "\x1b[m",
125		},
126		{
127			name: "from zero to zero",
128			from: &Style{},
129			to:   &Style{},
130			want: "",
131		},
132		{
133			name: "from nil to styled",
134			from: nil,
135			to:   &Style{Fg: red, Attrs: AttrBold},
136			want: "\x1b[1;38;2;255;0;0m",
137		},
138
139		// Foreground color tests
140		{
141			name: "foreground color change",
142			from: &Style{Fg: red},
143			to:   &Style{Fg: blue},
144			want: "\x1b[38;2;0;0;255m",
145		},
146		{
147			name: "add foreground color",
148			from: &Style{},
149			to:   &Style{Fg: red},
150			want: "\x1b[38;2;255;0;0m",
151		},
152		{
153			name: "remove foreground color",
154			from: &Style{Fg: red},
155			to:   &Style{},
156			want: "\x1b[m",
157		},
158		{
159			name: "foreground color same",
160			from: &Style{Fg: red},
161			to:   &Style{Fg: red},
162			want: "",
163		},
164
165		// Background color tests
166		{
167			name: "background color change",
168			from: &Style{Bg: red},
169			to:   &Style{Bg: blue},
170			want: "\x1b[48;2;0;0;255m",
171		},
172		{
173			name: "add background color",
174			from: &Style{},
175			to:   &Style{Bg: blue},
176			want: "\x1b[48;2;0;0;255m",
177		},
178		{
179			name: "remove background color",
180			from: &Style{Bg: blue},
181			to:   &Style{},
182			want: "\x1b[m",
183		},
184		{
185			name: "background color same",
186			from: &Style{Bg: blue},
187			to:   &Style{Bg: blue},
188			want: "",
189		},
190
191		// Underline color tests
192		{
193			name: "underline color change",
194			from: &Style{UnderlineColor: red, Underline: UnderlineStyleSingle},
195			to:   &Style{UnderlineColor: blue, Underline: UnderlineStyleSingle},
196			want: "\x1b[58;2;0;0;255m",
197		},
198		{
199			name: "add underline color",
200			from: &Style{Underline: UnderlineStyleSingle},
201			to:   &Style{UnderlineColor: green, Underline: UnderlineStyleSingle},
202			want: "\x1b[58;2;0;255;0m",
203		},
204		{
205			name: "remove underline color",
206			from: &Style{UnderlineColor: green, Underline: UnderlineStyleSingle},
207			to:   &Style{Underline: UnderlineStyleSingle},
208			want: "\x1b[59m",
209		},
210		{
211			name: "underline color same",
212			from: &Style{UnderlineColor: green, Underline: UnderlineStyleSingle},
213			to:   &Style{UnderlineColor: green, Underline: UnderlineStyleSingle},
214			want: "",
215		},
216
217		// Bold attribute tests
218		{
219			name: "add bold",
220			from: &Style{},
221			to:   &Style{Attrs: AttrBold},
222			want: "\x1b[1m",
223		},
224		{
225			name: "remove bold",
226			from: &Style{Attrs: AttrBold},
227			to:   &Style{},
228			want: "\x1b[m",
229		},
230		{
231			name: "keep bold",
232			from: &Style{Attrs: AttrBold},
233			to:   &Style{Attrs: AttrBold},
234			want: "",
235		},
236
237		// Faint attribute tests
238		{
239			name: "add faint",
240			from: &Style{},
241			to:   &Style{Attrs: AttrFaint},
242			want: "\x1b[2m",
243		},
244		{
245			name: "remove faint",
246			from: &Style{Attrs: AttrFaint},
247			to:   &Style{},
248			want: "\x1b[m",
249		},
250		{
251			name: "keep faint",
252			from: &Style{Attrs: AttrFaint},
253			to:   &Style{Attrs: AttrFaint},
254			want: "",
255		},
256		{
257			name: "bold to faint",
258			from: &Style{Attrs: AttrBold},
259			to:   &Style{Attrs: AttrFaint},
260			want: "\x1b[22;2m",
261		},
262		{
263			name: "faint to bold",
264			from: &Style{Attrs: AttrFaint},
265			to:   &Style{Attrs: AttrBold},
266			want: "\x1b[22;1m",
267		},
268		{
269			name: "bold and faint to bold",
270			from: &Style{Attrs: AttrBold | AttrFaint},
271			to:   &Style{Attrs: AttrBold},
272			want: "\x1b[22;1m",
273		},
274		{
275			name: "bold to bold and faint",
276			from: &Style{Attrs: AttrBold},
277			to:   &Style{Attrs: AttrBold | AttrFaint},
278			want: "\x1b[2m",
279		},
280
281		// Italic attribute tests
282		{
283			name: "add italic",
284			from: &Style{},
285			to:   &Style{Attrs: AttrItalic},
286			want: "\x1b[3m",
287		},
288		{
289			name: "remove italic",
290			from: &Style{Attrs: AttrItalic},
291			to:   &Style{},
292			want: "\x1b[m",
293		},
294		{
295			name: "keep italic",
296			from: &Style{Attrs: AttrItalic},
297			to:   &Style{Attrs: AttrItalic},
298			want: "",
299		},
300
301		// Bold and Italic combination tests
302		{
303			name: "bold to bold and italic",
304			from: &Style{Attrs: AttrBold},
305			to:   &Style{Attrs: AttrBold | AttrItalic},
306			want: "\x1b[3m",
307		},
308		{
309			name: "bold and italic to bold",
310			from: &Style{Attrs: AttrBold | AttrItalic},
311			to:   &Style{Attrs: AttrBold},
312			want: "\x1b[23m",
313		},
314
315		// Bold, Faint, and Italic combination tests
316		{
317			name: "bold and faint to italic",
318			from: &Style{Attrs: AttrBold | AttrFaint},
319			to:   &Style{Attrs: AttrItalic},
320			want: "\x1b[22;3m",
321		},
322		{
323			name: "italic to bold and faint",
324			from: &Style{Attrs: AttrItalic},
325			to:   &Style{Attrs: AttrBold | AttrFaint},
326			want: "\x1b[23;1;2m",
327		},
328		{
329			name: "bold, faint, and italic to bold",
330			from: &Style{Attrs: AttrBold | AttrFaint | AttrItalic},
331			to:   &Style{Attrs: AttrBold},
332			want: "\x1b[22;23;1m",
333		},
334		{
335			name: "bold to bold, faint, and italic",
336			from: &Style{Attrs: AttrBold},
337			to:   &Style{Attrs: AttrBold | AttrFaint | AttrItalic},
338			want: "\x1b[2;3m",
339		},
340		{
341			name: "faint to bold and italic",
342			from: &Style{Attrs: AttrFaint},
343			to:   &Style{Attrs: AttrBold | AttrItalic},
344			want: "\x1b[22;1;3m",
345		},
346		{
347			name: "italic to bold and faint",
348			from: &Style{Attrs: AttrItalic},
349			to:   &Style{Attrs: AttrBold | AttrFaint},
350			want: "\x1b[23;1;2m",
351		},
352		{
353			name: "bold, faint, and italic to faint",
354			from: &Style{Attrs: AttrBold | AttrFaint | AttrItalic},
355			to:   &Style{Attrs: AttrFaint},
356			want: "\x1b[22;23;2m",
357		},
358		{
359			name: "bold, faint, and italic to italic",
360			from: &Style{Attrs: AttrBold | AttrFaint | AttrItalic},
361			to:   &Style{Attrs: AttrItalic},
362			want: "\x1b[22m",
363		},
364		{
365			name: "faint to bold, faint, and italic",
366			from: &Style{Attrs: AttrFaint},
367			to:   &Style{Attrs: AttrBold | AttrFaint | AttrItalic},
368			want: "\x1b[1;3m",
369		},
370		{
371			name: "italic to bold, faint, and italic",
372			from: &Style{Attrs: AttrItalic},
373			to:   &Style{Attrs: AttrBold | AttrFaint | AttrItalic},
374			want: "\x1b[1;2m",
375		},
376		{
377			name: "bold, faint, and italic to bold, faint, and italic",
378			from: &Style{Attrs: AttrBold | AttrFaint | AttrItalic},
379			to:   &Style{Attrs: AttrBold | AttrFaint | AttrItalic},
380			want: "",
381		},
382		{
383			name: "no attributes to bold, faint, and italic",
384			from: &Style{},
385			to:   &Style{Attrs: AttrBold | AttrFaint | AttrItalic},
386			want: "\x1b[1;2;3m",
387		},
388
389		// Slow blink attribute tests
390		{
391			name: "add slow blink",
392			from: &Style{},
393			to:   &Style{Attrs: AttrBlink},
394			want: "\x1b[5m",
395		},
396		{
397			name: "remove slow blink",
398			from: &Style{Attrs: AttrBlink},
399			to:   &Style{},
400			want: "\x1b[m",
401		},
402		{
403			name: "keep slow blink",
404			from: &Style{Attrs: AttrBlink},
405			to:   &Style{Attrs: AttrBlink},
406			want: "",
407		},
408
409		// Rapid blink attribute tests
410		{
411			name: "add rapid blink",
412			from: &Style{},
413			to:   &Style{Attrs: AttrRapidBlink},
414			want: "\x1b[6m",
415		},
416		{
417			name: "remove rapid blink",
418			from: &Style{Attrs: AttrRapidBlink},
419			to:   &Style{},
420			want: "\x1b[m",
421		},
422		{
423			name: "keep rapid blink",
424			from: &Style{Attrs: AttrRapidBlink},
425			to:   &Style{Attrs: AttrRapidBlink},
426			want: "",
427		},
428		{
429			name: "change from slow to rapid blink",
430			from: &Style{Attrs: AttrBlink},
431			to:   &Style{Attrs: AttrRapidBlink},
432			want: "\x1b[25;6m",
433		},
434		{
435			name: "change from rapid to slow blink",
436			from: &Style{Attrs: AttrRapidBlink},
437			to:   &Style{Attrs: AttrBlink},
438			want: "\x1b[25;5m",
439		},
440		{
441			name: "slow and rapid blink to slow blink",
442			from: &Style{Attrs: AttrBlink | AttrRapidBlink},
443			to:   &Style{Attrs: AttrBlink},
444			want: "\x1b[25;5m",
445		},
446
447		// Reverse attribute tests
448		{
449			name: "add reverse",
450			from: &Style{},
451			to:   &Style{Attrs: AttrReverse},
452			want: "\x1b[7m",
453		},
454		{
455			name: "remove reverse",
456			from: &Style{Attrs: AttrReverse},
457			to:   &Style{},
458			want: "\x1b[m",
459		},
460		{
461			name: "keep reverse",
462			from: &Style{Attrs: AttrReverse},
463			to:   &Style{Attrs: AttrReverse},
464			want: "",
465		},
466
467		// Conceal attribute tests
468		{
469			name: "add conceal",
470			from: &Style{},
471			to:   &Style{Attrs: AttrConceal},
472			want: "\x1b[8m",
473		},
474		{
475			name: "remove conceal",
476			from: &Style{Attrs: AttrConceal},
477			to:   &Style{},
478			want: "\x1b[m",
479		},
480		{
481			name: "keep conceal",
482			from: &Style{Attrs: AttrConceal},
483			to:   &Style{Attrs: AttrConceal},
484			want: "",
485		},
486
487		// Strikethrough attribute tests
488		{
489			name: "add strikethrough",
490			from: &Style{},
491			to:   &Style{Attrs: AttrStrikethrough},
492			want: "\x1b[9m",
493		},
494		{
495			name: "remove strikethrough",
496			from: &Style{Attrs: AttrStrikethrough},
497			to:   &Style{},
498			want: "\x1b[m",
499		},
500		{
501			name: "keep strikethrough",
502			from: &Style{Attrs: AttrStrikethrough},
503			to:   &Style{Attrs: AttrStrikethrough},
504			want: "",
505		},
506
507		// Underline style tests
508		{
509			name: "add single underline",
510			from: &Style{},
511			to:   &Style{Underline: UnderlineStyleSingle},
512			want: "\x1b[4m",
513		},
514		{
515			name: "add double underline",
516			from: &Style{},
517			to:   &Style{Underline: UnderlineStyleDouble},
518			want: "\x1b[4:2m",
519		},
520		{
521			name: "add curly underline",
522			from: &Style{},
523			to:   &Style{Underline: UnderlineStyleCurly},
524			want: "\x1b[4:3m",
525		},
526		{
527			name: "add dotted underline",
528			from: &Style{},
529			to:   &Style{Underline: UnderlineStyleDotted},
530			want: "\x1b[4:4m",
531		},
532		{
533			name: "add dashed underline",
534			from: &Style{},
535			to:   &Style{Underline: UnderlineStyleDashed},
536			want: "\x1b[4:5m",
537		},
538		{
539			name: "change underline style single to double",
540			from: &Style{Underline: UnderlineStyleSingle},
541			to:   &Style{Underline: UnderlineStyleDouble},
542			want: "\x1b[4:2m",
543		},
544		{
545			name: "change underline style double to curly",
546			from: &Style{Underline: UnderlineStyleDouble},
547			to:   &Style{Underline: UnderlineStyleCurly},
548			want: "\x1b[4:3m",
549		},
550		{
551			name: "remove underline",
552			from: &Style{Underline: UnderlineStyleSingle},
553			to:   &Style{},
554			want: "\x1b[m",
555		},
556		{
557			name: "keep underline style",
558			from: &Style{Underline: UnderlineStyleSingle},
559			to:   &Style{Underline: UnderlineStyleSingle},
560			want: "",
561		},
562
563		// Multiple attribute combinations
564		{
565			name: "add multiple attributes",
566			from: &Style{},
567			to:   &Style{Attrs: AttrBold | AttrItalic, Underline: UnderlineStyleSingle},
568			want: "\x1b[1;3;4m",
569		},
570		{
571			name: "remove multiple attributes",
572			from: &Style{Attrs: AttrBold | AttrItalic | AttrReverse},
573			to:   &Style{},
574			want: "\x1b[m",
575		},
576		{
577			name: "combine multiple attribute changes",
578			from: &Style{Attrs: AttrBold | AttrItalic},
579			to:   &Style{Attrs: AttrBold | AttrReverse},
580			want: "\x1b[23;7m",
581		},
582		{
583			name: "swap italic and strikethrough",
584			from: &Style{Attrs: AttrItalic},
585			to:   &Style{Attrs: AttrStrikethrough},
586			want: "\x1b[23;9m",
587		},
588		{
589			name: "all attributes added",
590			from: &Style{},
591			to:   &Style{Attrs: AttrBold | AttrFaint | AttrItalic | AttrBlink | AttrRapidBlink | AttrReverse | AttrConceal | AttrStrikethrough},
592			want: "\x1b[1;2;3;5;6;7;8;9m",
593		},
594		{
595			name: "all attributes removed",
596			from: &Style{Attrs: AttrBold | AttrFaint | AttrItalic | AttrBlink | AttrRapidBlink | AttrReverse | AttrConceal | AttrStrikethrough},
597			to:   &Style{},
598			want: "\x1b[m",
599		},
600
601		// Complex style changes with colors and attributes
602		{
603			name: "complex style change with all properties",
604			from: &Style{
605				Fg:    red,
606				Bg:    blue,
607				Attrs: AttrBold,
608			},
609			to: &Style{
610				Fg:             green,
611				Bg:             yellow,
612				UnderlineColor: cyan,
613				Attrs:          AttrItalic,
614				Underline:      UnderlineStyleSingle,
615			},
616			want: "\x1b[38;2;0;255;0;48;2;255;255;0;58;2;0;255;255;22;3;4m",
617		},
618		{
619			name: "complex change keeping some properties",
620			from: &Style{
621				Fg:        red,
622				Bg:        blue,
623				Attrs:     AttrBold | AttrItalic,
624				Underline: UnderlineStyleSingle,
625			},
626			to: &Style{
627				Fg:        red,
628				Bg:        green,
629				Attrs:     AttrBold | AttrReverse,
630				Underline: UnderlineStyleDouble,
631			},
632			want: "\x1b[48;2;0;255;0;23;7;4:2m",
633		},
634		{
635			name: "complete style reset",
636			from: &Style{
637				Fg:             red,
638				Bg:             blue,
639				UnderlineColor: green,
640				Attrs:          AttrBold | AttrItalic | AttrReverse,
641				Underline:      UnderlineStyleSingle,
642			},
643			to:   &Style{},
644			want: "\x1b[m",
645		},
646
647		// Edge cases
648		{
649			name: "no changes with all properties",
650			from: &Style{
651				Fg:             red,
652				Bg:             blue,
653				UnderlineColor: green,
654				Attrs:          AttrBold | AttrItalic,
655				Underline:      UnderlineStyleSingle,
656			},
657			to: &Style{
658				Fg:             red,
659				Bg:             blue,
660				UnderlineColor: green,
661				Attrs:          AttrBold | AttrItalic,
662				Underline:      UnderlineStyleSingle,
663			},
664			want: "",
665		},
666		{
667			name: "only colors change",
668			from: &Style{
669				Fg:    red,
670				Bg:    blue,
671				Attrs: AttrBold,
672			},
673			to: &Style{
674				Fg:    green,
675				Bg:    yellow,
676				Attrs: AttrBold,
677			},
678			want: "\x1b[38;2;0;255;0;48;2;255;255;0m",
679		},
680		{
681			name: "only attributes change",
682			from: &Style{
683				Fg:    red,
684				Attrs: AttrBold,
685			},
686			to: &Style{
687				Fg:    red,
688				Attrs: AttrItalic,
689			},
690			want: "\x1b[22;3m",
691		},
692		{
693			name: "add all colors",
694			from: &Style{},
695			to: &Style{
696				Fg:             red,
697				Bg:             blue,
698				UnderlineColor: green,
699				Underline:      UnderlineStyleSingle,
700			},
701			want: "\x1b[38;2;255;0;0;48;2;0;0;255;58;2;0;255;0;4m",
702		},
703		{
704			name: "add all colors without underline",
705			from: &Style{},
706			to: &Style{
707				Fg:             red,
708				Bg:             blue,
709				UnderlineColor: green,
710			},
711			want: "\x1b[38;2;255;0;0;48;2;0;0;255;58;2;0;255;0m",
712		},
713		{
714			name: "remove all colors with attributes",
715			from: &Style{
716				Fg:    red,
717				Bg:    blue,
718				Attrs: AttrBold,
719			},
720			to: &Style{
721				Attrs: AttrBold,
722			},
723			want: "\x1b[39;49m",
724		},
725		{
726			name: "change all colors",
727			from: &Style{
728				Fg:             red,
729				Bg:             blue,
730				UnderlineColor: green,
731			},
732			to: &Style{
733				Fg:             cyan,
734				Bg:             magenta,
735				UnderlineColor: yellow,
736			},
737			want: "\x1b[38;2;0;255;255;48;2;255;0;255;58;2;255;255;0m",
738		},
739	}
740
741	for _, tt := range tests {
742		t.Run(tt.name, func(t *testing.T) {
743			got := StyleDiff(tt.from, tt.to)
744			if got != tt.want {
745				t.Errorf("StyleDiff() = %q, want %q", got, tt.want)
746			}
747		})
748	}
749}