#[cfg(target_os = "macos")] fn main() { use std::sync::{ atomic::{AtomicBool, Ordering}, Arc, }; use tauri::{webview::PageLoadEvent, Manager}; let complete = Arc::new(AtomicBool::new(false)); let passed = Arc::new(AtomicBool::new(false)); let navigation_complete = complete.clone(); let navigation_passed = passed.clone(); let plugin = tauri::plugin::Builder::::new("macos-smoke") .js_init_script("window.__nativeSmokeErrors = []; window.addEventListener('error', event => window.__nativeSmokeErrors.push(event.message || event.target.src), true);") .on_page_load(|webview, payload| { if payload.event() != PageLoadEvent::Finished { return; } if webview.label() == "launcher" { webview.eval(r#" (async () => { for (let attempt = 0; attempt < 100; attempt++) { if (document.querySelector('.open-workspace')?.textContent === 'Open workspace') break; await new Promise(resolve => setTimeout(resolve, 100)); } const link = document.querySelector('.open-workspace'); const appearance = document.querySelector('#appearance'); const translated = link?.textContent === 'Open workspace'; appearance.value = 'dark'; appearance.dispatchEvent(new Event('change')); const dark = getComputedStyle(document.documentElement).backgroundColor === 'rgb(15, 23, 42)'; appearance.value = 'light'; appearance.dispatchEvent(new Event('change')); const light = getComputedStyle(document.documentElement).backgroundColor === 'rgb(247, 249, 251)'; appearance.value = 'system'; appearance.dispatchEvent(new Event('change')); if (translated && dark && light) { link.click(); } else { location.href = 'tauri://localhost/__native_smoke_result?passed=false&stage=launcher&translated=' + translated + '&dark=' + dark + '&light=' + light + '&background=' + encodeURIComponent(getComputedStyle(document.documentElement).backgroundColor) + '&label=' + encodeURIComponent(link?.textContent) + '&errors=' + encodeURIComponent(JSON.stringify(window.__nativeSmokeErrors)); } })(); "#).unwrap(); } else if webview.label() == "workspace" { webview.eval(r#" (async () => { for (let attempt = 0; attempt < 100; attempt++) { if (document.querySelector('#app')?.innerText.trim().length > 100) break; await new Promise(resolve => setTimeout(resolve, 100)); } const root = document.querySelector('#app'); const rendered = root?.innerText.trim().length > 100 && root.getBoundingClientRect().height > 0; let denied = true; for (const cmd of ['plugin:path|resolve_directory', 'plugin:window|get_all_windows']) { try { await window.__TAURI_INTERNALS__.invoke(cmd); denied = false; } catch (error) { denied = denied && String(error).includes('not allowed'); } } const noGlobalApi = typeof window.__TAURI__ === 'undefined'; const report = new URL('/__native_smoke_result', location.origin); report.searchParams.set('passed', String(rendered && denied && noGlobalApi)); report.searchParams.set('rendered', String(rendered)); report.searchParams.set('nativeDenied', String(denied)); report.searchParams.set('globalAbsent', String(noGlobalApi)); location.href = report.href; })(); "#).unwrap(); } }) .on_navigation(move |webview, url| { if url.path() != "/__native_smoke_result" { return true; } let success = url.query_pairs().any(|(key, value)| key == "passed" && value == "true"); println!("macOS WebView smoke test: {}", url.query().unwrap_or_default()); navigation_passed.store(success, Ordering::SeqCst); navigation_complete.store(true, Ordering::SeqCst); webview.app_handle().exit(if success { 0 } else { 1 }); false }) .build(); let app = kanbangit_native::builder() .plugin(plugin) .build(kanbangit_native::context()) .expect("The macOS application must start"); let handle = app.handle().clone(); std::thread::spawn(move || { std::thread::sleep(std::time::Duration::from_secs(30)); if !complete.load(Ordering::SeqCst) { eprintln!("macOS smoke test timed out before the server UI rendered"); handle.exit(1); } }); app.run_return(|_, _| {}); assert!( passed.load(Ordering::SeqCst), "The native smoke test must complete successfully" ); } #[cfg(not(target_os = "macos"))] fn main() { panic!("Run this smoke test on macOS with an active graphical session"); }