mirror of
https://github.com/appium/appium.git
synced 2026-10-28 04:14:00 +08:00
* Update hash * Add validateCapabilities and parseCapabilities * Added merge capabilities * Moved capabilities into it's own file * Added capabilities as a possible required param * Can pass desiredCapabilities or capabilities into createSession * Remove 'required' desiredCapabilities * desiredCapabilities isn't required any more because we can provide capabilities as well * Will need to update validation logic in 'driver.createSession' to do the validation there instead * Code cleanup * Skip presence constraint in parseCapabilities * We don't need to check that items are present until after we've formed the matched capabilities * Added validation function to routes * Function 'validate' takes params as an arg and then returns a message if the validation fails * Uses this to validate that either "capabilities" or "desiredCapabilities" was provided * Renamed capabilities to caps * Move caps required validator from process to parse * Removed InvalidParametersError * Update BadParametersError so that if a message is added as the 3rd argument it shows a different error message * Made validation re-usable
107 lines
3.7 KiB
JavaScript
107 lines
3.7 KiB
JavaScript
import _ from 'lodash';
|
|
import { validator } from './desired-caps';
|
|
import { util } from 'appium-support';
|
|
|
|
// Takes primary caps object and merges it into a secondary caps object.
|
|
// (see https://www.w3.org/TR/webdriver/#dfn-merging-capabilities)
|
|
function mergeCaps (primary = {}, secondary = {}) {
|
|
let result = Object.assign({}, primary);
|
|
|
|
for (let [name, value] of _.toPairs(secondary)) {
|
|
// Overwriting is not allowed. Primary and secondary must have different properties (w3c rule 4.4)
|
|
if (!_.isUndefined(primary[name])) {
|
|
throw new Error(`property ${name} should not exist on both primary and secondary`);
|
|
}
|
|
result[name] = value;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Validates caps against a set of constraints
|
|
function validateCaps (caps, constraints = {}, skipPresenceConstraint = false) {
|
|
if (!_.isObject(caps)) {
|
|
throw new Error(`must be a JSON object`);
|
|
}
|
|
|
|
constraints = _.cloneDeep(constraints); // Defensive copy
|
|
|
|
// Remove the 'presence' constraint if we're not checking for it
|
|
if (skipPresenceConstraint) {
|
|
for (let key of Object.keys(constraints)) {
|
|
delete constraints[key].presence;
|
|
}
|
|
}
|
|
|
|
let validationErrors = validator.validate(_.pickBy(caps, util.hasValue),
|
|
constraints,
|
|
{fullMessages: false});
|
|
|
|
if (validationErrors) {
|
|
let message = [];
|
|
for (let [attribute, reasons] of _.toPairs(validationErrors)) {
|
|
for (let reason of reasons) {
|
|
message.push(` ${attribute} ${reason},`);
|
|
}
|
|
}
|
|
throw new Error(message.join(''));
|
|
}
|
|
|
|
// Return caps
|
|
return caps;
|
|
}
|
|
|
|
// Parse capabilities (based on https://www.w3.org/TR/webdriver/#processing-capabilities)
|
|
function parseCaps (caps, constraints = {}, shouldValidateCaps = true) {
|
|
if (!_.isObject(caps)) {
|
|
throw new Error('The capabilities argument was not valid for the following reason(s): "capabilities" must be a JSON object.');
|
|
}
|
|
|
|
let {alwaysMatch: requiredCaps, firstMatch: allFirstMatchCaps = []} = caps; // jshint ignore:line
|
|
|
|
if (!_.isObject(requiredCaps)) {
|
|
requiredCaps = {};
|
|
}
|
|
|
|
requiredCaps = validateCaps(requiredCaps, constraints);
|
|
|
|
// Reject 'firstMatch' argument if it's not an array (see spec #3.2)
|
|
if (!_.isArray(allFirstMatchCaps)) {
|
|
throw new Error('The capabilities.firstMatch argument was not valid for the following reason(s): "capabilities.firstMatch" must be a JSON array or undefined');
|
|
}
|
|
|
|
// Validate all of the first match capabilities (see spec #5)
|
|
let validatedFirstMatchCaps = allFirstMatchCaps.map((firstMatchCaps) => {
|
|
try {
|
|
return shouldValidateCaps ? validateCaps(firstMatchCaps, constraints) : firstMatchCaps;
|
|
} catch (e) {
|
|
throw new Error(`The capabilities.firstMatch argument was not valid for the following reason: ${firstMatchCaps} ${e.message}`);
|
|
}
|
|
});
|
|
|
|
// Try to merge requiredCaps with first match capabilities, break once it finds its first match (see spec #6)
|
|
let matchedCaps = null;
|
|
for (let firstMatchCaps of validatedFirstMatchCaps) {
|
|
try {
|
|
matchedCaps = mergeCaps(requiredCaps, firstMatchCaps);
|
|
if (matchedCaps) {
|
|
break;
|
|
}
|
|
} catch (ign) { }
|
|
}
|
|
|
|
// If there was no match, use the de-facto requiredCaps on its own
|
|
matchedCaps = matchedCaps || requiredCaps;
|
|
|
|
// Returns variables for testing purposes
|
|
return {requiredCaps, allFirstMatchCaps, validatedFirstMatchCaps, matchedCaps};
|
|
}
|
|
|
|
// Calls parseCaps and just returns the matchedCaps variable
|
|
function processCaps (caps, constraints = {}, shouldValidateCaps = true) {
|
|
return parseCaps(caps, constraints, shouldValidateCaps).matchedCaps;
|
|
}
|
|
|
|
|
|
export default { parseCaps, processCaps, validateCaps, mergeCaps };
|