@@ -9,7 +9,14 @@ import {spawn} from 'node:child_process';
99import assert from 'node:assert' ;
1010import { isTvOs } from './utils' ;
1111import type { XCUITestDriver , XCUITestDriverOpts } from './driver' ;
12- import type { StringRecord , HTTPHeaders , DownloadAppOptions , PostProcessOptions , PostProcessResult , CachedAppInfo } from '@appium/types' ;
12+ import type {
13+ StringRecord ,
14+ HTTPHeaders ,
15+ DownloadAppOptions ,
16+ PostProcessOptions ,
17+ PostProcessResult ,
18+ CachedAppInfo ,
19+ } from '@appium/types' ;
1320import type { Readable } from 'node:stream' ;
1421
1522export const SAFARI_BUNDLE_ID = 'com.apple.mobilesafari' ;
@@ -77,7 +84,7 @@ export async function verifyApplicationPlatform(this: XCUITestDriver): Promise<v
7784
7885 const executablePath = path . resolve (
7986 this . opts . app ,
80- await this . appInfosCache . extractExecutableName ( this . opts . app )
87+ await this . appInfosCache . extractExecutableName ( this . opts . app ) ,
8188 ) ;
8289 const [ resFile , resUname ] = await B . all ( [
8390 exec ( 'lipo' , [ '-info' , executablePath ] ) ,
@@ -92,29 +99,28 @@ export async function verifyApplicationPlatform(this: XCUITestDriver): Promise<v
9299 if ( isAppleSiliconCpu && processArch === INTEL_ARCH ) {
93100 this . log . warn (
94101 `It looks like the Appium server process is running under Rosetta emulation. ` +
95- `This might lead to various performance/compatibility issues while running tests on Simulator. ` +
96- `Consider using binaries compiled natively for the ARM64 architecture to run Appium server ` +
97- `with this driver.`
102+ `This might lead to various performance/compatibility issues while running tests on Simulator. ` +
103+ `Consider using binaries compiled natively for the ARM64 architecture to run Appium server ` +
104+ `with this driver.` ,
98105 ) ;
99106 }
100107 if ( _ . includes ( bundleExecutableInfo , processArch ) ) {
101108 return ;
102109 }
103- const hasRosetta = isAppleSiliconCpu && await isRosettaInstalled ( ) ;
110+ const hasRosetta = isAppleSiliconCpu && ( await isRosettaInstalled ( ) ) ;
104111 const isIntelApp = _ . includes ( bundleExecutableInfo , INTEL_ARCH ) ;
105112 // We cannot run Simulator builds compiled for arm64 on Intel machines
106113 // Rosetta allows only to run Intel ones on arm64
107- if (
108- ( isIntelApp && ( ! isAppleSiliconCpu || hasRosetta ) ) || ( ! isIntelApp && isAppleSiliconCpu )
109- ) {
114+ if ( ( isIntelApp && ( ! isAppleSiliconCpu || hasRosetta ) ) || ( ! isIntelApp && isAppleSiliconCpu ) ) {
110115 return ;
111116 }
112- const advice = isIntelApp && isAppleSiliconCpu && ! hasRosetta
113- ? `Please install Rosetta and try again.`
114- : `Please rebuild your application to support the ${ processArch } platform.` ;
117+ const advice =
118+ isIntelApp && isAppleSiliconCpu && ! hasRosetta
119+ ? `Please install Rosetta and try again.`
120+ : `Please rebuild your application to support the ${ processArch } platform.` ;
115121 throw new Error (
116122 `The ${ this . opts . bundleId } application does not support the ${ processArch } Simulator ` +
117- `architecture:\n${ bundleExecutableInfo } \n\n${ advice } `
123+ `architecture:\n${ bundleExecutableInfo } \n\n${ advice } ` ,
118124 ) ;
119125}
120126
@@ -123,7 +129,7 @@ export async function verifyApplicationPlatform(this: XCUITestDriver): Promise<v
123129 */
124130export async function parseLocalizableStrings (
125131 this : XCUITestDriver ,
126- opts : LocalizableStringsOptions = { }
132+ opts : LocalizableStringsOptions = { } ,
127133) : Promise < StringRecord > {
128134 const { app, language = 'en' , localizableStringsDir, stringFile, strictMode} = opts ;
129135 if ( ! app ) {
@@ -185,29 +191,35 @@ export async function parseLocalizableStrings(
185191 . map ( ( name ) => path . resolve ( lprojRoot , name ) ) ;
186192 resourcePaths . push ( ...resourceFiles ) ;
187193 }
188- this . log . info ( `Got ${ util . pluralize ( 'resource file' , resourcePaths . length , true ) } in '${ lprojRoot } '` ) ;
194+ this . log . info (
195+ `Got ${ util . pluralize ( 'resource file' , resourcePaths . length , true ) } in '${ lprojRoot } '` ,
196+ ) ;
189197
190198 if ( _ . isEmpty ( resourcePaths ) ) {
191199 return { } ;
192200 }
193201
194202 const resultStrings : StringRecord = { } ;
195- const toAbsolutePath = ( p : string ) => path . isAbsolute ( p ) ? p : path . resolve ( process . cwd ( ) , p ) ;
203+ const toAbsolutePath = ( p : string ) => ( path . isAbsolute ( p ) ? p : path . resolve ( process . cwd ( ) , p ) ) ;
196204 for ( const resourcePath of resourcePaths ) {
197205 if ( ! util . isSubPath ( toAbsolutePath ( resourcePath ) , toAbsolutePath ( bundleRoot ) ) ) {
198206 // security precaution
199207 throw new Error ( `'${ resourcePath } ' is expected to be located under '${ bundleRoot } '` ) ;
200208 }
201209 try {
202210 const data = await readResource ( resourcePath ) ;
203- this . log . debug ( `Parsed ${ util . pluralize ( 'string' , _ . keys ( data ) . length , true ) } from '${ resourcePath } '` ) ;
211+ this . log . debug (
212+ `Parsed ${ util . pluralize ( 'string' , _ . keys ( data ) . length , true ) } from '${ resourcePath } '` ,
213+ ) ;
204214 _ . merge ( resultStrings , data ) ;
205215 } catch ( e : any ) {
206216 this . log . warn ( `Cannot parse '${ resourcePath } ' resource. Original error: ${ e . message } ` ) ;
207217 }
208218 }
209219
210- this . log . info ( `Retrieved ${ util . pluralize ( 'string' , _ . keys ( resultStrings ) . length , true ) } from '${ lprojRoot } '` ) ;
220+ this . log . info (
221+ `Retrieved ${ util . pluralize ( 'string' , _ . keys ( resultStrings ) . length , true ) } from '${ lprojRoot } '` ,
222+ ) ;
211223 return resultStrings ;
212224 } finally {
213225 if ( tmpRoot ) {
@@ -251,14 +263,13 @@ export async function unzipFile(archivePath: string): Promise<UnzipInfo> {
251263 */
252264export async function unzipStream ( zipStream : Readable ) : Promise < UnzipInfo > {
253265 const tmpRoot = await tempDir . openDir ( ) ;
254- const bsdtarProcess = spawn ( await fs . which ( 'bsdtar' ) , [
255- '-x' ,
256- '--exclude' , MACOS_RESOURCE_FOLDER ,
257- '--exclude' , `${ MACOS_RESOURCE_FOLDER } /*` ,
258- '-' ,
259- ] , {
260- cwd : tmpRoot ,
261- } ) ;
266+ const bsdtarProcess = spawn (
267+ await fs . which ( 'bsdtar' ) ,
268+ [ '-x' , '--exclude' , MACOS_RESOURCE_FOLDER , '--exclude' , `${ MACOS_RESOURCE_FOLDER } /*` , '-' ] ,
269+ {
270+ cwd : tmpRoot ,
271+ } ,
272+ ) ;
262273 let archiveSize = 0 ;
263274 bsdtarProcess . stderr . on ( 'data' , ( chunk ) => {
264275 const stderr = chunk . toString ( ) ;
@@ -329,38 +340,43 @@ export function buildSafariPreferences(opts: XCUITestDriverOpts): StringRecord {
329340 * .zip and .ipa formats are supported.
330341 * A .zip archive can contain one or more
331342 */
332- export async function onDownloadApp ( this : XCUITestDriver , opts : DownloadAppOptions ) : Promise < string > {
343+ export async function onDownloadApp (
344+ this : XCUITestDriver ,
345+ opts : DownloadAppOptions ,
346+ ) : Promise < string > {
333347 return this . isRealDevice ( )
334348 ? await downloadIpa . bind ( this ) ( opts . stream , opts . headers )
335349 : await unzipApp . bind ( this ) ( opts . stream ) ;
336350}
337351
338352export async function onPostConfigureApp (
339353 this : XCUITestDriver ,
340- opts : PostProcessOptions
354+ opts : PostProcessOptions ,
341355) : Promise < PostProcessResult | false > {
342356 // Pick the previously cached entry if its integrity has been preserved
343- const appInfo = _ . isPlainObject ( opts . cachedAppInfo ) ? opts . cachedAppInfo as CachedAppInfo : undefined ;
357+ const appInfo = _ . isPlainObject ( opts . cachedAppInfo )
358+ ? ( opts . cachedAppInfo as CachedAppInfo )
359+ : undefined ;
344360 const cachedPath = appInfo ? appInfo . fullPath : undefined ;
345361
346362 const shouldUseCachedApp = async ( ) => {
347- if ( ! appInfo || ! cachedPath || ! await fs . exists ( cachedPath ) ) {
363+ if ( ! appInfo || ! cachedPath || ! ( await fs . exists ( cachedPath ) ) ) {
348364 return false ;
349365 }
350366
351367 const isCachedPathAFile = ( await fs . stat ( cachedPath ) ) . isFile ( ) ;
352368 if ( isCachedPathAFile ) {
353- return await fs . hash ( cachedPath ) === ( appInfo . integrity as any ) ?. file ;
369+ return ( await fs . hash ( cachedPath ) ) === ( appInfo . integrity as any ) ?. file ;
354370 }
355371 // If the cached path is a folder then it is expected to be previously extracted from
356372 // an archive located under appPath whose hash is stored as `cachedAppInfo.packageHash`
357373 if (
358- ! isCachedPathAFile
359- && opts . cachedAppInfo ?. packageHash
360- && opts . appPath
361- && await fs . exists ( opts . appPath )
362- && ( await fs . stat ( opts . appPath ) ) . isFile ( )
363- && opts . cachedAppInfo . packageHash === await fs . hash ( opts . appPath )
374+ ! isCachedPathAFile &&
375+ opts . cachedAppInfo ?. packageHash &&
376+ opts . appPath &&
377+ ( await fs . exists ( opts . appPath ) ) &&
378+ ( await fs . stat ( opts . appPath ) ) . isFile ( ) &&
379+ opts . cachedAppInfo . packageHash === ( await fs . hash ( opts . appPath ) )
364380 ) {
365381 const nestedItemsCountInCache = ( appInfo . integrity as any ) ?. folder ;
366382 if ( nestedItemsCountInCache !== undefined ) {
@@ -384,7 +400,7 @@ export async function onPostConfigureApp(
384400 }
385401
386402 const isLocalIpa = await isIpaBundle ( opts . appPath ) ;
387- const isLocalApp = ! isLocalIpa && await isAppBundle ( opts . appPath ) ;
403+ const isLocalApp = ! isLocalIpa && ( await isAppBundle ( opts . appPath ) ) ;
388404 const isPackageReadyForInstall = isLocalApp || ( this . isRealDevice ( ) && isLocalIpa ) ;
389405 if ( isPackageReadyForInstall ) {
390406 await this . appInfosCache . put ( opts . appPath ) ;
@@ -396,9 +412,7 @@ export async function onPostConfigureApp(
396412 }
397413 // Cache the app while unpacking the bundle if necessary
398414 return {
399- appPath : isPackageReadyForInstall
400- ? opts . appPath
401- : await unzipApp . bind ( this ) ( opts . appPath )
415+ appPath : isPackageReadyForInstall ? opts . appPath : await unzipApp . bind ( this ) ( opts . appPath ) ,
402416 } ;
403417}
404418
@@ -456,14 +470,18 @@ function parseFileName(headers: HTTPHeaders): string | null {
456470/**
457471 * Downloads and verifies remote applications for real devices
458472 */
459- async function downloadIpa ( this : XCUITestDriver , stream : Readable , headers : HTTPHeaders ) : Promise < string > {
473+ async function downloadIpa (
474+ this : XCUITestDriver ,
475+ stream : Readable ,
476+ headers : HTTPHeaders ,
477+ ) : Promise < string > {
460478 const timer = new timing . Timer ( ) . start ( ) ;
461479
462480 const logPerformance = ( dstPath : string , fileSize : number , action : string ) => {
463481 const secondsElapsed = timer . getDuration ( ) . asSeconds ;
464482 this . log . info (
465483 `The remote file (${ util . toReadableSizeString ( fileSize ) } ) ` +
466- `has been ${ action } to '${ dstPath } ' in ${ secondsElapsed . toFixed ( 3 ) } s`
484+ `has been ${ action } to '${ dstPath } ' in ${ secondsElapsed . toFixed ( 3 ) } s` ,
467485 ) ;
468486 if ( secondsElapsed >= 1 ) {
469487 const bytesPerSec = Math . floor ( fileSize / secondsElapsed ) ;
@@ -481,7 +499,7 @@ async function downloadIpa(this: XCUITestDriver, stream: Readable, headers: HTTP
481499 if ( ! _ . isEmpty ( matchedPaths ) ) {
482500 this . log . debug (
483501 `Found ${ util . pluralize ( `${ IPA_EXT } application` , matchedPaths . length , true ) } in ` +
484- `'${ path . basename ( rootDir ) } ': ${ matchedPaths } `
502+ `'${ path . basename ( rootDir ) } ': ${ matchedPaths } ` ,
485503 ) ;
486504 }
487505 for ( const matchedPath of matchedPaths ) {
@@ -585,9 +603,10 @@ async function isolateApp(appPath: string): Promise<string> {
585603async function unzipApp (
586604 this : XCUITestDriver ,
587605 appPathOrZipStream : string | Readable ,
588- depth : number = 0
606+ depth : number = 0 ,
589607) : Promise < string > {
590- const errMsg = `The archive did not have any matching ${ APP_EXT } or ${ IPA_EXT } ` +
608+ const errMsg =
609+ `The archive did not have any matching ${ APP_EXT } or ${ IPA_EXT } ` +
591610 `bundles. Please make sure the provided package is valid and contains at least one matching ` +
592611 `application bundle which is not nested.` ;
593612 if ( depth > MAX_ARCHIVE_SCAN_DEPTH ) {
@@ -608,15 +627,13 @@ async function unzipApp(
608627 }
609628 } catch ( e : any ) {
610629 this . log . debug ( e . stack ) ;
611- throw new Error (
612- `Cannot prepare the application for testing. Original error: ${ e . message } `
613- ) ;
630+ throw new Error ( `Cannot prepare the application for testing. Original error: ${ e . message } ` ) ;
614631 }
615632 const secondsElapsed = timer . getDuration ( ) . asSeconds ;
616633 this . log . info (
617634 `The file (${ util . toReadableSizeString ( archiveSize ) } ) ` +
618635 `has been ${ _ . isString ( appPathOrZipStream ) ? 'extracted' : 'downloaded and extracted' } ` +
619- `to '${ rootDir } ' in ${ secondsElapsed . toFixed ( 3 ) } s`
636+ `to '${ rootDir } ' in ${ secondsElapsed . toFixed ( 3 ) } s` ,
620637 ) ;
621638 // it does not make much sense to approximate the speed for short downloads
622639 if ( secondsElapsed >= 1 ) {
@@ -635,14 +652,14 @@ async function unzipApp(
635652 if ( this . isSimulator ( ) && ! platforms . some ( ( p ) => _ . includes ( p , 'Simulator' ) ) ) {
636653 this . log . info (
637654 `'${ appPath } ' does not have Simulator devices in the list of supported platforms ` +
638- `(${ platforms . join ( ',' ) } ). Skipping it`
655+ `(${ platforms . join ( ',' ) } ). Skipping it` ,
639656 ) ;
640657 return false ;
641658 }
642659 if ( this . isRealDevice ( ) && ! platforms . some ( ( p ) => _ . includes ( p , 'OS' ) ) ) {
643660 this . log . info (
644661 `'${ appPath } ' does not have real devices in the list of supported platforms ` +
645- `(${ platforms . join ( ',' ) } ). Skipping it`
662+ `(${ platforms . join ( ',' ) } ). Skipping it` ,
646663 ) ;
647664 return false ;
648665 }
@@ -662,8 +679,8 @@ async function unzipApp(
662679 for ( const matchedPath of matchedPaths ) {
663680 const fullPath = path . join ( rootDir , matchedPath ) ;
664681 if (
665- ( await isAppBundle ( fullPath ) || ( this . isRealDevice ( ) && await isIpaBundle ( fullPath ) ) )
666- && await isCompatibleWithCurrentPlatform ( fullPath )
682+ ( ( await isAppBundle ( fullPath ) ) || ( this . isRealDevice ( ) && ( await isIpaBundle ( fullPath ) ) ) ) &&
683+ ( await isCompatibleWithCurrentPlatform ( fullPath ) )
667684 ) {
668685 this . log . debug ( `Selecting the application at '${ matchedPath } '` ) ;
669686 return await isolateApp ( fullPath ) ;
0 commit comments