//! Test-only UI automation sink (cargo feature `smoke`, never in release builds). use serde_json::Value; use std::io::Write; use tauri::Manager; #[tauri::command] pub fn smoke_report(webview: tauri::Webview, payload: Option) -> Result { let Ok(path) = std::env::var("CHJ_SMOKE_OUT") else { return Ok(Value::Null) }; let line = serde_json::json!({ "window": webview.label(), "at": chrono::Utc::now().to_rfc3339(), "report": payload }); if let Ok(mut file) = std::fs::OpenOptions::new().create(true).append(true).open(path) { let _ = writeln!(file, "{line}"); } if payload.as_ref().and_then(|p| p.get("exit")).and_then(Value::as_bool) == Some(true) { webview.app_handle().exit(0); } Ok(Value::Null) } /// PNG of this window's web content, without any screen-recording permission and also when the /// window is covered. Writes only below `CHJ_SMOKE_SHOTS`. #[tauri::command] pub async fn smoke_snapshot(webview: tauri::Webview, window: tauri::Window, payload: Option) -> Result { use crate::core::error::CoreError; let payload = payload.unwrap_or_default(); let root = std::env::var("CHJ_SMOKE_SHOTS").map_err(|_| CoreError::plain("CHJ_SMOKE_SHOTS is not set."))?; let name = payload["name"].as_str().filter(|n| !n.is_empty() && !n.contains("..") && !n.starts_with('/')).ok_or_else(|| CoreError::plain("Invalid snapshot name."))?; let path = std::path::Path::new(&root).join(name); if let Some(parent) = path.parent() { std::fs::create_dir_all(parent)?; } if let (Some(width), Some(height)) = (payload["width"].as_f64(), payload["height"].as_f64()) { let _ = window.set_size(tauri::LogicalSize::new(width, height)); tokio::time::sleep(std::time::Duration::from_millis(500)).await; } capture(&webview, path).await } #[cfg(target_os = "macos")] async fn capture(webview: &tauri::Webview, path: std::path::PathBuf) -> Result { use crate::core::error::CoreError; use objc2_app_kit::{NSBitmapImageFileType, NSBitmapImageRep, NSImage}; use objc2_foundation::{NSDictionary, NSError}; use objc2_web_kit::WKWebView; let (sender, receiver) = tokio::sync::oneshot::channel::, String>>(); let sender = std::sync::Mutex::new(Some(sender)); webview .with_webview(move |platform| unsafe { let view: &WKWebView = &*platform.inner().cast::(); let block = block2::RcBlock::new(move |image: *mut NSImage, _error: *mut NSError| { let png = image.as_ref().and_then(|image| image.TIFFRepresentation()).and_then(|tiff| NSBitmapImageRep::imageRepWithData(&tiff)).and_then(|rep| rep.representationUsingType_properties(NSBitmapImageFileType::PNG, &NSDictionary::new())).map(|data| data.to_vec()); if let Some(sender) = sender.lock().unwrap().take() { let _ = sender.send(png.ok_or_else(|| "The web view returned no image.".to_string())); } }); view.takeSnapshotWithConfiguration_completionHandler(None, &block); }) .map_err(|error| CoreError::plain(&error.to_string()))?; let bytes = receiver.await.map_err(|_| CoreError::plain("The snapshot was not delivered."))?.map_err(|message| CoreError::plain(&message))?; std::fs::write(&path, &bytes)?; Ok(serde_json::json!({ "bytes": bytes.len() })) } #[cfg(not(target_os = "macos"))] async fn capture(_webview: &tauri::Webview, _path: std::path::PathBuf) -> Result { Err(crate::core::error::CoreError::plain("Screenshots are taken on macOS.")) }