11#!/usr/bin/env node
22'use strict' ;
33
4- import { execSync , spawn } from 'child_process' ;
4+ import { execSync , execFileSync , spawn } from 'child_process' ;
55import * as fs from 'fs' ;
66import * as path from 'path' ;
77import { argv , env } from 'process' ;
@@ -64,8 +64,9 @@ const appRepoDir = `${e2eDir}/react-native-versions/${RNVersion}`;
6464const appName = 'RnDiffApp' ;
6565const appDir = `${ appRepoDir } /${ appName } ` ;
6666const testAppName = `${ appName } .${ platform == 'ios' ? 'app' : 'apk' } ` ;
67- const runtime = env . IOS_RUNTIME ? env . IOS_RUNTIME : 'latest' ;
68- const device = env . IOS_DEVICE ? env . IOS_DEVICE : 'iPhone 15' ;
67+ const testApp = `${ e2eDir } /${ testAppName } ` ;
68+ const appId = platform === 'ios' ? 'org.reactjs.native.example.RnDiffApp' : 'com.rndiffapp' ;
69+ const sentryAuthToken = env . SENTRY_AUTH_TOKEN ;
6970
7071// Build and publish the SDK - we only need to do this once in CI.
7172// Locally, we may want to get updates from the latest build so do it on every app build.
@@ -112,9 +113,12 @@ if (actions.includes('create')) {
112113 env : Object . assign ( env , { YARN_ENABLE_IMMUTABLE_INSTALLS : false } ) ,
113114 } ) ;
114115
115- console . log ( `done` ) ;
116-
117- console . log ( `done2` ) ;
116+ execSync ( `yarn add [email protected] ` , { 117+ stdio : 'inherit' ,
118+ cwd : appDir ,
119+ // yarn v3 run immutable install by default in CI
120+ env : Object . assign ( env , { YARN_ENABLE_IMMUTABLE_INSTALLS : false } ) ,
121+ } ) ;
118122
119123 // Patch the app
120124 execSync ( `patch --verbose --strip=0 --force --ignore-whitespace --fuzz 4 < ${ patchScriptsDir } /rn.patch` , {
@@ -138,7 +142,6 @@ if (actions.includes('create')) {
138142 cwd : `${ appDir } /ios` ,
139143 env : env ,
140144 } ) ;
141- console . log ( `done3` ) ;
142145
143146 if ( fs . existsSync ( `${ appDir } /Gemfile` ) ) {
144147 execSync ( `bundle install` , { stdio : 'inherit' , cwd : appDir , env : env } ) ;
@@ -185,7 +188,8 @@ if (actions.includes('build')) {
185188 -workspace ${ appName } .xcworkspace \
186189 -configuration ${ buildType } \
187190 -scheme ${ appName } \
188- -destination 'platform=iOS Simulator,OS=${ runtime } ,name=${ device } ' \
191+ -sdk 'iphonesimulator' \
192+ -destination 'generic/platform=iOS Simulator' \
189193 ONLY_ACTIVE_ARCH=yes \
190194 -derivedDataPath DerivedData \
191195 build | tee xcodebuild.log | xcbeautify` ,
@@ -202,123 +206,40 @@ if (actions.includes('build')) {
202206 appProduct = `${ appDir } /android/app/build/outputs/apk/release/app-release.apk` ;
203207 }
204208
205- var testApp = `${ e2eDir } /${ testAppName } ` ;
206209 console . log ( `Moving ${ appProduct } to ${ testApp } ` ) ;
207210 if ( fs . existsSync ( testApp ) ) fs . rmSync ( testApp , { recursive : true } ) ;
208211 fs . renameSync ( appProduct , testApp ) ;
209212}
210213
211214if ( actions . includes ( 'test' ) ) {
212- if (
213- platform == 'ios' &&
214- ! fs . existsSync ( `${ e2eDir } /DerivedData/Build/Products/Debug-iphonesimulator/WebDriverAgentRunner-Runner.app` )
215- ) {
216- // Build iOS WebDriverAgent
217- execSync (
218- `set -o pipefail && xcodebuild \
219- -project node_modules/appium-webdriveragent/WebDriverAgent.xcodeproj \
220- -scheme WebDriverAgentRunner \
221- -destination 'platform=iOS Simulator,OS=${ runtime } ,name=${ device } ' \
222- GCC_TREAT_WARNINGS_AS_ERRORS=0 \
223- COMPILER_INDEX_STORE_ENABLE=NO \
224- ONLY_ACTIVE_ARCH=yes \
225- -derivedDataPath DerivedData \
226- build | tee xcodebuild-agent.log | xcbeautify` ,
227- { stdio : 'inherit' , cwd : e2eDir , env : env } ,
228- ) ;
229- }
230-
231- // Start the appium server.
232- var processesToKill = { } ;
233- async function newProcess ( name , process ) {
234- await new Promise ( ( resolve , reject ) => {
235- process . on ( 'error' , e => {
236- console . error ( `Failed to start process '${ name } ': ${ e } ` ) ;
237- reject ( e ) ;
238- } ) ;
239- process . on ( 'spawn' , ( ) => {
240- console . log ( `Process '${ name } ' (${ process . pid } ) started` ) ;
241- resolve ( ) ;
242- } ) ;
243- } ) ;
244-
245- processesToKill [ name ] = {
246- process : process ,
247- complete : new Promise ( ( resolve , _reject ) => {
248- process . on ( 'close' , resolve ) ;
249- } ) ,
250- } ;
251- }
252- await newProcess (
253- 'appium' ,
254- spawn ( 'node_modules/.bin/appium' , [ '--log-timestamp' , '--log-no-colors' , '--log' , `appium${ platform } .log` ] , {
255- stdio : 'inherit' ,
256- cwd : e2eDir ,
257- env : env ,
258- shell : false ,
259- } ) ,
260- ) ;
261-
262- try {
263- await waitForAppium ( ) ;
264-
265- // Run e2e tests
266- const testEnv = env ;
267- testEnv . PLATFORM = platform ;
268- testEnv . APPIUM_APP = `./${ testAppName } ` ;
269-
270- if ( platform == 'ios' ) {
271- testEnv . APPIUM_DERIVED_DATA = 'DerivedData' ;
272- } else if ( platform == 'android' ) {
273- execSync ( `adb devices -l` , { stdio : 'inherit' , cwd : e2eDir , env : env } ) ;
274-
275- execSync ( `adb logcat -c` , { stdio : 'inherit' , cwd : e2eDir , env : env } ) ;
276-
277- var adbLogStream = fs . createWriteStream ( `${ e2eDir } /adb.log` ) ;
278- const adbLogProcess = spawn ( 'adb' , [ 'logcat' ] , { cwd : e2eDir , env : env , shell : false } ) ;
279- adbLogProcess . stdout . pipe ( adbLogStream ) ;
280- adbLogProcess . stderr . pipe ( adbLogStream ) ;
281- adbLogProcess . on ( 'close' , ( ) => adbLogStream . close ( ) ) ;
282- await newProcess ( 'adb logcat' , adbLogProcess ) ;
283- }
284-
285- execSync ( `yarn test:e2e:runner --verbose` , { stdio : 'inherit' , cwd : e2eDir , env : testEnv } ) ;
286- } finally {
287- for ( const [ name , info ] of Object . entries ( processesToKill ) ) {
288- console . log ( `Sending termination signal to process '${ name } ' (${ info . process . pid } )` ) ;
289-
290- // Send SIGTERM first to allow graceful shutdown.
291- info . process . kill ( 15 ) ;
292-
293- // Also send SIGKILL after 10 seconds.
294- const killTimeout = setTimeout ( ( ) => process . kill ( 9 ) , '10000' ) ;
295-
296- // Wait for the process to exit (either via SIGTERM or SIGKILL).
297- const code = await info . complete ;
298-
299- // Successfully exited now, no need to kill (if it hasn't run yet).
300- clearTimeout ( killTimeout ) ;
301-
302- console . log ( `Process '${ name } ' (${ info . process . pid } ) exited with code ${ code } ` ) ;
215+ // Run e2e tests
216+ if ( platform == 'ios' ) {
217+ try {
218+ execSync ( 'xcrun simctl list devices | grep -q "(Booted)"' ) ;
219+ } catch ( error ) {
220+ throw new Error ( 'No simulator is currently booted. Please boot a simulator before running this script.' ) ;
303221 }
304- }
305- }
306222
307- async function waitForAppium ( ) {
308- console . log ( 'Waiting for Appium server to start...' ) ;
309- for ( let i = 0 ; i < 60 ; i ++ ) {
223+ execFileSync ( 'xcrun' , [ 'simctl' , 'install' , 'booted' , testApp ] ) ;
224+ } else if ( platform == 'android' ) {
310225 try {
311- await fetch ( 'http://127.0.0.1:4723/sessions' , { method : 'HEAD' } ) ;
312- console . log ( 'Appium server started' ) ;
313- return ;
226+ execSync ( 'adb devices | grep -q "emulator"' ) ;
314227 } catch ( error ) {
315- console . log ( `Appium server hasn't started yet (${ error } )...` ) ;
316- await sleep ( 1000 ) ;
228+ throw new Error ( 'No Android emulator is currently running. Please start an emulator before running this script.' ) ;
317229 }
230+
231+ execFileSync ( 'adb' , [ 'install' , '-r' , '-d' , testApp ] ) ;
318232 }
319- throw new Error ( 'Appium server failed to start' ) ;
320- }
321233
322- async function sleep ( millis ) {
323- return new Promise ( resolve => setTimeout ( resolve , millis ) ) ;
234+ execSync (
235+ `maestro test maestro \
236+ --env=APP_ID="${ appId } " \
237+ --env=SENTRY_AUTH_TOKEN="${ sentryAuthToken } " \
238+ --debug-output maestro-logs \
239+ --flatten-debug-output` ,
240+ {
241+ stdio : 'inherit' ,
242+ cwd : e2eDir ,
243+ } ,
244+ ) ;
324245}
0 commit comments