diff --git a/lib/check/all.sh b/lib/check/all.sh index 4a6960a..f38021b 100644 --- a/lib/check/all.sh +++ b/lib/check/all.sh @@ -13,6 +13,11 @@ list_login_items() { return fi + # Skip AppleScript during tests to avoid permission dialogs + if [[ "${MOLE_TEST_MODE:-0}" == "1" || "${MOLE_TEST_NO_AUTH:-0}" == "1" ]]; then + return + fi + local raw_items raw_items=$(osascript -e 'tell application "System Events" to get the name of every login item' 2> /dev/null || echo "") [[ -z "$raw_items" || "$raw_items" == "missing value" ]] && return diff --git a/lib/clean/apps.sh b/lib/clean/apps.sh index 9966ed0..ac91ac1 100644 --- a/lib/clean/apps.sh +++ b/lib/clean/apps.sh @@ -123,8 +123,11 @@ scan_installed_apps() { done # Collect running apps and LaunchAgents to avoid false orphan cleanup. ( - local running_apps=$(run_with_timeout 5 osascript -e 'tell application "System Events" to get bundle identifier of every application process' 2> /dev/null || echo "") - echo "$running_apps" | tr ',' '\n' | sed -e 's/^ *//;s/ *$//' -e '/^$/d' > "$scan_tmp_dir/running.txt" + # Skip AppleScript during tests to avoid permission dialogs + if [[ "${MOLE_TEST_MODE:-0}" != "1" && "${MOLE_TEST_NO_AUTH:-0}" != "1" ]]; then + local running_apps=$(run_with_timeout 5 osascript -e 'tell application "System Events" to get bundle identifier of every application process' 2> /dev/null || echo "") + echo "$running_apps" | tr ',' '\n' | sed -e 's/^ *//;s/ *$//' -e '/^$/d' > "$scan_tmp_dir/running.txt" + fi # Fallback: lsappinfo is more reliable than osascript if command -v lsappinfo > /dev/null 2>&1; then run_with_timeout 3 lsappinfo list 2> /dev/null | grep -o '"CFBundleIdentifier"="[^"]*"' | cut -d'"' -f4 >> "$scan_tmp_dir/running.txt" 2> /dev/null || true diff --git a/lib/clean/user.sh b/lib/clean/user.sh index 71d531d..0763dc5 100644 --- a/lib/clean/user.sh +++ b/lib/clean/user.sh @@ -11,7 +11,13 @@ clean_user_essentials() { if ! is_path_whitelisted "$HOME/.Trash"; then local trash_count local trash_count_status=0 - trash_count=$(run_with_timeout 3 osascript -e 'tell application "Finder" to count items in trash' 2> /dev/null) || trash_count_status=$? + # Skip AppleScript during tests to avoid permission dialogs + if [[ "${MOLE_TEST_MODE:-0}" == "1" || "${MOLE_TEST_NO_AUTH:-0}" == "1" ]]; then + trash_count=$(command find "$HOME/.Trash" -mindepth 1 -maxdepth 1 -exec printf '.' ';' 2> /dev/null | + wc -c | awk '{print $1}' || echo "0") + else + trash_count=$(run_with_timeout 3 osascript -e 'tell application "Finder" to count items in trash' 2> /dev/null) || trash_count_status=$? + fi if [[ $trash_count_status -eq 124 ]]; then debug_log "Finder trash count timed out, using direct .Trash scan" trash_count=$(command find "$HOME/.Trash" -mindepth 1 -maxdepth 1 -exec printf '.' ';' 2> /dev/null | @@ -22,10 +28,18 @@ clean_user_essentials() { if [[ "$DRY_RUN" == "true" ]]; then [[ $trash_count -gt 0 ]] && echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} Trash · would empty, $trash_count items" || echo -e " ${GREEN}${ICON_SUCCESS}${NC} Trash · already empty" elif [[ $trash_count -gt 0 ]]; then - if run_with_timeout 5 osascript -e 'tell application "Finder" to empty trash' > /dev/null 2>&1; then - echo -e " ${GREEN}${ICON_SUCCESS}${NC} Trash · emptied, $trash_count items" - note_activity + local emptied_via_finder=false + # Skip AppleScript during tests to avoid permission dialogs + if [[ "${MOLE_TEST_MODE:-0}" == "1" || "${MOLE_TEST_NO_AUTH:-0}" == "1" ]]; then + debug_log "Skipping Finder AppleScript in test mode" else + if run_with_timeout 5 osascript -e 'tell application "Finder" to empty trash' > /dev/null 2>&1; then + emptied_via_finder=true + echo -e " ${GREEN}${ICON_SUCCESS}${NC} Trash · emptied, $trash_count items" + note_activity + fi + fi + if [[ "$emptied_via_finder" != "true" ]]; then debug_log "Finder trash empty failed or timed out, falling back to direct deletion" local cleaned_count=0 while IFS= read -r -d '' item; do @@ -452,8 +466,11 @@ clean_support_app_data() { # Clean system-level idle/aerial screensaver videos (macOS re-downloads as needed). local sys_idle_assets_dir="/Library/Application Support/com.apple.idleassetsd/Customer" - if sudo test -d "$sys_idle_assets_dir" 2> /dev/null; then - safe_sudo_find_delete "$sys_idle_assets_dir" "*" "$support_age_days" "f" || true + # Skip sudo operations during tests to avoid password prompts + if [[ "${MOLE_TEST_MODE:-0}" != "1" && "${MOLE_TEST_NO_AUTH:-0}" != "1" ]]; then + if sudo test -d "$sys_idle_assets_dir" 2> /dev/null; then + safe_sudo_find_delete "$sys_idle_assets_dir" "*" "$support_age_days" "f" || true + fi fi # Clean old aerial wallpaper videos (can be large, safe to remove). diff --git a/lib/core/log.sh b/lib/core/log.sh index f4c88ea..8001402 100644 --- a/lib/core/log.sh +++ b/lib/core/log.sh @@ -303,8 +303,10 @@ log_system_info() { fi echo "Shell: ${SHELL:-unknown}, ${TERM:-unknown}" - # Check sudo status non-interactively - if sudo -n true 2> /dev/null; then + # Check sudo status non-interactively (skip in test mode) + if [[ "${MOLE_TEST_MODE:-0}" == "1" || "${MOLE_TEST_NO_AUTH:-0}" == "1" ]]; then + echo "Sudo Access: Skipped (test mode)" + elif sudo -n true 2> /dev/null; then echo "Sudo Access: Active" else echo "Sudo Access: Required" diff --git a/lib/uninstall/batch.sh b/lib/uninstall/batch.sh index 8a22f9a..a3931c5 100755 --- a/lib/uninstall/batch.sh +++ b/lib/uninstall/batch.sh @@ -186,26 +186,29 @@ remove_login_item() { # Remove from Login Items using index-based deletion (handles broken items) if [[ -n "$clean_name" ]]; then - # Escape double quotes and backslashes for AppleScript - local escaped_name="${clean_name//\\/\\\\}" - escaped_name="${escaped_name//\"/\\\"}" + # Skip AppleScript during tests to avoid permission dialogs + if [[ "${MOLE_TEST_MODE:-0}" != "1" && "${MOLE_TEST_NO_AUTH:-0}" != "1" ]]; then + # Escape double quotes and backslashes for AppleScript + local escaped_name="${clean_name//\\/\\\\}" + escaped_name="${escaped_name//\"/\\\"}" - osascript <<- EOF > /dev/null 2>&1 || true - tell application "System Events" - try - set itemCount to count of login items - -- Delete in reverse order to avoid index shifting - repeat with i from itemCount to 1 by -1 - try - set itemName to name of login item i - if itemName is "$escaped_name" then - delete login item i - end if - end try - end repeat - end try - end tell - EOF + osascript <<- EOF > /dev/null 2>&1 || true + tell application "System Events" + try + set itemCount to count of login items + -- Delete in reverse order to avoid index shifting + repeat with i from itemCount to 1 by -1 + try + set itemName to name of login item i + if itemName is "$escaped_name" then + delete login item i + end if + end try + end repeat + end try + end tell + EOF + fi fi } diff --git a/tests/brew_uninstall.bats b/tests/brew_uninstall.bats index d3aab74..ae49f75 100644 --- a/tests/brew_uninstall.bats +++ b/tests/brew_uninstall.bats @@ -9,6 +9,10 @@ setup_file() { HOME="$(mktemp -d "${BATS_TEST_DIRNAME}/tmp-brew-uninstall-home.XXXXXX")" export HOME + + # Prevent AppleScript permission dialogs during tests + MOLE_TEST_MODE=1 + export MOLE_TEST_MODE } teardown_file() { diff --git a/tests/clean_app_caches.bats b/tests/clean_app_caches.bats index 067664f..dee061f 100644 --- a/tests/clean_app_caches.bats +++ b/tests/clean_app_caches.bats @@ -10,6 +10,10 @@ setup_file() { HOME="$(mktemp -d "${BATS_TEST_DIRNAME}/tmp-app-caches.XXXXXX")" export HOME + # Prevent AppleScript permission dialogs during tests + MOLE_TEST_MODE=1 + export MOLE_TEST_MODE + mkdir -p "$HOME" } diff --git a/tests/clean_apps.bats b/tests/clean_apps.bats index 2fc7cdd..3ab13db 100644 --- a/tests/clean_apps.bats +++ b/tests/clean_apps.bats @@ -10,6 +10,10 @@ setup_file() { HOME="$(mktemp -d "${BATS_TEST_DIRNAME}/tmp-apps-module.XXXXXX")" export HOME + # Prevent AppleScript permission dialogs during tests + MOLE_TEST_MODE=1 + export MOLE_TEST_MODE + mkdir -p "$HOME" } diff --git a/tests/clean_browser_versions.bats b/tests/clean_browser_versions.bats index b90350a..0a2a3ed 100644 --- a/tests/clean_browser_versions.bats +++ b/tests/clean_browser_versions.bats @@ -10,6 +10,10 @@ setup_file() { HOME="$(mktemp -d "${BATS_TEST_DIRNAME}/tmp-browser-cleanup.XXXXXX")" export HOME + # Prevent AppleScript permission dialogs during tests + MOLE_TEST_MODE=1 + export MOLE_TEST_MODE + mkdir -p "$HOME" } diff --git a/tests/clean_core.bats b/tests/clean_core.bats index 30bed50..8373378 100644 --- a/tests/clean_core.bats +++ b/tests/clean_core.bats @@ -10,6 +10,10 @@ setup_file() { HOME="$(mktemp -d "${BATS_TEST_DIRNAME}/tmp-clean-home.XXXXXX")" export HOME + # Prevent AppleScript permission dialogs during tests + MOLE_TEST_MODE=1 + export MOLE_TEST_MODE + mkdir -p "$HOME" } diff --git a/tests/clean_misc.bats b/tests/clean_misc.bats index 31282bb..2d0617d 100644 --- a/tests/clean_misc.bats +++ b/tests/clean_misc.bats @@ -10,6 +10,10 @@ setup_file() { HOME="$(mktemp -d "${BATS_TEST_DIRNAME}/tmp-clean-extras.XXXXXX")" export HOME + # Prevent AppleScript permission dialogs during tests + MOLE_TEST_MODE=1 + export MOLE_TEST_MODE + mkdir -p "$HOME" } diff --git a/tests/clean_system_maintenance.bats b/tests/clean_system_maintenance.bats index 870eb96..0626f2d 100644 --- a/tests/clean_system_maintenance.bats +++ b/tests/clean_system_maintenance.bats @@ -10,6 +10,10 @@ setup_file() { HOME="$(mktemp -d "${BATS_TEST_DIRNAME}/tmp-system-clean.XXXXXX")" export HOME + # Prevent AppleScript permission dialogs during tests + MOLE_TEST_MODE=1 + export MOLE_TEST_MODE + mkdir -p "$HOME" } diff --git a/tests/clean_user_core.bats b/tests/clean_user_core.bats index 89d226e..fa7ae29 100644 --- a/tests/clean_user_core.bats +++ b/tests/clean_user_core.bats @@ -10,6 +10,10 @@ setup_file() { HOME="$(mktemp -d "${BATS_TEST_DIRNAME}/tmp-user-core.XXXXXX")" export HOME + # Prevent AppleScript permission dialogs during tests + MOLE_TEST_MODE=1 + export MOLE_TEST_MODE + mkdir -p "$HOME" }