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