11import { fs , plist } from '@appium/support' ;
22import { exec , SubProcess } from 'teen_process' ;
3- import path from 'path' ;
3+ import path , { dirname } from 'node:path' ;
4+ import { fileURLToPath } from 'node:url' ;
45import { log } from './logger' ;
56import _ from 'lodash' ;
67import { WDA_RUNNER_BUNDLE_ID , PLATFORM_NAME_TVOS } from './constants' ;
78import B from 'bluebird' ;
8- import _fs from 'fs' ;
9+ import _fs from 'node: fs' ;
910import { waitForCondition } from 'asyncbox' ;
10- import { arch } from 'os' ;
11+ import { arch } from 'node: os' ;
1112import type { DeviceInfo } from './types' ;
12- import { fileURLToPath } from 'url' ;
1313
1414const PROJECT_FILE = 'project.pbxproj' ;
1515
16+ // Get current filename - works in both CommonJS and ESM
17+ const currentFilename =
18+ typeof __filename !== 'undefined'
19+ ? __filename
20+ : fileURLToPath ( new Function ( 'return import.meta.url' ) ( ) ) ;
21+
22+ const currentDirname = dirname ( currentFilename ) ;
23+
1624/**
1725 * Calculates the path to the current module's root folder
1826 *
1927 * @returns {string } The full path to module root
2028 * @throws {Error } If the current module root folder cannot be determined
2129 */
2230const getModuleRoot = _ . memoize ( function getModuleRoot ( ) : string {
23- // In TypeScript/ESM, we need to use import.meta.url to get the current file path
24- // @ts -ignore - __filename may be available in CommonJS context
25- const currentFile = typeof __filename !== 'undefined' ? __filename : fileURLToPath ( import . meta. url ) ;
26- let currentDir = path . dirname ( path . resolve ( currentFile ) ) ;
31+ let currentDir = currentDirname ;
2732 let isAtFsRoot = false ;
2833 while ( ! isAtFsRoot ) {
2934 const manifestPath = path . join ( currentDir , 'package.json' ) ;
@@ -41,24 +46,7 @@ const getModuleRoot = _.memoize(function getModuleRoot (): string {
4146
4247export const BOOTSTRAP_PATH = getModuleRoot ( ) ;
4348
44- async function getPIDsUsingPattern ( pattern : string ) : Promise < string [ ] > {
45- const args = [
46- '-if' , // case insensitive, full cmdline match
47- pattern ,
48- ] ;
49- try {
50- const { stdout} = await exec ( 'pgrep' , args ) ;
51- return stdout . split ( / \s + / )
52- . map ( ( x ) => parseInt ( x , 10 ) )
53- . filter ( _ . isInteger )
54- . map ( ( x ) => `${ x } ` ) ;
55- } catch ( err : any ) {
56- log . debug ( `'pgrep ${ args . join ( ' ' ) } ' didn't detect any matching processes. Return code: ${ err . code } ` ) ;
57- return [ ] ;
58- }
59- }
60-
61- async function killAppUsingPattern ( pgrepPattern : string ) : Promise < void > {
49+ export async function killAppUsingPattern ( pgrepPattern : string ) : Promise < void > {
6250 const signals = [ 2 , 15 , 9 ] ;
6351 for ( const signal of signals ) {
6452 const matchedPids = await getPIDsUsingPattern ( pgrepPattern ) ;
@@ -102,26 +90,17 @@ async function killAppUsingPattern (pgrepPattern: string): Promise<void> {
10290 * @param platformName The name of the platorm
10391 * @returns Return true if the platformName is tvOS
10492 */
105- function isTvOS ( platformName : string ) : boolean {
93+ export function isTvOS ( platformName : string ) : boolean {
10694 return _ . toLower ( platformName ) === _ . toLower ( PLATFORM_NAME_TVOS ) ;
10795}
10896
109- async function replaceInFile ( file : string , find : string | RegExp , replace : string ) : Promise < void > {
110- const contents = await fs . readFile ( file , 'utf8' ) ;
111-
112- const newContents = contents . replace ( find , replace ) ;
113- if ( newContents !== contents ) {
114- await fs . writeFile ( file , newContents , 'utf8' ) ;
115- }
116- }
117-
11897/**
11998 * Update WebDriverAgentRunner project bundle ID with newBundleId.
12099 * This method assumes project file is in the correct state.
121100 * @param agentPath - Path to the .xcodeproj directory.
122101 * @param newBundleId the new bundle ID used to update.
123102 */
124- async function updateProjectFile ( agentPath : string , newBundleId : string ) : Promise < void > {
103+ export async function updateProjectFile ( agentPath : string , newBundleId : string ) : Promise < void > {
125104 const projectFilePath = path . resolve ( agentPath , PROJECT_FILE ) ;
126105 try {
127106 // Assuming projectFilePath is in the correct state, create .old from projectFilePath
@@ -139,7 +118,7 @@ async function updateProjectFile (agentPath: string, newBundleId: string): Promi
139118 * Reset WebDriverAgentRunner project bundle ID to correct state.
140119 * @param agentPath - Path to the .xcodeproj directory.
141120 */
142- async function resetProjectFile ( agentPath : string ) : Promise < void > {
121+ export async function resetProjectFile ( agentPath : string ) : Promise < void > {
143122 const projectFilePath = path . join ( agentPath , PROJECT_FILE ) ;
144123 try {
145124 // restore projectFilePath from .old file
@@ -156,7 +135,7 @@ async function resetProjectFile (agentPath: string): Promise<void> {
156135 }
157136}
158137
159- async function setRealDeviceSecurity ( keychainPath : string , keychainPassword : string ) : Promise < void > {
138+ export async function setRealDeviceSecurity ( keychainPath : string , keychainPassword : string ) : Promise < void > {
160139 log . debug ( 'Setting security for iOS device' ) ;
161140 await exec ( 'security' , [ '-v' , 'list-keychains' , '-s' , keychainPath ] ) ;
162141 await exec ( 'security' , [ '-v' , 'unlock-keychain' , '-p' , keychainPassword , keychainPath ] ) ;
@@ -188,7 +167,7 @@ export interface XctestrunFileArgs {
188167 * or WebDriverAgentRunner_iphonesimulator${sdkVersion|platformVersion}-x86_64.xctestrun for simulator is not found @bootstrapPath,
189168 * then it will throw a file not found exception
190169 */
191- async function setXctestrunFile ( args : XctestrunFileArgs ) : Promise < string > {
170+ export async function setXctestrunFile ( args : XctestrunFileArgs ) : Promise < string > {
192171 const { deviceInfo, sdkVersion, bootstrapPath, wdaRemotePort, wdaBindingIP} = args ;
193172 const xctestrunFilePath = await getXctestrunFilePath ( deviceInfo , sdkVersion , bootstrapPath ) ;
194173 const xctestRunContent = await plist . parsePlistFile ( xctestrunFilePath ) ;
@@ -206,7 +185,7 @@ async function setXctestrunFile (args: XctestrunFileArgs): Promise<string> {
206185 * @param wdaBindingIP - The IP address to bind to. If not given, it binds to all interfaces.
207186 * @return returns a runner object which has USE_PORT and optionally USE_IP
208187 */
209- function getAdditionalRunContent ( platformName : string , wdaRemotePort : number | string , wdaBindingIP ?: string ) : Record < string , any > {
188+ export function getAdditionalRunContent ( platformName : string , wdaRemotePort : number | string , wdaBindingIP ?: string ) : Record < string , any > {
210189 const runner = `WebDriverAgentRunner${ isTvOS ( platformName ) ? '_tvOS' : '' } ` ;
211190 return {
212191 [ runner ] : {
@@ -225,7 +204,7 @@ function getAdditionalRunContent (platformName: string, wdaRemotePort: number |
225204 * @param sdkVersion - The Xcode SDK version of OS.
226205 * @param bootstrapPath - The folder path containing xctestrun file.
227206 */
228- async function getXctestrunFilePath ( deviceInfo : DeviceInfo , sdkVersion : string , bootstrapPath : string ) : Promise < string > {
207+ export async function getXctestrunFilePath ( deviceInfo : DeviceInfo , sdkVersion : string , bootstrapPath : string ) : Promise < string > {
229208 // First try the SDK path, for Xcode 10 (at least)
230209 const sdkBased : [ string , string ] = [
231210 path . resolve ( bootstrapPath , `${ deviceInfo . udid } _${ sdkVersion } .xctestrun` ) ,
@@ -266,7 +245,7 @@ async function getXctestrunFilePath (deviceInfo: DeviceInfo, sdkVersion: string,
266245 * @param version - The Xcode SDK version of OS.
267246 * @return returns xctestrunFilePath for given device
268247 */
269- function getXctestrunFileName ( deviceInfo : DeviceInfo , version : string ) : string {
248+ export function getXctestrunFileName ( deviceInfo : DeviceInfo , version : string ) : string {
270249 const archSuffix = deviceInfo . isRealDevice
271250 ? `os${ version } -arm64`
272251 : `simulator${ version } -${ arch ( ) === 'arm64' ? 'arm64' : 'x86_64' } ` ;
@@ -276,7 +255,7 @@ function getXctestrunFileName (deviceInfo: DeviceInfo, version: string): string
276255/**
277256 * Ensures the process is killed after the timeout
278257 */
279- async function killProcess ( name : string , proc : SubProcess | null | undefined ) : Promise < void > {
258+ export async function killProcess ( name : string , proc : SubProcess | null | undefined ) : Promise < void > {
280259 if ( ! proc || ! proc . isRunning ) {
281260 return ;
282261 }
@@ -309,14 +288,14 @@ async function killProcess (name: string, proc: SubProcess | null | undefined):
309288/**
310289 * Generate a random integer in range [low, high). `low` is inclusive and `high` is exclusive.
311290 */
312- function randomInt ( low : number , high : number ) : number {
291+ export function randomInt ( low : number , high : number ) : number {
313292 return Math . floor ( Math . random ( ) * ( high - low ) + low ) ;
314293}
315294
316295/**
317296 * Retrieves WDA upgrade timestamp. The manifest only gets modified on package upgrade.
318297 */
319- async function getWDAUpgradeTimestamp ( ) : Promise < number | null > {
298+ export async function getWDAUpgradeTimestamp ( ) : Promise < number | null > {
320299 const packageManifest = path . resolve ( getModuleRoot ( ) , 'package.json' ) ;
321300 if ( ! await fs . exists ( packageManifest ) ) {
322301 return null ;
@@ -328,7 +307,7 @@ async function getWDAUpgradeTimestamp (): Promise<number | null> {
328307/**
329308 * Kills running XCTest processes for the particular device.
330309 */
331- async function resetTestProcesses ( udid : string , isSimulator : boolean ) : Promise < void > {
310+ export async function resetTestProcesses ( udid : string , isSimulator : boolean ) : Promise < void > {
332311 const processPatterns = [ `xcodebuild.*${ udid } ` ] ;
333312 if ( isSimulator ) {
334313 processPatterns . push ( `${ udid } .*XCTRunner` ) ;
@@ -352,7 +331,7 @@ async function resetTestProcesses (udid: string, isSimulator: boolean): Promise<
352331 * from the resulting array.
353332 * @returns - the list of matched process ids.
354333 */
355- async function getPIDsListeningOnPort ( port : string | number , filteringFunc : ( ( cmdline : string ) => boolean | Promise < boolean > ) | null = null ) : Promise < string [ ] > {
334+ export async function getPIDsListeningOnPort ( port : string | number , filteringFunc : ( ( cmdline : string ) => boolean | Promise < boolean > ) | null = null ) : Promise < string [ ] > {
356335 const result : string [ ] = [ ] ;
357336 try {
358337 // This only works since Mac OS X El Capitan
@@ -384,10 +363,31 @@ async function getPIDsListeningOnPort (port: string | number, filteringFunc: ((c
384363 } ) ;
385364}
386365
387- export { updateProjectFile , resetProjectFile , setRealDeviceSecurity ,
388- getAdditionalRunContent , getXctestrunFileName ,
389- setXctestrunFile , getXctestrunFilePath , killProcess , randomInt ,
390- getWDAUpgradeTimestamp , resetTestProcesses ,
391- getPIDsListeningOnPort , killAppUsingPattern , isTvOS
392- } ;
366+ // Private functions
367+
368+ async function getPIDsUsingPattern ( pattern : string ) : Promise < string [ ] > {
369+ const args = [
370+ '-if' , // case insensitive, full cmdline match
371+ pattern ,
372+ ] ;
373+ try {
374+ const { stdout} = await exec ( 'pgrep' , args ) ;
375+ return stdout . split ( / \s + / )
376+ . map ( ( x ) => parseInt ( x , 10 ) )
377+ . filter ( _ . isInteger )
378+ . map ( ( x ) => `${ x } ` ) ;
379+ } catch ( err : any ) {
380+ log . debug ( `'pgrep ${ args . join ( ' ' ) } ' didn't detect any matching processes. Return code: ${ err . code } ` ) ;
381+ return [ ] ;
382+ }
383+ }
384+
385+ async function replaceInFile ( file : string , find : string | RegExp , replace : string ) : Promise < void > {
386+ const contents = await fs . readFile ( file , 'utf8' ) ;
387+
388+ const newContents = contents . replace ( find , replace ) ;
389+ if ( newContents !== contents ) {
390+ await fs . writeFile ( file , newContents , 'utf8' ) ;
391+ }
392+ }
393393
0 commit comments