appium-xcuitest-driver
Version:
Appium driver for iOS using XCUITest for backend
141 lines • 6.16 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const lodash_1 = __importDefault(require("lodash"));
const driver_1 = require("../driver");
const driver_2 = require("appium/driver");
const support_1 = require("appium/support");
/**
* Checks if script expects a particular parameter (either optional or required).
* @template {keyof XCUITestDriver.executeMethodMap} Script
* @param {Script} script - Script name
* @param {string} param - Parameter name
* @returns {boolean}
*/
function executeMethodExpectsParam(script, param) {
/** @type {ReadonlyArray<string>|undefined} */
let required;
/** @type {ReadonlyArray<string>|undefined} */
let optional;
const execMethodDef = driver_1.XCUITestDriver.executeMethodMap[script];
if ('params' in execMethodDef) {
if ('required' in execMethodDef.params) {
required = execMethodDef.params.required;
}
if ('optional' in execMethodDef.params) {
optional = execMethodDef.params.optional;
}
}
const allParams = new Set(lodash_1.default.flatten([...(required ?? []), ...(optional ?? [])]));
return allParams.has(param);
}
/**
* @param {any} script
* @returns {script is keyof XCUITestDriver.executeMethodMap}
*/
function isExecuteMethod(script) {
return script in driver_1.XCUITestDriver.executeMethodMap;
}
/**
* Massages the arguments going into an execute method.
* @param {keyof XCUITestDriver.executeMethodMap} script
* @param {ExecuteMethodArgs} [args]
* @returns {StringRecord<unknown>}
*/
function preprocessExecuteMethodArgs(script, args) {
if (lodash_1.default.isArray(args)) {
args = lodash_1.default.first(args);
}
const executeMethodArgs = /** @type {StringRecord<unknown>} */ (args ?? {});
/**
* Renames the deprecated `element` key to `elementId`. Historically,
* all of the pre-Execute-Method-Map execute methods accepted an `element` _or_ and `elementId` param.
* This assigns the `element` value to `elementId` if `elementId` is not already present.
*/
if (!('elementId' in executeMethodArgs) && 'element' in executeMethodArgs) {
executeMethodArgs.elementId = executeMethodArgs.element;
delete executeMethodArgs.element;
}
/**
* Automatically unwraps the `elementId` prop _if and only if_ the execute method expects it.
*
* Most of these Execute Methods (typically beginning with `mobile*`) will accept an `Element|string` for `elementId`, in practice they will only ever get a `string`. `Element|string` in the method's docstring is simply for documentation purposes.
*/
if ('elementId' in executeMethodArgs && executeMethodExpectsParam(script, 'elementId')) {
executeMethodArgs.elementId = support_1.util.unwrapElement(
/** @type {import('@appium/types').Element|string} */ (executeMethodArgs.elementId));
}
return executeMethodArgs;
}
exports.default = {
/**
* Collect the response of an async script execution
* @this {XCUITestDriver}
* @deprecated
* @privateRemarks It's unclear what this is for. Don't use it.
*/
async receiveAsyncResponse(status, value) {
this.log.debug(`Received async response: ${JSON.stringify(value)}`);
if (!support_1.util.hasValue(this.asyncPromise)) {
this.log.warn(`Received async response when we were not expecting one! ` +
`Response was: ${JSON.stringify(value)}`);
return;
}
if (support_1.util.hasValue(status) && status !== 0) {
// MJSONWP
return this.asyncPromise.reject((0, driver_2.errorFromCode)(status, value.message));
}
if (!support_1.util.hasValue(status) && value && lodash_1.default.isString(value.error)) {
// W3C
return this.asyncPromise.reject((0, driver_2.errorFromW3CJsonCode)(value.error, value.message, value.stacktrace));
}
return this.asyncPromise.resolve(value);
},
/**
* @template {ExecuteMethodArgs} [TArgs = unknown[]]
* @template [TReturn = unknown]
* @param {string} script - Either a script to run, or in the case of an Execute Method, the name of the script to execute.
* @param {TArgs} [args]
* @this {XCUITestDriver}
* @returns {Promise<TReturn>}
*/
async execute(script, args) {
// TODO: create a type that converts args to the parameters of the associated method using the `command` prop of `executeMethodMap`
script = script.trim().replace(/^mobile:\s*/, 'mobile: ');
if (isExecuteMethod(script)) {
const executeMethodArgs = preprocessExecuteMethodArgs(script, args);
return await this.executeMethod(script, [executeMethodArgs]);
}
else if (this.isWebContext()) {
const atomsArgs = this.convertElementsForAtoms(/** @type {readonly any[]} */ (args));
const result = await this.executeAtom('execute_script', [script, atomsArgs]);
return this.cacheWebElements(result);
}
else {
throw new driver_2.errors.NotImplementedError();
}
},
/**
* @this {XCUITestDriver}
* @group Mobile Web Only
*/
async executeAsync(script, args) {
if (!this.isWebContext()) {
throw new driver_2.errors.NotImplementedError();
}
args = this.convertElementsForAtoms(args);
this.asyncWaitMs = this.asyncWaitMs || 0;
const promise = ( /** @type {import('appium-remote-debugger').RemoteDebugger} */(this.remote)).executeAtomAsync('execute_async_script', [script, args, this.asyncWaitMs], this.curWebFrames);
return this.cacheWebElements(await this.waitForAtom(promise));
},
};
/**
* @template [T=any]
* @typedef {import('@appium/types').StringRecord<T>} StringRecord
*/
/**
* @typedef {readonly any[] | readonly [StringRecord] | Readonly<StringRecord>} ExecuteMethodArgs
*/
//# sourceMappingURL=execute.js.map