1package view
2
3import (
4 "os"
5 "strings"
6 "testing"
7
8 "github.com/charmbracelet/lipgloss"
9)
10
11// clearAllTerminalEnv clears all environment variables that could indicate terminal capabilities
12func clearAllTerminalEnv() {
13 // Clear hyperlink support indicators
14 os.Unsetenv("VTE_VERSION")
15 os.Unsetenv("KITTY_WINDOW_ID")
16 os.Unsetenv("GHOSTTY_RESOURCES_DIR")
17 os.Unsetenv("WEZTERM_EXECUTABLE")
18 os.Unsetenv("WEZTERM_CONFIG_FILE")
19 os.Unsetenv("ITERM_SESSION_ID")
20 os.Unsetenv("ITERM_PROFILE")
21 os.Unsetenv("WARP_IS_LOCAL_SHELL_SESSION")
22 os.Unsetenv("WARP_COMBINED_PROMPT_COMMAND_FINISHED")
23 os.Unsetenv("KONSOLE_DBUS_SESSION")
24 os.Unsetenv("KONSOLE_VERSION")
25
26 // Set basic terminal that doesn't support anything special
27 os.Setenv("TERM", "xterm")
28 os.Setenv("TERM_PROGRAM", "basic")
29}
30
31func TestDecodeQuotedPrintable(t *testing.T) {
32 testCases := []struct {
33 name string
34 input string
35 expected string
36 }{
37 {
38 name: "Simple case",
39 input: "Hello=2C world=21",
40 expected: "Hello, world!",
41 },
42 {
43 name: "With soft line break",
44 input: "This is a long line that gets wrapped=\r\n and continues here.",
45 expected: "This is a long line that gets wrapped and continues here.",
46 },
47 {
48 name: "No encoding",
49 input: "Just a plain string.",
50 expected: "Just a plain string.",
51 },
52 }
53
54 for _, tc := range testCases {
55 t.Run(tc.name, func(t *testing.T) {
56 decoded, err := decodeQuotedPrintable(tc.input)
57 if err != nil {
58 t.Fatalf("decodeQuotedPrintable() failed: %v", err)
59 }
60 if decoded != tc.expected {
61 t.Errorf("Expected %q, got %q", tc.expected, decoded)
62 }
63 })
64 }
65}
66
67func TestMarkdownToHTML(t *testing.T) {
68 testCases := []struct {
69 name string
70 input string
71 expected string
72 }{
73 {
74 name: "Heading",
75 input: "# Hello",
76 expected: "<h1>Hello</h1>",
77 },
78 {
79 name: "Bold",
80 input: "**bold text**",
81 expected: "<p><strong>bold text</strong></p>",
82 },
83 {
84 name: "Link",
85 input: "[link](http://example.com)",
86 expected: `<p><a href="http://example.com">link</a></p>`,
87 },
88 }
89
90 for _, tc := range testCases {
91 t.Run(tc.name, func(t *testing.T) {
92 html := markdownToHTML([]byte(tc.input))
93 // Trim newlines for consistent comparison
94 if strings.TrimSpace(string(html)) != tc.expected {
95 t.Errorf("Expected %s, got %s", tc.expected, html)
96 }
97 })
98 }
99}
100
101func TestGhosttySupported(t *testing.T) {
102 // Save original environment variables
103 origTerm := os.Getenv("TERM")
104 origTermProgram := os.Getenv("TERM_PROGRAM")
105 origGhosttyResources := os.Getenv("GHOSTTY_RESOURCES_DIR")
106
107 // Restore environment variables after test
108 defer func() {
109 os.Setenv("TERM", origTerm)
110 os.Setenv("TERM_PROGRAM", origTermProgram)
111 os.Setenv("GHOSTTY_RESOURCES_DIR", origGhosttyResources)
112 }()
113
114 testCases := []struct {
115 name string
116 term string
117 termProgram string
118 ghosttyResourcesDir string
119 expected bool
120 }{
121 {
122 name: "No Ghostty environment variables",
123 term: "xterm",
124 termProgram: "",
125 ghosttyResourcesDir: "",
126 expected: false,
127 },
128 {
129 name: "TERM contains ghostty",
130 term: "xterm-ghostty",
131 termProgram: "",
132 ghosttyResourcesDir: "",
133 expected: true,
134 },
135 {
136 name: "TERM_PROGRAM is ghostty",
137 term: "xterm",
138 termProgram: "ghostty",
139 ghosttyResourcesDir: "",
140 expected: true,
141 },
142 {
143 name: "GHOSTTY_RESOURCES_DIR is set",
144 term: "xterm",
145 termProgram: "",
146 ghosttyResourcesDir: "/usr/share/ghostty",
147 expected: true,
148 },
149 {
150 name: "Multiple Ghostty indicators",
151 term: "ghostty",
152 termProgram: "ghostty",
153 ghosttyResourcesDir: "/usr/share/ghostty",
154 expected: true,
155 },
156 }
157
158 for _, tc := range testCases {
159 t.Run(tc.name, func(t *testing.T) {
160 os.Setenv("TERM", tc.term)
161 os.Setenv("TERM_PROGRAM", tc.termProgram)
162 os.Setenv("GHOSTTY_RESOURCES_DIR", tc.ghosttyResourcesDir)
163
164 result := ghosttySupported()
165 if result != tc.expected {
166 t.Errorf("Expected %t, got %t", tc.expected, result)
167 }
168 })
169 }
170}
171
172func TestImageProtocolSupported(t *testing.T) {
173 // Save original environment variables
174 origTerm := os.Getenv("TERM")
175 origKittyWindow := os.Getenv("KITTY_WINDOW_ID")
176 origTermProgram := os.Getenv("TERM_PROGRAM")
177 origGhosttyResources := os.Getenv("GHOSTTY_RESOURCES_DIR")
178 origItermlSession := os.Getenv("ITERM_SESSION_ID")
179 origWeztermExec := os.Getenv("WEZTERM_EXECUTABLE")
180 origWarpLocal := os.Getenv("WARP_IS_LOCAL_SHELL_SESSION")
181 origKonsoleDBus := os.Getenv("KONSOLE_DBUS_SESSION")
182
183 // Restore environment variables after test
184 defer func() {
185 os.Setenv("TERM", origTerm)
186 os.Setenv("KITTY_WINDOW_ID", origKittyWindow)
187 os.Setenv("TERM_PROGRAM", origTermProgram)
188 os.Setenv("GHOSTTY_RESOURCES_DIR", origGhosttyResources)
189 os.Setenv("ITERM_SESSION_ID", origItermlSession)
190 os.Setenv("WEZTERM_EXECUTABLE", origWeztermExec)
191 os.Setenv("WARP_IS_LOCAL_SHELL_SESSION", origWarpLocal)
192 os.Setenv("KONSOLE_DBUS_SESSION", origKonsoleDBus)
193 }()
194
195 testCases := []struct {
196 name string
197 setupEnv func()
198 clearAllEnv func()
199 expected bool
200 }{
201 {
202 name: "No supported terminals",
203 setupEnv: func() {
204 os.Setenv("TERM", "xterm")
205 os.Setenv("TERM_PROGRAM", "basic")
206 },
207 clearAllEnv: func() {
208 os.Unsetenv("KITTY_WINDOW_ID")
209 os.Unsetenv("GHOSTTY_RESOURCES_DIR")
210 os.Unsetenv("ITERM_SESSION_ID")
211 os.Unsetenv("WEZTERM_EXECUTABLE")
212 os.Unsetenv("WARP_IS_LOCAL_SHELL_SESSION")
213 os.Unsetenv("KONSOLE_DBUS_SESSION")
214 },
215 expected: false,
216 },
217 {
218 name: "Kitty supported via TERM",
219 setupEnv: func() {
220 os.Setenv("TERM", "xterm-kitty")
221 },
222 clearAllEnv: func() {
223 os.Unsetenv("KITTY_WINDOW_ID")
224 os.Unsetenv("GHOSTTY_RESOURCES_DIR")
225 os.Unsetenv("ITERM_SESSION_ID")
226 os.Unsetenv("WEZTERM_EXECUTABLE")
227 os.Unsetenv("WARP_IS_LOCAL_SHELL_SESSION")
228 os.Unsetenv("KONSOLE_DBUS_SESSION")
229 },
230 expected: true,
231 },
232 {
233 name: "Kitty supported via KITTY_WINDOW_ID",
234 setupEnv: func() {
235 os.Setenv("TERM", "xterm")
236 os.Setenv("KITTY_WINDOW_ID", "1")
237 },
238 clearAllEnv: func() {
239 os.Unsetenv("GHOSTTY_RESOURCES_DIR")
240 os.Unsetenv("ITERM_SESSION_ID")
241 os.Unsetenv("WEZTERM_EXECUTABLE")
242 os.Unsetenv("WARP_IS_LOCAL_SHELL_SESSION")
243 os.Unsetenv("KONSOLE_DBUS_SESSION")
244 },
245 expected: true,
246 },
247 {
248 name: "Ghostty supported via TERM_PROGRAM",
249 setupEnv: func() {
250 os.Setenv("TERM", "xterm")
251 os.Setenv("TERM_PROGRAM", "ghostty")
252 },
253 clearAllEnv: func() {
254 os.Unsetenv("KITTY_WINDOW_ID")
255 os.Unsetenv("GHOSTTY_RESOURCES_DIR")
256 os.Unsetenv("ITERM_SESSION_ID")
257 os.Unsetenv("WEZTERM_EXECUTABLE")
258 os.Unsetenv("WARP_IS_LOCAL_SHELL_SESSION")
259 os.Unsetenv("KONSOLE_DBUS_SESSION")
260 },
261 expected: true,
262 },
263 {
264 name: "iTerm2 supported via TERM_PROGRAM",
265 setupEnv: func() {
266 os.Setenv("TERM", "xterm")
267 os.Setenv("TERM_PROGRAM", "iterm.app")
268 },
269 clearAllEnv: func() {
270 os.Unsetenv("KITTY_WINDOW_ID")
271 os.Unsetenv("GHOSTTY_RESOURCES_DIR")
272 os.Unsetenv("ITERM_SESSION_ID")
273 os.Unsetenv("WEZTERM_EXECUTABLE")
274 os.Unsetenv("WARP_IS_LOCAL_SHELL_SESSION")
275 os.Unsetenv("KONSOLE_DBUS_SESSION")
276 },
277 expected: true,
278 },
279 {
280 name: "WezTerm supported via WEZTERM_EXECUTABLE",
281 setupEnv: func() {
282 os.Setenv("TERM", "xterm")
283 os.Setenv("WEZTERM_EXECUTABLE", "/usr/bin/wezterm")
284 },
285 clearAllEnv: func() {
286 os.Unsetenv("KITTY_WINDOW_ID")
287 os.Unsetenv("GHOSTTY_RESOURCES_DIR")
288 os.Unsetenv("ITERM_SESSION_ID")
289 os.Unsetenv("WARP_IS_LOCAL_SHELL_SESSION")
290 os.Unsetenv("KONSOLE_DBUS_SESSION")
291 },
292 expected: true,
293 },
294 {
295 name: "Warp supported via WARP_IS_LOCAL_SHELL_SESSION",
296 setupEnv: func() {
297 os.Setenv("TERM", "xterm")
298 os.Setenv("WARP_IS_LOCAL_SHELL_SESSION", "1")
299 },
300 clearAllEnv: func() {
301 os.Unsetenv("KITTY_WINDOW_ID")
302 os.Unsetenv("GHOSTTY_RESOURCES_DIR")
303 os.Unsetenv("ITERM_SESSION_ID")
304 os.Unsetenv("WEZTERM_EXECUTABLE")
305 os.Unsetenv("KONSOLE_DBUS_SESSION")
306 },
307 expected: true,
308 },
309 {
310 name: "Konsole supported via KONSOLE_DBUS_SESSION",
311 setupEnv: func() {
312 os.Setenv("TERM", "xterm")
313 os.Setenv("KONSOLE_DBUS_SESSION", "/Sessions/1")
314 },
315 clearAllEnv: func() {
316 os.Unsetenv("KITTY_WINDOW_ID")
317 os.Unsetenv("GHOSTTY_RESOURCES_DIR")
318 os.Unsetenv("ITERM_SESSION_ID")
319 os.Unsetenv("WEZTERM_EXECUTABLE")
320 os.Unsetenv("WARP_IS_LOCAL_SHELL_SESSION")
321 },
322 expected: true,
323 },
324 }
325
326 for _, tc := range testCases {
327 t.Run(tc.name, func(t *testing.T) {
328 tc.clearAllEnv()
329 tc.setupEnv()
330
331 result := imageProtocolSupported()
332 if result != tc.expected {
333 t.Errorf("Expected %t, got %t", tc.expected, result)
334 }
335 })
336 }
337}
338
339func TestHyperlinkSupported(t *testing.T) {
340 // Save original environment variables
341 origTerm := os.Getenv("TERM")
342 origTermProgram := os.Getenv("TERM_PROGRAM")
343 origVTEVersion := os.Getenv("VTE_VERSION")
344 origKittyWindow := os.Getenv("KITTY_WINDOW_ID")
345 origGhosttyResources := os.Getenv("GHOSTTY_RESOURCES_DIR")
346 origWeztermExec := os.Getenv("WEZTERM_EXECUTABLE")
347
348 // Restore environment variables after test
349 defer func() {
350 os.Setenv("TERM", origTerm)
351 os.Setenv("TERM_PROGRAM", origTermProgram)
352 os.Setenv("VTE_VERSION", origVTEVersion)
353 os.Setenv("KITTY_WINDOW_ID", origKittyWindow)
354 os.Setenv("GHOSTTY_RESOURCES_DIR", origGhosttyResources)
355 os.Setenv("WEZTERM_EXECUTABLE", origWeztermExec)
356 }()
357
358 testCases := []struct {
359 name string
360 setupEnv func()
361 clearAllEnv func()
362 expected bool
363 }{
364 {
365 name: "No hyperlink support",
366 setupEnv: func() {
367 os.Setenv("TERM", "xterm")
368 os.Setenv("TERM_PROGRAM", "basic")
369 },
370 clearAllEnv: func() {
371 os.Unsetenv("VTE_VERSION")
372 os.Unsetenv("KITTY_WINDOW_ID")
373 os.Unsetenv("GHOSTTY_RESOURCES_DIR")
374 os.Unsetenv("WEZTERM_EXECUTABLE")
375 },
376 expected: false,
377 },
378 {
379 name: "Kitty hyperlink support via TERM",
380 setupEnv: func() {
381 os.Setenv("TERM", "xterm-kitty")
382 },
383 clearAllEnv: func() {
384 os.Unsetenv("VTE_VERSION")
385 os.Unsetenv("KITTY_WINDOW_ID")
386 os.Unsetenv("GHOSTTY_RESOURCES_DIR")
387 os.Unsetenv("WEZTERM_EXECUTABLE")
388 },
389 expected: true,
390 },
391 {
392 name: "VTE-based terminal hyperlink support",
393 setupEnv: func() {
394 os.Setenv("TERM", "xterm")
395 os.Setenv("VTE_VERSION", "0.60.3")
396 },
397 clearAllEnv: func() {
398 os.Unsetenv("KITTY_WINDOW_ID")
399 os.Unsetenv("GHOSTTY_RESOURCES_DIR")
400 os.Unsetenv("WEZTERM_EXECUTABLE")
401 },
402 expected: true,
403 },
404 {
405 name: "iTerm2 hyperlink support",
406 setupEnv: func() {
407 os.Setenv("TERM", "xterm")
408 os.Setenv("TERM_PROGRAM", "iterm.app")
409 },
410 clearAllEnv: func() {
411 os.Unsetenv("VTE_VERSION")
412 os.Unsetenv("KITTY_WINDOW_ID")
413 os.Unsetenv("GHOSTTY_RESOURCES_DIR")
414 os.Unsetenv("WEZTERM_EXECUTABLE")
415 },
416 expected: true,
417 },
418 {
419 name: "WezTerm hyperlink support",
420 setupEnv: func() {
421 os.Setenv("TERM", "xterm")
422 os.Setenv("WEZTERM_EXECUTABLE", "/usr/bin/wezterm")
423 },
424 clearAllEnv: func() {
425 os.Unsetenv("VTE_VERSION")
426 os.Unsetenv("KITTY_WINDOW_ID")
427 os.Unsetenv("GHOSTTY_RESOURCES_DIR")
428 },
429 expected: true,
430 },
431 }
432
433 for _, tc := range testCases {
434 t.Run(tc.name, func(t *testing.T) {
435 tc.clearAllEnv()
436 tc.setupEnv()
437
438 result := hyperlinkSupported()
439 if result != tc.expected {
440 t.Errorf("Expected %t, got %t", tc.expected, result)
441 }
442 })
443 }
444}
445
446func TestProcessBodyWithHyperlinkSupport(t *testing.T) {
447 // Save original environment variables
448 origTerm := os.Getenv("TERM")
449 origTermProgram := os.Getenv("TERM_PROGRAM")
450 origVTEVersion := os.Getenv("VTE_VERSION")
451 origKittyWindow := os.Getenv("KITTY_WINDOW_ID")
452
453 // Restore environment variables after test
454 defer func() {
455 os.Setenv("TERM", origTerm)
456 os.Setenv("TERM_PROGRAM", origTermProgram)
457 os.Setenv("VTE_VERSION", origVTEVersion)
458 os.Setenv("KITTY_WINDOW_ID", origKittyWindow)
459 }()
460
461 h1Style := lipgloss.NewStyle().SetString("H1")
462 h2Style := lipgloss.NewStyle().SetString("H2")
463 bodyStyle := lipgloss.NewStyle().SetString("BODY")
464
465 testCases := []struct {
466 name string
467 setupHyperlinks func()
468 input string
469 expectedContains string
470 expectedNotContains string
471 }{
472 {
473 name: "Link with hyperlink support",
474 setupHyperlinks: func() {
475 os.Setenv("TERM", "xterm-kitty")
476 os.Unsetenv("VTE_VERSION")
477 os.Unsetenv("KITTY_WINDOW_ID")
478 },
479 input: `<a href="http://example.com">Click here</a>`,
480 expectedContains: "Click here",
481 expectedNotContains: "<http://example.com>",
482 },
483 {
484 name: "Link without hyperlink support",
485 setupHyperlinks: func() {
486 clearAllTerminalEnv()
487 },
488 input: `<a href="http://example.com">Click here</a>`,
489 expectedContains: "Click here <http://example.com>",
490 },
491 {
492 name: "Image link with hyperlink support",
493 setupHyperlinks: func() {
494 os.Setenv("TERM", "xterm")
495 os.Setenv("VTE_VERSION", "0.60.3")
496 os.Unsetenv("KITTY_WINDOW_ID")
497 },
498 input: `<img src="http://example.com/img.png" alt="alt text">`,
499 expectedContains: "[Click here to view image: alt text]",
500 expectedNotContains: "<http://example.com/img.png>",
501 },
502 {
503 name: "Image link without hyperlink support",
504 setupHyperlinks: func() {
505 clearAllTerminalEnv()
506 },
507 input: `<img src="http://example.com/img.png" alt="alt text">`,
508 expectedContains: "[Image: alt text, http://example.com/img.png]",
509 },
510 }
511
512 for _, tc := range testCases {
513 t.Run(tc.name, func(t *testing.T) {
514 tc.setupHyperlinks()
515
516 processed, err := ProcessBody(tc.input, h1Style, h2Style, bodyStyle, false)
517 if err != nil {
518 t.Fatalf("ProcessBody() failed: %v", err)
519 }
520
521 if !strings.Contains(processed, tc.expectedContains) {
522 t.Errorf("Processed body does not contain expected text.\nGot: %q\nWant to contain: %q", processed, tc.expectedContains)
523 }
524
525 if tc.expectedNotContains != "" && strings.Contains(processed, tc.expectedNotContains) {
526 t.Errorf("Processed body contains unexpected text.\nGot: %q\nShould not contain: %q", processed, tc.expectedNotContains)
527 }
528 })
529 }
530}
531
532func TestProcessBodyWithImageProtocol(t *testing.T) {
533 // Save original environment variables
534 origTerm := os.Getenv("TERM")
535 origTermProgram := os.Getenv("TERM_PROGRAM")
536 origKittyWindow := os.Getenv("KITTY_WINDOW_ID")
537 origGhosttyResources := os.Getenv("GHOSTTY_RESOURCES_DIR")
538 origItermlSession := os.Getenv("ITERM_SESSION_ID")
539 origWeztermExec := os.Getenv("WEZTERM_EXECUTABLE")
540
541 // Restore environment variables after test
542 defer func() {
543 os.Setenv("TERM", origTerm)
544 os.Setenv("TERM_PROGRAM", origTermProgram)
545 os.Setenv("KITTY_WINDOW_ID", origKittyWindow)
546 os.Setenv("GHOSTTY_RESOURCES_DIR", origGhosttyResources)
547 os.Setenv("ITERM_SESSION_ID", origItermlSession)
548 os.Setenv("WEZTERM_EXECUTABLE", origWeztermExec)
549 }()
550
551 h1Style := lipgloss.NewStyle().SetString("H1")
552 h2Style := lipgloss.NewStyle().SetString("H2")
553 bodyStyle := lipgloss.NewStyle().SetString("BODY")
554
555 // Create a simple base64 PNG image (1x1 pixel white PNG)
556 testBase64PNG := "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg=="
557
558 testCases := []struct {
559 name string
560 setupImageProtocol func()
561 clearAllImageEnv func()
562 input string
563 expectedContains string
564 expectedNotContains string
565 }{
566 {
567 name: "Data URI image with Kitty support",
568 setupImageProtocol: func() {
569 os.Setenv("TERM", "xterm-kitty")
570 },
571 clearAllImageEnv: func() {
572 os.Unsetenv("KITTY_WINDOW_ID")
573 os.Unsetenv("GHOSTTY_RESOURCES_DIR")
574 os.Unsetenv("ITERM_SESSION_ID")
575 os.Unsetenv("WEZTERM_EXECUTABLE")
576 },
577 input: `<img src="data:image/png;base64,` + testBase64PNG + `" alt="test image">`,
578 expectedContains: "\x1b_Gf=100,a=T,q=2,C=1,m=0;",
579 expectedNotContains: "[Image: test image,",
580 },
581 {
582 name: "Data URI image with iTerm2 support",
583 setupImageProtocol: func() {
584 os.Setenv("TERM", "xterm")
585 os.Setenv("TERM_PROGRAM", "iterm.app")
586 },
587 clearAllImageEnv: func() {
588 os.Unsetenv("KITTY_WINDOW_ID")
589 os.Unsetenv("GHOSTTY_RESOURCES_DIR")
590 os.Unsetenv("ITERM_SESSION_ID")
591 os.Unsetenv("WEZTERM_EXECUTABLE")
592 },
593 input: `<img src="data:image/png;base64,` + testBase64PNG + `" alt="test image">`,
594 expectedContains: "\x1b]1337;File=inline=1:",
595 expectedNotContains: "[Image: test image,",
596 },
597 {
598 name: "Data URI image without protocol support",
599 setupImageProtocol: func() {
600 clearAllTerminalEnv()
601 },
602 clearAllImageEnv: func() {
603 // This is handled by clearAllTerminalEnv now
604 },
605 input: `<img src="data:image/png;base64,` + testBase64PNG + `" alt="test image">`,
606 expectedContains: "[Image: test image,",
607 },
608 {
609 name: "Remote image with WezTerm support (has hyperlink support)",
610 setupImageProtocol: func() {
611 clearAllTerminalEnv()
612 os.Setenv("WEZTERM_EXECUTABLE", "/usr/bin/wezterm")
613 },
614 clearAllImageEnv: func() {
615 // This is handled by clearAllTerminalEnv now
616 },
617 input: `<img src="http://example.com/img.png" alt="remote image">`,
618 expectedContains: "[Click here to view image: remote image]", // Remote images won't render without actual fetch, but hyperlinks work
619 },
620 {
621 name: "Remote image without protocol support",
622 setupImageProtocol: func() {
623 clearAllTerminalEnv()
624 },
625 clearAllImageEnv: func() {
626 // This is handled by clearAllTerminalEnv now
627 },
628 input: `<img src="http://example.com/img.png" alt="remote image">`,
629 expectedContains: "[Image: remote image,",
630 },
631 }
632
633 for _, tc := range testCases {
634 t.Run(tc.name, func(t *testing.T) {
635 tc.clearAllImageEnv()
636 tc.setupImageProtocol()
637
638 processed, err := ProcessBody(tc.input, h1Style, h2Style, bodyStyle, false)
639 if err != nil {
640 t.Fatalf("ProcessBody() failed: %v", err)
641 }
642
643 if !strings.Contains(processed, tc.expectedContains) {
644 t.Errorf("Processed body does not contain expected text.\nGot: %q\nWant to contain: %q", processed, tc.expectedContains)
645 }
646
647 if tc.expectedNotContains != "" && strings.Contains(processed, tc.expectedNotContains) {
648 t.Errorf("Processed body contains unexpected text.\nGot: %q\nShould not contain: %q", processed, tc.expectedNotContains)
649 }
650 })
651 }
652}
653
654func TestProcessBody(t *testing.T) {
655 h1Style := lipgloss.NewStyle().SetString("H1")
656 h2Style := lipgloss.NewStyle().SetString("H2")
657 bodyStyle := lipgloss.NewStyle().SetString("BODY")
658
659 testCases := []struct {
660 name string
661 input string
662 expected string
663 }{
664 {
665 name: "Simple HTML",
666 input: "<p>Hello, world!</p>",
667 expected: "Hello, world!",
668 },
669 {
670 name: "With headers HTML",
671 input: "<h1>Header 1</h1>",
672 expected: "Header 1",
673 },
674 {
675 name: "With headers Markdown",
676 input: "# Header 1",
677 expected: "Header 1",
678 },
679 {
680 name: "Plain text",
681 input: "Just plain text without any markup",
682 expected: "Just plain text without any markup",
683 },
684 }
685
686 for _, tc := range testCases {
687 t.Run(tc.name, func(t *testing.T) {
688 processed, err := ProcessBody(tc.input, h1Style, h2Style, bodyStyle, false)
689 if err != nil {
690 t.Fatalf("ProcessBody() failed: %v", err)
691 }
692 // Use Contains because styles add ANSI codes
693 if !strings.Contains(processed, tc.expected) {
694 t.Errorf("Processed body does not contain expected text.\nGot: %q\nWant to contain: %q", processed, tc.expected)
695 }
696 })
697 }
698}