#[cfg(target_os = "macos")] #[tauri::command] fn updater_smoke_menu(app: tauri::AppHandle) -> Result<(), String> { let submenu = app.menu().unwrap().get("app-menu").unwrap(); let item = submenu.as_submenu().unwrap().get("install-update").unwrap(); let item = item.as_menuitem().unwrap(); for (available, busy, expected) in [ (false, false, false), // No update. (true, false, true), // Available update, also after a retryable failure. (true, true, false), // Download or installation in progress. (true, false, true), // Retry after a failed download. (false, true, false), // A new check clears the previous update. (false, false, false), // Up to date. ] { kanbangit_native::refresh_update_menu_for_smoke_test(&app, available, busy); if item.is_enabled().map_err(|error| error.to_string())? != expected { return Err(format!( "Native install menu has wrong state: available={available}, busy={busy}" )); } } Ok(()) } #[cfg(target_os = "macos")] #[tauri::command] fn updater_smoke_report(app: tauri::AppHandle, passed: bool, reason: String) { use tauri::Manager; println!("Updater WebView test: passed={passed}, {reason}"); app.state::>() .store(passed, std::sync::atomic::Ordering::SeqCst); app.exit(if passed { 0 } else { 1 }); } #[cfg(target_os = "macos")] fn main() { use std::sync::{ atomic::{AtomicBool, Ordering}, Arc, }; use tauri::webview::PageLoadEvent; let passed = Arc::new(AtomicBool::new(false)); let config: serde_json::Value = serde_json::from_str(include_str!("../tauri.conf.json")).unwrap(); let expected_installed = format!( "Installed version {} (build {})", config["version"].as_str().unwrap(), config["bundle"]["macOS"]["bundleVersion"].as_str().unwrap() ); let plugin = tauri::plugin::Builder::::new("updater-smoke") .js_init_script(format!("window.expectedInstalled = {};", serde_json::to_string(&expected_installed).unwrap())) .on_page_load(|window, payload| { if window.label() != "updates" || payload.event() != PageLoadEvent::Finished { return; } window.eval(r#" (async () => { try { for (let i = 0; i < 100 && !window.updateStatus; i++) await new Promise(r => setTimeout(r, 50)); if (!window.updateStatus) throw new Error('Update UI did not load'); const $ = id => document.getElementById(id); for (let i = 0; i < 100 && !$('installed').textContent; i++) await new Promise(r => setTimeout(r, 50)); if ($('installed').textContent !== window.expectedInstalled) throw new Error('Actual installed version/build failed: ' + $('installed').textContent); await window.__TAURI_INTERNALS__.invoke('updater_smoke_menu'); const state = { phase: 'current', currentVersion: '0.1.2', build: '2', version: null, notes: null, downloaded: 0, total: null, error: null, preferences: { automatic: true, intervalHours: 6 } }; window.updateStatus(state); if (!$('installed').textContent.includes('0.1.2 (build 2)') || !$('install').hidden || $('check').disabled) throw new Error('Current version state failed'); Object.assign(state, { phase: 'available', version: '0.1.3', notes: '\nRelease notes' }); window.updateStatus(state); if ($('install').hidden || $('release-notes').querySelector('script') || !$('release-notes').textContent.includes('