fix(ios): improve test script reliability and pending notification detection
Fixed multiple issues in iOS test script and added logging for test compatibility: Test Script Fixes: - Fixed get_simulator_id() to correctly extract UUID from booted devices - Fixed get_app_logs() to use log show (historical) instead of log stream (live) to avoid hanging - Improved check_plugin_configured() with multiple detection methods (app container, app listing, data directory) - Added ensure_plugin_configured() function matching Android pattern for consistent user interaction flow - Fixed integer comparison error in booted device check (removed newlines from count) - Removed 'local' keyword from variables in main script body (local can only be used in functions) - Fixed APP_BUNDLE_ID to match actual bundle identifier Pending Notification Detection: - Improved get_pending_notifications() to parse pendingCount from plugin logs - Added direct log query without restrictive predicate to catch plugin logs - Added multiple fallback methods for detecting pending count Plugin Logging Enhancement: - Added explicit pendingCount logging in DailyNotificationScheduler after scheduling - Uses both NSLog() and print() to ensure logs appear in system logs and Xcode console - Matches Android's alarm count logging pattern for test script compatibility This resolves script crashes and enables reliable detection of pending notifications for automated testing.
This commit is contained in:
@@ -188,7 +188,11 @@ class DailyNotificationScheduler {
|
||||
self.scheduledNotifications.insert(content.id)
|
||||
}
|
||||
|
||||
print("\(Self.TAG): Notification scheduled successfully for \(scheduledDate)")
|
||||
// Log pending count for test scripts (matches Android's alarm count logging)
|
||||
// Use NSLog to ensure it appears in system logs (print() may not always be captured)
|
||||
let pendingCount = await getPendingNotificationCount()
|
||||
NSLog("\(Self.TAG): Notification scheduled successfully for \(scheduledDate), id=\(content.id), pendingCount=\(pendingCount)")
|
||||
print("\(Self.TAG): Notification scheduled successfully for \(scheduledDate), id=\(content.id), pendingCount=\(pendingCount)")
|
||||
return true
|
||||
|
||||
} catch {
|
||||
|
||||
@@ -13,7 +13,7 @@ if [ -f "${SCRIPT_DIR}/ios-test-lib.sh" ]; then
|
||||
fi
|
||||
|
||||
# Phase 1 specific configuration
|
||||
APP_BUNDLE_ID="com.timesafari.ios-test-app"
|
||||
APP_BUNDLE_ID="com.timesafari.dailynotification.test"
|
||||
SIMULATOR_DEVICE="iPhone 15"
|
||||
LOG_PREFIX="DNP"
|
||||
|
||||
@@ -70,13 +70,47 @@ wait_for_ui_action() {
|
||||
|
||||
# iOS-specific helper functions
|
||||
get_simulator_id() {
|
||||
xcrun simctl list devices available | grep "${SIMULATOR_DEVICE}" | head -1 | sed -E 's/.*\(([^)]+)\).*/\1/'
|
||||
# First try to find a booted device matching the name
|
||||
# Extract UUID from line like: " iPhone 15 (6514F1D6-80C2-4D0E-8CB4-6F561C8EA1F1) (Booted)"
|
||||
local booted_id=$(xcrun simctl list devices | grep "${SIMULATOR_DEVICE}" | grep "Booted" | head -1 | sed -E 's/.*\(([A-F0-9-]{36})\).*\(Booted\).*/\1/')
|
||||
if [ -n "${booted_id}" ] && [ "${booted_id}" != "Booted" ]; then
|
||||
echo "${booted_id}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# If no booted device, try available devices
|
||||
local available_id=$(xcrun simctl list devices available | grep "${SIMULATOR_DEVICE}" | head -1 | sed -E 's/.*\(([A-F0-9-]{36})\).*/\1/')
|
||||
if [ -n "${available_id}" ]; then
|
||||
echo "${available_id}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Last resort: try any device with similar name (handles "iPhone 15" vs "iPhone 15 Pro")
|
||||
# Prefer booted devices
|
||||
local any_id=$(xcrun simctl list devices | grep -i "iphone.*15" | grep "Booted" | head -1 | sed -E 's/.*\(([A-F0-9-]{36})\).*\(Booted\).*/\1/')
|
||||
if [ -n "${any_id}" ] && [ "${any_id}" != "Booted" ]; then
|
||||
echo "${any_id}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Try any iPhone 15 device (not necessarily booted)
|
||||
any_id=$(xcrun simctl list devices | grep -i "iphone.*15" | head -1 | sed -E 's/.*\(([A-F0-9-]{36})\).*/\1/')
|
||||
if [ -n "${any_id}" ]; then
|
||||
echo "${any_id}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
get_app_logs() {
|
||||
local device_id=$1
|
||||
local lines=${2:-50}
|
||||
xcrun simctl spawn "${device_id}" log stream --level=debug --predicate 'processImagePath contains "ios-test-app"' --style=compact 2>/dev/null | head -n "${lines}" || echo ""
|
||||
# Use log show (historical) instead of log stream (live) to avoid hanging
|
||||
# This matches Android's approach of using logcat -d (historical logs)
|
||||
# log stream can block indefinitely waiting for new logs
|
||||
# Remove predicate to catch all logs (plugin logs may not match processImagePath predicate)
|
||||
xcrun simctl spawn "${device_id}" log show --last 2m --style=compact 2>/dev/null | grep -iE "(dailynotification|ios-test-app|App)" | tail -n "${lines}" || echo ""
|
||||
}
|
||||
|
||||
check_plugin_configured() {
|
||||
@@ -88,16 +122,32 @@ check_plugin_configured() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if app has been launched (indicates configuration may exist)
|
||||
local app_data=$(xcrun simctl get_app_container "${device_id}" "${APP_BUNDLE_ID}" 2>/dev/null || echo "")
|
||||
# Check multiple ways to determine if app is installed/configured:
|
||||
# 1. Check if app container exists (most reliable for installed apps)
|
||||
local app_container=$(xcrun simctl get_app_container "${device_id}" "${APP_BUNDLE_ID}" 2>/dev/null || echo "")
|
||||
|
||||
if [ -n "${app_data}" ]; then
|
||||
print_success "App data exists (plugin may be configured)"
|
||||
# 2. Check if app is listed in simulator (installed)
|
||||
local app_listed=$(xcrun simctl listapps "${device_id}" 2>/dev/null | grep -c "${APP_BUNDLE_ID}" || echo "0")
|
||||
app_listed=$(echo "${app_listed}" | tr -d '\n' | head -1)
|
||||
app_listed=${app_listed:-0}
|
||||
|
||||
# 3. Check if app data directory exists (indicates app has been launched)
|
||||
local data_root="$HOME/Library/Developer/CoreSimulator/Devices/${device_id}/data/Containers/Data/Application"
|
||||
local app_data_exists=""
|
||||
if [ -d "${data_root}" ]; then
|
||||
# Check if any app data directory exists (app has been launched at least once)
|
||||
app_data_exists=$(find "${data_root}" -maxdepth 1 -type d 2>/dev/null | head -1 || echo "")
|
||||
fi
|
||||
|
||||
# If any check indicates app exists, consider it potentially configured
|
||||
if [ -n "${app_container}" ] || [ "${app_listed}" -gt "0" ] || [ -n "${app_data_exists}" ]; then
|
||||
print_success "App detected (plugin may be configured)"
|
||||
print_info "Please verify in the app UI that you see:"
|
||||
echo " ⚙️ Plugin Settings: ✅ Configured"
|
||||
echo " 🔌 Native Fetcher: ✅ Configured"
|
||||
echo ""
|
||||
echo "If both show ✅, the plugin is configured and you can skip configuration."
|
||||
echo "If not configured, you'll need to click 'Configure Plugin' in the app UI."
|
||||
return 0
|
||||
else
|
||||
print_info "Plugin not configured (no app data found)"
|
||||
@@ -106,6 +156,48 @@ check_plugin_configured() {
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_plugin_configured() {
|
||||
if check_plugin_configured; then
|
||||
# Plugin might be configured, but let user verify (matches Android pattern)
|
||||
wait_for_ui_action "Please check the Plugin Status section at the top of the app.
|
||||
|
||||
If you see:
|
||||
- ⚙️ Plugin Settings: ✅ Configured
|
||||
- 🔌 Native Fetcher: ✅ Configured
|
||||
- 🔔 Notifications: ✅ Granted (or similar)
|
||||
|
||||
Then the plugin is already configured - just press Enter to continue.
|
||||
|
||||
If any show ❌ or 'Not configured':
|
||||
- Click 'Request Permissions' if notifications are not granted
|
||||
- Click 'Configure Plugin' if settings/fetcher are not configured
|
||||
- Wait for all to show ✅, then press Enter."
|
||||
|
||||
# Give a moment for any configuration that just happened
|
||||
sleep 2
|
||||
print_success "Continuing with tests (plugin configuration verified or skipped)"
|
||||
return 0
|
||||
else
|
||||
# Plugin definitely needs configuration
|
||||
print_info "Plugin needs configuration"
|
||||
|
||||
# First ensure permissions
|
||||
ensure_permissions
|
||||
|
||||
wait_for_ui_action "Click the 'Configure Plugin' button in the app UI.
|
||||
|
||||
Wait for the status to update:
|
||||
- ⚙️ Plugin Settings: Should change to ✅ Configured
|
||||
- 🔌 Native Fetcher: Should change to ✅ Configured
|
||||
|
||||
Once both show ✅, press Enter to continue."
|
||||
|
||||
# Verify configuration completed
|
||||
sleep 2
|
||||
print_success "Plugin configuration completed (or verified)"
|
||||
fi
|
||||
}
|
||||
|
||||
check_permissions() {
|
||||
print_info "Checking notification permissions..."
|
||||
|
||||
@@ -152,7 +244,9 @@ launch_app() {
|
||||
fi
|
||||
|
||||
# Boot simulator if not running
|
||||
local booted=$(xcrun simctl list devices | grep "${device_id}" | grep -c "Booted" || echo "0")
|
||||
local booted=$(xcrun simctl list devices | grep "${device_id}" | grep -c "Booted" 2>/dev/null || echo "0")
|
||||
booted=$(echo "${booted}" | tr -d '\n' | head -1) # Remove newlines and take first value
|
||||
booted=${booted:-0} # Default to 0 if empty
|
||||
if [ "${booted}" -eq "0" ]; then
|
||||
print_info "Booting simulator..."
|
||||
xcrun simctl boot "${device_id}" 2>/dev/null || true
|
||||
@@ -181,11 +275,38 @@ get_pending_notifications() {
|
||||
return
|
||||
fi
|
||||
|
||||
# Note: iOS doesn't provide direct command-line access to pending notifications
|
||||
# This would need to be implemented via a plugin method
|
||||
# For now, we'll check logs for notification scheduling
|
||||
local count=$(get_app_logs "${device_id}" 100 | grep -c "scheduled notification" || echo "0")
|
||||
echo "${count}"
|
||||
# Note: iOS doesn't provide direct command-line access to pending notifications like Android's dumpsys alarm
|
||||
# We check logs for the explicit pendingCount that the plugin now logs after scheduling
|
||||
# Get logs directly without predicate to catch plugin logs (plugin runs in app process)
|
||||
local logs=$(xcrun simctl spawn "${device_id}" log show --last 2m --style=compact 2>/dev/null | grep -iE "(DailyNotificationScheduler|pendingCount|dailynotification)" | tail -100 || echo "")
|
||||
|
||||
# Method 1: Look for explicit pendingCount in scheduling logs (most reliable)
|
||||
# The plugin logs: "Notification scheduled successfully for ..., id=..., pendingCount=X"
|
||||
# Match case-insensitive and extract the number after pendingCount=
|
||||
local pending_count=$(echo "${logs}" | grep -iE "pendingCount[=:][[:space:]]*[0-9]+" | tail -1 | grep -oE "[0-9]+" | head -1 || echo "")
|
||||
|
||||
# Method 2: Look for "pending" count in status responses
|
||||
# The plugin's getNotificationStatus returns "pending": count
|
||||
local status_count=$(echo "${logs}" | grep -E "\"pending\"[[:space:]]*:[[:space:]]*[0-9]+" | tail -1 | grep -oE "[0-9]+" | head -1 || echo "")
|
||||
|
||||
# Method 3: Count unique notification IDs that were scheduled (fallback)
|
||||
# Extract notification IDs from scheduling logs (format: "id=daily_...")
|
||||
local notification_ids=$(echo "${logs}" | grep -oE "id=[a-zA-Z0-9_.-]+" | sed 's/id=//' | sort -u | wc -l | tr -d ' ')
|
||||
notification_ids=${notification_ids:-0}
|
||||
|
||||
# Prefer explicit pendingCount from scheduling log (most reliable)
|
||||
if [ -n "${pending_count}" ] && [ "${pending_count}" -ge "0" ] 2>/dev/null && [ "${pending_count}" -le "64" ] 2>/dev/null; then
|
||||
echo "${pending_count}"
|
||||
# Otherwise use status count if available
|
||||
elif [ -n "${status_count}" ] && [ "${status_count}" -ge "0" ] 2>/dev/null && [ "${status_count}" -le "64" ] 2>/dev/null; then
|
||||
echo "${status_count}"
|
||||
# Fallback to counting unique notification IDs
|
||||
elif [ "${notification_ids}" -gt "0" ]; then
|
||||
echo "${notification_ids}"
|
||||
# Default to 0 if nothing found
|
||||
else
|
||||
echo "0"
|
||||
fi
|
||||
}
|
||||
|
||||
should_run_test() {
|
||||
@@ -223,13 +344,7 @@ if should_run_test "0" "$@"; then
|
||||
|
||||
print_step "1" "Schedule a test notification for near-future..."
|
||||
launch_app
|
||||
check_plugin_configured || {
|
||||
wait_for_ui_action "In the app UI, click 'Configure Plugin' to set up the plugin.
|
||||
|
||||
This will initialize the database and storage.
|
||||
|
||||
Once configured, press Enter to continue."
|
||||
}
|
||||
ensure_plugin_configured
|
||||
|
||||
INITIAL_COUNT=$(get_pending_notifications)
|
||||
print_info "Current pending notifications: ${INITIAL_COUNT}"
|
||||
@@ -302,7 +417,7 @@ This creates a notification that should persist across app termination."
|
||||
print_step "2" "Terminate app (simulate cold start)..."
|
||||
print_info "Terminating app to simulate cold start scenario..."
|
||||
|
||||
local device_id=$(get_simulator_id)
|
||||
device_id=$(get_simulator_id)
|
||||
xcrun simctl terminate "${device_id}" "${APP_BUNDLE_ID}" 2>/dev/null || true
|
||||
|
||||
print_info "App terminated. Waiting 5 seconds..."
|
||||
@@ -314,8 +429,8 @@ This creates a notification that should persist across app termination."
|
||||
print_info "Checking logs for recovery activity..."
|
||||
sleep 3
|
||||
|
||||
local device_id=$(get_simulator_id)
|
||||
local logs=$(get_app_logs "${device_id}" 100)
|
||||
device_id=$(get_simulator_id)
|
||||
logs=$(get_app_logs "${device_id}" 100)
|
||||
|
||||
if echo "${logs}" | grep -q "DNP-REACTIVATION\|recovery\|missed"; then
|
||||
print_success "Recovery activity detected in logs"
|
||||
@@ -355,7 +470,7 @@ if should_run_test "2" "$@"; then
|
||||
print_step "2" "Terminate app..."
|
||||
print_info "Terminating app (simulating swipe from app switcher)..."
|
||||
|
||||
local device_id=$(get_simulator_id)
|
||||
device_id=$(get_simulator_id)
|
||||
xcrun simctl terminate "${device_id}" "${APP_BUNDLE_ID}" 2>/dev/null || true
|
||||
|
||||
print_info "App terminated. Waiting 3 seconds..."
|
||||
@@ -399,8 +514,8 @@ The app should show an error message and NOT crash."
|
||||
print_info "Checking logs for error handling..."
|
||||
sleep 2
|
||||
|
||||
local device_id=$(get_simulator_id)
|
||||
local logs=$(get_app_logs "${device_id}" 50)
|
||||
device_id=$(get_simulator_id)
|
||||
logs=$(get_app_logs "${device_id}" 50)
|
||||
|
||||
if echo "${logs}" | grep -q "error\|invalid\|Error"; then
|
||||
print_success "Error handling detected in logs"
|
||||
|
||||
Reference in New Issue
Block a user