1import spawnAsync from '@expo/spawn-async'; 2import fs from 'fs-extra'; 3import path from 'path'; 4 5import * as Directories from '../Directories'; 6import * as Packages from '../Packages'; 7 8export async function iosNativeUnitTests() { 9 const packages = await Packages.getListOfPackagesAsync(); 10 let packagesTested: string[] = []; 11 let errors: any[] = []; 12 for (const pkg of packages) { 13 if (!(await pkg.hasNativeTestsAsync('ios'))) { 14 continue; 15 } 16 17 try { 18 await spawnAsync('fastlane', ['prepare_schemes', `pod:${pkg.podspecName}`], { 19 cwd: Directories.getExpoRepositoryRootDir(), 20 stdio: 'inherit', 21 }); 22 23 // make schemes shared by moving them from xcodeproj/xcuserdata/runner.xcuserdatad/xcschemes 24 // to xcodeproj/xcshareddata/xcschemes 25 // otherwise they aren't visible to fastlane 26 const xcodeprojDir = path.join( 27 Directories.getIosDir(), 28 'Pods', 29 `${pkg.podspecName}.xcodeproj` 30 ); 31 const destinationDir = path.join(xcodeprojDir, 'xcshareddata', 'xcschemes'); 32 await fs.mkdirp(destinationDir); 33 34 // find user directory name, should be runner.xcuserdatad but depends on the OS username 35 const xcuserdataDirName = (await fs.readdir(path.join(xcodeprojDir, 'xcuserdata')))[0]; 36 37 const xcschemesDir = path.join(xcodeprojDir, 'xcuserdata', xcuserdataDirName, 'xcschemes'); 38 const xcschemesFiles = (await fs.readdir(xcschemesDir)).filter((file) => 39 file.endsWith('.xcscheme') 40 ); 41 if (!xcschemesFiles.length) { 42 throw new Error(`No scheme could be found to run tests for ${pkg.podspecName}`); 43 } 44 for (const file of xcschemesFiles) { 45 await fs.move(path.join(xcschemesDir, file), path.join(destinationDir, file), { 46 overwrite: true, 47 }); 48 } 49 50 await spawnAsync('fastlane', ['test_module', `pod:${pkg.podspecName}`], { 51 cwd: Directories.getExpoRepositoryRootDir(), 52 stdio: 'inherit', 53 }); 54 55 packagesTested.push(pkg.packageName); 56 } catch (error) { 57 errors.push(error); 58 } 59 } 60 if (errors.length) { 61 console.error('One or more iOS unit tests failed:'); 62 for (const error of errors) { 63 console.error(error.message); 64 console.error('stdout >', error.stdout); 65 console.error('stderr >', error.stderr); 66 if (error.message.startsWith('fastlane exited')) { 67 console.warn( 68 "Did you add unit tests to a package that didn't have unit tests before? If so, make sure to add the correct subspec to ios/Podfile." 69 ); 70 } 71 } 72 throw new Error('Unit tests failed'); 73 } else { 74 console.log('✅ All unit tests passed for the following packages:', packagesTested.join(', ')); 75 } 76} 77 78export default (program: any) => { 79 program 80 .command('ios-native-unit-tests') 81 .description('Runs iOS native unit tests for each package that provides them.') 82 .asyncAction(iosNativeUnitTests); 83}; 84