Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-ui-core/src/clipboard_utils_tests.rs
StratoSDK / crates / strato-ui-core / src / clipboard_utils_tests.rs
1use super::*;
2use crate::clipboard::{ClipboardContent, ImageData};
3 
4// ============================================================================
5// HELPER FUNCTIONS (shared across tests)
6// ============================================================================
7 
8#[cfg(any(target_os = "linux", target_os = "windows"))]
9fn create_rgba_data(w: usize, h: usize) -> Vec<u8> {
10 // Simple test pattern: red gradient
11 (0..h)
12 .flat_map(|y| {
13 (0..w).flat_map(move |x| [((x * 255) / w) as u8, ((y * 255) / h) as u8, 128, 255])
14 })
15 .collect()
16}
17 
18#[cfg(any(target_os = "linux", target_os = "windows"))]
19fn create_simple_png() -> Vec<u8> {
20 // PNG header for 1x1 red pixel
21 vec![0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A] // PNG signature
22}
23 
24#[cfg(any(target_os = "linux", target_os = "windows"))]
25fn create_simple_jpeg() -> Vec<u8> {
26 // JPEG header
27 vec![0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46]
28}
29 
30#[cfg(any(target_os = "linux", target_os = "windows"))]
31fn create_simple_gif() -> Vec<u8> {
32 // GIF header
33 let mut data = Vec::new();
34 data.extend_from_slice(b"GIF87a");
35 data.extend_from_slice(&[1, 0, 1, 0, 0, 0, 0]); // minimal 1x1 GIF
36 data
37}
38 
39#[cfg(any(target_os = "linux", target_os = "windows"))]
40fn create_simple_webp() -> Vec<u8> {
41 // WebP header
42 let mut data = Vec::new();
43 data.extend_from_slice(b"RIFF");
44 data.extend_from_slice(&[12, 0, 0, 0]); // file size
45 data.extend_from_slice(b"WEBP");
46 data.extend_from_slice(b"VP8 ");
47 data
48}
49 
50#[cfg(any(target_os = "linux", target_os = "windows"))]
51fn assert_valid_png(result: Option<ImageData>) {
52 let image_data = result.expect("Should process image successfully");
53 assert_eq!(image_data.mime_type, "image/png");
54 assert_eq!(&image_data.data[0..8], &[137, 80, 78, 71, 13, 10, 26, 10]); // PNG header
55}
56 
57// ============================================================================
58// FILENAME EXTRACTION TESTS
59// ============================================================================
60 
61#[test]
62fn test_extract_filename_from_html() {
63 // Test extraction from src attribute with file:// URL (common on macOS)
64 let html1 = r##"<img src="file:///Users/test/Pictures/screenshot.png" alt="Screenshot">"##;
65 let filename = extract_filename_from_html(html1);
66 assert_eq!(filename, Some("screenshot.png".to_string()));
67 
68 // Test extraction from src attribute with http URL
69 let html2 = r##"<img src="https://example.com/images/photo.jpg" alt="Photo">"##;
70 let filename = extract_filename_from_html(html2);
71 assert_eq!(filename, Some("photo.jpg".to_string()));
72 
73 // Test extraction from title attribute
74 let html3 = r##"<img title="document.gif" src="data:image/gif;base64,R0lGOD...">"##;
75 let filename = extract_filename_from_html(html3);
76 assert_eq!(filename, Some("document.gif".to_string()));
77 
78 // Test extraction from alt attribute
79 let html4 = r##"<img alt="image.webp" src="data:image/webp;base64,UklGR...">"##;
80 let filename = extract_filename_from_html(html4);
81 assert_eq!(filename, Some("image.webp".to_string()));
82 
83 // Test extraction from free text
84 let html5 = r##"<div>Here is my image: myfile.jpeg that I copied</div>"##;
85 let filename = extract_filename_from_html(html5);
86 assert_eq!(filename, Some("myfile.jpeg".to_string()));
87 
88 // Test no filename found
89 let html6 = r##"<div>Just some text with no image references</div>"##;
90 let filename = extract_filename_from_html(html6);
91 assert_eq!(filename, None);
92 
93 // Test non-image extension ignored
94 let html7 = r##"<div>document.pdf and archive.zip should be ignored</div>"##;
95 let filename = extract_filename_from_html(html7);
96 assert_eq!(filename, None);
97 
98 // Test complex path extraction with Windows-style paths
99 let html8 =
100 r##"<img src="file://C:\Users\John%20Doe\Desktop\My%20Images\vacation-photo.png">"##;
101 let filename = extract_filename_from_html(html8);
102 assert_eq!(filename, Some("vacation-photo.png".to_string()));
103 
104 // Test case-insensitive extension matching
105 let html9 = r##"<img src="test.PNG" alt="Test">"##;
106 let filename = extract_filename_from_html(html9);
107 assert_eq!(filename, Some("test.PNG".to_string()));
108 
109 // Test extraction with various punctuation
110 let html10 = r##"<div>Look at "my-image.jpg", (another.gif), or <file.webp>!</div>"##;
111 let filename = extract_filename_from_html(html10);
112 // Should find the first one
113 assert_eq!(filename, Some("my-image.jpg".to_string()));
114}
115 
116#[test]
117fn test_extract_filename_from_text() {
118 // Test full file path
119 let file_path = "/Users/test/Documents/screenshot.png";
120 let result = extract_filename_from_text(file_path);
121 assert_eq!(result, Some("screenshot.png".to_string()));
122 
123 // Test Windows path
124 let windows_path = "C:\\Users\\test\\Documents\\image.jpg";
125 let result = extract_filename_from_text(windows_path);
126 assert_eq!(result, Some("image.jpg".to_string()));
127 
128 // Test file:// URL
129 let file_url = "file:///Users/test/screenshot.gif";
130 let result = extract_filename_from_text(file_url);
131 assert_eq!(result, Some("screenshot.gif".to_string()));
132 
133 // Test multiline with file path
134 let multiline = "Some text\n/path/to/image.webp\nMore text";
135 let result = extract_filename_from_text(multiline);
136 assert_eq!(result, Some("image.webp".to_string()));
137 
138 // Test non-image file (should return None)
139 let text_file = "/Users/test/document.txt";
140 let result = extract_filename_from_text(text_file);
141 assert_eq!(result, None);
142 
143 // Test no file path
144 let plain_text = "Just some plain text";
145 let result = extract_filename_from_text(plain_text);
146 assert_eq!(result, None);
147 
148 // Test just filename
149 let just_filename = "my-screenshot.png";
150 let result = extract_filename_from_text(just_filename);
151 assert_eq!(result, Some("my-screenshot.png".to_string()));
152 
153 // Test empty string
154 let empty = "";
155 let result = extract_filename_from_text(empty);
156 assert_eq!(result, None);
157}
158 
159#[test]
160fn test_extract_filename_from_clipboard_content() {
161 // Test HTML takes precedence over text
162 let html_content = Some(r##"<img src="test.png" alt="Test">"##.to_string());
163 let text_content = "other-file.jpg";
164 let result = extract_filename_from_clipboard_content(&html_content, text_content);
165 assert_eq!(result, Some("test.png".to_string()));
166 
167 // Test fallback to text when HTML has no filename
168 let html_content = Some("<div>No images here</div>".to_string());
169 let text_content = "/path/to/image.gif";
170 let result = extract_filename_from_clipboard_content(&html_content, text_content);
171 assert_eq!(result, Some("image.gif".to_string()));
172 
173 // Test fallback to text when no HTML
174 let html_content = None;
175 let text_content = "screenshot.webp";
176 let result = extract_filename_from_clipboard_content(&html_content, text_content);
177 assert_eq!(result, Some("screenshot.webp".to_string()));
178 
179 // Test no filename found
180 let html_content = Some("<div>Just text</div>".to_string());
181 let text_content = "No images here either";
182 let result = extract_filename_from_clipboard_content(&html_content, text_content);
183 assert_eq!(result, None);
184}
185 
186// ============================================================================
187// IMAGE PROCESSING TESTS (Linux/Windows platforms only)
188// ============================================================================
189 
190#[cfg(any(target_os = "linux", target_os = "windows"))]
191mod image_processing_tests {
192 use super::*;
193 
194 #[test]
195 fn test_rgba_bitmap_processing() {
196 let arboard_image = arboard::ImageData {
197 width: 8,
198 height: 6,
199 bytes: create_rgba_data(8, 6).into(),
200 };
201 assert_valid_png(process_clipboard_image(&arboard_image, None));
202 }
203 
204 #[test]
205 fn test_invalid_data_rejection() {
206 let arboard_image = arboard::ImageData {
207 width: 10,
208 height: 10,
209 bytes: vec![1, 2, 3, 4, 5].into(),
210 };
211 assert!(process_clipboard_image(&arboard_image, None).is_none());
212 }
213 
214 #[test]
215 fn test_various_dimensions() {
216 for (w, h) in [(100, 100), (782, 297), (1, 1)] {
217 let arboard_image = arboard::ImageData {
218 width: w,
219 height: h,
220 bytes: create_rgba_data(w, h).into(),
221 };
222 let result = process_clipboard_image(&arboard_image, None)
223 .unwrap_or_else(|| panic!("Failed to process {w}x{h} image"));
224 let loaded = image::load_from_memory(&result.data)
225 .unwrap_or_else(|e| panic!("Failed to load processed {w}x{h} image: {e}"));
226 assert_eq!((loaded.width(), loaded.height()), (w as u32, h as u32));
227 }
228 }
229 
230 #[test]
231 fn test_format_preservation_and_detection() {
232 let test_cases = vec![
233 (create_simple_png(), "image/png", "test.png"),
234 (create_simple_jpeg(), "image/jpeg", "test.jpg"),
235 (create_simple_gif(), "image/gif", "test.gif"),
236 (create_simple_webp(), "image/webp", "test.webp"),
237 ];
238 
239 for (data, expected_mime, filename) in test_cases {
240 let result = try_preserve_original_format(&data, Some(filename.to_string()));
241 if let Some(image_data) = result {
242 assert_eq!(image_data.mime_type, expected_mime);
243 assert_eq!(image_data.filename, Some(filename.to_string()));
244 // Format preservation should keep original data
245 assert_eq!(image_data.data, data);
246 }
247 }
248 }
249 
250 #[test]
251 fn test_unsupported_format_fallback() {
252 // Create some random data that doesn't match any supported format
253 let unsupported_data = vec![0x50, 0x4B, 0x03, 0x04]; // ZIP signature
254 let arboard_image = arboard::ImageData {
255 width: 4,
256 height: 4,
257 bytes: unsupported_data.into(),
258 };
259 
260 // Should return None since ZIP is not a supported image format
261 let result = process_clipboard_image(&arboard_image, None);
262 assert!(result.is_none(), "Should reject unsupported format");
263 }
264 
265 #[test]
266 fn test_convert_raw_bitmap_to_png() {
267 // Test valid conversion
268 let width = 2;
269 let height = 2;
270 let rgba_data = vec![
271 255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255, 255, 255, 255, 255,
272 ];
273 
274 let result =
275 convert_raw_bitmap_to_png(width, height, rgba_data, Some("test.png".to_string()));
276 if let Some(image_data) = result {
277 assert_eq!(image_data.mime_type, "image/png");
278 assert_eq!(image_data.filename, Some("test.png".to_string()));
279 assert!(!image_data.data.is_empty());
280 }
281 
282 // Test invalid dimensions
283 let result = convert_raw_bitmap_to_png(usize::MAX, 1, vec![255, 0, 0, 255], None);
284 assert!(result.is_none());
285 }
286}
287 
288// ============================================================================
289// CLIPBOARD CONTENT STRUCTURE TESTS
290// ============================================================================
291 
292#[test]
293fn test_clipboard_content_with_images() {
294 let image_data = ImageData {
295 data: vec![0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A],
296 mime_type: "image/png".to_string(),
297 filename: Some("test.png".to_string()),
298 };
299 
300 let content = ClipboardContent {
301 plain_text: "Test text".to_string(),
302 html: Some(r##"<img src="test.png">"##.to_string()),
303 images: Some(vec![image_data.clone()]),
304 paths: None,
305 };
306 
307 assert!(!content.is_empty());
308 assert!(content.images.is_some());
309 assert_eq!(content.images.as_ref().unwrap().len(), 1);
310 assert_eq!(content.images.as_ref().unwrap()[0].mime_type, "image/png");
311}
312