@appium/base-driver
Version:
Base driver class for Appium drivers
635 lines • 23 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.NO_SESSION_ID_COMMANDS = exports.ALL_COMMANDS = exports.METHOD_MAP = void 0;
exports.routeToCommandName = routeToCommandName;
const lodash_1 = __importDefault(require("lodash"));
const constants_1 = require("../constants");
const path_to_regexp_1 = require("path-to-regexp");
const lru_cache_1 = require("lru-cache");
const COMMAND_NAMES_CACHE = new lru_cache_1.LRUCache({
max: 1024,
});
/**
* Define the routes: mapping of HTTP methods to particular driver commands, and
* any parameters that are expected in a request. Parameters can be `required` or
* `optional`.
*/
exports.METHOD_MAP = {
// #region W3C WebDriver
// https://www.w3.org/TR/webdriver2/
'/session': {
POST: {
command: 'createSession',
payloadParams: {
optional: ['capabilities', 'capabilities', 'capabilities'],
},
},
},
'/session/:sessionId': {
// TODO: JSONWP route, remove in the future
GET: { command: 'getSession', deprecated: true },
DELETE: { command: 'deleteSession' },
},
'/status': {
GET: { command: 'getStatus' },
},
'/session/:sessionId/timeouts': {
GET: { command: 'getTimeouts' },
POST: {
command: 'timeouts',
payloadParams: {
optional: ['type', 'ms', 'script', 'pageLoad', 'implicit'],
},
},
},
'/session/:sessionId/url': {
GET: { command: 'getUrl' },
POST: { command: 'setUrl', payloadParams: { required: ['url'] } },
},
'/session/:sessionId/forward': {
POST: { command: 'forward' },
},
'/session/:sessionId/back': {
POST: { command: 'back' },
},
'/session/:sessionId/refresh': {
POST: { command: 'refresh' },
},
'/session/:sessionId/title': {
GET: { command: 'title' },
},
'/session/:sessionId/window': {
GET: { command: 'getWindowHandle' },
POST: {
command: 'setWindow',
payloadParams: {
required: ['handle'],
},
},
DELETE: { command: 'closeWindow' },
},
'/session/:sessionId/window/handles': {
GET: { command: 'getWindowHandles' },
},
'/session/:sessionId/window/new': {
POST: { command: 'createNewWindow', payloadParams: { optional: ['type'] } },
},
'/session/:sessionId/frame': {
POST: { command: 'setFrame', payloadParams: { required: ['id'] } },
},
'/session/:sessionId/frame/parent': {
POST: { command: 'switchToParentFrame' },
},
'/session/:sessionId/window/rect': {
GET: { command: 'getWindowRect' },
POST: {
command: 'setWindowRect',
payloadParams: { optional: ['x', 'y', 'width', 'height'] },
},
},
'/session/:sessionId/window/maximize': {
POST: { command: 'maximizeWindow' },
},
'/session/:sessionId/window/minimize': {
POST: { command: 'minimizeWindow' },
},
'/session/:sessionId/window/fullscreen': {
POST: { command: 'fullScreenWindow' },
},
'/session/:sessionId/element/active': {
GET: { command: 'active' },
},
'/session/:sessionId/element/:elementId/shadow': {
GET: { command: 'elementShadowRoot' },
},
'/session/:sessionId/element': {
POST: {
command: 'findElement',
payloadParams: { required: ['using', 'value'] },
},
},
'/session/:sessionId/elements': {
POST: {
command: 'findElements',
payloadParams: { required: ['using', 'value'] },
},
},
'/session/:sessionId/element/:elementId/element': {
POST: {
command: 'findElementFromElement',
payloadParams: { required: ['using', 'value'] },
},
},
'/session/:sessionId/element/:elementId/elements': {
POST: {
command: 'findElementsFromElement',
payloadParams: { required: ['using', 'value'] },
},
},
'/session/:sessionId/shadow/:shadowId/element': {
POST: {
command: 'findElementFromShadowRoot',
payloadParams: { required: ['using', 'value'] },
},
},
'/session/:sessionId/shadow/:shadowId/elements': {
POST: {
command: 'findElementsFromShadowRoot',
payloadParams: { required: ['using', 'value'] },
},
},
'/session/:sessionId/element/:elementId/selected': {
GET: { command: 'elementSelected' },
},
'/session/:sessionId/element/:elementId/displayed': {
GET: { command: 'elementDisplayed' },
},
'/session/:sessionId/element/:elementId/attribute/:name': {
GET: { command: 'getAttribute' },
},
'/session/:sessionId/element/:elementId/property/:name': {
GET: { command: 'getProperty' },
},
'/session/:sessionId/element/:elementId/css/:propertyName': {
GET: { command: 'getCssProperty' },
},
'/session/:sessionId/element/:elementId/text': {
GET: { command: 'getText' },
},
'/session/:sessionId/element/:elementId/name': {
GET: { command: 'getName' },
},
'/session/:sessionId/element/:elementId/rect': {
GET: { command: 'getElementRect' },
},
'/session/:sessionId/element/:elementId/enabled': {
GET: { command: 'elementEnabled' },
},
'/session/:sessionId/element/:elementId/computedrole': {
GET: { command: 'getComputedRole' },
},
'/session/:sessionId/element/:elementId/computedlabel': {
GET: { command: 'getComputedLabel' },
},
'/session/:sessionId/element/:elementId/click': {
POST: { command: 'click' },
},
'/session/:sessionId/element/:elementId/clear': {
POST: { command: 'clear' },
},
'/session/:sessionId/element/:elementId/value': {
POST: {
command: 'setValue',
payloadParams: {
required: ['text'],
},
},
},
'/session/:sessionId/source': {
GET: { command: 'getPageSource' },
},
'/session/:sessionId/execute/sync': {
POST: { command: 'execute', payloadParams: { required: ['script', 'args'] } },
},
'/session/:sessionId/execute/async': {
POST: {
command: 'executeAsync',
payloadParams: { required: ['script', 'args'] },
},
},
'/session/:sessionId/cookie': {
GET: { command: 'getCookies' },
POST: { command: 'setCookie', payloadParams: { required: ['cookie'] } },
DELETE: { command: 'deleteCookies' },
},
'/session/:sessionId/cookie/:name': {
GET: { command: 'getCookie' },
DELETE: { command: 'deleteCookie' },
},
'/session/:sessionId/actions': {
POST: { command: 'performActions', payloadParams: { required: ['actions'] } },
DELETE: { command: 'releaseActions' },
},
'/session/:sessionId/alert/dismiss': {
POST: { command: 'postDismissAlert' },
},
'/session/:sessionId/alert/accept': {
POST: { command: 'postAcceptAlert' },
},
'/session/:sessionId/alert/text': {
GET: { command: 'getAlertText' },
POST: {
command: 'setAlertText',
payloadParams: {
required: ['text'],
},
},
},
'/session/:sessionId/screenshot': {
GET: { command: 'getScreenshot' },
},
'/session/:sessionId/element/:elementId/screenshot': {
GET: { command: 'getElementScreenshot' },
},
'/session/:sessionId/print': {
POST: {
command: 'printPage',
payloadParams: {
optional: [
'orientation',
'scale',
'background',
'page',
'margin',
'shrinkToFit',
'pageRanges',
],
}
}
},
// #endregion
// #region JSONWP
// https://www.selenium.dev/documentation/legacy/json_wire_protocol/
'/session/:sessionId/ime/available_engines': {
GET: { command: 'availableIMEEngines', deprecated: true },
},
'/session/:sessionId/ime/active_engine': {
GET: { command: 'getActiveIMEEngine', deprecated: true },
},
'/session/:sessionId/ime/activated': {
GET: { command: 'isIMEActivated', deprecated: true },
},
'/session/:sessionId/ime/deactivate': {
POST: { command: 'deactivateIMEEngine', deprecated: true },
},
'/session/:sessionId/ime/activate': {
POST: {
command: 'activateIMEEngine',
payloadParams: { required: ['engine'] },
deprecated: true,
},
},
'/session/:sessionId/orientation': {
GET: { command: 'getOrientation' },
POST: {
command: 'setOrientation',
payloadParams: { required: ['orientation'] }
},
},
'/session/:sessionId/location': {
GET: {
command: 'getGeoLocation',
deprecated: true,
},
POST: {
command: 'setGeoLocation',
payloadParams: { required: ['location'] },
deprecated: true,
},
},
// #endregion
// #region MJSONWP
// https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md
'/session/:sessionId/rotation': {
GET: { command: 'getRotation' },
POST: { command: 'setRotation', payloadParams: { required: ['x', 'y', 'z'] } },
},
'/session/:sessionId/context': {
GET: { command: 'getCurrentContext' },
POST: { command: 'setContext', payloadParams: { required: ['name'] } },
},
'/session/:sessionId/contexts': {
GET: { command: 'getContexts' },
},
'/session/:sessionId/network_connection': {
GET: { command: 'getNetworkConnection', deprecated: true },
POST: {
command: 'setNetworkConnection',
payloadParams: { unwrap: 'parameters', required: ['type'] },
deprecated: true,
},
},
// #endregion
// #region Appium
'/appium/sessions': {
GET: { command: 'getAppiumSessions' },
},
'/session/:sessionId/appium/capabilities': {
GET: { command: 'getAppiumSessionCapabilities' }
},
'/session/:sessionId/appium/settings': {
POST: { command: 'updateSettings', payloadParams: { required: ['settings'] } },
GET: { command: 'getSettings' },
},
'/session/:sessionId/appium/commands': {
GET: { command: 'listCommands' },
},
'/session/:sessionId/appium/extensions': {
GET: { command: 'listExtensions' },
},
'/session/:sessionId/appium/events': {
POST: { command: 'getLogEvents', payloadParams: { optional: ['type'] } },
},
'/session/:sessionId/appium/log_event': {
POST: {
command: 'logCustomEvent',
payloadParams: { required: ['vendor', 'event'] },
},
},
'/session/:sessionId/appium/device/system_time': {
GET: { command: 'getDeviceTime' },
POST: { command: 'getDeviceTime', payloadParams: { optional: ['format'] } },
},
'/session/:sessionId/appium/device/activate_app': {
POST: {
command: 'activateApp',
payloadParams: {
required: [['appId'], ['bundleId']],
optional: ['options'],
},
},
},
'/session/:sessionId/appium/device/terminate_app': {
POST: {
command: 'terminateApp',
payloadParams: {
required: [['appId'], ['bundleId']],
optional: ['options'],
},
},
},
'/session/:sessionId/appium/device/app_state': {
POST: {
command: 'queryAppState',
payloadParams: {
required: [['appId'], ['bundleId']],
},
},
},
'/session/:sessionId/appium/device/install_app': {
POST: {
command: 'installApp',
payloadParams: {
required: ['appPath'],
optional: ['options'],
},
},
},
'/session/:sessionId/appium/device/remove_app': {
POST: {
command: 'removeApp',
payloadParams: {
required: [['appId'], ['bundleId']],
optional: ['options'],
},
},
},
'/session/:sessionId/appium/device/app_installed': {
POST: {
command: 'isAppInstalled',
payloadParams: {
required: [['appId'], ['bundleId']],
},
},
},
'/session/:sessionId/appium/device/hide_keyboard': {
POST: {
command: 'hideKeyboard',
payloadParams: { optional: ['strategy', 'key', 'keyCode', 'keyName'] },
},
},
'/session/:sessionId/appium/device/is_keyboard_shown': {
GET: { command: 'isKeyboardShown' },
},
'/session/:sessionId/appium/device/push_file': {
POST: { command: 'pushFile', payloadParams: { required: ['path', 'data'] } },
},
'/session/:sessionId/appium/device/pull_file': {
POST: { command: 'pullFile', payloadParams: { required: ['path'] } },
},
'/session/:sessionId/appium/device/pull_folder': {
POST: { command: 'pullFolder', payloadParams: { required: ['path'] } },
},
// #endregion
// #region Unknown
'/session/:sessionId/receive_async_response': {
POST: {
command: 'receiveAsyncResponse',
payloadParams: { required: ['status', 'value'] },
deprecated: true,
},
},
'/session/:sessionId/element/:elementId': {
GET: {},
},
// #endregion
// #region Other Protocols
// Selenium/Chromium browsers
'/session/:sessionId/se/log': {
POST: { command: 'getLog', payloadParams: { required: ['type'] } },
},
'/session/:sessionId/se/log/types': {
GET: { command: 'getLogTypes' },
},
// Chromium devtools
// https://chromium.googlesource.com/chromium/src/+/master/chrome/test/chromedriver/server/http_handler.cc
'/session/:sessionId/:vendor/cdp/execute': {
POST: { command: 'executeCdp', payloadParams: { required: ['cmd', 'params'] } },
},
// Reporting
// https://www.w3.org/TR/reporting-1/
'/session/:sessionId/reporting/generate_test_report': {
POST: {
command: 'generateTestReport',
payloadParams: { required: ['message'], optional: ['group'] },
},
},
// Permissions
// https://www.w3.org/TR/permissions/
'/session/:sessionId/permissions': {
POST: { command: 'setPermissions', payloadParams: { required: ['descriptor', 'state'] } },
},
// Device Posture
// https://www.w3.org/TR/device-posture/
'/session/:sessionId/deviceposture': {
POST: { command: 'setDevicePosture', payloadParams: { required: ['posture'] } },
DELETE: { command: 'clearDevicePosture' },
},
// Generic Sensor
// https://www.w3.org/TR/generic-sensor/
'/session/:sessionId/sensor': {
POST: {
command: 'createVirtualSensor',
payloadParams: {
required: ['type'],
optional: ['connected', 'maxSamplingFrequency', 'minSamplingFrequency'],
},
},
},
'/session/:sessionId/sensors/:sensorType': {
GET: { command: 'getVirtualSensorInfo' },
POST: { command: 'updateVirtualSensorReading', payloadParams: { required: ['reading'] } },
DELETE: { command: 'deleteVirtualSensor' },
},
// Custom Handlers
// https://html.spec.whatwg.org/multipage/system-state.html#user-agent-automation
'/session/:sessionId/custom-handlers/set-mode': {
POST: { command: 'setRPHRegistrationMode', payloadParams: { required: ['mode'] } },
},
// Webauthn
// https://www.w3.org/TR/webauthn-2/#sctn-automation-add-virtual-authenticator
'/session/:sessionId/webauthn/authenticator': {
POST: {
command: 'addVirtualAuthenticator',
payloadParams: {
required: ['protocol', 'transport'],
optional: ['hasResidentKey', 'hasUserVerification', 'isUserConsenting', 'isUserVerified'],
},
},
},
'/session/:sessionId/webauthn/authenticator/:authenticatorId': {
DELETE: {
command: 'removeVirtualAuthenticator',
},
},
'/session/:sessionId/webauthn/authenticator/:authenticatorId/credential': {
POST: {
command: 'addAuthCredential',
payloadParams: {
required: ['credentialId', 'isResidentCredential', 'rpId', 'privateKey'],
optional: ['userHandle', 'signCount'],
},
},
},
'/session/:sessionId/webauthn/authenticator/:authenticatorId/credentials': {
GET: { command: 'getAuthCredential' },
DELETE: { command: 'removeAllAuthCredentials' },
},
'/session/:sessionId/webauthn/authenticator/:authenticatorId/credentials/:credentialId': {
DELETE: { command: 'removeAuthCredential' },
},
'/session/:sessionId/webauthn/authenticator/:authenticatorId/uv': {
POST: {
command: 'setUserAuthVerified',
payloadParams: {
required: ['isUserVerified'],
},
},
},
// Secure Payment Confirmation
// https://www.w3.org/TR/secure-payment-confirmation/
'/session/:sessionId/secure-payment-confirmation/set-mode': {
POST: { command: 'setSPCTransactionMode', payloadParams: { required: ['mode'] } },
},
// Federated Credential Management
// https://www.w3.org/TR/fedcm-1/
'/session/:sessionId/fedcm/canceldialog': {
POST: { command: 'fedCMCancelDialog' },
},
'/session/:sessionId/fedcm/selectaccount': {
POST: { command: 'fedCMSelectAccount', payloadParams: { required: ['accountIndex'] } },
},
'/session/:sessionId/fedcm/clickdialogbutton': {
POST: { command: 'fedCMClickDialogButton', payloadParams: { required: ['dialogButton'] } },
},
'/session/:sessionId/fedcm/accountlist': {
GET: { command: 'fedCMGetAccounts' },
},
'/session/:sessionId/fedcm/gettitle': {
GET: { command: 'fedCMGetTitle' },
},
'/session/:sessionId/fedcm/getdialogtype': {
GET: { command: 'fedCMGetDialogType' },
},
'/session/:sessionId/fedcm/setdelayenabled': {
POST: { command: 'fedCMSetDelayEnabled', payloadParams: { required: ['enabled'] } },
},
'/session/:sessionId/fedcm/resetcooldown': {
POST: { command: 'fedCMResetCooldown' },
},
// Compute Pressure
// https://www.w3.org/TR/compute-pressure/
'/session/:sessionId/pressuresource': {
POST: {
command: 'createVirtualPressureSource',
payloadParams: { required: ['type'], optional: ['supported'] },
},
},
'/session/:sessionId/pressuresource/:pressureSourceType': {
POST: { command: 'updateVirtualPressureSource', payloadParams: { required: ['sample'] } },
DELETE: { command: 'deleteVirtualPressureSource' },
},
// Global Privacy Control (GPC)
// https://www.w3.org/TR/gpc/
'/session/:sessionId/privacy': {
GET: { command: 'getGlobalPrivacyControl' },
POST: { command: 'setGlobalPrivacyControl', payloadParams: { required: ['gpc'] } },
},
// Storage Access
// https://privacycg.github.io/storage-access/
'/session/:sessionId/storageaccess': {
POST: { command: 'setStorageAccess', payloadParams: { required: ['blocked', 'origin'] } },
},
// #endregion
};
// driver command names
exports.ALL_COMMANDS = lodash_1.default.flatMap(lodash_1.default.values(exports.METHOD_MAP).map(lodash_1.default.values))
.filter((m) => Boolean(m.command))
.map((m) => m.command);
/**
* Resolve a WebDriver URL path and HTTP method to a driver command name from {@link METHOD_MAP}.
* @param endpoint - Request URL or path (may include base path)
* @param method - HTTP method (used when one path maps to multiple commands)
* @param basePath - Optional base path prefix to strip before matching
*/
function routeToCommandName(endpoint, method, basePath) {
const resolvedBasePath = basePath ?? constants_1.DEFAULT_BASE_PATH;
let normalizedEndpoint = resolvedBasePath
? endpoint.replace(new RegExp(`^${lodash_1.default.escapeRegExp(resolvedBasePath)}`), '')
: endpoint;
normalizedEndpoint = `${lodash_1.default.startsWith(normalizedEndpoint, '/') ? '' : '/'}${normalizedEndpoint}`;
let normalizedPathname;
try {
// we could use any prefix there as we anyway need to only extract the pathname
normalizedPathname = new URL(`https://appium.io${normalizedEndpoint}`).pathname;
}
catch (err) {
const msg = err instanceof Error ? err.message : String(err);
throw new Error(`'${endpoint}' cannot be translated to a command name: ${msg}`, { cause: err });
}
const normalizedMethod = lodash_1.default.toUpper(method ?? '');
const cacheKey = toCommandNameCacheKey(normalizedPathname, normalizedMethod);
const cached = COMMAND_NAMES_CACHE.get(cacheKey);
if (cached !== undefined) {
return cached || undefined;
}
const possiblePathnames = [];
if (!normalizedPathname.startsWith('/session/')) {
possiblePathnames.push(`/session/any-session-id${normalizedPathname}`);
}
possiblePathnames.push(normalizedPathname);
for (const [routePath, routeSpec] of lodash_1.default.toPairs(exports.METHOD_MAP)) {
const routeMatcher = (0, path_to_regexp_1.match)(routePath);
if (possiblePathnames.some((pp) => routeMatcher(pp))) {
const spec = routeSpec;
const commandForAnyMethod = () => lodash_1.default.first(lodash_1.default.keys(spec).map((key) => spec[key]?.command));
const commandName = normalizedMethod ? spec[normalizedMethod]?.command : commandForAnyMethod();
if (commandName) {
COMMAND_NAMES_CACHE.set(cacheKey, commandName);
return commandName;
}
}
}
// storing an empty string means we did not find any match for this set of arguments
// and we want to cache this result
COMMAND_NAMES_CACHE.set(cacheKey, '');
}
function toCommandNameCacheKey(endpoint, method) {
return `${endpoint}:${method ?? ''}`;
}
// driver commands that do not require a session to already exist
exports.NO_SESSION_ID_COMMANDS = ['createSession', 'getStatus', 'getAppiumSessions'];
//# sourceMappingURL=routes.js.map