UNPKG

appium-remote-debugger

Version:
220 lines 9.37 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getModuleRoot = exports.RESPONSE_LOG_LENGTH = exports.WEB_CONTENT_BUNDLE_ID = void 0; exports.appInfoFromDict = appInfoFromDict; exports.pageArrayFromDict = pageArrayFromDict; exports.appIdsForBundle = appIdsForBundle; exports.checkParams = checkParams; exports.simpleStringify = simpleStringify; exports.convertJavascriptEvaluationResult = convertJavascriptEvaluationResult; exports.getModuleProperties = getModuleProperties; exports.canUseWebInspectorShim = canUseWebInspectorShim; const lodash_1 = __importDefault(require("lodash")); const base_driver_1 = require("@appium/base-driver"); const support_1 = require("@appium/support"); const node_fs_1 = __importDefault(require("node:fs")); const node_path_1 = __importDefault(require("node:path")); const MODULE_NAME = 'appium-remote-debugger'; exports.WEB_CONTENT_BUNDLE_ID = 'com.apple.WebKit.WebContent'; const INACTIVE_APP_CODE = 0; // values for the page `WIRTypeKey` entry const ACCEPTED_PAGE_TYPES = [ 'WIRTypeWeb', // up to iOS 11.3 'WIRTypeWebPage', // iOS 11.4 'WIRTypePage', // iOS 11.4 webview ]; exports.RESPONSE_LOG_LENGTH = 100; /** * Takes a dictionary from the remote debugger and converts it into a more * manageable AppInfo object with understandable keys. * * @param dict - Dictionary from the remote debugger containing application information. * @returns A tuple containing the application ID and the AppInfo object. */ function appInfoFromDict(dict) { const id = dict.WIRApplicationIdentifierKey; const isProxy = lodash_1.default.isString(dict.WIRIsApplicationProxyKey) ? dict.WIRIsApplicationProxyKey.toLowerCase() === 'true' : dict.WIRIsApplicationProxyKey; // automation enabled can be either from the keys // - WIRRemoteAutomationEnabledKey (boolean) // - WIRAutomationAvailabilityKey (string or boolean) let isAutomationEnabled = !!dict.WIRRemoteAutomationEnabledKey; if (lodash_1.default.has(dict, 'WIRAutomationAvailabilityKey')) { if (lodash_1.default.isString(dict.WIRAutomationAvailabilityKey)) { isAutomationEnabled = dict.WIRAutomationAvailabilityKey === 'WIRAutomationAvailabilityUnknown' ? 'Unknown' : dict.WIRAutomationAvailabilityKey === 'WIRAutomationAvailabilityAvailable'; } else { isAutomationEnabled = !!dict.WIRAutomationAvailabilityKey; } } const entry = { id, isProxy, name: dict.WIRApplicationNameKey, bundleId: dict.WIRApplicationBundleIdentifierKey, hostId: dict.WIRHostApplicationIdentifierKey, isActive: dict.WIRIsApplicationActiveKey !== INACTIVE_APP_CODE, isAutomationEnabled, }; return [id, entry]; } /** * Takes a dictionary from the remote debugger and converts it into an array * of Page objects with understandable keys. Filters out non-web pages. * * @param pageDict - Dictionary from the remote debugger containing page information. * @returns An array of Page objects representing the available pages. */ function pageArrayFromDict(pageDict) { return (lodash_1.default.values(pageDict) // count only WIRTypeWeb pages and ignore all others (WIRTypeJavaScript etc) .filter((dict) => lodash_1.default.isUndefined(dict.WIRTypeKey) || ACCEPTED_PAGE_TYPES.includes(dict.WIRTypeKey)) .map((dict) => ({ id: dict.WIRPageIdentifierKey, title: dict.WIRTitleKey, url: dict.WIRURLKey, isKey: !lodash_1.default.isUndefined(dict.WIRConnectionIdentifierKey), }))); } /** * Finds all application identifier keys that match the given bundle ID. * If no matches are found and the bundle ID is not WEB_CONTENT_BUNDLE_ID, * falls back to searching for WEB_CONTENT_BUNDLE_ID. * * @param bundleId - The bundle identifier to search for. * @param appDict - The application dictionary to search in. * @returns An array of unique application identifier keys matching the bundle ID. */ function appIdsForBundle(bundleId, appDict) { const appIds = lodash_1.default.toPairs(appDict) .filter(([, data]) => data.bundleId === bundleId) .map(([key]) => key); // if nothing is found, try to get the generic app if (appIds.length === 0 && bundleId !== exports.WEB_CONTENT_BUNDLE_ID) { return appIdsForBundle(exports.WEB_CONTENT_BUNDLE_ID, appDict); } return lodash_1.default.uniq(appIds); } /** * Validates that all parameters in the provided object have non-nil values. * Throws an error if any parameters are missing (null or undefined). * * @template T - The type of the parameters object. * @param params - An object containing parameters to validate. * @returns The same parameters object if all values are valid. * @throws Error if any parameters are missing, listing all missing parameter names. */ function checkParams(params) { // check if all parameters have a value const errors = lodash_1.default.toPairs(params) .filter(([, value]) => lodash_1.default.isNil(value)) .map(([param]) => param); if (errors.length) { throw new Error(`Missing ${support_1.util.pluralize('parameter', errors.length)}: ${errors.join(', ')}`); } return params; } /** * Converts a value to a JSON string, removing noisy function properties * that can muddy the logs. * * @param value - The value to stringify. * @param multiline - If true, formats the JSON with indentation. Defaults to false. * @returns A JSON string representation of the value with noisy properties removed. */ function simpleStringify(value, multiline = false) { if (!value) { return JSON.stringify(value); } const cleanValue = removeNoisyProperties(lodash_1.default.clone(value)); return multiline ? JSON.stringify(cleanValue, null, 2) : JSON.stringify(cleanValue); } /** * Converts the result from a JavaScript evaluation in the remote debugger * into a usable format. Handles errors, serialization, and cleans up noisy * function properties. * * @param res - The raw result from the remote debugger's JavaScript evaluation. * @returns The cleaned and converted result value. * @throws Error if the result is undefined, has an unexpected type, or contains * an error status code. */ function convertJavascriptEvaluationResult(res) { if (lodash_1.default.isUndefined(res)) { throw new Error(`Did not get OK result from remote debugger. Result was: ${lodash_1.default.truncate(simpleStringify(res), { length: exports.RESPONSE_LOG_LENGTH })}`); } else if (lodash_1.default.isString(res)) { try { res = JSON.parse(res); } catch { // we might get a serialized object, but we might not // if we get here, it is just a value } } else if (!lodash_1.default.isObject(res)) { throw new Error(`Result has unexpected type: (${typeof res}).`); } if (res.status && res.status !== 0) { // we got some form of error. throw (0, base_driver_1.errorFromMJSONWPStatusCode)(res.status, res.value.message || res.value); } // with either have an object with a `value` property (even if `null`), // or a plain object const value = lodash_1.default.has(res, 'value') ? res.value : res; return removeNoisyProperties(value); } /** * Calculates the path to the current module's root folder. * The result is memoized for performance. * * @returns The full path to the module root directory. * @throws Error if the module root folder cannot be determined. */ exports.getModuleRoot = lodash_1.default.memoize(function getModuleRoot() { const root = support_1.node.getModuleRootSync(MODULE_NAME, __filename); if (!root) { throw new Error(`Cannot find the root folder of the ${MODULE_NAME} Node.js module`); } return root; }); /** * Reads and parses the package.json file from the module root. * * @returns The parsed package.json contents as a StringRecord. */ function getModuleProperties() { const fullPath = node_path_1.default.resolve((0, exports.getModuleRoot)(), 'package.json'); return JSON.parse(node_fs_1.default.readFileSync(fullPath, 'utf8')); } /** * Determines if the WebInspector shim can be used based on the provided iOS platform version. * @param platformVersion - The iOS platform version string (e.g., "18.0", "17.5.1") * @returns true if the WebInspector shim can be used, false otherwise */ function canUseWebInspectorShim(platformVersion) { return !!platformVersion && support_1.util.compareVersions(platformVersion, '>=', '18.0'); } /** * Removes noisy function properties from an object that can muddy the logs. * These properties are often added by JavaScript number objects and similar. * * @param obj - The object to clean. * @returns The cleaned object. */ function removeNoisyProperties(obj) { if (lodash_1.default.isObject(obj)) { for (const property of ['ceil', 'clone', 'floor', 'round', 'scale', 'toString']) { delete obj[property]; } } return obj; } //# sourceMappingURL=utils.js.map