@ -40,7 +40,21 @@
const { execSync } = require ( 'child_process' ) ;
const { join } = require ( 'path' ) ;
const { existsSync , mkdirSync , appendFileSync , readFileSync , writeFileSync } = require ( 'fs' ) ;
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 = ( ) => {
@ -316,122 +330,84 @@ const verifyXcodeInstallation = (log) => {
// Generate test data using generate_data.ts
const generateTestData = async ( log ) => {
log ( '🔄 Generating test data ...' ) ;
log ( '\n🔍 DEBUG: Starting test data generation ...' ) ;
// Check if test-scripts directory exists
if ( ! existsSync ( 'test-scripts' ) ) {
log ( '⚠️ test-scripts directory not found' ) ;
log ( '⚠️ Current directory: ' + process . cwd ( ) ) ;
// List directories to help debug
const { readdirSync } = require ( 'fs' ) ;
log ( '📂 Directories in current path:' ) ;
try {
const files = readdirSync ( '.' ) ;
files . forEach ( file => {
const isDir = existsSync ( file ) && require ( 'fs' ) . statSync ( file ) . isDirectory ( ) ;
log ( ` ${ isDir ? '📁' : '📄' } ${ file } ` ) ;
} ) ;
} catch ( err ) {
log ( ` ⚠️ Error listing directory: ${ err . message } ` ) ;
}
} else {
log ( '✅ Found test-scripts directory' ) ;
// Check if generate_data.ts exists
if ( existsSync ( 'test-scripts/generate_data.ts' ) ) {
log ( '✅ Found generate_data.ts' ) ;
} else {
log ( '⚠️ generate_data.ts not found in test-scripts directory' ) ;
// List files in test-scripts to help debug
const { readdirSync } = require ( 'fs' ) ;
log ( '📂 Files in test-scripts:' ) ;
try {
const files = readdirSync ( 'test-scripts' ) ;
files . forEach ( file => {
log ( ` 📄 ${ file } ` ) ;
} ) ;
} catch ( err ) {
log ( ` ⚠️ Error listing test-scripts: ${ err . message } ` ) ;
}
}
}
// Check directory structure
log ( '📁 Current directory:' , process . cwd ( ) ) ;
log ( '📁 Directory contents:' , require ( 'fs' ) . readdirSync ( '.' ) ) ;
// Create .generated directory if it doesn't exist
if ( ! existsSync ( '.generated' ) ) {
log ( '📁 Creating .generated directory' ) ;
mkdirSync ( '.generated' , { recursive : true } ) ;
}
try {
// Try to generate test data using the script
log ( '🔄 Running test data generation script...' ) ;
log ( '🔄 Attempting to run generate_data.ts...' ) ;
execSync ( 'npx ts-node test-scripts/generate_data.ts' , { stdio : 'inherit' } ) ;
log ( '✅ Test data generation script completed' ) ;
log ( '✅ Test data generation completed' ) ;
// Verify the generated files exis t
// Verify and log generated files content
const requiredFiles = [
'.generated/test-env.json' ,
'.generated/claim_details.json' ,
'.generated/contacts.json'
] ;
log ( '🔍 Verifying generated files:' ) ;
log ( '\n📝 Verifying generated files:' ) ;
for ( const file of requiredFiles ) {
if ( ! existsSync ( file ) ) {
log ( ` ⚠️ Required file ${ file } was not generated ` ) ;
throw new Error ( ` Required file ${ file } was not generated ` ) ;
log ( ` ❌ Missing file: ${ file } ` ) ;
} else {
log ( ` ✅ ${ file } exists ` ) ;
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 ( ` ⚠️ Failed to generate test data: ${ error . message } ` ) ;
log ( ` \n ⚠️ Test data generation failed : ${ error . message } ` ) ;
log ( '⚠️ Creating fallback test data...' ) ;
// Create minimal fallback test data
// Create fallback data with detailed logging
const fallbackTestEnv = {
"CONTACT1_DID" : "did:example:123456789" ,
"CONTACT1_DID" : "did:ethr:0x35A71Ac3fA0A4D5a4903f10F0f7A3ac4034FaB5B " ,
"APP_URL" : "https://app.timesafari.example"
} ;
const fallbackClaimDetails = {
"claim_id" : "claim_12345" ,
"title" : "Test Claim" ,
"description" : "This is a test claim"
} ;
const fallbackContacts = [
{
"id" : "contact1" ,
"name" : "Test Contact" ,
"did" : "did:example:123456789 "
"did" : "did:ethr:0x35A71Ac3fA0A4D5a4903f10F0f7A3ac4034FaB5B"
}
] ;
// Use writeFileSync to overwrite any existing files
const { writeFileSync } = require ( 'fs' ) ;
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/claim_details.json' , JSON . stringify ( fallbackClaimDetails , null , 2 ) ) ;
writeFileSync ( '.generated/contacts.json' , JSON . stringify ( fallbackContacts , null , 2 ) ) ;
log ( '✅ Fallback test data created' ) ;
// Verify files were created
const requiredFiles = [
'.generated/test-env.json' ,
'.generated/claim_details.json' ,
'.generated/contacts.json'
] ;
log ( '🔍 Verifying fallback files:' ) ;
for ( const file of requiredFiles ) {
if ( ! existsSync ( file ) ) {
log ( ` ⚠️ Failed to create ${ file } ` ) ;
} else {
log ( ` ✅ Created ${ file } ` ) ;
}
// 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 ) ;
}
}
} ;
@ -535,6 +511,96 @@ const runIosApp = async (log, simulator) => {
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
@ -543,76 +609,65 @@ const runIosApp = async (log, simulator) => {
* @ returns { Promise < void > }
* /
const runDeeplinkTests = async ( log ) => {
log ( '🔗 Starting deeplink tests...' ) ;
// Import readline module for user input
const readline = require ( 'readline' ) ;
// Create readline interface
const rl = readline . createInterface ( {
input : process . stdin ,
output : process . stdout
} ) ;
// Promisify the question method
const question = ( query ) => new Promise ( resolve => rl . question ( query , resolve ) ) ;
// Register URL scheme if needed
checkAndRegisterUrlScheme ( log ) ;
// Check if test data files exist first
const requiredFiles = [
'.generated/test-env.json' ,
'.generated/claim_details.json' ,
'.generated/contacts.json'
] ;
log ( '\n=== Starting Deeplink Tests ===' ) ;
for ( const file of requiredFiles ) {
if ( ! existsSync ( file ) ) {
log ( ` ⚠️ Required file ${ file } does not exist ` ) ;
log ( '⚠️ Skipping deeplink tests' ) ;
rl . close ( ) ;
return ;
}
// 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
}
// Check if our app is actually running in the simulator
log ( '🔍 Checking if app is currently running in simulator...' ) ;
try {
const runningApps = execSync ( 'xcrun simctl listapps booted' ) . toString ( ) ;
const appIdentifier = getAppIdentifier ( ) ;
if ( ! runningApps . includes ( appIdentifier ) ) {
log ( '⚠️ The app does not appear to be running in the simulator.' ) ;
const shouldLaunch = await question ( 'Would you like to launch the app now? (y/n): ' ) ;
if ( shouldLaunch . toLowerCase ( ) === 'y' || shouldLaunch . toLowerCase ( ) === 'yes' ) {
// Try launching the app again
log ( '🚀 Launching app in simulator...' ) ;
const simulatorInfo = JSON . parse ( execSync ( 'xcrun simctl list -j devices booted' ) . toString ( ) ) ;
const booted = Object . values ( simulatorInfo . devices )
. flat ( )
. find ( device => device . state === 'Booted' ) ;
if ( booted ) {
execSync ( ` npx cap run ios --target=" ${ booted . udid } " ` , { stdio : 'inherit' } ) ;
log ( '✅ App launched' ) ;
} else {
log ( '⚠️ No booted simulator found' ) ;
// 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' ) ;
}
} else {
log ( '⚠️ Deeplink tests require the app to be running' ) ;
log ( '⚠️ Please launch the app manually and restart the tests' ) ;
rl . close ( ) ;
return ;
}
} else {
log ( '✅ App is running in simulator' ) ;
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'
}
} catch ( error ) {
log ( ` ⚠️ Unable to check if app is running: ${ error . message } ` ) ;
log ( '⚠️ Proceeding with deeplink tests, but they may fail if app is not running' ) ;
}
] ;
// Log the final test configuration
log ( '\n5. Final Test Configuration:' ) ;
deeplinkTests . forEach ( ( test , i ) => {
log ( ` \n Test ${ i + 1 } : ` ) ;
log ( ` Description: ${ test . description } ` ) ;
log ( ` URL: ${ test . url } ` ) ;
} ) ;
// Show instructions for iOS security dialogs
log ( '\n📱 IMPORTANT: iOS Security Dialog Instructions:' ) ;
@ -627,110 +682,41 @@ const runDeeplinkTests = async (log) => {
await question ( 'Press Enter when the app is visible and in the foreground...' ) ;
try {
// Load test data
log ( '📂 Loading test data from .generated directory' ) ;
let testEnv , claimDetails , contacts ;
try {
const testEnvContent = readFileSync ( '.generated/test-env.json' , 'utf8' ) ;
testEnv = JSON . parse ( testEnvContent ) ;
log ( '✅ Loaded test-env.json' ) ;
} catch ( error ) {
log ( ` ⚠️ Failed to load test-env.json: ${ error . message } ` ) ;
rl . close ( ) ;
return ;
}
try {
const claimDetailsContent = readFileSync ( '.generated/claim_details.json' , 'utf8' ) ;
claimDetails = JSON . parse ( claimDetailsContent ) ;
log ( '✅ Loaded claim_details.json' ) ;
} catch ( error ) {
log ( ` ⚠️ Failed to load claim_details.json: ${ error . message } ` ) ;
rl . close ( ) ;
return ;
}
try {
const contactsContent = readFileSync ( '.generated/contacts.json' , 'utf8' ) ;
contacts = JSON . parse ( contactsContent ) ;
log ( '✅ Loaded contacts.json' ) ;
} catch ( error ) {
log ( ` ⚠️ Failed to load contacts.json: ${ error . message } ` ) ;
rl . close ( ) ;
return ;
}
// Check if the app URL scheme is registered in the simulator
log ( '🔍 Checking if URL scheme is registered in simulator...' ) ;
try {
// Attempt to open a simple URL with the scheme
log ( '⚠️ A security dialog will appear - Click "Open" to continue' ) ;
execSync ( ` xcrun simctl openurl booted "timesafari://test" ` , { stdio : 'pipe' } ) ;
log ( '✅ URL scheme is registered and working' ) ;
} catch ( error ) {
const errorMessage = error . message || '' ;
// Check for the specific error code that indicates an unregistered URL scheme
if ( errorMessage . includes ( 'OSStatus error -10814' ) || errorMessage . includes ( 'NSOSStatusErrorDomain, code=-10814' ) ) {
log ( '⚠️ URL scheme "timesafari://" is not registered in the app or app is not running' ) ;
log ( '⚠️ The scheme was added to Info.plist but the app may need to be rebuilt' ) ;
log ( '⚠️ Trying to continue with tests, but they may fail' ) ;
}
}
// Wait for user confirmation before proceeding
await question ( 'Press Enter to continue with the tests...' ) ;
// 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
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 ( ` \n 🔗 Testing deeplink: ${ test . description } ` ) ;
log ( ` URL: ${ test . url } ` ) ;
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 ++ ;
// Wait for user to press Enter before continuing to next test
// Show progress
log ( ` \n 📊 Progress: ${ testsCompleted } / ${ deeplinkTests . length } tests completed ` ) ;
// If there are more tests, show the next one
if ( testsCompleted < deeplinkTests . length ) {
await question ( 'Press Enter to continue to the next test...' ) ;
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 || '' ;
@ -745,14 +731,22 @@ const runDeeplinkTests = async (log) => {
}
log ( '⚠️ Continuing with next test...' ) ;
// Wait for user to press Enter before continuing to next test
// Show next test info after error handling
if ( testsCompleted + testsSkipped < deeplinkTests . length ) {
await question ( 'Press Enter to continue to the next test...' ) ;
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 ( ` ✅ Deeplink tests completed: ${ testsCompleted } successful, ${ testsSkipped } skipped ` ) ;
log ( '\n🎉 All deeplink tests completed!' ) ;
log ( ` ✅ Successful: ${ testsCompleted } ` ) ;
log ( ` ⚠️ Skipped: ${ testsSkipped } ` ) ;
if ( testsSkipped > 0 ) {
log ( '\n📝 Note about skipped tests:' ) ;
@ -762,15 +756,9 @@ const runDeeplinkTests = async (log) => {
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' ) ;
}
// Close readline interface
rl . close ( ) ;
} catch ( error ) {
log ( ` ❌ Deeplink tests setup failed: ${ error . message } ` ) ;
log ( '⚠️ Deeplink tests might be unavailable or test data is missing' ) ;
// Close readline interface
rl . close ( ) ;
// Don't rethrow the error to prevent halting the process
}
} ;