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.
@@ -143,9 +144,6 @@ if (actions.includes('create')) {
143144 } ) ;
144145
145146 if ( fs . existsSync ( `${ appDir } /Gemfile` ) ) {
146- // TMP Fix for https://github.com/CocoaPods/Xcodeproj/issues/989
147- fs . appendFileSync ( `${ appDir } /Gemfile` , "gem 'xcodeproj', '< 1.26.0'\n" ) ;
148-
149147 execSync ( `bundle install` , { stdio : 'inherit' , cwd : appDir , env : env } ) ;
150148 execSync ( 'bundle exec pod install --repo-update' , { stdio : 'inherit' , cwd : `${ appDir } /ios` , env : env } ) ;
151149 } else {
@@ -190,7 +188,8 @@ if (actions.includes('build')) {
190188 -workspace ${ appName } .xcworkspace \
191189 -configuration ${ buildType } \
192190 -scheme ${ appName } \
193- -destination 'platform=iOS Simulator,OS=${ runtime } ,name=${ device } ' \
191+ -sdk 'iphonesimulator' \
192+ -destination 'generic/platform=iOS Simulator' \
194193 ONLY_ACTIVE_ARCH=yes \
195194 -derivedDataPath DerivedData \
196195 build | tee xcodebuild.log | xcbeautify` ,
@@ -207,123 +206,40 @@ if (actions.includes('build')) {
207206 appProduct = `${ appDir } /android/app/build/outputs/apk/release/app-release.apk` ;
208207 }
209208
210- var testApp = `${ e2eDir } /${ testAppName } ` ;
211209 console . log ( `Moving ${ appProduct } to ${ testApp } ` ) ;
212210 if ( fs . existsSync ( testApp ) ) fs . rmSync ( testApp , { recursive : true } ) ;
213211 fs . renameSync ( appProduct , testApp ) ;
214212}
215213
216214if ( actions . includes ( 'test' ) ) {
217- if (
218- platform == 'ios' &&
219- ! fs . existsSync ( `${ e2eDir } /DerivedData/Build/Products/Debug-iphonesimulator/WebDriverAgentRunner-Runner.app` )
220- ) {
221- // Build iOS WebDriverAgent
222- execSync (
223- `set -o pipefail && xcodebuild \
224- -project node_modules/appium-webdriveragent/WebDriverAgent.xcodeproj \
225- -scheme WebDriverAgentRunner \
226- -destination 'platform=iOS Simulator,OS=${ runtime } ,name=${ device } ' \
227- GCC_TREAT_WARNINGS_AS_ERRORS=0 \
228- COMPILER_INDEX_STORE_ENABLE=NO \
229- ONLY_ACTIVE_ARCH=yes \
230- -derivedDataPath DerivedData \
231- build | tee xcodebuild-agent.log | xcbeautify` ,
232- { stdio : 'inherit' , cwd : e2eDir , env : env } ,
233- ) ;
234- }
235-
236- // Start the appium server.
237- var processesToKill = { } ;
238- async function newProcess ( name , process ) {
239- await new Promise ( ( resolve , reject ) => {
240- process . on ( 'error' , e => {
241- console . error ( `Failed to start process '${ name } ': ${ e } ` ) ;
242- reject ( e ) ;
243- } ) ;
244- process . on ( 'spawn' , ( ) => {
245- console . log ( `Process '${ name } ' (${ process . pid } ) started` ) ;
246- resolve ( ) ;
247- } ) ;
248- } ) ;
249-
250- processesToKill [ name ] = {
251- process : process ,
252- complete : new Promise ( ( resolve , _reject ) => {
253- process . on ( 'close' , resolve ) ;
254- } ) ,
255- } ;
256- }
257- await newProcess (
258- 'appium' ,
259- spawn ( 'node_modules/.bin/appium' , [ '--log-timestamp' , '--log-no-colors' , '--log' , `appium${ platform } .log` ] , {
260- stdio : 'inherit' ,
261- cwd : e2eDir ,
262- env : env ,
263- shell : false ,
264- } ) ,
265- ) ;
266-
267- try {
268- await waitForAppium ( ) ;
269-
270- // Run e2e tests
271- const testEnv = env ;
272- testEnv . PLATFORM = platform ;
273- testEnv . APPIUM_APP = `./${ testAppName } ` ;
274-
275- if ( platform == 'ios' ) {
276- testEnv . APPIUM_DERIVED_DATA = 'DerivedData' ;
277- } else if ( platform == 'android' ) {
278- execSync ( `adb devices -l` , { stdio : 'inherit' , cwd : e2eDir , env : env } ) ;
279-
280- execSync ( `adb logcat -c` , { stdio : 'inherit' , cwd : e2eDir , env : env } ) ;
281-
282- var adbLogStream = fs . createWriteStream ( `${ e2eDir } /adb.log` ) ;
283- const adbLogProcess = spawn ( 'adb' , [ 'logcat' ] , { cwd : e2eDir , env : env , shell : false } ) ;
284- adbLogProcess . stdout . pipe ( adbLogStream ) ;
285- adbLogProcess . stderr . pipe ( adbLogStream ) ;
286- adbLogProcess . on ( 'close' , ( ) => adbLogStream . close ( ) ) ;
287- await newProcess ( 'adb logcat' , adbLogProcess ) ;
288- }
289-
290- execSync ( `yarn test:e2e:runner --verbose` , { stdio : 'inherit' , cwd : e2eDir , env : testEnv } ) ;
291- } finally {
292- for ( const [ name , info ] of Object . entries ( processesToKill ) ) {
293- console . log ( `Sending termination signal to process '${ name } ' (${ info . process . pid } )` ) ;
294-
295- // Send SIGTERM first to allow graceful shutdown.
296- info . process . kill ( 15 ) ;
297-
298- // Also send SIGKILL after 10 seconds.
299- const killTimeout = setTimeout ( ( ) => process . kill ( 9 ) , '10000' ) ;
300-
301- // Wait for the process to exit (either via SIGTERM or SIGKILL).
302- const code = await info . complete ;
303-
304- // Successfully exited now, no need to kill (if it hasn't run yet).
305- clearTimeout ( killTimeout ) ;
306-
307- 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.' ) ;
308221 }
309- }
310- }
311222
312- async function waitForAppium ( ) {
313- console . log ( 'Waiting for Appium server to start...' ) ;
314- for ( let i = 0 ; i < 60 ; i ++ ) {
223+ execFileSync ( 'xcrun' , [ 'simctl' , 'install' , 'booted' , testApp ] ) ;
224+ } else if ( platform == 'android' ) {
315225 try {
316- await fetch ( 'http://127.0.0.1:4723/sessions' , { method : 'HEAD' } ) ;
317- console . log ( 'Appium server started' ) ;
318- return ;
226+ execSync ( 'adb devices | grep -q "emulator"' ) ;
319227 } catch ( error ) {
320- console . log ( `Appium server hasn't started yet (${ error } )...` ) ;
321- await sleep ( 1000 ) ;
228+ throw new Error ( 'No Android emulator is currently running. Please start an emulator before running this script.' ) ;
322229 }
230+
231+ execFileSync ( 'adb' , [ 'install' , '-r' , '-d' , testApp ] ) ;
323232 }
324- throw new Error ( 'Appium server failed to start' ) ;
325- }
326233
327- async function sleep ( millis ) {
328- 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+ ) ;
329245}
0 commit comments