@@ -10,6 +10,7 @@ import { readTextFile, toVersionNumber, v } from "../helpers.js";
1010import { setReactVersion } from "../internal/set-react-version.mts" ;
1111import type { BuildConfig , TargetPlatform } from "../types.js" ;
1212import { green , red , yellow } from "../utils/colors.mjs" ;
13+ import { rm_r } from "../utils/filesystem.mjs" ;
1314import { getIOSSimulatorName , installPods } from "./test-apple.mts" ;
1415import { $ , $$ , test } from "./test-e2e.mts" ;
1516
@@ -18,16 +19,37 @@ type PlatformConfig = {
1819 engines : ReadonlyArray < "hermes" | "jsc" > ;
1920 isAvailable : ( config : Required < BuildConfig > ) => boolean ;
2021 prebuild : ( config : Required < BuildConfig > ) => Promise < void > ;
22+ additionalBuildArgs ?: ( ) => string [ ] ;
23+ requiresManualTesting ?: boolean ;
2124} ;
2225
2326const DEFAULT_PLATFORMS = [ "android" , "ios" ] ;
27+ const PACKAGE_MANAGER = "yarn" ;
28+ const TAG = "┃" ;
2429const TEST_VARIANTS = [ "paper" , "fabric" ] as const ;
2530
31+ function isVariantSupported ( { version, variant } : Required < BuildConfig > ) {
32+ return variant === "fabric" || toVersionNumber ( version ) < v ( 0 , 82 , 0 ) ;
33+ }
34+
35+ function isAppleVariantSupported ( config : Required < BuildConfig > ) {
36+ if ( process . platform !== "darwin" ) {
37+ return false ;
38+ }
39+
40+ const { version, engine } = config ;
41+ if ( engine === "jsc" && toVersionNumber ( version ) >= v ( 0 , 80 , 0 ) ) {
42+ return false ;
43+ }
44+
45+ return isVariantSupported ( config ) ;
46+ }
47+
2648const PLATFORM_CONFIG : Record < TargetPlatform , PlatformConfig > = {
2749 android : {
2850 name : "Android" ,
2951 engines : [ "hermes" ] ,
30- isAvailable : ( { engine } ) => engine === "hermes" ,
52+ isAvailable : isVariantSupported ,
3153 prebuild : ( { variant } ) => {
3254 if ( variant === "fabric" ) {
3355 const properties = "android/gradle.properties" ;
@@ -43,42 +65,42 @@ const PLATFORM_CONFIG: Record<TargetPlatform, PlatformConfig> = {
4365 ios : {
4466 name : "iOS" ,
4567 engines : [ "jsc" , "hermes" ] ,
46- isAvailable : ( { version, engine } ) => {
47- if ( process . platform !== "darwin" ) {
48- return false ;
49- }
50-
51- if ( engine === "jsc" && toVersionNumber ( version ) >= v ( 0 , 80 , 0 ) ) {
52- return false ;
53- }
54-
55- return true ;
56- } ,
68+ isAvailable : isAppleVariantSupported ,
5769 prebuild : installPods ,
70+ additionalBuildArgs : ( ) => [ "--device" , getIOSSimulatorName ( ) ] ,
5871 } ,
5972 macos : {
6073 name : "macOS" ,
6174 engines : [ "jsc" , "hermes" ] ,
62- isAvailable : ( ) => false ,
75+ isAvailable : isAppleVariantSupported ,
6376 prebuild : installPods ,
77+ requiresManualTesting : true ,
6478 } ,
6579 visionos : {
6680 name : "visionOS" ,
6781 engines : [ "jsc" , "hermes" ] ,
68- isAvailable : ( ) => false ,
82+ isAvailable : isAppleVariantSupported ,
6983 prebuild : installPods ,
84+ requiresManualTesting : true ,
7085 } ,
7186 windows : {
7287 name : "Windows" ,
7388 engines : [ "hermes" ] ,
74- isAvailable : ( ) => false ,
75- prebuild : ( ) => Promise . resolve ( ) ,
89+ isAvailable : ( ) => process . platform === "win32" ,
90+ prebuild : ( ) => {
91+ rm_r ( "windows/ExperimentalFeatures.props" ) ;
92+ $ (
93+ PACKAGE_MANAGER ,
94+ "install-windows-test-app" ,
95+ "--msbuildprops" ,
96+ "WindowsTargetPlatformVersion=10.0.26100.0"
97+ ) ;
98+ return Promise . resolve ( ) ;
99+ } ,
100+ requiresManualTesting : true ,
76101 } ,
77102} ;
78103
79- const PACKAGE_MANAGER = "yarn" ;
80- const TAG = "┃" ;
81-
82104const rootDir = fileURLToPath ( new URL ( "../.." , import . meta. url ) ) ;
83105
84106function log ( message = "" , tag = TAG ) {
@@ -92,6 +114,7 @@ function run(script: string, logPath: string) {
92114 const fd = fs . openSync ( logPath , "a" , 0o644 ) ;
93115 const proc = spawn ( PACKAGE_MANAGER , [ "run" , script ] , {
94116 stdio : [ "ignore" , fd , fd ] ,
117+ shell : process . platform === "win32" ,
95118 } ) ;
96119 return proc ;
97120}
@@ -124,13 +147,10 @@ function validatePlatforms(platforms: string[]): TargetPlatform[] {
124147 switch ( platform ) {
125148 case "android" :
126149 case "ios" :
127- filtered . push ( platform ) ;
128- break ;
129-
130150 case "macos" :
131151 case "visionos" :
132152 case "windows" :
133- log ( yellow ( `⚠ Unsupported platform: ${ platform } ` ) ) ;
153+ filtered . push ( platform ) ;
134154 break ;
135155
136156 default :
@@ -153,6 +173,18 @@ function parseArgs(args: string[]) {
153173 description : "Test iOS" ,
154174 type : "boolean" ,
155175 } ,
176+ macos : {
177+ description : "Test macOS" ,
178+ type : "boolean" ,
179+ } ,
180+ visionos : {
181+ description : "Test visionOS" ,
182+ type : "boolean" ,
183+ } ,
184+ windows : {
185+ description : "Test Windows" ,
186+ type : "boolean" ,
187+ } ,
156188 } ,
157189 strict : true ,
158190 allowPositionals : true ,
@@ -168,10 +200,10 @@ function parseArgs(args: string[]) {
168200 } ;
169201}
170202
171- function prestart ( ) {
203+ function waitForUserInput ( message : string ) : Promise < void > {
172204 return ! process . stdin . isTTY
173205 ? Promise . resolve ( )
174- : new Promise ( ( resolve ) => {
206+ : new Promise ( ( resolve , reject ) => {
175207 const stdin = process . stdin ;
176208 const rawMode = stdin . isRaw ;
177209 const encoding = stdin . readableEncoding || undefined ;
@@ -185,31 +217,24 @@ function prestart() {
185217 stdin . setRawMode ( rawMode ) ;
186218 if ( typeof key === "string" && key === "\u0003" ) {
187219 showBanner ( "❌ Canceled" ) ;
188- // eslint-disable-next-line local/no-process-exit
189- process . exit ( 1 ) ;
220+ reject ( 1 ) ;
221+ } else {
222+ resolve ( ) ;
190223 }
191- resolve ( true ) ;
192224 } ) ;
193- process . stdout . write (
194- `${ TAG } Before continuing, make sure all emulators/simulators and Appium/Metro instances are closed.\n${ TAG } \n${ TAG } Press any key to continue...`
195- ) ;
225+ process . stdout . write ( message ) ;
196226 } ) ;
197227}
198228
199229/**
200- * Invokes `react-native run- <platform>`.
230+ * Invokes `rnx-cli run --platform <platform>`.
201231 */
202232function buildAndRun ( platform : TargetPlatform ) {
203- switch ( platform ) {
204- case "ios" : {
205- const simulator = getIOSSimulatorName ( ) ;
206- $ ( PACKAGE_MANAGER , platform , "--device" , simulator ) ;
207- break ;
208- }
209- default : {
210- $ ( PACKAGE_MANAGER , platform ) ;
211- break ;
212- }
233+ const { additionalBuildArgs } = PLATFORM_CONFIG [ platform ] ;
234+ if ( additionalBuildArgs ) {
235+ $ ( PACKAGE_MANAGER , platform , ...additionalBuildArgs ( ) ) ;
236+ } else {
237+ $ ( PACKAGE_MANAGER , platform ) ;
213238 }
214239}
215240
@@ -229,7 +254,13 @@ async function buildRunTest({ version, platform, variant }: BuildConfig) {
229254 showBanner ( `Build ${ setup . name } [${ variant } , ${ engine } ]` ) ;
230255 await setup . prebuild ( configWithEngine ) ;
231256 buildAndRun ( platform ) ;
232- await test ( platform , [ variant , engine ] ) ;
257+ if ( setup . requiresManualTesting ) {
258+ await waitForUserInput (
259+ `${ TAG } \n${ TAG } ${ yellow ( "⚠" ) } ${ setup . name } requires manual testing. When you're done, press any key to continue...`
260+ ) ;
261+ } else {
262+ await test ( platform , [ variant , engine ] ) ;
263+ }
233264 }
234265}
235266
@@ -265,7 +296,7 @@ async function withReactNativeVersion(
265296 reset ( rootDir ) ;
266297
267298 if ( version ) {
268- await setReactVersion ( version , true ) ;
299+ await setReactVersion ( version , false ) ;
269300 } else {
270301 log ( ) ;
271302 }
@@ -291,15 +322,20 @@ if (platforms.length === 0) {
291322 process . exitCode = 1 ;
292323 showBanner ( red ( "No valid platforms were specified" ) ) ;
293324} else {
294- TEST_VARIANTS . reduce ( ( job , variant ) => {
295- return job . then ( ( ) =>
296- withReactNativeVersion ( version , async ( ) => {
297- for ( const platform of platforms ) {
298- await buildRunTest ( { version, platform, variant } ) ;
299- }
300- } )
301- ) ;
302- } , prestart ( ) )
325+ TEST_VARIANTS . reduce (
326+ ( job , variant ) => {
327+ return job . then ( ( ) =>
328+ withReactNativeVersion ( version , async ( ) => {
329+ for ( const platform of platforms ) {
330+ await buildRunTest ( { version, platform, variant } ) ;
331+ }
332+ } )
333+ ) ;
334+ } ,
335+ waitForUserInput (
336+ `${ TAG } Before continuing, make sure all emulators/simulators and Appium/Metro instances are closed.\n${ TAG } \n${ TAG } Press any key to continue...`
337+ )
338+ )
303339 . then ( ( ) => {
304340 showBanner ( "Initialize new app" ) ;
305341 $ (
@@ -330,12 +366,23 @@ if (platforms.length === 0) {
330366 "-p" ,
331367 "windows" ,
332368 ] ;
333- const { status } = spawnSync ( PACKAGE_MANAGER , args , { stdio : "inherit" } ) ;
369+ const { status } = spawnSync ( PACKAGE_MANAGER , args , {
370+ stdio : "inherit" ,
371+ shell : process . platform === "win32" ,
372+ } ) ;
334373 if ( status !== 1 ) {
335374 throw new Error ( "Expected an error" ) ;
336375 }
337376 } )
338377 . then ( ( ) => {
339378 showBanner ( green ( "✔ Pass" ) ) ;
379+ } )
380+ . catch ( ( e ) => {
381+ if ( typeof e === "number" ) {
382+ process . exitCode = e ;
383+ } else {
384+ process . exitCode = 1 ;
385+ showBanner ( `❌ ${ e } ` ) ;
386+ }
340387 } ) ;
341388}
0 commit comments