mirror of
https://github.com/appium/appium.git
synced 2026-11-09 11:24:08 +08:00
Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0f1e56ede | ||
|
|
b0857eefb1 | ||
|
|
8efff6895e | ||
|
|
69eed603c3 | ||
|
|
f91c6b50aa | ||
|
|
46395d7c46 | ||
|
|
5a2cf8d1c4 | ||
|
|
281b3cfdeb |
@@ -1,3 +1,9 @@
|
||||
CHANGES IN VERSION 1.8.0-beta2 (from 1.8.0-beta)
|
||||
===================================
|
||||
|
||||
* Use MJSONWP if desiredCapabilities have extraneuous parameters
|
||||
* Fall back to MJSONWP if session request for W3C Capabilities throws an exception
|
||||
|
||||
CHANGES IN VERSION 1.8.0-beta (from 1.7.2)
|
||||
===================================
|
||||
|
||||
|
||||
@@ -42,6 +42,8 @@ Chrome version that is automatable:
|
||||
|
||||
| Version | Minimum Chrome Version | Link to Chromedriver |
|
||||
|---------|------------------------|-----------------------------------------------------------------------------------|
|
||||
| 2.35 | 62.0.3202.0 | [v2.35 (link)](https://chromedriver.storage.googleapis.com/index.html?path=2.35/) |
|
||||
| 2.34 | 61.0.3163.0 | [v2.34 (link)](https://chromedriver.storage.googleapis.com/index.html?path=2.34/) |
|
||||
| 2.33 | 60.0.3112.0 | [v2.33 (link)](https://chromedriver.storage.googleapis.com/index.html?path=2.33/) |
|
||||
| 2.32 | 59.0.3071.0 | [v2.32 (link)](https://chromedriver.storage.googleapis.com/index.html?path=2.32/) |
|
||||
| 2.31 | 58.0.3029.0 | [v2.31 (link)](https://chromedriver.storage.googleapis.com/index.html?path=2.31/) |
|
||||
|
||||
+22
-10
@@ -16,6 +16,7 @@ import { EspressoDriver } from 'appium-espresso-driver';
|
||||
import B from 'bluebird';
|
||||
import AsyncLock from 'async-lock';
|
||||
import { inspectObject, parseCapsForInnerDriver } from './utils';
|
||||
import semver from 'semver';
|
||||
|
||||
const sessionsListGuard = new AsyncLock();
|
||||
const pendingDriversGuard = new AsyncLock();
|
||||
@@ -88,18 +89,25 @@ class AppiumDriver extends BaseDriver {
|
||||
}
|
||||
|
||||
if (caps.platformName.toLowerCase() === 'android') {
|
||||
const platformVersion = semver.valid(semver.coerce(caps.platformVersion));
|
||||
if (platformVersion && semver.satisfies(platformVersion, '>=6.0.0')) {
|
||||
log.warn("Consider setting 'automationName' capability to " +
|
||||
`'${this.validAutomations.UIAUTOMATOR2.toLowerCase()}' ` +
|
||||
"on Android >= 6, since UIAutomator framework " +
|
||||
"is not maintained anymore by the OS vendor.");
|
||||
}
|
||||
|
||||
return AndroidDriver;
|
||||
}
|
||||
|
||||
if (caps.platformName.toLowerCase() === 'ios') {
|
||||
if (caps.platformVersion) {
|
||||
let majorVer = caps.platformVersion.toString().split(".")[0];
|
||||
if (parseInt(majorVer, 10) >= 10) {
|
||||
log.info("Requested iOS support with version >= 10, using XCUITest " +
|
||||
"driver instead of UIAutomation-based driver, since the " +
|
||||
"latter is unsupported on iOS 10 and up.");
|
||||
return XCUITestDriver;
|
||||
}
|
||||
const platformVersion = semver.valid(semver.coerce(caps.platformVersion));
|
||||
if (platformVersion && semver.satisfies(platformVersion, '>=10.0.0')) {
|
||||
log.info("Requested iOS support with version >= 10, " +
|
||||
`using '${this.validAutomations.XCUITEST.toLowerCase()}' ` +
|
||||
"driver instead of UIAutomation-based driver, since the " +
|
||||
"latter is unsupported on iOS 10 and up.");
|
||||
return XCUITestDriver;
|
||||
}
|
||||
|
||||
return IosDriver;
|
||||
@@ -182,11 +190,15 @@ class AppiumDriver extends BaseDriver {
|
||||
let {defaultCapabilities} = this.args;
|
||||
|
||||
// Parse the caps into a format that the InnerDriver will accept
|
||||
let {desiredCaps, processedJsonwpCaps, processedW3CCapabilities} = parseCapsForInnerDriver(
|
||||
let {desiredCaps, processedJsonwpCapabilities, processedW3CCapabilities} = parseCapsForInnerDriver(
|
||||
jsonwpCaps,
|
||||
w3cCapabilities,
|
||||
this.desiredCapConstraints, defaultCapabilities
|
||||
);
|
||||
|
||||
// Since the processed caps may be different from the payload, update the protocol accordingly
|
||||
this.protocol = BaseDriver.determineProtocol(processedJsonwpCapabilities, reqCaps, processedW3CCapabilities);
|
||||
|
||||
let InnerDriver = this.getDriverForCaps(desiredCaps);
|
||||
this.printNewSessionAnnouncement(InnerDriver, desiredCaps);
|
||||
|
||||
@@ -220,7 +232,7 @@ class AppiumDriver extends BaseDriver {
|
||||
let innerSessionId, dCaps;
|
||||
try {
|
||||
[innerSessionId, dCaps] = await d.createSession(
|
||||
processedJsonwpCaps,
|
||||
processedJsonwpCapabilities,
|
||||
reqCaps,
|
||||
processedW3CCapabilities,
|
||||
[...runningDriversData, ...otherPendingDriversData]
|
||||
|
||||
+27
-32
@@ -64,55 +64,50 @@ function parseCapsForInnerDriver (jsonwpCaps, w3cCapabilities, constraints={}, d
|
||||
|
||||
// Get MJSONWP caps
|
||||
let desiredCaps = {};
|
||||
let processedJsonwpCapabilities = null;
|
||||
if (hasJSONWPCaps) {
|
||||
desiredCaps = jsonwpCaps;
|
||||
processedJsonwpCapabilities = {...desiredCaps};
|
||||
}
|
||||
|
||||
// Get W3C caps
|
||||
let processedW3CCapabilities = null;
|
||||
if (hasW3CCaps) {
|
||||
// Call the process capabilities algorithm to find matching caps on the W3C
|
||||
// (see: https://github.com/jlipps/simple-wd-spec#processing-capabilities)
|
||||
let matchingW3CCaps = processCapabilities(w3cCapabilities, constraints, true);
|
||||
|
||||
if (hasJSONWPCaps) {
|
||||
let jsonwpKeys = _.keys(desiredCaps);
|
||||
let matchingW3CCapsKeys = _.keys(matchingW3CCaps);
|
||||
|
||||
// If JSONWP has keys that W3C doesn't have, log a warning message
|
||||
let differingKeys = _.differenceWith(jsonwpKeys, matchingW3CCapsKeys);
|
||||
if (!_.isEmpty(differingKeys)) {
|
||||
logger.warn(`The following capabilities were provided in the JSONWP desired capabilities but
|
||||
not the W3C capabilities: ${JSON.stringify(differingKeys)}`);
|
||||
let matchingW3CCaps;
|
||||
try {
|
||||
matchingW3CCaps = processCapabilities(w3cCapabilities, constraints, true);
|
||||
} catch (err) {
|
||||
if (jsonwpCaps) {
|
||||
logger.warn(`Could not parse W3C capabilities: ${err.message}. Falling back to JSONWP protocol.`);
|
||||
return {desiredCaps, processedJsonwpCapabilities, processedW3CCapabilities};
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// If JSONWP caps and W3C caps were provided and there are discrepancies between them
|
||||
// we'll be forgiving for now and just merge the two. But in the future, the clients
|
||||
// should not be sending discrepant caps and Appium should ignore the extraneous MJSONWP caps
|
||||
desiredCaps = {
|
||||
...desiredCaps, // TODO: This line should be removed once we decide to not allow discrepent caps
|
||||
...matchingW3CCaps,
|
||||
};
|
||||
}
|
||||
desiredCaps = matchingW3CCaps;
|
||||
|
||||
let processedJsonwpCaps = null;
|
||||
let processedW3CCapabilities = null;
|
||||
|
||||
// Create new JSONWP desired capabilities and W3C 'capabilities' objects that have the reconciled attributes
|
||||
if (hasJSONWPCaps) {
|
||||
processedJsonwpCaps = {...desiredCaps};
|
||||
}
|
||||
|
||||
// Translate the matched W3C caps back into W3C capabilities object so that it
|
||||
// can be proxied to the inner driver
|
||||
if (hasW3CCaps) {
|
||||
// Create a new w3c capabilities payload that contains only the matching caps in `alwaysMatch`
|
||||
processedW3CCapabilities = {
|
||||
alwaysMatch: {...insertAppiumPrefixes(desiredCaps)}, // Insert 'appium:' prefix to non-prefixed caps so that the Inner Driver doesn't complain about bad W3C prefixes
|
||||
alwaysMatch: {...insertAppiumPrefixes(desiredCaps)},
|
||||
firstMatch: [{}],
|
||||
};
|
||||
|
||||
// If we found extraneuous keys in JSONWP caps, fall back to JSONWP
|
||||
if (hasJSONWPCaps) {
|
||||
let differingKeys = _.difference(_.keys(jsonwpCaps), _.keys(matchingW3CCaps));
|
||||
if (!_.isEmpty(differingKeys)) {
|
||||
logger.warn(`The following capabilities were provided in the JSONWP desired capabilities that are missing ` +
|
||||
`in W3C capabilities: ${JSON.stringify(differingKeys)}. Falling back to JSONWP protocol.`);
|
||||
desiredCaps = jsonwpCaps;
|
||||
return {desiredCaps, processedJsonwpCapabilities, processedW3CCapabilities: null};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {desiredCaps, processedJsonwpCaps, processedW3CCapabilities};
|
||||
return {desiredCaps, processedJsonwpCapabilities, processedW3CCapabilities};
|
||||
}
|
||||
|
||||
|
||||
|
||||
+3
-2
@@ -11,7 +11,7 @@
|
||||
"firefoxos",
|
||||
"testing"
|
||||
],
|
||||
"version": "1.8.0-beta",
|
||||
"version": "1.8.0-beta2",
|
||||
"author": "https://github.com/appium",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
@@ -35,7 +35,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"appium-android-driver": "1.x",
|
||||
"appium-base-driver": "^2.20.0",
|
||||
"appium-base-driver": "^2.20.2",
|
||||
"appium-espresso-driver": "^1.0.0-beta.3",
|
||||
"appium-fake-driver": "^0.3.0",
|
||||
"appium-ios-driver": "1.x",
|
||||
@@ -58,6 +58,7 @@
|
||||
"npmlog": "2.x",
|
||||
"request": "^2.81.0",
|
||||
"request-promise": "1.x",
|
||||
"semver": "^5.5.0",
|
||||
"source-map-support": "0.x",
|
||||
"teen_process": "1.x",
|
||||
"winston": "2.x"
|
||||
|
||||
+76
-76
@@ -15,9 +15,9 @@ let should = chai.should();
|
||||
chai.use(chaiAsPromised);
|
||||
|
||||
|
||||
describe('Config', () => {
|
||||
describe('getGitRev', () => {
|
||||
it('should get a reasonable git revision', async () => {
|
||||
describe('Config', function () {
|
||||
describe('getGitRev', function () {
|
||||
it('should get a reasonable git revision', async function () {
|
||||
let rev = await getGitRev();
|
||||
rev.should.be.a('string');
|
||||
rev.length.should.be.equal(40);
|
||||
@@ -25,9 +25,9 @@ describe('Config', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Appium config', () => {
|
||||
describe('getAppiumConfig', () => {
|
||||
it('should get a configuration object', async () => {
|
||||
describe('Appium config', function () {
|
||||
describe('getAppiumConfig', function () {
|
||||
it('should get a configuration object', async function () {
|
||||
let config = await getAppiumConfig();
|
||||
config.should.be.an('object');
|
||||
should.exist(config['git-sha']);
|
||||
@@ -35,11 +35,11 @@ describe('Config', () => {
|
||||
should.exist(config.version);
|
||||
});
|
||||
});
|
||||
describe('showConfig', () => {
|
||||
before(() => {
|
||||
describe('showConfig', function () {
|
||||
before(function () {
|
||||
sinon.spy(console, "log");
|
||||
});
|
||||
it('should log the config to console', async () => {
|
||||
it('should log the config to console', async function () {
|
||||
let config = await getAppiumConfig();
|
||||
await showConfig();
|
||||
console.log.calledOnce.should.be.true; // eslint-disable-line no-console
|
||||
@@ -48,9 +48,9 @@ describe('Config', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('node.js config', () => {
|
||||
describe('node.js config', function () {
|
||||
let _process = process;
|
||||
before(() => {
|
||||
before(function () {
|
||||
// need to be able to write to process.version
|
||||
// but also to have access to process methods
|
||||
// so copy them over to a writable object
|
||||
@@ -60,11 +60,11 @@ describe('Config', () => {
|
||||
}
|
||||
process = tempProcess;
|
||||
});
|
||||
after(() => {
|
||||
after(function () {
|
||||
process = _process;
|
||||
});
|
||||
describe('checkNodeOk', () => {
|
||||
it('should fail if node is below 4', () => {
|
||||
describe('checkNodeOk', function () {
|
||||
it('should fail if node is below 4', function () {
|
||||
process.version = 'v4.4.7';
|
||||
checkNodeOk.should.throw();
|
||||
process.version = 'v0.9.12';
|
||||
@@ -76,53 +76,53 @@ describe('Config', () => {
|
||||
process.version = 'v0.12.14';
|
||||
checkNodeOk.should.throw();
|
||||
});
|
||||
it('should succeed if node is 5+', () => {
|
||||
it('should succeed if node is 5+', function () {
|
||||
process.version = 'v5.7.0';
|
||||
checkNodeOk.should.not.throw();
|
||||
});
|
||||
it('should succeed if node is 6+', () => {
|
||||
it('should succeed if node is 6+', function () {
|
||||
process.version = 'v6.3.1';
|
||||
checkNodeOk.should.not.throw();
|
||||
});
|
||||
it('should succeed if node is 7+', () => {
|
||||
it('should succeed if node is 7+', function () {
|
||||
process.version = 'v7.1.1';
|
||||
checkNodeOk.should.not.throw();
|
||||
});
|
||||
it('should succeed if node is 8+', () => {
|
||||
it('should succeed if node is 8+', function () {
|
||||
process.version = 'v8.1.2';
|
||||
checkNodeOk.should.not.throw();
|
||||
});
|
||||
});
|
||||
|
||||
describe('warnNodeDeprecations', () => {
|
||||
describe('warnNodeDeprecations', function () {
|
||||
let spy;
|
||||
before(() => {
|
||||
before(function () {
|
||||
spy = sinon.spy(logger, "warn");
|
||||
});
|
||||
beforeEach(() => {
|
||||
beforeEach(function () {
|
||||
spy.reset();
|
||||
});
|
||||
it('should log a warning if node is below 4', () => {
|
||||
it('should log a warning if node is below 4', function () {
|
||||
process.version = 'v0.9.12';
|
||||
warnNodeDeprecations();
|
||||
logger.warn.callCount.should.equal(1);
|
||||
});
|
||||
it('should log a warning if node is 0.12', () => {
|
||||
it('should log a warning if node is 0.12', function () {
|
||||
process.version = 'v0.12.0';
|
||||
warnNodeDeprecations();
|
||||
logger.warn.callCount.should.equal(1);
|
||||
});
|
||||
it('should not log a warning if node is 4+', () => {
|
||||
it('should not log a warning if node is 4+', function () {
|
||||
process.version = 'v4.4.7';
|
||||
warnNodeDeprecations();
|
||||
logger.warn.callCount.should.equal(0);
|
||||
});
|
||||
it('should not log a warning if node is 5+', () => {
|
||||
it('should not log a warning if node is 5+', function () {
|
||||
process.version = 'v5.7.0';
|
||||
warnNodeDeprecations();
|
||||
logger.warn.callCount.should.equal(0);
|
||||
});
|
||||
it('should not log a warning if node is 6+', () => {
|
||||
it('should not log a warning if node is 6+', function () {
|
||||
process.version = 'v6.3.1';
|
||||
warnNodeDeprecations();
|
||||
logger.warn.callCount.should.equal(0);
|
||||
@@ -130,22 +130,22 @@ describe('Config', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('server arguments', () => {
|
||||
describe('server arguments', function () {
|
||||
let parser = getParser();
|
||||
parser.debug = true; // throw instead of exit on error; pass as option instead?
|
||||
let args = {};
|
||||
beforeEach(() => {
|
||||
beforeEach(function () {
|
||||
// give all the defaults
|
||||
for (let rawArg of parser.rawArgs) {
|
||||
args[rawArg[1].dest] = rawArg[1].defaultValue;
|
||||
}
|
||||
});
|
||||
describe('getNonDefaultArgs', () => {
|
||||
it('should show none if we have all the defaults', () => {
|
||||
describe('getNonDefaultArgs', function () {
|
||||
it('should show none if we have all the defaults', function () {
|
||||
let nonDefaultArgs = getNonDefaultArgs(parser, args);
|
||||
_.keys(nonDefaultArgs).length.should.equal(0);
|
||||
});
|
||||
it('should catch a non-default argument', () => {
|
||||
it('should catch a non-default argument', function () {
|
||||
args.isolateSimDevice = true;
|
||||
let nonDefaultArgs = getNonDefaultArgs(parser, args);
|
||||
_.keys(nonDefaultArgs).length.should.equal(1);
|
||||
@@ -153,18 +153,18 @@ describe('Config', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDeprecatedArgs', () => {
|
||||
it('should show none if we have no deprecated arguments', () => {
|
||||
describe('getDeprecatedArgs', function () {
|
||||
it('should show none if we have no deprecated arguments', function () {
|
||||
let deprecatedArgs = getDeprecatedArgs(parser, args);
|
||||
_.keys(deprecatedArgs).length.should.equal(0);
|
||||
});
|
||||
it('should catch a deprecated argument', () => {
|
||||
it('should catch a deprecated argument', function () {
|
||||
args.showIOSLog = true;
|
||||
let deprecatedArgs = getDeprecatedArgs(parser, args);
|
||||
_.keys(deprecatedArgs).length.should.equal(1);
|
||||
should.exist(deprecatedArgs['--show-ios-log']);
|
||||
});
|
||||
it('should catch a non-boolean deprecated argument', () => {
|
||||
it('should catch a non-boolean deprecated argument', function () {
|
||||
args.calendarFormat = 'orwellian';
|
||||
let deprecatedArgs = getDeprecatedArgs(parser, args);
|
||||
_.keys(deprecatedArgs).length.should.equal(1);
|
||||
@@ -173,58 +173,58 @@ describe('Config', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('checkValidPort', () => {
|
||||
it('should be false for port too high', () => {
|
||||
describe('checkValidPort', function () {
|
||||
it('should be false for port too high', function () {
|
||||
checkValidPort(65536).should.be.false;
|
||||
});
|
||||
it('should be false for port too low', () => {
|
||||
it('should be false for port too low', function () {
|
||||
checkValidPort(0).should.be.false;
|
||||
});
|
||||
it('should be true for port 1', () => {
|
||||
it('should be true for port 1', function () {
|
||||
checkValidPort(1).should.be.true;
|
||||
});
|
||||
it('should be true for port 65535', () => {
|
||||
it('should be true for port 65535', function () {
|
||||
checkValidPort(65535).should.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateTmpDir', () => {
|
||||
it('should fail to use a tmp dir with incorrect permissions', async () => {
|
||||
describe('validateTmpDir', function () {
|
||||
it('should fail to use a tmp dir with incorrect permissions', async function () {
|
||||
validateTmpDir('/private/if_you_run_with_sudo_this_wont_fail').should.be.rejectedWith(/could not ensure/);
|
||||
});
|
||||
it('should fail to use an undefined tmp dir', async () => {
|
||||
it('should fail to use an undefined tmp dir', async function () {
|
||||
validateTmpDir().should.be.rejectedWith(/could not ensure/);
|
||||
});
|
||||
it('should be able to use a tmp dir with correct permissions', async () => {
|
||||
it('should be able to use a tmp dir with correct permissions', async function () {
|
||||
validateTmpDir('/tmp/test_tmp_dir/with/any/number/of/levels').should.not.be.rejected;
|
||||
});
|
||||
});
|
||||
|
||||
describe('parsing args with empty argv[1]', () => {
|
||||
describe('parsing args with empty argv[1]', function () {
|
||||
let argv1;
|
||||
|
||||
before(() => {
|
||||
before(function () {
|
||||
argv1 = process.argv[1];
|
||||
});
|
||||
|
||||
after(() => {
|
||||
after(function () {
|
||||
process.argv[1] = argv1;
|
||||
});
|
||||
|
||||
it('should not fail if process.argv[1] is undefined', () => {
|
||||
it('should not fail if process.argv[1] is undefined', function () {
|
||||
process.argv[1] = undefined;
|
||||
let args = getParser();
|
||||
args.prog.should.be.equal('Appium');
|
||||
});
|
||||
|
||||
it('should set "prog" to process.argv[1]', () => {
|
||||
it('should set "prog" to process.argv[1]', function () {
|
||||
process.argv[1] = 'Hello World';
|
||||
let args = getParser();
|
||||
args.prog.should.be.equal('Hello World');
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateServerArgs', () => {
|
||||
describe('validateServerArgs', function () {
|
||||
let parser = getParser();
|
||||
parser.debug = true; // throw instead of exit on error; pass as option instead?
|
||||
const defaultArgs = {};
|
||||
@@ -233,98 +233,98 @@ describe('Config', () => {
|
||||
defaultArgs[rawArg[1].dest] = rawArg[1].defaultValue;
|
||||
}
|
||||
let args = {};
|
||||
beforeEach(() => {
|
||||
beforeEach(function () {
|
||||
args = _.clone(defaultArgs);
|
||||
});
|
||||
describe('mutually exclusive server arguments', () => {
|
||||
describe('noReset and fullReset', () => {
|
||||
it('should not allow both', () => {
|
||||
describe('mutually exclusive server arguments', function () {
|
||||
describe('noReset and fullReset', function () {
|
||||
it('should not allow both', function () {
|
||||
(() => {
|
||||
args.noReset = args.fullReset = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.throw();
|
||||
});
|
||||
it('should allow noReset', () => {
|
||||
it('should allow noReset', function () {
|
||||
(() => {
|
||||
args.noReset = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.not.throw();
|
||||
});
|
||||
it('should allow fullReset', () => {
|
||||
it('should allow fullReset', function () {
|
||||
(() => {
|
||||
args.fullReset = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.not.throw();
|
||||
});
|
||||
});
|
||||
describe('ipa and safari', () => {
|
||||
it('should not allow both', () => {
|
||||
describe('ipa and safari', function () {
|
||||
it('should not allow both', function () {
|
||||
(() => {
|
||||
args.ipa = args.safari = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.throw();
|
||||
});
|
||||
it('should allow ipa', () => {
|
||||
it('should allow ipa', function () {
|
||||
(() => {
|
||||
args.ipa = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.not.throw();
|
||||
});
|
||||
it('should allow safari', () => {
|
||||
it('should allow safari', function () {
|
||||
(() => {
|
||||
args.safari = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.not.throw();
|
||||
});
|
||||
});
|
||||
describe('app and safari', () => {
|
||||
it('should not allow both', () => {
|
||||
describe('app and safari', function () {
|
||||
it('should not allow both', function () {
|
||||
(() => {
|
||||
args.app = args.safari = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.throw();
|
||||
});
|
||||
it('should allow app', () => {
|
||||
it('should allow app', function () {
|
||||
(() => {
|
||||
args.app = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.not.throw();
|
||||
});
|
||||
});
|
||||
describe('forceIphone and forceIpad', () => {
|
||||
it('should not allow both', () => {
|
||||
describe('forceIphone and forceIpad', function () {
|
||||
it('should not allow both', function () {
|
||||
(() => {
|
||||
args.forceIphone = args.forceIpad = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.throw();
|
||||
});
|
||||
it('should allow forceIphone', () => {
|
||||
it('should allow forceIphone', function () {
|
||||
(() => {
|
||||
args.forceIphone = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.not.throw();
|
||||
});
|
||||
it('should allow forceIpad', () => {
|
||||
it('should allow forceIpad', function () {
|
||||
(() => {
|
||||
args.forceIpad = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.not.throw();
|
||||
});
|
||||
});
|
||||
describe('deviceName and defaultDevice', () => {
|
||||
it('should not allow both', () => {
|
||||
describe('deviceName and defaultDevice', function () {
|
||||
it('should not allow both', function () {
|
||||
(() => {
|
||||
args.deviceName = args.defaultDevice = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.throw();
|
||||
});
|
||||
it('should allow deviceName', () => {
|
||||
it('should allow deviceName', function () {
|
||||
(() => {
|
||||
args.deviceName = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.not.throw();
|
||||
});
|
||||
it('should allow defaultDevice', () => {
|
||||
it('should allow defaultDevice', function () {
|
||||
(() => {
|
||||
args.defaultDevice = true;
|
||||
validateServerArgs(parser, args);
|
||||
@@ -332,19 +332,19 @@ describe('Config', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('validated arguments', () => {
|
||||
describe('validated arguments', function () {
|
||||
// checking ports is already done.
|
||||
// the only argument left is `backendRetries`
|
||||
describe('backendRetries', () => {
|
||||
it('should fail with value less than 0', () => {
|
||||
describe('backendRetries', function () {
|
||||
it('should fail with value less than 0', function () {
|
||||
args.backendRetries = -1;
|
||||
(() => {validateServerArgs(parser, args);}).should.throw();
|
||||
});
|
||||
it('should succeed with value of 0', () => {
|
||||
it('should succeed with value of 0', function () {
|
||||
args.backendRetries = 0;
|
||||
(() => {validateServerArgs(parser, args);}).should.not.throw();
|
||||
});
|
||||
it('should succeed with value above 0', () => {
|
||||
it('should succeed with value above 0', function () {
|
||||
args.backendRetries = 100;
|
||||
(() => {validateServerArgs(parser, args);}).should.not.throw();
|
||||
});
|
||||
|
||||
+71
-23
@@ -15,23 +15,23 @@ const should = chai.should();
|
||||
const shouldStartServer = process.env.USE_RUNNING_SERVER !== "0";
|
||||
const caps = {platformName: "Fake", deviceName: "Fake", app: TEST_FAKE_APP};
|
||||
|
||||
describe('FakeDriver - via HTTP', () => {
|
||||
describe('FakeDriver - via HTTP', function () {
|
||||
let server = null;
|
||||
const baseUrl = `http://${TEST_HOST}:${TEST_PORT}/wd/hub/session`;
|
||||
before(async () => {
|
||||
before(async function () {
|
||||
if (shouldStartServer) {
|
||||
let args = {port: TEST_PORT, host: TEST_HOST};
|
||||
server = await appiumServer(args);
|
||||
}
|
||||
});
|
||||
after(async () => {
|
||||
after(async function () {
|
||||
if (server) {
|
||||
await server.close();
|
||||
}
|
||||
});
|
||||
|
||||
describe('session handling', () => {
|
||||
it('should start and stop a session', async () => {
|
||||
describe('session handling', function () {
|
||||
it('should start and stop a session', async function () {
|
||||
let driver = wd.promiseChainRemote(TEST_HOST, TEST_PORT);
|
||||
let [sessionId] = await driver.init(caps);
|
||||
should.exist(sessionId);
|
||||
@@ -40,7 +40,7 @@ describe('FakeDriver - via HTTP', () => {
|
||||
await driver.title().should.eventually.be.rejectedWith(/terminated/);
|
||||
});
|
||||
|
||||
it('should be able to run two FakeDriver sessions simultaneously', async () => {
|
||||
it('should be able to run two FakeDriver sessions simultaneously', async function () {
|
||||
let driver1 = wd.promiseChainRemote(TEST_HOST, TEST_PORT);
|
||||
let [sessionId1] = await driver1.init(caps);
|
||||
should.exist(sessionId1);
|
||||
@@ -54,7 +54,7 @@ describe('FakeDriver - via HTTP', () => {
|
||||
await driver2.quit();
|
||||
});
|
||||
|
||||
it('should not be able to run two FakeDriver sessions simultaneously when one is unique', async () => {
|
||||
it('should not be able to run two FakeDriver sessions simultaneously when one is unique', async function () {
|
||||
let uniqueCaps = _.clone(caps);
|
||||
uniqueCaps.uniqueApp = true;
|
||||
let driver1 = wd.promiseChainRemote(TEST_HOST, TEST_PORT);
|
||||
@@ -66,7 +66,7 @@ describe('FakeDriver - via HTTP', () => {
|
||||
await driver1.quit();
|
||||
});
|
||||
|
||||
it('should use the newCommandTimeout of the inner Driver on session creation', async () => {
|
||||
it('should use the newCommandTimeout of the inner Driver on session creation', async function () {
|
||||
let driver = wd.promiseChainRemote(TEST_HOST, TEST_PORT);
|
||||
|
||||
caps.newCommandTimeout = 0.25;
|
||||
@@ -78,7 +78,7 @@ describe('FakeDriver - via HTTP', () => {
|
||||
await driver.source().should.eventually.be.rejectedWith(/terminated/);
|
||||
});
|
||||
|
||||
it('should accept valid W3C capabilities and start a W3C session', async () => {
|
||||
it('should accept valid W3C capabilities and start a W3C session', async function () {
|
||||
// Try with valid capabilities and check that it returns a session ID
|
||||
const w3cCaps = {
|
||||
capabilities: {
|
||||
@@ -105,15 +105,18 @@ describe('FakeDriver - via HTTP', () => {
|
||||
screenshotValue.should.equal('hahahanotreallyascreenshot');
|
||||
|
||||
// Now use that sessionID to call an arbitrary W3C-only endpoint that isn't implemented to see if it responds with correct error
|
||||
const {statusCode, message} = await request.post({url: `${baseUrl}/${value.sessionId}/execute/async`, json: {script: '', args: ['a']}}).should.eventually.be.rejected;
|
||||
const {statusCode, error} = await request.post({url: `${baseUrl}/${value.sessionId}/execute/async`, json: {script: '', args: ['a']}}).should.eventually.be.rejected;
|
||||
statusCode.should.equal(404);
|
||||
const {error:errorMessage, message, stacktrace} = error.value;
|
||||
errorMessage.should.match(/A command could not be executed/);
|
||||
message.should.match(/Method has not yet been implemented/);
|
||||
stacktrace.should.match(/FakeDriver.executeCommand/);
|
||||
|
||||
// End session
|
||||
await request.delete({url: `${baseUrl}/${value.sessionId}`}).should.eventually.be.resolved;
|
||||
});
|
||||
|
||||
it('should reject invalid W3C capabilities and respond with a 400 Bad Parameters error', async () => {
|
||||
it('should reject invalid W3C capabilities and respond with a 400 Bad Parameters error', async function () {
|
||||
const badW3Ccaps = {
|
||||
capabilities: {
|
||||
alwaysMatch: {},
|
||||
@@ -121,16 +124,15 @@ describe('FakeDriver - via HTTP', () => {
|
||||
}
|
||||
};
|
||||
|
||||
const {statusCode, message} = await request.post({url: baseUrl, json: badW3Ccaps}).should.eventually.be.rejected;
|
||||
const {statusCode, error} = await request.post({url: baseUrl, json: badW3Ccaps}).should.eventually.be.rejected;
|
||||
statusCode.should.equal(400);
|
||||
message.should.match(/can't be blank/);
|
||||
error.value.message.should.match(/can't be blank/);
|
||||
});
|
||||
|
||||
it('should accept a combo of W3C and JSONWP capabilities but default to W3C', async () => {
|
||||
it('should accept a combo of W3C and JSONWP capabilities but default to W3C', async function () {
|
||||
const combinedCaps = {
|
||||
"desiredCapabilities": {
|
||||
...caps,
|
||||
mjsonwpParam: 'mjsonwpParam',
|
||||
},
|
||||
"capabilities": {
|
||||
"alwaysMatch": {...caps},
|
||||
@@ -147,11 +149,36 @@ describe('FakeDriver - via HTTP', () => {
|
||||
value.capabilities.should.deep.equal({
|
||||
...caps,
|
||||
w3cParam: 'w3cParam',
|
||||
mjsonwpParam: 'mjsonwpParam', // Note: For now we're accepting a mix of the two to alleviate client issues. This ought to not be supported in the future though.
|
||||
});
|
||||
});
|
||||
|
||||
it('should reject bad W3C capabilities with a BadParametersError (400)', async () => {
|
||||
it('should accept a combo of W3C and JSONWP but use JSONWP if desiredCapabilities contains extraneous keys', async function () {
|
||||
const combinedCaps = {
|
||||
"desiredCapabilities": {
|
||||
...caps,
|
||||
automationName: 'Fake',
|
||||
anotherParam: 'Hello',
|
||||
},
|
||||
"capabilities": {
|
||||
"alwaysMatch": {...caps},
|
||||
"firstMatch": [{
|
||||
w3cParam: 'w3cParam',
|
||||
}],
|
||||
}
|
||||
};
|
||||
|
||||
const {status, sessionId, value} = await request.post({url: baseUrl, json: combinedCaps});
|
||||
status.should.exist;
|
||||
sessionId.should.exist;
|
||||
should.not.exist(value.sessionId);
|
||||
value.should.deep.equal({
|
||||
...caps,
|
||||
automationName: 'Fake',
|
||||
anotherParam: 'Hello',
|
||||
});
|
||||
});
|
||||
|
||||
it('should reject bad W3C capabilities with a BadParametersError (400)', async function () {
|
||||
const w3cCaps = {
|
||||
"capabilities": {
|
||||
"alwaysMatch": {
|
||||
@@ -160,12 +187,14 @@ describe('FakeDriver - via HTTP', () => {
|
||||
}
|
||||
},
|
||||
};
|
||||
const {message, statusCode} = await request.post({url: baseUrl, json: w3cCaps}).should.eventually.be.rejected;
|
||||
const {error, statusCode, response} = await request.post({url: baseUrl, json: w3cCaps}).should.eventually.be.rejected;
|
||||
response.headers['content-type'].should.match(/application\/json/);
|
||||
const {message} = error.value;
|
||||
message.should.match(/BadAutomationName not part of/);
|
||||
statusCode.should.equal(400);
|
||||
});
|
||||
|
||||
it('should accept capabilities that are provided in the firstMatch array', async () => {
|
||||
it('should accept capabilities that are provided in the firstMatch array', async function () {
|
||||
const w3cCaps = {
|
||||
"capabilities": {
|
||||
"alwaysMatch": {},
|
||||
@@ -179,10 +208,29 @@ describe('FakeDriver - via HTTP', () => {
|
||||
should.not.exist(sessionId);
|
||||
value.capabilities.should.deep.equal(caps);
|
||||
});
|
||||
|
||||
it('should fall back to MJSONWP if w3c caps are invalid', async function () {
|
||||
const combinedCaps = {
|
||||
"desiredCapabilities": {
|
||||
...caps,
|
||||
},
|
||||
"capabilities": {
|
||||
"alwaysMatch": {},
|
||||
"firstMatch": [{}, {
|
||||
...caps,
|
||||
deviceName: null,
|
||||
}],
|
||||
},
|
||||
};
|
||||
const {value, sessionId, status} = await request.post({url: baseUrl, json: combinedCaps});
|
||||
status.should.exist;
|
||||
sessionId.should.exist;
|
||||
value.should.deep.equal(caps);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Logsink', () => {
|
||||
describe('Logsink', function () {
|
||||
let server = null;
|
||||
let logs = [];
|
||||
let logHandler = (level, message) => {
|
||||
@@ -190,15 +238,15 @@ describe('Logsink', () => {
|
||||
};
|
||||
let args = {port: TEST_PORT, host: TEST_HOST, logHandler};
|
||||
|
||||
before(async () => {
|
||||
before(async function () {
|
||||
server = await appiumServer(args);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
after(async function () {
|
||||
await server.close();
|
||||
});
|
||||
|
||||
it('should send logs to a logHandler passed in by a parent package', async () => {
|
||||
it('should send logs to a logHandler passed in by a parent package', async function () {
|
||||
logs.length.should.be.above(1);
|
||||
logs[0].length.should.equal(2);
|
||||
logs[0][1].should.include("Welcome to Appium");
|
||||
|
||||
+58
-36
@@ -17,15 +17,15 @@ chai.use(chaiAsPromised);
|
||||
|
||||
const SESSION_ID = 1;
|
||||
|
||||
describe('AppiumDriver', () => {
|
||||
describe('getAppiumRouter', () => {
|
||||
it('should return a route configuring function', async () => {
|
||||
describe('AppiumDriver', function () {
|
||||
describe('getAppiumRouter', function () {
|
||||
it('should return a route configuring function', async function () {
|
||||
let routeConfiguringFunction = getAppiumRouter({});
|
||||
routeConfiguringFunction.should.be.a.function;
|
||||
});
|
||||
});
|
||||
|
||||
describe('AppiumDriver', () => {
|
||||
describe('AppiumDriver', function () {
|
||||
function getDriverAndFakeDriver () {
|
||||
let appium = new AppiumDriver({});
|
||||
let fakeDriver = new FakeDriver();
|
||||
@@ -37,25 +37,25 @@ describe('AppiumDriver', () => {
|
||||
};
|
||||
return [appium, mockFakeDriver];
|
||||
}
|
||||
describe('createSession', () => {
|
||||
describe('createSession', function () {
|
||||
let appium;
|
||||
let mockFakeDriver;
|
||||
beforeEach(() => {
|
||||
beforeEach(function () {
|
||||
[appium, mockFakeDriver] = getDriverAndFakeDriver();
|
||||
});
|
||||
afterEach(async () => {
|
||||
afterEach(async function () {
|
||||
mockFakeDriver.restore();
|
||||
await appium.deleteSession(SESSION_ID);
|
||||
});
|
||||
|
||||
it('should call inner driver\'s createSession with desired capabilities', async () => {
|
||||
it('should call inner driver\'s createSession with desired capabilities', async function () {
|
||||
mockFakeDriver.expects("createSession")
|
||||
.once().withExactArgs(BASE_CAPS, undefined, null, [])
|
||||
.returns([SESSION_ID, BASE_CAPS]);
|
||||
await appium.createSession(BASE_CAPS);
|
||||
mockFakeDriver.verify();
|
||||
});
|
||||
it('should call inner driver\'s createSession with desired and default capabilities', async () => {
|
||||
it('should call inner driver\'s createSession with desired and default capabilities', async function () {
|
||||
let defaultCaps = {deviceName: 'Emulator'}
|
||||
, allCaps = _.extend(_.clone(defaultCaps), BASE_CAPS);
|
||||
appium.args.defaultCapabilities = defaultCaps;
|
||||
@@ -65,7 +65,7 @@ describe('AppiumDriver', () => {
|
||||
await appium.createSession(BASE_CAPS);
|
||||
mockFakeDriver.verify();
|
||||
});
|
||||
it('should call inner driver\'s createSession with desired and default capabilities without overriding caps', async () => {
|
||||
it('should call inner driver\'s createSession with desired and default capabilities without overriding caps', async function () {
|
||||
// a default capability with the same key as a desired capability
|
||||
// should do nothing
|
||||
let defaultCaps = {platformName: 'Ersatz'};
|
||||
@@ -76,7 +76,7 @@ describe('AppiumDriver', () => {
|
||||
await appium.createSession(BASE_CAPS);
|
||||
mockFakeDriver.verify();
|
||||
});
|
||||
it('should kill all other sessions if sessionOverride is on', async () => {
|
||||
it('should kill all other sessions if sessionOverride is on', async function () {
|
||||
appium.args.sessionOverride = true;
|
||||
|
||||
// mock three sessions that should be removed when the new one is created
|
||||
@@ -139,17 +139,39 @@ describe('AppiumDriver', () => {
|
||||
await appium.createSession(undefined, undefined, w3cCaps);
|
||||
mockFakeDriver.verify();
|
||||
});
|
||||
it('should call "createSession" with JSONWP capabilities if W3C has incomplete capabilities', async function () {
|
||||
let w3cCaps = {
|
||||
...W3C_CAPS,
|
||||
alwaysMatch: {
|
||||
...W3C_CAPS.alwaysMatch,
|
||||
'appium:someOtherParm': 'someOtherParm',
|
||||
},
|
||||
};
|
||||
|
||||
let jsonwpCaps = {
|
||||
...BASE_CAPS,
|
||||
automationName: 'Fake',
|
||||
someOtherParam: 'someOtherParam',
|
||||
};
|
||||
|
||||
mockFakeDriver.expects("createSession")
|
||||
.once().withArgs(jsonwpCaps, undefined, null)
|
||||
.returns([SESSION_ID, jsonwpCaps]);
|
||||
|
||||
await appium.createSession(jsonwpCaps, undefined, w3cCaps);
|
||||
mockFakeDriver.verify();
|
||||
});
|
||||
});
|
||||
describe('deleteSession', () => {
|
||||
describe('deleteSession', function () {
|
||||
let appium;
|
||||
let mockFakeDriver;
|
||||
beforeEach(() => {
|
||||
beforeEach(function () {
|
||||
[appium, mockFakeDriver] = getDriverAndFakeDriver();
|
||||
});
|
||||
afterEach(() => {
|
||||
afterEach(function () {
|
||||
mockFakeDriver.restore();
|
||||
});
|
||||
it('should remove the session if it is found', async () => {
|
||||
it('should remove the session if it is found', async function () {
|
||||
let [sessionId] = await appium.createSession(BASE_CAPS);
|
||||
let sessions = await appium.getSessions();
|
||||
sessions.should.have.length(1);
|
||||
@@ -157,7 +179,7 @@ describe('AppiumDriver', () => {
|
||||
sessions = await appium.getSessions();
|
||||
sessions.should.have.length(0);
|
||||
});
|
||||
it('should call inner driver\'s deleteSession method', async () => {
|
||||
it('should call inner driver\'s deleteSession method', async function () {
|
||||
const [sessionId] = await appium.createSession(BASE_CAPS);
|
||||
mockFakeDriver.expects("deleteSession")
|
||||
.once().withExactArgs(sessionId, [])
|
||||
@@ -169,23 +191,23 @@ describe('AppiumDriver', () => {
|
||||
await mockFakeDriver.object.deleteSession();
|
||||
});
|
||||
});
|
||||
describe('getSessions', () => {
|
||||
describe('getSessions', function () {
|
||||
let appium;
|
||||
let sessions;
|
||||
before(() => {
|
||||
before(function () {
|
||||
appium = new AppiumDriver({});
|
||||
});
|
||||
afterEach(async () => {
|
||||
afterEach(async function () {
|
||||
for (let session of sessions) {
|
||||
await appium.deleteSession(session.id);
|
||||
}
|
||||
});
|
||||
it('should return an empty array of sessions', async () => {
|
||||
it('should return an empty array of sessions', async function () {
|
||||
sessions = await appium.getSessions();
|
||||
sessions.should.be.an.array;
|
||||
sessions.should.be.empty;
|
||||
});
|
||||
it('should return sessions created', async () => {
|
||||
it('should return sessions created', async function () {
|
||||
let session1 = await appium.createSession(_.extend(_.clone(BASE_CAPS), {cap: 'value'}));
|
||||
let session2 = await appium.createSession(_.extend(_.clone(BASE_CAPS), {cap: 'other value'}));
|
||||
|
||||
@@ -198,32 +220,32 @@ describe('AppiumDriver', () => {
|
||||
sessions[1].capabilities.should.eql(session2[1]);
|
||||
});
|
||||
});
|
||||
describe('getStatus', () => {
|
||||
describe('getStatus', function () {
|
||||
let appium;
|
||||
before(() => {
|
||||
before(function () {
|
||||
appium = new AppiumDriver({});
|
||||
});
|
||||
it('should return a status', async () => {
|
||||
it('should return a status', async function () {
|
||||
let status = await appium.getStatus();
|
||||
status.build.should.exist;
|
||||
status.build.version.should.exist;
|
||||
});
|
||||
});
|
||||
describe('sessionExists', () => {
|
||||
describe('sessionExists', function () {
|
||||
});
|
||||
describe('attachUnexpectedShutdownHandler', () => {
|
||||
describe('attachUnexpectedShutdownHandler', function () {
|
||||
let appium
|
||||
, mockFakeDriver;
|
||||
beforeEach(() => {
|
||||
beforeEach(function () {
|
||||
[appium, mockFakeDriver] = getDriverAndFakeDriver();
|
||||
});
|
||||
afterEach(async () => {
|
||||
afterEach(async function () {
|
||||
await mockFakeDriver.object.deleteSession();
|
||||
mockFakeDriver.restore();
|
||||
appium.args.defaultCapabilities = {};
|
||||
});
|
||||
|
||||
it('should remove session if inner driver unexpectedly exits with an error', async () => {
|
||||
it('should remove session if inner driver unexpectedly exits with an error', async function () {
|
||||
let [sessionId,] = await appium.createSession(_.clone(BASE_CAPS)); // eslint-disable-line comma-spacing
|
||||
_.keys(appium.sessions).should.contain(sessionId);
|
||||
appium.sessions[sessionId].unexpectedShutdownDeferred.reject(new Error("Oops"));
|
||||
@@ -231,7 +253,7 @@ describe('AppiumDriver', () => {
|
||||
await sleep(1);
|
||||
_.keys(appium.sessions).should.not.contain(sessionId);
|
||||
});
|
||||
it('should remove session if inner driver unexpectedly exits with no error', async () => {
|
||||
it('should remove session if inner driver unexpectedly exits with no error', async function () {
|
||||
let [sessionId,] = await appium.createSession(_.clone(BASE_CAPS)); // eslint-disable-line comma-spacing
|
||||
_.keys(appium.sessions).should.contain(sessionId);
|
||||
appium.sessions[sessionId].unexpectedShutdownDeferred.resolve();
|
||||
@@ -239,7 +261,7 @@ describe('AppiumDriver', () => {
|
||||
await sleep(1);
|
||||
_.keys(appium.sessions).should.not.contain(sessionId);
|
||||
});
|
||||
it('should not remove session if inner driver cancels unexpected exit', async () => {
|
||||
it('should not remove session if inner driver cancels unexpected exit', async function () {
|
||||
let [sessionId,] = await appium.createSession(_.clone(BASE_CAPS)); // eslint-disable-line comma-spacing
|
||||
_.keys(appium.sessions).should.contain(sessionId);
|
||||
appium.sessions[sessionId].onUnexpectedShutdown.cancel();
|
||||
@@ -248,12 +270,12 @@ describe('AppiumDriver', () => {
|
||||
_.keys(appium.sessions).should.contain(sessionId);
|
||||
});
|
||||
});
|
||||
describe('getDriverForCaps', () => {
|
||||
it('should not blow up if user does not provide platformName', () => {
|
||||
describe('getDriverForCaps', function () {
|
||||
it('should not blow up if user does not provide platformName', function () {
|
||||
let appium = new AppiumDriver({});
|
||||
(() => { appium.getDriverForCaps({}); }).should.throw(/platformName/);
|
||||
});
|
||||
it('should get XCUITestDriver driver for automationName of XCUITest', () => {
|
||||
it('should get XCUITestDriver driver for automationName of XCUITest', function () {
|
||||
let appium = new AppiumDriver({});
|
||||
let driver = appium.getDriverForCaps({
|
||||
platformName: 'iOS',
|
||||
@@ -262,7 +284,7 @@ describe('AppiumDriver', () => {
|
||||
driver.should.be.an.instanceof(Function);
|
||||
driver.should.equal(XCUITestDriver);
|
||||
});
|
||||
it('should get iosdriver for ios < 10', () => {
|
||||
it('should get iosdriver for ios < 10', function () {
|
||||
let appium = new AppiumDriver({});
|
||||
let caps = {
|
||||
platformName: 'iOS',
|
||||
@@ -292,7 +314,7 @@ describe('AppiumDriver', () => {
|
||||
driver = appium.getDriverForCaps(caps);
|
||||
driver.should.equal(IosDriver);
|
||||
});
|
||||
it('should get xcuitestdriver for ios >= 10', () => {
|
||||
it('should get xcuitestdriver for ios >= 10', function () {
|
||||
let appium = new AppiumDriver({});
|
||||
let caps = {
|
||||
platformName: 'iOS',
|
||||
|
||||
+2
-2
@@ -12,12 +12,12 @@ const TEST_FAKE_APP = path.resolve(__dirname, "..", "..", "node_modules",
|
||||
function initSession (caps) {
|
||||
let resolve = () => {};
|
||||
let driver;
|
||||
before(async () => {
|
||||
before(async function () {
|
||||
driver = wd.promiseChainRemote({host: TEST_HOST, port: TEST_PORT});
|
||||
resolve(driver);
|
||||
await driver.init(caps);
|
||||
});
|
||||
after(async () => {
|
||||
after(async function () {
|
||||
await driver.quit();
|
||||
});
|
||||
return new B((_resolve) => {
|
||||
|
||||
@@ -10,19 +10,19 @@ let forceLogs = process.env._FORCE_LOGS;
|
||||
process.env._FORCE_LOGS = 1;
|
||||
let log = logger.getLogger('Appium');
|
||||
|
||||
describe('logging', () => {
|
||||
describe('logging', function () {
|
||||
let stderrSpy;
|
||||
let stdoutSpy;
|
||||
beforeEach(() => {
|
||||
beforeEach(function () {
|
||||
stderrSpy = sinon.spy(process.stderr, 'write');
|
||||
stdoutSpy = sinon.spy(process.stdout, 'write');
|
||||
logsinkClear();
|
||||
});
|
||||
afterEach(() => {
|
||||
afterEach(function () {
|
||||
stderrSpy.restore();
|
||||
stdoutSpy.restore();
|
||||
});
|
||||
after(() => {
|
||||
after(function () {
|
||||
process.env._FORCE_LOGS = forceLogs;
|
||||
});
|
||||
|
||||
@@ -36,7 +36,7 @@ describe('logging', () => {
|
||||
log.debug(debugMsg);
|
||||
}
|
||||
|
||||
it('should send error, info and debug when loglevel is debug', async () => {
|
||||
it('should send error, info and debug when loglevel is debug', async function () {
|
||||
await logsinkInit({loglevel: 'debug'});
|
||||
|
||||
doLogging();
|
||||
@@ -48,7 +48,7 @@ describe('logging', () => {
|
||||
stdoutSpy.args[0][0].should.include(warnMsg);
|
||||
stdoutSpy.args[1][0].should.include(debugMsg);
|
||||
});
|
||||
it('should send error and info when loglevel is info', async () => {
|
||||
it('should send error and info when loglevel is info', async function () {
|
||||
await logsinkInit({loglevel: 'info'});
|
||||
|
||||
doLogging();
|
||||
@@ -59,7 +59,7 @@ describe('logging', () => {
|
||||
stdoutSpy.callCount.should.equal(1);
|
||||
stdoutSpy.args[0][0].should.include(warnMsg);
|
||||
});
|
||||
it('should send error when loglevel is error', async () => {
|
||||
it('should send error when loglevel is error', async function () {
|
||||
await logsinkInit({loglevel: 'error'});
|
||||
|
||||
doLogging();
|
||||
|
||||
@@ -5,43 +5,43 @@ import chai from 'chai';
|
||||
|
||||
const should = chai.should();
|
||||
|
||||
describe('Parser', () => {
|
||||
describe('Parser', function () {
|
||||
let p = getParser();
|
||||
p.debug = true; // throw instead of exit on error; pass as option instead?
|
||||
it('should return an arg parser', () => {
|
||||
it('should return an arg parser', function () {
|
||||
should.exist(p.parseArgs);
|
||||
p.parseArgs([]).should.have.property('port');
|
||||
});
|
||||
it('should keep the raw server flags array', () => {
|
||||
it('should keep the raw server flags array', function () {
|
||||
should.exist(p.rawArgs);
|
||||
});
|
||||
it('should have help for every arg', () => {
|
||||
it('should have help for every arg', function () {
|
||||
for (let arg of p.rawArgs) {
|
||||
arg[1].should.have.property('help');
|
||||
}
|
||||
});
|
||||
it('should throw an error with unknown argument', () => {
|
||||
it('should throw an error with unknown argument', function () {
|
||||
(() => {p.parseArgs(['--apple']);}).should.throw();
|
||||
});
|
||||
it('should parse default capabilities correctly from a string', () => {
|
||||
it('should parse default capabilities correctly from a string', function () {
|
||||
let defaultCapabilities = {a: 'b'};
|
||||
let args = p.parseArgs(['--default-capabilities',
|
||||
JSON.stringify(defaultCapabilities)]);
|
||||
args.defaultCapabilities.should.eql(defaultCapabilities);
|
||||
});
|
||||
it('should parse default capabilities correctly from a file', () => {
|
||||
it('should parse default capabilities correctly from a file', function () {
|
||||
let defaultCapabilities = {a: 'b'};
|
||||
let args = p.parseArgs(['--default-capabilities',
|
||||
'test/fixtures/caps.json']);
|
||||
args.defaultCapabilities.should.eql(defaultCapabilities);
|
||||
});
|
||||
it('should throw an error with invalid arg to default capabilities', () => {
|
||||
it('should throw an error with invalid arg to default capabilities', function () {
|
||||
(() => {p.parseArgs(['-dc', '42']);}).should.throw();
|
||||
(() => {p.parseArgs(['-dc', 'false']);}).should.throw();
|
||||
(() => {p.parseArgs(['-dc', 'null']);}).should.throw();
|
||||
(() => {p.parseArgs(['-dc', 'does/not/exist.json']);}).should.throw();
|
||||
});
|
||||
it('should parse args that are caps into default capabilities', () => {
|
||||
it('should parse args that are caps into default capabilities', function () {
|
||||
let defaultCapabilities = {localizableStringsDir: '/my/dir'};
|
||||
let args = p.parseArgs(['--localizable-strings-dir', '/my/dir']);
|
||||
args.defaultCapabilities.should.eql(defaultCapabilities);
|
||||
|
||||
@@ -8,12 +8,12 @@ import chaiAsPromised from 'chai-as-promised';
|
||||
chai.use(chaiAsPromised);
|
||||
const expect = chai.expect;
|
||||
|
||||
describe.skip('shrinkwrap checks', () => {
|
||||
it('shrinkwrap file should exist', async () => {
|
||||
describe.skip('shrinkwrap checks', function () {
|
||||
it('shrinkwrap file should exist', async function () {
|
||||
require('../../npm-shrinkwrap.json');
|
||||
});
|
||||
|
||||
it('shrinkwrap should not include fsevents', async () => {
|
||||
it('shrinkwrap should not include fsevents', async function () {
|
||||
// fsevents is an optional dep that only works on Mac.
|
||||
// if it's in shrinkwrap, non-Mac hosts won't be able to install appium
|
||||
let shrinkwrap = require('../../npm-shrinkwrap.json');
|
||||
|
||||
+40
-70
@@ -8,91 +8,33 @@ const should = chai.should();
|
||||
chai.use(chaiAsPromised);
|
||||
|
||||
describe('utils', function () {
|
||||
describe('parseCapsForInnerDriver()', () => {
|
||||
describe('parseCapsForInnerDriver()', function () {
|
||||
it('should return JSONWP caps unchanged if only JSONWP caps provided', function () {
|
||||
let {desiredCaps, processedJsonwpCaps, processedW3CCapabilities} = parseCapsForInnerDriver(BASE_CAPS);
|
||||
let {desiredCaps, processedJsonwpCapabilities, processedW3CCapabilities} = parseCapsForInnerDriver(BASE_CAPS);
|
||||
desiredCaps.should.deep.equal(BASE_CAPS);
|
||||
processedJsonwpCaps.should.deep.equal(BASE_CAPS);
|
||||
processedJsonwpCapabilities.should.deep.equal(BASE_CAPS);
|
||||
should.not.exist(processedW3CCapabilities);
|
||||
});
|
||||
it('should return W3C caps unchanged if only W3C caps were provided', function () {
|
||||
let {desiredCaps, processedJsonwpCaps, processedW3CCapabilities} = parseCapsForInnerDriver(undefined, W3C_CAPS);
|
||||
let {desiredCaps, processedJsonwpCapabilities, processedW3CCapabilities} = parseCapsForInnerDriver(undefined, W3C_CAPS);
|
||||
desiredCaps.should.deep.equal(BASE_CAPS);
|
||||
should.not.exist(processedJsonwpCaps);
|
||||
should.not.exist(processedJsonwpCapabilities);
|
||||
processedW3CCapabilities.should.deep.equal(W3C_CAPS);
|
||||
});
|
||||
it('should return JSONWP and W3C caps if both were provided', function () {
|
||||
let {desiredCaps, processedJsonwpCaps, processedW3CCapabilities} = parseCapsForInnerDriver(BASE_CAPS, W3C_CAPS);
|
||||
let {desiredCaps, processedJsonwpCapabilities, processedW3CCapabilities} = parseCapsForInnerDriver(BASE_CAPS, W3C_CAPS);
|
||||
desiredCaps.should.deep.equal(BASE_CAPS);
|
||||
processedJsonwpCaps.should.deep.equal(BASE_CAPS);
|
||||
processedJsonwpCapabilities.should.deep.equal(BASE_CAPS);
|
||||
processedW3CCapabilities.should.deep.equal(W3C_CAPS);
|
||||
});
|
||||
it('should merge the capabilities together if some are different', function () {
|
||||
let jsonwpCaps = {
|
||||
...BASE_CAPS,
|
||||
foo: 'bar',
|
||||
};
|
||||
let w3cCaps = {
|
||||
...W3C_CAPS,
|
||||
alwaysMatch: {
|
||||
...W3C_CAPS.alwaysMatch,
|
||||
'appium:hello': 'world',
|
||||
}
|
||||
};
|
||||
let {desiredCaps, processedJsonwpCaps, processedW3CCapabilities} = parseCapsForInnerDriver(jsonwpCaps, w3cCaps);
|
||||
|
||||
let expectedCaps = {
|
||||
...jsonwpCaps,
|
||||
'hello': 'world'
|
||||
};
|
||||
|
||||
desiredCaps.should.deep.equal(expectedCaps);
|
||||
processedJsonwpCaps.should.deep.equal(expectedCaps);
|
||||
processedW3CCapabilities.should.deep.equal({
|
||||
alwaysMatch: {...insertAppiumPrefixes(expectedCaps)},
|
||||
firstMatch: [{}],
|
||||
});
|
||||
});
|
||||
it('should merge the capabilities together and give preference to W3C if some are matching', function () {
|
||||
// Add caps to JSONWP caps
|
||||
let jsonwpCaps = {
|
||||
foo: 'bar',
|
||||
...BASE_CAPS,
|
||||
};
|
||||
|
||||
// Add caps to W3C caps
|
||||
let w3cCaps = {
|
||||
alwaysMatch: {
|
||||
hello: 'world',
|
||||
foo: 'BAR',
|
||||
...BASE_CAPS,
|
||||
},
|
||||
firstMatch: [{}],
|
||||
};
|
||||
|
||||
// Expected result is that w3c caps override jsonwp caps
|
||||
let expectedDesiredCaps = {
|
||||
...jsonwpCaps,
|
||||
...w3cCaps.alwaysMatch,
|
||||
};
|
||||
|
||||
let {desiredCaps, processedJsonwpCaps, processedW3CCapabilities} = parseCapsForInnerDriver(jsonwpCaps, w3cCaps);
|
||||
desiredCaps.foo.should.equal('BAR');
|
||||
desiredCaps.should.deep.equal(expectedDesiredCaps);
|
||||
processedJsonwpCaps.should.deep.equal(expectedDesiredCaps);
|
||||
processedW3CCapabilities.should.deep.equal({
|
||||
alwaysMatch: insertAppiumPrefixes(expectedDesiredCaps),
|
||||
firstMatch: [{}],
|
||||
});
|
||||
});
|
||||
it('should include default capabilities in results', function () {
|
||||
let {desiredCaps, processedJsonwpCaps, processedW3CCapabilities} = parseCapsForInnerDriver(BASE_CAPS, W3C_CAPS, {}, {foo: 'bar'});
|
||||
let {desiredCaps, processedJsonwpCapabilities, processedW3CCapabilities} = parseCapsForInnerDriver(BASE_CAPS, W3C_CAPS, {}, {foo: 'bar'});
|
||||
desiredCaps.should.deep.equal({foo: 'bar', ...BASE_CAPS});
|
||||
processedJsonwpCaps.should.deep.equal({foo: 'bar', ...BASE_CAPS});
|
||||
processedJsonwpCapabilities.should.deep.equal({foo: 'bar', ...BASE_CAPS});
|
||||
processedW3CCapabilities.alwaysMatch.should.deep.equal({'appium:foo': 'bar', ...insertAppiumPrefixes(BASE_CAPS)});
|
||||
});
|
||||
it('should reject if W3C caps are not passing constraints', function () {
|
||||
(() => parseCapsForInnerDriver(BASE_CAPS, W3C_CAPS, {hello: {presence: true}})).should.throw(/'hello' can't be blank/);
|
||||
(() => parseCapsForInnerDriver(undefined, W3C_CAPS, {hello: {presence: true}})).should.throw(/'hello' can't be blank/);
|
||||
});
|
||||
it('should only accept W3C caps that have passing constraints', function () {
|
||||
let w3cCaps = {
|
||||
@@ -102,10 +44,10 @@ describe('utils', function () {
|
||||
{hello: 'world'},
|
||||
],
|
||||
};
|
||||
let {desiredCaps, processedJsonwpCaps, processedW3CCapabilities} = parseCapsForInnerDriver(BASE_CAPS, w3cCaps, {hello: {presence: true}});
|
||||
let {desiredCaps, processedJsonwpCapabilities, processedW3CCapabilities} = parseCapsForInnerDriver(BASE_CAPS, w3cCaps, {hello: {presence: true}});
|
||||
const expectedResult = {hello: 'world', ...BASE_CAPS};
|
||||
desiredCaps.should.deep.equal(expectedResult);
|
||||
processedJsonwpCaps.should.deep.equal(expectedResult);
|
||||
processedJsonwpCapabilities.should.deep.equal({...BASE_CAPS});
|
||||
processedW3CCapabilities.alwaysMatch.should.deep.equal(insertAppiumPrefixes(expectedResult));
|
||||
});
|
||||
it('should add appium prefixes to W3C caps that are not standard in W3C', function () {
|
||||
@@ -119,6 +61,34 @@ describe('utils', function () {
|
||||
firstMatch: [{}],
|
||||
});
|
||||
});
|
||||
it('should fall back to MJSONWP caps if MJSONWP contains extraneous caps that aren not in W3C', function () {
|
||||
let jsonwpCaps = {
|
||||
...BASE_CAPS,
|
||||
automationName: 'Fake',
|
||||
};
|
||||
const {desiredCaps, processedJsonwpCapabilities, processedW3CCapabilities} = parseCapsForInnerDriver(jsonwpCaps, {
|
||||
alwaysMatch: {platformName: 'Fake', propertyName: 'PROP_NAME'},
|
||||
});
|
||||
|
||||
should.not.exist(processedW3CCapabilities);
|
||||
desiredCaps.should.eql(jsonwpCaps);
|
||||
processedJsonwpCapabilities.should.eql(jsonwpCaps);
|
||||
});
|
||||
it('should fall back to MJSONWP caps if W3C capabilities are invalid', function () {
|
||||
let w3cCapabilities = {
|
||||
alwaysMatch: {platformName: 'Fake', propertyName: 'PROP_NAME'},
|
||||
};
|
||||
let constraints = {
|
||||
deviceName: {
|
||||
presence: true,
|
||||
}
|
||||
};
|
||||
const {desiredCaps, processedJsonwpCapabilities, processedW3CCapabilities} = parseCapsForInnerDriver({...BASE_CAPS}, w3cCapabilities, constraints);
|
||||
|
||||
should.not.exist(processedW3CCapabilities);
|
||||
desiredCaps.should.eql(BASE_CAPS);
|
||||
processedJsonwpCapabilities.should.eql(BASE_CAPS);
|
||||
});
|
||||
});
|
||||
|
||||
describe('insertAppiumPrefixes()', function () {
|
||||
|
||||
Reference in New Issue
Block a user