|
| 1 | +#[cfg(test)] |
| 2 | +#[cfg(target_os = "macos")] |
| 3 | +mod macos_tests { |
| 4 | + use std::path::Path; |
| 5 | + use std::process::Command; |
| 6 | + |
| 7 | + use super::{StaticExporterBuilder, ImageFormat}; |
| 8 | + |
| 9 | + /// Test that Google Chrome is installed and accessible |
| 10 | + #[test] |
| 11 | + fn test_chrome_installation() { |
| 12 | + let chrome_paths = [ |
| 13 | + "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", |
| 14 | + "/Applications/Chromium.app/Contents/MacOS/Chromium", |
| 15 | + ]; |
| 16 | + |
| 17 | + let mut chrome_found = false; |
| 18 | + for path in &chrome_paths { |
| 19 | + if Path::new(path).exists() { |
| 20 | + println!("Found Chrome at: {}", path); |
| 21 | + chrome_found = true; |
| 22 | + break; |
| 23 | + } |
| 24 | + } |
| 25 | + |
| 26 | + if !chrome_found { |
| 27 | + // Try to find Chrome using 'which' command |
| 28 | + if let Ok(output) = Command::new("which").arg("google-chrome").output() { |
| 29 | + if output.status.success() { |
| 30 | + let path = String::from_utf8_lossy(&output.stdout).trim(); |
| 31 | + println!("Found Chrome via 'which': {}", path); |
| 32 | + chrome_found = true; |
| 33 | + } |
| 34 | + } |
| 35 | + } |
| 36 | + |
| 37 | + if !chrome_found { |
| 38 | + // Try to find Chrome using 'mdfind' (macOS Spotlight) |
| 39 | + if let Ok(output) = Command::new("mdfind") |
| 40 | + .args(["-name", "Google Chrome.app"]) |
| 41 | + .output() |
| 42 | + { |
| 43 | + if output.status.success() { |
| 44 | + let paths = String::from_utf8_lossy(&output.stdout); |
| 45 | + for path in paths.lines() { |
| 46 | + let chrome_binary = format!("{}/Contents/MacOS/Google Chrome", path.trim()); |
| 47 | + if Path::new(&chrome_binary).exists() { |
| 48 | + println!("Found Chrome via Spotlight: {}", chrome_binary); |
| 49 | + chrome_found = true; |
| 50 | + break; |
| 51 | + } |
| 52 | + } |
| 53 | + } |
| 54 | + } |
| 55 | + } |
| 56 | + |
| 57 | + assert!( |
| 58 | + chrome_found, |
| 59 | + "Google Chrome not found on macOS. Please install Chrome or Chromium." |
| 60 | + ); |
| 61 | + } |
| 62 | + |
| 63 | + /// Test that chromedriver is installed and can be executed |
| 64 | + #[test] |
| 65 | + fn test_chromedriver_installation() { |
| 66 | + // Check if chromedriver is in PATH |
| 67 | + let mut chromedriver_found = false; |
| 68 | + |
| 69 | + if let Ok(output) = Command::new("chromedriver").arg("--version").output() { |
| 70 | + if output.status.success() { |
| 71 | + let version = String::from_utf8_lossy(&output.stdout); |
| 72 | + println!("Found chromedriver: {}", version.trim()); |
| 73 | + chromedriver_found = true; |
| 74 | + } |
| 75 | + } |
| 76 | + |
| 77 | + // Check common installation paths |
| 78 | + let chromedriver_paths = [ |
| 79 | + "/usr/local/bin/chromedriver", |
| 80 | + "/opt/homebrew/bin/chromedriver", |
| 81 | + "/usr/bin/chromedriver", |
| 82 | + ]; |
| 83 | + |
| 84 | + for path in &chromedriver_paths { |
| 85 | + if Path::new(path).exists() { |
| 86 | + println!("Found chromedriver at: {}", path); |
| 87 | + chromedriver_found = true; |
| 88 | + break; |
| 89 | + } |
| 90 | + } |
| 91 | + |
| 92 | + assert!( |
| 93 | + chromedriver_found, |
| 94 | + "chromedriver not found on macOS. Please install chromedriver." |
| 95 | + ); |
| 96 | + } |
| 97 | + |
| 98 | + /// Test that we can create a StaticExporter with Chrome on macOS |
| 99 | + #[test] |
| 100 | + fn test_static_exporter_creation_macos() { |
| 101 | + let exporter = StaticExporterBuilder::default() |
| 102 | + .spawn_webdriver(true) |
| 103 | + .webdriver_port(4444) |
| 104 | + .build(); |
| 105 | + |
| 106 | + match exporter { |
| 107 | + Ok(_) => println!("Successfully created StaticExporter on macOS"), |
| 108 | + Err(e) => { |
| 109 | + // If it fails, it might be because port 4444 is in use |
| 110 | + // Try with a different port |
| 111 | + let exporter2 = StaticExporterBuilder::default() |
| 112 | + .spawn_webdriver(true) |
| 113 | + .webdriver_port(4445) |
| 114 | + .build(); |
| 115 | + |
| 116 | + match exporter2 { |
| 117 | + Ok(_) => { |
| 118 | + println!("Successfully created StaticExporter on macOS with port 4445") |
| 119 | + } |
| 120 | + Err(e2) => panic!( |
| 121 | + "Failed to create StaticExporter on macOS: {:?}, {:?}", |
| 122 | + e, e2 |
| 123 | + ), |
| 124 | + } |
| 125 | + } |
| 126 | + } |
| 127 | + } |
| 128 | + |
| 129 | + /// Test basic PNG export functionality on macOS |
| 130 | + #[test] |
| 131 | + fn test_basic_png_export_macos() { |
| 132 | + let test_plot = serde_json::json!({ |
| 133 | + "data": [{ |
| 134 | + "type": "scatter", |
| 135 | + "x": [1, 2, 3, 4], |
| 136 | + "y": [10, 11, 12, 13] |
| 137 | + }], |
| 138 | + "layout": { |
| 139 | + "title": "macOS Test Plot" |
| 140 | + } |
| 141 | + }); |
| 142 | + |
| 143 | + let mut exporter = StaticExporterBuilder::default() |
| 144 | + .spawn_webdriver(true) |
| 145 | + .webdriver_port(4446) |
| 146 | + .build() |
| 147 | + .expect("Failed to create StaticExporter"); |
| 148 | + |
| 149 | + let result = exporter.write_to_string(&test_plot, ImageFormat::PNG, 800, 600, 1.0); |
| 150 | + |
| 151 | + match result { |
| 152 | + Ok(data) => { |
| 153 | + assert!(!data.is_empty(), "PNG export returned empty data"); |
| 154 | + assert!(data.len() > 100, "PNG export data seems too small"); |
| 155 | + println!("Successfully exported PNG on macOS ({} bytes)", data.len()); |
| 156 | + } |
| 157 | + Err(e) => { |
| 158 | + panic!("Failed to export PNG on macOS: {:?}", e); |
| 159 | + } |
| 160 | + } |
| 161 | + } |
| 162 | + |
| 163 | + /// Test that the macOS-specific Chrome flags are working |
| 164 | + #[test] |
| 165 | + fn test_macos_chrome_flags() { |
| 166 | + let exporter = StaticExporterBuilder::default() |
| 167 | + .spawn_webdriver(true) |
| 168 | + .webdriver_port(4447) |
| 169 | + .build() |
| 170 | + .expect("Failed to create StaticExporter"); |
| 171 | + |
| 172 | + // Check that the macOS-specific flags are in the browser capabilities |
| 173 | + let caps = exporter |
| 174 | + .build_webdriver_caps() |
| 175 | + .expect("Failed to build capabilities"); |
| 176 | + |
| 177 | + if let Some(args) = caps |
| 178 | + .get("goog:chromeOptions") |
| 179 | + .and_then(|opts| opts.get("args")) |
| 180 | + .and_then(|args| args.as_array()) |
| 181 | + { |
| 182 | + let args_str: Vec<&str> = args.iter().filter_map(|arg| arg.as_str()).collect(); |
| 183 | + |
| 184 | + // Check for macOS-specific flags |
| 185 | + let required_flags = [ |
| 186 | + "--enable-unsafe-swiftshader", |
| 187 | + "--use-mock-keychain", |
| 188 | + "--password-store=basic", |
| 189 | + "--disable-web-security", |
| 190 | + ]; |
| 191 | + |
| 192 | + for flag in &required_flags { |
| 193 | + assert!( |
| 194 | + args_str.contains(flag), |
| 195 | + "Missing required macOS flag: {}", |
| 196 | + flag |
| 197 | + ); |
| 198 | + } |
| 199 | + |
| 200 | + println!("All required macOS Chrome flags are present"); |
| 201 | + } else { |
| 202 | + panic!("Could not extract Chrome arguments from capabilities"); |
| 203 | + } |
| 204 | + } |
| 205 | + |
| 206 | + /// Test that user data directories are properly created and cleaned up |
| 207 | + #[test] |
| 208 | + fn test_user_data_directory_management() { |
| 209 | + use std::sync::atomic::{AtomicU32, Ordering}; |
| 210 | + static PORT_COUNTER: AtomicU32 = AtomicU32::new(4448); |
| 211 | + |
| 212 | + let port = PORT_COUNTER.fetch_add(1, Ordering::SeqCst); |
| 213 | + |
| 214 | + let exporter = StaticExporterBuilder::default() |
| 215 | + .spawn_webdriver(true) |
| 216 | + .webdriver_port(port) |
| 217 | + .build() |
| 218 | + .expect("Failed to create StaticExporter"); |
| 219 | + |
| 220 | + // Check that user data directory was created |
| 221 | + assert!( |
| 222 | + exporter.user_data_dir.is_some(), |
| 223 | + "User data directory should be set" |
| 224 | + ); |
| 225 | + |
| 226 | + let user_data_dir = exporter.user_data_dir.as_ref().unwrap(); |
| 227 | + assert!( |
| 228 | + user_data_dir.exists(), |
| 229 | + "User data directory should exist: {:?}", |
| 230 | + user_data_dir |
| 231 | + ); |
| 232 | + |
| 233 | + println!( |
| 234 | + "User data directory created successfully: {:?}", |
| 235 | + user_data_dir |
| 236 | + ); |
| 237 | + |
| 238 | + // The directory should be cleaned up when exporter is dropped |
| 239 | + // We can't easily test this in a single test, but we can verify it |
| 240 | + // exists |
| 241 | + } |
| 242 | +} |
0 commit comments