appium-xcuitest-driver
Version:
Appium driver for iOS using XCUITest for backend
111 lines • 4.37 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CrashReportsClient = void 0;
const remotexpc_utils_1 = require("./remotexpc-utils");
const logger_1 = require("../logger");
const CRASH_REPORT_EXTENSIONS = ['.ips'];
const MAX_FILES_IN_ERROR = 10;
/**
* Lists and exports device crash reports (`.ips`) on real hardware over RemoteXPC.
*
* Requires **iOS/tvOS 18+** and the optional **`appium-ios-remotexpc`** package.
* Used by {@link IOSCrashLog} for BiDi / `crashlog` collection on real devices.
*/
class CrashReportsClient {
crashReportsService;
constructor(crashReportsService) {
this.crashReportsService = crashReportsService;
}
/**
* Opens a RemoteXPC crash-reports service for the given UDID.
*
* @param udid - Real device UDID
* @param useRemoteXPC - Must be `true`; callers derive this from `isIos18OrNewer` / session options
* @throws {Error} If `useRemoteXPC` is false, or RemoteXPC setup fails
*/
static async create(udid, useRemoteXPC) {
if (!useRemoteXPC) {
throw new Error('Real device crash report access requires iOS/tvOS 18 or newer with the appium-ios-remotexpc ' +
'package installed.');
}
try {
const Services = await (0, remotexpc_utils_1.getRemoteXPCServices)();
const crashReportsService = await Services.startCrashReportsService(udid);
return new CrashReportsClient(crashReportsService);
}
catch (err) {
throw (0, remotexpc_utils_1.wrapRemoteXPCConnectionError)(err, 'Failed to create crash reports client via RemoteXPC');
}
}
/**
* @returns Basenames of crash report files on the device (e.g. `MyApp-2024-01-01-120000.ips`)
*/
async listCrashes() {
const allFiles = await this._listCrashReportPaths();
return allFiles.map((filePath) => {
const parts = filePath.split('/');
return parts[parts.length - 1];
});
}
/**
* Pulls a single crash report off the device into a local folder.
*
* @param name - Crash file basename as returned by {@link CrashReportsClient.listCrashes}
* @param dstFolder - Existing local directory to write into
* @throws {Error} If the named report is not found on the device
*/
async exportCrash(name, dstFolder) {
const allFiles = await this._listCrashReportPaths();
const fullPath = allFiles.find((p) => p.endsWith(`/${name}`) || p === `/${name}`);
if (!fullPath) {
const filesList = allFiles.slice(0, MAX_FILES_IN_ERROR).join(', ');
const hasMore = allFiles.length > MAX_FILES_IN_ERROR;
throw new Error(`Crash report '${name}' not found on device. ` +
`Available files: ${filesList}${hasMore ? `, ... and ${allFiles.length - MAX_FILES_IN_ERROR} more` : ''}`);
}
await this.crashReportsService.pull(dstFolder, fullPath);
}
/**
* Tears down the crash-reports service.
*/
async close() {
try {
this.crashReportsService.close();
}
catch (err) {
logger_1.log.warn(`Error closing crash reports service: ${err.message}`);
}
}
/**
* Walk the crash-reports tree and collect `.ips` paths without listing the full tree upfront.
*/
async _listCrashReportPaths() {
const results = [];
await this._collectCrashReportPaths('/', results);
return results;
}
async _collectCrashReportPaths(dirPath, results) {
let children;
try {
children = await this.crashReportsService.ls(dirPath, 1);
}
catch {
return;
}
for (const entryPath of children) {
const basename = entryPath.split('/').pop() ?? entryPath;
if (CRASH_REPORT_EXTENSIONS.some((ext) => basename.endsWith(ext))) {
results.push(entryPath);
continue;
}
try {
await this._collectCrashReportPaths(entryPath, results);
}
catch {
// Skip entries we can't access or that aren't directories
}
}
}
}
exports.CrashReportsClient = CrashReportsClient;
//# sourceMappingURL=crash-reports-client.js.map