Compare commits

...
85 Commits
Author SHA1 Message Date
Isaac Murchie 47e5a86e10 Beta 1 of 1.6.0 2016-08-18 17:00:47 -07:00
Isaac A. Murchie 5e07e5b3ee Merge pull request #6746 from appium/isaac-beta-cl
Add changelog for 1.6.0 beta 1
2016-08-18 16:56:47 -07:00
Isaac A. Murchie b6c62e259d Merge pull request #6747 from appium/isaac-xcuitest-deps
Add documentation about dependencies for XCUITest testing
2016-08-18 16:54:25 -07:00
Isaac Murchie a3792a0185 Add documentation about dependencies for XCUITest testing 2016-08-18 16:49:36 -07:00
Jonathan Lipps 52f990a2f4 don't do an implicit string+int comparison 2016-08-18 16:28:18 -07:00
Jonathan Lipps 8afc543316 Merge pull request #6745 from jlipps/master
make XCUITest driver the default for ios tests with platformVersion >= 10
2016-08-18 16:28:05 -07:00
Jonathan Lipps b7dc1cdc4f fix doc link location 2016-08-18 16:19:58 -07:00
Isaac Murchie 99b0dfddb6 Add changelog for 1.6.0 beta 1 2016-08-18 16:14:40 -07:00
Jonathan Lipps 25ac4d3e3a Merge pull request #6742 from Microsoft/master
Adding Windows documentation
2016-08-18 16:05:34 -07:00
t-mamith 3002117c0e Added Windows Documentation with corrected setup instructions 2016-08-18 16:01:17 -07:00
Jonathan Lipps fb4a9c0be0 make XCUITest driver the default for ios tests with platformVersion >= 10 2016-08-18 14:59:31 -07:00
Isaac A. Murchie 99c8e182d2 Merge pull request #6722 from midopa/no-chromedriver-default-port
Remove default ChromeDriver port in arg parse
2016-08-18 09:47:47 -07:00
Sam Park 1482423198 Remove default ChromeDriver port in arg parse
If null and required, Android driver will pick a random available one
2016-08-18 09:40:33 -07:00
Isaac A. Murchie 77ea7e9866 Merge pull request #6732 from rz-robsn/master
just updated CONTRIBUTING.md page to point to the ISSUE_TEMPLATE.md page
2016-08-16 08:56:45 -07:00
Raza Robson 9341bbde35 just updated CONTRIBUTING.md page to point to the new issue page. 2016-08-16 11:43:04 -04:00
Isaac A. Murchie 32d5b7a132 Merge pull request #6723 from Coeur/patch-1
spelling
2016-08-09 07:49:13 -07:00
Antoine Cœur 72a97f2910 spelling 2016-08-09 16:05:10 +08:00
Isaac A. Murchie 0c14015659 Merge pull request #6664 from jlipps/master
add WinAppDriver beta under the Windows platform
2016-08-08 12:08:15 -07:00
Isaac A. Murchie a1425e5870 Merge pull request #6716 from appium/isaac-node
Require Node 4+
2016-08-08 12:01:33 -07:00
Isaac Murchie 7403a13c1a Require Node 4+ 2016-08-03 12:37:20 -07:00
Isaac A. Murchie d3ee825371 Merge pull request #6715 from appium/isaac-eslint
Add eslint and pre-commit hooks
2016-08-03 12:35:47 -07:00
Isaac Murchie 50725af059 Add eslint and pre-commit hooks 2016-08-03 12:14:29 -07:00
Isaac A. Murchie f418cb3486 Merge pull request #6713 from stepstone-tech/master
Fix handling of grid node id
2016-08-03 10:47:46 -07:00
Tomasz Wojtuń 4ae1b28ff5 Fix handling of grid node id 2016-08-03 17:09:42 +02:00
Isaac A. Murchie 068c4e5d0a Merge pull request #6671 from jlipps/jlipps-colors
Update parser and logging to handle log spy
2016-07-20 07:51:45 -07:00
Jonathan Lipps 02b9fcb30c add option for calling library to have appium throw instead of process.exit 2016-07-19 21:51:05 -07:00
Jonathan Lipps 6413d55b4f add 'dest' key to some parser options since we rely on this key and it was undefined in places 2016-07-19 15:03:05 -07:00
Jonathan Lipps c22c00fb82 add ability for importing package to send in logHandler which can spy on logs
this is necessary for the new appium desktop
2016-07-19 15:03:05 -07:00
Jonathan Lipps 56bea2e511 ensure we don't include args in deprecated/non-default list which don't actually have values 2016-07-18 23:06:12 -07:00
Isaac A. Murchie e026a14141 Merge pull request #6649 from akhillb/androidScreenshotPath
Add support to set directory on android device to save screenshots
2016-07-15 11:37:18 -07:00
jlipps fa98737c4f add WinAppDriver beta under the Windows platform 2016-07-15 10:10:42 -07:00
Akhil Lb 486827af71 Add support to set directory on device to save screenshots for android 2016-07-14 23:37:09 +05:30
Isaac A. Murchie 1f8ce36b7d Merge pull request #6647 from appium/isaac-xcuitest
Update to new name for xcuitest driver
2016-07-12 10:48:55 -07:00
Isaac Murchie 4843d41679 Update to new name for xcuitest driver 2016-07-08 10:52:08 -07:00
Isaac A. Murchie 135e43a523 Merge pull request #6215 from simongranger/youi
Added You.i Engine support
2016-07-08 08:15:08 -07:00
Simon Granger 340a83ab93 Added You.i Engine support 2016-07-07 17:26:33 -04:00
Isaac A. Murchie 97b5015f9d Merge pull request #6629 from spearminty/adb-push-timeout
Add androidInstallTimeout cap, update Android dep
2016-07-01 13:12:11 -07:00
spearminty 154cb49102 Add androidInstallTimeout cap, update Android dep
Add androidInstallTimeout capability to docs

Android driver dependency updates from 1.10.6 > 1.10.12:
* Fix 'release' touch action
* Unlock helper deflake
* Add device udid to android session information
* Improve text clear command
* Allow fast reset without 'app' capability
* Add 'androidInstallTimeout' capability
2016-07-01 15:04:54 -05:00
Isaac A. Murchie 0b5df721d4 Merge pull request #6561 from truebit/master
add support to set UiAutomator Congfigurator values
2016-07-01 07:46:21 -07:00
汪啸 e593208f9e add support to set UiAutomator Congfigurator values 2016-07-01 12:33:01 +08:00
Isaac A. Murchie 2276be5333 Merge pull request #6611 from appium/isaac-xcuitest
Add ability to use automationName XCUITest
2016-06-22 11:11:47 -07:00
Isaac Murchie 5a67708df0 Add ability to use automationName XCUITest 2016-06-22 09:56:45 -07:00
Jonathan Lipps c92d23d4be Merge pull request #6599 from scottdixon/uiautomator2
Docs: UIAutomator 2
2016-06-17 15:36:35 -07:00
Scott Dixon 0f99de7715 Docs: UIAutomator 2 2016-06-17 11:12:41 -07:00
Isaac A. Murchie 72fde58638 Merge pull request #6587 from aelr/troubleshooting-tip-instruments-crash
Added solution for Instruments crashes on devices
2016-06-14 08:35:44 -07:00
aelr 312c0e924c Added solution for Instruments crashes on devices 2016-06-14 08:56:15 -04:00
Scott Dixon 1b86d0cba9 Merge pull request #6566 from scottdixon/iframe
README: cross-domain iframes
2016-06-08 14:39:46 -07:00
Scott Dixon 101bad0aa6 README: cross-domain iframes 2016-06-08 14:22:32 -07:00
Isaac Murchie 9c663cf18e 1.5.3 2016-06-07 12:42:15 -07:00
Isaac Murchie e494df980f Add changelog entry for 1.5.3 2016-06-07 12:41:25 -07:00
Jonathan Lipps 1662884395 Merge pull request #6551 from scottdixon/xcuitest
XCUI Docs
2016-06-03 15:36:19 -07:00
Scott Dixon 3babf0ce3d XCUI Docs 2016-06-02 10:58:13 -07:00
bootstraponline f6c8faff9c Add -persistent to OS X launch agent config
Required for using nodes plus plugin
2016-05-31 14:47:38 -04:00
Isaac Murchie 169ca60023 Update dependencies in preparation of release 2016-05-27 08:47:30 -07:00
Jonathan Lipps 3b56d2d1d8 Merge pull request #6518 from jlipps/master
use appium-base-driver instead of mjsonwp/express/proxy
2016-05-19 15:37:13 -07:00
Jonathan Lipps a09e3e00f6 use appium-base-driver instead of mjsonwp/express/proxy 2016-05-19 14:02:12 -07:00
Isaac A. Murchie ad054100d6 Merge pull request #6483 from SrinivasanTarget/master_new
doc tweaks
2016-05-09 11:14:55 -07:00
Srinivasan Sekar e22414b4ce doc tweaks 2016-05-08 19:24:17 +05:30
Isaac A. Murchie 6e5f3a308a Merge pull request #6471 from scottdixon/webkit-debug-fix
ios-webkit-debug-proxy-launcher fix: replace underscore with lodash
2016-05-05 07:36:38 -07:00
Scott Dixon 2ffc5b00bf ios-webkit-debug-proxy-launcher fix: replace underscore with lodash 2016-05-04 19:37:48 -07:00
Jonathan Lipps 3c2c86f0aa Merge pull request #6462 from appium/isaac-comment
Add comment on sync code
2016-05-03 09:20:56 -07:00
Isaac Murchie 350c452901 Add comment on sync code 2016-05-03 08:16:24 -07:00
Isaac A. Murchie 9c1b85ff88 Merge pull request #6455 from usmonster/fix-tests-incorrect-assertion-syntax
Fixes tests' incorrect use of .throw() assertion
2016-05-02 04:51:09 +02:00
Isaac A. Murchie ebcf3da165 Merge pull request #6451 from usmonster/feat-read-desired-caps-from-file
Allows --desired-caps to be passed a filename
2016-05-02 04:50:41 +02:00
Usman Akeju 7966de6703 Fixes tests' incorrect use of .throw() assertion
As pointed out in appium/appium#6451, there are many test cases that do
`should[.not].throw` instead of `should[.not].throw()`, which is
incorrect and will never fail.

This fixes those incorrect uses of the method as well as refactors/fixes
the tests that turned out to be actually failing.
2016-05-01 16:31:59 +02:00
Usman Akeju 734a96be5a Allows --desired-caps to be passed a filename
The --desired-caps flag can now be passed the name of a JSON file as an
argument. This also adds an alias for the flag, `-dc`, and fixes tests.
2016-05-01 14:55:50 +02:00
Jonathan Lipps 00041de3a3 Merge pull request #6449 from appium/isaac-is-stupid
Remove an only-restrictor from tests
2016-04-29 12:04:37 -07:00
bootstraponline b3c1e9c622 Merge pull request #6446 from bootstraponline/jenkins
Add Jenkins instructions to running-on-osx
2016-04-29 13:34:35 -04:00
bootstraponline 1ddeb74939 Add Jenkins instructions to running-on-osx 2016-04-29 13:33:22 -04:00
Isaac Murchie 609ba21b13 Remove an only-restrictor from tests 2016-04-29 18:32:10 +02:00
Isaac A. Murchie 1672203f42 Merge pull request #6445 from appium/isaac-so
Support the sessionOverride flag
2016-04-29 13:34:33 +02:00
Isaac Murchie 406a06a7de Support the sessionOverride flag 2016-04-29 11:02:41 +02:00
Isaac A. Murchie 216264ff16 Merge pull request #6434 from appium/isaac-proxy
Add documentation for Android-only nativeWebScreenshot capability
2016-04-24 13:43:49 -07:00
Isaac Murchie 1eb84376b3 Add documentation for Android-only nativeWebScreenshot capability 2016-04-24 13:30:52 -07:00
bootstraponline fa6d8b7442 Fix authorize-ios path
$ stat node_modules/.bin/authorize-ios.js
stat: node_modules/.bin/authorize-ios.js: stat: No such file or directory
2016-04-22 16:24:40 -04:00
Isaac A. Murchie afd50a2637 Merge pull request #6421 from appium/isaac-1.5.2-release
Add changelog and bump to 1.5.2
2016-04-20 10:43:15 -07:00
Isaac Murchie f6ed186748 Add changelog and bump to 1.5.2 2016-04-20 10:15:48 -07:00
Isaac Murchie 9ab17fdd1b Fix touch action docs 2016-04-18 11:21:39 -07:00
Isaac A. Murchie d4c7e5500c Merge pull request #6413 from appium/isaac-windows
Update docs for windows install
2016-04-15 08:56:49 -07:00
Isaac Murchie 6eb48cf2e3 Update docs for windows install 2016-04-15 08:33:27 -07:00
Isaac A. Murchie 7aee45c1ab Merge pull request #6375 from appium/isaac-dep
Deprecate --command-timeout server arg
2016-04-08 11:11:59 -04:00
Isaac Murchie caa1323bf0 Deprecate --command-timeout server arg 2016-04-06 12:59:26 -07:00
Isaac A. Murchie 566147c20d Merge pull request #6352 from appium/isaac-release
Add comment to release docs
2016-04-04 15:29:31 -07:00
Isaac Murchie 0f65f2b657 Update version to 1.5.1 2016-03-30 07:52:05 -07:00
Isaac Murchie 141948b035 Add comment to release docs 2016-03-29 13:00:35 -07:00
44 changed files with 1161 additions and 219 deletions
+1
View File
@@ -0,0 +1 @@
sample-code
+3
View File
@@ -0,0 +1,3 @@
{
"extends": "appium"
}
+3 -1
View File
@@ -3,6 +3,8 @@ sudo: false
node_js:
- "stable"
- "4"
- "0.12"
script:
- npm run lint
- npm run test
after_success:
- gulp coveralls
+111
View File
@@ -1,3 +1,114 @@
CHANGES IN VERSION 1.6.0 beta 1 (from 1.5.3)
===================================
This release of Appium marks the beginning of support for two brand new platforms:
* [Windows](https://www.microsoft.com/en-us/windows) desktop applications (see the [usage documentation](https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/windows-app-testing.md))
* [You.i TV](http://www.youi.tv/) (see the [driver documentation](https://github.com/YOU-i-Labs/appium-youiengine-driver#appium-youi-engine-driver)).
Further, since Apple removed the Instruments automation functionality in Xcode 8,
this release of Appium has preliminary support for XCUITest, allowing for the automation
of applications in iOS 9.3 and 10.
#### General
- Require Node 4 or above (**possible breaking change**)
- Add `automationName` entries for `XCUITest`, and `youiengine`
- Add `platformName` entry for `Windows`
#### iOS
- Add preliminary support for Xcode 8 and iOS 10 (using `automationName` of `XCUITest`). For information on using this driver, see the [driver documentation](https://github.com/appium/appium-xcuitest-driver#external-dependencies)
- Make sure device name gets properly translated into actual device name
- Fix case where orientation would get lost
- Fix Safari page change logic to actually catch when a page changes
- Try harder to kill Instruments if the normal way does not work, to avoid hanging processes
#### Android
- Add `androidInstallTimeout` desired capability, to customize the timeout when installing an app
- Add `androidScreenshotPath` desired capability, to set the path in which screenshot files are saved on the device
- Add `appWaitDuration` desired capability, to customize how long to wait for an application
- Fix optional intent arguments to allow for hyphens
- Wait for apps to launch before proceeding
- Switch to clearing text fields using adb, to improve reliability and speed
- Add ability to detect screen orientation
- Make sure Selendroid mode doesn't lose connection through adb when network changes
- Make sure the release action in a touch action chain doesn't happen in the wrong place
- Make application install more reliable
- Fix screenshot on Windows
- Make Chromedriver connect on a random port if none specified
CHANGES IN VERSION 1.5.3 (from 1.5.2)
===================================
*NOTE*: Appium 1.5.3 is primarily a bug-fix release.
It also adds functionality to support for iOS [WKWebViews](https://developer.apple.com/library/ios/documentation/WebKit/Reference/WKWebView_Ref/) and for automating the iOS Calendar app, as well as adding a capability to use `abd` for screenshots in Android web/hybrid sessions, selecting the correct version of Android emulator, and providing a list of Android activities to wait for on session startup.
Further, Android session reporting is increased, so that automation clients can get the device UDID for the automation session, as well as the currently running `appPackage` and `appActivity`.
#### General
- Allow `--default-capabilities` server argument to specify a file containing the default capabilities.
- Fix handling of statuses from proxied drivers (Chrome, Selendroid, etc.).
- Fix handling of `browserName` capability for Selenium Grid usage.
- Fix intermittent bug where creating a directory might crash the server.
- Fix handling of `--session-override` server argument.
#### iOS
- Add support for `WKWebView`-based webviews.
- Add support for automating the built-in Calendar app.
- Fix `ios-debug-proxy-launcher`.
- Fix bug where scrolling in Safari would crash.
- Fix handling of Safari in "new window" state.
- Fix handling of arguments for `keys` method so that clients that send an array do not fail.
- Clean up handling of `ipa` files for real device tests.
- Make sure `processArguments` parsing works.
- Fix iwd script to allow re-running.
#### Android
- Add `nativeWebScreenshot` capability, to use `adb` screenshots instead of ChromeDriver if necessary.
- Add support to auto-select device based on `platformVersion` rather than using first available device.
- Allow `appWaitActivity` to be a list of activities
- Fix setting wifi and data state
- Implement `initAutoWebView` capability.
- Report `appPackage`, `appActivity`, and `deviceUDID` when requesting session details.
- Make sure unlock app is closed at the end of a session so that the device works again.
- Fix handling of unlock to make less flakey.
- Make sure unicode encoding works correctly when strings are long.
- Fix handling of `disableAndroidWatchers` capability.
- Fix killing ChromeDriver on Ubuntu 16.04.
- Fix bug where early ChromeDriver failure crashed Appium.
CHANGES IN VERSION 1.5.2 (from 1.5.1)
===================================
*NOTE*: Appium 1.5.2 is a bug-fix release.
#### General
- deprecated `--command-timeout`. Use `newCommandTimeout` desired capability instead
- ensure implicit wait can be set through `timeout` method
- add better logging for `EPIPE` errors
#### iOS
- make sure `ipa` files are handled correctly for installing on real devices
- ensure that existing SafariLauncher on device is used instead of rebuilding and reinstalling
- fix issues with getting webview contexts on real devices
- add full timeout support through `timeout` method
- make sure Xpath searches respect implicit wait timeout
- make sure bare Instruments process arguments are accepted
#### Android
- fix failure when `apk` file is too large
- re-implement setting geolocation so it does not use Telnet.
- add support for Chromium browser
- fix issues with `flick`
- fix bug where touch action `release` would throw an error
- fix bug in later Android SDK version where noticing a newly started avd would fail
- implement `autoWebviewTimeout`
CHANGES IN VERSION 1.5.1 (from 1.5)
===================================
+1 -8
View File
@@ -60,11 +60,4 @@ are any questions that you can answer.
### Submit bug reports or feature requests
Just use the GitHub issue tracker to submit your bug reports and feature
requests. If you are submitting a bug report, please remember to include:
* Your complete Appium log (as a [gist](https://gist.github.com)---do *NOT* paste the whole thing into the issue description). This will tell us the exact version of Appium and the exact capabilities you are using, so you don't have to.
* Your relevant client code, if you are able to share it (again a gist is most useful).
* Any other information helpful in trying to reproduce your issue.
In general, the less work we have to do to reproduce a bug, the faster we will
fix it. So please include as much information as possible up front!
requests. If you are submitting a bug report, please follow the [issue template](https://github.com/appium/appium/issues/new).
+11 -3
View File
@@ -10,13 +10,14 @@
[![Build Status](https://team-appium.ci.cloudbees.com/job/Appium/badge/icon)](https://team-appium.ci.cloudbees.com/job/Appium/)
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).
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, Windows, FirefoxOS).
### Supported Platforms
* iOS
* Android
* Windows
* FirefoxOS
See the [platform support doc](/docs/en/appium-setup/platform-support.md) for more detailed information.
@@ -49,7 +50,7 @@ Your environment needs to be setup for the particular mobile platforms that you
want to run tests on. See below for particular platform requirements.
If you want to run Appium via an `npm install`, hack with or contribute to Appium, you will need
[node.js and npm](http://nodejs.org) 0.12 or greater (use [n](https://github.com/visionmedia/n) or
[node.js and npm](http://nodejs.org) 4 or greater (use [n](https://github.com/visionmedia/n) or
`brew install node` to install Node.js. Make sure you have not installed Node or Appium with `sudo`,
otherwise you'll run into problems). We recommend the latest stable version.
@@ -57,7 +58,7 @@ To verify that all of Appium's dependencies are met you can use
`appium-doctor`. Install it with `npm install -g appium-doctor` (or run it
from [source](https://github.com/appium/appium-doctor)), then run
`appium-doctor` and supply the `--ios` or `--android` flags to verify that all
of the dependencies are set up correctly.
of the dependencies are set up correctly.
You also need to download the Appium client for your language so you can write tests. The Appium clients are simple extensions to the WebDriver clients. You can see the list of clients and links to download instructions at the [Appium clients list](/docs/en/about-appium/appium-clients.md).
@@ -76,6 +77,11 @@ You also need to download the Appium client for your language so you can write t
* [linux](/docs/en/appium-setup/running-on-linux.md)
* [osx](/docs/en/appium-setup/running-on-osx.md)
* [windows](/docs/en/appium-setup/running-on-windows.md)
#### Windows Requirements
* Windows 10
* [Documentation](/docs/en/appium-setup/running-on-windows.md)
#### FirefoxOS Requirements
@@ -124,6 +130,8 @@ Appium drives Apple's UIAutomation library for iOS support, which is based on
Android support uses the UiAutomator framework for newer platforms and
[Selendroid](http://github.com/DominikDary/selendroid) for older Android platforms.
Windows support uses Microsoft's [WinAppDriver](https://github.com/Microsoft/WinAppDriver)
FirefoxOS support leverages [Marionette](https://developer.mozilla.org/en-US/docs/Marionette),
an automation driver that is compatible with WebDriver and is used to automate
Gecko-based platforms.
+4 -2
View File
@@ -1,5 +1,7 @@
#!/usr/bin/env node
/* eslint no-console:0 */
/*
* Small tool, launching and monitoring ios-web-kit-proxy, and relauching
* on predefined errors.
@@ -19,7 +21,7 @@
"use strict";
var spawn = require('child_process').spawn,
_ = require('underscore');
_ = require('lodash');
var args = process.argv.slice(2);
@@ -48,7 +50,7 @@ var startProxy = function () {
proxy.stderr.on('data', function (data) {
console.log('stderr: ' + data);
var restartMessage = _(RESTART_ON_MESSAGES).find(function (message) {
return ('' + data).indexOf(message) >= 0;
return ('' + data).indexOf(message) >= 0;
});
if (restartMessage) {
console.log('Detected error message:', restartMessage);
+5 -4
View File
@@ -2,7 +2,7 @@
Appium is an open-source tool for automating native,
mobile web, and hybrid applications on iOS and Android platforms.
**Native apps** are those written using the iOS or Android SDKs. **Mobile web apps** are web apps accessed using a mobile browser (Appium supports
**Native apps** are those written using the iOS, Android, or Windows SDKs. **Mobile web apps** are web apps accessed using a mobile browser (Appium supports
Safari on iOS and Chrome or the built-in 'Browser' app on Android). **Hybrid apps** have a
wrapper around a "webview" -- a native control that enables interaction with
web content. Projects like [Phonegap](http://phonegap.com/),
@@ -10,7 +10,7 @@ make it easy to build apps using web technologies that are then bundled into
a native wrapper, creating a hybrid app.
Importantly, Appium is "cross-platform": it allows you to write tests against
multiple platforms (iOS, Android), using the same API. This enables code reuse between iOS and Android testsuites.
multiple platforms (iOS, Android, Windows), using the same API. This enables code reuse between iOS, Android, and Windows testsuites.
For specific information about what it means for Appium to "support" its
platforms, and automation modalities, please see the [platform support doc](/docs/en/appium-setup/platform-support.md).
@@ -34,6 +34,7 @@ third-party code or frameworks to your app. This means **you're testing the same
* iOS: Apple's [UIAutomation](https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/)
* Android 4.2+: Google's [UiAutomator](http://developer.android.com/tools/help/uiautomator/index.html)
* Android 2.3+: Google's [Instrumentation](http://developer.android.com/reference/android/app/Instrumentation.html). (Instrumentation support is provided by bundling a separate project, [Selendroid](http://selendroid.io))
* Windows: Microsoft's [WinAppDriver](http://github.com/microsoft/winappdriver)
We meet requirement #2 by wrapping the vendor-provided frameworks in one API,
the [WebDriver](http://docs.seleniumhq.org/projects/webdriver/) API.
@@ -83,13 +84,13 @@ a map or hash) sent to the Appium server to tell the server what kind of
automation session we're interested in starting up. There are also various
capabilities which can modify the behavior of the server during automation.
For example, we might set the `platformName` capability to `iOS` to tell
Appium that we want an iOS session, rather than an Android one. Or we might
Appium that we want an iOS session, rather than an Android or Windows one. Or we might
set the `safariAllowPopups` capability to `true` in order to ensure that,
during a Safari automation session, we're allowed to use JavaScript to open
up new windows. See the [capabilities doc](/docs/en/writing-running-appium/caps.md) for the complete list of capabilities available for Appium.
**Appium Server**<br/>
Appium is a server written in Node.js. It can be built and installed [from source](github.com/appium/appium) or installed directly from NPM:
Appium is a server written in Node.js. It can be built and installed [from source](https://github.com/appium/appium/blob/master/docs/en/contributing-to-appium/appium-from-source.md) or installed directly from NPM:
```
$ npm install -g appium
$ appium
@@ -0,0 +1,35 @@
## Cross-domain iFrame Automation
[Same-origin policy](https://en.wikipedia.org/wiki/Same-origin_policy) prevents Appium from automating iFrames that have a different domain to the parent.
### Subdomain workaround
If the parent and the iFrame share the same domain (e.g. `site.com` and `shop.site.com`), you can
set `document.domain` on both the parent and each iFrame to a common domain. This solves the same-origin policy issue and allows automation. For example:
Parent:
```
<html>
<head>
<script>
document.domain = 'site.com';
</script>
</head>
<body>
<iframe src="http://shop.site.com" width="200" height="200"></iframe>
</body>
</html>
```
Child iFrame:
```
<html>
<head>
<script>
document.domain = 'site.com';
</script>
</head>
<body>
<p>This is an iFrame!</p>
</body>
</html>
```
+14
View File
@@ -38,3 +38,17 @@ Settings are implemented via the following API endpoints:
### Supported Settings
**"ignoreUnimportantViews"** - Boolean which sets whether Android devices should use `setCompressedLayoutHeirarchy()` which ignores all views which are marked IMPORTANT_FOR_ACCESSIBILITY_NO or IMPORTANT_FOR_ACCESSIBILITY_AUTO (and have been deemed not important by the system), in an attempt to make things less confusing or faster.
#### Android UiAutomator Configurator
sets [UiAutomator Configurator](https://developer.android.com/reference/android/support/test/uiautomator/Configurator.html) timeouts and delays in Android devices. only works in Android API 18 and above.
**"actionAcknowledgmentTimeout"** - Int which is the same as [setActionAcknowledgmentTimeout](https://developer.android.com/reference/android/support/test/uiautomator/Configurator.html#setActionAcknowledgmentTimeout(long)). If a negative value is given, it would set to default(3 * 1000 milliseconds)
**"keyInjectionDelay"** - Int which is the same as [setKeyInjectionDelay](https://developer.android.com/reference/android/support/test/uiautomator/Configurator.html#setKeyInjectionDelay(long)). If a negative value is given, it would set to default(0 milliseconds)
**"scrollAcknowledgmentTimeout"** - Int which is the same as [setScrollAcknowledgmentTimeout](https://developer.android.com/reference/android/support/test/uiautomator/Configurator.html#setScrollAcknowledgmentTimeout(long)). If a negative value is given, it would set to default(200 milliseconds)
**"waitForIdleTimeout"** - Int which is the same as [setWaitForIdleTimeout](https://developer.android.com/reference/android/support/test/uiautomator/Configurator.html#setWaitForIdleTimeout(long)). If a negative value is given, it would set to default(10 * 1000 milliseconds)
**"waitForSelectorTimeout"** - Int which is the same as [setWaitForSelectorTimeout](https://developer.android.com/reference/android/support/test/uiautomator/Configurator.html#setWaitForSelectorTimeout(long)). If a negative value is given, it would set to default(10 * 1000 milliseconds)
+1 -1
View File
@@ -1,6 +1,6 @@
## Android Setup
To get started, you'll need to install node.js (v0.12 or greater). Just
To get started, you'll need to install node.js (v4 or greater). Just
follow the [instructions for your flavor of linux](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager).
Once you've got node.js installed, install the [Android SDK](http://developer.android.com/sdk/index.html).
+7 -1
View File
@@ -38,7 +38,7 @@ instructions.
* Versions: 2.3 and up
* Versions 2.3 through 4.2 are supported via Appium's bundled version of
[Selendroid](http://selendroid.io), which utilizes [Instrumentation](http://developer.android.com/reference/android/app/Instrumentation.html). Selendroid has a different set of commands than the default Appium (though this is rapidly being minimized) and a different support profile. To access this automation backend, use the `automationName` capability with the value `Selendroid`.
* Versions 4.2 and up are supported via Appium's own [UiAutomator](http://developer.android.com/tools/help/uiautomator/index.html)
* Versions 4.2 and up are supported via Appium's own [UiAutomator](http://developer.android.com/tools/testing-support-library/index.html#UIAutomator)
libraries. This is the default automation backend.
* Devices: Android emulators and real Android devices
* Native app support: Yes
@@ -61,3 +61,9 @@ instructions.
* Support for automating vendor-provided or third-party apps: Yes (but not
when using the Selendroid backend)
* Support for automating custom, non-standard UI controls: No
### Windows Support
See [running on Windows](running-on-windows.md) for details
* [Windows App Testing](windows-app-testing.md)
+16
View File
@@ -21,6 +21,22 @@ can find information about this in the [Apple documentation](https://developer.a
Appium will attempt to install your app using Fruitstrap, but it is often easier
to pre-install your app using Xcode to ensure there are no problems (see the [iOS deploy](ios-deploy.md) doc for more information).
### Testing using Xcode 8 (including iOS 10) with XCUITest
This functionality currently depends on logging based on `idevicesyslog`, and
port forwarding based on `iProxy`, both of which are part of `libimobiledevice`.
Install it with [Homebrew](http://brew.sh/),
```
brew install libimobiledevice
```
Alternatively, logging can be done using `deviceconsole`, which is available
[here](https://github.com/rpetrich/deviceconsole). To choose between them, use
the desired capability `realDeviceLogger`, passing in the path to the logging
program.
### Running your tests with Appium
Once your device and app are configured, you can run tests on that device by
+78
View File
@@ -61,7 +61,85 @@ If you're using multiple Xcode versions, you can switch between them using:
sudo xcode-select --switch &lt;path to required xcode&gt;
### Testing using Xcode 8 (including iOS 10) with XCUITest
In order to automate iOS devices with Xcode 8 (which includes all testing of iOS 10+),
you need to install the [Carthage](https://github.com/Carthage/Carthage) dependency
manager:
```
brew install carthage
```
### System setup (Android)
Instructions for setting up Android and running tests on Mac OS X are the same as
those on Linux. See the [Android setup docs](/docs/en/appium-setup/android-setup.md).
### Running iOS tests on OS X using Jenkins
First download the jenkins-cli.jar and verify the Mac successfully connects to Jenkins master. Ensure you've run the `authorize_ios` command mentioned above.
`wget https://jenkins.ci.cloudbees.com/jnlpJars/jenkins-cli.jar`
```
java -jar jenkins-cli.jar \
-s https://team-appium.ci.cloudbees.com \
-i ~/.ssh/id_rsa \
on-premise-executor \
-fsroot ~/jenkins \
-labels osx \
-name mac_appium
```
Next define a LaunchAgent for Jenkins to launch automatically on login. A LaunchDaemon will not work because daemons don't have GUI access. Make sure the plist doesn't contain the `SessionCreate` or `User` key as that may prevent tests from running. You'll see a `Failed to authorize rights` error if misconfigured.
```
$ sudo nano /Library/LaunchAgents/com.jenkins.ci.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.jenkins.ci</string>
<key>ProgramArguments</key>
<array>
<string>java</string>
<string>-Djava.awt.headless=true</string>
<string>-jar</string>
<string>/Users/appium/jenkins/jenkins-cli.jar</string>
<string>-s</string>
<string>https://instructure.ci.cloudbees.com</string>
<string>on-premise-executor</string>
<string>-fsroot</string>
<string>/Users/appium/jenkins</string>
<string>-executors</string>
<string>1</string>
<string>-labels</string>
<string>mac</string>
<string>-name</string>
<string>mac_appium</string>
<string>-persistent</string>
</array>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/appium/jenkins/stdout.log</string>
<key>StandardErrorPath</key>
<string>/Users/appium/jenkins/error.log</string>
</dict>
</plist>
```
Finally set the owner, permissions, and then start the agent.
```
sudo chown root:wheel /Library/LaunchAgents/com.jenkins.ci.plist
sudo chmod 644 /Library/LaunchAgents/com.jenkins.ci.plist
launchctl load /Library/LaunchAgents/com.jenkins.ci.plist
launchctl start com.jenkins.ci
```
+32 -42
View File
@@ -1,61 +1,48 @@
#Windows Setup
Appium on Windows supports both Windows and Android app automation!
See [Windows App Testing](/docs/en/writing-running-appium/windows-app-testing.md) for more details.
## Running Appium on Windows
### Limitations
If you are running Appium on Windows, you can use the
[Appium.exe](https://github.com/appium/appium-dot-exe) client, which will allow
you to quickly launch an Appium server and use the Inspector. You will not be
able to test iOS apps on a locally hosted server, because Appium relies on OS
X-only libraries to support iOS testing. You can however use the `Remote Server`
option to connect to an Appium server running on a Mac.
### Setup
## Setup
To get started:
1. Install [node.js](http://nodejs.org/download/) (v.0.12 or greater). Use the
installer from nodejs.org.
1. Install the [Android SDK](http://developer.android.com/sdk/index.html).
You will need to run the 'android' tool (included in the SDK, in the `tools` folder) and make sure
you have an API Level 17 or greater API installed. Set `ANDROID_HOME` to be
1. Download latest [node and npm tools](https://nodejs.org/download/release/v6.3.0/node-v6.3.0-x64.msi) MSI (version >= 6.0). The `npm` and `nodejs` paths should be in your PATH environment variable.
2. Download latest Java JDK [here](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) (accept the license agreement first). Set 'JAVA_HOME' to be your JDK path. The `bin` in that directory should be added to your PATH variable.
3. Install the [Android SDK](http://developer.android.com/sdk/index.html). Set `ANDROID_HOME` to be
your Android SDK path and add the `tools` and `platform-tools` folders to your
PATH variable.
1. Install the Java JDK and set `JAVA_HOME` to your JDK folder.
1. Install [Apache Ant](http://ant.apache.org/bindownload.cgi) or use the one
that comes with the Android Windows SDK in the eclipse\plugins folder. Be
sure to add the folder containing Ant to your PATH variable.
1. Install [Apache Maven](http://maven.apache.org/download.cgi) and set the
M2HOME and M2 environment variables. Set `M2HOME` to the directory maven is
installed in, and set `M2` to `%M2HOME\bin`. Add the path you used for `M2` to
your PATH.
1. Install [Git](http://git-scm.com/download/win) Be sure to install Git for
windows to run in the regular command prompt.
1. Install [cURL](http://curl.haxx.se/download.html).
4. Install [Apache Ant](http://ant.apache.org/bindownload.cgi) or use the one that comes with the Android Windows SDK in the eclipse\plugins folder. Be sure to add the folder containing Ant to your PATH variable.
5. Install [Apache Maven](http://maven.apache.org/download.cgi) and set the M2HOME and M2 environment variables. Set `M2_HOME` to the directory maven is installed in, and set `M2` to the `bin` in that directory. Add the path you used for `M2` to your PATH.
6. Open admin cmd
7. run command: npm install -g appium
8. run command: appium
9. Run a test from any Appium client (including Visual Studio)
Now that you've downloaded everything, if you're running from source, run the
following in the folder where you cloned appium:
## Additional Setup for Android App Testing
```center
rm -rf node_modules
npm install
```
1. To run tests on Windows, you will need to have the Android Emulator booted or an Android Device connected that is running an AVD with API Level 17 orgreater. Then run Appium on the command line (via the `appium` command)
2. Within test script, change value of 'platformVersion' to correspond with emulator version (i.e. '4.4.4')
3. Within test script, change value of 'app' to PATH('path of .apk') and ensure the entire path (including close parenthesis) is on one line
### Running Appium
## Additional Setup for Windows App Testing
To run tests on Windows, you will need to have the Android Emulator booted or
an Android Device connected that is running an AVD with API Level 17 or
greater. Then run Appium on the command line (via the `appium` command), or if
you're running from source, inside the folder where you installed appium, using
node.js:
1. To test a Windows app, make sure you have turned [developer mode](https://msdn.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development) on.
```center
node .
```
## Running Appium
See the [server documentation](/docs/en/writing-running-appium/server-args.md) for all the command line
arguments.
### Notes
## Notes
- URL endpoint has to append "/wd/hub"
- DesiredCapabilities:
- platformName = "Windows"
- deviceName = "WindowsPC"
* On windows run appium.app as administrator or when running from source you need to run cmd as administrator.
* You must supply the `--no-reset` or `--full-reset` flags for
@@ -64,3 +51,6 @@ arguments.
limitations. For more information you can check out this
[page](/docs/en/appium-setup/android-hax-emulator.md).
* Make sure that `hw.battery=yes` in your AVD's `config.ini`.
+7
View File
@@ -36,6 +36,12 @@ own items in the SDK manager. Make sure you install the build-tools and platform
reset the connection to the Android device.
* Make sure you set ANDROID_HOME pointing to the Android SDK directory
### Windows
* Make sure developer mode is on
* Make sure command prompt is Admin
* Check that the URL Appium server is listening to matches the one specified in test script
### IOS
* Make sure Instruments.app is not open
@@ -51,6 +57,7 @@ own items in the SDK manager. Make sure you install the build-tools and platform
/tmp/instruments_sock` and try again as not-sudo.
* If this is the first time you've run Appium, make sure to authorize the use
of Instruments. See [running on OSX documentation](./running-on-osx.md#authorizing-ios-on-the-computer).
* If Instruments is crashing when running against a physical device ("exited with code 253"), ensure Xcode has downloaded device symbols. Go to Window -> Devices, and it should start automatically. This is needed after iOS version upgrades.
* If you see `iOS Simulator failed to install the application.` and the
paths are correct, try restarting the computer.
* If you have custom elements in your app, they will not be automatable by
@@ -22,7 +22,7 @@ git clone https://github.com/appium/appium.git
cd appium
npm install
gulp transpile # requires gulp, see below
sudo node_modules/.bin/authorize-ios.js # for ios only
sudo node_modules/.bin/authorize-ios # for ios only
# alternatively
sudo $(npm bin)/authorize-ios
node .
@@ -4,7 +4,7 @@
## appium
- runs basic checks
- node version (>= 0.12)
- node version (>= 4)
- CLI arguments checks
- contains all available and supported CLI arguments
- check for deprecation and mutual exclusion
@@ -16,14 +16,14 @@
- starts baseServer (appium-express)
- passes routes given by driver
## appium-express
## appium-express (part of appium-base-driver)
- starts express server (allows x-domain-origin)
- initialises routes from AppiumDriver
- timeout handling
- serves a static page for test purposes
- connects req/res events to logger
## mobile-json-wire-protocol
## mobile-json-wire-protocol (part of appium-base-driver)
- provides list of Appium commands
- subclassed by drivers that will use the protocol
- kind of middleware between client and driver
@@ -146,7 +146,7 @@
- launches, restarts and stops (or kills all) chrome instances
- uses appium-jsonwp-proxy to send json wire protocol commands to the driver
## jsonwp-proxy
## jsonwp-proxy (part of appium-base-driver)
- allows to send json wire protocol commands to a server that understands it (browser drivers)
- parses response into json
- allows to proxy requests to a proxied server
@@ -13,3 +13,4 @@
* [ios-webkit-debug-proxy](https://github.com/google/ios-webkit-debug-proxy)
* [instruments-without-delay](https://github.com/facebook/instruments-without-delay)
* [deviceconsole](https://github.com/rpetrich/deviceconsole)
* [WinAppDriver](http://github.com/Microsoft/WinAppDriver)
@@ -151,6 +151,7 @@ checked in to GitHub along with changes to `package.json`.
0. Update the CHANGELOG/README with appropriate changes and submit for review as a PR, along with shrinkwrap and `package.json` changes. Wait for it to be merged, then pull it into the release branch.
0. Create a tag of the form `v<version>` on the release branch (usually a minor branch like `1.5` or `1.4`), with: `git tag -a v<version>`, e.g., `git tag -a 1.5.0`. This is not necessary for beta versions.
0. Push the tag to upstream: `git push --tags <remote> <branch>`
0. Install dev dependencies (or at least `gulp` and `appium-gulp-plugins`).
0. Run `npm publish` (with `--tag beta` if this isn't an official release).
0. Update the docs at appium.io. Check out the appium.io repo from github, check out the `gh-pages` branch and pull latest. Run `rake publish`.
0. Create a new release on GitHub: go to `https://github.com/appium/appium/releases/tag/v<VERSION>` and hit "Edit Tag". Make the release name `<VERSION>` (e.g., `2.0.5`), then paste in the changelog (but not the changelog header for this version). If it's a beta release, mark as pre-release.
@@ -0,0 +1,245 @@
# Apple XCUITest
Xcode 7 introduces [new UI testing features](https://developer.apple.com/videos/play/wwdc2015/406/). Moving forward, [Appium iOS Driver](https://github.com/appium/appium-ios-driver) will leverage this framework, replacing UI Automation. While there are no official docs, @joemasilotti has created [some handy unofficial docs](http://masilotti.com/xctest-documentation/index.html) by pulling out comments from the header files.
## Launching the App (XCUIApplication)
- ##### – launch
Launches the application. This call is synchronous and when it returns the application is launched
and ready to handle user events. Any failure in the launch sequence is reported as a test failure
and halts the test at this point. If the application is already running, this call will first
terminate the existing instance to ensure clean state of the launched instance.
- ##### – terminate
Terminates any running instance of the application. If the application has an existing debug session
via Xcode, the termination is implemented as a halt via that debug connection. Otherwise, a SIGKILL
is sent to the process.
- #####   launchArguments
The arguments that will be passed to the application on launch. If not modified, these are the
arguments that Xcode will pass on launch. Those arguments can be changed, added to, or removed.
Unlike NSTask, it is legal to modify these arguments after the application has been launched. These
changes will not affect the current launch session, but will take effect the next time the application
is launched.
- #####   launchEnvironment
The environment that will be passed to the application on launch. If not modified, this is the
environment that Xcode will pass on launch. Those variables can be changed, added to, or removed.
Unlike NSTask, it is legal to modify the environment after the application has been launched. These
changes will not affect the current launch session, but will take effect the next time the application
is launched.
## Accessing the Device (XCUIDevice)
- ##### + sharedDevice
The current device.
- #####   orientation
The orientation of the device.
- ##### – pressButton:
Simulates the user pressing a physical button.
## Querying for Elements (XCUIElementQuery)
- #####   element
Returns an element that will use the query for resolution.
- #####   count
Evaluates the query at the time it is called and returns the number of matches found.
- ##### – elementAtIndex:
Returns an element that will resolve to the index into the query’s result set.
- ##### – elementBoundByIndex:
Returns an element that will use the index into the query’s results to determine which underlying accessibility element it is matched with.
- ##### – elementMatchingPredicate:
Returns an element that matches the predicate. The predicate will be evaluated against objects of type idXCUIElementAttributes.
- ##### – elementMatchingType:identifier:
Returns an element that matches the type and identifier.
- ##### – objectForKeyedSubscript:
Keyed subscripting is implemented as a shortcut for matching an identifier only. For example, app.descendants[“Foo”] -> XCUIElement.
- #####   allElementsBoundByAccessibilityElement
Immediately evaluates the query and returns an array of elements bound to the resulting accessibility elements.
- #####   allElementsBoundByIndex
Immediately evaluates the query and returns an array of elements bound by the index of each result.
- ##### – descendantsMatchingType:
Returns a new query that finds the descendants of all the elements found by the receiver.
- ##### – childrenMatchingType:
Returns a new query that finds the direct children of all the elements found by the receiver.
- ##### – matchingPredicate:
Returns a new query that applies the specified attributes or predicate to the receiver. The predicate will be evaluated against objects of type idXCUIElementAttributes.
- ##### – matchingType:identifier:
- ##### – matchingIdentifier:
- ##### – containingPredicate:
Returns a new query for finding elements that contain a descendant matching the specification. The predicate will be evaluated against objects of type idXCUIElementAttributes.
- ##### – containingType:identifier:
- #####   debugDescription
@discussion
Provides debugging information about the query. The data in the string will vary based on the time
at which it is captured, but it may include any of the following as well as additional data:
• A description of each step of the query.
• Information about the inputs and matched outputs of each step of the query.
This data should be used for debugging only - depending on any of the data as part of a test is unsupported.
## Interacting with Elements (XCUIElement)
### XCUIElementEventSynthesis Methods
- ##### – typeText:
Types a string into the element. The element or a descendant must have keyboard focus; otherwise an
error is raised.
- ##### – tap
Sends a tap event to a hittable point computed for the element.
- ##### – doubleTap
Sends a double tap event to a hittable point computed for the element.
- ##### – twoFingerTap
Sends a two finger tap event to a hittable point computed for the element.
- ##### – tapWithNumberOfTaps:numberOfTouches:
Sends one or more taps with one of more touch points.
- ##### – pressForDuration:
Sends a long press gesture to a hittable point computed for the element, holding for the specified duration.
- ##### – pressForDuration:thenDragToElement:
Initiates a press-and-hold gesture that then drags to another element, suitable for table cell reordering and similar operations.
@param duration
Duration of the initial press-and-hold.
@param otherElement
The element to finish the drag gesture over. In the example of table cell reordering, this would be the reorder element of the destination row.
- ##### – swipeUp
Sends a swipe-up gesture.
- ##### – swipeDown
Sends a swipe-down gesture.
- ##### – swipeLeft
Sends a swipe-left gesture.
- ##### – swipeRight
Sends a swipe-right gesture.
- ##### – pinchWithScale:velocity:
Sends a pinching gesture with two touches.
- ##### – rotate:withVelocity:
Sends a rotation gesture with two touches.
### XCUIElementTypeSlider Methods
- ##### – adjustToNormalizedSliderPosition:
Manipulates the UI to change the displayed value of the slider to one based on a normalized position. 0 corresponds to the minimum value of the slider, 1 corresponds to its maximum value. The adjustment is a “best effort” to move the indicator to the desired position; absolute fidelity is not guaranteed.
- #####   normalizedSliderPosition
Returns the position of the slider’s indicator as a normalized value where 0 corresponds to the minimum value of the slider and 1 corresponds to its maximum value.
### XCUIElementTypePickerWheel Methods
- ##### – adjustToPickerWheelValue:
Changes the displayed value for the picker wheel. Will generate a test failure if the specified value is not available.
### Other Methods
- #####   exists
Test to determine if the element exists.
- #####   hittable
Whether or not a hit point can be computed for the element for the purpose of synthesizing events.
- ##### – descendantsMatchingType:
Returns a query for all descendants of the element matching the specified type.
- ##### – childrenMatchingType:
Returns a query for direct children of the element matching the specified type.
- ##### – coordinateWithNormalizedOffset:
Creates and returns a new coordinate that will compute its screen point by adding the offset multiplied by the size of the element’s frame to the origin of the element’s frame.
- #####   debugDescription
@discussion
Provides debugging information about the element. The data in the string will vary based on the
time at which it is captured, but it may include any of the following as well as additional data:
• Values for the elements attributes.
• The entire tree of descendants rooted at the element.
• The element’s query.
This data should be used for debugging only - depending on any of the data as part of a test is unsupported.
## Using Coordinates (XCUICoordinate)
- ##### – init
Coordinates are never instantiated directly. Instead, they are created by elements or by other coordinates.
- #####   referencedElement
The element that the coordinate is based on, either directly or via the coordinate from which it was derived.
- #####   screenPoint
The dynamically computed value of the coordinate’s location on screen. Note that this value is dependent on the current frame of the referenced element; if the element’s frame changes, so will the value returned by this property. If the referenced element does exist when this is called, it will fail the test; check the referenced element’s exists property if the element may not be present.
- ##### – coordinateWithOffset:
Creates a new coordinate with an absolute offset in points from the original coordinate.
- ##### – tap
- ##### – doubleTap
- ##### – pressForDuration:
- ##### – pressForDuration:thenDragToCoordinate:
- ##### – hover
+23 -1
View File
@@ -33,6 +33,7 @@
|`androidCoverage`| Fully qualified instrumentation class. Passed to -w in adb shell am instrument -e coverage true -w | `com.my.Pkg/com.my.Pkg.instrumentation.MyInstrumentation`|
|`enablePerformanceLogging`| (Chrome and webview only) Enable Chromedriver's performance logging (default `false`)| `true`, `false`|
|`androidDeviceReadyTimeout`|Timeout in seconds used to wait for a device to become ready after booting|e.g., `30`|
|`androidInstallTimeout`|Timeout in milliseconds used to wait for an apk to install to the device. Defaults to `90000` |e.g., `90000`|
|`adbPort`|Port used to connect to the ADB server (default `5037`)|`5037`|
|`androidDeviceSocket`|Devtools socket name. Needed only when tested app is a Chromium embedding browser. The socket is open by the browser and Chromedriver connects to it as a devtools client.|e.g., `chrome_devtools_remote`|
|`avd`| Name of avd to launch|e.g., `api19`|
@@ -49,7 +50,7 @@
|`intentAction`| Intent action which will be used to start activity (default `android.intent.action.MAIN`)| e.g.`android.intent.action.MAIN`, `android.intent.action.VIEW`|
|`intentCategory`| Intent category which will be used to start activity (default `android.intent.category.LAUNCHER`)| e.g. `android.intent.category.LAUNCHER`, `android.intent.category.APP_CONTACTS`
|`intentFlags`| Flags that will be used to start activity (default `0x10200000`)| e.g. `0x10200000`
|`optionalIntentArguments`| Additional intent arguments that will be used to start activity. See [Intent arguments](http://developer.android.com/tools/help/adb.html#IntentSpec) | e.g. `--esn <EXTRA_KEY>`, `--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE>`, etc.
|`optionalIntentArguments`| Additional intent arguments that will be used to start activity. See [Intent arguments](http://developer.android.com/reference/android/content/Intent.html) | e.g. `--esn <EXTRA_KEY>`, `--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE>`, etc.
|`dontStopAppOnReset`| Doesn't stop the process of the app under test, before starting the app using adb. If the app under test is created by another anchor app, setting this false, allows the process of the anchor app to be still alive, during the start of the test app using adb. In other words, with `dontStopAppOnReset` set to `true`, we will not include the `-S` flag in the `adb shell am start` call. With this capability omitted or set to `false`, we include the `-S` flag. Default `false`| `true` or `false`|
|`unicodeKeyboard`| Enable Unicode input, default `false`| `true` or `false`|
|`resetKeyboard`| Reset keyboard to its original state, after running Unicode tests with `unicodeKeyboard` capability. Ignored if used alone. Default `false`| `true` or `false`|
@@ -58,6 +59,8 @@
|`disableAndroidWatchers`| Disables android watchers that watch for application not responding and application crash, this will reduce cpu usage on android device/emulator. This capability will work only with UiAutomator and not with selendroid, default `false`| `true` or `false`|
|`chromeOptions`| Allows passing chromeOptions capability for ChromeDriver. For more information see [chromeOptions](https://sites.google.com/a/chromium.org/chromedriver/capabilities) | `chromeOptions: {args: ['--disable-popup-blocking']}` |
|`recreateChromeDriverSessions`| Kill ChromeDriver session when moving to a non-ChromeDriver webview. Defaults to `false` | `true` or `false`|
|`nativeWebScreenshot`| In a web context, use native (adb) method for taking a screenshot, rather than proxying to ChromeDriver. Defaults to `false` | `true` or `false`|
|`androidScreenshotPath`| The name of the directory on the device in which the screenshot will be put. Defaults to `/data/local/tmp` |e.g. `/sdcard/screenshots/`|
### iOS Only
@@ -89,3 +92,22 @@
|`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`|
|`appName`| The display name of the application under test. Used to automate backgrounding the app in iOS 9+.|e.g., `UICatalog`|
### You.i Engine Only
<expand_table>
|Capability|Description|Values|
|----|-----------|-------|
|`youiEngineAppAddress`| The IP address of the device on which the app is running. Use `localhost` for simulator.
Use device’s IP address for a real device. |e.g. `localhost` or `192.168.1.203`|
### WinAppDriver Only
<expand_table>
|Capability|Description|Values|
|----|-----------|-------|
|`platformName`| Which platform the test is being done on |e.g. `Windows`|
|`deviceName`| The name of the device being tested on | e.g. `WindowsPC`|
|`app`| appID of the windows app for testing or the path to the .exe file. See [this page](/docs/en/writing-running-appium/windows-app-testing.md) for details on how to find the appID | e.g. `c24c8163-548e-4b84-a466-530178fc0580_scyf5npe3hv32!App`|
@@ -3,10 +3,13 @@
Appium 1.5 does away with most CLI flags; the remainder can be converted into JSON and made part of the `--default-capabilities` flag. For example:
```
# raw JSON as an argument
--default-capabilities '{"app": "myapp.app", "deviceName": "iPhone Simulator"}'
# or the name of a JSON file
--default-capabilities /path/to/file.json
```
**Windows users** will need to escape the quotes: `--default-capabilities "{\"app\": \"myapp.app\"}"`
**Windows users** will need to escape the quotes in JSON passed on the command line: `--default-capabilities "{\"app\": \"myapp.app\"}"`
@@ -9,7 +9,7 @@ It is worth looking at *'-ios uiautomation'* search strategy with **Predicates**
```
Native JS search strategy (powered by Apple) provides much more flexibility and is like Xpath.
**[Predicates](https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Predicates/predicates.html)** can be used to restrict an elements set to select only those ones for which some condition is true.
**[Predicates](https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Predicates/AdditionalChapters/Introduction.html)** can be used to restrict an elements set to select only those ones for which some condition is true.
For example:
@@ -20,7 +20,7 @@ appiumDriver.findElementsByIosUIAutomation("collectionViews()[0].cells().withPre
\- will select only those ```UIACollectionCell``` elements that have visible ```UIAStaticText``` child elements, and themselves are childs of 1st ```UIACollectionView``` element that should be located under the main app window. Here ```staticTexts()``` and ```isVisible()``` are methods available in ```UIAElementArray``` and ```UIAElement``` classes respectively. **Note that ```UIAElementArray``` numbering begins with ```0``` unlike Xpath where indexes counting starts from ```1```**
Here's a list of available Predicates (mostly taken from [Predicates Programming Guide](https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Predicates/predicates.html))
Here's a list of available Predicates (mostly taken from [Predicates Programming Guide](https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Predicates/AdditionalChapters/Introduction.html))
### Basic Comparisons
@@ -20,6 +20,11 @@ connection API for iOS.
Choose the setting you would like to use, and then send the correct bitmask from
the table above.
### Windows
Unfortunately, at the moment Appium does not support the Selenium network
connection API for Windows.
```javascript
// javascript
// set airplane mode
@@ -19,6 +19,10 @@ unpack it for you. Nice if you're not using Appium locally.
Nothing in particular needs to be done to run your .apk using Appium. If you
want to zip it up, you can.
### Preparing your app for test (Windows)
Nothing in particular needs to be done to run your test.
### Running your test app with Appium (iOS)
The best way to see what to do currently is to look at the example tests:
@@ -91,9 +95,9 @@ First, make sure you have one and only one Android emulator or device
connected. If you run `adb devices`, for example, you should see one device
connected. This is the device Appium will use for tests. Of course, to have
a device connected, you'll need to have made an Android AVD (see system
setup ([Windows](running-on-windows.md),
[Mac](running-on-osx.md),
or [Linux](running-on-linux.md))
setup ([Windows](https://github.com/appium/appium/blob/master/docs/en/appium-setup/running-on-windows.md),
[Mac](https://github.com/appium/appium/blob/master/docs/en/appium-setup/running-on-osx.md),
or [Linux](https://github.com/appium/appium/blob/master/docs/en/appium-setup/running-on-linux.md))
for more information). If the Android SDK tools are on your path, you can
simply run:
@@ -196,7 +200,7 @@ all set now!
### Running your test app with Appium (Android devices &lt; 4.2, and hybrid tests)
Android devices before version 4.2 (API Level 17) do not have Google's
[UiAutomator framework](http://developer.android.com/tools/help/uiautomator/index.html)
[UiAutomator framework](http://developer.android.com/tools/testing-support-library/index.html#UIAutomator)
installed. This is what Appium uses to perform the automation behaviors on
the device. For earlier devices or tests of hybrid (webview-based) apps,
Appium comes bundled with another automation backend called [Selendroid]
@@ -268,3 +272,11 @@ session. One of the downsides to using Selendroid is that its API differs
sometimes significantly with Appium's. Therefore we recommend you thoroughly
read [Selendroid's documentation](http://selendroid.io/native.html) before
writing your scripts for older devices or hybrid apps.
### Running your test app with Appium (Windows)
Ensure that Appium is listening
Deploy your test from your test runner of choice
See our [samples](https://github.com/Microsoft/WinAppDriver/tree/master/Samples) for details
@@ -1,10 +1,10 @@
## Appium server arguments
# Appium server arguments
Many Appium 1.5 server arguments have been deprecated in favor of the [--default-capabilities flag](/docs/en/writing-running-appium/default-capabilities-arg.md).
Usage: `node . [flags]`
### Server flags
## Server flags
All flags are optional, but some are required in conjunction with certain others.
@@ -43,7 +43,6 @@ All flags are optional, but some are required in conjunction with certain others
|`--chromedriver-executable`|null|ChromeDriver executable full path||
|`--show-config`|false|Show info about the appium server configuration and exit||
|`--no-perms-check`|false|Bypass Appium's checks to ensure we can read/write necessary files||
|`--command-timeout`|60|The default command timeout for the server to use for all sessions (in seconds and should be less than 2147483). Will still be overridden by newCommandTimeout cap||
|`--strict-caps`|false|Cause sessions to fail if desired caps are sent in that Appium does not recognize as valid for the selected device||
|`--isolate-sim-device`|false|Xcode 6 has a bug on some platforms where a certain simulator can only be launched without error if all other simulator devices are first deleted. This option causes Appium to delete all devices other than the one being used by Appium. Note that this is a permanent deletion, and you are responsible for using simctl or xcode to manage the categories of devices used with Appium.||
|`--tmp`|null|Absolute path to directory Appium can use to manage temporary files, like built-in iOS apps it needs to move around. On *nix/Mac defaults to /tmp, on Windows defaults to C:\Windows\Temp||
@@ -52,7 +51,8 @@ All flags are optional, but some are required in conjunction with certain others
|`--suppress-adb-kill-server`|false|(Android-only) If set, prevents Appium from killing the adb server instance||
|`--async-trace`|false|Add long stack traces to log entries. Recommended for debugging only.||
|`--webkit-debug-proxy-port`|27753|(IOS-only) Local port used for communication with ios-webkit-debug-proxy|`--webkit-debug-proxy-port 27753`|
|`--default-capabilities`|{}|Set the default desired capabilities, which will be set on each session unless overridden by received capabilities. [More infomation](/docs/en/writing-running-appium/default-capabilities-arg.md). |`--default-capabilities '{"app": "myapp.app", "deviceName": "iPhone Simulator"}'`|
|`-dc`, `--default-capabilities`|{}|Set the default desired capabilities, which will be set on each session unless overridden by received capabilities.|`--default-capabilities [ '{"app": "myapp.app", "deviceName": "iPhone Simulator"}' | /path/to/caps.json ]`|
|`--command-timeout`|60|[DEPRECATED] No effect. This used to be the default command timeout for the server to use for all sessions (in seconds and should be less than 2147483). Use newCommandTimeout cap instead||
|`-k`, `--keep-artifacts`|false|[DEPRECATED] - no effect, trace is now in tmp dir by default and is cleared before each run. Please also refer to the --trace-dir flag.||
|`--platform-name`|null|[DEPRECATED] - Name of the mobile platform: iOS, Android, or FirefoxOS|`--platform-name iOS`|
|`--platform-version`|null|[DEPRECATED] - Version of the mobile platform|`--platform-version 7.1`|
@@ -47,7 +47,7 @@ the timing of the gesture.
`moveTo` coordinates are *relative* to the current position. For example, dragging from
100,100 to 200,200 can be achieved by:
```
.press(100,100) // Start at 100,00
.press(100,100) // Start at 100,100
.moveTo(100,100) // Increase X & Y by 100 each, ending up at 200,200
```
@@ -0,0 +1,27 @@
# UIAutomator 2
While the API remains almost the same, the internal implementation has changed and we see the introduction of UIObject2
### UIObject2
Unlike UiObject, the UIElement is bound to a particular view instance and can become stale if the underlying view object is destroyed. As a result, it may be necessary to call findObject(BySelector) to obtain a new UiObject2 instance if the UI changes significantly.
### The Build System
UIAutomator 2 utilizes capabilities of Gradle as a build system. Previous, UIAutomator used Maven/Ant.
### Test Assets
Test packages produced are now APKs. Previously, UIAutomator produced .jar or .zip files. This makes UIAutomator 2 completely Android instrumentation capable.
### ADB
ADB deals with UIAutomator 2 slightly differently.
The original version of UiAutomator ran as a shell program:
```adb shell uiautomator runtest ...```
UiAutomator 2 is based on Android Instrumentation. Tests are compiled to APKs, and run in an application process:
```adb shell am instrument ...```
@@ -1,7 +1,7 @@
## uiautomator UiSelector
Appium enables searching using [UiSelectors](http://developer.android.com/tools/help/uiautomator/UiSelector.html).
[UiScrollable](http://developer.android.com/tools/help/uiautomator/UiScrollable.html)
Appium enables searching using [UiSelectors](http://developer.android.com/reference/android/support/test/uiautomator/UiSelector.html).
[UiScrollable](http://developer.android.com/reference/android/support/test/uiautomator/UiScrollable.html)
is also supported.
Note that the index selector is unreliable so prefer instance instead. The
@@ -0,0 +1,52 @@
#Windows Application UI Testing
Windows specific UI testing on Appium is powered by [WinAppDriver] (https://github.com/Microsoft/WinAppDriver), which is downloaded as part of the Appium install.
## Java Samples
1. Open the sample folder as an existing project in a Java IDE such as IntelliJ. For example: [CalculatorTest](https://github.com/Microsoft/WinAppDriver/tree/master/Samples/Java/CalculatorTest)
2. In the Java IDE build and run the test
## C# Samples
1. Pull and open `CalculatorTest.sln` under [CalculatorTest](https://github.com/Microsoft/WinAppDriver/tree/master/Samples/C%23/CalculatorTest)
2. In Visual Studio 2015 with the test solution open build the test and select **Test > Run > All Tests**
## Features
Windows Application Driver supports testing **Universal Windows Platform (UWP)** and **Classic Windows (Win32)** apps on **Windows 10 PC**
## Creating Your Own Test Script
You can choose any programming language or tools supported by Appium/Selenium to write your test scripts. In the example below, we will author the test script in C# using Microsoft Visual Studio 2015.
### Create Test Project
1. Open **Microsoft Visual Studio 2015**
2. Create the test project and solution. I.e. select **New Project > Templates > Visual C# > Test > Unit Test Project**
3. Once created, select **Project > Manage NuGet Packages... > Browse** and search for **Appium.WebDriver**
4. Install the **Appium.WebDriver** NuGet packages for the test project
5. Start writing your test (see sample code under [samples])
### Universal Windows Platform App Testing
To test a UWP app, you can use any Selenium supported language and simply specify the **Application Id** for the app under test in the **app** capabilities entry. Below is an example of creating a test session for Windows **Alarms & Clock** app written in C#:
```c#
// Launch the AlarmClock app
DesiredCapabilities appCapabilities = new DesiredCapabilities();
appCapabilities.SetCapability("app", "Microsoft.WindowsAlarms_8wekyb3d8bbwe!App");
AlarmClockSession = new IOSDriver<IOSElement>(new Uri("http://127.0.0.1:4723"), appCapabilities);
// Control the AlarmClock app
AlarmClockSession.FindElementByAccessibilityId("AddAlarmButton").Click();
AlarmClockSession.FindElementByAccessibilityId("AlarmNameTextBox").Clear();
```
When testing the application you authored yourself, you can find the **Application Id** in the generetated `AppX\vs.appxrecipe` file under `RegisteredUserNmodeAppID` node. E.g. ```c24c8163-548e-4b84-a466-530178fc0580_scyf5npe3hv32!App```
### Classic Windows App Testing
To test a classic Windows app, you can also use any Selenium supported language and specify the **full executable path** for the app under test in the **app** capabilities entry. Below is an example of creating a test session for Windows **Notepad** app:
```c#
// Launch Notepad
DesiredCapabilities appCapabilities = new DesiredCapabilities();
appCapabilities.SetCapability("app", @"C:\Windows\System32\notepad.exe");
NotepadSession = new IOSDriver<IOSElement>(new Uri("http://127.0.0.1:4723"), appCapabilities);
// Control the AlarmClock app
NotepadSession.FindElementByClassName("Edit").SendKeys("This is some text");
```
### Inspecting UI Elements
Microsoft Visual Studio 2015 by default includes Windows SDK that provides great tool to inspect the application you are testing. This tool allows you to see every UI element/node that you can query using Windows Application Driver. This **inspect.exe** tool can be found under the Windows SDK folder such as `C:\Program Files (x86)\Windows Kits\10\bin\x86`
| Locator Strategy | Matched Attribute |
|------------------|-------------------|
| accessibility id | AutomationId |
| class name | ClassName |
| name | Name |
+8 -3
View File
@@ -1,5 +1,10 @@
/* eslint no-console:0 */
"use strict";
// turn all logging on since we have tests that rely on npmlog logs actually
// getting sent to the handler
process.env._FORCE_LOGS="1";
var gulp = require('gulp'),
boilerplate = require('appium-gulp-plugins').boilerplate.use(gulp),
path = require('path'),
@@ -39,8 +44,8 @@ gulp.task('docs', ['transpile'], function () {
var appiumArguments = parser.getParser().rawArgs;
var docFile = path.resolve(__dirname, "docs/en/writing-running-appium/server-args.md");
var md = "# Appium server arguments\n\n";
md += "Many Appium 1.5 server arguments have been deprecated in favor of the "
md += "[--default-capabilities flag](/docs/en/writing-running-appium/default-capabilities-arg.md)."
md += "Many Appium 1.5 server arguments have been deprecated in favor of the ";
md += "[--default-capabilities flag](/docs/en/writing-running-appium/default-capabilities-arg.md).";
md += "\n\nUsage: `node . [flags]`\n\n";
md += "## Server flags\n";
md += "All flags are optional, but some are required in conjunction with " +
@@ -57,7 +62,7 @@ gulp.task('docs', ['transpile'], function () {
// let's replace it with <user>/...
if (arg[0][0] === '--keystore-path') {
var userPath = process.env.HOME || process.env.USERPROFILE;
argOpts.defaultValue = argOpts.defaultValue.replace(userPath, '&lt;user&gt;')
argOpts.defaultValue = argOpts.defaultValue.replace(userPath, '&lt;user&gt;');
}
// handle empty objects
+51 -10
View File
@@ -1,13 +1,15 @@
import _ from 'lodash';
import log from './logger';
import { getAppiumConfig } from './config';
import { BaseDriver } from 'appium-base-driver';
import { BaseDriver, routeConfiguringFunction, errors,
isSessionCommand } from 'appium-base-driver';
import { FakeDriver } from 'appium-fake-driver';
import { AndroidDriver } from 'appium-android-driver';
import { IosDriver } from 'appium-ios-driver';
import { SelendroidDriver } from 'appium-selendroid-driver';
import { routeConfiguringFunction, errors,
isSessionCommand } from 'mobile-json-wire-protocol';
import { XCUITestDriver } from 'appium-xcuitest-driver';
import { YouiEngineDriver } from 'appium-youiengine-driver';
import { WindowsDriver } from 'appium-windows-driver';
import B from 'bluebird';
import util from 'util';
@@ -41,9 +43,17 @@ class AppiumDriver extends BaseDriver {
}
// we don't necessarily have an `automationName` capability,
// but if we do and it is 'Selendroid', act on it
if ((caps.automationName || '').toLowerCase() === 'selendroid') {
return SelendroidDriver;
if (caps.automationName) {
if (caps.automationName.toLowerCase() === 'selendroid') {
// but if we do and it is 'Selendroid', act on it
return SelendroidDriver;
} else if (caps.automationName.toLowerCase() === 'xcuitest') {
// but if we do and it is 'XCUITest', act on it
return XCUITestDriver;
} else if (caps.automationName.toLowerCase() === 'youiengine') {
// but if we do and it is 'YouiEngine', act on it
return YouiEngineDriver;
}
}
if (caps.platformName.toLowerCase() === "fake") {
@@ -55,9 +65,23 @@ class AppiumDriver extends BaseDriver {
}
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;
}
}
return IosDriver;
}
if (caps.platformName.toLowerCase() === 'windows') {
return WindowsDriver;
}
let msg;
if (caps.automationName) {
msg = `Could not find a driver for automationName '${caps.automationName}' and platformName ` +
@@ -81,7 +105,7 @@ class AppiumDriver extends BaseDriver {
async getSessions () {
let sessions = [];
for (let [id, driver] of _.toPairs(this.sessions)) {
sessions.push({id: id, capabilities: driver.caps});
sessions.push({id, capabilities: driver.caps});
}
return sessions;
}
@@ -96,11 +120,28 @@ class AppiumDriver extends BaseDriver {
for (let [cap, value] of _.toPairs(caps)) {
log.info(` ${cap}: ${util.inspect(value)}`);
}
// sessionOverride server flag check
// this will need to be re-thought when we go to multiple session support
if (this.args.sessionOverride && !!this.sessions && _.keys(this.sessions).length > 0) {
log.info('Session override is on. Deleting other sessions.');
for (let id of _.keys(this.sessions)) {
log.info(` Deleting session '${id}'`);
try {
await this.deleteSession(id);
} catch (ign) {
// the error has already been logged in AppiumDriver.deleteSession
// continue
}
}
}
try {
curSessions = this.curSessionDataForDriver(InnerDriver);
} catch (e) {
throw new errors.SessionNotCreatedError(e.message);
}
let d = new InnerDriver(this.args);
let [innerSessionId, dCaps] = await d.createSession(caps, reqCaps, curSessions);
this.sessions[innerSessionId] = d;
@@ -129,8 +170,8 @@ class AppiumDriver extends BaseDriver {
curSessionDataForDriver (InnerDriver) {
let data = _.values(this.sessions)
.filter(s => s.constructor.name === InnerDriver.name)
.map(s => s.driverData);
.filter((s) => s.constructor.name === InnerDriver.name)
.map((s) => s.driverData);
for (let datum of data) {
if (!datum) {
throw new Error(`Problem getting session data for driver type ` +
@@ -139,7 +180,7 @@ class AppiumDriver extends BaseDriver {
}
}
return data;
}
}
async deleteSession (sessionId) {
try {
+11 -11
View File
@@ -3,7 +3,7 @@ import path from 'path';
import { mkdirp, fs } from 'appium-support';
import { exec } from 'teen_process';
import logger from './logger';
import pkgObj from '../../package.json';
import pkgObj from '../../package.json'; // eslint-disable-line import/no-unresolved
const APPIUM_VER = pkgObj.version;
@@ -30,24 +30,24 @@ async function getAppiumConfig () {
let built = stat.mtime.getTime();
let config = {
'git-sha': await getGitRev(),
'built': built,
'version': APPIUM_VER,
built,
version: APPIUM_VER,
};
return config;
}
function checkNodeOk () {
let [major, minor] = getNodeVersion();
if (major === 0 && minor < 12) {
let msg = `Node version must be >= 0.12. Currently ${major}.${minor}`;
if (major < 4) {
let msg = `Node version must be >= 4. Currently ${major}.${minor}`;
logger.errorAndThrow(msg);
}
}
function warnNodeDeprecations () {
let [major, minor] = getNodeVersion();
if (major === 0 && minor < 12) {
logger.warn("Appium support for versions of node < 0.12 has been " +
let [major] = getNodeVersion();
if (major < 4) {
logger.warn("Appium support for versions of node < 4 has been " +
"deprecated and will be removed in a future version. Please " +
"upgrade!");
}
@@ -55,14 +55,14 @@ function warnNodeDeprecations () {
async function showConfig () {
let config = await getAppiumConfig();
console.log(JSON.stringify(config));
console.log(JSON.stringify(config)); // eslint-disable-line no-console
}
function getNonDefaultArgs (parser, args) {
let nonDefaults = {};
for (let rawArg of parser.rawArgs) {
let arg = rawArg[1].dest;
if (args[arg] !== rawArg[1].defaultValue) {
if (args[arg] && args[arg] !== rawArg[1].defaultValue) {
nonDefaults[arg] = args[arg];
}
}
@@ -77,7 +77,7 @@ function getDeprecatedArgs (parser, args) {
let arg = rawArg[1].dest;
let defaultValue = rawArg[1].defaultValue;
let isDeprecated = !!rawArg[1].deprecatedFor;
if (args[arg] !== defaultValue && isDeprecated) {
if (args[arg] && args[arg] !== defaultValue && isDeprecated) {
deprecated[rawArg[0]] = rawArg[1].deprecatedFor;
}
}
+8 -4
View File
@@ -51,10 +51,14 @@ async function postRequest (data, addr, port) {
jsonObject.configuration.url = `http://${addr}:${port}/wd/hub`;
jsonObject.configuration.host = addr;
jsonObject.configuration.port = port;
// re-serialize the configuration with the auto populated data
data = JSON.stringify(jsonObject);
}
// if the node config does not have id automatically add it
if (!jsonObject.configuration.id) {
jsonObject.configuration.id = `http://${jsonObject.configuration.host}:${jsonObject.configuration.port}`;
}
// re-serialize the configuration with the auto populated data
data = JSON.stringify(jsonObject);
// prepare the header
let post_headers = {
@@ -97,7 +101,7 @@ async function postRequest (data, addr, port) {
async function isAlreadyRegistered (jsonObject) {
//check if node is already registered
let id = `http://${jsonObject.configuration.host}:${jsonObject.configuration.port}`;
let id = jsonObject.configuration.id;
try {
let response = await request({
uri: `http://${jsonObject.configuration.hubHost}:${jsonObject.configuration.hubPort}/grid/api/proxy?id=${id}`,
+21 -11
View File
@@ -69,7 +69,7 @@ function _createConsoleTransport (args, logLvl) {
exitOnError: false,
json: false,
level: logLvl,
formatter: function (options) {
formatter (options) {
let meta = options.meta && Object.keys(options.meta).length ? `\n\t${JSON.stringify(options.meta)}` : '';
let timestampPrefix = '';
if (options.timestamp) {
@@ -81,21 +81,21 @@ function _createConsoleTransport (args, logLvl) {
if (args.logNoColors) {
applyStripColorPatch(transport);
}
return transport;
}
function _createFileTransport (args, logLvl) {
let transport = new (winston.transports.File)({
name: "file",
timestamp: timestamp,
filename: args.log,
maxFiles: 1,
handleExceptions: true,
exitOnError: false,
json: false,
level: logLvl,
}
);
name: "file",
timestamp,
filename: args.log,
maxFiles: 1,
handleExceptions: true,
exitOnError: false,
json: false,
level: logLvl,
});
applyStripColorPatch(transport);
return transport;
}
@@ -151,6 +151,7 @@ async function _createTransports (args) {
transports.push(_createFileTransport(args, fileLogLevel));
} catch (e) {
// eslint-disable-next-line no-console
console.log(`Tried to attach logging to file ${args.log} but an error ` +
`occurred: ${e.message}`);
}
@@ -160,6 +161,7 @@ async function _createTransports (args) {
try {
transports.push(_createHttpTransport(args, fileLogLevel));
} catch (e) {
// eslint-disable-next-line no-console
console.log(`Tried to attach logging to Http at ${args.webhook} but ` +
`an error occurred: ${e.message}`);
}
@@ -178,6 +180,10 @@ async function init (args) {
winston.addColors(colors);
}
// clean up in case we have initted before since npmlog is a global
// object
clear();
logger = new (winston.Logger)({
transports: await _createTransports(args)
});
@@ -191,6 +197,10 @@ async function init (args) {
msg = `${prefix.magenta} ${msg}`;
}
logger[winstonLevel](msg);
if (args.logHandler && typeof args.logHandler === "function") {
args.logHandler(logObj.level, msg);
}
});
+25 -6
View File
@@ -4,9 +4,9 @@
import { init as logsinkInit } from './logsink';
import logger from './logger'; // logger needs to remain first of imports
import _ from 'lodash';
import { default as baseServer } from 'appium-express';
import { server as baseServer } from 'appium-base-driver';
import { asyncify } from 'asyncbox';
import getParser from './parser';
import { default as getParser, getDefaultArgs } from './parser';
import { showConfig, checkNodeOk, validateServerArgs,
warnNodeDeprecations, validateTmpDir, getNonDefaultArgs,
getDeprecatedArgs, getGitRev, APPIUM_VER } from './config';
@@ -15,7 +15,7 @@ import registerNode from './grid-register';
import util from 'util';
async function preflightChecks (parser, args) {
async function preflightChecks (parser, args, throwInsteadOfExit = false) {
try {
checkNodeOk();
if (args.asyncTrace) {
@@ -32,6 +32,10 @@ async function preflightChecks (parser, args) {
}
} catch (err) {
logger.error(err.message.red);
if (throwInsteadOfExit) {
throw err;
}
process.exit(1);
}
}
@@ -93,11 +97,26 @@ function logServerPort (address, port) {
async function main (args = null) {
let parser = getParser();
if (!args) {
let throwInsteadOfExit = false;
if (args) {
// a containing package passed in their own args, let's fill them out
// with defaults
args = Object.assign({}, getDefaultArgs(), args);
// if we have a containing package instead of running as a CLI process,
// that package might not appreciate us calling 'process.exit' willy-
// nilly, so give it the option to have us throw instead of exit
if (args.throwInsteadOfExit) {
throwInsteadOfExit = true;
// but remove it since it's not a real server arg per se
delete args.throwInsteadOfExit;
}
} else {
// otherwise parse from CLI
args = parser.parseArgs();
}
await logsinkInit(args);
await preflightChecks(parser, args);
await preflightChecks(parser, args, throwInsteadOfExit);
await logStartupInfo(parser, args);
let router = getAppiumRouter(args);
let server = await baseServer(router, args.port, args.address);
@@ -110,7 +129,7 @@ async function main (args = null) {
await registerNode(args.nodeconfig, args.address, args.port);
}
} catch (err) {
server.close();
await server.close();
throw err;
}
logServerPort(args.address, args.port);
+46 -17
View File
@@ -1,7 +1,8 @@
import fs from 'fs';
import path from 'path';
import _ from 'lodash';
import { ArgumentParser } from 'argparse';
import pkgObj from '../../package.json';
import pkgObj from '../../package.json'; // eslint-disable-line import/no-unresolved
const args = [
@@ -10,6 +11,7 @@ const args = [
defaultValue: null,
help: 'Enter REPL mode',
nargs: 0,
dest: 'shell',
}],
[['--ipa'], {
@@ -17,6 +19,7 @@ const args = [
defaultValue: null,
help: '(IOS-only) abs path to compiled .ipa file',
example: '/abs/path/to/my.ipa',
dest: 'ipa',
}],
[['-a', '--address'], {
@@ -24,6 +27,7 @@ const args = [
required: false,
example: '0.0.0.0',
help: 'IP Address to listen on',
dest: 'address',
}],
[['-p', '--port'], {
@@ -32,6 +36,7 @@ const args = [
type: 'int',
example: '4723',
help: 'port to listen on',
dest: 'port',
}],
[['-ca', '--callback-address'], {
@@ -140,12 +145,14 @@ const args = [
defaultValue: null,
required: false,
example: 'localhost:9876',
dest: 'webhook',
help: 'Also send log output to this HTTP listener',
}],
[['--safari'], {
defaultValue: false,
action: 'storeTrue',
dest: 'safari',
required: false,
help: '(IOS-Only) Use the safari app',
nargs: 0,
@@ -197,6 +204,7 @@ const args = [
[['--nodeconfig'], {
required: false,
defaultValue: null,
dest: 'nodeconfig',
help: 'Configuration JSON file to register appium with selenium grid',
example: '/abs/path/to/nodeconfig.json',
}],
@@ -228,12 +236,12 @@ const args = [
}],
[['--chromedriver-port'], {
defaultValue: 9515,
defaultValue: null,
dest: 'chromeDriverPort',
required: false,
type: 'int',
example: '9515',
help: 'Port upon which ChromeDriver will run',
help: 'Port upon which ChromeDriver will run. If not given, Android driver will pick a random available port.',
}],
[['--chromedriver-executable'], {
@@ -259,16 +267,6 @@ const args = [
help: 'Bypass Appium\'s checks to ensure we can read/write necessary files',
}],
[['--command-timeout'], {
defaultValue: 60,
dest: 'defaultCommandTimeout',
type: 'int',
required: false,
help: 'The default command timeout for the server to use for all ' +
'sessions (in seconds and should be less than 2147483). ' +
'Will still be overridden by newCommandTimeout cap',
}],
[['--strict-caps'], {
defaultValue: false,
dest: 'enforceStrictCaps',
@@ -344,18 +342,29 @@ const args = [
help: '(IOS-only) Local port used for communication with ios-webkit-debug-proxy'
}],
[['--default-capabilities'], {
[['-dc', '--default-capabilities'], {
dest: 'defaultCapabilities',
defaultValue: {},
type: JSON.parse,
type: parseDefaultCaps,
required: false,
example: '{"app": "myapp.app", "deviceName": "iPhone Simulator"}',
example: '[ \'{"app": "myapp.app", "deviceName": "iPhone Simulator"}\' ' +
'| /path/to/caps.json ]',
help: 'Set the default desired capabilities, which will be set on each ' +
'session unless overridden by received capabilities.'
}],
];
const deprecatedArgs = [
[['--command-timeout'], {
defaultValue: 60,
dest: 'defaultCommandTimeout',
type: 'int',
required: false,
help: '[DEPRECATED] No effect. This used to be the default command ' +
'timeout for the server to use for all sessions (in seconds and ' +
'should be less than 2147483). Use newCommandTimeout cap instead'
}],
[['-k', '--keep-artifacts'], {
defaultValue: false,
dest: 'keepArtifacts',
@@ -731,6 +740,26 @@ function updateParseArgsForDefaultCapabilities (parser) {
};
}
function parseDefaultCaps (caps) {
try {
// use synchronous file access, as `argparse` provides no way of either
// awaiting or using callbacks. This step happens in startup, in what is
// effectively command-line code, so nothing is blocked in terms of
// sessions, so holding up the event loop does not incur the usual
// drawbacks.
if (fs.statSync(caps).isFile()) {
caps = fs.readFileSync(caps, 'utf8');
}
} catch (err) {
// not a file, or not readable
}
caps = JSON.parse(caps);
if (!_.isPlainObject(caps)) {
throw 'Invalid format for default capabilities';
}
return caps;
}
function getParser () {
let parser = new ArgumentParser({
version: pkgObj.version,
@@ -749,7 +778,7 @@ function getParser () {
function getDefaultArgs () {
let defaults = {};
for (let [,arg] of args) {
for (let [, arg] of args) {
defaults[arg.dest] = arg.defaultValue;
}
return defaults;
+37 -22
View File
@@ -11,7 +11,7 @@
"firefoxos",
"testing"
],
"version": "1.5.0",
"version": "1.6.0-beta1",
"author": "https://github.com/appium",
"license": "Apache-2.0",
"repository": {
@@ -22,8 +22,8 @@
"url": "https://github.com/appium/appium/issues"
},
"engines": {
"node": ">=0.12",
"iojs": ">=1.0.0"
"node": ">=4",
"npm": ">=3"
},
"main": "./build/lib/main.js",
"bin": {
@@ -35,44 +35,59 @@
"doc": "./docs"
},
"dependencies": {
"appium-android-driver": "^1.8.0",
"appium-base-driver": "^1.4.1",
"appium-express": "^1.2.2",
"appium-fake-driver": "^0.1.9",
"appium-ios-driver": "^1.9.0",
"appium-android-driver": "^1.10.19",
"appium-base-driver": "^2.0.8",
"appium-fake-driver": "^0.1.10",
"appium-ios-driver": "^1.12.19",
"appium-logger": "^2.1.0",
"appium-selendroid-driver": "^1.3.0",
"appium-support": "^2.0.10",
"appium-selendroid-driver": "^1.3.4",
"appium-support": "^2.3.0",
"appium-xcuitest-driver": "^2.0.0",
"appium-youiengine-driver": "^1.0.3",
"appium-windows-driver": "^0.1.2",
"argparse": "^1.0.7",
"asyncbox": "^2.3.1",
"authorize-ios": "^1.0.4",
"babel-runtime": "=5.8.24",
"bluebird": "^2.10.2",
"continuation-local-storage": "^3.1.6",
"date-utils": "^1.2.19",
"lodash": "^4.6.1",
"mobile-json-wire-protocol": "^1.3.3",
"npmlog": "^2.0.3",
"continuation-local-storage": "^3.1.7",
"date-utils": "^1.2.21",
"lodash": "^4.13.1",
"npmlog": "^2.0.4",
"request-promise": "^1.0.2",
"source-map-support": "^0.4.0",
"teen_process": "^1.5.2",
"teen_process": "^1.7.1",
"winston": "^2.2.0"
},
"scripts": {
"prepublish": "gulp prepublish",
"test": "gulp once",
"watch": "gulp"
"watch": "gulp",
"precommit-msg": "echo 'Pre-commit checks...' && exit 0",
"lint": "gulp eslint"
},
"pre-commit": [
"precommit-msg",
"lint",
"test"
],
"devDependencies": {
"appium-gulp-plugins": "^1.4.3",
"appium-gulp-plugins": "^1.4.5",
"babel-eslint": "^6.1.0",
"chai": "^3.5.0",
"chai-as-promised": "^5.2.0",
"chai-as-promised": "^5.3.0",
"eslint": "^2.13.1",
"eslint-config-appium": "0.0.6",
"eslint-plugin-babel": "^3.3.0",
"eslint-plugin-import": "^1.9.2",
"eslint-plugin-mocha": "^3.0.0",
"gulp": "^3.9.1",
"mocha": "^2.4.5",
"sinon": "^1.17.3",
"mocha": "^2.5.3",
"pre-commit": "^1.1.3",
"sinon": "^1.17.4",
"wd": "~0.4.0"
},
"optionalDependencies": {
"fsevents": "^1.0.8"
"fsevents": "^1.0.12"
}
}
+93 -45
View File
@@ -42,8 +42,8 @@ describe('Config', () => {
it('should log the config to console', async () => {
let config = await getAppiumConfig();
await showConfig();
console.log.calledOnce.should.be.true;
console.log.getCall(0).args[0].should.contain(JSON.stringify(config));
console.log.calledOnce.should.be.true; // eslint-disable-line no-console
console.log.getCall(0).args[0].should.contain(JSON.stringify(config)); // eslint-disable-line no-console
});
});
});
@@ -64,17 +64,27 @@ describe('Config', () => {
process = _process;
});
describe('checkNodeOk', () => {
it('should fail if node is below 0.10', () => {
it('should fail if node is below 4', () => {
process.version = 'v0.9.12';
checkNodeOk.should.throw;
checkNodeOk.should.throw();
process.version = 'v0.1';
checkNodeOk.should.throw();
process.version = 'v0.10.36';
checkNodeOk.should.throw();
process.version = 'v0.12.14';
checkNodeOk.should.throw();
});
it('should succeed if node is 0.10+', () => {
process.version = 'v0.10.0';
checkNodeOk.should.not.throw;
it('should succeed if node is 4+', () => {
process.version = 'v4.4.7';
checkNodeOk.should.not.throw();
});
it('should succeed if node is 1.x', () => {
process.version = 'v1.0.0';
checkNodeOk.should.not.throw;
it('should succeed if node is 5+', () => {
process.version = 'v5.7.0';
checkNodeOk.should.not.throw();
});
it('should succeed if node is 6+', () => {
process.version = 'v6.3.1';
checkNodeOk.should.not.throw();
});
});
@@ -86,18 +96,28 @@ describe('Config', () => {
beforeEach(() => {
spy.reset();
});
it('should log a warning if node is below 0.12', () => {
it('should log a warning if node is below 4', () => {
process.version = 'v0.9.12';
warnNodeDeprecations();
logger.warn.callCount.should.equal(1);
});
it('should not log a warning if node is 0.12+', () => {
it('should log a warning if node is 0.12', () => {
process.version = 'v0.12.0';
warnNodeDeprecations();
logger.warn.callCount.should.equal(1);
});
it('should not log a warning if node is 4+', () => {
process.version = 'v4.4.7';
warnNodeDeprecations();
logger.warn.callCount.should.equal(0);
});
it('should not log a warning if node is 1.x', () => {
process.version = 'v1.0.0';
it('should not log a warning if node is 5+', () => {
process.version = 'v5.7.0';
warnNodeDeprecations();
logger.warn.callCount.should.equal(0);
});
it('should not log a warning if node is 6+', () => {
process.version = 'v6.3.1';
warnNodeDeprecations();
logger.warn.callCount.should.equal(0);
});
@@ -106,6 +126,7 @@ describe('Config', () => {
describe('server arguments', () => {
let parser = getParser();
parser.debug = true; // throw instead of exit on error; pass as option instead?
let args = {};
beforeEach(() => {
// give all the defaults
@@ -175,85 +196,109 @@ describe('Config', () => {
describe('validateServerArgs', () => {
let parser = getParser();
parser.debug = true; // throw instead of exit on error; pass as option instead?
const defaultArgs = {};
// give all the defaults
for (let rawArg of parser.rawArgs) {
defaultArgs[rawArg[1].dest] = rawArg[1].defaultValue;
}
let args = {};
beforeEach(() => {
args = _.clone(defaultArgs);
});
describe('mutually exclusive server arguments', () => {
describe('noReset and fullReset', () => {
it('should not allow both', () => {
(() => {
validateServerArgs(parser, {noReset: true, fullReset: true});
}).should.throw;
args.noReset = args.fullReset = true;
validateServerArgs(parser, args);
}).should.throw();
});
it('should allow noReset', () => {
(() => {
validateServerArgs(parser, {noReset: true});
}).should.not.throw;
args.noReset = true;
validateServerArgs(parser, args);
}).should.not.throw();
});
it('should allow fullReset', () => {
(() => {
validateServerArgs(parser, {fullReset: true});
}).should.not.throw;
args.fullReset = true;
validateServerArgs(parser, args);
}).should.not.throw();
});
});
describe('ipa and safari', () => {
it('should not allow both', () => {
(() => {
validateServerArgs(parser, {ipa: true, safari: true});
}).should.throw;
args.ipa = args.safari = true;
validateServerArgs(parser, args);
}).should.throw();
});
it('should allow ipa', () => {
(() => {
validateServerArgs(parser, {ipa: true});
}).should.not.throw;
args.ipa = true;
validateServerArgs(parser, args);
}).should.not.throw();
});
it('should allow safari', () => {
(() => {
validateServerArgs(parser, {safari: true});
}).should.not.throw;
args.safari = true;
validateServerArgs(parser, args);
}).should.not.throw();
});
});
describe('app and safari', () => {
it('should not allow both', () => {
(() => {
validateServerArgs(parser, {app: true, safari: true});
}).should.throw;
args.app = args.safari = true;
validateServerArgs(parser, args);
}).should.throw();
});
it('should allow app', () => {
(() => {
validateServerArgs(parser, {app: true});
}).should.not.throw;
args.app = true;
validateServerArgs(parser, args);
}).should.not.throw();
});
});
describe('forceIphone and forceIpad', () => {
it('should not allow both', () => {
(() => {
validateServerArgs(parser, {forceIphone: true, forceIpad: true});
}).should.throw;
args.forceIphone = args.forceIpad = true;
validateServerArgs(parser, args);
}).should.throw();
});
it('should allow forceIphone', () => {
(() => {
validateServerArgs(parser, {forceIphone: true});
}).should.not.throw;
args.forceIphone = true;
validateServerArgs(parser, args);
}).should.not.throw();
});
it('should allow forceIpad', () => {
(() => {
validateServerArgs(parser, {forceIpad: true});
}).should.not.throw;
args.forceIpad = true;
validateServerArgs(parser, args);
}).should.not.throw();
});
});
describe('deviceName and defaultDevice', () => {
it('should not allow both', () => {
(() => {
validateServerArgs(parser, {deviceName: true, defaultDevice: true});
}).should.throw;
args.deviceName = args.defaultDevice = true;
validateServerArgs(parser, args);
}).should.throw();
});
it('should allow deviceName', () => {
(() => {
validateServerArgs(parser, {deviceName: true});
}).should.not.throw;
args.deviceName = true;
validateServerArgs(parser, args);
}).should.not.throw();
});
it('should allow defaultDevice', () => {
(() => {
validateServerArgs(parser, {defaultDevice: true});
}).should.not.throw;
args.defaultDevice = true;
validateServerArgs(parser, args);
}).should.not.throw();
});
});
});
@@ -262,13 +307,16 @@ describe('Config', () => {
// the only argument left is `backendRetries`
describe('backendRetries', () => {
it('should fail with value less than 0', () => {
(() => {validateServerArgs(parser, {backendRetries: -1});}).should.throw;
args.backendRetries = -1;
(() => {validateServerArgs(parser, args);}).should.throw();
});
it('should succeed with value of 0', () => {
(() => {validateServerArgs(parser, {backendRetries: 0});}).should.not.throw;
args.backendRetries = 0;
(() => {validateServerArgs(parser, args);}).should.not.throw();
});
it('should succeed with value above 0', () => {
(() => {validateServerArgs(parser, {backendRetries: 100});}).should.not.throw;
args.backendRetries = 100;
(() => {validateServerArgs(parser, args);}).should.not.throw();
});
});
});
+26 -5
View File
@@ -6,7 +6,6 @@ import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import wd from 'wd';
import { main as appiumServer } from '../lib/main';
import { getDefaultArgs } from '../lib/parser';
import { TEST_FAKE_APP, TEST_HOST, TEST_PORT } from './helpers';
chai.use(chaiAsPromised);
@@ -19,15 +18,13 @@ describe('FakeDriver - via HTTP', () => {
let server = null;
before(async () => {
if (shouldStartServer) {
let args = getDefaultArgs();
args.port = TEST_PORT;
args.host = TEST_HOST;
let args = {port: TEST_PORT, host: TEST_HOST};
server = await appiumServer(args);
}
});
after(async () => {
if (server) {
await B.promisify(server.close.bind(server))();
await server.close();
}
});
@@ -80,3 +77,27 @@ describe('FakeDriver - via HTTP', () => {
});
});
});
describe('Logsink', () => {
let server = null;
let logs = [];
let logHandler = (level, message) => {
logs.push([level, message]);
};
let args = {port: TEST_PORT, host: TEST_HOST, logHandler};
before(async () => {
server = await appiumServer(args);
});
after(async () => {
await server.close();
});
it('should send logs to a logHandler passed in by a parent package', async () => {
logs.length.should.be.above(1);
logs[0].length.should.equal(2);
logs[0][1].should.include("Welcome to Appium");
});
});
+100 -1
View File
@@ -7,6 +7,9 @@ import _ from 'lodash';
import sinon from 'sinon';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import { XCUITestDriver } from 'appium-xcuitest-driver';
import { IosDriver } from 'appium-ios-driver';
chai.use(chaiAsPromised);
@@ -71,6 +74,41 @@ describe('AppiumDriver', () => {
await appium.createSession(BASE_CAPS);
mockFakeDriver.verify();
});
it('should kill all other sessions if sessionOverride is on', async () => {
appium.args.sessionOverride = true;
// mock three sessions that should be removed when the new one is created
let fakeDrivers = [new FakeDriver(),
new FakeDriver(),
new FakeDriver()];
let mockFakeDrivers = _.map(fakeDrivers, (fd) => {return sinon.mock(fd);});
mockFakeDrivers[0].expects('deleteSession')
.once();
mockFakeDrivers[1].expects('deleteSession')
.once()
.throws('Cannot shut down Android driver; it has already shut down');
mockFakeDrivers[2].expects('deleteSession')
.once();
appium.sessions['abc-123-xyz'] = fakeDrivers[0];
appium.sessions['xyz-321-abc'] = fakeDrivers[1];
appium.sessions['123-abc-xyz'] = fakeDrivers[2];
let sessions = await appium.getSessions();
sessions.should.have.length(3);
mockFakeDriver.expects("createSession")
.once().withExactArgs(BASE_CAPS, undefined, [])
.returns([1, BASE_CAPS]);
await appium.createSession(BASE_CAPS);
sessions = await appium.getSessions();
sessions.should.have.length(1);
for (let mfd of mockFakeDrivers) {
mfd.verify();
}
mockFakeDriver.verify();
});
});
describe('deleteSession', () => {
let appium
@@ -135,10 +173,71 @@ describe('AppiumDriver', () => {
describe('sessionExists', () => {
});
describe('getDriverForCaps', () => {
it('should not blow up if user doesnt provide platformName', () => {
it('should not blow up if user does not provide platformName', () => {
let appium = new AppiumDriver({});
(() => { appium.getDriverForCaps({}); }).should.throw(/platformName/);
});
it('should get XCUITestDriver driver for automationName of XCUITest', () => {
let appium = new AppiumDriver({});
let driver = appium.getDriverForCaps({
platformName: 'iOS',
automationName: 'XCUITest'
});
driver.should.be.an.instanceof(Function);
driver.should.equal(XCUITestDriver);
});
it('should get iosdriver for ios < 10', () => {
let appium = new AppiumDriver({});
let caps = {
platformName: 'iOS',
platformVersion: '8.0',
};
let driver = appium.getDriverForCaps(caps);
driver.should.be.an.instanceof(Function);
driver.should.equal(IosDriver);
caps.platformVersion = '8.1';
driver = appium.getDriverForCaps(caps);
driver.should.equal(IosDriver);
caps.platformVersion = '9.4';
driver = appium.getDriverForCaps(caps);
driver.should.equal(IosDriver);
caps.platformVersion = '';
driver = appium.getDriverForCaps(caps);
driver.should.equal(IosDriver);
caps.platformVersion = 'foo';
driver = appium.getDriverForCaps(caps);
driver.should.equal(IosDriver);
delete caps.platformVersion;
driver = appium.getDriverForCaps(caps);
driver.should.equal(IosDriver);
});
it('should get xcuitestdriver for ios >= 10', () => {
let appium = new AppiumDriver({});
let caps = {
platformName: 'iOS',
platformVersion: '10',
};
let driver = appium.getDriverForCaps(caps);
driver.should.be.an.instanceof(Function);
driver.should.equal(XCUITestDriver);
caps.platformVersion = '10.0';
driver = appium.getDriverForCaps(caps);
driver.should.equal(XCUITestDriver);
caps.platformVersion = '10.1';
driver = appium.getDriverForCaps(caps);
driver.should.equal(XCUITestDriver);
caps.platformVersion = '12.14';
driver = appium.getDriverForCaps(caps);
driver.should.equal(XCUITestDriver);
});
});
});
});
+3
View File
@@ -0,0 +1,3 @@
{
"a": "b"
}
+15 -2
View File
@@ -7,6 +7,7 @@ const should = chai.should();
describe('Parser', () => {
let p = getParser();
p.debug = true; // throw instead of exit on error; pass as option instead?
it('should return an arg parser', () => {
should.exist(p.parseArgs);
p.parseArgs([]).should.have.property('port');
@@ -20,14 +21,26 @@ describe('Parser', () => {
}
});
it('should throw an error with unknown argument', () => {
(() => {p.parseArgs(['--apple']);}).should.throw;
(() => {p.parseArgs(['--apple']);}).should.throw();
});
it('should parse default capabilities correctly', () => {
it('should parse default capabilities correctly from a string', () => {
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', () => {
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', () => {
(() => {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', () => {
let defaultCapabilities = {localizableStringsDir: '/my/dir'};
let args = p.parseArgs(['--localizable-strings-dir', '/my/dir']);