Files
flipper/desktop/flipper-server-core/src/devices/ios/IOSDevice.tsx
Luke De Feo 6c5faf2932 Add command to install app to flipper server
Summary: There is a new flipper server command to install apps. For android it uses adb (via adb kit) For ios depending on idb availablity it will use idb or xcrun. Consumed in the next diff

Reviewed By: lblasa, aigoncharov

Differential Revision: D36936637

fbshipit-source-id: e09d34d840a9f3bf9136bcaf94fb8ca15dd27cbb
2022-07-07 07:50:14 -07:00

138 lines
3.7 KiB
TypeScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
import {DeviceType, timeout} from 'flipper-common';
import {ChildProcess} from 'child_process';
import {IOSBridge} from './IOSBridge';
import {ServerDevice} from '../ServerDevice';
import {FlipperServerImpl} from '../../FlipperServerImpl';
import {iOSCrashWatcher} from './iOSCrashUtils';
import {iOSLogListener} from './iOSLogListener';
export default class IOSDevice extends ServerDevice {
private recording?: {process: ChildProcess; destination: string};
private iOSBridge: IOSBridge;
readonly logListener: iOSLogListener;
readonly crashWatcher: iOSCrashWatcher;
constructor(
flipperServer: FlipperServerImpl,
iOSBridge: IOSBridge,
serial: string,
deviceType: DeviceType,
title: string,
) {
super(flipperServer, {
serial,
deviceType,
title,
os: 'iOS',
icon: 'mobile',
features: {
screenCaptureAvailable: true,
screenshotAvailable: true,
},
});
this.iOSBridge = iOSBridge;
this.logListener = new iOSLogListener(
() => this.connected,
(logEntry) => this.addLogEntry(logEntry),
this.iOSBridge,
this.serial,
this.info.deviceType,
);
// It is OK not to await the start of the log listener. We just spawn it and handle errors internally.
this.logListener
.start()
.catch((e) =>
console.error('IOSDevice.logListener.start -> unexpected error', e),
);
this.crashWatcher = new iOSCrashWatcher(this);
// It is OK not to await the start of the crash watcher. We just spawn it and handle errors internally.
this.crashWatcher
.start()
.catch((e) =>
console.error('IOSDevice.crashWatcher.start -> unexpected error', e),
);
}
async screenshot(): Promise<Buffer> {
if (!this.connected) {
return Buffer.from([]);
}
return await this.iOSBridge.screenshot(this.serial);
}
async navigateToLocation(location: string) {
return this.iOSBridge.navigate(this.serial, location).catch((err) => {
console.warn(`Failed to navigate to location ${location}:`, err);
return err;
});
}
async startScreenCapture(destination: string) {
const recording = this.recording;
if (recording) {
throw new Error(
`There is already an active recording at ${recording.destination}`,
);
}
const process = this.iOSBridge.recordVideo(this.serial, destination);
this.recording = {process, destination};
}
async stopScreenCapture(): Promise<string> {
const recording = this.recording;
if (!recording) {
throw new Error('No recording in progress');
}
const prom = new Promise<void>((resolve, _reject) => {
recording.process.on(
'exit',
async (_code: number | null, _signal: NodeJS.Signals | null) => {
resolve();
},
);
recording.process.kill('SIGINT');
});
const output: string = await timeout<void>(
5000,
prom,
'Timed out to stop a screen capture.',
)
.then(() => {
this.recording = undefined;
return recording.destination;
})
.catch((e) => {
this.recording = undefined;
console.warn('Failed to terminate iOS screen recording:', e);
throw e;
});
return output;
}
async installApp(ipaPath: string): Promise<void> {
return this.iOSBridge.installApp(
this.serial,
ipaPath,
this.flipperServer.config.paths.tempPath,
);
}
disconnect() {
if (this.recording) {
this.stopScreenCapture();
}
super.disconnect();
}
}