UNPKG

appium-xcuitest-driver

Version:

Appium driver for iOS using XCUITest for backend

111 lines 4.37 kB
"use strict"; 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