1*a272999eSBartosz Kaszubowskiimport spawnAsync from '@expo/spawn-async'; 2eeffdb10STomasz Sapetaimport { spawn } from 'child_process'; 3eeffdb10STomasz Sapetaimport { Transform, TransformCallback, TransformOptions } from 'stream'; 4eeffdb10STomasz Sapeta 5eeffdb10STomasz Sapeta/** 6eeffdb10STomasz Sapeta * Starts an arbitrary iOS simulator so that simctl can reference a "booted" simulator. 7eeffdb10STomasz Sapeta */ 8eeffdb10STomasz Sapetaexport async function startSimulatorAsync(): Promise<void> { 9eeffdb10STomasz Sapeta try { 10eeffdb10STomasz Sapeta await spawnAsync('xcrun', ['instruments', '-w', 'iPhone X (11.2) [']); 11eeffdb10STomasz Sapeta } catch (e) { 12eeffdb10STomasz Sapeta // Instruments exits with an expected error 13eeffdb10STomasz Sapeta if (!e.stderr.includes('Instruments Usage Error')) { 14eeffdb10STomasz Sapeta throw e; 15eeffdb10STomasz Sapeta } 16eeffdb10STomasz Sapeta } 17eeffdb10STomasz Sapeta} 18eeffdb10STomasz Sapeta 19eeffdb10STomasz Sapetaexport async function installSimulatorAppAsync( 20eeffdb10STomasz Sapeta simulatorId: string, 21eeffdb10STomasz Sapeta archivePath: string 22eeffdb10STomasz Sapeta): Promise<void> { 23eeffdb10STomasz Sapeta try { 24eeffdb10STomasz Sapeta await spawnAsync('xcrun', ['simctl', 'install', simulatorId, archivePath]); 25eeffdb10STomasz Sapeta } catch (e) { 26*a272999eSBartosz Kaszubowski const error = new Error(e.stderr); 27eeffdb10STomasz Sapeta (error as any).status = e.status; 28eeffdb10STomasz Sapeta throw error; 29eeffdb10STomasz Sapeta } 30eeffdb10STomasz Sapeta} 31eeffdb10STomasz Sapeta 32eeffdb10STomasz Sapetaexport async function launchSimulatorAppAsync( 33eeffdb10STomasz Sapeta simulatorId: string, 34eeffdb10STomasz Sapeta bundleIdentifier: string 35eeffdb10STomasz Sapeta): Promise<void> { 36eeffdb10STomasz Sapeta try { 37eeffdb10STomasz Sapeta await spawnAsync('xcrun', ['simctl', 'launch', simulatorId, bundleIdentifier]); 38eeffdb10STomasz Sapeta } catch (e) { 39*a272999eSBartosz Kaszubowski const error = new Error(e.stderr); 40eeffdb10STomasz Sapeta (error as any).status = e.status; 41eeffdb10STomasz Sapeta throw error; 42eeffdb10STomasz Sapeta } 43eeffdb10STomasz Sapeta} 44eeffdb10STomasz Sapeta 45eeffdb10STomasz Sapetaexport function getSimulatorLogProcess(simulatorId: string, predicate?: string) { 46eeffdb10STomasz Sapeta return spawn( 47eeffdb10STomasz Sapeta 'xcrun', 48eeffdb10STomasz Sapeta [ 49eeffdb10STomasz Sapeta 'simctl', 50eeffdb10STomasz Sapeta 'spawn', 51eeffdb10STomasz Sapeta simulatorId, 52eeffdb10STomasz Sapeta 'log', 53eeffdb10STomasz Sapeta 'stream', 54eeffdb10STomasz Sapeta '--style', 55eeffdb10STomasz Sapeta 'json', 56eeffdb10STomasz Sapeta ...(predicate ? ['--predicate', predicate] : []), 57eeffdb10STomasz Sapeta ], 58eeffdb10STomasz Sapeta { 59eeffdb10STomasz Sapeta stdio: ['ignore', 'pipe', 'inherit'], 60eeffdb10STomasz Sapeta } 61eeffdb10STomasz Sapeta ); 62eeffdb10STomasz Sapeta} 63eeffdb10STomasz Sapeta 64eeffdb10STomasz Sapetaexport class IOSLogStream extends Transform { 65eeffdb10STomasz Sapeta constructor(options?: TransformOptions) { 66eeffdb10STomasz Sapeta super({ ...options, objectMode: true }); 67eeffdb10STomasz Sapeta } 68eeffdb10STomasz Sapeta 69eeffdb10STomasz Sapeta _transform(data: Buffer, encoding: string, callback: TransformCallback): void { 70eeffdb10STomasz Sapeta // In practice, we receive each log entry as a separate chunk and can test if they are valid, 71eeffdb10STomasz Sapeta // JSON-formatted log entries 72eeffdb10STomasz Sapeta let entry; 73eeffdb10STomasz Sapeta try { 74eeffdb10STomasz Sapeta entry = JSON.parse(data.toString('utf8')); 75*a272999eSBartosz Kaszubowski } catch {} 76eeffdb10STomasz Sapeta 77eeffdb10STomasz Sapeta if (entry?.eventMessage) { 78eeffdb10STomasz Sapeta this.push(entry); 79eeffdb10STomasz Sapeta } 80eeffdb10STomasz Sapeta callback(); 81eeffdb10STomasz Sapeta } 82eeffdb10STomasz Sapeta} 83