1/** 2 * Copyright (c) Meta Platforms, Inc. and affiliates. 3 * 4 * This source code is licensed under the MIT license found in the 5 * LICENSE file in the root directory of this source tree. 6 * 7 * @format 8 */ 9 10'use strict'; 11 12/* 13 * This script is a re-interpretation of the old test-manual.e2e.sh script. 14 * the idea is to provide a better DX for the manual testing. 15 * It's using Javascript over Bash for consistency with the rest of the recent scripts 16 * and to make it more accessible for other devs to play around with. 17 */ 18 19const {exec, exit, pushd, popd, pwd, cd, cp} = require('shelljs'); 20const yargs = require('yargs'); 21const fs = require('fs'); 22const path = require('path'); 23const os = require('os'); 24 25const { 26 launchAndroidEmulator, 27 isPackagerRunning, 28 launchPackagerInSeparateWindow, 29} = require('./testing-utils'); 30 31const { 32 generateAndroidArtifacts, 33 saveFilesToRestore, 34 generateiOSArtifacts, 35} = require('./release-utils'); 36 37const { 38 downloadHermesSourceTarball, 39 expandHermesSourceTarball, 40} = require('./hermes/hermes-utils'); 41 42const argv = yargs 43 .option('t', { 44 alias: 'target', 45 default: 'RNTester', 46 choices: ['RNTester', 'RNTestProject'], 47 }) 48 .option('p', { 49 alias: 'platform', 50 default: 'iOS', 51 choices: ['iOS', 'Android'], 52 }) 53 .option('h', { 54 alias: 'hermes', 55 type: 'boolean', 56 default: true, 57 }).argv; 58 59/* 60 * see the test-local-e2e.js script for clean up process 61 */ 62 63// command order: we ask the user to select if they want to test RN tester 64// or RNTestProject 65 66// if they select RN tester, we ask if iOS or Android, and then we run the tests 67// if they select RNTestProject, we run the RNTestProject test 68 69// let's check if Metro is already running, if it is let's kill it and start fresh 70if (isPackagerRunning() === 'running') { 71 exec("lsof -i :8081 | grep LISTEN | /usr/bin/awk '{print $2}' | xargs kill"); 72} 73 74const onReleaseBranch = exec('git rev-parse --abbrev-ref HEAD', { 75 silent: true, 76}) 77 .stdout.trim() 78 .endsWith('-stable'); 79 80if (argv.target === 'RNTester') { 81 // FIXME: make sure that the commands retains colors 82 // (--ansi) doesn't always work 83 // see also https://github.com/shelljs/shelljs/issues/86 84 85 if (argv.platform === 'iOS') { 86 console.info( 87 `We're going to test the ${ 88 argv.hermes ? 'Hermes' : 'JSC' 89 } version of RNTester iOS with the new Architecture enabled`, 90 ); 91 92 // remember that for this to be successful 93 // you should have run bundle install once 94 // in your local setup - also: if I'm on release branch, I pick the 95 // hermes ref from the hermes ref file (see hermes-engine.podspec) 96 exec( 97 `cd packages/rn-tester && USE_HERMES=${ 98 argv.hermes ? 1 : 0 99 } REACT_NATIVE_CI=${onReleaseBranch} RCT_NEW_ARCH_ENABLED=1 bundle exec pod install --ansi`, 100 ); 101 102 // if everything succeeded so far, we can launch Metro and the app 103 // start the Metro server in a separate window 104 launchPackagerInSeparateWindow(); 105 106 // launch the app on iOS simulator 107 pushd('packages/rn-tester'); 108 exec('npx react-native run-ios --scheme RNTester'); 109 popd(); 110 } else { 111 // we do the android path here 112 113 launchAndroidEmulator(); 114 115 console.info( 116 `We're going to test the ${ 117 argv.hermes ? 'Hermes' : 'JSC' 118 } version of RNTester Android with the new Architecture enabled`, 119 ); 120 exec( 121 `./gradlew :packages:rn-tester:android:app:${ 122 argv.hermes ? 'installHermesDebug' : 'installJscDebug' 123 } --quiet`, 124 ); 125 126 // launch the app on Android simulator 127 // TODO: we should find a way to make it work like for iOS, via npx react-native run-android 128 // currently, that fails with an error. 129 130 // if everything succeeded so far, we can launch Metro and the app 131 // start the Metro server in a separate window 132 launchPackagerInSeparateWindow(); 133 134 // launch the app 135 exec( 136 'adb shell am start -n com.facebook.react.uiapp/com.facebook.react.uiapp.RNTesterActivity', 137 ); 138 139 // just to make sure that the Android up won't have troubles finding the Metro server 140 exec('adb reverse tcp:8081 tcp:8081'); 141 } 142} else { 143 console.info("We're going to test a fresh new RN project"); 144 145 // create the local npm package to feed the CLI 146 147 // base setup required (specular to publish-npm.js) 148 const tmpPublishingFolder = fs.mkdtempSync( 149 path.join(os.tmpdir(), 'rn-publish-'), 150 ); 151 console.info(`The temp publishing folder is ${tmpPublishingFolder}`); 152 153 saveFilesToRestore(tmpPublishingFolder); 154 155 // we need to add the unique timestamp to avoid npm/yarn to use some local caches 156 const baseVersion = require('../package.json').version; 157 158 // in local testing, 1000.0.0 mean we are on main, every other case means we are 159 // working on a release version 160 const buildType = baseVersion !== '1000.0.0' ? 'release' : 'dry-run'; 161 162 const dateIdentifier = new Date() 163 .toISOString() 164 .slice(0, -8) 165 .replace(/[-:]/g, '') 166 .replace(/[T]/g, '-'); 167 168 const releaseVersion = `${baseVersion}-${dateIdentifier}`; 169 170 // this is needed to generate the Android artifacts correctly 171 const exitCode = exec( 172 `node scripts/set-rn-version.js --to-version ${releaseVersion} --build-type ${buildType}`, 173 ).code; 174 175 if (exitCode !== 0) { 176 console.error( 177 `Failed to set the RN version. Version ${releaseVersion} is not valid for ${buildType}`, 178 ); 179 process.exit(exitCode); 180 } 181 182 // Generate native files for Android 183 generateAndroidArtifacts(releaseVersion, tmpPublishingFolder); 184 185 // Setting up generating native iOS (will be done later) 186 const repoRoot = pwd(); 187 const jsiFolder = `${repoRoot}/ReactCommon/jsi`; 188 const hermesCoreSourceFolder = `${repoRoot}/sdks/hermes`; 189 190 if (!fs.existsSync(hermesCoreSourceFolder)) { 191 console.info('The Hermes source folder is missing. Downloading...'); 192 downloadHermesSourceTarball(); 193 expandHermesSourceTarball(); 194 } 195 196 // need to move the scripts inside the local hermes cloned folder 197 // cp sdks/hermes-engine/utils/*.sh <your_hermes_checkout>/utils/. 198 cp( 199 `${repoRoot}/sdks/hermes-engine/utils/*.sh`, 200 `${repoRoot}/sdks/hermes/utils/.`, 201 ); 202 203 // for this scenario, we only need to create the debug build 204 // (env variable PRODUCTION defines that podspec side) 205 const buildTypeiOSArtifacts = 'Debug'; 206 207 // the android ones get set into /private/tmp/maven-local 208 const localMavenPath = '/private/tmp/maven-local'; 209 210 // Generate native files for iOS 211 const tarballOutputPath = generateiOSArtifacts( 212 jsiFolder, 213 hermesCoreSourceFolder, 214 buildTypeiOSArtifacts, 215 localMavenPath, 216 ); 217 218 // create locally the node module 219 exec('npm pack'); 220 221 const localNodeTGZPath = `${repoRoot}/react-native-${releaseVersion}.tgz`; 222 exec(`node scripts/set-rn-template-version.js "file:${localNodeTGZPath}"`); 223 224 pushd('/tmp/'); 225 // need to avoid the pod install step - we'll do it later 226 exec( 227 `node ${repoRoot}/cli.js init RNTestProject --template ${repoRoot} --skip-install`, 228 ); 229 230 cd('RNTestProject'); 231 exec('yarn install'); 232 233 // need to do this here so that Android will be properly setup either way 234 exec( 235 'echo "REACT_NATIVE_MAVEN_LOCAL_REPO=/private/tmp/maven-local" >> android/gradle.properties', 236 ); 237 238 // doing the pod install here so that it's easier to play around RNTestProject 239 cd('ios'); 240 exec('bundle install'); 241 exec( 242 `HERMES_ENGINE_TARBALL_PATH=${tarballOutputPath} USE_HERMES=${ 243 argv.hermes ? 1 : 0 244 } bundle exec pod install --ansi`, 245 ); 246 247 cd('..'); 248 249 if (argv.platform === 'iOS') { 250 exec('yarn ios'); 251 } else { 252 // android 253 exec('yarn android'); 254 } 255 popd(); 256 257 // just cleaning up the temp folder, the rest is done by the test clean script 258 exec(`rm -rf ${tmpPublishingFolder}`); 259} 260 261exit(0); 262