Compare commits

...
Author SHA1 Message Date
moizjv 1ea1dfecb5 Updating changelog, package.json and npm-shrinkwrap for 1.4.15 2015-11-17 15:18:49 -08:00
moizjv 46e25a7475 Better way for selecting address bar 2015-11-17 11:41:32 -08:00
Isaac Murchie d0abe2176a Keep track of redirects for safari 2015-11-17 11:40:35 -08:00
Isaac Murchie 3b71a7261a Add check for particular url in safari app selection 2015-11-17 11:40:11 -08:00
Isaac Murchie c3fe347241 Fix building of selendroid 2015-11-06 10:17:42 -08:00
Scott Dixon 53b76a10f6 Bumped to 1.4.14, changelog and shrinkwrap 2015-11-06 09:47:25 -08:00
Isaac Murchie 2a20daba57 Keep track of updated remote debugger app 2015-11-06 09:47:10 -08:00
Scott Dixon 21d22b7998 Updated tests, fixed mocha grep string 2015-11-06 09:47:01 -08:00
Scott Dixon 4093049d1d Fixed touch tests 2015-11-06 09:46:51 -08:00
Scott Dixon 2c35eaba9a Fixed language tests for iOS9+ 2015-11-06 09:46:41 -08:00
Scott Dixon 0a87b96a56 Initial 9.2 support 2015-11-06 09:45:57 -08:00
moizjv 0643e6f12f Fixing typo 2015-11-06 09:43:01 -08:00
Luke Inman-Semerau a2f7230de4 update selendroid to 0.17.0 release 2015-11-06 09:42:51 -08:00
moizjv 752aa57a40 Adding xcode_iwd.sh and documentation 2015-11-06 09:42:31 -08:00
Dan Mendoza 418b610f6f Removing obsoleted installApp and removeApp functions. 2015-11-06 09:42:22 -08:00
Dan Mendoza 2ee886c2d9 Using appium-adb for installApp and removeApp 2015-11-06 09:42:06 -08:00
Jonathan Lipps 3f6a8cfa8d update license with a legal entity
"Appium Committers" was not a legal entity

fyi @imurchie might need to pull this into 1.5 as well
2015-11-06 09:38:55 -08:00
ahansen1 eb9491b2b1 UiScrollableParser - Verify UiScrollable Exists 2015-11-06 09:38:44 -08:00
Michal Sarna 4e645a656e removed "("
removed 'webkitDebugProxyPort'
2015-11-06 09:37:56 -08:00
Scott Dixon c75d8adcb6 Merge pull request #5674 from moizjv/1.4
Reverting selendroid, appium-chromedriver, bumping version and changelog
2015-09-29 18:26:31 -07:00
moizjv 86d038ab8c Bumping version to 1.4.13 and updating changelog 2015-09-29 18:22:55 -07:00
moizjv beaeeff21a Reverting selendroid submodule and appium-chromedriver to stable versions 2015-09-29 17:54:34 -07:00
Scott Dixon 8db2d00b9a Updated changelog 2015-09-28 13:48:47 -07:00
Scott Dixon 3465cdce71 Updating changelog, shrinkwrap and bumping version to 1.4.12 2015-09-28 13:23:17 -07:00
Scott Dixon 14a5920d1d uiauto update 2015-09-28 13:18:16 -07:00
scottdixon 601c5a90dd Updated submodule: appium-instruments 2015-09-28 13:17:56 -07:00
Scott Dixon 9578637215 iOS9+ about:blank fix 2015-09-28 13:17:50 -07:00
Jonathan Lipps d4a6967749 update packweb with new module 2015-09-28 13:17:10 -07:00
Sakari Rautiainen 8232845a8f Fix to use udid option with idevicesyslog 2015-09-28 12:08:51 -07:00
Scott Dixon 117340535d Used xcode.getVersion to fix pref tests 2015-09-28 12:08:42 -07:00
Scott Dixon eea1646a47 Updated submodules 2015-09-28 12:08:32 -07:00
Scott Dixon 24fa3c0786 package.json - sample-apps update 2015-09-28 12:07:53 -07:00
Scott Dixon ed4f23812f appium-uiauto 1.10.9 / iOS9.1 support 2015-09-28 12:05:22 -07:00
moizjv c79537b0aa Fixing ambiguous device issue for iPhone 6 (9.0) #5619 2015-09-28 12:05:08 -07:00
Scott Dixon 499815054e Initial iOS 9.1 support 2015-09-28 12:04:52 -07:00
moizjv 89539899bf Fixing device name fix for xcode 7 2015-09-28 12:03:48 -07:00
moizjv 8cf8311f00 Changelog, npm-shrinkwrap and version bump for 1.4.11 2015-09-15 17:24:36 -07:00
moizjv 1291ce9651 Fixing ios device fix for ios9 2015-09-15 17:24:36 -07:00
Eric Engelking 815ecbb52b Fix Xcode simulator selector for Xcode 7 beta 6 2015-09-15 17:24:36 -07:00
Jonathan Lipps 33e72e9c64 Merge pull request #5523 from appium/isaac-rd-retries
Add ability to tweak remote debugger retries
2015-08-21 10:12:35 -07:00
Isaac Murchie 7a4ffc091f Add ability to tweak remote debugger retries 2015-08-19 11:50:28 -07:00
Jonathan Lipps ec6d3fdca5 Merge pull request #5492 from amake/xcode7
Fix iOS device string for iOS 9 SDK
2015-08-12 10:58:31 -07:00
Aaron Madlon-Kay 490d930aec Fix iOS device string for iOS 9 SDK 2015-08-11 09:48:16 +09:00
moizjv c97e824665 Bumping version to 1.4.10 also updating changelog 2015-08-06 20:56:14 -07:00
moizjv 3d09c6545c Bumping version to 1.4.9 and updating changelog 2015-08-06 18:56:33 -07:00
moizjv 40f594c0ae Updating appium-instruments and shrink-wrap 2015-08-06 16:33:29 -07:00
moizjv 29b2c4d02d adding iOS9 support to tests 2015-07-31 16:10:23 -07:00
Alexander Bayandin edefce21e3 Fix appium-doctor for OS X 10.11 2015-07-31 16:10:13 -07:00
Jonathan Lipps 9b032dd3c9 use numeric instead of string comparisons for xcode versions, courtesy @bayandin 2015-07-31 16:10:00 -07:00
Jonathan Lipps 8beeae6560 add basic ios9 selection logic 2015-07-31 16:06:48 -07:00
Jonah Stiennon c8179bd835 bump to 1.4.8 for real (not beta) 2015-07-15 15:28:18 -07:00
Jonah b05c08f5bb Merge pull request #5383 from Jonahss/1.4
bump to 1.4.8
2015-07-15 14:48:47 -07:00
Jonah Stiennon 0f25482641 bump to 1.4.8 2015-07-15 14:48:25 -07:00
Jonah Stiennon 43ae31f62f update appium-support to version 1.1.2 2015-07-14 12:02:55 -07:00
Jonah Stiennon 66a62bde36 have calls to /status endpoint hit the appium server and NOT be proxied to chromedriver when testing in chrome on android. fixes #5335 2015-07-10 11:31:56 +08:00
moizjv 3b1a3b3ddf Bumping to version 1.4.7 2015-07-01 19:29:10 -07:00
moizjv f5590b4989 Correct version of shrinkwrap 2015-07-01 18:06:28 -07:00
moizjv a4c39d6082 Adding socket.io-client back to devDep, regenerating shrinkwrap and bumping to beta3 2015-07-01 17:12:35 -07:00
moizjv b33388f56f Adding socket.io-client as a dependency instead of dev dependency 2015-07-01 16:00:15 -07:00
moizjv 529401b642 Bumping version to 1.4.7 beta, adding changes to changelog and npm-shrinkwrap 2015-06-30 12:07:17 -07:00
Jonathan Lipps 1efd333302 changelog and bump to 1.4.6 for publish..again 2015-06-19 10:27:24 -07:00
Jonathan Lipps 114b3b31db bump to 1.4.5, since npm-shrinkwrap seems to work 2015-06-18 18:00:33 -07:00
Jonathan Lipps 564d4b6f63 changelog and bump to 1.4.5-beta to test if this new shrinkwrap works 2015-06-18 17:48:09 -07:00
Jonathan Lipps cd67dd6fef change shrinkwrap to be generated by npm-shrinkwrap, seems to actually work 2015-06-18 17:48:06 -07:00
Jonathan Lipps 5e072e99b4 stack-trace is not a dev dep, it's an actual dep 2015-06-18 17:48:00 -07:00
Jonathan Lipps 5f76497b94 changelog and bump to 1.4.4, also use npm shrinkwrap as we should have been doing all along 2015-06-18 16:59:09 -07:00
Jonathan Lipps aa8df18bd4 don't rely on async functions returning Promise objects; just use .then 2015-06-18 16:53:48 -07:00
Jonathan Lipps a357c7fa73 also update appium-instruments submodule since we do that still 2015-06-09 10:27:07 -07:00
Jonathan Lipps 44474700f4 update appium-instruments with subdep fix, bump to 1.4.3 and changelog 2015-06-09 10:27:03 -07:00
Jonathan Lipps fb5df00a6a bump version to 1.4.2 2015-06-08 17:23:02 -07:00
Jonathan Lipps 2da504e68f changelog for 1.4.1 and 1.4.2 2015-06-08 14:27:17 -07:00
Jonathan Lipps 6d0143439c upgrade es6 deps to fix babel/regenerator issue 2015-06-08 14:23:18 -07:00
Isaac Murchie 0923f50830 Keep track of the correct app id for safari webview 2015-06-08 14:23:12 -07:00
moizjv aa64ac7383 Fixing sendKeys and clear text for real samsung devices 2015-05-26 15:13:47 -07:00
moizjv ce62208290 Bumping to 1.4.1 for patch release 2015-05-19 08:49:41 -07:00
Jonathan Lipps e7cad78f2d appium-chromedriver now manages and builds chromedriver binaries, so remove all logic that has to do with building and finding them in appium itself (fix #5095) 2015-05-19 08:45:45 -07:00
moizjv 8f63e2f91e Bump to 1.4.0 to publish 2015-05-08 13:56:28 -07:00
moizjv a89066c6c4 Fixing broken link for UiAutomation 2015-05-08 13:49:41 -07:00
moizjv 7c3437fb46 Adding chromeOptions to documentation 2015-05-06 19:45:03 -07:00
Jonathan Lipps 4caad14ba7 final 1.4 changelog update 2015-05-06 14:32:05 -07:00
Isaac Murchie 393c37eba7 Fix Android keys by finding focused element 2015-05-06 14:32:05 -07:00
moizjv 20bed5e41d Fixing extractStrings method 2015-05-06 14:32:05 -07:00
Jonah Stiennon d62cf6de1f fix #5061, execute_async on Safari Real Device 2015-05-06 14:31:57 -07:00
Jonathan Lipps dc6a500287 update 1.4 changelog 2015-05-05 14:18:18 -07:00
moizjv 7e804db96c Adding supress-adb-kill-server to changelog 2015-05-05 14:18:18 -07:00
moizjv 36fc3fec0e Fixing location settings for iOS 2015-05-05 14:18:18 -07:00
Jonathan Lipps 94fe13693b add troubleshooting note about accessibility status for custom controls 2015-05-05 14:18:18 -07:00
sebv f8890302a8 bumping io.appium.gappium.sampleapp 2015-05-05 08:01:05 -07:00
sebv 26471c5386 android test fix 2015-05-05 08:01:00 -07:00
Jonathan Lipps 7257b1f8d7 node version deprecation warning should go after the show-config branch, otherwise it'll mess up JSON 2015-05-04 16:10:26 -07:00
Jonathan Lipps dea97f6844 update 1.4 changelog 2015-05-04 15:21:27 -07:00
Jonathan Lipps a7becef815 add an es6 Promise polyfill so node 0.10 still works (fix #5046) 2015-05-04 14:54:17 -07:00
Jonathan Lipps 15c6906cf4 deprecate node 0.10 2015-05-04 14:54:17 -07:00
Isaac Murchie 6f4f3bcdeb Update android ApiDemos app submodule 2015-05-04 14:54:17 -07:00
Isaac Murchie 6729fdd0eb Update unicode ime submodule 2015-05-04 14:54:16 -07:00
Jonathan Lipps 1c871e054d update appium-instruments/appium-xcode with another trailing slash fix 2015-05-04 11:23:02 -07:00
Jonathan Lipps 44faa8db0e update appium-instruments with appium-xcode path fix (fix #5018) 2015-05-01 14:50:59 -07:00
Jonathan Lipps fdbe71473b add more logging and error handling for extracting android strings, to prevent a crash (fix #4759, fix #4630) 2015-05-01 14:50:55 -07:00
Jonathan Lipps 80a285627e fix remote debugger so it doesn't use console.log, and truncate logs displayed 2015-05-01 14:50:48 -07:00
Jonathan Lipps 3efec0b073 update xcode deprecation to allow Xcode6.0.1+iOS8.0, and update 1.4 beta changelog 2015-04-30 21:05:55 -05:00
moizjv effe14b6ab Fixing perform touch with release action 2015-04-30 21:05:49 -05:00
Jonah Stiennon 437b7bed17 Safari Launcher updated to use Instruments to press a button to launch safari. Fixes Safari tests on real devices iOS 8.0 and up 2015-04-30 21:05:39 -05:00
Jonah Stiennon 045ec45ab9 fix a bug in finding ios real device id 2015-04-30 21:05:31 -05:00
Jonathan Lipps 0b30bd1cb4 fix issue with chromedriver not returning sessionId for override (fix #5013) 2015-04-29 12:15:10 -05:00
Isaac Murchie 4d4c8a2ba0 Update to allow safari on a 7.1 sim above xcode 5 2015-04-29 11:55:27 -05:00
Jonathan Lipps 60ce2c302e it's ok for the url to be about:blank when starting an older version of safari 2015-04-28 18:07:19 -05:00
Jonathan Lipps aa2e40fe40 update appium-xcode with trailing path fix (fix #5005) 2015-04-28 16:14:33 -05:00
moizjv a9e1b78f00 Fixing chrome driver to use correct ADB path 2015-04-28 15:37:17 -05:00
Jonathan Lipps 8f4eaa7745 fix incorrect .bind() causing an undefined crash when prelaunching sim (cc @Jonahss) 2015-04-28 15:07:45 -05:00
Jonathan Lipps 5bc053be9a update appium-xcode with ios6.1/xcrun fix 2015-04-28 12:49:06 -05:00
Jonathan Lipps 13420f8093 deprecate iOS < 7 and xcode < 6.3 (resolve #4996) 2015-04-28 12:49:00 -05:00
moizjv a1def6633b Fixing actual android capability 2015-04-28 12:48:56 -05:00
moizjv df21a5390c Fixing headings for documentation 2015-04-28 12:48:53 -05:00
Jonathan Lipps 2ba44979a3 don't use the 'center' fenced code block because jekyll doesn't like it in the docs build for appium.io 2015-04-28 12:48:46 -05:00
Jonathan Lipps f19e0fdbee update publish.sh so it checks for node 0.12 and removes antiquted xcode check 2015-04-28 12:48:29 -05:00
79 changed files with 5075 additions and 711 deletions
-3
View File
@@ -16,9 +16,6 @@
[submodule "submodules/unlock_apk"]
path = submodules/unlock_apk
url = https://github.com/appium/unlock_apk.git
[submodule "submodules/SafariLauncher"]
path = submodules/SafariLauncher
url = https://github.com/snevesbarros/SafariLauncher.git
[submodule "submodules/libimobiledevice-macosx"]
path = submodules/libimobiledevice-macosx
url = https://github.com/benvium/libimobiledevice-macosx.git
+141 -3
View File
@@ -1,4 +1,130 @@
CHANGES IN VERSION 1.4
CHANGES IN VERSION 1.5 (from 1.4.?)
======================================
*IN DEVELOPMENT*
- add Tapster support for some more touch methods
CHANGES IN VERSION 1.4.15 (from 1.4.14)
===================================
#### iOS
- fix for safari and webview issues in iOS9+
CHANGES IN VERSION 1.4.14 (from 1.4.13)
===================================
#### iOS
- support for iOS9.2
- fix for webview, resolving context issues in iOS9+
#### Selendroid
- upgrade to Selendroid 0.17.0.
CHANGES IN VERSION 1.4.13 (from 1.4.12)
===================================
#### Chromedriver
- Setting appium-chromedriver version to 2.3.2 as it is stable on Node version 0.10.32
CHANGES IN VERSION 1.4.12 (from 1.4.11)
===================================
#### iOS
- support for iOS9.1
- fix for iOS simulator with similar device names on Xcode 7
- fix to idevicelog (real device) to record logs only for device under test
CHANGES IN VERSION 1.4.11 (from 1.4.10)
===================================
#### iOS
- fix for iOS simulator selector for Xcode 7
- fix for selecting default device for iOS9
CHANGES IN VERSION 1.4.10 (from 1.4.9)
===================================
#### General
- fixed bug in a config file accidentally published in version 1.4.9
CHANGES IN VERSION 1.4.9 (from 1.4.8)
===================================
#### iOS
- support for iOS9 and xcode 7
- at this point instruments-without-delay is not supported for xcode-7
CHANGES IN VERSION 1.4.8 (from 1.4.7)
===================================
#### General
- fix for Windows users! Now the system architecture is properly detected, npm installs should work again
#### Chromedriver / Android hybrid
- fix for an issue in responding to `/status` api endpoint, this is used heavily by Selenium grid
CHANGES IN VERSION 1.4.7 (from 1.4.6)
===================================
#### Chromedriver / Android hybrid
- fix for problem in downstream jsonwp-proxy; now sessionId returned is replaced
with sessionId present in url(original sessionId). Fixed this in appium-jsonwp-proxy 1.2.3
CHANGES IN VERSION 1.4.6 (from 1.4.5)
===================================
#### General
- this is a republish because of a misconfiguration before 1.4.5 got published
resulting in a server startup failure
CHANGES IN VERSION 1.4.5 (from 1.4.4)
===================================
#### General
- fix problem with npm shrinkwrap that caused Appium not to start
CHANGES IN VERSION 1.4.4 (from 1.4.3)
===================================
#### Chromedriver / Android hybrid
- fix a problem in downstream appium-chromedriver; no longer rely on async
methods returning objects of the Promise type. This was causing undefined
errors when running Chromedriver/hybrid tests.
CHANGES IN VERSION 1.4.3 (from 1.4.2)
===================================
#### iOS
- update the appium-instruments dependency with working subdeps
CHANGES IN VERSION 1.4.2 (from 1.4.1)
===================================
#### General
- update ES6-based dependencies which got bit by a breaking Regenerator
change
#### iOS
- keep track of the correct app ID for Safari webviews (fixes problems in
finding the webview for iOS)
#### Android
- fix sendKeys and clear for Samsung devices
CHANGES IN VERSION 1.4.1 (from 1.4)
===================================
#### Chromedriver
- have the appium-chromedriver manage the chromedriver binary download/install
CHANGES IN VERSION 1.4 (from 1.3.7)
======================================
#### General
@@ -7,10 +133,15 @@ CHANGES IN VERSION 1.4
- updated sample code
- updated documentation
- updated test suite
- deprecate node 0.10
#### iOS
- Support for iOS 8.3
- fix for issues relating to finding xcode folder
- support for iOS 8.3
- deprecate iOS 6.1 and iOS 7.0 (support will be removed soon, probably 1.5)
- deprecate Xcodes less than 6.3 (moving to support of latest versions only,
and Xcode 6.3+ together allows automation of all supported iOS versions). The
only exception to this is the combination of Xcode 6.0.1 + iOS 8.0.
- fix for issues relating to finding xcode folder and Info.plist
- new sever flag `--instruments <path>` to specify custom path to instruments commandLine tool
- fix for getOrientation
- fix for iOS crash log retrieval
@@ -24,6 +155,9 @@ CHANGES IN VERSION 1.4
- new capability `disableAndroidWatchers`
- deprecated capability `stopAppOnReset`
- new capability `dontStopAppOnReset`
- fix a crash possibly encountered during extracting app strings
- new server argument `suppress-adb-kill-server`
- fix issue with `keys()`; now it correctly targets currently-focused element
#### Selendroid
- Support for installApp, isAppInstalled and removeApp
@@ -31,10 +165,14 @@ CHANGES IN VERSION 1.4
#### Android+Chrome
- Chormedriver version updated to 2.15
- fix for driver.quit()
- use the correct ADB path already identified by Appium
#### iOS+Safari
- fix for handling real device object
- fix for safariAllowPopups for iOS 8.x
- reduce logging in remote debugger for real devices, and don't use console.log
- fix issues with SafariLauncher on real devices
- fix execute_async so that it now works at all
CHANGES IN VERSION 1.3.7
======================================
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright 2012-2013 Appium Committers
Copyright 2012-2015 Sauce Labs, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -88,7 +88,7 @@ You can run an Appium server using node.js or using the application, see below.
#### Using Node.js
```center
```
$ npm install -g appium
$ appium &
```
+3 -12
View File
@@ -7,18 +7,9 @@ else
branch="$1"
fi
XCODE_PATH=$(xcode-select -print-path)
if test -d $XCODE_PATH/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.1.sdk ||
[ -e /Library/Developer/CoreSimulator/Profiles/Runtimes/iOS\ 7.1.simruntime ]; then
echo "Confirmed iOS7.1 SDK available"
else
echo "You don't have iOS 7.1 SDK available. Switch to Xcode 5?"
exit 1
fi
node --version | grep "v0.10" >/dev/null
node --version | grep "v0.12" >/dev/null
if [ $? -gt 0 ]; then
echo "You need to publish Appium using Node 0.10.x, not $(node --version)"
echo "You need to publish Appium using Node 0.12.x, not $(node --version)"
exit 1;
else
echo "Node version OK"
@@ -50,7 +41,7 @@ set -e
echo "Getting latest from upstream:$branch"
git pull upstream $branch
echo "Resetting"
./reset.sh --hardcore --real-safari --chromedriver-install-all --chromedriver-version 2.15
./reset.sh --hardcore --chromedriver-install-all --chromedriver-version 2.15
version=$(cat package.json | $(npm bin)/underscore extract version | sed 's/\"//g')
echo "Clearing npm cache"
npm cache clear appium
+36 -3
View File
@@ -10,6 +10,9 @@ ios81_only=false
ios82_only=false
ios83_only=false
ios84_only=false
ios9_only=false
ios91_only=false
ios92_only=false
android_only=false
android_chrome=false
selendroid_only=false
@@ -57,6 +60,15 @@ for arg in "$@"; do
elif [ "$arg" = "--ios84" ]; then
ios84_only=true
all_tests=false
elif [ "$arg" = "--ios9" ]; then
ios9_only=true
all_tests=false
elif [ "$arg" = "--ios91" ]; then
ios91_only=true
all_tests=false
elif [ "$arg" = "--ios92" ]; then
ios92_only=true
all_tests=false
elif [ "$arg" = "--real-device" ]; then
real_device=true
elif [ "$arg" =~ " " ]; then
@@ -72,10 +84,19 @@ run_ios_tests() {
echo "RUNNING IOS $1 TESTS"
echo "---------------------"
tag_regex=$3
if $real_device; then
tag_regex="$3|@skip-real-device"
fi
DEVICE=$2 time $appium_mocha -g $3 -i \
test/functional/common \
test/functional/ios
if $real_device; then
DEVICE=$2 REAL_DEVICE=true time $appium_mocha -g $tag_regex -i \
test/functional/ios
else
DEVICE=$2 time $appium_mocha -g $3 -i \
test/functional/common \
test/functional/ios
fi
}
if $ios6_only || $ios_only || $all_tests; then
@@ -110,6 +131,18 @@ if $ios84_only || $all_tests; then
run_ios_tests "8.4" "ios84" "@skip-ios84|@skip-ios82|@skip-ios81|@skip-ios8|@skip-ios-all|@skip-ios7up"
fi
if $ios9_only || $all_tests; then
run_ios_tests "9" "ios9" "@skip-ios9|@skip-ios84|@skip-ios82|@skip-ios81|@skip-ios8|@skip-ios-all|@skip-ios7up"
fi
if $ios91_only || $all_tests; then
run_ios_tests "9.1" "ios91" "@skip-ios91|@skip-ios9|@skip-ios84|@skip-ios82|@skip-ios81|@skip-ios8|@skip-ios-all|@skip-ios7up"
fi
if $ios92_only || $all_tests; then
run_ios_tests "9.2" "ios92" "@skip-ios92|@skip-ios-all|@skip-ios7up"
fi
if $android_only || $all_tests; then
echo "RUNNING ANDROID TESTS"
echo "---------------------"
+5
View File
@@ -0,0 +1,5 @@
plist_path=$1/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/Developer/Library/LaunchDaemons/com.apple.instruments.deviceservice.plist
iwd_path=$2/node_modules/appium-instruments/thirdparty/iwd7
/usr/libexec/PlistBuddy -c "Add :EnvironmentVariables dict" $plist_path
/usr/libexec/PlistBuddy -c "Add :EnvironmentVariables:DYLD_INSERT_LIBRARIES string $iwd_path/DTMobileISShim.dylib" $plist_path
/usr/libexec/PlistBuddy -c "Add :EnvironmentVariables:LIB_PATH string $iwd_path/" $plist_path
+1 -1
View File
@@ -21,7 +21,7 @@ Appium 是一个自动化测试开源工具,支持 iOS 平台和 Android 平
那么 Appium 架构是如何实现这个哲学呢?为了满足第一条,Appium 真正的工作引擎其实是第三方自动化框架。这样,我们就不需在你的应用里植入 Appium 相关或者第三方的代码。这意味着你测试使用的应用与最终发布的应用并无二致。我们使用以下的第三方框架:
* iOS: 苹果的 [UIAutomation](https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/_index.html)
* iOS: 苹果的 [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由单独的项目[Selendroid](http://selendroid.io)提供支持 )
+1 -1
View File
@@ -9,7 +9,7 @@ Appimu的时候支持的平台版本和上述测试方式的必备条件。
* 版本号:6.1,7.0,以及7.1。
* 支持设备:iPhone模拟器,iPad模拟器以及iPhones和iPads真机。
* 是否支持原生应用:支持。同时支持模拟器中调试应用版本和正确签名的真机ipa。其他相关支持由苹果的[UIAutomation](https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/_index.html)框架提供。
* 是否支持原生应用:支持。同时支持模拟器中调试应用版本和正确签名的真机ipa。其他相关支持由苹果的[UIAutomation](https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/)框架提供。
* 是否支持内置移动浏览器:支持。Safari浏览器已经通过测试。对于真机,则需要安装调试工具ios-webkit-remote-debugger。很遗憾,对于Safari的原生界面的自动化是不支持的。更多信息请移步至[mobile web doc](/docs/cn/writing-running-appium/mobile-web.cn.md) 。
* 是否支持混合应用:支持。同样对于真机需要安装调试工具ios-webkit-remote-debugger,更多详情请移步至[hybrid doc](/docs/cn/advanced-concepts/hybrid.cn.md) 查看详情。
* 是否支持在同一个session中执行多个应用的自动化:不支持。
@@ -1,6 +1,6 @@
## iOS 谓词(Predicate)
在查看 *'-ios uiautomation'* 搜索策略时了解 **谓词(Predicate)** 十分必要。 [UIAutomation JavaScript API](https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/_index.html)有下列几种非常有用的方法:
在查看 *'-ios uiautomation'* 搜索策略时了解 **谓词(Predicate)** 十分必要。 [UIAutomation JavaScript API](https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/)有下列几种非常有用的方法:
```center
(UIAElement) UIAElementArray.firstWithPredicate(PredicateString predicateString)
@@ -243,4 +243,4 @@ predicate = "name contains[c] '#{text}' || label contains[c] '#{text}' || value
element = execute_script(%Q(au.mainApp().getFirstWithPredicate("#{predicate}");))
puts element.name # Buttons, Various uses of UIButton
```
```
+1 -1
View File
@@ -31,7 +31,7 @@ meet requirement #1 by using vendor-provided automation frameworks under the
hood. That way, we don't need to compile in any Appium-specific or
third-party code or frameworks to your app. This means **you're testing the same app you're shipping**. The vendor-provided frameworks we use are:
* iOS: Apple's [UIAutomation](https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/_index.html)
* 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))
+16
View File
@@ -0,0 +1,16 @@
## Setting up instruments without delay (iwd) for xcode 7 and iOS >= 9.0
For iOS >= 9.0 instruments without delay (iwd) does not work by passing binaries through
the command line (appium does this under the hood for xcode < 7). See [iwd](https://github.com/lawrencelomax/instruments-without-delay/tree/xcode7-quirks#xcode-7--ios-9-support)
For enabling iwd for xcode >= 7, from your appium repository run:
```
sh ./bin/xcode_iwd.sh <path to xcode> <path to appium>
```
eg. `sh ./bin/xcode_iwd.sh /Applications/Xcode.app /Users/xyz/appium/`
Note:
- Make sure your appium version >= 1.4.11 and node_modules are updated
- iwd with xcode7 will only work for iOS >= 9.0, you can switch to older xcode for
iOS < 9.0
+1 -1
View File
@@ -13,7 +13,7 @@ instructions.
* Devices: iPhone Simulator, iPad Simulator, and real iPhones and iPads
* Native app support: Yes, with debug version of .app (simulator),
or correctly-signed .ipa (real devices). Underlying support is provided by
Apple's [UIAutomation](https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/_index.html)
Apple's [UIAutomation](https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/)
framework.
* Mobile web support: Yes, via automation of mobile Safari. For real devices,
ios-webkit-remote-debugger is required, and automation of native aspects of
+8
View File
@@ -67,6 +67,14 @@ own items in the SDK manager. Make sure you install the build-tools and platform
a new version of Xcode, as well.
* 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
UIAutomation (and therefore Appium) by default. You need to set the
accessibility status to 'enabled' on them. The way to do this in code is:
```center
[myCustomView setAccessibilityEnabled:YES];
```
* Tests on iOS may exhibit symptoms similar to a memory leak including sluggish
performance or hangs. If you experience this problem, it's likely due to a
known issue with NSLog. One option is to remove NSLog from your code.
+1 -1
View File
@@ -2,7 +2,7 @@
### These are the projects that inspired us, or are depended upon to make Appium awesome.
* [Apple UIAutomation](http://developer.apple.com/library/ios/#documentation/DeveloperTools/Reference/UIAutomationRef/_index.html)
* [Apple UIAutomation](https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/)
* [GhostDriver](https://github.com/detro/ghostdriver)
* [IOS Auto](https://github.com/penguinho/applecart)
* [IOS Driver](https://github.com/ios-driver/ios-driver)
+2
View File
@@ -56,6 +56,7 @@
|`noSign`| Skip checking and signing of app with debug keys, will work only with UiAutomator and not with selendroid, default `false`| `true` or `false`|
|`ignoreUnimportantViews`| Calls the `setCompressedLayoutHierarchy()` uiautomator function. This capability can speed up test execution, since Accessibility commands will run faster ignoring some elements. The ignored elements will not be findable, which is why this capability has also been implemented as a toggle-able *setting* as well as a capability. Defaults to `false` | `true` or `false`
|`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 chrome driver. For more information see [chromeOptions](https://sites.google.com/a/chromium.org/chromedriver/capabilities) | `chromeOptions: {args: ['--disable-popup-blocking']}` |
### iOS Only
@@ -85,3 +86,4 @@
|`sendKeyStrategy`| strategy to use to type test into a test field. Simulator default: `oneByOne`. Real device default: `grouped` |`oneByOne`, `grouped` or `setValue`|
|`screenshotWaitTimeout`| Max timeout in sec to wait for a screenshot to be generated. default: 10 |e.g., `5`|
|`waitForAppScript`| The ios automation script used to determined if the app has been launched, by default the system wait for the page source not to be empty. The result must be a boolean |e.g. `true;`, `target.elements().length > 0;`, `$.delay(5000); true;` |
|`webviewConnectRetries`| Number of times to send connection message to remote debugger, to get webview. Default: `8` |e.g., `12`|
@@ -1,7 +1,7 @@
## iOS Predicate
It is worth looking at *'-ios uiautomation'* search strategy with **Predicates**.
[UIAutomation JavaScript API](https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/_index.html) has following methods which can are very useful.
[UIAutomation JavaScript API](https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/) has following methods which can are very useful.
```center
(UIAElement) UIAElementArray.firstWithPredicate(PredicateString predicateString)
@@ -1,8 +1,8 @@
# Appium server arguments
## Appium server arguments
Usage: `node . [flags]`
## Server flags
### Server flags
All flags are optional, but some are required in conjunction with certain others.
@@ -86,3 +86,4 @@ All flags are optional, but some are required in conjunction with certain others
|`--dont-stop-app-on-reset`|false|(Android-only) When included, refrains from stopping the app before restart||
|`--debug-log-spacing`|false|Add exaggerated spacing in logs to help with visual inspection||
|`--suppress-adb-kill-server`|false|(Android-only) If set, prevents Appium from killing the adb server instance||
|`--webkit-debug-proxy-port`|false|(IOS-only) Local port used for communication with ios-webkit-debug-proxy|`--webkit-debug-proxy-port 27753`|
+1 -1
View File
@@ -27,7 +27,7 @@ Appium 是一个自动化测试开源工具,支持 iOS 平台和 Android 平
这样,我们就不需在你的应用里植入 Appium 特定或者第三方的代码。这就意味着你在测试你将发布的应用。我们
使用以下的第三方框架:
* iOS: 苹果的 [UIAutomation](https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/_index.html)
* iOS: 苹果的 [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))
+1 -1
View File
@@ -1,6 +1,6 @@
# iOS Predicate
I'd like to share my experience in using *'-ios uiautomation'* search strategy with **Predicates**. After using Instruments/UIAutomation tool for a long time I paid attention on the following methods in [UIAutomation JavaScript API](https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/_index.html):
I'd like to share my experience in using *'-ios uiautomation'* search strategy with **Predicates**. After using Instruments/UIAutomation tool for a long time I paid attention on the following methods in [UIAutomation JavaScript API](https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/):
```java
(UIAElement) UIAElementArray.firstWithPredicate(PredicateString predicateString)
+1 -1
View File
@@ -11,7 +11,7 @@ Appimu时候支持的平台版本和上述测试方式的必备条件。
* 版本号:6.1 , 7.0 , and 7.1
* 支持设备: iPhone 模拟器, iPad 模拟器以及iPhones 和 iPads真机
* 是否支持原生应用:支持 。同时支持模拟器中调试应用版本和正确签名的真机ipa。
其他相关支持由苹果的[UIAutomation](https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/_index.html)
其他相关支持由苹果的[UIAutomation](https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/)
框架提供。
* 是否支持内嵌浏览器:支持, Safari浏览器已经通过测试。对于
真机,则需要安装调试工具 ios-webkit-remote-debugger,很遗憾
+36 -39
View File
@@ -355,7 +355,7 @@ module.exports.signApp = function (appName, certName, cb) {
};
module.exports.buildSafariLauncherApp = function (cb, sdk, xcconfig) {
var appRoot = path.resolve(__dirname, "submodules", "SafariLauncher");
var appRoot = path.resolve(require.resolve('safari-launcher'), '..');
module.exports.build(appRoot, function (err) {
if (err !== null) {
console.log(err);
@@ -512,65 +512,62 @@ module.exports.fixSelendroidAndroidManifest = fixSelendroidAndroidManifest;
module.exports.buildSelendroidServer = function (cb) {
getSelendroidVersion(function (err, version) {
if (err) return cb(err);
console.log("Installing Cordova");
var cordovaDir = path.resolve(__dirname, "submodules", "selendroid",
"third-party", "cordova-3.7.0");
var cmd = "mvn install:install-file -Dfile=classes.jar -DgroupId=org.apache.cordova " +
"-DartifactId=cordova -Dversion=3.7.0 -Dclassifier=android -Dpackaging=jar";
exec(cmd, {cwd: cordovaDir}, function (err, stdout, stderr) {
if (err) {
console.error("Unable to install Cordova. Stdout was: ");
console.error(stdout);
console.error(stderr);
return cb(err);
}
});
console.log("Building selendroid server");
console.log("Installing third-party software for Selendroid");
var buildDir = path.resolve(__dirname, "submodules", "selendroid");
var target = path.resolve(buildDir, "selendroid-server", "target",
"selendroid-server-" + version + ".apk");
var destDir = path.resolve(__dirname, "build", "selendroid");
var destBin = path.resolve(destDir, "selendroid.apk");
var srcManifest = path.resolve(__dirname, "submodules", "selendroid",
"selendroid-server", "AndroidManifest.xml");
var dstManifest = path.resolve(destDir, "AndroidManifest.xml");
cmd = "mvn clean package -DskipTests=true";
exec(cmd, {cwd: buildDir, maxBuffer: SELENDROID_MAX_BUFFER_SIZE}, function (err, stdout, stderr) {
exec('third-party/mvnInstall.sh', {cwd: buildDir}, function (err, stdout, stderr) {
if (err) {
console.error("Unable to build selendroid server. Stdout was: ");
console.error('Unable to install third-party software. Stdout was: ');
console.error(stdout);
console.error(stderr);
return cb(err);
}
console.log("Making sure target exists");
fs.stat(target, function (err) {
console.log("Building selendroid server");
var target = path.resolve(buildDir, "selendroid-server", "target",
"selendroid-server-" + version + ".apk");
var destDir = path.resolve(__dirname, "build", "selendroid");
var destBin = path.resolve(destDir, "selendroid.apk");
var srcManifest = path.resolve(__dirname, "submodules", "selendroid",
"selendroid-server", "AndroidManifest.xml");
var dstManifest = path.resolve(destDir, "AndroidManifest.xml");
var cmd = "mvn clean package -DskipTests=true";
exec(cmd, {cwd: buildDir, maxBuffer: SELENDROID_MAX_BUFFER_SIZE}, function (err, stdout, stderr) {
if (err) {
console.error("Selendroid doesn't exist! Not sure what to do.");
console.error("Unable to build selendroid server. Stdout was: ");
console.error(stdout);
console.error(stderr);
return cb(err);
}
console.log("Selendroid server built successfully, copying to build/selendroid");
rimraf(destDir, function (err) {
console.log("Making sure target exists");
fs.stat(target, function (err) {
if (err) {
console.error("Could not remove " + destDir);
console.error("Selendroid doesn't exist! Not sure what to do.");
return cb(err);
}
mkdirp(destDir, function (err) {
console.log("Selendroid server built successfully, copying to build/selendroid");
rimraf(destDir, function (err) {
if (err) {
console.error("Could not create " + destDir);
console.error("Could not remove " + destDir);
return cb(err);
}
ncp(target, destBin, function (err) {
mkdirp(destDir, function (err) {
if (err) {
console.error("Could not copy " + target + " to " + destBin);
console.error("Could not create " + destDir);
return cb(err);
}
console.log("Copying selendroid manifest as well");
ncp(srcManifest, dstManifest, function (err) {
ncp(target, destBin, function (err) {
if (err) {
console.error("Could not copy manifest");
console.error("Could not copy " + target + " to " + destBin);
return cb(err);
}
fixSelendroidAndroidManifest(dstManifest, cb);
console.log("Copying selendroid manifest as well");
ncp(srcManifest, dstManifest, function (err) {
if (err) {
console.error("Could not copy manifest");
return cb(err);
}
fixSelendroidAndroidManifest(dstManifest, cb);
});
});
});
});
+16 -2
View File
@@ -819,6 +819,7 @@ androidCommon.extractStringsFromApk = function (makeStrings, language, cb) {
};
androidCommon.extractStrings = function (cb, language) {
logger.debug("Extracting strings for language: " + (language || "default"));
var outputPath = path.resolve(this.args.tmpDir, this.args.appPackage);
var stringsJson = 'strings.json';
if (!fs.existsSync(this.args.app)) {
@@ -835,8 +836,21 @@ androidCommon.extractStrings = function (cb, language) {
this.language = null;
return cb(err);
}
var file = fs.readFileSync(path.join(outputPath, stringsJson), 'utf8');
this.apkStrings = JSON.parse(file);
var file;
try {
logger.debug("Reading strings from converted strings.json");
file = fs.readFileSync(path.join(outputPath, stringsJson)).toString('utf8');
this.apkStrings = JSON.parse(file);
} catch (e) {
var msg = "Could not parse strings from strings.json. Original " +
"error: " + e.message;
logger.debug(msg);
if (file) {
logger.debug("Content started with: " + file.slice(0, 300));
}
return cb(new Error(msg));
}
logger.debug("Setting language to " + (language || "default"));
this.language = language;
cb();
}.bind(this));
+22 -21
View File
@@ -12,8 +12,7 @@ var errors = require('../../server/errors.js')
, async = require('async')
, mkdirp = require('mkdirp')
, path = require('path')
, AdmZip = require("adm-zip")
, helpers = require('../../helpers.js');
, AdmZip = require("adm-zip");
var androidController = {};
@@ -202,15 +201,15 @@ androidController.getPageIndex = function (elementId, cb) {
cb(new NotYetImplementedError(), null);
};
androidController.keys = function (elementId, keys, cb) {
androidController.keys = function (keys, cb) {
var params = {
elementId: elementId,
text: keys
text: keys,
replace: false
};
if (this.args.unicodeKeyboard) {
params.unicodeKeyboard = true;
}
this.proxy(["element:setText", params], cb);
this.proxy(['setText', params], cb);
};
androidController.frame = function (frame, cb) {
@@ -780,23 +779,25 @@ androidController.isAppInstalled = function (appPackage, cb) {
};
androidController.removeApp = function (appPackage, cb) {
var removeCommand = null;
if (this.args.udid) {
removeCommand = 'adb -s ' + this.args.udid + ' uninstall ' + appPackage;
} else {
removeCommand = 'adb uninstall ' + appPackage;
}
deviceCommon.removeApp(removeCommand, this.args.udid, appPackage, cb);
var deviceId = this.adb.curDeviceId;
this.adb.uninstallApk(appPackage, function (err) {
if (err) {
cb(err, 'Unable to un-install [' + appPackage + '] from device with id [' + deviceId + ']. Error [' + err + ']');
} else {
cb(null, 'Successfully un-installed [' + appPackage + '] from device with id [' + deviceId + ']');
}
});
};
androidController.installApp = function (appPath, cb) {
var installationCommand = null;
if (this.args.udid) {
installationCommand = 'adb -s ' + this.args.udid + ' install ' + appPath;
} else {
installationCommand = 'adb install ' + appPath;
}
deviceCommon.installApp(installationCommand, this.args.udid, appPath, cb);
var deviceId = this.adb.curDeviceId;
this.adb.install(appPath, false, function (err) {
if (err) {
cb(err, 'Unable to install [' + appPath + '] to device with id [' + deviceId + ']. Error [' + err + ']');
} else {
cb(null, 'Successfully unzipped and installed [' + appPath + '] to device with id [' + deviceId + ']');
}
});
};
androidController.unpackApp = function (req, cb) {
@@ -1026,7 +1027,7 @@ androidController.performTouch = function (gestures, cb) {
};
androidController.parseTouch = function (gestures, multi, cb) {
if (_.last(gestures).action === 'release') {
if (multi && _.last(gestures).action === 'release') {
gestures.pop();
}
+11 -50
View File
@@ -3,11 +3,6 @@
var logger = require('../../server/logger.js').get('appium')
, _ = require('underscore')
, errors = require('../../server/errors.js')
, path = require('path')
, system = require('appium-support').system
, isWindows = system.isWindows()
, isLinux = system.isLinux()
, exec = require('child_process').exec
, UnknownError = errors.UnknownError
, async = require('async')
, Chromedriver = require('appium-chromedriver')
@@ -173,12 +168,11 @@ androidHybrid.setupNewChromedriver = function (context, cb) {
this.onChromedriverStop(context);
}
}.bind(this));
this.chromedriver.start(caps).nodeify(function (err) {
if (err) return cb(err);
this.chromedriver.start(caps).then(function () {
// save the chromedriver object under the context
this.sessionChromedrivers[context] = this.chromedriver;
cb();
}.bind(this));
}.bind(this), cb);
};
@@ -192,18 +186,16 @@ androidHybrid.setupExistingChromedriver = function (context, cb) {
// check the status by sending a simple window-based command to ChromeDriver
// if there is an error, we want to recreate the ChromeDriver session
this.chromedriver.hasWorkingWebview().nodeify(function (err, works) {
if (err) return cb(err);
this.chromedriver.hasWorkingWebview().then(function (works) {
if (works) return cb();
logger.debug("ChromeDriver is not associated with a window. " +
"Re-initializing the session.");
this.chromedriverRestartingContext = context;
this.chromedriver.restart().nodeify(function (err) {
if (err) return cb(err);
this.chromedriver.restart().then(function () {
this.chromedriverRestartingContext = null;
cb();
}.bind(this));
}.bind(this));
}.bind(this), cb);
}.bind(this), cb);
};
androidHybrid.onChromedriverStop = function (context) {
@@ -242,12 +234,15 @@ androidHybrid.stopChromedriverProxies = function (ocb) {
logger.debug("Stopping chromedriver for context " + context);
// stop listening for the stopped state event
this.sessionChromedrivers[context].removeAllListeners(Chromedriver.EVENT_CHANGED);
this.sessionChromedrivers[context].stop().nodeify(function (err) {
var onStop = function (err) {
if (err) logger.warn("Error stopping Chromedriver: " + err.message);
// chromedriver isn't valid anymore, so remove it from context list
delete this.sessionChromedrivers[context];
cb();
}.bind(this));
}.bind(this);
this.sessionChromedrivers[context].stop().then(function () {
onStop();
}, onStop);
}.bind(this), function (err) {
// if one of these fails, go back to last proxy state and error out
this.restoreProxyState();
@@ -277,38 +272,4 @@ androidHybrid.initAutoWebview = function (cb) {
}
};
// get the correct chromedriver executable path based on our system
// TODO: don't download/build chromedriver in reset.sh, instead let this be
// something that the appium-chromedriver package manages
androidHybrid.initChromedriverPath = function (cb) {
if (this.args.chromedriverExecutable) {
cb();
} else {
var setPath = function (platform, executable) {
this.args.chromedriverExecutable = path.resolve(__dirname, "..", "..",
"..", "build", "chromedriver", platform, executable);
logger.debug("Set chromedriver binary as: " + this.args.chromedriverExecutable);
}.bind(this);
if (isLinux) {
logger.debug("Determining linux architecture");
exec("uname -m", function (err, stdout) {
var executable;
if (err) return cb(err);
if (stdout.trim() === "i686") {
executable = "chromedriver32";
} else {
executable = "chromedriver64";
}
setPath("linux", executable);
cb();
});
} else {
var executable = isWindows ? "chromedriver.exe" : "chromedriver";
var platform = isWindows ? "windows" : "mac";
setPath(platform, executable);
cb();
}
}
};
module.exports = androidHybrid;
+4 -2
View File
@@ -101,7 +101,6 @@ Android.prototype.start = function (cb, onDie) {
this.initAdb.bind(this),
this.packageAndLaunchActivityFromManifest.bind(this),
this.initUiautomator.bind(this),
this.initChromedriverPath.bind(this),
this.prepareDevice.bind(this),
this.checkApiLevel.bind(this),
this.pushStrings.bind(this),
@@ -330,12 +329,15 @@ Android.prototype.pushStrings = function (cb, language) {
if (err) {
if (!fs.existsSync(this.args.app)) {
// apk doesn't exist locally so remove old strings.json
logger.debug("Could not get strings, but it looks like we had an " +
"old strings file anyway, so ignoring");
return this.adb.rimraf(remotePath + '/' + stringsJson, function (err) {
if (err) return cb(new Error("Could not remove old strings"));
cb();
});
} else {
// if we can't get strings, just dump an empty json and continue
logger.warn("Could not get strings, continuing anyway");
var remoteFile = remotePath + '/' + stringsJson;
return this.adb.shell("echo '{}' > " + remoteFile, cb);
}
@@ -573,8 +575,8 @@ Android.prototype.setActualCapabilities = function (cb) {
logger.debug("Device is at release version " + version);
this.capabilities.platformVersion = version;
}
return cb();
}.bind(this));
cb();
};
Android.prototype.resetTimeout = deviceCommon.resetTimeout;
@@ -2,7 +2,10 @@ package io.appium.android.bootstrap.handler;
import com.android.uiautomator.core.UiDevice;
import com.android.uiautomator.core.UiObjectNotFoundException;
import com.android.uiautomator.core.UiSelector;
import io.appium.android.bootstrap.*;
import io.appium.android.bootstrap.exceptions.ElementNotFoundException;
import io.appium.android.bootstrap.handler.Find;
import org.json.JSONException;
import java.util.Hashtable;
@@ -26,51 +29,60 @@ public class SetText extends CommandHandler {
@Override
public AndroidCommandResult execute(final AndroidCommand command)
throws JSONException {
AndroidElement el = null;
if (command.isElementCommand()) {
// Only makes sense on an element
try {
final Hashtable<String, Object> params = command.params();
final AndroidElement el = command.getElement();
boolean replace = Boolean.parseBoolean(params.get("replace").toString());
String text = params.get("text").toString();
boolean pressEnter = false;
if (text.endsWith("\\n")) {
pressEnter = true;
text = text.replace("\\n", "");
Logger.debug("Will press enter after setting text");
}
boolean unicodeKeyboard = false;
if (params.get("unicodeKeyboard") != null) {
unicodeKeyboard = Boolean.parseBoolean(params.get("unicodeKeyboard").toString());
}
String currText = el.getText();
new Clear().execute(command);
if (!el.getText().isEmpty()) {
// clear could have failed, or we could have a hint in the field
// we'll assume it is the latter
Logger.debug("Text not cleared. Assuming remainder is hint text.");
currText = "";
}
if (!replace) {
text = currText + text;
}
final boolean result = el.setText(text, unicodeKeyboard);
if (!result) {
return getErrorResult("el.setText() failed!");
}
if (pressEnter) {
final UiDevice d = UiDevice.getInstance();
d.pressEnter();
}
return getSuccessResult(result);
} catch (final UiObjectNotFoundException e) {
return new AndroidCommandResult(WDStatus.NO_SUCH_ELEMENT,
e.getMessage());
} catch (final Exception e) { // handle NullPointerException
return getErrorResult("Unknown error");
}
Logger.debug("Using element passed in.");
el = command.getElement();
} else {
return getErrorResult("Unable to set text without an element.");
try {
Logger.debug("Using currently-focused element.");
AndroidElementsHash elements = AndroidElementsHash.getInstance();
el = elements.getElement(new UiSelector().focused(true), "");
} catch (ElementNotFoundException e) {
Logger.debug("Error retrieving focused element: " + e);
return getErrorResult("Unable to set text without a focused element.");
}
}
try {
final Hashtable<String, Object> params = command.params();
boolean replace = Boolean.parseBoolean(params.get("replace").toString());
String text = params.get("text").toString();
boolean pressEnter = false;
if (text.endsWith("\\n")) {
pressEnter = true;
text = text.replace("\\n", "");
Logger.debug("Will press enter after setting text");
}
boolean unicodeKeyboard = false;
if (params.get("unicodeKeyboard") != null) {
unicodeKeyboard = Boolean.parseBoolean(params.get("unicodeKeyboard").toString());
}
String currText = el.getText();
new Clear().execute(command);
if (!el.getText().isEmpty()) {
// clear could have failed, or we could have a hint in the field
// we'll assume it is the latter
Logger.debug("Text not cleared. Assuming remainder is hint text.");
currText = "";
}
if (!replace) {
text = currText + text;
}
final boolean result = el.setText(text, unicodeKeyboard);
if (!result) {
return getErrorResult("el.setText() failed!");
}
if (pressEnter) {
final UiDevice d = UiDevice.getInstance();
d.pressEnter();
}
return getSuccessResult(result);
} catch (final UiObjectNotFoundException e) {
return new AndroidCommandResult(WDStatus.NO_SUCH_ELEMENT,
e.getMessage());
} catch (final Exception e) { // handle NullPointerException
return getErrorResult("Unknown error");
}
}
}
@@ -230,6 +230,10 @@ public class UiScrollableParser {
UiSelector arg = (UiSelector) convertedArgs.get(0);
returnedUiObject = true;
uiObject = new UiObject(arg);
if (!scrollable.exists()) {
Logger.debug("Not scrolling because scrollable does not exist: " + scrollable.getSelector());
return;
}
// scrollIntoView must return the object if it's already in view.
// without the exists check, the parser will error because there's no scrollable.
if (uiObject.exists()) {
@@ -49,7 +49,7 @@ public class UiAutomatorBridge {
}
public boolean injectInputEvent(InputEvent event, boolean sync) {
return (Boolean) invoke(method(CLASS_UI_AUTOMATOR_BRIDGE, METHOD_INJECT_INPUT_EVENT, InputEvent.class, Boolean.class),
return (Boolean) invoke(method(CLASS_UI_AUTOMATOR_BRIDGE, METHOD_INJECT_INPUT_EVENT, InputEvent.class, boolean.class),
uiAutomatorBridge, event, sync);
}
+14 -8
View File
@@ -8,7 +8,6 @@ var Android = require('./android.js')
, deviceCommon = require('../common.js')
, jwpSuccess = deviceCommon.jwpSuccess
, async = require('async')
, ADB = require('./adb.js')
, UiAutomator = require('./uiautomator.js');
var NATIVE_WIN = "NATIVE_APP";
@@ -34,6 +33,7 @@ ChromeAndroid.prototype.setChromedriverMode = function () {
// when proxying to chromedriver, we need to make sure the context endpoints
// are trapped by appium for its own purposes
this.avoidProxy = [
['GET', new RegExp('^/wd/hub/status.*')],
['POST', new RegExp('^/wd/hub/session/[^/]+/context')],
['GET', new RegExp('^/wd/hub/session/[^/]+/context')],
['POST', new RegExp('^/wd/hub/session/[^/]+/touch/perform')],
@@ -81,16 +81,13 @@ ChromeAndroid.prototype.startAutomation = function (cb) {
};
ChromeAndroid.prototype.start = function (cb, onDie) {
this.adb = new ADB(this.args);
this.uiautomator = new UiAutomator(this.adb, this.args);
this.uiautomator.setExitHandler(this.onUiautomatorExit.bind(this));
this.onDieCb = onDie;
async.series([
this.initAdb.bind(this),
this.prepareUiAutomator.bind(this),
this.initUiautomator.bind(this),
this.prepareDevice.bind(this),
this.initChromedriverPath.bind(this),
this.prepareChromedriver.bind(this),
this.pushAndUnlock.bind(this),
this.forwardPort.bind(this),
@@ -116,6 +113,12 @@ ChromeAndroid.prototype.prepareChromedriver = function (cb) {
cb();
};
ChromeAndroid.prototype.prepareUiAutomator = function (cb) {
this.uiautomator = new UiAutomator(this.adb, this.args);
this.uiautomator.setExitHandler(this.onUiautomatorExit.bind(this));
cb();
};
ChromeAndroid.prototype.pushAndUnlock = function (cb) {
this.pushUnlock(function (err) {
if (err) return cb(err);
@@ -149,17 +152,20 @@ ChromeAndroid.prototype.createSession = function (cb) {
this.onChromedriverStop(this.onDieCb);
}
}.bind(this));
this.chromedriver.start(caps).nodeify(cb);
this.chromedriver.start(caps).then(function () {
cb(null, this.chromedriver.sessionId());
}.bind(this), cb);
};
ChromeAndroid.prototype.stop = function (cb) {
// stop listening for the stopped state event
this.chromedriver.removeAllListeners(Chromedriver.EVENT_CHANGED);
// now we can handle the stop on our own
this.chromedriver.stop().nodeify(function (err) {
var onStop = function (err) {
if (err) logger.warn("Error stopping Chromedriver: " + err.message);
this.onChromedriverStop(cb);
}.bind(this));
}.bind(this);
this.chromedriver.stop().then(function () { onStop(); }, onStop);
};
ChromeAndroid.prototype.onChromedriverStop = function (cb) {
+24 -3
View File
@@ -70,6 +70,7 @@ Selendroid.prototype.init = function () {
, ['POST', new RegExp('^/wd/hub/session/[^/]+/network_connection')]
, ['POST', new RegExp('^/wd/hub/session/[^/]+/ime')]
, ['GET', new RegExp('^/wd/hub/session/[^/]+/ime')]
, ['POST', new RegExp('^/wd/hub/session/[^/]+/keys')]
];
this.curContext = this.defaultContext();
};
@@ -525,12 +526,11 @@ Selendroid.prototype.defaultWebviewName = function () {
return this.WEBVIEW_WIN + "_0";
};
Selendroid.prototype.setValue = function (elementId, value, cb) {
logger.debug('Setting text on element \'' + elementId + '\': \'' + value + '\'');
var encodeString = function (value, unicode) {
for (var i = 0; i < value.length; i++) {
var c = value.charCodeAt(i);
// if we're using the unicode keyboard, and this is unicode, maybe encode
if (this.args.unicodeKeyboard && (c > 127 || c === 38)) {
if (unicode && (c > 127 || c === 38)) {
// this is not simple ascii, or it is an ampersand (`&`)
if (c >= parseInt("E000", 16) && c <= parseInt("E040", 16)) {
// Selenium uses a Unicode PUA to cover certain special characters
@@ -542,6 +542,12 @@ Selendroid.prototype.setValue = function (elementId, value, cb) {
}
}
}
return value;
};
Selendroid.prototype.setValue = function (elementId, value, cb) {
logger.debug('Setting text on element \'' + elementId + '\': \'' + value + '\'');
value = encodeString(value, this.args.unicodeKeyboard);
var reqUrl = this.proxyHost + ':' + this.proxyPort +
'/wd/hub/session/' + this.proxySessionId +
'/element/' + elementId + '/value';
@@ -554,6 +560,21 @@ Selendroid.prototype.setValue = function (elementId, value, cb) {
});
};
Selendroid.prototype.keys = function (value, cb) {
logger.debug('Setting text: \'' + value + '\'');
value = encodeString(value, this.args.unicodeKeyboard);
var reqUrl = this.proxyHost + ':' + this.proxyPort +
'/wd/hub/session/' + this.proxySessionId +
'/keys';
doRequest(reqUrl, 'POST', { value: [value] }, null, function (err) {
if (err) return cb(err);
cb(null, {
status: status.codes.Success.code,
value: ''
});
});
};
Selendroid.prototype.back = function (cb) {
this.keyevent(4, null, cb);
};
-22
View File
@@ -129,28 +129,6 @@ exports.isAppInstalled = function (isInstalledCommand, cb) {
});
};
exports.removeApp = function (removeCommand, udid, bundleId, cb) {
logger.debug("Removing app using cmd: " + removeCommand);
exec(removeCommand, function (error) {
if (error !== null) {
cb(new Error('Unable to un-install [' + bundleId + '] from device with id [' + udid + ']. Error [' + error + ']'));
} else {
cb(error, 'Successfully un-installed [' + bundleId + '] from device with id [' + udid + ']');
}
});
};
exports.installApp = function (installationCommand, udid, unzippedAppPath, cb) {
logger.debug("Installing app using cmd: " + installationCommand);
exec(installationCommand, { maxBuffer: 524288 }, function (error) {
if (error !== null) {
cb(new Error('Unable to install [' + unzippedAppPath + '] to device with id [' + udid + ']. Error [' + error + ']'));
} else {
cb(error, 'Successfully unzipped and installed [' + unzippedAppPath + '] to device with id [' + udid + ']');
}
});
};
exports.unpackApp = function (req, packageExtension, cb) {
var reqAppPath = req.body.appPath;
if (reqAppPath.toLowerCase().substring(0, 4) === "http") {
+27 -1
View File
@@ -22,8 +22,11 @@ var uuid = require('uuid-js')
, IOSPerfLog = require('./ios-perf-log')
, errors = require('../../server/errors.js')
, isWindows = require('appium-support').system.isWindows
, localIp = require('appium-support').util.localIp
, NotImplementedError = errors.NotImplementedError
, NotYetImplementedError = errors.NotYetImplementedError;
, NotYetImplementedError = errors.NotYetImplementedError
, Parser = require('../../server/parser.js')
, url = require('url');
var iOSController = {};
var FLICK_MS = 3000;
@@ -41,6 +44,13 @@ var logTypesSupported = {
'performance': 'Performance Logs - Debug Timelines on real devices and simulators'
};
var getDefaultCallbackAddress = function () {
var parser = new Parser();
return parser.rawArgs.filter(function (a) {
return a[0][1] === '--address';
})[0][1].defaultValue;
};
iOSController.getStatusExtensions = function () {
var ext = {};
ext.isShuttingDown = this.isShuttingDown; // this is for testing purposes
@@ -1508,6 +1518,7 @@ iOSController.url = function (url, cb) {
// make sure to clear out any leftover web frames
this.curWebFrames = [];
this.processingRemoteCmd = true;
this.setCurrentUrl(url);
this.remote.navToUrl(url, function () {
cb(null, {
status: status.codes.Success.code
@@ -1887,6 +1898,21 @@ iOSController.execute = function (script, args, cb) {
iOSController.executeAsync = function (script, args, responseUrl, cb) {
if (this.isWebContext()) {
if (this.args.udid) {
var defaultHost = getDefaultCallbackAddress();
var urlObject = url.parse(responseUrl);
if (urlObject.hostname === defaultHost) {
logger.debug('Real device safari test and no custom ' +
'callback address set, ' +
'changing callback address to local ip.');
urlObject.hostname = localIp();
urlObject.host = null; // set to null, otherwise hostname is ignored
responseUrl = url.format(urlObject);
} else {
logger.debug('Custom callback address set, leaving as is.');
}
}
logger.debug("Response url for executeAsync is " + responseUrl);
this.convertElementForAtoms(args, function (err, res) {
if (err) {
+27 -4
View File
@@ -8,6 +8,14 @@ var logger = require('../../server/logger.js').get('appium')
var iOSHybrid = {};
iOSHybrid.setCurrentUrl = function (url) {
this._currentUrl = url;
};
iOSHybrid.getCurrentUrl = function () {
return this._currentUrl;
};
iOSHybrid.closeAlertBeforeTest = function (cb) {
this.proxy("au.alertIsPresent()", function (err, res) {
if (!err && res !== null && typeof res !== "undefined" && res.value === true) {
@@ -77,9 +85,11 @@ iOSHybrid.listWebFrames = function (cb, exitCb) {
if (this.remoteAppKey === null) {
return cb(new Error("remote debug app key must be set"));
}
this.remote.selectApp(this.remoteAppKey, function (res) {
this.remote.selectApp(this.remoteAppKey, this.getCurrentUrl(), function (err, perhapsModifiedAppKey, res) {
if (err) return onDone(err);
this.remoteAppKey = perhapsModifiedAppKey;
onDone(null, res);
});
}.bind(this), this.args.webviewConnectRetries);
}
} else {
if (this.args.udid !== null) {
@@ -107,9 +117,11 @@ iOSHybrid.listWebFrames = function (cb, exitCb) {
}
logger.debug("Using remote debugger app key: " + appKey);
this.remoteAppKey = appKey;
this.remote.selectApp(appKey, function (res) {
this.remote.selectApp(appKey, this.getCurrentUrl(), function (err, perhapsModifiedAppKey, res) {
if (err) return onDone(err);
this.remoteAppKey = perhapsModifiedAppKey;
onDone(null, res);
});
}.bind(this), this.args.webviewConnectRetries);
}.bind(this), this.onPageChange.bind(this));
var loopCloseRuns = 0;
var loopClose = function () {
@@ -186,6 +198,17 @@ iOSHybrid.onPageChange = function (pageArray) {
logger.debug("New page listing is same as old, doing nothing");
}
// make sure that the page listing isn't indicating a redirect
if (!_.isUndefined(this.curContext) && !_.isNull(this.curContext)) {
var page = _.find(pageArray, function (p) {
return parseInt(p.id, 10) === parseInt(this.curContext, 10);
}.bind(this));
if (page && page.url !== this.getCurrentUrl()) {
logger.debug('Redirected from \'' + this.getCurrentUrl() + '\' to \'' + page.url + '\'');
this.setCurrentUrl(page.url);
}
}
if (newPage !== null) {
this.selectingNewPage = true;
this.remote.selectPage(parseInt(newPage, 10), function () {
+1 -1
View File
@@ -47,7 +47,7 @@ IosLog.prototype.startCapture = function (cb) {
which("idevicesyslog", function (err) {
if (!err) {
try {
this.proc = spawn("idevicesyslog", {env: spawnEnv});
this.proc = spawn("idevicesyslog", ["-u", this.udid], {env: spawnEnv});
} catch (e) {
cb(e);
}
+102 -60
View File
@@ -30,7 +30,7 @@ var path = require('path')
, UnknownError = errors.UnknownError
, binaryPlist = true
, Args = require("vargs").Constructor
;
, logCustomDeprecationWarning = require('../../helpers.js').logCustomDeprecationWarning;
// XML Plist library helper
var parseXmlPlistFile = function (plistFilename, cb) {
@@ -155,6 +155,13 @@ IOS.prototype.configure = function (args, caps, cb) {
return cb(new Error(msg));
}
if (parseFloat(caps.platformVersion) < 7.1) {
logCustomDeprecationWarning('iOS version', caps.platformVersion,
'iOS ' + caps.platformVersion + ' support has ' +
'been deprecated and will be removed in a ' +
'future version of Appium.');
}
return this.configureApp(cb);
};
@@ -321,48 +328,30 @@ IOS.prototype.startInstruments = function (cb) {
});
}.bind(this);
if (this.shouldIgnoreInstrumentsExit()) {
// for safari on a real device, instruments is going to crash, but we
// expect that and ignore it
logger.debug("Not starting command proxy since we're expecting that " +
"Instruments won't be able to talk to it anyway");
this.instruments.start(
function (err) {
if (err) return treatError(err, cb);
// instruments will never check into the command proxy, so go ahead
// and just call back here once instruments is started, rather than
// waiting for it to make the first connection to the command proxy
cb();
}
, function () {
// ignore unexpected instruments exit since that's exactly what
// we're expecting anyway
});
} else {
logger.debug("Starting command proxy.");
this.commandProxy.start(
function (err) { // on first connection
// first let instruments know so that it does not restart itself
this.instruments.launchHandler(err);
// then we call the callback
cb(err);
}.bind(this)
//
, function (err) { // regular cb
if (err) return treatError(err, cb);
logger.debug("Starting instruments");
this.instruments.start(
function (err) {
if (err) return treatError(err, cb);
// we don't call cb here, waiting for first connection or error
}.bind(this)
, function (code) {
logger.debug("Starting command proxy.");
this.commandProxy.start(
function onFirstConnection(err) {
// first let instruments know so that it does not restart itself
this.instruments.launchHandler(err);
// then we call the callback
cb(err);
}.bind(this)
, function regularCallback(err) {
if (err) return treatError(err, cb);
logger.debug("Starting instruments");
this.instruments.start(
function (err) {
if (err) return treatError(err, cb);
// we don't call cb here, waiting for first connection or error
}.bind(this)
, function (code) {
if (!this.shouldIgnoreInstrumentsExit()) {
this.onUnexpectedInstrumentsExit(code);
}.bind(this)
);
}.bind(this)
);
}
}
}.bind(this)
);
}.bind(this)
);
};
IOS.prototype.makeInstruments = function (cb) {
@@ -372,7 +361,7 @@ IOS.prototype.makeInstruments = function (cb) {
var bootstrap = prepareBootstrap({
sock: this.sock,
interKeyDelay: this.args.interKeyDelay,
justLoopInfinitely: this.shouldIgnoreInstrumentsExit(),
justLoopInfinitely: false,
autoAcceptAlerts: !(!this.args.autoAcceptAlerts || this.args.autoAcceptAlerts === 'false'),
autoDismissAlerts: !(!this.args.autoDismissAlerts || this.args.autoDismissAlerts === 'false'),
sendKeyStrategy: this.args.sendKeyStrategy || (this.args.udid ? 'grouped' : 'oneByOne')
@@ -543,8 +532,22 @@ IOS.prototype.setXcodeVersion = function (cb) {
xcode.getVersion(function (err, versionNumber) {
if (err) {
logger.error("Could not determine Xcode version:" + err.message);
} else {
var minorVersion = parseFloat(versionNumber.slice(0, 3));
var pv = parseFloat(this.args.platformVersion);
// we deprecate Xcodes < 6.3, except for iOS 8.0 in which case we
// support Xcode 6.0 as well
if (minorVersion < 6.3 && (!(minorVersion === 6.0 && pv === 8.0))) {
logCustomDeprecationWarning('Xcode version', versionNumber,
'Support for Xcode ' + versionNumber + ' ' +
'has been deprecated and will be removed ' +
'in a future version. Please upgrade ' +
'to version 6.3 or higher (or version ' +
'6.0.1 for iOS 8.0)');
}
}
this.xcodeVersion = versionNumber;
logger.debug("Xcode version set to " + versionNumber);
cb();
}.bind(this));
};
@@ -707,9 +710,9 @@ IOS.prototype.instantLaunchAndQuit = function (needSafariDirs, cb) {
var condition = function () {
var simDirsExist = this.sim.dirsExist();
var locServicesExist = settings.locServicesDirsExist(this.sim);
var safariDirsExist = this.iOSSDKVersion < 7.0 ||
var safariDirsExist = this.args.platformVersion < 7.0 ||
(this.sim.safariDirsExist() &&
(this.iOSSDKVersion < 8.0 ||
(this.args.platformVersion < 8.0 ||
this.sim.userSettingsPlistExists())
);
var okToGo = simDirsExist && locServicesExist &&
@@ -808,7 +811,7 @@ IOS.prototype.detectUdid = function (cb) {
}
if (stdout && stdout.length > 2) {
this.args.udid = stdout.replace("\n", "");
this.args.udid = stdout.split("\n")[0];
logger.debug("Detected udid as " + this.args.udid);
cb();
} else {
@@ -938,6 +941,8 @@ IOS.getDeviceStringFromOpts = function (opts) {
deviceName: opts.deviceName,
platformVersion: opts.platformVersion
}));
var xcodeMajorVer = parseInt(opts.xcodeVersion.substr(0,
opts.xcodeVersion.indexOf('.')), 10);
var isiPhone = opts.forceIphone || opts.forceIpad === null || (opts.forceIpad !== null && !opts.forceIpad);
var isTall = isiPhone;
var isRetina = opts.xcodeVersion[0] !== '4';
@@ -963,13 +968,13 @@ IOS.getDeviceStringFromOpts = function (opts) {
}
var iosDeviceString = isiPhone ? "iPhone" : "iPad";
if (opts.xcodeVersion[0] === '4') {
if (xcodeMajorVer === 4) {
if (isiPhone && isRetina) {
iosDeviceString += isTall ? " (Retina 4-inch)" : " (Retina 3.5-inch)";
} else {
iosDeviceString += isRetina ? " (Retina)" : "";
}
} else if (opts.xcodeVersion[0] === '5') {
} else if (xcodeMajorVer === 5) {
iosDeviceString += isRetina ? " Retina" : "";
if (isiPhone) {
if (isRetina && isTall) {
@@ -980,12 +985,14 @@ IOS.getDeviceStringFromOpts = function (opts) {
} else {
iosDeviceString += is64bit ? " (64-bit)" : "";
}
} else if (opts.xcodeVersion[0] === '6') {
} else if (_.contains([6, 7], xcodeMajorVer)) {
iosDeviceString = opts.deviceName ||
(isiPhone ? "iPhone Simulator" : "iPad Simulator");
}
var reqVersion = opts.platformVersion || opts.iOSSDKVersion;
if (opts.iOSSDKVersion >= 8) {
if (opts.iOSSDKVersion >= 8 && xcodeMajorVer === 7) {
iosDeviceString += " (" + reqVersion + ")";
} else if (opts.iOSSDKVersion >= 8) {
iosDeviceString += " (" + reqVersion + " Simulator)";
} else if (opts.iOSSDKVersion >= 7.1) {
iosDeviceString += " - Simulator - iOS " + reqVersion;
@@ -1010,9 +1017,33 @@ IOS.getDeviceStringFromOpts = function (opts) {
'iPhone Simulator (8.0 Simulator)': 'iPhone 6 (8.0 Simulator)',
'iPhone Simulator (7.1 Simulator)': 'iPhone 5s (7.1 Simulator)'
};
if (CONFIG_FIX[iosDeviceString]) {
// For xcode major version 7
var CONFIG_FIX__XCODE_7 = {
'iPad Simulator (8.1)': 'iPad 2 (8.1)',
'iPad Simulator (8.2)': 'iPad 2 (8.2)',
'iPad Simulator (8.3)': 'iPad 2 (8.3)',
'iPad Simulator (8.4)': 'iPad 2 (8.4)',
'iPhone Simulator (8.1)': 'iPhone 6 (8.1)',
'iPhone Simulator (8.2)': 'iPhone 6 (8.2)',
'iPhone Simulator (8.3)': 'iPhone 6 (8.3)',
'iPhone Simulator (8.4)': 'iPhone 6 (8.4)',
'iPad Simulator (9.0)': 'iPad 2 (9.0)',
'iPad Simulator (9.1)': 'iPad 2 (9.1)',
'iPad Simulator (9.2)': 'iPad 2 (9.2)',
// Fixing ambiguous device name by adding '[' at the end so intruments
// correctly starts iPhone 6 [udid] and not the iPhone 6 (9.0) + Apple Watch
// for ios9.0 and above; see #5619
'iPhone Simulator (9.0)': 'iPhone 6 (9.0) [',
'iPhone Simulator (9.1)': 'iPhone 6 (9.1) [',
'iPhone Simulator (9.2)': 'iPhone 6 (9.2) [',
'iPhone 6 (9.0)': 'iPhone 6 (9.0) [',
'iPhone 6 (9.1)': 'iPhone 6 (9.1) [',
'iPhone 6 (9.2)': 'iPhone 6 (9.2) ['
};
var configFix = xcodeMajorVer === 7 ? CONFIG_FIX__XCODE_7 : CONFIG_FIX;
if (configFix[iosDeviceString]) {
var oldDeviceString = iosDeviceString;
iosDeviceString = CONFIG_FIX[iosDeviceString];
iosDeviceString = configFix[iosDeviceString];
logger.debug("Fixing device. Changed from: \"" + oldDeviceString +
"\" to: \"" + iosDeviceString + "\"");
}
@@ -1230,7 +1261,7 @@ IOS.prototype.prelaunchSimulator = function (cb) {
waitForSimulatorLogs(10);
}.bind(this));
}).bind(this);
}.bind(this));
};
IOS.prototype.parseLocalizableStrings = function (/* language, stringFile, cb */) {
@@ -1554,12 +1585,22 @@ IOS.prototype.getLatestWebviewContextForTitle = function (titleRegex, cb) {
if (err) return cb(err);
var matchingCtx;
_(contexts).each(function (ctx) {
if (ctx.view && ctx.view.url !== "about:blank" && (ctx.view.title || "").match(titleRegex)) {
matchingCtx = ctx;
if (ctx.view && (ctx.view.title || "").match(titleRegex)) {
if (ctx.view.url === "about:blank") {
// in the cases of Xcode < 5 (i.e., iOS SDK Version less than 7)
// iOS 7.1, iOS 9.0 & iOS 9.1 in a webview (not in Safari)
// we can have the url be `about:blank`
if (parseFloat(this.iOSSDKVersion) < 7 || parseFloat(this.iOSSDKVersion) >= 9 ||
(this.args.platformVersion === '7.1' && this.args.app && this.args.app.toLowerCase() !== 'safari')) {
matchingCtx = ctx;
}
} else {
matchingCtx = ctx;
}
}
});
}.bind(this));
cb(null, matchingCtx ? matchingCtx.id : undefined);
});
}.bind(this));
};
// Right now we don't necessarily wait for webview
@@ -1589,6 +1630,7 @@ IOS.prototype.navToInitialWebview = function (cb) {
IOS.prototype.typeAndNavToUrl = function (cb) {
var initialUrl = this.args.safariInitialUrl || 'http://127.0.0.1:' + this.args.port + '/welcome';
this.setCurrentUrl(initialUrl);
var oldImpWait = this.implicitWaitMs;
this.implicitWaitMs = 7000;
function noArgsCb(cb) { return function (err) { cb(err); }; }
@@ -1598,12 +1640,12 @@ IOS.prototype.typeAndNavToUrl = function (cb) {
this.implicitWaitMs = oldImpWait;
this.nativeTap(res.value.ELEMENT, noArgsCb(cb));
}.bind(this),
this.findElements.bind(this, 'name', 'Address'),
this.findElements.bind(this, 'class name', 'UIATextField'),
function (res, cb) {
var addressEl = res.value[res.value.length -1].ELEMENT;
var addressEl = res.value[0].ELEMENT;
this.setValueImmediate(addressEl, initialUrl, noArgsCb(cb));
}.bind(this),
this.findElement.bind(this, 'name', 'go'),
this.findElement.bind(this, 'name', 'Go'),
function (res, cb) {
this.nativeTap(res.value.ELEMENT, noArgsCb(cb));
}.bind(this)
+32 -11
View File
@@ -18,6 +18,7 @@ var net = require('net')
// CONFIG
// ====================================
var CONNECTION_RETRIES = 16;
var RemoteDebugger = function (onDisconnect) {
this.init(2, onDisconnect);
@@ -131,12 +132,13 @@ RemoteDebugger.prototype.setConnectionKey = function (cb) {
});
};
RemoteDebugger.prototype.selectApp = function (appIdKey, cb, tries) {
RemoteDebugger.prototype.selectApp = function (appIdKey, currentUrl, cb, maxTries, tries) {
if (_.isUndefined(maxTries)) maxTries = CONNECTION_RETRIES;
if (_.isUndefined(tries)) tries = 1;
try { assert.ok(this.connId); } catch (err) { return cb(err); }
this.appIdKey = appIdKey;
var connectToApp = messages.connectToApp(this.connId, this.appIdKey);
this.logger.debug("Selecting app " + this.appIdKey + " (try #" + tries + ")");
this.logger.debug("Selecting app " + this.appIdKey + " (try #" + tries + " of " + maxTries + ")");
var responded = false;
var onConnect = function (appIdKey, pageDict) {
@@ -146,29 +148,49 @@ RemoteDebugger.prototype.selectApp = function (appIdKey, cb, tries) {
// in iOS 8.2 the connect logic happens, but with an empty dictionary
// which leads to the remote debugger getting disconnected, and into a loop
if (_.isEmpty(pageDict)) {
var msg = "Empty page dictionary received. Trying again.";
return onRequiresRetry(this.appIdKey, msg);
return onRequiresRetry(this.appIdKey, "Empty page dictionary received. Trying again.");
}
if (currentUrl) {
var found = false;
_.each(pageDict, function (record) {
if (record.WIRURLKey === currentUrl) {
// this is the page we want
found = true;
}
});
if (!found) {
return onRequiresRetry(this.appIdKey, "Received app, but url is not correct. Trying again.");
}
}
if (responded) return;
if (this.appIdKey !== appIdKey) {
this.logger.debug('Received altered app id, updating from \'' + this.appIdKey + '\' to \'' + appIdKey + '\'');
this.appIdKey = appIdKey;
}
this.logger.debug('Connected to app ' + appIdKey + ' [' + JSON.stringify(this.pageArrayFromDict(pageDict)) + ']');
responded = true;
cb(this.pageArrayFromDict(pageDict));
cb(null, appIdKey, this.pageArrayFromDict(pageDict));
this.specialCbs['_rpc_forwardGetListing:'] = this.onPageChange.bind(this);
}.bind(this);
var onRequiresRetry = function (correctAppIdKey, msg) {
if (responded) return;
responded = true;
if (tries < 4) {
if (tries < maxTries) {
if (!msg) {
msg = "We were notified we connected to possibly the wrong " +
"app. Using the id key suggested and trying again";
}
this.logger.info(msg);
this.selectApp(correctAppIdKey, cb, tries + 1);
this.selectApp(correctAppIdKey, currentUrl, cb, maxTries, tries + 1);
} else {
msg = "Could not connect to a valid app after " + tries + " tries.";
this.logger.error(msg);
cb(new Error(msg));
this.logger.debug(msg);
this.logger.debug("Could not connect to a valid app after " + tries + " tries.");
cb();
}
}.bind(this);
@@ -592,7 +614,6 @@ RemoteDebugger.prototype.send = function (data, cb, cb2) {
if (data.__selector === '_rpc_reportIdentifier:') {
this.specialCbs.connect = cb2;
} else if (data.__selector === '_rpc_forwardGetListing:') {
this.logger.debug(cb2);
this.specialCbs.connectedToBadApp = cb2;
}
} else if (data.__argument && data.__argument.WIRSocketDataKey) {
+46 -15
View File
@@ -5,8 +5,7 @@ var IOS = require('./ios.js')
, path = require('path')
, _ = require('underscore');
var NATIVE_REAL_SAFARI_MIN_VER = 7.0
, SAFARI_BUNDLE = 'com.apple.mobilesafari';
var SAFARI_BUNDLE = 'com.apple.mobilesafari';
var Safari = function () {
this.init();
@@ -58,6 +57,38 @@ Safari.prototype.moveBuiltInApp = function (cb) {
}
};
// once safariLauncher is running and instrumented, this function
// finds the button which launches safari and clicks it.
Safari.prototype._clickButtonToLaunchSafari = function (cb) {
this.findElement('accessibility id', 'launch safari',
function (err, res) {
if (err || res.status !== 0) {
var msg = "Error. Could not find button to launch Safari. " +
"Make sure you are using the latest version of " +
"SafariLauncher that appium is using";
logger.error(msg);
return cb(new Error(msg));
}
var buttonId = res.value.ELEMENT;
this.click(buttonId, function (err, res) {
if (err || res.status !== 0) {
var msg = "Error. For whatever reason, could not click " +
"the 'launch safari' button of SafariLauncher";
logger.error(msg);
return cb(new Error(msg));
}
logger.debug("Clicked button, safari should be launching.");
// we're done with instruments at this point, we go on to Safari
this.navToInitialWebview(cb);
}.bind(this));
}.bind(this));
};
Safari.prototype._start = IOS.prototype.start;
Safari.prototype.start = function (cb, onDie) {
var newOnDie = function (err) {
@@ -69,13 +100,23 @@ Safari.prototype.start = function (cb, onDie) {
this._start(function (err) {
if (err) return cb(err);
this.navToInitialWebview(cb);
if (this.args.udid) {
logger.debug("On Safari Launcher. Tapping button to launch Safari");
this._clickButtonToLaunchSafari(function (err) {
if (err) return cb(err);
this.navToInitialWebview(cb);
}.bind(this));
} else {
this.navToInitialWebview(cb);
}
}.bind(this), newOnDie);
};
Safari.prototype.shouldIgnoreInstrumentsExit = function () {
return !!this.args.udid &&
this.iOSSDKVersion < NATIVE_REAL_SAFARI_MIN_VER;
return !!this.args.udid;
};
Safari.prototype._click = IOS.prototype.click;
@@ -103,16 +144,6 @@ Safari.prototype.setInitialOrientation = function (cb) {
this._setInitialOrientation(cb);
};
Safari.prototype._configureBootstrap = IOS.prototype.configureBootstrap;
Safari.prototype.configureBootstrap = function (cb) {
if (this.shouldIgnoreInstrumentsExit()) {
logger.debug("Not setting bootstrap config because we're on " +
"SafariLauncher");
return cb();
}
this._configureBootstrap(cb);
};
Safari.prototype.installToRealDevice = function (cb) {
if (this.args.udid) {
try {
+19 -41
View File
@@ -181,47 +181,25 @@ settings.updateLocationSettings = function (sim, bundleId, authorized) {
});
var curSettings = settings.getSettings(sim, 'locationClients');
_.each(curSettings, function (settingSet, file) {
/*
* Support location services for 8.x platform.
*/
if (parseFloat(sim.platformVer) >= 8) {
logger.debug("Using 8.X format locationd plist format.");
_.extend(newPrefs, {
Authorization: 2,
SupportedAuthorizationMask: 3,
});
// now add our app's data
if (!_.has(settingSet, bundleId)) {
settingSet[bundleId] = {};
}
_.extend(settingSet[bundleId], newPrefs);
if (!_.has(settingSet[bundleId], 'Executable')) {
settingSet[bundleId].Executable = "";
}
if (!_.has(settingSet[bundleId], 'Registered')) {
settingSet[bundleId].Registered = "";
}
} else {
// add this random data to the clients.plist since it always seems to be there
if (!_.has(settingSet, weirdLocKey)) {
settingSet[weirdLocKey] = {
BundlePath: "/System/Library/PrivateFrameworks/AOSNotification.framework",
Whitelisted: false,
Executable: "",
Registered: ""
};
}
// now add our app's data
if (!_.has(settingSet, bundleId)) {
settingSet[bundleId] = {};
}
_.extend(settingSet[bundleId], newPrefs);
if (!_.has(settingSet[bundleId], 'Executable')) {
settingSet[bundleId].Executable = "";
}
if (!_.has(settingSet[bundleId], 'Registered')) {
settingSet[bundleId].Registered = "";
}
// add this random data to the clients.plist since it always seems to be there
if (!_.has(settingSet, weirdLocKey)) {
settingSet[weirdLocKey] = {
BundlePath: "/System/Library/PrivateFrameworks/AOSNotification.framework",
Whitelisted: false,
Executable: "",
Registered: ""
};
}
// now add our app's data
if (!_.has(settingSet, bundleId)) {
settingSet[bundleId] = {};
}
_.extend(settingSet[bundleId], newPrefs);
if (!_.has(settingSet, 'Executable')) {
settingSet.Executable = "";
}
if (!_.has(settingSet, 'Registered')) {
settingSet.Registered = "";
}
prefSetPerFile[file] = settingSet;
});
+2 -1
View File
@@ -450,6 +450,7 @@ Simulator.prototype.cleanSim = function (keepKeychains, tempDir, cb) {
};
Simulator.prototype.moveBuiltInApp = function (appPath, appName, newAppDir, cb) {
safeRimRafSync(newAppDir);
ncp(appPath, newAppDir, function (err) {
if (err) return cb(err);
logger.debug("Copied " + appName + " to " + newAppDir);
@@ -496,7 +497,7 @@ Simulator.prototype.prepareBuiltInApp = function (appName, tmpDir, cb) {
fs.stat(newAppDir, function (err, s) {
if (err) {
logger.warn("App is also not at " + newAppDir);
return cb(new Error("Couldn't find built in app in its home " +
return cb(new Error("Couldn't find built in app " + appName + " in its home " +
"or temp dir!"));
}
if (checkApp(s, appPath, cb)) {
+7 -3
View File
@@ -44,10 +44,14 @@ WebKitRemoteDebugger.prototype.connect = function (pageId, cb, pageChangeCb) {
}
}.bind(this));
this.socket.on('error', function (exception) {
console.log('Debugger web socket error %s', exception);
});
this.logger.debug('Debugger web socket error: ' + exception.message);
}.bind(this));
this.socket.on('message', function (data) {
console.log('Debugger web socket received data : %s', data);
var truncated = data || '';
if (truncated.length > 300) {
truncated = truncated.slice(0, 300) + '...';
}
this.logger.debug('Debugger web socket received data: ' + truncated);
this.receive(data);
}.bind(this));
};
+7 -3
View File
@@ -3,6 +3,7 @@ var path = require('path')
, fs = require('fs')
, env = process.env
, exec = require('child_process').exec
, _s = require('underscore.string')
, async = require('async');
require("./common.js");
@@ -27,15 +28,18 @@ IOSChecker.prototype.runAllChecks = function (cb) {
IOSChecker.prototype.getMacOSXVersion = function (cb) {
exec("sw_vers -productVersion", function (err, stdout) {
if (err === null) {
if (stdout.match('10.8') !== null) {
if (_s.startsWith(stdout, '10.8')) {
this.osVersion = '10.8';
cb(null, "Mac OS X 10.8 is installed.");
} else if (stdout.match('10.9') !== null) {
} else if (_s.startsWith(stdout, '10.9')) {
this.osVersion = '10.9';
cb(null, "Mac OS X 10.9 is installed.");
} else if ((stdout.match('10.10') !== null) || (stdout.match('10.10.1') !== null)) {
} else if (_s.startsWith(stdout, '10.10')) {
this.osVersion = '10.10';
cb(null, "Mac OS X 10.10 is installed.");
} else if (_s.startsWith(stdout, '10.11')) {
this.osVersion = '10.11';
cb(null, "Mac OS X 10.11 is installed.");
} else {
this.log.fail("Could not detect Mac OS X Version", cb);
}
+9 -8
View File
@@ -34,15 +34,16 @@ var nodeifyFn = function (fn, bindObj) {
// if we've imported es6 Promise type functions, ensure we can still use
// Q/B-like "nodeify" on them in our transitional code
if (typeof Promise === "function") {
Promise.prototype.nodeify = function (cb) {
this.then(function (res) {
cb(null, res);
}, function (err) {
cb(err);
});
};
if (typeof Promise !== "function") {
require('es6-promise').polyfill();
}
Promise.prototype.nodeify = function (cb) {
this.then(function (res) {
cb(null, res);
}, function (err) {
cb(err);
});
};
module.exports = {
simctl: nodeify(simctl),
+1
View File
@@ -92,6 +92,7 @@ var iosCaps = [
, 'loggingPrefs'
, 'sendKeyStrategy'
, 'waitForAppScript'
, 'webviewConnectRetries'
];
var Capabilities = function (capabilities) {
+12
View File
@@ -72,6 +72,12 @@ var http = require('http')
var main = function (args, readyCb, doneCb) {
var nodeVer = Number(process.version.match(/^v(\d+\.\d+)/)[1]);
if (nodeVer < 0.10) {
logger.error("Appium requires Node >= 0.10");
process.exit(1);
}
if (args.asyncTrace) {
require('longjohn').async_trace_limit = -1;
}
@@ -85,6 +91,12 @@ var main = function (args, readyCb, doneCb) {
process.exit(0);
}
if (nodeVer < 0.12) {
logger.warn("Appium support for versions of node < 0.12 has been " +
"deprecated and will be removed in a future version. Please " +
"upgrade!");
}
checkArgs(parser, args);
if (typeof doneCb === "undefined") {
doneCb = function () {};
+1 -1
View File
@@ -61,7 +61,7 @@ var args = [
, dest: 'callbackAddress'
, defaultValue: null
, example: "127.0.0.1"
, help: 'callback IP Address (default: same as address)'
, help: 'callback IP Address (default: same as --address)'
}],
[['-cp', '--callback-port'], {
+3902
View File
File diff suppressed because it is too large Load Diff
+11 -8
View File
@@ -11,7 +11,7 @@
"firefoxos",
"testing"
],
"version": "1.4.0-beta",
"version": "1.4.15",
"author": "https://github.com/appium",
"licenses": [
{
@@ -43,11 +43,11 @@
"adm-zip": "~0.4.7",
"appium-adb": "=1.7.5",
"appium-atoms": "=0.0.5",
"appium-chromedriver": "^1.0.1",
"appium-instruments": "=2.0.0",
"appium-support": "=1.0.4",
"appium-uiauto": "=1.10.8",
"appium-xcode": "=2.0.0",
"appium-chromedriver": "=2.3.2",
"appium-instruments": "^2.0.6",
"appium-support": "=1.1.2",
"appium-uiauto": "^1.10.10",
"appium-xcode": "^2.0.5",
"argparse": "~1.0.1",
"async": "~0.9.0",
"binary-cookies": "~0.1.1",
@@ -60,6 +60,7 @@
"colors": "~1.0.3",
"date-utils": "~1.2.16",
"difflib": "~0.2.4",
"es6-promise": "^2.1.1",
"express": "~4.11.2",
"glob": "~4.4.0",
"grunt": "~0.4.5",
@@ -74,7 +75,7 @@
"namp": "0.2.25",
"ncp": "~2.0.0",
"node-idevice": "~0.1.5",
"node-simctl": "=2.0.0",
"node-simctl": "^2.0.1",
"node-uuid": "~1.4.2",
"npmlog": "~1.1.0",
"path": "~0.11.14",
@@ -83,9 +84,11 @@
"q": "~1.1.2",
"request": "~2.53.0",
"rimraf": "~2.2.8",
"safari-launcher": "=2.0.5",
"sandboxed-module": "~2.0.0",
"serve-favicon": "~2.2.0",
"socket.io": "~1.3.4",
"stack-trace": "0.0.9",
"swig": "~1.4.2",
"temp": "~0.8.1",
"through": "~2.3.6",
@@ -131,6 +134,7 @@
"promisepipe": "^1.0.1",
"rewire": "^2.3.1",
"run-sequence": "^1.0.2",
"sample-apps": "~2.0.2",
"saucelabs": "~0.1.1",
"sinon": "~1.12.2",
"sinon-chai": "~2.7.0",
@@ -138,7 +142,6 @@
"socks": "~0.0.1",
"spawn-mocha-parallel": "~1.3.1",
"split": "^0.3.3",
"stack-trace": "0.0.9",
"uncolor": "~0.3.0",
"underscore-cli": "~0.2.18",
"unorm": "~1.3.3",
+112
View File
@@ -0,0 +1,112 @@
{
"packages": {
"appium-core": [
"appium"
],
"appium-contrib": [
"android-apidemos",
"appium-adb",
"appium-android-bootstrap",
"appium-android-driver",
"appium-android-ime",
"appium-atoms",
"appium-base-driver",
"appium-chromedriver",
"appium-cookies",
"appium-doctor",
"appium-express",
"appium-fake-driver",
"appium-jsonwp-proxy",
"appium-logger",
"appium-instruments",
"appium-ios-driver",
"appium-ios-log",
"appium-ios-simulator",
"appium-remote-debugger",
"appium-selendroid-driver",
"appium-selendroid-installer",
"appium-support",
"appium-uiauto",
"appium-uiautomator",
"appium-unlock",
"appium-xcode",
"authorize-ios",
"io.appium.settings",
"jsonwp-status",
"mobile-json-wire-protocol",
"node-simctl"
],
"appium-tools": [
"appium-ci",
"appium-ci-standalone-tools",
"appium-gulp-plugins",
"appium-package-master",
"appium-transpile-runtime",
"appium-jenkins",
"teen_process",
"packweb",
"turtledeps"
],
"appium-apps": [
"gps-demo-app",
"io.appium.gappium.sampleapp",
"ios-test-app",
"ios-uicatalog",
"ios-webview-app",
"toggle-test-app",
"sample-apps"
],
"extra-jlipps": [
"asyncbox",
"mochawait"
],
"extra-wd": [
"wd"
]
},
"owners": {
"appium-core": [
"imurchie",
"jlipps",
"jonahss",
"moizjv",
"scottdixon",
"sebv"
],
"appium-contrib": [
"imurchie",
"jlipps",
"jonahss",
"moizjv",
"scottdixon",
"sebv",
"bootstraponline"
],
"appium-tools": [
"imurchie",
"jlipps",
"jonahss",
"moizjv",
"scottdixon",
"sebv"
],
"appium-apps": [
"imurchie",
"jlipps",
"jonahss",
"moizjv",
"sebv",
"scottdixon",
"bootstraponline"
],
"extra-jlipps": [
"jlipps"
],
"extra-wd": [
"admc",
"jlipps",
"sebv",
"sourishkrout"
]
}
}
+11 -67
View File
@@ -206,23 +206,20 @@ reset_ios() {
run_cmd "$grunt" buildApp:WebViewApp:iphonesimulator$sdk_ver
fi
if $should_reset_realsafari; then
echo "* Cloning/updating SafariLauncher"
run_cmd git submodule update --init submodules/SafariLauncher
echo "* Building SafariLauncher for real devices"
run_cmd rm -rf build/SafariLauncher
run_cmd mkdir -p build/SafariLauncher
run_cmd rm -f submodules/SafariLauncher/target.xcconfig
touch submodules/SafariLauncher/target.xcconfig
echo "BUNDLE_ID = com.bytearc.SafariLauncher" >> submodules/SafariLauncher/target.xcconfig
touch build/SafariLauncher/target.xcconfig
echo "BUNDLE_ID = com.bytearc.SafariLauncher" >> build/SafariLauncher/target.xcconfig
if [[ ! -z $code_sign_identity ]]; then
echo "IDENTITY_NAME = " $code_sign_identity >> submodules/SafariLauncher/target.xcconfig
echo "IDENTITY_NAME = " $code_sign_identity >> build/SafariLauncher/target.xcconfig
else
echo "IDENTITY_NAME = iPhone Developer" >> submodules/SafariLauncher/target.xcconfig
echo "IDENTITY_NAME = iPhone Developer" >> build/SafariLauncher/target.xcconfig
fi
echo "IDENTITY_CODE = " $provisioning_profile >> submodules/SafariLauncher/target.xcconfig
run_cmd "$grunt" buildSafariLauncherApp:iphoneos:"target.xcconfig"
echo "IDENTITY_CODE = " $provisioning_profile >> build/SafariLauncher/target.xcconfig
run_cmd "$grunt" buildSafariLauncherApp:iphoneos:"$appium_home/build/SafariLauncher/target.xcconfig"
echo "* Copying SafariLauncher for real devices to build"
run_cmd zip -r build/SafariLauncher/SafariLauncher submodules/SafariLauncher/build/Release-iphoneos/SafariLauncher.app
run_cmd zip -r build/SafariLauncher/SafariLauncher node_modules/safari-launcher/build/Release-iphoneos/SafariLauncher.app
fi
echo "* Cloning/updating libimobiledevice-macosx"
run_cmd git submodule update --init submodules/libimobiledevice-macosx
@@ -481,64 +478,11 @@ reset_gappium() {
reset_chromedriver() {
echo "RESETTING CHROMEDRIVER"
machine=$(run_cmd_output uname -m)
if [ "$machine" == "i686" ]; then
machine="32"
else
machine="64"
if $chromedriver_install_all ; then
run_cmd pushd node_modules/appium-chromedriver/
run_cmd npm run-script chromedriver_all
run_cmd popd
fi
if [ -d "$appium_home"/build/chromedriver ]; then
echo "* Clearing old ChromeDriver(s)"
run_cmd rm -rf "$appium_home"/build/chromedriver/*
else
run_cmd rm -rf "$appium_home"/build/chromedriver # could have been an old binary
run_cmd mkdir "$appium_home"/build/chromedriver
fi
if [ "$chromedriver_version" == false ]; then
echo "* Finding latest version"
chromedriver_version=$(run_cmd_output curl -L http://chromedriver.storage.googleapis.com/LATEST_RELEASE)
fi
if ! $chromedriver_install_all ; then
if [ "$platform" == "mac" ]; then
chromedriver_file="chromedriver_mac32.zip"
run_cmd mkdir "$appium_home"/build/chromedriver/mac
install_chromedriver $platform $chromedriver_version $chromedriver_file
else
chromedriver_file="chromedriver_linux$machine.zip"
binary="chromedriver$machine"
run_cmd mkdir "$appium_home"/build/chromedriver/linux
install_chromedriver $platform $chromedriver_version $chromedriver_file $binary
fi
else
echo "* Building directory structure"
run_cmd mkdir "$appium_home"/build/chromedriver/mac
run_cmd mkdir "$appium_home"/build/chromedriver/linux
run_cmd mkdir "$appium_home"/build/chromedriver/windows
install_chromedriver "mac" $chromedriver_version "chromedriver_mac32.zip"
install_chromedriver "linux" $chromedriver_version "chromedriver_linux32.zip" "chromedriver32"
install_chromedriver "linux" $chromedriver_version "chromedriver_linux64.zip" "chromedriver64"
install_chromedriver "windows" $chromedriver_version "chromedriver_win32.zip"
fi
}
install_chromedriver() {
platform=$1
version=$2
file=$3
binary=$4
echo "* Downloading ChromeDriver version $version for $platform"
run_cmd curl -L http://chromedriver.storage.googleapis.com/$version/$file -o "$appium_home"/build/chromedriver/$platform/chromedriver.zip
run_cmd pushd "$appium_home"/build/chromedriver/$platform
echo "* Unzipping ChromeDriver"
run_cmd unzip chromedriver.zip
run_cmd rm chromedriver.zip
if [[ $binary != "" ]]; then
run_cmd mv chromedriver $binary
fi
run_cmd popd
}
reset_firefoxos() {
@@ -57,3 +57,38 @@ describe("apidemo - touch - press", function () {
});
});
});
describe("apidemo - touch - longpress - release", function () {
var driver;
setup(this, _.defaults({
appActivity: '.view.SecureView'
}, desired)).then(function (d) { driver= d;});
describe('press released after pause should call release', function () {
it('should open an alert dialog', function (done) {
driver
.elementByName("Don't click! It'll cost you!")
.then(function (el) {
var action = new TouchAction(driver);
return action.press({el: el}).wait(1000).release().perform();
})
.elementByName("Oops...").should.eventually.exist
.then(function (el) {
el.click();
})
.nodeify(done);
});
});
describe('longPress should call release', function () {
it('should open an alert dialog', function (done) {
driver
.elementByName("Don't click! It'll cost you!")
.then(function (el) {
var action = new TouchAction(driver);
return action.longPress({el: el}).release().perform();
})
.elementByName("Oh no!").should.eventually.exist
.nodeify(done);
});
});
});
+159 -175
View File
@@ -3,13 +3,11 @@
var env = require('../../helpers/env')
, setup = require("./setup-base")
, _ = require('underscore')
, getAppPath = require('../../helpers/app').getAppPath
, status = require('../../../lib/server/status');
, getAppPath = require('../../helpers/app').getAppPath;
var desired = {
app: getAppPath('ApiDemos'),
appActivity: '.view.TextFields',
newCommandTimeout: 90
};
if (env.SELENDROID) {
@@ -19,215 +17,201 @@ if (env.SELENDROID) {
module.exports = function () {
var driver;
var runTextEditTest = function (testText, done) {
var runTextEditTest = function (testText, keys, done) {
var el;
driver
.waitForElementByClassName('android.widget.EditText')
.then(function (_el) { el = _el; })
.waitForElementsByClassName('android.widget.EditText')
// use a text field with no hint text, so clear is faster
.then(function (els) {
el = _.last(els);
return el;
})
.clear()
.then(function () {
if (env.SELENDROID) {
return el.clear();
if (keys) {
return driver.keys(testText);
} else {
return el.sendKeys(testText);
}
})
.then(function () { return el.sendKeys(testText); })
.then(function () { return el.text().should.become(testText); })
.then(function () {
if (env.SELENDROID) {
// in Selendroid mode we sometimes get the text before
// it is fully sent to the element
return driver.sleep(300);
}
})
.then(function () { return el.text(); })
.then(function (text) {
// For samsung samsung S5 text is appended with ". Editing."
text = text.replace(". Editing.", "");
return text.should.be.equal(testText);
})
.nodeify(done);
};
var runEditAndClearTest = function (testText, done) {
var runEditAndClearTest = function (testText, keys, done) {
var el;
driver
.waitForElementByClassName('android.widget.EditText')
.then(function (_el) { el = _el; })
.waitForElementsByClassName('android.widget.EditText')
.then(function (els) {
el = _.last(els);
return el;
})
.clear()
.then(function () {
if (env.SELENDROID) {
return el.clear();
if (keys) {
return driver.keys(testText);
} else {
return el.sendKeys(testText);
}
})
.then(function () { return el.sendKeys(testText).text().should.become(testText); })
.then(function () {
el.text().should.become(testText);
})
.then(function () {
return el.clear().should.not.be.rejected;
})
.then(function () {
// Selendroid and uiautomator have different ways of dealing with
// hint text. In particular, Selendroid does not return it
// and uiautomator does.
var expectedText = "hint text";
if (env.SELENDROID) {
expectedText = "";
}
return el.text().should.become(expectedText);
return el.text();
})
.then(function (text) {
// For samsung samsung S5 text is appended with ". Editing."
text = text.replace("Editing.", "");
text.should.be.equal("");
})
.nodeify(done);
};
describe('editing ascii text field', function () {
setup(this, desired).then(function (d) { driver = d; });
var runKeyboardTests = function (testText) {
return function () {
it('should work with sendKeys', function (done) {
runTextEditTest(testText, false, done);
});
it('should work with keys', function (done) {
runTextEditTest(testText, true, done);
});
};
};
it('should be able to edit a text field', function (done) {
var testText = "Life, the Universe and Everything.";
runTextEditTest(testText, done);
var runKeyEventTests = function () {
var editTextField = 'android.widget.TextView';
if (env.SELENDROID) {
// with Selendroid we can't find classes by their parent class
// and with uiautomator we can't find the subclass.
editTextField = 'io.appium.android.apis.text.LogTextBox';
}
// skip selendroid because selendroid implements keyevent with an adb
// call, and we are unable to send metastate that way
it('should be able to send combination keyevents @skip-selendroid-all', function (done) {
driver
.elementById('clear').click()
.pressDeviceKey(29, 193)
.elementsByClassName(editTextField)
.then(function (els) {
return _.last(els).text();
})
.then(function (txt) {
txt.should.include('keyCode=KEYCODE_A');
txt.should.include('metaState=META_SHIFT_ON');
})
.nodeify(done);
});
it('should be able to edit and manually clear a text field', function (done) {
var testText = "The answer is 42.";
runEditAndClearTest(testText, done);
it('should be able to send keyevents', function (done) {
driver
.elementById('clear').click()
.pressDeviceKey(82)
.elementsByClassName(editTextField)
.then(function (els) {
return _.last(els).text();
})
.then(function (txt) {
txt.should.include('[keycode=82]');
txt.should.include('keyCode=KEYCODE_MENU');
})
.nodeify(done);
});
};
it('should be able to send &-', function (done) {
var testText = '&-';
runTextEditTest(testText, done);
var runManualClearTests = function () {
var testText = "The answer is 42.";
it('should work with sendKeys', function (done) {
runEditAndClearTest(testText, false, done);
});
it('should be able to send & and - in other text', function (done) {
var testText = 'In the mid-1990s he ate fish & chips as mayor-elect.';
runTextEditTest(testText, done);
it('should work with keys', function (done) {
runEditAndClearTest(testText, true, done);
});
};
it('should be able to send - in text', function (done) {
var testText = 'Super-test.';
runTextEditTest(testText, done);
});
describe('pressing device key', function () {
it('should be able to send keyevents', function (done) {
driver
.waitForElementByClassName('android.widget.EditText')
.elementById('com.android.launcher:id/search_button')
.should.be.rejectedWith(status.codes.NoSuchElement.code)
.pressDeviceKey(3)
.waitForElementById('com.android.launcher:id/search_button')
.elementByClassName('android.widget.EditText')
.should.be.rejectedWith(status.codes.NoSuchElement.code)
.nodeify(done);
describe('editing a text field', function () {
var appActivity = '.view.TextFields';
var tests = [
{ label: 'editing a text field', text: 'Life, the Universe and Everything.' },
{ label: 'sending &-', text: '&-' },
{ label: 'sending & and - in other text', text: 'In the mid-1990s he ate fish & chips as mayor-elect.' },
{ label: 'sending - in text', text: 'Super-test.' },
];
describe('ascii', function () {
setup(this, _.defaults({
appActivity: appActivity
}, desired)).then(function (d) { driver = d; });
_.each(tests, function (test) {
describe(test.label, runKeyboardTests(test.text));
});
// skip selendroid because selendroid implements keyevent with an adb
// call, and we are unable to send metastate that way
it('should be able to send combination keyevents @skip-selendroid-all', function (done) {
driver
.waitForElementByClassName('android.widget.EditText')
.clear()
.pressDeviceKey(29, 193)
.elementByClassName('android.widget.EditText')
.text().should.become('A')
.nodeify(done);
describe('editing and manually clearing a text field', runManualClearTests);
});
describe('unicode', function () {
setup(this, _.defaults({
appActivity: appActivity,
unicodeKeyboard: true,
resetKeyboard: true
}, desired)).then(function (d) { driver = d; });
var unicodeTests = _.union(tests, [
{ label: 'should be able to send - in unicode text', text: 'परीक्षा-परीक्षण' },
{ label: 'should be able to send & in text', text: 'Fish & chips' },
{ label: 'should be able to send & in unicode text', text: 'Mīna & chips' },
{ label: 'should be able to send roman characters with diacritics', text: 'Áé Œ ù ḍ' },
{ label: 'should be able to send a u with an umlaut', text: 'ü' },
{ label: 'should be able to send Tamil', text: 'சோதனை' },
{ label: 'should be able to send Gujarati', text: 'પરીક્ષણ' },
{ label: 'should be able to send Chinese', text: '测试' },
{ label: 'should be able to send Russian', text: 'тестирование' },
// skip rtl languages, which don't clear correctly atm
// { label: 'should be able to send Arabic', 'تجريب'],
// { label: 'should be able to send Hebrew', 'בדיקות'],
]);
_.each(unicodeTests, function (test) {
describe(test.label, runKeyboardTests(test.text));
});
describe('editing and manually clearing a text field', runManualClearTests);
});
});
describe('editing unicode text field', function () {
setup(this, _.defaults({
unicodeKeyboard: true,
resetKeyboard: true
}, desired)).then(function (d) { driver = d; });
it('should be able to edit a text field', function (done) {
var testText = "Life, the Universe and Everything.";
runTextEditTest(testText, done);
describe('key events', function () {
var appActivity = '.text.KeyEventText';
describe('ascii', function () {
setup(this, _.defaults({
appActivity: appActivity
}, desired)).then(function (d) { driver = d; });
describe('pressing device key', runKeyEventTests);
});
it('should be able to edit and manually clear a text field', function (done) {
var testText = "The answer is 42.";
runEditAndClearTest(testText, done);
});
it('should be able to send &-', function (done) {
var testText = '&-';
runTextEditTest(testText, done);
});
it('should be able to send & and - in other text', function (done) {
var testText = 'In the mid-1990s he ate fish & chips as mayor-elect.';
runTextEditTest(testText, done);
});
it('should be able to send - in text', function (done) {
var testText = 'Super-test.';
runTextEditTest(testText, done);
});
it('should be able to send - in unicode text', function (done) {
var testText = 'परीक्षा-परीक्षण';
runTextEditTest(testText, done);
});
it('should be able to send & in text', function (done) {
var testText = 'Fish & chips';
runTextEditTest(testText, done);
});
it('should be able to send & in unicode text', function (done) {
var testText = 'Mīna & chips';
runTextEditTest(testText, done);
});
it('should be able to send roman characters with diacritics', function (done) {
var testText = 'Áé Œ ù ḍ';
runTextEditTest(testText, done);
});
it('should be able to send a u with an umlaut', function (done) {
var testText = 'ü';
runTextEditTest(testText, done);
});
// skipping because clear doesn't work reliably on RTL scripts
it.skip('should be able to send Arabic', function (done) {
var testText = 'تجريب';
runTextEditTest(testText, done);
});
// skipping because clear doesn't work reliably on RTL scripts
it.skip('should be able to send Hebrew', function (done) {
var testText = 'בדיקות';
runTextEditTest(testText, done);
});
it('should be able to send Tamil', function (done) {
var testText = 'சோதனை';
runTextEditTest(testText, done);
});
it('should be able to send Gujarati', function (done) {
var testText = 'પરીક્ષણ';
runTextEditTest(testText, done);
});
it('should be able to send Chinese', function (done) {
var testText = '测试';
runTextEditTest(testText, done);
});
it('should be able to send Russian', function (done) {
var testText = 'тестирование';
runTextEditTest(testText, done);
});
describe('pressing device key with unicode keyboard', function () {
it('should be able to send keyevents', function (done) {
driver
.waitForElementByClassName('android.widget.EditText')
.elementById('com.android.launcher:id/search_button')
.should.be.rejectedWith(status.codes.NoSuchElement.code)
.pressDeviceKey(3)
.waitForElementById('com.android.launcher:id/search_button')
.elementByClassName('android.widget.EditText')
.should.be.rejectedWith(status.codes.NoSuchElement.code)
.nodeify(done);
});
// skip selendroid because selendroid implements keyevent with an adb
// call, and we are unable to send metastate that way
it('should be able to send combination keyevents @skip-selendroid-all', function (done) {
driver
.waitForElementByClassName('android.widget.EditText')
.clear()
.pressDeviceKey(29, 193)
.elementByClassName('android.widget.EditText')
.text().should.become('A')
.nodeify(done);
});
describe('unicode', function () {
setup(this, _.defaults({
appActivity: appActivity,
unicodeKeyboard: true,
resetKeyboard: true
}, desired)).then(function (d) { driver = d; });
describe('pressing device key', runKeyEventTests);
});
});
};
+1 -2
View File
@@ -17,7 +17,7 @@ describe('crash recovery', function () {
.elementByAccessibilityId("Crash")
.click()
.then(function () {
return driver.sleep(10000);
return driver.sleep(30000);
})
.source() // will 404 because the session is gone
.should.eventually.be.rejectedWith('6')
@@ -48,4 +48,3 @@ describe('crash commands', function () {
.nodeify(done);
});
});
+5 -2
View File
@@ -98,9 +98,12 @@ describe('file movements - pullFile and pushFile', function () {
});
};
if (parseFloat(sdk) >= 8) {
getSimUdid('6', sdk, env.CAPS, function (err, udid) {
xcode.getVersion(function (err, xcodeVersion) {
if (err) return done(err);
next(udid);
getSimUdid(xcodeVersion, sdk, env.CAPS, function (err, udid) {
if (err) return done(err);
next(udid);
});
});
} else {
next();
@@ -16,13 +16,13 @@ describe("prefs @skip-ios6", function () {
var driver;
setup(this, desired).then(function (d) { driver = d; });
it('should turn off autocomplete', function (done) {
var ios7up = env.IOS7 || env.IOS8;
it('should turn off autocorrection', function (done) {
var ios7up = env.IOS7 || env.IOS8 || env.IOS9;
var switchEl;
driver
.elementsByClassName("UIATableCell").at(ios7up ? 0 : 1).click()
.sleep(1000)
.elementsByClassName("UIATableCell").at(ios7up ? (env.IOS8 ? 2 : 3) : 1)
.elementsByClassName("UIATableCell").at(ios7up ? (env.IOS8 || env.IOS9 ? 2 : 3) : 1)
.click()
.elementByXPath('//UIASwitch[@name="Auto-Correction"]')
.then(function (el) { switchEl = el; return el; })
@@ -62,9 +62,12 @@ var ios7up = function (version, udid, setting, expected, cb) {
exports.ios7up = function (desired, setting, expected, cb) {
xcode.getMaxIOSSDK(function (err, sdk) {
if (parseFloat(sdk) >= 8) {
getSimUdid('6', sdk, desired, function (err, udid) {
xcode.getVersion(function (err, xcodeVersion) {
if (err) return cb(err);
ios7up(desired.platformVersion, udid, setting, expected, cb);
getSimUdid(xcodeVersion, sdk, desired, function (err, udid) {
if (err) return cb(err);
ios7up(desired.platformVersion, udid, setting, expected, cb);
});
});
} else {
ios7up(desired.platformVersion, null, setting, expected, cb);
+2 -2
View File
@@ -3,8 +3,8 @@
var env = require('../../../helpers/env'),
setup = require("../../common/setup-base");
describe('safari - basics @skip-ios6', function () {
if (env.IOS8 && !env.IOS80) {
describe('safari - basics @skip-ios6 @skip-real-device', function () {
if ((env.IOS8 || env.IOS9) && !env.IOS80) {
describe('default init' ,function () {
var driver;
setup(this, {browserName: 'safari'}).then(function (d) { driver = d; });
+25 -16
View File
@@ -49,7 +49,7 @@ describe('testapp - device', function () {
});
});
describe('invalid deviceName @skip-ios6', function () {
describe('invalid deviceName @skip-ios6 @skip-real-device', function () {
var newDesired = _.extend(_.clone(desired), {deviceName: "iFailure 3.5-inch"});
var session = initSession(newDesired, {'no-retry': true});
@@ -60,23 +60,32 @@ describe('testapp - device', function () {
});
});
_.each(['iPhone', 'iPad'], function (device) {
describe('generic ' + device + ' deviceName @skip-ios6', function () {
var newDesired = _.extend(_.clone(desired), {
deviceName: device + " Simulator"
});
var session = initSession(newDesired, {'no-retry': true});
after(function (done) {
session.tearDown(this.currentTest.state === 'passed').nodeify(done);
});
it('should work with a generic ' + device + ' deviceName', function (done) {
session.setUp()
.nodeify(done);
});
describe('generic deviceName @skip-ios6', function () {
this.timeout(60000);
var session;
afterEach(function (done) {
session.tearDown(this.currentTest.state === 'passed').nodeify(done);
});
it('should work with a generic iPhone deviceName', function (done) {
var newDesired = _.extend(_.clone(desired), {
deviceName: "iPhone Simulator"
});
session = initSession(newDesired, {'no-retry': true});
session.setUp()
.nodeify(done);
});
it('should work with a generic iPad deviceName', function (done) {
var newDesired = _.extend(_.clone(desired), {
deviceName: "iPad Simulator"
});
session = initSession(newDesired, {'no-retry': true});
session.setUp()
.nodeify(done);
});
});
describe("real device", function () {
@@ -7,7 +7,7 @@ var env = require('../../../../helpers/env'),
rimraf = require('rimraf'),
path = require('path');
describe('localization - language @skip-ios8', function () {
describe('localization - language', function () {
this.timeout(env.MOCHA_INIT_TIMEOUT);
after(function () {
@@ -21,9 +21,10 @@ describe('localization - language @skip-ios8', function () {
var driver;
setup(this, _.defaults({language: 'fr'}, desired)).then(function (d) { driver = d; });
it('should be fr', function (done) {
it('should contain fr', function (done) {
// iOS 9+ returns 'fr-US', earlier iOS versions return 'fr'
driver.execute('$.mainApp().preferencesValueForKey("AppleLanguages")[0];')
.should.become('fr')
.should.eventually.contain('fr')
.nodeify(done);
});
});
@@ -32,9 +33,10 @@ describe('localization - language @skip-ios8', function () {
var driver;
setup(this, _.defaults({language: 'de'}, desired)).then(function (d) { driver = d; });
it('should be de', function (done) {
it('should contain de', function (done) {
// iOS 9+ returns 'de-US', earlier iOS versions return 'de'
driver.execute('$.mainApp().preferencesValueForKey("AppleLanguages")[0];')
.should.become('de')
.should.eventually.contain('de')
.nodeify(done);
});
});
@@ -101,7 +101,7 @@ describe('testapp - location - 5 @skip-ci', function () {
bundleId: 'io.appium.TestApp',
app: 'assets/TestApp7.1.app.zip'
});
if (env.IOS8) {
if (env.IOS8 || env.IOS9) {
newDesired.app = 'assets/TestApp8.app.zip';
}
+7 -2
View File
@@ -131,8 +131,13 @@ describe('testapp - swipe actions', function () {
rightPos.x = x + re.width + 5;
leftPos.y = rightPos.y = centerPos.y = y + (re.height * 0.5);
})
.elementByAccessibilityId("Access'ibility")
.then(function (el) { destEl = el;})
.elementByAccessibilityId("location alert")
.then(function (el) {
// Our tests below expect the slider to be taken to 100%, so
// here we're getting the 'location alert' element because it's
// on the right hand side of the app, which we'll drag to.
destEl = el;
})
.then(getSliderValue)
.then(testSliderValueNot0or100)
.nodeify(done);
@@ -26,8 +26,8 @@ describe('uicatalog - find - basics @skip-ios6', function () {
});
it('should find a single element using elementByAccessibilityId', function (done) {
var axId = env.IOS8 ? 'AAPLImageViewController' :
'Image View, AAPLImageViewController';
var axId = (env.IOS8 || env.IOS9) ? 'AAPLImageViewController' :
'Image View, AAPLImageViewController';
driver
.elementByAccessibilityId(axId)
.then(function (el) {
@@ -56,7 +56,7 @@ describe('uicatalog - find - basics @skip-ios6', function () {
});
it('should find some elements within itself', function (done) {
var elLength = env.IOS8 ? 2 : 1;
var elLength = (env.IOS8 || env.IOS9) ? 2 : 1;
driver
.elementByXPath("//UIATableCell[contains(@name, 'Buttons')]")
.then(function (el) {
@@ -78,7 +78,7 @@ describe('uicatalog - find - basics @skip-ios6', function () {
describe('no mix up', function () {
after(function (done) {
if (!env.IOS81 && !env.IOS82 && !env.IOS83 && !env.IOS84) {
if (!env.IOS81 && !env.IOS82 && !env.IOS83 && !env.IOS84 && !env.IOS9 && !env.IOS91 && !env.IOS92) {
driver
.clickButton('UICatalog')
.nodeify(done);
@@ -129,7 +129,7 @@ describe('uicatalog - find - basics @skip-ios6', function () {
describe('findElementsByClassName textfield case', function () {
after(function (done) {
if (!env.IOS81 && !env.IOS82 && !env.IOS83 && !env.IOS84) {
if (!env.IOS81 && !env.IOS82 && !env.IOS83 && !env.IOS84 && !env.IOS9 && !env.IOS91 && !env.IOS92) {
driver
.clickButton('UICatalog')
.nodeify(done);
@@ -137,7 +137,7 @@ describe('uicatalog - find - basics @skip-ios6', function () {
done();
}
});
var axIdExt = env.IOS8 ? '' : ', AAPLActionSheetViewController';
var axIdExt = (env.IOS8 || env.IOS9) ? '' : ', AAPLActionSheetViewController';
it('should find only one textfield', function (done) {
driver
.elementByAccessibilityId("Action Sheets" + axIdExt).click()
@@ -156,7 +156,7 @@ describe('uicatalog - find - basics @skip-ios6', function () {
.nodeify(done);
});
var axIdExt = env.IOS8 ? '' : ', AAPLActionSheetViewController';
var axIdExt = (env.IOS8 || env.IOS9) ? '' : ', AAPLActionSheetViewController';
it('should find one element', function (done) {
driver
.elementByAccessibilityId("Action Sheets" + axIdExt).click()
@@ -182,7 +182,7 @@ describe('uicatalog - find - basics @skip-ios6', function () {
.nodeify(done);
});
var axIdExt = env.IOS8 ? '' : ', AAPLTextFieldViewController';
var axIdExt = (env.IOS8 || env.IOS9) ? '' : ', AAPLTextFieldViewController';
it('should find only one element per text field', function (done) {
driver
.execute("mobile: scroll", {direction: 'down'})
@@ -14,7 +14,7 @@ describe('uicatalog - find by accessibility id @skip-ios6', function () {
});
it('should find an element by name beneath another element', function (done) {
var axIdExt = env.IOS8 ? '' : ', AAPLActionSheetViewController';
var axIdExt = env.IOS8 || env.IOS9 ? '' : ', AAPLActionSheetViewController';
driver
.elementByAccessibilityId("UICatalog").click()
.elementByAccessibilityId("Action Sheets" + axIdExt)
@@ -49,7 +49,7 @@ describe('uicatalog - find by ios-ui-automation @skip-ios6', function () {
.nodeify(done);
});
it('should get a single element with non-zero index', function (done) {
var variableName = env.IOS8 ? '' : 'Empty list';
var variableName = env.IOS8 || env.IOS9 ? '' : 'Empty list';
driver.element(byUIA, '.elements()[1]').getAttribute('name')
.should.become(variableName)
.nodeify(done);
@@ -81,12 +81,12 @@ describe('uicatalog - find by ios-ui-automation @skip-ios6', function () {
driver.element(byUIA, '.elements()[1]').then(function (el) {
el
.elements(byUIA, filterVisible('.elements();'))
.should.eventually.have.length(12)
.should.eventually.have.length(env.IOS9 ? 10 : 12)
.nodeify(done);
});
});
it('should find elements by name', function (done) {
var axIdExt = env.IOS8 ? "" : ", AAPLButtonViewController";
var axIdExt = env.IOS8 || env.IOS9 ? "" : ", AAPLButtonViewController";
driver.element(byUIA, '.elements()[1]').then(function (el) {
el
.element(byUIA, '.elements()["Buttons' + axIdExt + '"]')
@@ -6,7 +6,7 @@ var env = require('../../../../helpers/env')
var SLOW_DOWN_MS = 1000;
describe('uicatalog - gestures - flick @skip-ios8 @skip-ios7 @skip-ios6', function () {
describe('uicatalog - gestures - flick @skip-ios-all', function () {
var driver;
setup(this, desired).then(function (d) { driver = d; });
@@ -69,11 +69,13 @@ describe('uicatalog - gestures - flick @skip-ios8 @skip-ios7 @skip-ios6', functi
});
describe('with element', function () {
afterEach(function () {
driver.back();
});
it("slider value should change", function (done) {
var valueBefore, slider;
driver
.elementsByClassName('UIATableCell').then(function (els) { return els[1]; })
.elementsByClassName('UIATableCell').then(function (els) { return els[10]; })
.click()
.elementByClassName("UIASlider").then(function (el) { slider = el; })
.then(function () { return slider.getAttribute("value"); })
@@ -88,7 +90,7 @@ describe('uicatalog - gestures - flick @skip-ios8 @skip-ios7 @skip-ios6', functi
it("should work with mobile flick", function (done) {
var valueBefore, slider;
driver
.elementsByClassName('UIATableCell').then(function (els) { return els[1]; })
.elementsByClassName('UIATableCell').then(function (els) { return els[10]; })
.click()
.elementByClassName("UIASlider").then(function (el) { slider = el; })
.then(function () { return slider.getAttribute("value"); })
@@ -106,7 +108,7 @@ describe('uicatalog - gestures - flick @skip-ios8 @skip-ios7 @skip-ios6', functi
it("should work with mobile flick and percent", function (done) {
var valueBefore, slider;
driver
.elementsByClassName('UIATableCell').then(function (els) { return els[1]; })
.elementsByClassName('UIATableCell').then(function (els) { return els[10]; })
.click()
.elementByClassName("UIASlider").then(function (el) { slider = el; })
.then(function () { return slider.getAttribute("value"); })
@@ -7,7 +7,7 @@ var env = require('../../../helpers/env'),
describe('uicatalog - lock device @skip-ios6', function () {
var driver;
setup(this, desired).then(function (d) { driver = d; });
var allowance = (env.IOS7 || env.IOS8) ? 9 : 2;
var allowance = (env.IOS7 || env.IOS8 || env.IOS9) ? 9 : 2;
it("should lock the device for 4 seconds (+/- " + allowance + " secs)", function (done) {
var before = new Date().getTime() / 1000;
driver
+1 -1
View File
@@ -11,7 +11,7 @@ describe('uicatalog - move @skip-ios6', function () {
setup(this, desired).then(function (d) { driver = d; });
it('should be able to click on arbitrary x-y elements', function (done) {
var axIdExt = env.IOS8 ? "" : ", AAPLButtonViewController";
var axIdExt = env.IOS8 || env.IOS9 ? "" : ", AAPLButtonViewController";
driver
.elementByAccessibilityId("Buttons" + axIdExt).moveTo(10, 10).click()
.elementByXPath("//UIAElement['SYSTEM (CONTACT ADD)']")
+45 -17
View File
@@ -1,8 +1,7 @@
"use strict";
var path = require('path')
, _ = require('underscore')
, os = require('os');
, localIp = require('appium-support').util.localIp;
var env = {};
@@ -73,6 +72,12 @@ function iphoneOrIpadSimulator(device, version) {
return isIpad ? 'iPad 2' : 'iPhone 6';
case '8.4':
return isIpad ? 'iPad 2' : 'iPhone 6';
case '9.0':
return isIpad ? 'iPad 2' : 'iPhone 6';
case '9.1':
return isIpad ? 'iPad 2' : 'iPhone 6';
case '9.2':
return isIpad ? 'iPad 2' : 'iPhone 6';
default:
throw new Error("invalid version");
}
@@ -153,6 +158,33 @@ switch (env.DEVICE) {
, app: process.env.APP ? path.resolve(__dirname, "../../sample-code/apps/" + process.env.APP + "/build/Release-iphonesimulator/" + process.env.APP + ".app") : ''
};
break;
case 'ios9':
case 'ios9_iphone':
case 'ios9_ipad':
env.CAPS = {
browserName: ''
, deviceName: iphoneOrIpadSimulator(env.DEVICE, "9.0")
, app: process.env.APP ? path.resolve(__dirname, "../../sample-code/apps/" + process.env.APP + "/build/Release-iphonesimulator/" + process.env.APP + ".app") : ''
};
break;
case 'ios91':
case 'ios91_iphone':
case 'ios91_ipad':
env.CAPS = {
browserName: ''
, deviceName: iphoneOrIpadSimulator(env.DEVICE, "9.1")
, app: process.env.APP ? path.resolve(__dirname, "../../sample-code/apps/" + process.env.APP + "/build/Release-iphonesimulator/" + process.env.APP + ".app") : ''
};
break;
case 'ios92':
case 'ios92_iphone':
case 'ios92_ipad':
env.CAPS = {
browserName: ''
, deviceName: iphoneOrIpadSimulator(env.DEVICE, "9.2")
, app: process.env.APP ? path.resolve(__dirname, "../../sample-code/apps/" + process.env.APP + "/build/Release-iphonesimulator/" + process.env.APP + ".app") : ''
};
break;
case 'android':
env.CAPS = {
browserName: ''
@@ -192,12 +224,15 @@ env.IOS81 = env.DEVICE.match(/ios81/i);
env.IOS82 = env.DEVICE.match(/ios82/i);
env.IOS83 = env.DEVICE.match(/ios83/i);
env.IOS84 = env.DEVICE.match(/ios84/i);
env.IOS9 = env.DEVICE.match(/ios9/i);
env.IOS91 = env.DEVICE.match(/ios91/i);
env.IOS92 = env.DEVICE.match(/ios92/i);
env.ANDROID = env.DEVICE.match(/android/i);
env.SELENDROID = env.DEVICE.match(/selendroid/i);
// better timeout settings for 71
env.LAUNCH_TIMEOUT = process.env.LAUNCH_TIMEOUT ? JSON.parse(process.env.LAUNCH_TIMEOUT) :
((env.IOS71 || env.IOS8) ? {"global": 60000, "afterSimLaunch": 10000} : 60000);
((env.IOS71 || env.IOS8 || env.IOS9) ? {"global": 60000, "afterSimLaunch": 10000} : 60000);
env.CAPS.launchTimeout = env.LAUNCH_TIMEOUT;
@@ -231,6 +266,12 @@ if (env.VERSION) {
env.CAPS.platformVersion = "8.4";
} else if (env.IOS8) {
env.CAPS.platformVersion = "8.0";
} else if (env.IOS92) {
env.CAPS.platformVersion = "9.2";
} else if (env.IOS91) {
env.CAPS.platformVersion = "9.1";
} else if (env.IOS9) {
env.CAPS.platformVersion = "9.0";
}
// max retry
@@ -251,24 +292,11 @@ if (env.SAUCE && env.TARBALL) {
env.CAPS.tags = [env.DEVICE];
}
// rest enf points
env.localIP = function () {
var ip = _.chain(os.networkInterfaces())
.values()
.flatten()
.filter(function (val) {
return (val.family === 'IPv4' && val.internal === false);
})
.pluck('address')
.first().value();
return ip;
};
env.LOCAL_APPIUM_PORT = env.SAUCE ? 4443 : env.APPIUM_PORT;
env.TEST_END_POINT = 'http://localhost:' + env.LOCAL_APPIUM_PORT + '/test/';
env.GUINEA_TEST_END_POINT = env.TEST_END_POINT + 'guinea-pig';
if (env.REAL_DEVICE) {
env.CHROME_TEST_END_POINT = 'http://' + env.localIP() + ':' + env.LOCAL_APPIUM_PORT + '/test/';
env.CHROME_TEST_END_POINT = 'http://' + localIp() + ':' + env.LOCAL_APPIUM_PORT + '/test/';
} else {
env.CHROME_TEST_END_POINT = 'http://10.0.2.2:' + env.LOCAL_APPIUM_PORT + '/test/';
}
+7 -4
View File
@@ -17,16 +17,19 @@ describe('android-controller', function () {
{ action: 'moveTo', options: { x: -40, y: -41 } },
{ action: 'release', options: {} } ];
controller.parseTouch(actions, false, function (err, touchStates) {
touchStates.length.should.equal(4);
touchStates.length.should.equal(5);
var actions = [{action: 'press', x: 100, y: 101},
{action: 'moveTo', x: 150, y: 152},
{action: 'wait', x: 150, y: 152},
{action: 'moveTo', x: 110, y: 111}];
{action: 'moveTo', x: 110, y: 111},
{action: 'release'}];
_.each(touchStates, function (state, index) {
state.action.should.equal(actions[index].action);
state.options.x.should.equal(actions[index].x);
state.options.y.should.equal(actions[index].y);
if (actions[index].action !== 'release') {
state.options.x.should.equal(actions[index].x);
state.options.y.should.equal(actions[index].y);
}
});
done();