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
138 lines
3.7 KiB
TypeScript
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();
|
|
}
|
|
}
|