1package view
2
3import (
4 "os"
5 "strings"
6 "testing"
7
8 "github.com/charmbracelet/lipgloss"
9)
10
11func TestDecodeQuotedPrintable(t *testing.T) {
12 testCases := []struct {
13 name string
14 input string
15 expected string
16 }{
17 {
18 name: "Simple case",
19 input: "Hello=2C world=21",
20 expected: "Hello, world!",
21 },
22 {
23 name: "With soft line break",
24 input: "This is a long line that gets wrapped=\r\n and continues here.",
25 expected: "This is a long line that gets wrapped and continues here.",
26 },
27 {
28 name: "No encoding",
29 input: "Just a plain string.",
30 expected: "Just a plain string.",
31 },
32 }
33
34 for _, tc := range testCases {
35 t.Run(tc.name, func(t *testing.T) {
36 decoded, err := decodeQuotedPrintable(tc.input)
37 if err != nil {
38 t.Fatalf("decodeQuotedPrintable() failed: %v", err)
39 }
40 if decoded != tc.expected {
41 t.Errorf("Expected %q, got %q", tc.expected, decoded)
42 }
43 })
44 }
45}
46
47func TestMarkdownToHTML(t *testing.T) {
48 testCases := []struct {
49 name string
50 input string
51 expected string
52 }{
53 {
54 name: "Heading",
55 input: "# Hello",
56 expected: "<h1>Hello</h1>",
57 },
58 {
59 name: "Bold",
60 input: "**bold text**",
61 expected: "<p><strong>bold text</strong></p>",
62 },
63 {
64 name: "Link",
65 input: "[link](http://example.com)",
66 expected: `<p><a href="http://example.com">link</a></p>`,
67 },
68 }
69
70 for _, tc := range testCases {
71 t.Run(tc.name, func(t *testing.T) {
72 html := markdownToHTML([]byte(tc.input))
73 // Trim newlines for consistent comparison
74 if strings.TrimSpace(string(html)) != tc.expected {
75 t.Errorf("Expected %s, got %s", tc.expected, html)
76 }
77 })
78 }
79}
80
81func TestGhosttySupported(t *testing.T) {
82 // Save original environment variables
83 origTerm := os.Getenv("TERM")
84 origTermProgram := os.Getenv("TERM_PROGRAM")
85 origGhosttyResources := os.Getenv("GHOSTTY_RESOURCES_DIR")
86
87 // Restore environment variables after test
88 defer func() {
89 os.Setenv("TERM", origTerm)
90 os.Setenv("TERM_PROGRAM", origTermProgram)
91 os.Setenv("GHOSTTY_RESOURCES_DIR", origGhosttyResources)
92 }()
93
94 testCases := []struct {
95 name string
96 term string
97 termProgram string
98 ghosttyResourcesDir string
99 expected bool
100 }{
101 {
102 name: "No Ghostty environment variables",
103 term: "xterm",
104 termProgram: "",
105 ghosttyResourcesDir: "",
106 expected: false,
107 },
108 {
109 name: "TERM contains ghostty",
110 term: "xterm-ghostty",
111 termProgram: "",
112 ghosttyResourcesDir: "",
113 expected: true,
114 },
115 {
116 name: "TERM_PROGRAM is ghostty",
117 term: "xterm",
118 termProgram: "ghostty",
119 ghosttyResourcesDir: "",
120 expected: true,
121 },
122 {
123 name: "GHOSTTY_RESOURCES_DIR is set",
124 term: "xterm",
125 termProgram: "",
126 ghosttyResourcesDir: "/usr/share/ghostty",
127 expected: true,
128 },
129 {
130 name: "Multiple Ghostty indicators",
131 term: "ghostty",
132 termProgram: "ghostty",
133 ghosttyResourcesDir: "/usr/share/ghostty",
134 expected: true,
135 },
136 }
137
138 for _, tc := range testCases {
139 t.Run(tc.name, func(t *testing.T) {
140 os.Setenv("TERM", tc.term)
141 os.Setenv("TERM_PROGRAM", tc.termProgram)
142 os.Setenv("GHOSTTY_RESOURCES_DIR", tc.ghosttyResourcesDir)
143
144 result := ghosttySupported()
145 if result != tc.expected {
146 t.Errorf("Expected %t, got %t", tc.expected, result)
147 }
148 })
149 }
150}
151
152func TestImageProtocolSupported(t *testing.T) {
153 // Save original environment variables
154 origTerm := os.Getenv("TERM")
155 origKittyWindow := os.Getenv("KITTY_WINDOW_ID")
156 origTermProgram := os.Getenv("TERM_PROGRAM")
157 origGhosttyResources := os.Getenv("GHOSTTY_RESOURCES_DIR")
158
159 // Restore environment variables after test
160 defer func() {
161 os.Setenv("TERM", origTerm)
162 os.Setenv("KITTY_WINDOW_ID", origKittyWindow)
163 os.Setenv("TERM_PROGRAM", origTermProgram)
164 os.Setenv("GHOSTTY_RESOURCES_DIR", origGhosttyResources)
165 }()
166
167 testCases := []struct {
168 name string
169 term string
170 kittyWindow string
171 termProgram string
172 ghosttyResourcesDir string
173 expected bool
174 }{
175 {
176 name: "No supported terminals",
177 term: "xterm",
178 kittyWindow: "",
179 termProgram: "",
180 ghosttyResourcesDir: "",
181 expected: false,
182 },
183 {
184 name: "Kitty supported",
185 term: "xterm-kitty",
186 kittyWindow: "",
187 termProgram: "",
188 ghosttyResourcesDir: "",
189 expected: true,
190 },
191 {
192 name: "Ghostty supported",
193 term: "xterm",
194 kittyWindow: "",
195 termProgram: "ghostty",
196 ghosttyResourcesDir: "",
197 expected: true,
198 },
199 {
200 name: "Both Kitty and Ghostty supported",
201 term: "xterm-kitty",
202 kittyWindow: "1",
203 termProgram: "ghostty",
204 ghosttyResourcesDir: "/usr/share/ghostty",
205 expected: true,
206 },
207 }
208
209 for _, tc := range testCases {
210 t.Run(tc.name, func(t *testing.T) {
211 os.Setenv("TERM", tc.term)
212 os.Setenv("KITTY_WINDOW_ID", tc.kittyWindow)
213 os.Setenv("TERM_PROGRAM", tc.termProgram)
214 os.Setenv("GHOSTTY_RESOURCES_DIR", tc.ghosttyResourcesDir)
215
216 result := imageProtocolSupported()
217 if result != tc.expected {
218 t.Errorf("Expected %t, got %t", tc.expected, result)
219 }
220 })
221 }
222}
223
224func TestProcessBody(t *testing.T) {
225 h1Style := lipgloss.NewStyle().SetString("H1")
226 h2Style := lipgloss.NewStyle().SetString("H2")
227 bodyStyle := lipgloss.NewStyle().SetString("BODY")
228
229 testCases := []struct {
230 name string
231 input string
232 expected string
233 }{
234 {
235 name: "Simple HTML",
236 input: "<p>Hello, world!</p>",
237 expected: "Hello, world!",
238 },
239 {
240 name: "With link HTML",
241 input: `<a href="http://example.com">Click here</a>`,
242 expected: "Click here",
243 },
244 {
245 name: "With image HTML",
246 input: `<img src="http://example.com/img.png" alt="alt text">`,
247 expected: "[Click here to view image: alt text]",
248 },
249 {
250 name: "With headers HTML",
251 input: "<h1>Header 1</h1>",
252 expected: "Header 1",
253 },
254 {
255 name: "With link Markdown",
256 input: `[Click here](http://example.com)`,
257 expected: "Click here",
258 },
259 {
260 name: "With image Markdown",
261 input: `>`,
262 expected: "[Click here to view image: alt text]",
263 },
264 {
265 name: "With headers Markdown",
266 input: "# Header 1",
267 expected: "Header 1",
268 },
269 }
270
271 for _, tc := range testCases {
272 t.Run(tc.name, func(t *testing.T) {
273 processed, err := ProcessBody(tc.input, h1Style, h2Style, bodyStyle)
274 if err != nil {
275 t.Fatalf("ProcessBody() failed: %v", err)
276 }
277 // Use Contains because styles add ANSI codes
278 if !strings.Contains(processed, tc.expected) {
279 t.Errorf("Processed body does not contain expected text.\nGot: %q\nWant to contain: %q", processed, tc.expected)
280 }
281 })
282 }
283}