tinterm/
lib.rs

1mod color;
2mod color_presets;
3mod gradient;
4mod shimmer;
5mod text_modifier;
6
7pub use color::Color;
8pub use gradient::Gradient;
9pub use shimmer::{Shimmer, ShimmerText};
10pub use text_modifier::TextModifier;
11
12#[cfg(test)]
13mod tests {
14    use super::*;
15
16    #[test]
17    fn test_basic_gradient() {
18        let result = "test".gradient(Color::RED, Color::BLUE, None);
19        assert!(!result.is_empty());
20        assert!(result.contains("\x1b[38;2;"));
21        assert!(result.contains("\x1b[39m")); // Check reset sequence
22    }
23
24    #[test]
25    fn test_basic_gradient_bg() {
26        let result = "test".gradient_bg(Color::RED, Color::BLUE, None);
27        assert!(!result.is_empty());
28        assert!(result.contains("\x1b[48;2;"));
29        assert!(result.contains("\x1b[49m")); // Check reset sequence
30    }
31
32    #[test]
33    fn test_empty_string() {
34        let result = "".gradient(Color::RED, Color::BLUE, None);
35        assert!(result.is_empty());
36
37        let result_bg = "".gradient_bg(Color::RED, Color::BLUE, None);
38        assert!(result_bg.is_empty());
39    }
40
41    #[test]
42    fn test_multiline_block_true() {
43        let text = "line1\nline2";
44        let result = text.gradient(Color::RED, Color::BLUE, Some(true));
45        assert!(result.contains("\n"));
46        // Each line should start with its own color sequence
47        let lines: Vec<&str> = result.split('\n').collect();
48        assert_eq!(lines.len(), 2);
49        assert!(lines[0].starts_with("\x1b[38;2;"));
50        assert!(lines[1].starts_with("\x1b[38;2;"));
51    }
52
53    #[test]
54    fn test_multiline_block_false() {
55        let text = "line1\nline2";
56        let result = text.gradient(Color::RED, Color::BLUE, Some(false));
57        assert!(result.contains("\n"));
58        // Color should continue across lines
59        let lines: Vec<&str> = result.split('\n').collect();
60        assert_eq!(lines.len(), 2);
61        assert!(lines[0].starts_with("\x1b[38;2;"));
62        assert!(lines[1].contains("\x1b[38;2;")); // Second line should contain gradient but not start with it
63    }
64
65    #[test]
66    fn test_multiline_bg_block_false() {
67        let text = "line1\nline2";
68        let result = text.gradient_bg(Color::RED, Color::BLUE, Some(false));
69        assert!(result.contains("\n"));
70        // Should have background reset at end of each line
71        assert!(result.contains("\x1b[49m\n"));
72        // Color should continue across lines
73        let lines: Vec<&str> = result.split('\n').collect();
74        assert_eq!(lines.len(), 2);
75        assert!(lines[0].contains("\x1b[48;2;"));
76        assert!(lines[0].ends_with("\x1b[49m"));
77    }
78
79    #[test]
80    fn test_gradient_chaining() {
81        // Test FG then BG
82        let result = "test".gradient(Color::RED, Color::BLUE, None).gradient_bg(
83            Color::GREEN,
84            Color::YELLOW,
85            None,
86        );
87
88        // Should contain both FG and BG sequences
89        assert!(result.contains("\x1b[38;2;")); // FG
90        assert!(result.contains("\x1b[48;2;")); // BG
91                                                // Should contain both reset sequences
92        assert!(result.contains("\x1b[39m")); // FG reset
93        assert!(result.contains("\x1b[49m")); // BG reset
94
95        // Test BG then FG
96        let result = "test"
97            .gradient_bg(Color::GREEN, Color::YELLOW, None)
98            .gradient(Color::RED, Color::BLUE, None);
99
100        // Should contain both FG and BG sequences
101        assert!(result.contains("\x1b[38;2;")); // FG
102        assert!(result.contains("\x1b[48;2;")); // BG
103                                                // Should contain both reset sequences
104        assert!(result.contains("\x1b[39m")); // FG reset
105        assert!(result.contains("\x1b[49m")); // BG reset
106    }
107
108    #[test]
109    fn test_with_existing_ansi() {
110        // Test with existing bold text
111        let bold_text = "test".bold();
112        let result = bold_text.gradient(Color::RED, Color::BLUE, None);
113        assert!(result.contains("\x1b[1m")); // Bold sequence preserved
114        assert!(result.contains("\x1b[38;2;")); // FG color added
115
116        // Test with existing color
117        let colored_text = "test".fg(Color::RED);
118        let result = colored_text.gradient_bg(Color::GREEN, Color::BLUE, None);
119        assert!(result.contains("\x1b[38;2;")); // Original FG color preserved
120        assert!(result.contains("\x1b[48;2;")); // BG gradient added
121    }
122
123    #[test]
124    fn test_special_characters() {
125        let text = "test 👍 emoji";
126        let result = text.gradient(Color::RED, Color::BLUE, None);
127        assert!(!result.is_empty());
128        assert!(result.contains("👍"));
129
130        let result_bg = text.gradient_bg(Color::RED, Color::BLUE, None);
131        assert!(!result_bg.is_empty());
132        assert!(result_bg.contains("👍"));
133    }
134
135    #[test]
136    fn test_color_interpolation() {
137        let color1 = Color::new(255, 0, 0);
138        let color2 = Color::new(0, 0, 255);
139        let mid = color1.interpolate(&color2, 0.5);
140        assert_eq!(mid.r, 127);
141        assert_eq!(mid.b, 127);
142    }
143
144    #[test]
145    fn test_shimmer_creation() {
146        let shimmer_text = "Hello World".shimmer(Color::RED, None);
147        let rendered = shimmer_text.static_render();
148        assert!(!rendered.is_empty());
149        assert!(rendered.contains("\x1b[38;2;"));
150    }
151
152    #[test]
153    fn test_shine_creation() {
154        let shine_text = "Shining Text".shine(Color::BLUE);
155        let rendered = shine_text.static_render();
156        assert!(!rendered.is_empty());
157        assert!(rendered.contains("\x1b[38;2;"));
158    }
159
160    #[test]
161    fn test_glow_creation() {
162        let glow_text = "Glowing Text".glow(Color::GREEN, 200);
163        let rendered = glow_text.static_render();
164        assert!(!rendered.is_empty());
165        assert!(rendered.contains("\x1b[38;2;"));
166    }
167
168    #[test]
169    fn test_shimmer_chaining() {
170        let shimmer_text = "Test"
171            .shimmer(Color::RED, None)
172            .speed(50)
173            .background(Color::BLACK)
174            .intensity(180);
175        let rendered = shimmer_text.static_render();
176        assert!(!rendered.is_empty());
177    }
178
179    #[test]
180    fn test_mixed_content() {
181        let text = "line1\nline2";
182        let result = text.gradient_bg(Color::RED, Color::BLUE, Some(false));
183        assert!(result.contains("\x1b[49m\n")); // Check background reset at newlines
184        assert!(result.contains("\n")); // Empty line preserved
185
186        let lines: Vec<&str> = result.split('\n').collect();
187        assert_eq!(lines.len(), 2);
188        assert!(lines[0].contains("\x1b[48;2;")); // First line has background
189    }
190}