refactor: Allow any of createSession arguments to be W3C capabilities object
MODIFY
packages/base-driver/lib/basedriver/capabilities.js
packages/base-driver/lib/basedriver/capabilities.js
@@ -24,6 +24,26 @@ function mergeCaps (primary = {}, secondary = {}) {
return result;
}
+function isW3cCaps (caps) {
+ if (!_.isPlainObject(caps)) {
+ return false;
+ }
+
+ const isFirstMatchValid = () => _.isArray(caps.firstMatch)
+ && !_.isEmpty(caps.firstMatch) && _.every(caps.firstMatch, _.isPlainObject);
+ const isAlwaysMatchValid = () => _.isPlainObject(caps.alwaysMatch);
+ if (_.has(caps, 'firstMatch') && _.has(caps, 'alwaysMatch')) {
+ return isFirstMatchValid() && isAlwaysMatchValid();
+ }
+ if (_.has(caps, 'firstMatch')) {
+ return isFirstMatchValid();
+ }
+ if (_.has(caps, 'alwaysMatch')) {
+ return isAlwaysMatchValid();
+ }
+ return false;
+}
+
// Validates caps against a set of constraints
function validateCaps (caps, constraints = {}, opts = {}) {
@@ -272,4 +292,5 @@ function promoteAppiumOptions (originalCaps) {
export {
parseCaps, processCapabilities, validateCaps, mergeCaps, APPIUM_VENDOR_PREFIX, APPIUM_OPTS_CAP,
findNonPrefixedCaps, isStandardCap, stripAppiumPrefixes, promoteAppiumOptions, PREFIXED_APPIUM_OPTS_CAP,
+ isW3cCaps,
};

MODIFY
packages/base-driver/lib/basedriver/commands/session.js
packages/base-driver/lib/basedriver/commands/session.js
@@ -3,38 +3,38 @@ import _ from 'lodash';
import log from '../logger';
import { errors } from '../../protocol';
import { util } from '@appium/support';
-import { processCapabilities, promoteAppiumOptions, APPIUM_OPTS_CAP, PREFIXED_APPIUM_OPTS_CAP } from '../capabilities';
+import {
+ processCapabilities, promoteAppiumOptions, APPIUM_OPTS_CAP, PREFIXED_APPIUM_OPTS_CAP,
+ isW3cCaps,
+} from '../capabilities';
-let commands = {};
+const commands = {};
-// TODO: Remove jsonwpDesiredCapabilities and jsonwpRequiredCaps completely
-// since Appium 2.0 no longer supports them.
-commands.createSession = async function createSession (jsonwpDesiredCapabilities, jsonwpRequiredCaps, w3cCapabilities) {
+commands.createSession = async function createSession (w3cCapabilities1, w3cCapabilities2, w3cCapabilities) {
if (this.sessionId !== null) {
- throw new errors.SessionNotCreatedError('Cannot create a new session ' +
- 'while one is in progress');
+ throw new errors.SessionNotCreatedError('Cannot create a new session while one is in progress');
}
log.debug();
- if (!w3cCapabilities) {
+ // Historically the first two arguments were reserved for JSONWP capabilities.
+ // Appium 2 has dropped the support of these, so now we only accept capability
+ // objects in W3C format and thus allow any of the three arguments to represent
+ // the latter.
+ const originalCaps = [w3cCapabilities, w3cCapabilities1, w3cCapabilities2].find(isW3cCaps);
+ if (!originalCaps) {
throw new errors.SessionNotCreatedError('Appium only supports W3C-style capability objects. ' +
'Your client is sending an older capabilities format. Please update your client library.');
}
- if (jsonwpDesiredCapabilities) {
- log.warn('Appium received (M)JSONWP desired capabilities in alongside the W3C capabilities; they will be ignored');
- }
-
this.setProtocolW3C();
- this.originalCaps = _.cloneDeep(w3cCapabilities);
- log.debug(`Creating session with W3C capabilities: ${JSON.stringify(w3cCapabilities, null, 2)}`);
-
+ this.originalCaps = _.cloneDeep(originalCaps);
+ log.debug(`Creating session with W3C capabilities: ${JSON.stringify(originalCaps, null, 2)}`);
let caps;
try {
- caps = processCapabilities(w3cCapabilities, this.desiredCapConstraints, this.shouldValidateCaps);
+ caps = processCapabilities(originalCaps, this.desiredCapConstraints, this.shouldValidateCaps);
if (caps[APPIUM_OPTS_CAP]) {
log.debug(`Found ${PREFIXED_APPIUM_OPTS_CAP} capability present; will promote items inside to caps`);
caps = promoteAppiumOptions(caps);

MODIFY
packages/base-driver/test/basedriver/capabilities-specs.js
packages/base-driver/test/basedriver/capabilities-specs.js
-import { parseCaps, validateCaps, mergeCaps, processCapabilities, findNonPrefixedCaps,
- promoteAppiumOptions, APPIUM_OPTS_CAP, stripAppiumPrefixes } from '../../lib/basedriver/capabilities';
+import {
+ parseCaps, validateCaps, mergeCaps, processCapabilities, findNonPrefixedCaps,
+ promoteAppiumOptions, APPIUM_OPTS_CAP, stripAppiumPrefixes, isW3cCaps
+} from '../../lib/basedriver/capabilities';
import _ from 'lodash';
import { desiredCapabilityConstraints } from '../../lib/basedriver/desired-caps';
@@ -502,4 +504,33 @@ describe('caps', function () {
});
});
});
+
+ describe('#isW3cCaps', function () {
+ it('should drop invalid W3C capabilities', function () {
+ for (const invalidCaps of [
+ null, undefined, [], {},
+ {firstMatch: null},
+ {firtMatch: [{}]},
+ {alwaysMatch: null},
+ {firstMatch: [{}], alwaysMatch: null},
+ {firstMatch: [], alwaysMatch: {}},
+ {firstMatch: []},
+ {firstMatch: {}},
+ {alwaysMatch: []},
+ ]) {
+ isW3cCaps(invalidCaps).should.be.false;
+ }
+ });
+
+ it('should accept valid W3C capabilities', function () {
+ for (const validCaps of [
+ {firstMatch: [{}]},
+ {firstMatch: [{}], alaysMatch: {}},
+ {firtMatch: [{}], alwaysMatch: {}},
+ {alwaysMatch: {}},
+ ]) {
+ isW3cCaps(validCaps).should.be.true;
+ }
+ });
+ });
});

MODIFY
packages/base-driver/test/basedriver/capability-specs.js
packages/base-driver/test/basedriver/capability-specs.js
@@ -17,7 +17,9 @@ describe('Desired Capabilities', function () {
it('should require platformName and deviceName', async function () {
try {
- await d.createSession(null, null, {});
+ await d.createSession({
+ firstMatch: [{}]
+ });
} catch (e) {
e.should.be.instanceof(errors.SessionNotCreatedError);
e.message.should.contain('platformName');
@@ -30,7 +32,11 @@ describe('Desired Capabilities', function () {
it('should require platformName', async function () {
try {
- await d.createSession(null, null, {'deviceName': 'Delorean'});
+ await d.createSession({
+ alwaysMatch: {
+ 'appium:deviceName': 'Delorean'
+ }
+ });
} catch (e) {
e.should.be.instanceof(errors.SessionNotCreatedError);
e.message.should.contain('platformName');
@@ -353,28 +359,36 @@ describe('Desired Capabilities', function () {
}
}).should.eventually.be.rejectedWith(/blank/);
- await d.createSession(null, null, {
+ await d.createSession(null, {
+ alwaysMatch: {
platformName: 'iOS',
'appium:deviceName': 'Dumb',
'appium:foo': ''
+ }
}).should.eventually.be.rejectedWith(/blank/);
- await d.createSession(null, null, {
+ await d.createSession({
+ firstMatch: [{
platformName: 'iOS',
'appium:deviceName': 'Dumb',
'appium:foo': {}
+ }]
}).should.eventually.be.rejectedWith(/blank/);
- await d.createSession(null, null, {
+ await d.createSession({
+ alwaysMatch: {
platformName: 'iOS',
'appium:deviceName': 'Dumb',
'appium:foo': []
+ }
}).should.eventually.be.rejectedWith(/blank/);
- await d.createSession(null, null, {
+ await d.createSession({
+ alwaysMatch: {
platformName: 'iOS',
'appium:deviceName': 'Dumb',
'appium:foo': ' '
+ }
}).should.eventually.be.rejectedWith(/blank/);
});

refactor: Allow any of createSession arguments to be W3C capabilities object (#16408)