mirror of
https://github.com/appium/appium.git
synced 2026-11-10 03:44:40 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41a29cef73 | ||
|
|
9786562587 | ||
|
|
943136d69c | ||
|
|
a9442928f2 | ||
|
|
29bd662572 | ||
|
|
97ae607397 | ||
|
|
64453e98d3 | ||
|
|
cf29ba8ce4 | ||
|
|
f6f77293e8 | ||
|
|
ce1b549794 | ||
|
|
6c300e3ba2 | ||
|
|
ffdc47899c | ||
|
|
5b2f7fafbd | ||
|
|
b365ba7caa | ||
|
|
61a99e56ab | ||
|
|
7c3f22c45f | ||
|
|
1fd1c1bb44 | ||
|
|
a27ee52375 | ||
|
|
576a53d714 | ||
|
|
9d1af07681 | ||
|
|
5af75f101e | ||
|
|
bd2a8bff36 | ||
|
|
8f951d1f93 | ||
|
|
fdcfc3088c | ||
|
|
a769778a94 | ||
|
|
5ac788838d | ||
|
|
32855b7cc0 | ||
|
|
c091694985 | ||
|
|
50e11276af | ||
|
|
22886a1937 | ||
|
|
68553ed916 | ||
|
|
6d23089d12 | ||
|
|
8930b9e4ab | ||
|
|
5c1200e23c | ||
|
|
6491fda813 | ||
|
|
288446add2 | ||
|
|
5587d08453 | ||
|
|
ab96bccf28 | ||
|
|
51fdfeb4e0 | ||
|
|
cf19544579 | ||
|
|
42aca68fd1 | ||
|
|
4057a81731 | ||
|
|
2113a4cb45 | ||
|
|
8e3aaf8d44 | ||
|
|
abe9ec34d1 | ||
|
|
7d1082aec1 | ||
|
|
3c6b0889c9 | ||
|
|
e6927c60e2 | ||
|
|
023049fc1e | ||
|
|
68255c4a44 | ||
|
|
1c39f50240 | ||
|
|
22dc8bc5e6 | ||
|
|
13a64d9709 | ||
|
|
586ee9baf6 | ||
|
|
d5abb9f48e | ||
|
|
d143fd3432 | ||
|
|
4ce4d44e87 | ||
|
|
0a831eab47 | ||
|
|
6f8795090d |
+3
-3
@@ -49,7 +49,7 @@
|
||||
[submodule "submodules/deviceconsole"]
|
||||
path = submodules/deviceconsole
|
||||
url = https://github.com/rpetrich/deviceconsole.git
|
||||
[submodule "submodules/appium-ci-gulper"]
|
||||
path = submodules/appium-ci-gulper
|
||||
url = https://github.com/appium/appium-ci-gulper.git
|
||||
[submodule "submodules/appium-ci"]
|
||||
path = submodules/appium-ci
|
||||
url = https://github.com/appium/appium-ci.git
|
||||
branch = master
|
||||
|
||||
@@ -6,6 +6,75 @@ CHANGES IN VERSION 1.5 (from 1.4.?)
|
||||
- add Tapster support for some more touch methods
|
||||
|
||||
|
||||
CHANGES IN VERSION 1.4.8 (from 1.4.7)
|
||||
===================================
|
||||
|
||||
#### General
|
||||
- fix for Windows users! Now the system architecture is properly detected, npm installs should work again
|
||||
|
||||
#### Chromedriver / Android hybrid
|
||||
- fix for an issue in responding to `/status` api endpoint, this is used heavily by Selenium grid
|
||||
|
||||
CHANGES IN VERSION 1.4.7 (from 1.4.6)
|
||||
===================================
|
||||
|
||||
#### Chromedriver / Android hybrid
|
||||
- fix for problem in downstream jsonwp-proxy; now sessionId returned is replaced
|
||||
with sessionId present in url(original sessionId). Fixed this in appium-jsonwp-proxy 1.2.3
|
||||
|
||||
CHANGES IN VERSION 1.4.6 (from 1.4.5)
|
||||
===================================
|
||||
|
||||
#### General
|
||||
- this is a republish because of a misconfiguration before 1.4.5 got published
|
||||
resulting in a server startup failure
|
||||
|
||||
|
||||
CHANGES IN VERSION 1.4.5 (from 1.4.4)
|
||||
===================================
|
||||
|
||||
#### General
|
||||
- fix problem with npm shrinkwrap that caused Appium not to start
|
||||
|
||||
|
||||
CHANGES IN VERSION 1.4.4 (from 1.4.3)
|
||||
===================================
|
||||
|
||||
#### Chromedriver / Android hybrid
|
||||
- fix a problem in downstream appium-chromedriver; no longer rely on async
|
||||
methods returning objects of the Promise type. This was causing undefined
|
||||
errors when running Chromedriver/hybrid tests.
|
||||
|
||||
|
||||
CHANGES IN VERSION 1.4.3 (from 1.4.2)
|
||||
===================================
|
||||
|
||||
#### iOS
|
||||
- update the appium-instruments dependency with working subdeps
|
||||
|
||||
|
||||
CHANGES IN VERSION 1.4.2 (from 1.4.1)
|
||||
===================================
|
||||
|
||||
#### General
|
||||
- update ES6-based dependencies which got bit by a breaking Regenerator
|
||||
change
|
||||
|
||||
#### iOS
|
||||
- keep track of the correct app ID for Safari webviews (fixes problems in
|
||||
finding the webview for iOS)
|
||||
|
||||
#### Android
|
||||
- fix sendKeys and clear for Samsung devices
|
||||
|
||||
|
||||
CHANGES IN VERSION 1.4.1 (from 1.4)
|
||||
===================================
|
||||
|
||||
#### Chromedriver
|
||||
- have the appium-chromedriver manage the chromedriver binary download/install
|
||||
|
||||
|
||||
CHANGES IN VERSION 1.4 (from 1.3.7)
|
||||
======================================
|
||||
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
|
||||
Appium is an open source, cross-platform test automation tool for native, hybrid and mobile web apps, tested on simulators (iOS, FirefoxOS), emulators (Android), and real devices (iOS, Android, FirefoxOS).
|
||||
|
||||
**Note:** we have just recently released Appium 1.0. If you already have a bunch of Appium tests, you might want to check out the [Migrating to 1.0](/docs/en/advanced-concepts/migrating-to-1-0.md) doc!
|
||||
|
||||
### Supported Platforms
|
||||
|
||||
* iOS
|
||||
@@ -133,6 +131,10 @@ Gecko-based platforms.
|
||||
Please take a look at our [contribution documentation](CONTRIBUTING.md)
|
||||
for instructions on how to build, test and run Appium from source.
|
||||
|
||||
### Roadmap
|
||||
|
||||
Interested in where Appium is heading in the future? Check out the [Roadmap](ROADMAP.md)
|
||||
|
||||
### Project Credits & Inspiration
|
||||
|
||||
[Credits](/docs/en/contributing-to-appium/credits.md)
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
Appium Roadmap
|
||||
---------------
|
||||
|
||||
This document is designed to be a living record of the current and projected
|
||||
priorities for the Appium project. It is, of course, always subject to change
|
||||
and exists solely to give the community an idea of where we're going. The
|
||||
roadmap is set by the Appium core team. If you have feature suggestions for
|
||||
Appium, please submit a [GitHub issue](https://github.com/appium/appium/issues)
|
||||
and, if of sufficient size and approved by the team, it will be added here. If
|
||||
you have concerns about the roadmap, you can e-mail the Appium devs at
|
||||
`appium-developers@googlegroups.com`. Note that bugfixes and miscellaneous
|
||||
features are not considered "roadmap projects", and will be addressed in the
|
||||
due course of normal Appium development. This page exists to outline
|
||||
large-scale future plans for Appium.
|
||||
|
||||
## Current Roadmap Projects
|
||||
|
||||
| Project | Description | ETD | Projected Appium Release |
|
||||
|---------|-------------|-----|--------------------------|
|
||||
|[Architecture Overhaul](https://github.com/appium/appium/issues/5169)|Appium's architecture has grown unwieldy enough that adding new features and preventing regressions has become difficult. A rearchitecture is in order to solve these issues as well as migrate the codebase to a form of JS much easier to work in and understand.|8/2015|1.5|
|
||||
|
||||
## Upcoming Projects (in rough order of priority)
|
||||
|
||||
| Project | Description |
|
||||
|---------|-------------|
|
||||
|iOS 8.4 support|Appium should support iOS 8.4 / Xcode 6.2|
|
||||
|Apple Watch support|Appium should support Apple Watch apps via the Watch simulator|
|
||||
|Onboarding Overhaul|The current Appium docs leave a lot to be desired. The format is confusing (it would be better for API docs), the content is outdated, and many things are still undocumented. This project would go back to the drawing board in all of these areas and put together something genuinely helpful for the community, which hopefully reduces the kind of support issues we're currently flooded by.|
|
||||
|New Appium GUIs|The current Appium GUIs (Appium.app and Appium.exe) have a number of issues. They aren't maintained by the core Appium team. They're written in Objective-C and C# respectively: two languages, and specifically, two languages other than Appium's core language. Because of this they lag behind the Appium server and are often broken. However, users love them primarily because of the Inspector feature (the thing that lets them graphically navigate their app), so we have to field a ton of complaints about these projects. There's an opportunity to start over. With [Electron](http://electron.atom.io/), we'd be able to take the new version of Appium we're working on and embed it directly inside a Node-based GUI app written using the languages and technologies the core team is experienced with. We'd have one codebase and could distribute the app on Mac, Windows, and Linux. The GUIs would become an official part of Appium's release cycle so they wouldn't be broken.|
|
||||
|DroidDriver backend integration|Appium's current UiAutomator-based approach to Android automation has a number of issues. We have been investigating [DroidDriver](https://github.com/appium/droiddriver) as a potential replacement. This project (dependend on the rearchitecture) would add DroidDriver as a new `automationBackend`, ideally aiming for feature parity with the current Android support. If successful, the UiAutomator backend would be deprecated and eventually removed.|
|
||||
|Unity3d backend POC|This is a research project to determine whether we can add some kind of support for 3d game automation, probably based on the Unity3d game development platform.|
|
||||
|Dynamic Test App|Our current test applications (the ones used to test Appium's iOS and Android support) are not ideal. We use too many of them and the ones we use have a random hodgepodge of app SDK features, sometimes not enough to sufficiently test automation support. Using [Titanium](https://github.com/appcelerator/titanium_mobile), we can develop an app that allows us to inject whatever native code we need, as we need it. The app thus changes itself test-by-test to have whatever UI features we need for that particular test. It gives us much more fine-grained control over what we're testing. The app code would also be cross-platform and written in JS, which would make it easier for the core team to write and maintain.|
|
||||
|
||||
## Projects Under Consideration (unprioritized)
|
||||
|
||||
| Project | Description |
|
||||
|---------|-------------|
|
||||
|Non-UIAutomation iOS backend|Instruments + UIAutomation has been working well for many Appium users, but requires crazy workarounds to deal with bugs which remain unfixed on the Apple side. We should explore a different approach (a la [Calabash](https://github.com/calabash/calabash-ios)), even though it's in tension with Appium's philosophy of not modifying your app, in order to see if the overall experience is more stable. This new backend would exist alongside the UIAutomation backend.|
|
||||
|Windows Phone Support|There has been some work done on automating Windows Phone apps. We could take this all the way to integration with Appium in the form of a WindowsDriver.|
|
||||
|FirefoxOS Support|Our initial support for FirefoxOS has not been updated. It should be re-added as a FirefoxOSDriver in the new architecture|
|
||||
|
||||
## Completed Roadmap Projects
|
||||
|
||||
| Project | Description | Completed | Appium Release | Notes |
|
||||
|---------|-------------|-----------|----------------|-------|
|
||||
|Appium CI|Appium has unique needs for Continuous Integration because of the highly specialized environments required (iOS simulators, Xcode, Android emulators, the Android SDK, etc...). We therefore need to create a custom CI solution (perhaps sitting underneath Cloudbees or some other OSS-friendly CI server) that can be used for all Appium projects which need integration tests.|5/2015|_N/A_|Usage instructions at [appium/appium-ci](https://github.com/appium/appium-ci)|
|
||||
@@ -2,15 +2,15 @@
|
||||
{ set -e; } 2>/dev/null
|
||||
{ set -x; } 2>/dev/null
|
||||
|
||||
# Loading appium-ci-gulper submodule
|
||||
# Loading appium-ci submodule
|
||||
export APPIUM_ROOT="$(pwd)"
|
||||
git submodule update --remote --init submodules/appium-ci-gulper
|
||||
pushd submodules/appium-ci-gulper
|
||||
git submodule update --remote --init submodules/appium-ci
|
||||
pushd submodules/appium-ci
|
||||
git pull origin master
|
||||
{ set +x; } 2>/dev/null
|
||||
popd
|
||||
|
||||
# Running appium-ci-gulper submodule
|
||||
# This sets up node etc...
|
||||
pushd submodules/appium-ci/jenkins-gulper
|
||||
source ./setup
|
||||
{ set -x; } 2>/dev/null
|
||||
|
||||
|
||||
@@ -86,3 +86,4 @@
|
||||
|`sendKeyStrategy`| strategy to use to type test into a test field. Simulator default: `oneByOne`. Real device default: `grouped` |`oneByOne`, `grouped` or `setValue`|
|
||||
|`screenshotWaitTimeout`| Max timeout in sec to wait for a screenshot to be generated. default: 10 |e.g., `5`|
|
||||
|`waitForAppScript`| The ios automation script used to determined if the app has been launched, by default the system wait for the page source not to be empty. The result must be a boolean |e.g. `true;`, `target.elements().length > 0;`, `$.delay(5000); true;` |
|
||||
|`webviewConnectRetries`| Number of times to send connection message to remote debugger, to get webview. Default: `8` |e.g., `12`|
|
||||
|
||||
@@ -6,7 +6,8 @@ var logger = require('../../server/logger.js').get('appium')
|
||||
, UnknownError = errors.UnknownError
|
||||
, async = require('async')
|
||||
, Chromedriver = require('appium-chromedriver')
|
||||
, status = require("../../server/status.js");
|
||||
, status = require("../../server/status.js")
|
||||
, Q = require('q');
|
||||
|
||||
var androidHybrid = {};
|
||||
|
||||
@@ -168,15 +169,12 @@ androidHybrid.setupNewChromedriver = function (context, cb) {
|
||||
this.onChromedriverStop(context);
|
||||
}
|
||||
}.bind(this));
|
||||
this.chromedriver.start(caps).nodeify(function (err) {
|
||||
if (err) return cb(err);
|
||||
new Q(this.chromedriver.start(caps)).then(function () {
|
||||
// save the chromedriver object under the context
|
||||
this.sessionChromedrivers[context] = this.chromedriver;
|
||||
cb();
|
||||
}.bind(this));
|
||||
}.bind(this)).nodeify(cb);
|
||||
};
|
||||
|
||||
|
||||
androidHybrid.setupExistingChromedriver = function (context, cb) {
|
||||
logger.debug("Found existing Chromedriver for context '" + context + "'." +
|
||||
" Using it.");
|
||||
@@ -187,18 +185,15 @@ androidHybrid.setupExistingChromedriver = function (context, cb) {
|
||||
|
||||
// check the status by sending a simple window-based command to ChromeDriver
|
||||
// if there is an error, we want to recreate the ChromeDriver session
|
||||
this.chromedriver.hasWorkingWebview().nodeify(function (err, works) {
|
||||
if (err) return cb(err);
|
||||
if (works) return cb();
|
||||
new Q(this.chromedriver.hasWorkingWebview()).then(function (works) {
|
||||
if (works) return;
|
||||
logger.debug("ChromeDriver is not associated with a window. " +
|
||||
"Re-initializing the session.");
|
||||
this.chromedriverRestartingContext = context;
|
||||
this.chromedriver.restart().nodeify(function (err) {
|
||||
if (err) return cb(err);
|
||||
return new Q(this.chromedriver.restart()).then(function () {
|
||||
this.chromedriverRestartingContext = null;
|
||||
cb();
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}.bind(this)).nodeify(cb);
|
||||
};
|
||||
|
||||
androidHybrid.onChromedriverStop = function (context) {
|
||||
@@ -237,12 +232,10 @@ androidHybrid.stopChromedriverProxies = function (ocb) {
|
||||
logger.debug("Stopping chromedriver for context " + context);
|
||||
// stop listening for the stopped state event
|
||||
this.sessionChromedrivers[context].removeAllListeners(Chromedriver.EVENT_CHANGED);
|
||||
this.sessionChromedrivers[context].stop().nodeify(function (err) {
|
||||
if (err) logger.warn("Error stopping Chromedriver: " + err.message);
|
||||
// chromedriver isn't valid anymore, so remove it from context list
|
||||
delete this.sessionChromedrivers[context];
|
||||
cb();
|
||||
}.bind(this));
|
||||
new Q(this.sessionChromedrivers[context].stop())
|
||||
.catch(function (err) { logger.warn("Error stopping Chromedriver: " + err.message); })
|
||||
.finally(function () { delete this.sessionChromedrivers[context]; }.bind(this))
|
||||
.nodeify(cb);
|
||||
}.bind(this), function (err) {
|
||||
// if one of these fails, go back to last proxy state and error out
|
||||
this.restoreProxyState();
|
||||
|
||||
@@ -41,7 +41,7 @@ public class AndroidElement {
|
||||
public boolean click() throws UiObjectNotFoundException {
|
||||
return el.click();
|
||||
}
|
||||
|
||||
|
||||
public boolean exists() {
|
||||
return el.exists();
|
||||
}
|
||||
@@ -65,13 +65,13 @@ public class AndroidElement {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Point getAbsolutePosition(final Point point)
|
||||
throws UiObjectNotFoundException, InvalidCoordinatesException {
|
||||
final Rect rect = this.getBounds();
|
||||
|
||||
|
||||
Logger.debug("Element bounds: " + rect.toShortString());
|
||||
|
||||
|
||||
return PositionHelper.getAbsolutePosition(point, rect, new Point(rect.left, rect.top), false);
|
||||
}
|
||||
|
||||
@@ -171,6 +171,8 @@ public class AndroidElement {
|
||||
if (res.equals("")) {
|
||||
res = getText();
|
||||
}
|
||||
} else if (attr.equals("contentDescription")) {
|
||||
res = getContentDesc();
|
||||
} else if (attr.equals("text")) {
|
||||
res = getText();
|
||||
} else if (attr.equals("className")) {
|
||||
|
||||
+3
-2
@@ -33,8 +33,9 @@ public class GetAttribute extends CommandHandler {
|
||||
try {
|
||||
final AndroidElement el = command.getElement();
|
||||
final String attr = params.get("attribute").toString();
|
||||
if (attr.equals("name") || attr.equals("text")
|
||||
|| attr.equals("className") || attr.equals("resourceId")) {
|
||||
if (attr.equals("name") || attr.equals("contentDescription")
|
||||
|| attr.equals("text") || attr.equals("className")
|
||||
|| attr.equals("resourceId")) {
|
||||
return getSuccessResult(el.getStringAttribute(attr));
|
||||
} else {
|
||||
return getSuccessResult(String.valueOf(el.getBoolAttribute(attr)));
|
||||
|
||||
@@ -8,7 +8,8 @@ var Android = require('./android.js')
|
||||
, deviceCommon = require('../common.js')
|
||||
, jwpSuccess = deviceCommon.jwpSuccess
|
||||
, async = require('async')
|
||||
, UiAutomator = require('./uiautomator.js');
|
||||
, UiAutomator = require('./uiautomator.js')
|
||||
, Q = require('q');
|
||||
|
||||
var NATIVE_WIN = "NATIVE_APP";
|
||||
var WEBVIEW_WIN = "WEBVIEW";
|
||||
@@ -33,12 +34,15 @@ ChromeAndroid.prototype.setChromedriverMode = function () {
|
||||
// when proxying to chromedriver, we need to make sure the context endpoints
|
||||
// are trapped by appium for its own purposes
|
||||
this.avoidProxy = [
|
||||
['GET', new RegExp('^/wd/hub/status.*')],
|
||||
['POST', new RegExp('^/wd/hub/session/[^/]+/context')],
|
||||
['GET', new RegExp('^/wd/hub/session/[^/]+/context')],
|
||||
['POST', new RegExp('^/wd/hub/session/[^/]+/touch/perform')],
|
||||
['POST', new RegExp('^/wd/hub/session/[^/]+/touch/multi/perform')],
|
||||
['POST', new RegExp('^/wd/hub/session/[^/]+/orientation')],
|
||||
['GET', new RegExp('^/wd/hub/session/[^/]+/orientation')]
|
||||
['GET', new RegExp('^/wd/hub/session/[^/]+/orientation')],
|
||||
['POST', new RegExp('^/wd/hub/session/[^/]+/appium')],
|
||||
['GET', new RegExp('^/wd/hub/session/[^/]+/appium')]
|
||||
];
|
||||
};
|
||||
|
||||
@@ -153,20 +157,20 @@ ChromeAndroid.prototype.createSession = function (cb) {
|
||||
this.onChromedriverStop(this.onDieCb);
|
||||
}
|
||||
}.bind(this));
|
||||
this.chromedriver.start(caps).nodeify(function (err) {
|
||||
if (err) return cb(err);
|
||||
cb(null, this.chromedriver.sessionId());
|
||||
}.bind(this));
|
||||
new Q(this.chromedriver.start(caps)).then(function () {
|
||||
return this.chromedriver.sessionId();
|
||||
}.bind(this)).nodeify(cb);
|
||||
};
|
||||
|
||||
ChromeAndroid.prototype.stop = function (cb) {
|
||||
// stop listening for the stopped state event
|
||||
this.chromedriver.removeAllListeners(Chromedriver.EVENT_CHANGED);
|
||||
// now we can handle the stop on our own
|
||||
this.chromedriver.stop().nodeify(function (err) {
|
||||
if (err) logger.warn("Error stopping Chromedriver: " + err.message);
|
||||
this.onChromedriverStop(cb);
|
||||
}.bind(this));
|
||||
new Q(this.chromedriver.stop())
|
||||
.catch(function (err) { logger.warn("Error stopping Chromedriver: " + err.message); })
|
||||
.finally(function () {
|
||||
this.onChromedriverStop(cb); // TODO: do not rely on class method for callback
|
||||
}.bind(this)).done();
|
||||
};
|
||||
|
||||
ChromeAndroid.prototype.onChromedriverStop = function (cb) {
|
||||
|
||||
@@ -519,13 +519,12 @@ iOSController._getFullPath = function (remotePath, cb) {
|
||||
var appName = null;
|
||||
|
||||
if (this.args.app) {
|
||||
var appNameRegex = new RegExp("\\" + path.sep + "(\\w+\\.app)");
|
||||
var appNameRegex = new RegExp("\\" + path.sep + "([\\w-]+\\.app)");
|
||||
var appNameMatches = appNameRegex.exec(this.args.app);
|
||||
if (appNameMatches) {
|
||||
appName = appNameMatches[1];
|
||||
}
|
||||
}
|
||||
|
||||
// de-absolutize the path
|
||||
if (isWindows()) {
|
||||
if (remotePath.indexof("://") === 1) {
|
||||
@@ -717,7 +716,7 @@ iOSController.lookForAlert = function (cb, counter, looks, timeout) {
|
||||
// so we can proxy the alert detection functionality
|
||||
this.alertCounter++;
|
||||
this.proxy("au.alertIsPresent()", function (err, res) {
|
||||
if (res !== null) {
|
||||
if (res) {
|
||||
if (res.value === true) {
|
||||
logger.debug("Found an alert, returning control to client");
|
||||
this.returnedFromExecuteAtom[counter] = true;
|
||||
|
||||
@@ -77,9 +77,11 @@ iOSHybrid.listWebFrames = function (cb, exitCb) {
|
||||
if (this.remoteAppKey === null) {
|
||||
return cb(new Error("remote debug app key must be set"));
|
||||
}
|
||||
this.remote.selectApp(this.remoteAppKey, function (res) {
|
||||
this.remote.selectApp(this.remoteAppKey, function (err, perhapsModifiedAppKey, res) {
|
||||
if (err) return onDone(err);
|
||||
this.remoteAppKey = perhapsModifiedAppKey;
|
||||
onDone(null, res);
|
||||
});
|
||||
}.bind(this), this.args.webviewConnectRetries);
|
||||
}
|
||||
} else {
|
||||
if (this.args.udid !== null) {
|
||||
@@ -107,9 +109,11 @@ iOSHybrid.listWebFrames = function (cb, exitCb) {
|
||||
}
|
||||
logger.debug("Using remote debugger app key: " + appKey);
|
||||
this.remoteAppKey = appKey;
|
||||
this.remote.selectApp(appKey, function (res) {
|
||||
this.remote.selectApp(appKey, function (err, perhapsModifiedAppKey, res) {
|
||||
if (err) return onDone(err);
|
||||
this.remoteAppKey = perhapsModifiedAppKey;
|
||||
onDone(null, res);
|
||||
});
|
||||
}.bind(this), this.args.webviewConnectRetries);
|
||||
}.bind(this), this.onPageChange.bind(this));
|
||||
var loopCloseRuns = 0;
|
||||
var loopClose = function () {
|
||||
|
||||
+10
-2
@@ -1494,7 +1494,7 @@ IOS.prototype.removeApp = function (bundleId, cb) {
|
||||
if (this.args.udid) {
|
||||
this.realDevice.remove(bundleId, cb);
|
||||
} else {
|
||||
cb(new Error("You can not call removeApp for the iOS simulator!"));
|
||||
this.sim.remove(bundleId, cb);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1502,7 +1502,15 @@ IOS.prototype.installApp = function (unzippedAppPath, cb) {
|
||||
if (this.args.udid) {
|
||||
this.realDevice.install(unzippedAppPath, cb);
|
||||
} else {
|
||||
cb(new Error("You can not call installApp for the iOS simulator!"));
|
||||
this.sim.install(unzippedAppPath, cb);
|
||||
}
|
||||
};
|
||||
|
||||
IOS.prototype.startApp = function (args, cb) {
|
||||
if (this.args.udid) {
|
||||
cb(new Error("You can not call startApp for a real device!"));
|
||||
} else {
|
||||
this.sim.launch(args.appPackage, cb);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ var net = require('net')
|
||||
// CONFIG
|
||||
// ====================================
|
||||
|
||||
var CONNECTION_RETRIES = 8;
|
||||
|
||||
var RemoteDebugger = function (onDisconnect) {
|
||||
this.init(2, onDisconnect);
|
||||
@@ -131,12 +132,13 @@ RemoteDebugger.prototype.setConnectionKey = function (cb) {
|
||||
});
|
||||
};
|
||||
|
||||
RemoteDebugger.prototype.selectApp = function (appIdKey, cb, tries) {
|
||||
RemoteDebugger.prototype.selectApp = function (appIdKey, cb, maxTries, tries) {
|
||||
if (_.isUndefined(maxTries)) maxTries = CONNECTION_RETRIES;
|
||||
if (_.isUndefined(tries)) tries = 1;
|
||||
try { assert.ok(this.connId); } catch (err) { return cb(err); }
|
||||
this.appIdKey = appIdKey;
|
||||
var connectToApp = messages.connectToApp(this.connId, this.appIdKey);
|
||||
this.logger.debug("Selecting app " + this.appIdKey + " (try #" + tries + ")");
|
||||
this.logger.debug("Selecting app " + this.appIdKey + " (try #" + tries + " of " + maxTries + ")");
|
||||
var responded = false;
|
||||
|
||||
var onConnect = function (appIdKey, pageDict) {
|
||||
@@ -150,25 +152,26 @@ RemoteDebugger.prototype.selectApp = function (appIdKey, cb, tries) {
|
||||
return onRequiresRetry(this.appIdKey, msg);
|
||||
}
|
||||
if (responded) return;
|
||||
this.logger.debug('Connected to app ' + appIdKey + ' [' + JSON.stringify(this.pageArrayFromDict(pageDict)) + ']');
|
||||
responded = true;
|
||||
cb(this.pageArrayFromDict(pageDict));
|
||||
cb(null, appIdKey, this.pageArrayFromDict(pageDict));
|
||||
this.specialCbs['_rpc_forwardGetListing:'] = this.onPageChange.bind(this);
|
||||
}.bind(this);
|
||||
|
||||
var onRequiresRetry = function (correctAppIdKey, msg) {
|
||||
if (responded) return;
|
||||
responded = true;
|
||||
if (tries < 4) {
|
||||
if (tries < maxTries) {
|
||||
if (!msg) {
|
||||
msg = "We were notified we connected to possibly the wrong " +
|
||||
"app. Using the id key suggested and trying again";
|
||||
}
|
||||
this.logger.info(msg);
|
||||
this.selectApp(correctAppIdKey, cb, tries + 1);
|
||||
this.selectApp(correctAppIdKey, cb, maxTries, tries + 1);
|
||||
} else {
|
||||
msg = "Could not connect to a valid app after " + tries + " tries.";
|
||||
this.logger.error(msg);
|
||||
cb(new Error(msg));
|
||||
this.logger.debug(msg);
|
||||
this.logger.debug("Could not connect to a valid app after " + tries + " tries.");
|
||||
cb();
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
@@ -592,7 +595,6 @@ RemoteDebugger.prototype.send = function (data, cb, cb2) {
|
||||
if (data.__selector === '_rpc_reportIdentifier:') {
|
||||
this.specialCbs.connect = cb2;
|
||||
} else if (data.__selector === '_rpc_forwardGetListing:') {
|
||||
this.logger.debug(cb2);
|
||||
this.specialCbs.connectedToBadApp = cb2;
|
||||
}
|
||||
} else if (data.__argument && data.__argument.WIRSocketDataKey) {
|
||||
|
||||
@@ -648,4 +648,45 @@ Simulator.prototype.deleteOtherSims = function (cb) {
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Simulator.prototype.getBootedDevice = function (cb) {
|
||||
simctl.getDevices(function (err, devices) {
|
||||
if (err) return cb(err);
|
||||
|
||||
var booted = null;
|
||||
|
||||
_.detect(devices, function (sdkDevices) {
|
||||
booted = _.findWhere(sdkDevices, {state: "Booted"});
|
||||
|
||||
return !!booted;
|
||||
});
|
||||
|
||||
if (booted) {
|
||||
cb(null, booted.udid);
|
||||
} else {
|
||||
cb("Simctl couldn't find any booted device");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Simulator.prototype.remove = function (bundleId, cb) {
|
||||
this.getBootedDevice(function (err, udid) {
|
||||
if (err) return cb(err);
|
||||
simctl.removeApp(udid, bundleId, cb);
|
||||
});
|
||||
};
|
||||
|
||||
Simulator.prototype.install = function (appPath, cb) {
|
||||
this.getBootedDevice(function (err, udid) {
|
||||
if (err) return cb(err);
|
||||
simctl.installApp(udid, appPath, cb);
|
||||
});
|
||||
};
|
||||
|
||||
Simulator.prototype.launch = function (bundleId, cb) {
|
||||
this.getBootedDevice(function (err, udid) {
|
||||
if (err) return cb(err);
|
||||
simctl.launch(udid, bundleId, cb);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = Simulator;
|
||||
|
||||
@@ -40,6 +40,7 @@ exports.copyLocalZip = function (localZipPath, cb) {
|
||||
fs.stat(localZipPath, function (err) {
|
||||
if (err) return cb(err);
|
||||
tempDir.open({prefix: 'appium-app', suffix: '.zip'}).nodeify(function (err, info) {
|
||||
if (err) return cb(err);
|
||||
var infile = fs.createReadStream(localZipPath);
|
||||
var outfile = fs.createWriteStream(info.path);
|
||||
infile.pipe(outfile).on('close', function () {
|
||||
|
||||
@@ -92,6 +92,7 @@ var iosCaps = [
|
||||
, 'loggingPrefs'
|
||||
, 'sendKeyStrategy'
|
||||
, 'waitForAppScript'
|
||||
, 'webviewConnectRetries'
|
||||
];
|
||||
|
||||
var Capabilities = function (capabilities) {
|
||||
|
||||
@@ -531,7 +531,8 @@ var args = [
|
||||
, type: 'int'
|
||||
, required: false
|
||||
, help: 'The default command timeout for the server to use for all ' +
|
||||
'sessions. Will still be overridden by newCommandTimeout cap'
|
||||
'sessions (in seconds and should be less than 2147483). ' +
|
||||
'Will still be overridden by newCommandTimeout cap'
|
||||
}],
|
||||
|
||||
[['--keep-keychains'], {
|
||||
|
||||
Generated
+3348
File diff suppressed because it is too large
Load Diff
+8
-8
@@ -11,7 +11,7 @@
|
||||
"firefoxos",
|
||||
"testing"
|
||||
],
|
||||
"version": "1.4.0",
|
||||
"version": "1.4.8",
|
||||
"author": "https://github.com/appium",
|
||||
"licenses": [
|
||||
{
|
||||
@@ -43,17 +43,17 @@
|
||||
"adm-zip": "~0.4.7",
|
||||
"appium-adb": "=1.7.5",
|
||||
"appium-atoms": "=0.0.5",
|
||||
"appium-chromedriver": "^2.0.0",
|
||||
"appium-instruments": "=2.0.2",
|
||||
"appium-support": "=1.0.5",
|
||||
"appium-chromedriver": "^2.1.3",
|
||||
"appium-instruments": "^2.0.4",
|
||||
"appium-support": "=1.1.2",
|
||||
"appium-uiauto": "=1.10.8",
|
||||
"appium-xcode": "=2.0.4",
|
||||
"appium-xcode": "^2.0.5",
|
||||
"argparse": "~1.0.1",
|
||||
"async": "~0.9.0",
|
||||
"binary-cookies": "~0.1.1",
|
||||
"body-parser": "~1.12.0",
|
||||
"bplist-creator": "~0.0.5",
|
||||
"bplist-parser": "~0.0.6",
|
||||
"bplist-parser": "^0.1.0",
|
||||
"bufferpack": "0.0.6",
|
||||
"bytes": "~1.0.0",
|
||||
"camel-back-promise": "^1.0.0",
|
||||
@@ -75,7 +75,7 @@
|
||||
"namp": "0.2.25",
|
||||
"ncp": "~2.0.0",
|
||||
"node-idevice": "~0.1.5",
|
||||
"node-simctl": "=2.0.0",
|
||||
"node-simctl": "^2.0.1",
|
||||
"node-uuid": "~1.4.2",
|
||||
"npmlog": "~1.1.0",
|
||||
"path": "~0.11.14",
|
||||
@@ -88,6 +88,7 @@
|
||||
"sandboxed-module": "~2.0.0",
|
||||
"serve-favicon": "~2.2.0",
|
||||
"socket.io": "~1.3.4",
|
||||
"stack-trace": "0.0.9",
|
||||
"swig": "~1.4.2",
|
||||
"temp": "~0.8.1",
|
||||
"through": "~2.3.6",
|
||||
@@ -141,7 +142,6 @@
|
||||
"socks": "~0.0.1",
|
||||
"spawn-mocha-parallel": "~1.3.1",
|
||||
"split": "^0.3.3",
|
||||
"stack-trace": "0.0.9",
|
||||
"uncolor": "~0.3.0",
|
||||
"underscore-cli": "~0.2.18",
|
||||
"unorm": "~1.3.3",
|
||||
|
||||
@@ -11,9 +11,11 @@
|
||||
"appium-atoms",
|
||||
"appium-base-driver",
|
||||
"appium-chromedriver",
|
||||
"appium-fake-driver",
|
||||
"appium-jsonwp-proxy",
|
||||
"appium-logger",
|
||||
"appium-instruments",
|
||||
"appium-selendroid-driver",
|
||||
"appium-support",
|
||||
"appium-express",
|
||||
"appium-uiauto",
|
||||
|
||||
@@ -29,6 +29,7 @@ verbose=false
|
||||
chromedriver_version=false
|
||||
chromedriver_install_all=false
|
||||
npmlink=true
|
||||
shrinkwrap_opt=""
|
||||
if test -d .git ; then
|
||||
is_git_checkout=true
|
||||
else
|
||||
@@ -56,6 +57,7 @@ do
|
||||
"--chromedriver-install-all") chromedriver_install_all=true;;
|
||||
"--udid") udid=$2;;
|
||||
"--no-npmlink") npmlink=false;;
|
||||
"--no-shrinkwrap") shrinkwrap_opt="--no-shrinkwrap";;
|
||||
esac
|
||||
|
||||
if [[ -n "$2" ]] && [[ "$2" != --* ]]; then
|
||||
@@ -121,10 +123,10 @@ reset_npm() {
|
||||
fi
|
||||
if $prod_deps ; then
|
||||
echo "* Installing new or updated NPM modules"
|
||||
run_cmd npm install --production .
|
||||
run_cmd npm install --production $shrinkwrap_opt .
|
||||
else
|
||||
echo "* Installing new or updated NPM modules (including devDeps)"
|
||||
run_cmd npm install .
|
||||
run_cmd npm install $shrinkwrap_opt .
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
Submodule
+1
Submodule submodules/appium-ci added at 83235d2f4a
Submodule submodules/appium-ci-gulper deleted from 8da4680cde
Submodule submodules/appium-instruments updated: 9420760b9a...ee74b689ed
@@ -34,6 +34,11 @@ describe("apidemos - attributes", function () {
|
||||
.back()
|
||||
.nodeify(done);
|
||||
});
|
||||
it('should be able to find content description attribute', function (done) {
|
||||
driver.elementByName('Animation').getAttribute('contentDescription')
|
||||
.should.become("Animation")
|
||||
.nodeify(done);
|
||||
});
|
||||
it('should be able to find displayed attribute', function (done) {
|
||||
driver
|
||||
.elementByName('Animation').getAttribute('displayed')
|
||||
|
||||
@@ -4,7 +4,7 @@ var setup = require("../common/setup-base")
|
||||
, _ = require("underscore")
|
||||
, getAppPath = require('../../helpers/app').getAppPath;
|
||||
|
||||
describe('crash recovery', function () {
|
||||
describe('crash recovery @skip-real-device', function () {
|
||||
var driver;
|
||||
var desired = {
|
||||
app: getAppPath('TestApp')
|
||||
@@ -25,7 +25,7 @@ describe('crash recovery', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('crash commands', function () {
|
||||
describe('crash commands @skip-real-device', function () {
|
||||
|
||||
var driver;
|
||||
var desired = {
|
||||
|
||||
@@ -36,7 +36,8 @@ describe('safari - webview - special capabilities @skip-ios6', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('performance logs', function () {
|
||||
// skipped on real devices, see https://github.com/appium/appium/issues/5166
|
||||
describe('performance logs @skip-real-device', function () {
|
||||
var driver;
|
||||
var specialCaps = _.clone(desired);
|
||||
specialCaps.loggingPrefs = {performance: 'ALL'};
|
||||
|
||||
@@ -50,9 +50,12 @@ describe('safari - windows and frames (' + env.DEVICE + ') @skip-ios6"', functio
|
||||
.elementById('only_on_page_2')
|
||||
.nodeify(done);
|
||||
});
|
||||
it("should be able to open js popup windows with safariAllowPopups set to true", function (done) {
|
||||
|
||||
// broken on real devices, see https://github.com/appium/appium/issues/5167
|
||||
it("should be able to open js popup windows with safariAllowPopups set to true @skip-real-device", function (done) {
|
||||
driver
|
||||
.execute("window.open('/test/guinea-pig2.html', null);")
|
||||
.elementByLinkText('i am a new window link')
|
||||
.click()
|
||||
.then(function () { return spinTitle("I am another page title", driver, 30); })
|
||||
.nodeify(done);
|
||||
});
|
||||
|
||||
@@ -49,7 +49,7 @@ describe('testapp - device', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('invalid deviceName @skip-ios6', function () {
|
||||
describe('invalid deviceName @skip-ios6 @skip-real-device', function () {
|
||||
var newDesired = _.extend(_.clone(desired), {deviceName: "iFailure 3.5-inch"});
|
||||
var session = initSession(newDesired, {'no-retry': true});
|
||||
|
||||
@@ -60,23 +60,32 @@ describe('testapp - device', function () {
|
||||
});
|
||||
});
|
||||
|
||||
_.each(['iPhone', 'iPad'], function (device) {
|
||||
describe('generic ' + device + ' deviceName @skip-ios6', function () {
|
||||
var newDesired = _.extend(_.clone(desired), {
|
||||
deviceName: device + " Simulator"
|
||||
});
|
||||
var session = initSession(newDesired, {'no-retry': true});
|
||||
describe('generic deviceName @skip-ios6', function () {
|
||||
var session;
|
||||
|
||||
after(function (done) {
|
||||
session.tearDown(this.currentTest.state === 'passed').nodeify(done);
|
||||
});
|
||||
|
||||
it('should work with a generic ' + device + ' deviceName', function (done) {
|
||||
session.setUp()
|
||||
.nodeify(done);
|
||||
});
|
||||
afterEach(function (done) {
|
||||
session.tearDown(this.currentTest.state === 'passed').nodeify(done);
|
||||
});
|
||||
|
||||
it('should work with a generic iPhone deviceName', function (done) {
|
||||
var newDesired = _.extend(_.clone(desired), {
|
||||
deviceName: "iPhone Simulator"
|
||||
});
|
||||
|
||||
session = initSession(newDesired, {'no-retry': true});
|
||||
session.setUp()
|
||||
.nodeify(done);
|
||||
});
|
||||
|
||||
it('should work with a generic iPad deviceName', function (done) {
|
||||
var newDesired = _.extend(_.clone(desired), {
|
||||
deviceName: "iPad Simulator"
|
||||
});
|
||||
|
||||
session = initSession(newDesired, {'no-retry': true});
|
||||
session.setUp()
|
||||
.nodeify(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe("real device", function () {
|
||||
|
||||
@@ -29,7 +29,7 @@ describe('localization - locale', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('changing locale', function () {
|
||||
describe('changing locale @skip-real-device', function () {
|
||||
var driver;
|
||||
setup(this, _.defaults({locale: 'fr'}, desired)).then(function (d) { driver = d; });
|
||||
|
||||
@@ -40,7 +40,7 @@ describe('localization - locale', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('changing back', function () {
|
||||
describe('changing back @skip-real-device', function () {
|
||||
var driver;
|
||||
setup(this, _.defaults({locale: 'en_US'}, desired)).then(function (d) { driver = d; });
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ describe('testapp - location - 3 @skip-ci', function () {
|
||||
});
|
||||
|
||||
// TODO: location tests are not working well on sauce
|
||||
describe('testapp - location - 4 @skip-ci', function () {
|
||||
describe('testapp - location - 4 @skip-ci @skip-real-device', function () {
|
||||
var driver;
|
||||
var newDesired = _.clone(desired);
|
||||
_.extend(newDesired, {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#!/bin/bash
|
||||
# Prevents force-pushing to master
|
||||
|
||||
if $ALLOW_FORCE_PUSH; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
BRANCH=`git rev-parse --abbrev-ref HEAD`
|
||||
PUSH_COMMAND=`ps -ocommand= -p $PPID`
|
||||
PROTECTED_BRANCHES="^(master)"
|
||||
|
||||
Reference in New Issue
Block a user