Monorepo for Tangled tangled.org
5

Configure Feed

Select the types of activity you want to include in your feed.

1package pipelines 2 3import ( 4 "strings" 5 "testing" 6) 7 8func TestAnsiState_SingleLine(t *testing.T) { 9 tests := []struct { 10 name string 11 input string 12 wantContain string // substring expected in rendered HTML 13 }{ 14 {"bold", "\033[1mBold\033[0m", "term-fg1"}, 15 {"red", "\033[31mRed\033[0m", "term-fg31"}, 16 {"green", "\033[32mGreen\033[0m", "term-fg32"}, 17 {"yellow", "\033[33mYellow\033[0m", "term-fg33"}, 18 {"blue", "\033[34mBlue\033[0m", "term-fg34"}, 19 {"magenta", "\033[35mMagenta\033[0m", "term-fg35"}, 20 {"cyan", "\033[36mCyan\033[0m", "term-fg36"}, 21 {"bold green", "\033[1;32mBold Green\033[0m", "term-fg32"}, 22 {"red background", "\033[41m Red background \033[0m", "term-bg41"}, 23 {"256 orange", "\033[38;5;208mANSI 256 orange\033[0m", "term-fgx208"}, 24 // true color is stripped 25 {"true color", "\033[38;2;255;100;0mTrue color orange\033[0m", "True color orange"}, 26 } 27 for _, tt := range tests { 28 t.Run(tt.name, func(t *testing.T) { 29 a := NewAnsiState() 30 got := string(a.Render(tt.input)) 31 t.Logf("%s → %s", tt.name, got) 32 if !strings.Contains(got, tt.wantContain) { 33 t.Errorf("expected output to contain %q, got: %s", tt.wantContain, got) 34 } 35 }) 36 } 37} 38 39func TestAnsiState_MultiLine_CarryOver(t *testing.T) { 40 // red opened on line 1 without reset — line 2 should carry it over. 41 a := NewAnsiState() 42 43 line1 := a.Render("\033[31mstart of red") 44 if !strings.Contains(string(line1), "term-fg31") { 45 t.Errorf("line1: expected term-fg31, got: %s", line1) 46 } 47 48 line2 := a.Render("still red\033[0m") 49 t.Logf("line2 → %s", line2) 50 if !strings.Contains(string(line2), "term-fg31") { 51 t.Errorf("line2: expected carry-over term-fg31, got: %s", line2) 52 } 53 54 // after the reset, line 3 should have no colour. 55 line3 := a.Render("plain text") 56 t.Logf("line3 → %s", line3) 57 if strings.Contains(string(line3), "term-fg31") { 58 t.Errorf("line3: expected no term-fg31 after reset, got: %s", line3) 59 } 60} 61 62func TestAnsiState_MultiLine_StackedSequences(t *testing.T) { 63 // bold opened line 1, red added line 2 — both should carry to line 2. 64 a := NewAnsiState() 65 66 a.Render("\033[1mBold opened") 67 68 line2 := a.Render("\033[31mRed added") 69 t.Logf("line2 → %s", line2) 70 if !strings.Contains(string(line2), "term-fg1") { 71 t.Errorf("line2: expected carried-over term-fg1, got: %s", line2) 72 } 73 if !strings.Contains(string(line2), "term-fg31") { 74 t.Errorf("line2: expected term-fg31, got: %s", line2) 75 } 76 77 a.Render("\033[0mReset") 78 79 line4 := a.Render("plain") 80 t.Logf("line4 → %s", line4) 81 if strings.Contains(string(line4), "term-") { 82 t.Errorf("line4: expected no term- classes after reset, got: %s", line4) 83 } 84} 85 86func TestAnsiState_Reset_ClearsStack(t *testing.T) { 87 a := NewAnsiState() 88 a.Render("\033[31m\033[1m\033[33mMultiple opens") 89 if len(a.stack) != 3 { 90 t.Errorf("expected stack depth 3, got %d", len(a.stack)) 91 } 92 a.Render("\033[0mReset line") 93 if len(a.stack) != 0 { 94 t.Errorf("expected empty stack after reset, got %d: %v", len(a.stack), a.stack) 95 } 96} 97 98func TestAnsiState_NoAnsi(t *testing.T) { 99 a := NewAnsiState() 100 got := string(a.Render("plain text no escapes")) 101 t.Logf("got → %s", got) 102 if strings.Contains(got, "term-") { 103 t.Errorf("expected no term- classes for plain text, got: %s", got) 104 } 105} 106 107func TestAnsiState_Sanitizer_XSS(t *testing.T) { 108 tests := []struct { 109 name string 110 input string 111 shuoldBeAbsent string // must not appear literally (unescaped) in output 112 }{ 113 { 114 name: "script tag not executable", 115 input: "<script>alert(1)</script>", 116 shuoldBeAbsent: "<script>", 117 }, 118 { 119 name: "img onerror not executable", 120 input: `<img src=x onerror="alert(1)">`, 121 shuoldBeAbsent: "<img", 122 }, 123 { 124 name: "ansi with embedded script not executable", 125 input: "\033[31m<script>alert(1)</script>\033[0m", 126 shuoldBeAbsent: "<script>", 127 }, 128 { 129 name: "only term- classes survive on span", 130 input: "\033[31mcolored\033[0m", 131 shuoldBeAbsent: "onclick", 132 }, 133 } 134 for _, tt := range tests { 135 t.Run(tt.name, func(t *testing.T) { 136 a := NewAnsiState() 137 got := string(a.Render(tt.input)) 138 t.Logf("%s → %s", tt.name, got) 139 if strings.Contains(got, tt.shuoldBeAbsent) { 140 t.Errorf("expected %q to be absent (unescaped), got: %s", tt.shuoldBeAbsent, got) 141 } 142 }) 143 } 144}