forked from trent_larson/crowd-funder-for-time-pwa
feat: Add comprehensive iOS test runner with deeplink testing
- Add test-ios.js script for iOS build and test automation - Add simulator detection and validation - Add CocoaPods dependency handling - Add iOS deeplink testing with xcrun simctl - Add detailed build logging for iOS tests The changes improve iOS testing by: 1. Automating iOS build and test process 2. Adding structured logging to build_logs directory 3. Supporting deeplink testing in iOS simulator 4. Validating iOS development environment 5. Matching Android test capabilities Technical details: - Uses xcodebuild for building and testing - Handles simulator device detection - Supports all deeplink test cases - Provides detailed error reporting - Maintains test logs for debugging
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
*
|
||||
* @requires child_process
|
||||
* @requires path
|
||||
* @requires fs
|
||||
*
|
||||
* @author TimeSafari Team
|
||||
* @license MIT
|
||||
@@ -37,6 +38,166 @@
|
||||
|
||||
const { execSync } = require('child_process');
|
||||
const { join } = require('path');
|
||||
const { existsSync, mkdirSync, appendFileSync, readFileSync } = require('fs');
|
||||
|
||||
// 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);
|
||||
};
|
||||
};
|
||||
|
||||
// Check for iOS simulator
|
||||
const checkSimulator = async (log) => {
|
||||
log('🔍 Checking for iOS simulator...');
|
||||
const simulators = execSync('xcrun simctl list devices available').toString();
|
||||
const bootedDevices = simulators.split('\n')
|
||||
.filter(line => line.includes('Booted'))
|
||||
.map(line => line.match(/(.*?)\s+\((.*?)\)/)?.[1])
|
||||
.filter(Boolean);
|
||||
|
||||
if (bootedDevices.length === 0) {
|
||||
throw new Error('No iOS simulator running. Please start a simulator first.');
|
||||
}
|
||||
|
||||
log(`📱 Found ${bootedDevices.length} simulator(s): ${bootedDevices.join(', ')}`);
|
||||
return bootedDevices;
|
||||
};
|
||||
|
||||
// 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('🔄 Generating test data...');
|
||||
try {
|
||||
execSync('npx ts-node test-scripts/generate_data.ts', { stdio: 'inherit' });
|
||||
log('✅ Test data generated successfully');
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to generate test data: ${error.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
// 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('📱 Syncing Capacitor project...');
|
||||
execSync('npx cap sync ios', { stdio: 'inherit' });
|
||||
log('✅ Capacitor sync completed');
|
||||
|
||||
log('⚙️ Installing CocoaPods dependencies...');
|
||||
execSync('cd ios/App && pod install', { stdio: 'inherit' });
|
||||
log('✅ CocoaPods installation completed');
|
||||
};
|
||||
|
||||
// Build and test iOS project
|
||||
const buildAndTestIos = async (log) => {
|
||||
log('🏗️ Building iOS project...');
|
||||
execSync('cd ios/App && xcodebuild clean -workspace App.xcworkspace -scheme App', { stdio: 'inherit' });
|
||||
log('✅ Xcode clean completed');
|
||||
|
||||
execSync('cd ios/App && xcodebuild build-for-testing -workspace App.xcworkspace -scheme App -destination "platform=iOS Simulator,name=iPhone 14"', { stdio: 'inherit' });
|
||||
log('✅ Xcode build completed');
|
||||
|
||||
log('🧪 Running iOS tests...');
|
||||
execSync('cd ios/App && xcodebuild test-without-building -workspace App.xcworkspace -scheme App -destination "platform=iOS Simulator,name=iPhone 14"', { stdio: 'inherit' });
|
||||
log('✅ iOS tests completed');
|
||||
};
|
||||
|
||||
// Run the app
|
||||
const runIosApp = async (log) => {
|
||||
log('📱 Running app in simulator...');
|
||||
execSync('npx cap run ios', { stdio: 'inherit' });
|
||||
log('✅ App launched successfully');
|
||||
};
|
||||
|
||||
// Run deeplink tests
|
||||
const runDeeplinkTests = async (log) => {
|
||||
log('🔗 Starting deeplink tests...');
|
||||
|
||||
try {
|
||||
// Load test data
|
||||
const testEnv = JSON.parse(readFileSync('.generated/test-env.json', 'utf8'));
|
||||
const claimDetails = JSON.parse(readFileSync('.generated/claim_details.json', 'utf8'));
|
||||
const contacts = JSON.parse(readFileSync('.generated/contacts.json', 'utf8'));
|
||||
|
||||
// Test URLs
|
||||
const deeplinkTests = [
|
||||
{
|
||||
url: `timesafari://claim/${claimDetails.claim_id}`,
|
||||
description: 'Claim view'
|
||||
},
|
||||
{
|
||||
url: `timesafari://claim-cert/${claimDetails.claim_id}`,
|
||||
description: 'Claim certificate view'
|
||||
},
|
||||
{
|
||||
url: `timesafari://claim-add-raw/${claimDetails.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: `timesafari://contact-edit/${testEnv.CONTACT1_DID}`,
|
||||
description: 'Contact editing'
|
||||
},
|
||||
{
|
||||
url: `timesafari://contacts/import?contacts=${encodeURIComponent(JSON.stringify(contacts))}`,
|
||||
description: 'Contacts import'
|
||||
}
|
||||
];
|
||||
|
||||
// Execute each test
|
||||
for (const test of deeplinkTests) {
|
||||
log(`\n🔗 Testing deeplink: ${test.description}`);
|
||||
log(`URL: ${test.url}`);
|
||||
|
||||
execSync(`xcrun simctl openurl booted "${test.url}"`);
|
||||
log(`✅ Successfully executed: ${test.description}`);
|
||||
|
||||
// Wait between tests
|
||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
}
|
||||
|
||||
log('✅ All deeplink tests completed successfully');
|
||||
} catch (error) {
|
||||
log('❌ Deeplink tests failed');
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Runs the complete iOS test suite including build and testing
|
||||
@@ -58,33 +219,35 @@ const { join } = require('path');
|
||||
* });
|
||||
*/
|
||||
async function runIosTests() {
|
||||
try {
|
||||
// Sync Capacitor project with latest web build
|
||||
// This ensures the iOS project has the latest web assets
|
||||
execSync('npx cap sync ios', {
|
||||
stdio: 'inherit',
|
||||
// Inherit stdio to show real-time output
|
||||
});
|
||||
|
||||
// Build and run tests using xcodebuild
|
||||
execSync(
|
||||
'cd ios && xcodebuild test ' +
|
||||
'-workspace App/App.xcworkspace ' + // Workspace containing the project
|
||||
'-scheme App ' + // The scheme to build and test
|
||||
'-destination "platform=iOS Simulator,name=iPhone 14"', // Target simulator
|
||||
{
|
||||
stdio: 'inherit',
|
||||
// test: Builds and runs tests
|
||||
// -workspace: Specifies the Xcode workspace
|
||||
// -scheme: Specifies the scheme to test
|
||||
// -destination: Specifies the target simulator
|
||||
}
|
||||
);
|
||||
// Create build_logs directory if it doesn't exist
|
||||
if (!existsSync('build_logs')) {
|
||||
mkdirSync('build_logs');
|
||||
}
|
||||
|
||||
console.log('✅ iOS tests completed successfully');
|
||||
const logFile = getLogFileName();
|
||||
const log = createLogger(logFile);
|
||||
|
||||
try {
|
||||
log('🚀 Starting iOS build and test process...');
|
||||
|
||||
// Generate test data first
|
||||
await generateTestData(log);
|
||||
|
||||
await checkSimulator(log);
|
||||
verifyXcodeInstallation(log);
|
||||
await buildWebAssets(log);
|
||||
await configureIosProject(log);
|
||||
await buildAndTestIos(log);
|
||||
await runIosApp(log);
|
||||
|
||||
// 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 the error and exit with failure code
|
||||
console.error('❌ iOS tests failed:', error);
|
||||
log(`❌ iOS tests failed: ${error.message}`);
|
||||
log(`📝 Check build log for details: ${logFile}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user