You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							939 lines
						
					
					
						
							35 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							939 lines
						
					
					
						
							35 KiB
						
					
					
				
								/**
							 | 
						|
								 * @fileoverview iOS test runner for Capacitor-based mobile app
							 | 
						|
								 * 
							 | 
						|
								 * This script handles the build and testing of the iOS app using Xcode's
							 | 
						|
								 * command-line tools. It ensures the app is properly synced with the latest
							 | 
						|
								 * web build and runs the test suite on a specified iOS simulator.
							 | 
						|
								 * 
							 | 
						|
								 * Process flow:
							 | 
						|
								 * 1. Clean and reset iOS platform (if needed)
							 | 
						|
								 * 2. Check prerequisites (Xcode, CocoaPods, Capacitor setup)
							 | 
						|
								 * 3. Sync Capacitor project with latest web build
							 | 
						|
								 * 4. Build app for iOS simulator
							 | 
						|
								 * 5. Run XCTest suite
							 | 
						|
								 * 
							 | 
						|
								 * Prerequisites:
							 | 
						|
								 * - macOS operating system
							 | 
						|
								 * - Xcode installed with command line tools
							 | 
						|
								 * - iOS simulator available
							 | 
						|
								 * - Capacitor iOS platform added to project
							 | 
						|
								 * - Valid iOS development certificates
							 | 
						|
								 * 
							 | 
						|
								 * Exit codes:
							 | 
						|
								 * - 0: Tests completed successfully
							 | 
						|
								 * - 1: Build or test failure
							 | 
						|
								 * 
							 | 
						|
								 * @example
							 | 
						|
								 * // Run directly
							 | 
						|
								 * node scripts/test-ios.js
							 | 
						|
								 * 
							 | 
						|
								 * // Run via npm script
							 | 
						|
								 * npm run test:ios
							 | 
						|
								 * 
							 | 
						|
								 * @requires child_process
							 | 
						|
								 * @requires path
							 | 
						|
								 * @requires fs
							 | 
						|
								 * 
							 | 
						|
								 * @author TimeSafari Team
							 | 
						|
								 * @license MIT
							 | 
						|
								 */
							 | 
						|
								
							 | 
						|
								const { execSync } = require('child_process');
							 | 
						|
								const { join } = require('path');
							 | 
						|
								const { existsSync, mkdirSync, appendFileSync, readFileSync, writeFileSync, readdirSync, statSync, accessSync } = require('fs');
							 | 
						|
								const readline = require('readline');
							 | 
						|
								const rl = readline.createInterface({
							 | 
						|
								    input: process.stdin,
							 | 
						|
								    output: process.stdout
							 | 
						|
								});
							 | 
						|
								const { constants } = require('fs');
							 | 
						|
								
							 | 
						|
								const question = (prompt) => new Promise((resolve) => rl.question(prompt, resolve));
							 | 
						|
								
							 | 
						|
								// Make sure to close readline at the end
							 | 
						|
								process.on('SIGINT', () => {
							 | 
						|
								    rl.close();
							 | 
						|
								    process.exit();
							 | 
						|
								});
							 | 
						|
								
							 | 
						|
								// Format date as YYYY-MM-DD-HHMMSS
							 | 
						|
								const getLogFileName = () => {
							 | 
						|
								    const now = new Date();
							 | 
						|
								    const date = now.toISOString().split('T')[0];
							 | 
						|
								    const time = now.toTimeString().split(' ')[0].replace(/:/g, '');
							 | 
						|
								    return `build_logs/ios-build-${date}-${time}.log`;
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								// Create logger function
							 | 
						|
								const createLogger = (logFile) => {
							 | 
						|
								    return (message) => {
							 | 
						|
								        const timestamp = new Date().toISOString();
							 | 
						|
								        const logMessage = `[${timestamp}] ${message}\n`;
							 | 
						|
								        console.log(message);
							 | 
						|
								        appendFileSync(logFile, logMessage);
							 | 
						|
								    };
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Clean up and reset iOS platform
							 | 
						|
								 * This function completely removes and recreates the iOS platform to ensure a fresh setup
							 | 
						|
								 * @param {function} log - Logging function
							 | 
						|
								 * @returns {boolean} - Success status
							 | 
						|
								 */
							 | 
						|
								const cleanIosPlatform = async (log) => {
							 | 
						|
								    log('🧹 Cleaning iOS platform (complete reset)...');
							 | 
						|
								    
							 | 
						|
								    // Check for package.json and capacitor.config.ts/js
							 | 
						|
								    if (!existsSync('package.json')) {
							 | 
						|
								        log('⚠️ package.json not found. Are you in the correct directory?');
							 | 
						|
								        throw new Error('package.json not found. Cannot continue without project configuration.');
							 | 
						|
								    }
							 | 
						|
								    log('✅ package.json exists');
							 | 
						|
								    
							 | 
						|
								    const capacitorConfigExists = 
							 | 
						|
								        existsSync('capacitor.config.ts') || 
							 | 
						|
								        existsSync('capacitor.config.js') || 
							 | 
						|
								        existsSync('capacitor.config.json');
							 | 
						|
								    
							 | 
						|
								    if (!capacitorConfigExists) {
							 | 
						|
								        log('⚠️ Capacitor config file not found');
							 | 
						|
								        log('Creating minimal capacitor.config.ts...');
							 | 
						|
								        
							 | 
						|
								        try {
							 | 
						|
								            // Get app name from package.json
							 | 
						|
								            const packageJson = JSON.parse(readFileSync('package.json', 'utf8'));
							 | 
						|
								            const appName = packageJson.name || 'App';
							 | 
						|
								            const appId = packageJson.build.appId || 'io.ionic.starter';
							 | 
						|
								            
							 | 
						|
								            // Create a minimal capacitor config
							 | 
						|
								            const capacitorConfig = `
							 | 
						|
								import { CapacitorConfig } from '@capacitor/cli';
							 | 
						|
								
							 | 
						|
								const config: CapacitorConfig = {
							 | 
						|
								  appId: '${appId}',
							 | 
						|
								  appName: '${appName}',
							 | 
						|
								  webDir: 'dist',
							 | 
						|
								  bundledWebRuntime: false
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								export default config;
							 | 
						|
								            `.trim();
							 | 
						|
								            
							 | 
						|
								            writeFileSync('capacitor.config.ts', capacitorConfig);
							 | 
						|
								            log('✅ Created capacitor.config.ts');
							 | 
						|
								        } catch (configError) {
							 | 
						|
								            log('⚠️ Failed to create Capacitor config file');
							 | 
						|
								            log('Please create a capacitor.config.ts file manually');
							 | 
						|
								            throw new Error('Capacitor configuration missing. Please configure manually.');
							 | 
						|
								        }
							 | 
						|
								    } else {
							 | 
						|
								        log('✅ Capacitor config exists');
							 | 
						|
								    }
							 | 
						|
								    
							 | 
						|
								    // Check if the platform exists first
							 | 
						|
								    if (existsSync('ios')) {
							 | 
						|
								        log('🗑️ Removing existing iOS platform directory...');
							 | 
						|
								        try {
							 | 
						|
								            execSync('rm -rf ios', { stdio: 'inherit' });
							 | 
						|
								            log('✅ Existing iOS platform removed');
							 | 
						|
								        } catch (error) {
							 | 
						|
								            log(`⚠️ Error removing iOS platform: ${error.message}`);
							 | 
						|
								            log('⚠️ You may need to manually remove the ios directory');
							 | 
						|
								            return false;
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								    
							 | 
						|
								    // Rebuild web assets first to ensure they're available
							 | 
						|
								    log('🔄 Building web assets before adding iOS platform...');
							 | 
						|
								    try {
							 | 
						|
								        execSync('rm -rf dist', { stdio: 'inherit' });
							 | 
						|
								        execSync('npm run build:web', { stdio: 'inherit' });
							 | 
						|
								        execSync('npm run build:capacitor', { stdio: 'inherit' });
							 | 
						|
								        log('✅ Web assets built successfully');
							 | 
						|
								    } catch (error) {
							 | 
						|
								        log(`⚠️ Error building web assets: ${error.message}`);
							 | 
						|
								        log('⚠️ Continuing with platform addition, but it may fail if web assets are required');
							 | 
						|
								    }
							 | 
						|
								    
							 | 
						|
								    // Add the platform back
							 | 
						|
								    log('➕ Adding iOS platform...');
							 | 
						|
								    try {
							 | 
						|
								        execSync('npx cap add ios', { stdio: 'inherit' });
							 | 
						|
								        log('✅ iOS platform added successfully');
							 | 
						|
								        
							 | 
						|
								        // Verify critical files were created
							 | 
						|
								        if (!existsSync('ios/App/Podfile')) {
							 | 
						|
								            log('⚠️ Podfile was not created - something is wrong with the Capacitor setup');
							 | 
						|
								            return false;
							 | 
						|
								        }
							 | 
						|
								        
							 | 
						|
								        if (!existsSync('ios/App/App/Info.plist')) {
							 | 
						|
								            log('⚠️ Info.plist was not created - something is wrong with the Capacitor setup');
							 | 
						|
								            return false;
							 | 
						|
								        }
							 | 
						|
								        
							 | 
						|
								        log('✅ iOS platform setup verified - critical files exist');
							 | 
						|
								        return true;
							 | 
						|
								    } catch (error) {
							 | 
						|
								        log(`⚠️ Error adding iOS platform: ${error.message}`);
							 | 
						|
								        return false;
							 | 
						|
								    }
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Check all prerequisites for iOS testing
							 | 
						|
								 * Verifies and attempts to install/initialize all required components
							 | 
						|
								 */
							 | 
						|
								const checkPrerequisites = async (log) => {
							 | 
						|
								    log('🔍 Checking prerequisites for iOS testing...');
							 | 
						|
								    
							 | 
						|
								    // Check for macOS
							 | 
						|
								    if (process.platform !== 'darwin') {
							 | 
						|
								        throw new Error('iOS testing is only supported on macOS');
							 | 
						|
								    }
							 | 
						|
								    log('✅ Running on macOS');
							 | 
						|
								    
							 | 
						|
								    // Verify Xcode installation
							 | 
						|
								    try {
							 | 
						|
								        const xcodeOutput = execSync('xcode-select -p').toString().trim();
							 | 
						|
								        log(`✅ Xcode command line tools found at: ${xcodeOutput}`);
							 | 
						|
								    } catch (error) {
							 | 
						|
								        log('⚠️ Xcode command line tools not found');
							 | 
						|
								        log('Please install Xcode from the App Store and run:');
							 | 
						|
								        log('xcode-select --install');
							 | 
						|
								        throw new Error('Xcode command line tools not found. Please install Xcode first.');
							 | 
						|
								    }
							 | 
						|
								    
							 | 
						|
								    // Check Xcode version
							 | 
						|
								    try {
							 | 
						|
								        const xcodeVersionOutput = execSync('xcodebuild -version').toString().trim();
							 | 
						|
								        log(`✅ Xcode version: ${xcodeVersionOutput.split('\n')[0]}`);
							 | 
						|
								    } catch (error) {
							 | 
						|
								        log('⚠️ Unable to determine Xcode version');
							 | 
						|
								    }
							 | 
						|
								    
							 | 
						|
								    // Check for CocoaPods
							 | 
						|
								    try {
							 | 
						|
								        const podVersionOutput = execSync('pod --version').toString().trim();
							 | 
						|
								        log(`✅ CocoaPods version: ${podVersionOutput}`);
							 | 
						|
								    } catch (error) {
							 | 
						|
								        log('⚠️ CocoaPods not found');
							 | 
						|
								        log('Attempting to install CocoaPods...');
							 | 
						|
								        
							 | 
						|
								        try {
							 | 
						|
								            log('🔄 Installing CocoaPods via gem...');
							 | 
						|
								            execSync('gem install cocoapods', { stdio: 'inherit' });
							 | 
						|
								            log('✅ CocoaPods installed successfully');
							 | 
						|
								        } catch (gemError) {
							 | 
						|
								            log('⚠️ Failed to install CocoaPods via gem');
							 | 
						|
								            log('Please install CocoaPods manually:');
							 | 
						|
								            log('1. sudo gem install cocoapods');
							 | 
						|
								            log('2. brew install cocoapods');
							 | 
						|
								            throw new Error('CocoaPods installation failed. Please install manually.');
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								    
							 | 
						|
								    log('✅ All prerequisites for iOS testing are met');
							 | 
						|
								    return true;
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								// Check for iOS simulator
							 | 
						|
								const checkSimulator = async (log) => {
							 | 
						|
								    log('🔍 Checking for iOS simulator...');
							 | 
						|
								    const simulatorsOutput = execSync('xcrun simctl list devices available -j').toString();
							 | 
						|
								    const simulatorsData = JSON.parse(simulatorsOutput);
							 | 
						|
								    
							 | 
						|
								    // Get all available devices/simulators with their UDIDs
							 | 
						|
								    const allDevices = [];
							 | 
						|
								    
							 | 
						|
								    // Process all runtime groups (iOS versions)
							 | 
						|
								    Object.entries(simulatorsData.devices).forEach(([runtime, devices]) => {
							 | 
						|
								        devices.forEach(device => {
							 | 
						|
								            allDevices.push({
							 | 
						|
								                name: device.name,
							 | 
						|
								                udid: device.udid,
							 | 
						|
								                state: device.state,
							 | 
						|
								                runtime: runtime,
							 | 
						|
								                isIphone: device.name.includes('iPhone'),
							 | 
						|
								            });
							 | 
						|
								        });
							 | 
						|
								    });
							 | 
						|
								    
							 | 
						|
								    // Check for booted simulators first
							 | 
						|
								    const bootedDevices = allDevices.filter(device => device.state === 'Booted');
							 | 
						|
								    
							 | 
						|
								    if (bootedDevices.length > 0) {
							 | 
						|
								        log(`📱 Found ${bootedDevices.length} running simulator(s): ${bootedDevices.map(d => d.name).join(', ')}`);
							 | 
						|
								        return bootedDevices;
							 | 
						|
								    }
							 | 
						|
								    
							 | 
						|
								    // No booted devices found, try to boot one
							 | 
						|
								    log('⚠️ No running iOS simulator found. Attempting to boot one...');
							 | 
						|
								    
							 | 
						|
								    // Prefer iPhone devices, especially newer models
							 | 
						|
								    const preferredDevices = [
							 | 
						|
								        'iPhone 15', 'iPhone 14', 'iPhone 13', 'iPhone 12', 'iPhone',  // Prefer newer iPhones first
							 | 
						|
								        'iPad'  // Then iPads if no iPhones available
							 | 
						|
								    ];
							 | 
						|
								    
							 | 
						|
								    let deviceToLaunch = null;
							 | 
						|
								    
							 | 
						|
								    // Try to find a device from our preferred list
							 | 
						|
								    for (const preferredName of preferredDevices) {
							 | 
						|
								        const matchingDevices = allDevices.filter(device => 
							 | 
						|
								            device.name.includes(preferredName) && device.state === 'Shutdown');
							 | 
						|
								        
							 | 
						|
								        if (matchingDevices.length > 0) {
							 | 
						|
								            // Sort by runtime to prefer newer iOS versions
							 | 
						|
								            matchingDevices.sort((a, b) => b.runtime.localeCompare(a.runtime));
							 | 
						|
								            deviceToLaunch = matchingDevices[0];
							 | 
						|
								            break;
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								    
							 | 
						|
								    // If no preferred device found, take any available device
							 | 
						|
								    if (!deviceToLaunch && allDevices.length > 0) {
							 | 
						|
								        const availableDevices = allDevices.filter(device => device.state === 'Shutdown');
							 | 
						|
								        if (availableDevices.length > 0) {
							 | 
						|
								            deviceToLaunch = availableDevices[0];
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								    
							 | 
						|
								    if (!deviceToLaunch) {
							 | 
						|
								        throw new Error('No available iOS simulators found. Please create a simulator in Xcode first.');
							 | 
						|
								    }
							 | 
						|
								    
							 | 
						|
								    // Boot the selected simulator
							 | 
						|
								    log(`🚀 Booting iOS simulator: ${deviceToLaunch.name} (${deviceToLaunch.runtime})`);
							 | 
						|
								    execSync(`xcrun simctl boot ${deviceToLaunch.udid}`);
							 | 
						|
								    
							 | 
						|
								    // Wait for simulator to fully boot
							 | 
						|
								    log('⏳ Waiting for simulator to boot completely...');
							 | 
						|
								    // Give the simulator time to fully boot before proceeding
							 | 
						|
								    await new Promise(resolve => setTimeout(resolve, 10000));
							 | 
						|
								    
							 | 
						|
								    log(`✅ Successfully booted simulator: ${deviceToLaunch.name}`);
							 | 
						|
								    
							 | 
						|
								    return [{ name: deviceToLaunch.name, udid: deviceToLaunch.udid }];
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								// Verify Xcode installation
							 | 
						|
								const verifyXcodeInstallation = (log) => {
							 | 
						|
								    log('🔍 Checking Xcode installation...');
							 | 
						|
								    try {
							 | 
						|
								        execSync('xcode-select -p');
							 | 
						|
								        log('✅ Xcode command line tools found');
							 | 
						|
								    } catch (error) {
							 | 
						|
								        throw new Error('Xcode command line tools not found. Please install Xcode first.');
							 | 
						|
								    }
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								// Generate test data using generate_data.ts
							 | 
						|
								const generateTestData = async (log) => {
							 | 
						|
								    log('\n🔍 Starting test data generation...');
							 | 
						|
								    
							 | 
						|
								    // Check directory structure
							 | 
						|
								    log('📁 Current directory:', process.cwd());
							 | 
						|
								    log('📁 Directory contents:', require('fs').readdirSync('.'));
							 | 
						|
								    
							 | 
						|
								    if (!existsSync('.generated')) {
							 | 
						|
								        log('📁 Creating .generated directory');
							 | 
						|
								        mkdirSync('.generated', { recursive: true });
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    try {
							 | 
						|
								        log('🔄 Attempting to run generate_data.ts...');
							 | 
						|
								        execSync('npx ts-node test-scripts/generate_data.ts', { stdio: 'inherit' });
							 | 
						|
								        log('✅ Test data generation completed');
							 | 
						|
								        
							 | 
						|
								        // Verify and log generated files content
							 | 
						|
								        const requiredFiles = [
							 | 
						|
								            '.generated/test-env.json',
							 | 
						|
								            '.generated/claim_details.json',
							 | 
						|
								            '.generated/contacts.json'
							 | 
						|
								        ];
							 | 
						|
								        
							 | 
						|
								        log('\n📝 Verifying generated files:');
							 | 
						|
								        for (const file of requiredFiles) {
							 | 
						|
								            if (!existsSync(file)) {
							 | 
						|
								                log(`❌ Missing file: ${file}`);
							 | 
						|
								            } else {
							 | 
						|
								                const content = readFileSync(file, 'utf8');
							 | 
						|
								                log(`\n📄 Content of ${file}:`);
							 | 
						|
								                log(content);
							 | 
						|
								                try {
							 | 
						|
								                    const parsed = JSON.parse(content);
							 | 
						|
								                    if (file.includes('test-env.json')) {
							 | 
						|
								                        log('🔑 CONTACT1_DID in test-env:', parsed.CONTACT1_DID);
							 | 
						|
								                    }
							 | 
						|
								                    if (file.includes('contacts.json')) {
							 | 
						|
								                        log('👥 First contact DID:', parsed[0]?.did);
							 | 
						|
								                    }
							 | 
						|
								                } catch (e) {
							 | 
						|
								                    log(`❌ Error parsing ${file}:`, e);
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								    } catch (error) {
							 | 
						|
								        log(`\n⚠️ Test data generation failed: ${error.message}`);
							 | 
						|
								        log('⚠️ Creating fallback test data...');
							 | 
						|
								        
							 | 
						|
								        // Create fallback data with detailed logging
							 | 
						|
								        const fallbackTestEnv = {
							 | 
						|
								            "CONTACT1_DID": "did:ethr:0x35A71Ac3fA0A4D5a4903f10F0f7A3ac4034FaB5B",
							 | 
						|
								            "APP_URL": "https://app.timesafari.example"
							 | 
						|
								        };
							 | 
						|
								        
							 | 
						|
								        const fallbackContacts = [
							 | 
						|
								            {
							 | 
						|
								                "id": "contact1",
							 | 
						|
								                "name": "Test Contact",
							 | 
						|
								                "did": "did:ethr:0x35A71Ac3fA0A4D5a4903f10F0f7A3ac4034FaB5B"
							 | 
						|
								            }
							 | 
						|
								        ];
							 | 
						|
								        
							 | 
						|
								        log('\n📝 Writing fallback data:');
							 | 
						|
								        log('TestEnv:', JSON.stringify(fallbackTestEnv, null, 2));
							 | 
						|
								        log('Contacts:', JSON.stringify(fallbackContacts, null, 2));
							 | 
						|
								        
							 | 
						|
								        writeFileSync('.generated/test-env.json', JSON.stringify(fallbackTestEnv, null, 2));
							 | 
						|
								        writeFileSync('.generated/contacts.json', JSON.stringify(fallbackContacts, null, 2));
							 | 
						|
								        
							 | 
						|
								        // Verify fallback data was written
							 | 
						|
								        log('\n🔍 Verifying fallback data:');
							 | 
						|
								        try {
							 | 
						|
								            const writtenTestEnv = JSON.parse(readFileSync('.generated/test-env.json', 'utf8'));
							 | 
						|
								            const writtenContacts = JSON.parse(readFileSync('.generated/contacts.json', 'utf8'));
							 | 
						|
								            log('Written TestEnv:', writtenTestEnv);
							 | 
						|
								            log('Written Contacts:', writtenContacts);
							 | 
						|
								        } catch (e) {
							 | 
						|
								            log('❌ Error verifying fallback data:', e);
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								// Build web assets
							 | 
						|
								const buildWebAssets = async (log) => {
							 | 
						|
								    log('🌐 Building web assets...');
							 | 
						|
								    execSync('rm -rf dist', { stdio: 'inherit' });
							 | 
						|
								    execSync('npm run build:web', { stdio: 'inherit' });
							 | 
						|
								    execSync('npm run build:capacitor', { stdio: 'inherit' });
							 | 
						|
								    log('✅ Web assets built successfully');
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								// Configure iOS project
							 | 
						|
								const configureIosProject = async (log) => {
							 | 
						|
								    log('📱 Configuring iOS project...');
							 | 
						|
								    
							 | 
						|
								    // Skip cap sync since we just did a clean platform add
							 | 
						|
								    log('✅ Using freshly created iOS platform');
							 | 
						|
								
							 | 
						|
								    // Register URL scheme for deeplink tests
							 | 
						|
								    log('🔗 Configuring URL scheme for deeplink tests...');
							 | 
						|
								    if (checkAndRegisterUrlScheme(log)) {
							 | 
						|
								        log('✅ URL scheme configuration completed');
							 | 
						|
								    } else {
							 | 
						|
								        log('⚠️ URL scheme could not be registered automatically');
							 | 
						|
								        log('⚠️ Deeplink tests may not work correctly');
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    log('⚙️ Installing CocoaPods dependencies...');
							 | 
						|
								    try {
							 | 
						|
								        // Try to run pod install normally first
							 | 
						|
								        log('🔄 Running "pod install" in ios/App directory...');
							 | 
						|
								    execSync('cd ios/App && pod install', { stdio: 'inherit' });
							 | 
						|
								    log('✅ CocoaPods installation completed');
							 | 
						|
								    } catch (error) {
							 | 
						|
								        // If that fails, provide detailed instructions
							 | 
						|
								        log(`⚠️ CocoaPods installation failed: ${error.message}`);
							 | 
						|
								        log('⚠️ Please ensure CocoaPods is installed correctly:');
							 | 
						|
								        log('1. If using system Ruby: "sudo gem install cocoapods"');
							 | 
						|
								        log('2. If using Homebrew Ruby: "brew install cocoapods"');
							 | 
						|
								        log('3. Then run: "cd ios/App && pod install"');
							 | 
						|
								        
							 | 
						|
								        // Try to continue despite the error
							 | 
						|
								        log('⚠️ Attempting to continue with the build process...');
							 | 
						|
								    }
							 | 
						|
								    
							 | 
						|
								    // Add information about iOS security dialogs
							 | 
						|
								    log('\n📱 iOS Security Dialog Information:');
							 | 
						|
								    log('⚠️ iOS will display security confirmation dialogs when testing deeplinks');
							 | 
						|
								    log('⚠️ This is a security feature of iOS and cannot be bypassed in normal testing');
							 | 
						|
								    log('⚠️ You will need to manually approve each deeplink test by clicking "Open" in the dialog');
							 | 
						|
								    log('⚠️ The app must be running in the foreground for deeplinks to work properly');
							 | 
						|
								    log('⚠️ If tests appear to hang, check if a security dialog is waiting for your confirmation');
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								// Build and test iOS project
							 | 
						|
								const buildAndTestIos = async (log, simulator) => {
							 | 
						|
								    const simulatorName = simulator[0].name;
							 | 
						|
								    log('🏗️ Building iOS project...', simulator[0]);
							 | 
						|
								    execSync('cd ios/App && xcodebuild clean -workspace App.xcworkspace -scheme App', { stdio: 'inherit' });
							 | 
						|
								    log('✅ Xcode clean completed');
							 | 
						|
								    
							 | 
						|
								    log(`🏗️ Building for simulator: ${simulatorName}`);
							 | 
						|
								    execSync(`cd ios/App && xcodebuild build -workspace App.xcworkspace -scheme App -destination "platform=iOS Simulator,OS=17.2,name=${simulatorName}"`, { stdio: 'inherit' });
							 | 
						|
								    log('✅ Xcode build completed');
							 | 
						|
								
							 | 
						|
								    // Check if the project is configured for testing by querying the scheme capabilities
							 | 
						|
								    try {
							 | 
						|
								        log(`🧪 Checking if scheme is configured for testing`);
							 | 
						|
								        const schemeInfo = execSync(`cd ios/App && xcodebuild -scheme App -showBuildSettings | grep TEST`).toString();
							 | 
						|
								        
							 | 
						|
								        if (schemeInfo.includes('ENABLE_TESTABILITY = YES')) {
							 | 
						|
								            log(`🧪 Attempting to run tests on simulator: ${simulatorName}`);
							 | 
						|
								            try {
							 | 
						|
								                execSync(`cd ios/App && xcodebuild test -workspace App.xcworkspace -scheme App -destination "platform=iOS Simulator,name=${simulatorName}"`, { stdio: 'inherit' });
							 | 
						|
								                log('✅ iOS tests completed successfully');
							 | 
						|
								            } catch (testError) {
							 | 
						|
								                log(`⚠️ Tests failed or scheme not properly configured for testing: ${testError.message}`);
							 | 
						|
								                log('⚠️ This is normal if no test targets have been added to the project');
							 | 
						|
								                log('⚠️ Skipping test step and continuing with the app launch');
							 | 
						|
								            }
							 | 
						|
								        } else {
							 | 
						|
								            log('⚠️ Project does not have testing enabled in build settings');
							 | 
						|
								            log('⚠️ Skipping test step and continuing with the app launch');
							 | 
						|
								        }
							 | 
						|
								    } catch (error) {
							 | 
						|
								        log('⚠️ Unable to determine if testing is configured');
							 | 
						|
								        log('⚠️ Skipping test step and continuing with the app launch');
							 | 
						|
								    }
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								// Run the app
							 | 
						|
								const runIosApp = async (log, simulator) => {
							 | 
						|
								    const simulatorName = simulator[0].name;
							 | 
						|
								    const simulatorUdid = simulator[0].udid;
							 | 
						|
								    
							 | 
						|
								    log(`📱 Running app in simulator: ${simulatorName} (${simulatorUdid})...`);
							 | 
						|
								    // Use the --target parameter to specify the device directly, avoiding the UI prompt
							 | 
						|
								    execSync(`npx cap run ios --target="${simulatorUdid}"`, { stdio: 'inherit' });
							 | 
						|
								    log('✅ App launched successfully');
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								const validateTestData = (log) => {
							 | 
						|
								    log('\n=== VALIDATING TEST DATA ===');
							 | 
						|
								    
							 | 
						|
								    const generateFreshTestData = () => {
							 | 
						|
								        log('\n🔄 Generating fresh test data...');
							 | 
						|
								        try {
							 | 
						|
								            // Ensure .generated directory exists
							 | 
						|
								            if (!existsSync('.generated')) {
							 | 
						|
								                mkdirSync('.generated', { recursive: true });
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            // Execute the generate_data.ts script synchronously
							 | 
						|
								            log('Running generate_data.ts...');
							 | 
						|
								            execSync('npx ts-node test-scripts/generate_data.ts', { 
							 | 
						|
								                stdio: 'inherit',
							 | 
						|
								                encoding: 'utf8'
							 | 
						|
								            });
							 | 
						|
								
							 | 
						|
								            // Read and validate the generated files
							 | 
						|
								            const testEnvPath = '.generated/test-env.json';
							 | 
						|
								            const contactsPath = '.generated/contacts.json';
							 | 
						|
								            
							 | 
						|
								            if (!existsSync(testEnvPath) || !existsSync(contactsPath)) {
							 | 
						|
								                throw new Error('Generated files not found after running generate_data.ts');
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            const testEnv = JSON.parse(readFileSync(testEnvPath, 'utf8'));
							 | 
						|
								            const contacts = JSON.parse(readFileSync(contactsPath, 'utf8'));
							 | 
						|
								
							 | 
						|
								            // Validate required fields
							 | 
						|
								            if (!testEnv.CONTACT1_DID) {
							 | 
						|
								                throw new Error('CONTACT1_DID missing from generated test data');
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            log('Generated test data:', {
							 | 
						|
								                testEnv: testEnv,
							 | 
						|
								                contacts: contacts
							 | 
						|
								            });
							 | 
						|
								
							 | 
						|
								            return { testEnv, contacts };
							 | 
						|
								        } catch (error) {
							 | 
						|
								            log('❌ Test data generation failed:', error);
							 | 
						|
								            throw error;
							 | 
						|
								        }
							 | 
						|
								    };
							 | 
						|
								
							 | 
						|
								    try {
							 | 
						|
								        // Try to read existing data or generate fresh data
							 | 
						|
								        const testEnvPath = '.generated/test-env.json';
							 | 
						|
								        const contactsPath = '.generated/contacts.json';
							 | 
						|
								        
							 | 
						|
								        let testData;
							 | 
						|
								        
							 | 
						|
								        // If either file is missing or invalid, generate fresh data
							 | 
						|
								        if (!existsSync(testEnvPath) || !existsSync(contactsPath)) {
							 | 
						|
								            testData = generateFreshTestData();
							 | 
						|
								        } else {
							 | 
						|
								            try {
							 | 
						|
								                const testEnv = JSON.parse(readFileSync(testEnvPath, 'utf8'));
							 | 
						|
								                const contacts = JSON.parse(readFileSync(contactsPath, 'utf8'));
							 | 
						|
								                
							 | 
						|
								                // Validate required fields
							 | 
						|
								                if (!testEnv.CLAIM_ID || !testEnv.CONTACT1_DID) {
							 | 
						|
								                    log('⚠️ Existing test data missing required fields, regenerating...');
							 | 
						|
								                    testData = generateFreshTestData();
							 | 
						|
								                } else {
							 | 
						|
								                    testData = { testEnv, contacts };
							 | 
						|
								                }
							 | 
						|
								            } catch (error) {
							 | 
						|
								                log('⚠️ Error reading existing test data, regenerating...');
							 | 
						|
								                testData = generateFreshTestData();
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								        
							 | 
						|
								        // Final validation of data
							 | 
						|
								        if (!testData.testEnv.CLAIM_ID || !testData.testEnv.CONTACT1_DID) {
							 | 
						|
								            throw new Error('Test data validation failed even after generation');
							 | 
						|
								        }
							 | 
						|
								        
							 | 
						|
								        log('✅ Test data validated successfully');
							 | 
						|
								        log('📄 Test Environment:', JSON.stringify(testData.testEnv, null, 2));
							 | 
						|
								        
							 | 
						|
								        return testData;
							 | 
						|
								        
							 | 
						|
								    } catch (error) {
							 | 
						|
								        log(`❌ Test data validation failed: ${error.message}`);
							 | 
						|
								        throw error;
							 | 
						|
								    }
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Run deeplink tests 
							 | 
						|
								 * Optionally tests deeplinks if the test data is available
							 | 
						|
								 * 
							 | 
						|
								 * @param {function} log - Logging function
							 | 
						|
								 * @returns {Promise<void>}
							 | 
						|
								 */
							 | 
						|
								const runDeeplinkTests = async (log) => {
							 | 
						|
								    log('\n=== Starting Deeplink Tests ===');
							 | 
						|
								    
							 | 
						|
								    // Validate test data before proceeding
							 | 
						|
								    let testEnv, contacts;
							 | 
						|
								    try {
							 | 
						|
								        ({ testEnv, contacts } = validateTestData(log));
							 | 
						|
								    } catch (error) {
							 | 
						|
								        log('❌ Cannot proceed with tests due to invalid test data');
							 | 
						|
								        log(`Error: ${error.message}`);
							 | 
						|
								        log('Please ensure test data is properly generated before running tests');
							 | 
						|
								        process.exit(1); // Exit with error code
							 | 
						|
								    }
							 | 
						|
								    
							 | 
						|
								    // Now we can safely create the deeplink tests knowing we have valid data
							 | 
						|
								        const deeplinkTests = [
							 | 
						|
								            {
							 | 
						|
								            url: `timesafari://claim/${testEnv.CLAIM_ID}`,
							 | 
						|
								                description: 'Claim view'
							 | 
						|
								            },
							 | 
						|
								            {
							 | 
						|
								            url: `timesafari://claim-cert/${testEnv.CERT_ID || testEnv.CLAIM_ID}`,
							 | 
						|
								                description: 'Claim certificate view'
							 | 
						|
								            },
							 | 
						|
								            {
							 | 
						|
								            url: `timesafari://claim-add-raw/${testEnv.RAW_CLAIM_ID || testEnv.CLAIM_ID}`,
							 | 
						|
								                description: 'Raw claim addition'
							 | 
						|
								            },
							 | 
						|
								            {
							 | 
						|
								                url: 'timesafari://did/test',
							 | 
						|
								                description: 'DID view with test identifier'
							 | 
						|
								            },
							 | 
						|
								            {
							 | 
						|
								                url: `timesafari://did/${testEnv.CONTACT1_DID}`,
							 | 
						|
								                description: 'DID view with contact DID'
							 | 
						|
								            },
							 | 
						|
								            {
							 | 
						|
								            url: (() => {
							 | 
						|
								                if (!testEnv?.CONTACT1_DID) {
							 | 
						|
								                    throw new Error('Cannot construct contact-edit URL: CONTACT1_DID is missing');
							 | 
						|
								                }
							 | 
						|
								                const url = `timesafari://contact-edit/${testEnv.CONTACT1_DID}`;
							 | 
						|
								                log('Created contact-edit URL:', url);
							 | 
						|
								                return url;
							 | 
						|
								            })(),
							 | 
						|
								                description: 'Contact editing'
							 | 
						|
								            },
							 | 
						|
								            {
							 | 
						|
								                url: `timesafari://contacts/import?contacts=${encodeURIComponent(JSON.stringify(contacts))}`,
							 | 
						|
								                description: 'Contacts import'
							 | 
						|
								            }
							 | 
						|
								        ];
							 | 
						|
								
							 | 
						|
								    // Log the final test configuration
							 | 
						|
								    log('\n5. Final Test Configuration:');
							 | 
						|
								    deeplinkTests.forEach((test, i) => {
							 | 
						|
								        log(`\nTest ${i + 1}:`);
							 | 
						|
								        log(`Description: ${test.description}`);
							 | 
						|
								        log(`URL: ${test.url}`);
							 | 
						|
								    });
							 | 
						|
								    
							 | 
						|
								    // Show instructions for iOS security dialogs
							 | 
						|
								    log('\n📱 IMPORTANT: iOS Security Dialog Instructions:');
							 | 
						|
								    log('1. Each deeplink test will trigger a security confirmation dialog');
							 | 
						|
								    log('2. You MUST click "Open" on each dialog to continue testing');
							 | 
						|
								    log('3. The app must be running in the FOREGROUND');
							 | 
						|
								    log('4. You will need to press Enter in this terminal after handling each dialog');
							 | 
						|
								    log('5. You can abort the testing process by pressing Ctrl+C\n');
							 | 
						|
								    
							 | 
						|
								    // Ensure app is in foreground
							 | 
						|
								    log('⚠️ IMPORTANT: Please make sure the app is in the FOREGROUND now');
							 | 
						|
								    await question('Press Enter when the app is visible and in the foreground...');
							 | 
						|
								    
							 | 
						|
								    try {
							 | 
						|
								        // Execute each test
							 | 
						|
								        let testsCompleted = 0;
							 | 
						|
								        let testsSkipped = 0;
							 | 
						|
								        
							 | 
						|
								        for (const test of deeplinkTests) {
							 | 
						|
								            // Show upcoming test info before execution
							 | 
						|
								            log('\n📱 NEXT TEST:');
							 | 
						|
								            log('------------------------');
							 | 
						|
								            log(`Description: ${test.description}`);
							 | 
						|
								            log(`URL to test: ${test.url}`);
							 | 
						|
								            log('------------------------');
							 | 
						|
								            
							 | 
						|
								            // Clear prompt for user action
							 | 
						|
								            await question('\n⏎  Press Enter to execute this test (or Ctrl+C to quit)...');
							 | 
						|
								            
							 | 
						|
								            try {
							 | 
						|
								                log('🚀 Executing deeplink test...');
							 | 
						|
								                log('⚠️ iOS SECURITY DIALOG WILL APPEAR - Click "Open" to continue');
							 | 
						|
								                
							 | 
						|
								                execSync(`xcrun simctl openurl booted "${test.url}"`, { stdio: 'pipe' });
							 | 
						|
								                log(`✅ Successfully executed: ${test.description}`);
							 | 
						|
								                testsCompleted++;
							 | 
						|
								                
							 | 
						|
								                // Show progress
							 | 
						|
								                log(`\n📊 Progress: ${testsCompleted}/${deeplinkTests.length} tests completed`);
							 | 
						|
								                
							 | 
						|
								                // If there are more tests, show the next one
							 | 
						|
								                if (testsCompleted < deeplinkTests.length) {
							 | 
						|
								                    const nextTest = deeplinkTests[testsCompleted];
							 | 
						|
								                    log('\n⏭️  NEXT UP:');
							 | 
						|
								                    log('------------------------');
							 | 
						|
								                    log(`Next test will be: ${nextTest.description}`);
							 | 
						|
								                    log(`URL: ${nextTest.url}`);
							 | 
						|
								                    log('------------------------');
							 | 
						|
								                    await question('\n⏎  Press Enter when ready for the next test...');
							 | 
						|
								                }
							 | 
						|
								            } catch (deeplinkError) {
							 | 
						|
								                const errorMessage = deeplinkError.message || '';
							 | 
						|
								                
							 | 
						|
								                // Handle specific error for URL scheme not registered
							 | 
						|
								                if (errorMessage.includes('OSStatus error -10814') || errorMessage.includes('NSOSStatusErrorDomain, code=-10814')) {
							 | 
						|
								                    log(`⚠️ URL scheme not properly handled: ${test.description}`);
							 | 
						|
								                    testsSkipped++;
							 | 
						|
								                } else {
							 | 
						|
								                    log(`⚠️ Failed to execute deeplink test: ${test.description}`);
							 | 
						|
								                    log(`⚠️ Error: ${errorMessage}`);
							 | 
						|
								                }
							 | 
						|
								                log('⚠️ Continuing with next test...');
							 | 
						|
								                
							 | 
						|
								                // Show next test info after error handling
							 | 
						|
								                if (testsCompleted + testsSkipped < deeplinkTests.length) {
							 | 
						|
								                    const nextTest = deeplinkTests[testsCompleted + testsSkipped];
							 | 
						|
								                    log('\n⏭️  NEXT UP:');
							 | 
						|
								                    log('------------------------');
							 | 
						|
								                    log(`Next test will be: ${nextTest.description}`);
							 | 
						|
								                    log(`URL: ${nextTest.url}`);
							 | 
						|
								                    log('------------------------');
							 | 
						|
								                    await question('\n⏎  Press Enter when ready for the next test...');
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        log('\n🎉 All deeplink tests completed!');
							 | 
						|
								        log(`✅ Successful: ${testsCompleted}`);
							 | 
						|
								        log(`⚠️ Skipped: ${testsSkipped}`);
							 | 
						|
								        
							 | 
						|
								        if (testsSkipped > 0) {
							 | 
						|
								            log('\n📝 Note about skipped tests:');
							 | 
						|
								            log('1. The app needs to have the URL scheme registered in Info.plist');
							 | 
						|
								            log('2. The app needs to be rebuilt after registering the URL scheme');
							 | 
						|
								            log('3. The app must be running in the foreground for deeplink tests to work');
							 | 
						|
								            log('4. iOS security dialogs must be manually approved for each deeplink test');
							 | 
						|
								            log('5. If these conditions are met and tests still fail, check URL handling in the app code');
							 | 
						|
								        }
							 | 
						|
								    } catch (error) {
							 | 
						|
								        log(`❌ Deeplink tests setup failed: ${error.message}`);
							 | 
						|
								        log('⚠️ Deeplink tests might be unavailable or test data is missing');
							 | 
						|
								    }
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								// Check and register URL scheme if needed
							 | 
						|
								const checkAndRegisterUrlScheme = (log) => {
							 | 
						|
								    log('🔍 Checking if URL scheme is registered in Info.plist...');
							 | 
						|
								    
							 | 
						|
								    const infoPlistPath = 'ios/App/App/Info.plist';
							 | 
						|
								    
							 | 
						|
								    // Check if Info.plist exists
							 | 
						|
								    if (!existsSync(infoPlistPath)) {
							 | 
						|
								        log('⚠️ Info.plist not found at: ' + infoPlistPath);
							 | 
						|
								        return false;
							 | 
						|
								    }
							 | 
						|
								    
							 | 
						|
								    // Read Info.plist content
							 | 
						|
								    const infoPlistContent = readFileSync(infoPlistPath, 'utf8');
							 | 
						|
								    
							 | 
						|
								    // Check if URL scheme is already registered
							 | 
						|
								    if (infoPlistContent.includes('<string>timesafari</string>')) {
							 | 
						|
								        log('✅ URL scheme "timesafari://" is already registered in Info.plist');
							 | 
						|
								        return true;
							 | 
						|
								    }
							 | 
						|
								    
							 | 
						|
								    log('⚠️ URL scheme "timesafari://" is not registered in Info.plist');
							 | 
						|
								    log('⚠️ Attempting to register the URL scheme automatically...');
							 | 
						|
								    
							 | 
						|
								    try {
							 | 
						|
								        // Look for the closing dict tag to insert our URL types
							 | 
						|
								        const closingDictIndex = infoPlistContent.lastIndexOf('</dict>');
							 | 
						|
								        if (closingDictIndex === -1) {
							 | 
						|
								            log('⚠️ Could not find closing dict tag in Info.plist');
							 | 
						|
								            return false;
							 | 
						|
								        }
							 | 
						|
								        
							 | 
						|
								        // Create URL types entry
							 | 
						|
								        const urlTypesEntry = `
							 | 
						|
									<key>CFBundleURLTypes</key>
							 | 
						|
									<array>
							 | 
						|
										<dict>
							 | 
						|
											<key>CFBundleURLName</key>
							 | 
						|
											<string>app.timesafari</string>
							 | 
						|
											<key>CFBundleURLSchemes</key>
							 | 
						|
											<array>
							 | 
						|
												<string>timesafari</string>
							 | 
						|
											</array>
							 | 
						|
										</dict>
							 | 
						|
									</array>`;
							 | 
						|
								        
							 | 
						|
								        // Insert URL types entry before closing dict
							 | 
						|
								        const updatedPlistContent = 
							 | 
						|
								            infoPlistContent.substring(0, closingDictIndex) + 
							 | 
						|
								            urlTypesEntry + 
							 | 
						|
								            infoPlistContent.substring(closingDictIndex);
							 | 
						|
								        
							 | 
						|
								        // Write updated content back to Info.plist
							 | 
						|
								        const { writeFileSync } = require('fs');
							 | 
						|
								        writeFileSync(infoPlistPath, updatedPlistContent, 'utf8');
							 | 
						|
								        
							 | 
						|
								        log('✅ URL scheme "timesafari://" registered in Info.plist');
							 | 
						|
								        log('⚠️ You will need to rebuild the app for changes to take effect');
							 | 
						|
								        return true;
							 | 
						|
								    } catch (error) {
							 | 
						|
								        log(`⚠️ Failed to register URL scheme: ${error.message}`);
							 | 
						|
								        return false;
							 | 
						|
								    }
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								// Helper function to get the app identifier from package.json or capacitor config
							 | 
						|
								const getAppIdentifier = () => {
							 | 
						|
								    try {
							 | 
						|
								        // Try to read from capacitor.config.ts/js/json
							 | 
						|
								        if (existsSync('capacitor.config.json')) {
							 | 
						|
								            const config = JSON.parse(readFileSync('capacitor.config.json', 'utf8'));
							 | 
						|
								            return config.appId;
							 | 
						|
								        } 
							 | 
						|
								        if (existsSync('capacitor.config.js')) {
							 | 
						|
								            // We can't directly require the file, but we can try to extract the appId
							 | 
						|
								            const content = readFileSync('capacitor.config.js', 'utf8');
							 | 
						|
								            const match = content.match(/appId:\s*['"]([^'"]+)['"]/);
							 | 
						|
								            if (match && match[1]) return match[1];
							 | 
						|
								        }
							 | 
						|
								        if (existsSync('capacitor.config.ts')) {
							 | 
						|
								            // Similar approach for TypeScript
							 | 
						|
								            const content = readFileSync('capacitor.config.ts', 'utf8');
							 | 
						|
								            const match = content.match(/appId:\s*['"]([^'"]+)['"]/);
							 | 
						|
								            if (match && match[1]) return match[1];
							 | 
						|
								        }
							 | 
						|
								        
							 | 
						|
								        // Fall back to package.json
							 | 
						|
								        const packageJson = JSON.parse(readFileSync('package.json', 'utf8'));
							 | 
						|
								        if (packageJson.capacitor && packageJson.capacitor.appId) {
							 | 
						|
								            return packageJson.capacitor.appId;
							 | 
						|
								        }
							 | 
						|
								        
							 | 
						|
								        // Default fallback
							 | 
						|
								        return 'app.timesafari';
							 | 
						|
								    } catch (error) {
							 | 
						|
								        console.error('Error getting app identifier:', error);
							 | 
						|
								        return 'app.timesafari'; // Default fallback
							 | 
						|
								    }
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Runs the complete iOS test suite including build and testing
							 | 
						|
								 * 
							 | 
						|
								 * The function performs the following steps:
							 | 
						|
								 * 1. Cleans and resets the iOS platform
							 | 
						|
								 * 2. Verifies prerequisites and project setup
							 | 
						|
								 * 3. Syncs the Capacitor project with latest web build
							 | 
						|
								 * 4. Builds the app using xcodebuild
							 | 
						|
								 * 5. Optionally runs tests if configured
							 | 
						|
								 * 6. Launches the app in the simulator
							 | 
						|
								 * 
							 | 
						|
								 * If no simulator is running, it automatically selects and boots one.
							 | 
						|
								 * 
							 | 
						|
								 * @async
							 | 
						|
								 * @throws {Error} If any step in the build process fails
							 | 
						|
								 * 
							 | 
						|
								 * @example
							 | 
						|
								 * runIosTests().catch(error => {
							 | 
						|
								 *     console.error('Test execution failed:', error);
							 | 
						|
								 *     process.exit(1);
							 | 
						|
								 * });
							 | 
						|
								 */
							 | 
						|
								async function runIosTests() {
							 | 
						|
								    // Create build_logs directory if it doesn't exist
							 | 
						|
								    if (!existsSync('build_logs')) {
							 | 
						|
								        mkdirSync('build_logs');
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    const logFile = getLogFileName();
							 | 
						|
								    const log = createLogger(logFile);
							 | 
						|
								
							 | 
						|
								    try {
							 | 
						|
								        log('🚀 Starting iOS build and test process...');
							 | 
						|
								
							 | 
						|
								        // Clean and reset iOS platform first
							 | 
						|
								        const cleanSuccess = await cleanIosPlatform(log);
							 | 
						|
								        if (!cleanSuccess) {
							 | 
						|
								            throw new Error('Failed to clean and reset iOS platform. Please check the logs for details.');
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        // Check prerequisites
							 | 
						|
								        await checkPrerequisites(log);
							 | 
						|
								
							 | 
						|
								        // Generate test data
							 | 
						|
								        await generateTestData(log);
							 | 
						|
								
							 | 
						|
								        // Verify Xcode installation
							 | 
						|
								        verifyXcodeInstallation(log);
							 | 
						|
								        
							 | 
						|
								        // Check for simulator or boot one if needed
							 | 
						|
								        const simulator = await checkSimulator(log);
							 | 
						|
								        
							 | 
						|
								        // Configure iOS project
							 | 
						|
								        await configureIosProject(log);
							 | 
						|
								        
							 | 
						|
								        // Build and test using the selected simulator
							 | 
						|
								        await buildAndTestIos(log, simulator);
							 | 
						|
								        
							 | 
						|
								        // Run the app in the simulator
							 | 
						|
								        await runIosApp(log, simulator);
							 | 
						|
								
							 | 
						|
								        // Run deeplink tests after app is installed
							 | 
						|
								        await runDeeplinkTests(log);
							 | 
						|
								
							 | 
						|
								        log('🎉 iOS build and test process completed successfully');
							 | 
						|
								        log(`📝 Full build log available at: ${logFile}`);
							 | 
						|
								    } catch (error) {
							 | 
						|
								        log(`❌ iOS tests failed: ${error.message}`);
							 | 
						|
								        log(`📝 Check build log for details: ${logFile}`);
							 | 
						|
								        process.exit(1);
							 | 
						|
								    }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Execute the test suite
							 | 
						|
								runIosTests(); 
							 |