Compare commits

...
Author SHA1 Message Date
dpgraham f9ee7d068e v1.9.1 2018-09-20 11:14:29 -07:00
dpgraham 7bd76ebb55 Add shrinkwrap 2018-09-20 11:14:29 -07:00
Dan Graham 3b6de23dba 1.9.1 changelog (#11390) 2018-09-20 10:52:04 -07:00
Dan Graham de1669e022 Actions example for a pending WD actions library (#11242) 2018-09-20 10:05:52 -04:00
Kazuaki Matsuo 991d7bb188 add mobile:mobileGetDeviceInfo (#11371) 2018-09-20 10:05:44 -04:00
Isaac Murchie b4e5f40228 Remove appium-android-bootstrap from packweb config 2018-09-20 10:05:37 -04:00
Isaac A. Murchie 110db3bdad Add 2.42 to chromedriver docs (#11375) 2018-09-20 10:05:28 -04:00
Kazuaki Matsuo 5fbe7dc81d add finger print (#11368)
* add fingre print

* add python support

* add links for touch id and finger print each other
2018-09-20 10:05:20 -04:00
Dan Graham 4a761abf83 Spelling error 2018-09-20 10:05:12 -04:00
Isaac A. Murchie ea5ac88119 Fix platformVersion in Java sample code (#11356) 2018-09-20 10:05:00 -04:00
Dan Graham 0fcbefedf1 Upgrade rubyzip (#11331) 2018-09-20 10:04:52 -04:00
dpgraham e53b33c85f v1.9.1-beta2 2018-09-20 10:04:43 -04:00
Mykola Mokhnach 686bdeec04 Fix the key name for the default capability (#11316)
* Fix the key name for the default capability

* Add a unit test for default caps
2018-09-20 10:04:33 -04:00
Dan Graham 6a570602e7 Change verbiage of set geolocation (#11322)
"current" geolocation latitude/longitude doesn't make much sense. Should be "desired" because we're setting it to something new.
2018-09-20 10:04:25 -04:00
dpgraham 4296fbe845 v1.9.1-beta1 2018-09-20 10:04:15 -04:00
Dan Graham 1b41f7f551 Add -image to selector strategies (#11281) 2018-09-20 10:04:05 -04:00
Mykola Mokhnach a5841da361 Write defaultCapabilities into firstMatch instead of alwaysMatch only if it is not overriden (#11303)
Partially addresses protocol setting issue in #11300
2018-09-20 10:03:56 -04:00
Kazuaki Matsuo 406d157df0 add multiple action chains for w3c (#11297) 2018-09-20 10:03:47 -04:00
Dan Graham 38580c4c2d Java code sample typo fix 2018-09-20 10:03:37 -04:00
greenkeeper[bot] 78b437c411 fix(package): update appium-android-driver to version 4.0.0 (#11277) 2018-09-20 10:02:53 -04:00
Kazuaki Matsuo 8d5f670366 Move sample code overhaul to ROADMAP completed list (#11274) 2018-09-20 10:02:38 -04:00
Dan Graham 24269c5bbb Make 'Onboarding Overhaul' in roadmap completed(#11226) 2018-09-20 10:02:30 -04:00
Isaac Murchie acbcfa645e Update packweb config 2018-09-20 10:02:23 -04:00
Mykola Mokhnach 67d21cfe05 Return more appropriate info as server status and speed it up (#11232)
* Improve /status response for the umbrella driver
2018-09-20 10:02:08 -04:00
Kazuaki Matsuo b498857e88 Add csharp sample (#11255)
* add git ignore

* add csharp sample tests

* update readme

* remove comments
2018-09-20 10:02:00 -04:00
Sai Krishna 2b03b841da [DOC] Updated java-example DragNDrop to use Point API (#11256)
* Updated java-example DragNDrop to use Point API

https://github.com/appium/appium-espresso-driver/issues/213

* fixed review comments

* Changed Point move durations to milliSeconds and set value to >=600ms for second movePoint
2018-09-20 10:01:52 -04:00
Isaac Murchie 82a86e329e Update some formatting in docs 2018-09-20 10:01:26 -04:00
Isaac Murchie 40eb96c281 Remove shrinkwrap file 2018-08-27 13:40:24 -04:00
Isaac Murchie 6b4f23e43a v1.9.0 2018-08-27 13:33:55 -04:00
Isaac Murchie 04caa6a5ea Update shrinkwrap for 1.9.0 2018-08-27 13:33:36 -04:00
Isaac A. Murchie 18008639a7 Add CHANGELOG for 1.9.0 (#11248)
* Add CHANGELOG for 1.9.0

* Address comments
2018-08-27 13:27:16 -04:00
39 changed files with 4247 additions and 103 deletions
+9
View File
@@ -31,7 +31,12 @@ build/
.idea
*DerivedData
__pycache__
# csharp
*.pyc
*.pdb
.vs
old
.tern-project
coverage
@@ -42,4 +47,8 @@ sample-code/php/composer.lock
sample-code/java/out/
sample-code/python/test/results/
sample-code/python/.pytest_cache/
sample-code/csharp/test/bin
sample-code/csharp/test/obj
sample-code/csharp/test/bin
sample-code/csharp/packages
appium.zip
+92 -1
View File
@@ -1,3 +1,94 @@
CHANGES IN VERSION 1.9.1 (from 1.9.0)
===================================
Appium 1.9.1 is a patch release. Next version will likely not support Xcode 8
#### General
* Append the current session identifier to the protocol name prefix in server logs
* Fix the way current protocol is determined from execute response
#### iOS
* Support Xcode 10 and iOS 12.0
* Add a possibility to set container type for Simulator while pushing/pulling files
* Fix W3C format handling by receiveAsyncResponse command
* Fix iPhone X Simulator screen recording
#### Android
* Fix `getStrings` for no app
* Fix screen recording bug
* Return the current geolocation from `getGeoLocation`
* Add `mobile:mobileGetDeviceInfo` command (UiAutomator2 only)
* Make 'by tag name' selector compatible with View Tag selector (Espresso only)
* Convert mouse action events to touch actions (Espresso only)
#### Windows
* Add more support for [W3C Actions API](https://www.w3.org/TR/webdriver1/#actions)
* Support pointer input methods: up, down and move
* Add advanced modifier attributes to 'touch' inputs: pressure, twist, width, height
* Add advanced modifier attributes to 'pen' inputs: pressure, twist, tilt x and y, eraser, barrel button
* Support interpolation for 'pen' and 'multitouch'
CHANGES IN VERSION 1.9.0 (from 1.8.1)
===================================
Appium 1.9.0 is a feature release, comprising multiple updates.
#### General
* Full W3C Specification support.
* Add full beta of [Espresso driver](https://github.com/appium/appium-espresso-driver)
for Android automation (used by specifying `automationName` capability to be
`"Espresso"`).
* Add driver for [Samsung Tizen devices](https://github.com/Samsung/appium-tizen-driver)
* Add `-image` find element strategy,
* Fix `--async-trace` server argument, and rename as `--long-stacktrace`
* Sample code has been moved into the main repository to aid in maintenance.
* Fix status retrieval to speed up performance.
#### iOS
* Add support for Xcode 10 beta 5 and iOS 12 beta 5.
* Add preliminary support for MacOS Mojave beta.
* Add face id biometric support.
* Fix retrieval of device time, and add optional `format` parameter.
* Do not crash if there is no `idevicesyslog` when ending session.
* Handle frames when page changes in Safari.
* Add desired capabilities:
* `remoteDebugProxy` - port or Unix domain socket on which a proxy for the
remote debugger sits.
* `safariGarbageCollect` - turn on/off JS garbage collection in Safari.
* `showSafariNetworkLog` - print Safari network logs in the Appium server logs.
* `mjpegServerPort` - port to which screenshots can be streamed.
* Fix handling of settings updates, so simulators are not restarted unnecessarily.
* Allow pulling of folder from real devices.
* Add `mobile: getContexts` execute function, to retrieve meta-information (title,
url, etc.) about available contexts.
* Fix certificate retrieval and handling.
* Fix cookie handling, to allow secure cookies.
* Fix Safari timeout issues.
* Add support to retrieve Safari network logs, as `safariNetwork` log type.
#### Android
* Update Chromedriver to v2.41.
* Get Chrome version for Webviews in Android 7+, to find correct Chromedriver.
* Make sure UiAutomator processes are cleaned up during test.
* Fix handling of `autoWebview` capability.
* New desired capabilities:
* `mjpegScreenshotUrl` - url to stream screenshots to.
* `chromedriverUseSystemExecutable` - boolean flag to use the default Chromedriver
installed with Appium, avoiding any attempt to find correct Chromedriver.
* `disableWindowAnimation` - disable window animations on device (now available
on UiAutomator _and_ UiAutomator2).
* `pageLoadStrategy` - page load strategy for Chromedriver.
* Allow test-only APKs to be installed.
* Fix implicit wait handling for finding elements.
* Better handle Unicode IME installation.
* Relax package validation logic.
* Fix error in UiAutomator searches with nested quotes.
* Perform accessibility refresh when needed on UiAutomator2.
* Improve logic for determining if apps need upgrade.
* Fix screen recording to allow longer recordings, up to 30 minutes.
CHANGES IN VERSION 1.8.1 (from 1.8.0)
===================================
@@ -324,7 +415,7 @@ the automation process configurable.
* Fix licensing in all dependencies.
#### Android
* Added handlers for basic system alerts, so now it is possible to accept/decline/get text of the most of
* Added handlers for basic system alerts, so now it is possible to accept/decline/get text of the most of
them using the standard Selenium's switchTo().alert() interface (UIA2 only)
* Fix backgrounding of app.
* Add `androidInstallPath` capability to specify where on the device apps are installed.
+2 -2
View File
@@ -23,9 +23,7 @@ exists to outline large-scale future plans for Appium.
| Project | Description |
|---------|-------------|
|[Onboarding Overhaul](https://github.com/appium/appium/issues/7169)|The current Appium docs leave a lot to be desired. The format is confusing (it would be better for API docs), the content is outdated, and many things are still undocumented. This project would go back to the drawing board in all of these areas and put together something genuinely helpful for the community, which hopefully reduces the kind of support issues we're currently flooded by.|
|[Appium Client Library Updates](https://github.com/appium/appium/issues/7170)|It's been too long since we ensured that Appium's client libraries were up to date with the server|
|[Sample Code Overhaul](https://github.com/appium/appium/issues/7171)|Our sample code repo has grown tangled and broken. Let's make a new one with minimal examples which are the same across every language, and actually maintain the repo so as not to let it become overgrown.|
|[Test App Overhaul](https://github.com/appium/appium/issues/7172)|Our current test applications (the ones used to test Appium's iOS and Android support) are not ideal. We use too many of them and the ones we use have a random hodgepodge of app SDK features, sometimes not enough to sufficiently test automation support. It'd be great to find a way to ensure our test app(s) have all the features we need to test, and keep in sync with our requirements.|
|[iOS Test Startup Time Improvement](https://github.com/appium/appium/issues/7173)|iOS startup times have gotten progressively longer with each iOS release. In part this is Apple's fault and there's not anything we can do about it. But we should investigate and see if we can be more efficient in how we start, especially given that we can now focus on XCUITest and leave Instruments behind|
|[Appium Dearchitecture](https://github.com/appium/appium/issues/7174)|OK, we went a bit overboard in Appium 1.5 by exploding Appium into 40+ repos and NPM packages. Let's think a little more critically about it and consolidate into the minimal number of repos/packages that make sense for our code and community patterns.|
@@ -43,6 +41,8 @@ exists to outline large-scale future plans for Appium.
| Project | Description | Completed | Appium Release | Notes |
|---------|-------------|-----------|----------------|-------|
|[Sample Code Overhaul](https://github.com/appium/appium/issues/7171)|Our sample code repo has grown tangled and broken. Let's make a new one with minimal examples which are the same across every language, and actually maintain the repo so as not to let it become overgrown.|
|[Onboarding Overhaul](https://github.com/appium/appium/issues/7169)|Have a new site built with MkDocs that documents all of the Selenium and Appium commands with code samples|1/2018|n/a|appium.io|
|[New Appium GUIs](https://github.com/appium/appium/issues/7167)|The current Appium GUIs (Appium.app and Appium.exe) have a number of issues. They aren't maintained by the core Appium team. They're written in Objective-C and C# respectively: two languages, and specifically, two languages other than Appium's core language. Because of this they lag behind the Appium server and are often broken. However, users love them primarily because of the Inspector feature (the thing that lets them graphically navigate their app), so we have to field a ton of complaints about these projects. There's an opportunity to start over. With [Electron](http://electron.atom.io/), we'd be able to take the new version of Appium we're working on and embed it directly inside a Node-based GUI app written using the languages and technologies the core team is experienced with. We'd have one codebase and could distribute the app on Mac, Windows, and Linux. The GUIs would become an official part of Appium's release cycle so they wouldn't be broken.|3/2017|N/A|[Appium Desktop website](https://github.com/appium/appium-desktop)|
|[Android 7.1.1 Support](https://github.com/appium/appium/issues/7165)|Google has released Android 7.1.1 (API Level 25). Ensure it works with UiAutomator 1/2|2/2017|1.6.4||
|[Android 7.0 Support](https://github.com/appium/appium/issues/7156)|Support Android 7 (Nougat)|11/2016|1.6.3||
@@ -0,0 +1,81 @@
---
name: Finger print
short_description: Authenticate users by using their finger print scans on supported emulators.
description:
|
For Android emulator.
Authenticate users by using their finger print scans.
Touch ID by iOS is [here](http://appium.io/docs/en/commands/device/simulator/touch-id/)
example_usage:
java:
|
driver.fingerPrint(1);
python:
|
self.driver.finger_print(1)
javascript_wd:
|
await driver.fingerprint(1);
javascript_wdio:
|
await driver.fingerprint(1);
ruby:
|
@driver.finger_print 1
php:
|
// PHP Code here
csharp:
|
// CSharp Code here
client_docs:
java: "https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/WebElement.html#click--"
python: "https://github.com/appium/python-client/blob/07180f7547ffcd13c212de21d027ea0355f9ab96/appium/webdriver/webdriver.py#L1390"
javascript_wdio: "https://github.com/appium/appium-android-driver/blob/d16c7161d92ab6f6c9cec3f22218219ac3659716/lib/commands/actions.js#L260"
javascript_wd: "https://github.com/appium/appium-android-driver/blob/d16c7161d92ab6f6c9cec3f22218219ac3659716/lib/commands/actions.js#L260"
ruby: "https://www.rubydoc.info/github/appium/ruby_lib_core/master/Appium/Core/Android/Device#finger_print-instance_method"
php:
csharp:
# Driver support by platform
driver_support:
ios:
xcuitest: false
uiautomation: false
android:
espresso: true
uiautomator2: true
uiautomator: true
mac:
mac: false
windows:
windows: false
client_support:
java: true
python: true
ruby: true
php: false
csharp: false
javascript_wd: true
javascript_wdio: true
# Information about the HTTP endpoints
endpoint:
url: /session/:session_id/appium/device/finger_print
method: 'POST'
url_parameters:
- name: session_id
description: ID of the session to route the command to
json_parameters:
- name: fingerprintId
type: number
description: finger prints stored in Android Keystore system (from 1 to 10)
# Links to specifications. Should link to at least one specification
specifications:
jsonwp: https://github.com/appium/appium-base-driver/blob/master/lib/protocol/routes.js#L379
@@ -11,6 +11,8 @@ description:
This call will only work if Appium process or its parent application (e.g. Terminal.app or Appium.app) has access to Mac OS accessibility in System Preferences > Security & Privacy > Privacy > Accessibility list
Finger Pring by Android is [here](http://appium.io/docs/en/commands/device/authentication/finger_print/)
example_usage:
java:
@@ -15,12 +15,15 @@ selector_strategies:
description: Name of element
- name: XPath
description: Search the app XML source using xpath (not recommended, has performance issues)
- name: Image
description: Locate an element by matching it with a base 64 encoded image file
- name: Android UiAutomator (UiAutomator2 only)
description: Use the UI Automator API, in particular the [UiSelector](https://developer.android.com/reference/android/support/test/uiautomator/UiSelector.html) class to locate elements. In Appium you send the Java code, as a string, to the server, which executes it in the application’s environment, returning the element or elements.
- name: Android View Tag (Espresso only)
description: Locate an element by its [view tag](https://developer.android.com/reference/android/support/test/espresso/matcher/ViewMatchers.html#withTagValue(org.hamcrest.Matcher%3Cjava.lang.Object%3E))
- name: IOS UIAutomation
description: When automating an iOS application, Apple’s [Instruments](/docs/en/drivers/ios-uiautomation.md) framework can be used to find elements
example_usage:
java:
|
+52 -11
View File
@@ -6,21 +6,26 @@ description:
* input source: Represents an input device (pointer or key) that a series of actions are dispatched to. The input source has a unique ID.
* action: An action that is dispatched to an input source. For a keyboard source, this can be 'keyDown' or 'keyUp'. For a pointer event this can be 'pointerMove',
'pointerDown', or 'pointerUp'. 'pause' events can also be sent to the device.
The Actions API takes a list of input sources and executes each 'tick'. A 'tick' is a slice of an action chain, so if you have two input sources, the first 'tick'
is the 0-indexed action, the second 'tick' is the 1-indexed action, etc.... All of the actions per tick are executed concurrently.
example_usage:
java:
WebElement source = (MobileElement) driver.findElementsByAccessibilityId("SomeAccessibilityID");
WebElement target = (MobileElement) driver.findElementsByAccessibilityId("SomeOtherAccessibilityID");
Actions actionBuilder = new Actions(driver);
Action dragAndDropAction = actionBuilder.clickAndHold(source)
.moveToElement(target, 1, 1)
.release(target)
.build();
dragAndDropAction.perform();
Point source = dragMe.getCenter();
Point target = driver.findElementByAccessibilityId("dropzone").getCenter();
PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
Sequence dragNDrop = new Sequence(finger, 1);
dragNDrop.addAction(finger.createPointerMove(Duration.ofMillis(0),
PointerInput.Origin.viewport(), source.x, source.y));
dragNDrop.addAction(finger.createPointerDown(PointerInput.MouseButton.LEFT.asArg()));
dragNDrop.addAction(finger.createPointerMove(Duration.ofMillis(700),
PointerInput.Origin.viewport(),target.x, target.y));
dragNDrop.addAction(finger.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));
driver.perform(Arrays.asList(dragNDrop));
python:
|
element = driver.find_element_by_accessibility_id("elId")
@@ -30,7 +35,24 @@ example_usage:
actions.perform()
javascript_wd:
|
// Not supported
// Performs a 'pinch-and-zoom'
var actions = new wd.W3CActions(driver);
var touchInput = actions.addTouchInput();
touchInput.pointerMove({duration: 0, x: 100, y: 100});
touchInput.pointerDown({button: 0});
touchInput.pause({duration: 500});
touchInput.pointerMove({duration: 1000, origin: 'pointer', x: -50, y: 100});
touchInput.pointerUp({button: 0});
var secondTouchInput = actions.addTouchInput();
secondTouchInput.pointerMove({duration: 0, x: 200, y: 200});
secondTouchInput.pointerDown({button: 0});
secondTouchInput.pause({duration: 300});
secondTouchInput.pointerMove({duration: 1000, origin: 'pointer', x: 50, y: 100});
secondTouchInput.pointerUp({button: 0});
await actions.perform();
// Releases any previously run actions (e.g.: if a key is 'down' because of /actions, releases it using key up)
await driver.releaseW3CActions();
javascript_wdio:
|
// Example: expressing a 1-second pinch-and-zoom
@@ -64,11 +86,30 @@ example_usage:
ruby:
|
# Send keys to an element
# Build Single action chain
action_builder = driver.action
keyboard = action_builder.key_input
keyboard = action_builder.key_inputs
el = driver.find_element(id: "some_id")
driver.action.click(el).pause(keyboard).pause(keyboard).pause(keyboard).send_keys('keys').perform
# Build multiple action chains
# Example: expressing a 1-second pinch-and-zoom
# with a 500ms wait after the fingers first touch:
f1 = driver.action.add_pointer_input(:touch, 'finger1')
f1.create_pointer_move(duration: 1, x: 200, y: 500, origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
f1.create_pointer_down(:left)
f1.create_pause(0.5)
f1.create_pointer_move(duration: 1, x: 200, y: 200, origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
f1.create_pointer_up(:left)
f2 = driver.action.add_pointer_input(:touch, 'finger2')
f2.create_pointer_move(duration: 1, x: 200, y: 500, origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
f2.create_pointer_down(:left)
f2.create_pause(0.5)
f2.create_pointer_move(duration: 1, x: 200, y: 800, origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
f2.create_pointer_up(:left)
driver.perform_actions [f1, f2]
php:
|
// TODO
@@ -162,5 +203,5 @@ endpoint:
description: Y coordinate of pointer move event
# Links to specifications. Should link to at least one specification
specifications:
specifications:
w3c: https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/interactions/Actions.html
+2 -2
View File
@@ -62,12 +62,12 @@
| mobile:scrollBackTo | Scroll from one element to another | `{elementId, elementToId}` | `{elementId: 2, elementToId: 1}` |
| mobile:viewportScreenshot | Like [screenshot](/commands/session/screenshot/) but only includes contents of viewport | <none> | <none> |
| mobile:deepLink | Opens a deep-link URL for testing [Instant Apps](https://support.google.com/googleplay/answer/7240211?hl=en) | `{url, package}` | `{url: "https://www.site.com/", package: "com.site.SomeAndroidPackage"}` |
| mobile:mobileGetDeviceInfo | Gets device information like manufacturer and model. Read [GetDeviceInfo](https://github.com/appium/appium-uiautomator2-server/blob/master/app/src/main/java/io/appium/uiautomator2/handler/GetDeviceInfo.java) for more details. | <none> | <none> |
example_usage:
java:
|
driver.executeScript("mobile: scroll", ImmutableMap.of("direction", "down"));;
driver.executeScript("mobile: scroll", ImmutableMap.of("direction", "down"));
python:
|
@@ -67,13 +67,13 @@ endpoint:
json_parameters:
- name: latitude
type: number
description: The current geolocation latitude
description: The desired geolocation latitude
- name: longitude
type: number
description: The current geolocation longitude
description: The desired geolocation longitude
- name: altitude
type: number
description: The current geolocation altitude
description: The desired geolocation altitude (optional)
# Links to specifications. Should link to at least one specification
specifications:
@@ -188,14 +188,14 @@ converted into the `npm-shrinkwrap.json` file.
1. Run `npm publish` (with `--tag beta` if this isn't an official release).
1. Remove the NPM shrinkwrap JSON file from Git and push the changes
1. Update the site docs or put in an [issue](https://github.com/appium/appium/issues) to have the site docs updated
* From Appium repo, run `npm run generate-docs` and make a pull request if there are any newly generated docs.
* When the above PR is merged, clone the [appium.io](https://github.com/appium/appium.io) repo (if you haven't done so already)
* Check out and pull the latest `gh-pages` branch
* Follow the setup guide in the README.md
* Generate the docs by running `npm run build:docs`
* Test by running `npm run serve` and navigating to https://localhost:4000/
* Make a new branch
* Commit and push changes and make a pull request to have changes merged into `gh-pages`
* From Appium repo, run `npm run generate-docs` and make a pull request if there are any newly generated docs.
* When the above PR is merged, clone the [appium.io](https://github.com/appium/appium.io) repo (if you haven't done so already)
* Check out and pull the latest `gh-pages` branch
* Follow the setup guide in the README.md
* Generate the docs by running `npm run build:docs`
* Test by running `npm run serve` and navigating to https://localhost:4000/
* Make a new branch
* Commit and push changes and make a pull request to have changes merged into `gh-pages`
1. Create a new release on GitHub: go to `https://github.com/appium/appium/releases/tag/v<VERSION>` and hit "Edit Tag". Make the release name `<VERSION>` (e.g., `2.0.5`), then paste in the changelog (but not the changelog header for this version). If it's a beta release, mark as pre-release.
1. Create a new post on discuss.appium.io announcing the release. Post it in the "News" category. Paste in the changelog and any choice comments. Pin it and unpin the previous release post.
1. Begin process of releasing `appium-desktop`.
+3 -3
View File
@@ -252,8 +252,8 @@ for this).
### Troubleshooting ideas
0. Make sure UDID is correct by checking it in Xcode Organizer or iTunes. It
1. Make sure UDID is correct by checking it in Xcode Organizer or iTunes. It
is a long string (20+ chars).
0. Make sure that you can run your tests against the Simulator.
0. Make sure UI Automation is enabled on your device. Settings -> Developer ->
1. Make sure that you can run your tests against the Simulator.
1. Make sure UI Automation is enabled on your device. Settings -> Developer ->
Enable UI Automation
@@ -221,7 +221,7 @@ one to select or value selection does not work because of XCTest bug.
* _order_: Either _next_ to select the value next to the current one
from the target picker wheel or _previous_ to select the previous one. Mandatory parameter
* _offset_: The value in range [0.01, 0.5]. It defines how far from picker
wheel's center the click should happen. The actual distance is culculated by
wheel's center the click should happen. The actual distance is calculated by
multiplying this value to the actual picker wheel height. Too small offset value
may not change the picker wheel value and too high value may cause the wheel to switch
two or more values at once. Usually the optimal value is located in range [0.15, 0.3]. _0.2_ by default
@@ -42,6 +42,7 @@ Chrome version that is automatable:
| Version | Minimum Chrome Version | Link to Chromedriver |
|---------|------------------------|-----------------------------------------------------------------------------------|
| 2.42 | 68.0.3440.0 | [v2.42 (link)](https://chromedriver.storage.googleapis.com/index.html?path=2.42/) |
| 2.41 | 67.0.3360.0 | [v2.41 (link)](https://chromedriver.storage.googleapis.com/index.html?path=2.41/) |
| 2.40 | 66.0.3359.0 | [v2.40 (link)](https://chromedriver.storage.googleapis.com/index.html?path=2.40/) |
| 2.39 | 66.0.3359.0 | [v2.39 (link)](https://chromedriver.storage.googleapis.com/index.html?path=2.39/) |
+6 -8
View File
@@ -1,6 +1,6 @@
import _ from 'lodash';
import log from './logger';
import { getAppiumConfig } from './config';
import { getBuildInfo, updateBuildInfo } from './config';
import { BaseDriver, errors, isSessionCommand } from 'appium-base-driver';
import { FakeDriver } from 'appium-fake-driver';
import { AndroidDriver } from 'appium-android-driver';
@@ -148,6 +148,8 @@ class AppiumDriver extends BaseDriver {
// it might be changed by other async calls at any time
// It is not recommended to access this property directly from the outside
this.pendingDrivers = {};
updateBuildInfo();
}
/**
@@ -201,13 +203,9 @@ class AppiumDriver extends BaseDriver {
}
async getStatus () {
const config = await getAppiumConfig();
const gitSha = config['git-sha'];
const status = {build: {version: config.version}};
if (!_.isEmpty(gitSha)) {
status.build.revision = gitSha;
}
return status;
return {
build: _.clone(await getBuildInfo()),
};
}
async getSessions () {
+108 -21
View File
@@ -1,11 +1,18 @@
import _ from 'lodash';
import path from 'path';
import { mkdirp, fs } from 'appium-support';
import { mkdirp, fs, system, util } from 'appium-support';
import request from 'request-promise';
import { exec } from 'teen_process';
import { rootDir } from './utils';
import logger from './logger';
const APPIUM_VER = require(path.resolve(rootDir, 'package.json')).version;
const GIT_META_ROOT = '.git';
const GIT_BINARY = `git${system.isWindows() ? '.exe' : ''}`;
const GITHUB_API = 'https://api.github.com/repos/appium/appium';
const BUILD_INFO = {
version: APPIUM_VER,
};
function getNodeVersion () {
// expect v<major>.<minor>.<patch>
@@ -14,24 +21,103 @@ function getNodeVersion () {
return [Number(version[1]), Number(version[2])];
}
async function getGitRev () {
let rev = null;
try {
let {stdout} = await exec("git", ["rev-parse", "HEAD"], {rootDir});
rev = stdout.trim();
} catch (ign) {}
return rev;
async function updateBuildInfo () {
const sha = await getGitRev(true);
if (!sha) {
return;
}
BUILD_INFO['git-sha'] = sha;
const built = await getGitTimestamp(sha, true);
if (!_.isEmpty(built)) {
BUILD_INFO.built = built;
}
}
async function getAppiumConfig () {
let stat = await fs.stat(path.resolve(__dirname, '..'));
let built = stat.mtime.getTime();
let config = {
'git-sha': await getGitRev(),
built,
version: APPIUM_VER,
};
return config;
async function getGitRev (useGitHubFallback = false) {
if (await fs.exists(path.resolve(rootDir, GIT_META_ROOT))) {
try {
const {stdout} = await exec(GIT_BINARY, ['rev-parse', 'HEAD'], {
cwd: rootDir
});
return stdout.trim();
} catch (err) {
logger.warn(`Cannot retrieve git revision for Appium version ${APPIUM_VER} from the local repository. ` +
`Original error: ${err.message}`);
}
}
if (!useGitHubFallback) {
return null;
}
try {
const response = await request.get(`${GITHUB_API}/tags`, {
headers: {
'User-Agent': `Appium ${APPIUM_VER}`
}
});
const resBodyObj = util.safeJsonParse(response);
if (_.isArray(resBodyObj)) {
for (const {name, commit} of resBodyObj) {
if (name === `v${APPIUM_VER}` && commit && commit.sha) {
return commit.sha;
}
}
}
} catch (err) {
logger.warn(`Cannot retrieve git revision for Appium version ${APPIUM_VER} from GitHub. ` +
`Original error: ${err.message}`);
}
return null;
}
async function getGitTimestamp (commitSha, useGitHubFallback = false) {
if (await fs.exists(path.resolve(rootDir, GIT_META_ROOT))) {
try {
const {stdout} = await exec(GIT_BINARY, ['show', '-s', '--format=%ci', commitSha], {
cwd: rootDir
});
return stdout.trim();
} catch (err) {
logger.warn(`Cannot retrieve the timestamp for Appium git commit ${commitSha} from the local repository. ` +
`Original error: ${err.message}`);
}
}
if (!useGitHubFallback) {
return null;
}
try {
const response = await request.get(`${GITHUB_API}/commits/${commitSha}`, {
headers: {
'User-Agent': `Appium ${APPIUM_VER}`
}
});
const resBodyObj = util.safeJsonParse(response);
if (resBodyObj && resBodyObj.commit) {
if (resBodyObj.commit.committer && resBodyObj.commit.committer.date) {
return resBodyObj.commit.committer.date;
}
if (resBodyObj.commit.author && resBodyObj.commit.author.date) {
return resBodyObj.commit.author.date;
}
}
} catch (err) {
logger.warn(`Cannot retrieve the timestamp for Appium git commit ${commitSha} from GitHub. ` +
`Original error: ${err.message}`);
}
return null;
}
/**
* @returns Mutable object containing Appium build information. By default it
* only contains the Appium version, but is updated with the build timestamp
* and git commit hash asynchronously as soon as `updateBuildInfo` is called
* and succeeds.
*/
async function getBuildInfo () {
return BUILD_INFO;
}
function checkNodeOk () {
@@ -52,8 +138,8 @@ function warnNodeDeprecations () {
}
async function showConfig () {
let config = await getAppiumConfig();
console.log(JSON.stringify(config)); // eslint-disable-line no-console
await updateBuildInfo();
console.log(JSON.stringify(await getBuildInfo())); // eslint-disable-line no-console
}
function getNonDefaultArgs (parser, args) {
@@ -142,6 +228,7 @@ async function validateTmpDir (tmpDir) {
}
}
export { getAppiumConfig, validateServerArgs, checkNodeOk, showConfig,
export { getBuildInfo, validateServerArgs, checkNodeOk, showConfig,
warnNodeDeprecations, validateTmpDir, getNonDefaultArgs,
getDeprecatedArgs, getGitRev, checkValidPort, APPIUM_VER };
getDeprecatedArgs, getGitRev, checkValidPort, APPIUM_VER,
updateBuildInfo };
+37 -18
View File
@@ -61,22 +61,38 @@ function parseCapsForInnerDriver (jsonwpCapabilities, w3cCapabilities, constrain
const {W3C, MJSONWP} = BaseDriver.DRIVER_PROTOCOL;
// Make copies of the capabilities that include the default capabilities
if (hasW3CCaps) {
w3cCapabilities = {
...w3cCapabilities,
alwaysMatch: {
...defaultCapabilities,
...w3cCapabilities.alwaysMatch,
},
};
}
// Make sure we don't mutate the original arguments
jsonwpCapabilities = _.cloneDeep(jsonwpCapabilities);
w3cCapabilities = _.cloneDeep(w3cCapabilities);
defaultCapabilities = _.cloneDeep(defaultCapabilities);
if (hasJSONWPCaps) {
jsonwpCapabilities = {
...defaultCapabilities,
...jsonwpCapabilities,
};
if (!_.isEmpty(defaultCapabilities)) {
if (hasW3CCaps) {
const {firstMatch = [], alwaysMatch = {}} = w3cCapabilities;
for (const [defaultCapKey, defaultCapValue] of _.toPairs(defaultCapabilities)) {
let isCapAlreadySet = false;
for (const firstMatchEntry of firstMatch) {
if (_.has(removeW3CPrefixes(firstMatchEntry), removeW3CPrefix(defaultCapKey))) {
isCapAlreadySet = true;
break;
}
}
isCapAlreadySet = isCapAlreadySet || _.has(removeW3CPrefixes(alwaysMatch), removeW3CPrefix(defaultCapKey));
if (isCapAlreadySet) {
continue;
}
// Only add the default capability if it is not overridden
if (_.isEmpty(firstMatch)) {
w3cCapabilities.firstMatch = [{[defaultCapKey]: defaultCapValue}];
} else {
firstMatch[0][defaultCapKey] = defaultCapValue;
}
}
}
if (hasJSONWPCaps) {
jsonwpCapabilities = Object.assign({}, removeW3CPrefixes(defaultCapabilities), jsonwpCapabilities);
}
}
// Get MJSONWP caps
@@ -220,13 +236,16 @@ function removeW3CPrefixes (caps) {
const fixedCaps = {};
for (let [name, value] of _.toPairs(caps)) {
const colonPos = name.indexOf(':');
const key = colonPos > 0 ? name.substring(colonPos + 1) : name;
fixedCaps[key] = value;
fixedCaps[removeW3CPrefix(name)] = value;
}
return fixedCaps;
}
function removeW3CPrefix (key) {
const colonPos = key.indexOf(':');
return colonPos > 0 && key.length > colonPos ? key.substring(colonPos + 1) : key;
}
function getPackageVersion (pkgName) {
const pkgInfo = require(`${pkgName}/package.json`) || {};
return pkgInfo.version;
+3103
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -11,7 +11,7 @@
"firefoxos",
"testing"
],
"version": "1.9.0-beta.1",
"version": "1.9.1",
"author": "https://github.com/appium",
"license": "Apache-2.0",
"repository": {
@@ -34,7 +34,7 @@
"doc": "./docs"
},
"dependencies": {
"appium-android-driver": "3.x",
"appium-android-driver": "^4.0.0",
"appium-base-driver": "3.x",
"appium-espresso-driver": ">=1.0.0-beta.13",
"appium-fake-driver": "^0.x",
+1 -2
View File
@@ -5,12 +5,12 @@
],
"appium-contrib": [
"appium-adb",
"appium-android-bootstrap",
"appium-android-driver",
"appium-android-ime",
"appium-base-driver",
"appium-chromedriver",
"appium-doctor",
"appium-espresso-driver",
"appium-fake-driver",
"appium-ios-driver",
"appium-ios-simulator",
@@ -18,7 +18,6 @@
"appium-remote-debugger",
"appium-selendroid-driver",
"appium-support",
"appium-uiautomator",
"appium-uiautomator2-driver",
"appium-uiautomator2-server",
"appium-unlock",
+11
View File
@@ -0,0 +1,11 @@
# Appium C# samples
## Setup
* Download [VisualStudio](https://visualstudio.microsoft.com/downloads/)
* Open `test.sln` solution file with the Visual Studio
## Running Tests
* Get all packages
* Run all tests
+17
View File
@@ -0,0 +1,17 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "test", "test\test.csproj", "{E22BA060-B513-4D45-885A-E7FEFBEEA667}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E22BA060-B513-4D45-885A-E7FEFBEEA667}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E22BA060-B513-4D45-885A-E7FEFBEEA667}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E22BA060-B513-4D45-885A-E7FEFBEEA667}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E22BA060-B513-4D45-885A-E7FEFBEEA667}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
+60
View File
@@ -0,0 +1,60 @@
using System;
namespace AppiumDotNetSamples.Helper
{
public static class Env
{
public static String rootDirectory = System.IO.Path.GetFullPath($"{System.AppDomain.CurrentDomain.BaseDirectory.ToString()}/../../../..");
static public bool IsSauce()
{
return Environment.GetEnvironmentVariable("SAUCE_LABS") != null;
}
static public Uri ServerUri()
{
String sauceUserName = Environment.GetEnvironmentVariable("SAUCE_USERNAME");
String sauceAccessKey = Environment.GetEnvironmentVariable("SAUCE_ACCESS_KEY");
return (sauceUserName == null) || (sauceAccessKey == null)
? new Uri("http://localhost:4723/wd/hub")
: new Uri($"https://{sauceUserName}:{sauceAccessKey}@ondemand.saucelabs.com:80/wd/hub");
}
public static TimeSpan INIT_TIMEOUT_SEC = TimeSpan.FromSeconds(180);
public static TimeSpan IMPLICIT_TIMEOUT_SEC = TimeSpan.FromSeconds(10);
}
public static class App
{
static public String IOSApp()
{
return Env.IsSauce() ? "http://appium.github.io/appium/assets/TestApp7.1.app.zip" : $"{Env.rootDirectory}/apps/TestApp.app.zip";
}
static public String IOSDeviceName()
{
return Environment.GetEnvironmentVariable("IOS_DEVICE_NAME") ?? "iPhone 6s";
}
static public String IOSPlatformVersion()
{
return Environment.GetEnvironmentVariable("IOS_PLATFORM_VERSION") ?? "11.4";
}
static public String AndroidApp()
{
return Env.IsSauce() ? "http://appium.github.io/appium/assets/ApiDemos-debug.apk" : $"{Env.rootDirectory}/apps/ApiDemos-debug.apk";
}
static public String AndroidDeviceName()
{
return Environment.GetEnvironmentVariable("ANDROID_DEVICE_VERSION") ?? "Android";
}
static public String AndroidPlatformVersion()
{
return Environment.GetEnvironmentVariable("ANDROID_PLATFORM_VERSION") ?? "7.1";
}
}
}
@@ -0,0 +1,67 @@
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Enums;
using OpenQA.Selenium.Appium.Android;
using OpenQA.Selenium.Remote;
using System;
using AppiumDotNetSamples.Helper;
namespace AppiumDotNetSamples
{
[TestFixture()]
public class AndroidBasicInteractionsTest
{
private AndroidDriver<AndroidElement> driver;
[SetUp()]
public void BeforeAll()
{
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.SetCapability(MobileCapabilityType.BrowserName, "");
capabilities.SetCapability(MobileCapabilityType.PlatformName, App.AndroidDeviceName());
capabilities.SetCapability(MobileCapabilityType.PlatformVersion, App.AndroidPlatformVersion());
capabilities.SetCapability(MobileCapabilityType.AutomationName, "UIAutomator2");
capabilities.SetCapability(MobileCapabilityType.DeviceName, "Nexus");
capabilities.SetCapability("appActivity", ".app.SearchInvoke");
capabilities.SetCapability(MobileCapabilityType.App, App.AndroidApp());
driver = new AndroidDriver<AndroidElement>(Env.ServerUri(), capabilities, Env.INIT_TIMEOUT_SEC);
driver.Manage().Timeouts().ImplicitWait = Env.IMPLICIT_TIMEOUT_SEC;
}
[TearDown()]
public void AfterAll()
{
driver.Quit();
}
[Test()]
public void TestShouldSendKetsToSearchBoxThenCheckTheValue()
{
AndroidElement searchBoxElement = driver.FindElementById("txt_query_prefill");
searchBoxElement.SendKeys("Hello World!");
AndroidElement onSearchRequestButton = driver.FindElementById("btn_start_search");
onSearchRequestButton.Click();
AndroidElement seachText = driver.FindElementById("android:id/search_src_text");
Assert.AreEqual("Hello World!", seachText.Text);
}
[Test()]
public void TestShouldClickAButtonThatOpensAnAlertAndThenDismissesIt()
{
driver.StartActivity("io.appium.android.apis", ".app.AlertDialogSamples");
AndroidElement openDialogButton = driver.FindElementById("io.appium.android.apis:id/two_buttons");
openDialogButton.Click();
AndroidElement alertElement = driver.FindElementById("android:id/alertTitle");
String alertText = alertElement.Text;
Assert.AreEqual("Lorem ipsum dolor sit aie consectetur adipiscing\nPlloaso mako nuto siwuf cakso dodtos anr koop.", alertText);
driver.FindElementById("android:id/button1").Click();
}
}
}
@@ -0,0 +1,46 @@
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Enums;
using OpenQA.Selenium.Appium.Android;
using OpenQA.Selenium.Remote;
using System;
using AppiumDotNetSamples.Helper;
namespace AppiumDotNetSamples
{
[TestFixture()]
public class AndroidCreateSessionTest
{
private AndroidDriver<AndroidElement> driver;
[OneTimeSetUp()]
public void BeforeAll()
{
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.SetCapability(MobileCapabilityType.BrowserName, "");
capabilities.SetCapability(MobileCapabilityType.PlatformName, App.AndroidDeviceName());
capabilities.SetCapability(MobileCapabilityType.PlatformVersion, App.AndroidPlatformVersion());
capabilities.SetCapability(MobileCapabilityType.AutomationName, "UIAutomator2");
capabilities.SetCapability(MobileCapabilityType.DeviceName, "Nexus");
capabilities.SetCapability(MobileCapabilityType.App, App.AndroidApp());
driver = new AndroidDriver<AndroidElement>(Env.ServerUri(), capabilities, Env.INIT_TIMEOUT_SEC);
driver.Manage().Timeouts().ImplicitWait = Env.IMPLICIT_TIMEOUT_SEC;
}
[Test()]
public void TestShouldCreateAndDestroyAndroidSessions()
{
String currentActivity = driver.CurrentActivity;
Assert.AreEqual(".ApiDemos", currentActivity);
driver.Quit();
Assert.Throws<WebDriverException>(
() => { currentActivity = driver.CurrentActivity; });
}
}
}
@@ -0,0 +1,46 @@
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Enums;
using OpenQA.Selenium.Appium.Android;
using OpenQA.Selenium.Remote;
using System;
using AppiumDotNetSamples.Helper;
namespace AppiumDotNetSamples
{
[TestFixture()]
public class AndroidCreateWebSessionTest
{
private IWebDriver driver;
[OneTimeSetUp()]
public void BeforeAll()
{
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.SetCapability(MobileCapabilityType.BrowserName, "Chrome");
capabilities.SetCapability(MobileCapabilityType.PlatformName, App.AndroidDeviceName());
capabilities.SetCapability(MobileCapabilityType.PlatformVersion, App.AndroidPlatformVersion());
capabilities.SetCapability(MobileCapabilityType.AutomationName, "UIAutomator2");
capabilities.SetCapability(MobileCapabilityType.DeviceName, "Nexus");
driver = new AndroidDriver<AppiumWebElement>(Env.ServerUri(), capabilities, Env.INIT_TIMEOUT_SEC);
driver.Manage().Timeouts().ImplicitWait = Env.IMPLICIT_TIMEOUT_SEC;
}
[Test()]
public void TestShouldCreateAndDestroyAndroidwebSessions()
{
driver.Url = "https://www.google.com";
String title = driver.Title;
Assert.AreEqual("Google", title);
driver.Quit();
Assert.Throws<WebDriverException>(
() => { title = driver.Title; });
}
}
}
@@ -0,0 +1,68 @@
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Enums;
using OpenQA.Selenium.Appium.Android;
using OpenQA.Selenium.Remote;
using System;
using System.Collections.Generic;
using AppiumDotNetSamples.Helper;
namespace AppiumDotNetSamples
{
[TestFixture()]
public class AndroidSelectorsTest
{
private AndroidDriver<AndroidElement> driver;
[OneTimeSetUp()]
public void BeforeAll()
{
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.SetCapability(MobileCapabilityType.BrowserName, "");
capabilities.SetCapability(MobileCapabilityType.PlatformName, App.AndroidDeviceName());
capabilities.SetCapability(MobileCapabilityType.PlatformVersion, App.AndroidPlatformVersion());
capabilities.SetCapability(MobileCapabilityType.AutomationName, "UIAutomator2");
capabilities.SetCapability(MobileCapabilityType.DeviceName, "Nexus");
capabilities.SetCapability(MobileCapabilityType.App, App.AndroidApp());
driver = new AndroidDriver<AndroidElement>(Env.ServerUri(), capabilities, Env.INIT_TIMEOUT_SEC);
driver.Manage().Timeouts().ImplicitWait = Env.IMPLICIT_TIMEOUT_SEC;
}
[OneTimeTearDown()]
public void AfterAll()
{
driver.Quit();
}
[Test()]
public void TestShouldFindElementsByAccessibilityId()
{
ICollection<AndroidElement> elements = driver.FindElementsByAccessibilityId("Content");
Assert.AreEqual(1, elements.Count);
}
[Test()]
public void TestShouldFindElementsById()
{
ICollection<AndroidElement> elements = driver.FindElementsById("android:id/action_bar_container");
Assert.AreEqual(1, elements.Count);
}
[Test()]
public void TestShouldFindElementsByClassName()
{
ICollection<AndroidElement> elements = driver.FindElementsByClassName("android.widget.FrameLayout");
Assert.AreEqual(3, elements.Count);
}
[Test()]
public void TestShouldFindElementsByXPath()
{
ICollection<AndroidElement> elements = driver.FindElementsByXPath("//*[@class='android.widget.FrameLayout']");
Assert.AreEqual(3, elements.Count);
}
}
}
@@ -0,0 +1,59 @@
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Enums;
using OpenQA.Selenium.Appium.iOS;
using OpenQA.Selenium.Remote;
using System;
using AppiumDotNetSamples.Helper;
namespace AppiumDotNetSamples
{
[TestFixture()]
public class IOSBasicInteractionsTest
{
private IOSDriver<IOSElement> driver;
[SetUp()]
public void BeforeAll()
{
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.SetCapability(MobileCapabilityType.BrowserName, "");
capabilities.SetCapability(MobileCapabilityType.PlatformName, "iOS");
capabilities.SetCapability(MobileCapabilityType.PlatformVersion, App.IOSPlatformVersion());
capabilities.SetCapability(MobileCapabilityType.AutomationName, "XCUITest");
capabilities.SetCapability(MobileCapabilityType.DeviceName, App.IOSDeviceName());
capabilities.SetCapability(MobileCapabilityType.App, App.IOSApp());
driver = new IOSDriver<IOSElement>(Env.ServerUri(), capabilities, Env.INIT_TIMEOUT_SEC);
driver.Manage().Timeouts().ImplicitWait = Env.IMPLICIT_TIMEOUT_SEC;
}
[TearDown()]
public void AfterAll()
{
driver.Quit();
}
[Test()]
public void TestShouldSendKetsToInputs()
{
IOSElement textField = driver.FindElementById("TextField1");
Assert.Null(textField.GetAttribute("value"));
textField.SendKeys("Hello World!");
Assert.AreEqual("Hello World!", textField.GetAttribute("value"));
}
[Test()]
public void TestShouldClickAButtonThatOpensAnAlert()
{
IOSElement buttonElement = driver.FindElementByAccessibilityId("show alert");
buttonElement.Click();
String alertTitle = "Cool title";
IOSElement alertTitleElement = driver.FindElementByAccessibilityId(alertTitle);
Assert.AreEqual(alertTitle, alertTitleElement.GetAttribute("name"));
}
}
}
@@ -0,0 +1,76 @@
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Enums;
using OpenQA.Selenium.Appium.iOS;
using OpenQA.Selenium.Remote;
using System;
using System.Collections.Generic;
using AppiumDotNetSamples.Helper;
namespace AppiumDotNetSamples
{
[TestFixture()]
public class IOSSelectorsTest
{
private IOSDriver<IOSElement> driver;
[OneTimeSetUp()]
public void BeforeAll()
{
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.SetCapability(MobileCapabilityType.BrowserName, "");
capabilities.SetCapability(MobileCapabilityType.PlatformName, "iOS");
capabilities.SetCapability(MobileCapabilityType.PlatformVersion, App.IOSPlatformVersion());
capabilities.SetCapability(MobileCapabilityType.AutomationName, "XCUITest");
capabilities.SetCapability(MobileCapabilityType.DeviceName, App.IOSDeviceName());
capabilities.SetCapability(MobileCapabilityType.App, App.IOSApp());
driver = new IOSDriver<IOSElement> (Env.ServerUri(), capabilities, Env.INIT_TIMEOUT_SEC);
driver.Manage().Timeouts().ImplicitWait = Env.IMPLICIT_TIMEOUT_SEC;
}
[OneTimeTearDown()]
public void AfterAll()
{
driver.Quit();
}
[Test()]
public void TestShouldFindElementsByAccessibilityId()
{
ICollection<IOSElement> elements = driver.FindElementsByAccessibilityId("ComputeSumButton");
Assert.AreEqual(1, elements.Count);
}
[Test()]
public void TestShouldFindElementsByClassName()
{
ICollection<IOSElement> elements = driver.FindElementsByClassName("XCUIElementTypeWindow");
Assert.AreEqual(2, elements.Count);
}
[Test()]
public void TestShouldFindElementsByPredicate()
{
// need newer version to use FindElementsByNSPredicate
ICollection<IOSElement> elements = driver.FindElements("-ios predicate string", "visible = 1");
Assert.AreEqual(27, elements.Count);
}
[Test()]
public void TestShouldFindElementsByClassChain()
{
// need newer version to use FindElementsByClassChain
ICollection<IOSElement> elements = driver.FindElements("-ios class chain", "XCUIElementTypeWindow[1]/*[2]");
Assert.AreEqual(1, elements.Count);
}
[Test()]
public void TestShouldFindElementsByXPath()
{
ICollection<IOSElement> elements = driver.FindElementsByXPath("//XCUIElementTypeWindow//XCUIElementTypeButton");
Assert.AreEqual(8, elements.Count);
}
}
}
@@ -0,0 +1,46 @@
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Enums;
using OpenQA.Selenium.Appium.iOS;
using OpenQA.Selenium.Remote;
using System;
using AppiumDotNetSamples.Helper;
namespace AppiumDotNetSamples
{
[TestFixture()]
public class IOSCreateSessionTest
{
private IOSDriver<IOSElement> driver;
[OneTimeSetUp()]
public void BeforeAll()
{
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.SetCapability(MobileCapabilityType.BrowserName, "");
capabilities.SetCapability(MobileCapabilityType.PlatformName, "iOS");
capabilities.SetCapability(MobileCapabilityType.PlatformVersion, App.IOSPlatformVersion());
capabilities.SetCapability(MobileCapabilityType.AutomationName, "XCUITest");
capabilities.SetCapability(MobileCapabilityType.DeviceName, App.IOSDeviceName());
capabilities.SetCapability(MobileCapabilityType.App, App.IOSApp());
driver = new IOSDriver<IOSElement>(Env.ServerUri(), capabilities, Env.INIT_TIMEOUT_SEC);
driver.Manage().Timeouts().ImplicitWait = Env.IMPLICIT_TIMEOUT_SEC;
}
[Test()]
public void TestShouldCreateAndDestroyIOSSessions()
{
IOSElement element = driver.FindElementByClassName("XCUIElementTypeApplication");
String application_name = element.GetAttribute("name");
Assert.AreEqual("TestApp", application_name);
driver.Quit();
Assert.Throws<WebDriverException>(
() => { element.GetAttribute("name"); });
}
}
}
@@ -0,0 +1,45 @@
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Enums;
using OpenQA.Selenium.Appium.iOS;
using OpenQA.Selenium.Remote;
using System;
using AppiumDotNetSamples.Helper;
namespace AppiumDotNetSamples
{
[TestFixture()]
public class IOSCreateWebSessionTest
{
private IWebDriver driver;
[OneTimeSetUp()]
public void BeforeAll()
{
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.SetCapability(MobileCapabilityType.BrowserName, "Safari");
capabilities.SetCapability(MobileCapabilityType.PlatformName, "iOS");
capabilities.SetCapability(MobileCapabilityType.PlatformVersion, App.IOSPlatformVersion());
capabilities.SetCapability(MobileCapabilityType.AutomationName, "XCUITest");
capabilities.SetCapability(MobileCapabilityType.DeviceName, App.IOSDeviceName());
driver = new IOSDriver<IWebElement>(Env.ServerUri(), capabilities, Env.INIT_TIMEOUT_SEC);
driver.Manage().Timeouts().ImplicitWait = Env.IMPLICIT_TIMEOUT_SEC;
}
[Test()]
public void TestShouldCreateAndDestroyIOSWebSessions()
{
driver.Url = "https://www.google.com";
String title = driver.Title;
Assert.AreEqual("Google", title);
driver.Quit();
Assert.Throws<WebDriverException>(
() => { title = driver.Title; });
}
}
}
+9
View File
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Appium.WebDriver" version="3.0.0.2" targetFramework="net47" />
<package id="Castle.Core" version="4.3.1" targetFramework="net47" />
<package id="Newtonsoft.Json" version="11.0.2" targetFramework="net47" />
<package id="NUnit" version="3.10.1" targetFramework="net47" />
<package id="Selenium.Support" version="3.8.0" targetFramework="net47" />
<package id="Selenium.WebDriver" version="3.8.0" targetFramework="net47" />
</packages>
+66
View File
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\NUnit.3.10.1\build\NUnit.props" Condition="Exists('..\packages\NUnit.3.10.1\build\NUnit.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{E22BA060-B513-4D45-885A-E7FEFBEEA667}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>AppiumDotNetSamples</RootNamespace>
<AssemblyName>AppiumDotNetSamples</AssemblyName>
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Drawing" />
<Reference Include="appium-dotnet-driver">
<HintPath>..\packages\Appium.WebDriver.3.0.0.2\lib\net45\appium-dotnet-driver.dll</HintPath>
</Reference>
<Reference Include="Castle.Core">
<HintPath>..\packages\Castle.Core.4.3.1\lib\net45\Castle.Core.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="nunit.framework">
<HintPath>..\packages\NUnit.3.10.1\lib\net45\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="WebDriver">
<HintPath>..\packages\Selenium.WebDriver.3.8.0\lib\net45\WebDriver.dll</HintPath>
</Reference>
<Reference Include="WebDriver.Support">
<HintPath>..\packages\Selenium.Support.3.8.0\lib\net45\WebDriver.Support.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="TestIosCreateSession.cs" />
<Compile Include="Helper.cs" />
<Compile Include="TestAndroidCreateSession.cs" />
<Compile Include="TestIosCreateWebSession.cs" />
<Compile Include="TestAndroidCreateWebSession.cs" />
<Compile Include="TestIOSBasicInteractions.cs" />
<Compile Include="TestAndroidBasicInteractions.cs" />
<Compile Include="TestAndroidSelectors.cs" />
<Compile Include="TestIOSSelectors.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>
@@ -24,7 +24,7 @@ public class IOSBasicInteractionsTest extends BaseTest {
String platformVersion = System.getenv("IOS_PLATFORM_VERSION");
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("deviceName", deviceName == null ? "iPhone 6s" : deviceName);
capabilities.setCapability("platformVerison", platformVersion == null ? "11.1" : platformVersion);
capabilities.setCapability("platformVersion", platformVersion == null ? "11.1" : platformVersion);
capabilities.setCapability("app", app.getAbsolutePath());
capabilities.setCapability("automationName", "XCUITest");
driver = new IOSDriver<WebElement>(getServiceUrl(), capabilities);
@@ -76,4 +76,4 @@ public class IOSBasicInteractionsTest extends BaseTest {
.until(ExpectedConditions.visibilityOfElementLocated(MobileBy.AccessibilityId("OK")));
okButtonElement.click();
}
}
}
@@ -22,7 +22,7 @@ public class IOSCreateSessionTest extends BaseTest {
String platformVersion = System.getenv("IOS_PLATFORM_VERSION");
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("deviceName", deviceName == null ? "iPhone 6s" : deviceName);
capabilities.setCapability("platformVerison", platformVersion == null ? "11.1" : platformVersion);
capabilities.setCapability("platformVersion", platformVersion == null ? "11.1" : platformVersion);
capabilities.setCapability("app", app.getAbsolutePath());
capabilities.setCapability("automationName", "XCUITest");
driver = new IOSDriver<WebElement>(getServiceUrl(), capabilities);
@@ -32,7 +32,7 @@ public class IOSCreateSessionTest extends BaseTest {
public void tearDown() {
driver.quit();
}
@Test
public void testCreateSession () {
// Check that the XCUIElementTypeApplication was what we expect it to be
@@ -40,4 +40,4 @@ public class IOSCreateSessionTest extends BaseTest {
String applicationName = applicationElement.getAttribute("name");
Assert.assertEquals(applicationName, "TestApp");
}
}
}
@@ -17,7 +17,7 @@ public class IOSCreateWebSessionTest extends BaseTest {
String platformVersion = System.getenv("IOS_PLATFORM_VERSION");
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("deviceName", deviceName == null ? "iPhone 6s" : deviceName);
capabilities.setCapability("platformVerison", platformVersion == null ? "11.1" : platformVersion);
capabilities.setCapability("platformVersion", platformVersion == null ? "11.1" : platformVersion);
capabilities.setCapability("browserName", "Safari");
capabilities.setCapability("automationName", "XCUITest");
driver = new IOSDriver<WebElement>(getServiceUrl(), capabilities);
@@ -38,4 +38,4 @@ public class IOSCreateWebSessionTest extends BaseTest {
Assert.assertEquals(pageTitle, "Google");
}
}
}
+2 -2
View File
@@ -21,7 +21,7 @@ public class IOSSelectorsTest extends BaseTest {
String platformVersion = System.getenv("IOS_PLATFORM_VERSION");
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("deviceName", deviceName == null ? "iPhone 6s" : deviceName);
capabilities.setCapability("platformVerison", platformVersion == null ? "11.1" : platformVersion);
capabilities.setCapability("platformVersion", platformVersion == null ? "11.1" : platformVersion);
capabilities.setCapability("app", app.getAbsolutePath());
capabilities.setCapability("automationName", "XCUITest");
driver = new IOSDriver<WebElement>(getServiceUrl(), capabilities);
@@ -69,4 +69,4 @@ public class IOSSelectorsTest extends BaseTest {
List<WebElement> buttons = driver.findElementsByXPath("//XCUIElementTypeWindow//XCUIElementTypeButton");
Assert.assertTrue(buttons.size() > 1);
};
}
}
+1 -1
View File
@@ -75,7 +75,7 @@ GEM
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.8.0)
rspec-support (3.8.0)
rubyzip (1.2.1)
rubyzip (1.2.2)
sauce_whisk (0.1.0)
json
rest-client (~> 2.0)
+89 -11
View File
@@ -4,12 +4,14 @@ import _ from 'lodash';
import chai from 'chai';
import sinon from 'sinon';
import chaiAsPromised from 'chai-as-promised';
import { getGitRev, getAppiumConfig, checkNodeOk, warnNodeDeprecations,
import { getGitRev, getBuildInfo, checkNodeOk, warnNodeDeprecations,
getNonDefaultArgs, getDeprecatedArgs, validateServerArgs,
validateTmpDir, showConfig, checkValidPort } from '../lib/config';
validateTmpDir, showConfig, checkValidPort, updateBuildInfo,
APPIUM_VER } from '../lib/config';
import getParser from '../lib/parser';
import logger from '../lib/logger';
import { fs } from 'appium-support';
import request from 'request-promise';
let should = chai.should();
chai.use(chaiAsPromised);
@@ -26,13 +28,89 @@ describe('Config', function () {
});
describe('Appium config', function () {
describe('getAppiumConfig', function () {
it('should get a configuration object', async function () {
let config = await getAppiumConfig();
config.should.be.an('object');
should.exist(config['git-sha']);
should.exist(config.built);
should.exist(config.version);
describe('getBuildInfo', function () {
async function verifyBuildInfoUpdate (useLocalGit) {
const buildInfo = await getBuildInfo();
mockFs.expects('exists').atLeast(1).returns(useLocalGit);
buildInfo['git-sha'] = undefined;
buildInfo.built = undefined;
await updateBuildInfo();
buildInfo.should.be.an('object');
should.exist(buildInfo['git-sha']);
should.exist(buildInfo.built);
should.exist(buildInfo.version);
}
let mockFs;
let getStub;
beforeEach(function () {
mockFs = sinon.mock(fs);
getStub = sinon.stub(request, 'get');
});
afterEach(function () {
getStub.restore();
mockFs.restore();
});
it('should get a configuration object if the local git metadata is present', async function () {
await verifyBuildInfoUpdate(true);
});
it('should get a configuration object if the local git metadata is not present', async function () {
getStub.onCall(0).returns([
{
"name": `v${APPIUM_VER}`,
"zipball_url": "https://api.github.com/repos/appium/appium/zipball/v1.9.0-beta.1",
"tarball_url": "https://api.github.com/repos/appium/appium/tarball/v1.9.0-beta.1",
"commit": {
"sha": "3c2752f9f9c56000705a4ae15b3ba68a5d2e644c",
"url": "https://api.github.com/repos/appium/appium/commits/3c2752f9f9c56000705a4ae15b3ba68a5d2e644c"
},
"node_id": "MDM6UmVmNzUzMDU3MDp2MS45LjAtYmV0YS4x"
},
{
"name": "v1.8.2-beta",
"zipball_url": "https://api.github.com/repos/appium/appium/zipball/v1.8.2-beta",
"tarball_url": "https://api.github.com/repos/appium/appium/tarball/v1.8.2-beta",
"commit": {
"sha": "5b98b9197e75aa85e7507d21d3126c1a63d1ce8f",
"url": "https://api.github.com/repos/appium/appium/commits/5b98b9197e75aa85e7507d21d3126c1a63d1ce8f"
},
"node_id": "MDM6UmVmNzUzMDU3MDp2MS44LjItYmV0YQ=="
}
]);
getStub.onCall(1).returns({
"sha": "3c2752f9f9c56000705a4ae15b3ba68a5d2e644c",
"node_id": "MDY6Q29tbWl0NzUzMDU3MDozYzI3NTJmOWY5YzU2MDAwNzA1YTRhZTE1YjNiYTY4YTVkMmU2NDRj",
"commit": {
"author": {
"name": "Isaac Murchie",
"email": "isaac@saucelabs.com",
"date": "2018-08-17T19:48:00Z"
},
"committer": {
"name": "Isaac Murchie",
"email": "isaac@saucelabs.com",
"date": "2018-08-17T19:48:00Z"
},
"message": "v1.9.0-beta.1",
"tree": {
"sha": "2c0974727470eba419ea0b9951c52f72f8036b18",
"url": "https://api.github.com/repos/appium/appium/git/trees/2c0974727470eba419ea0b9951c52f72f8036b18"
},
"url": "https://api.github.com/repos/appium/appium/git/commits/3c2752f9f9c56000705a4ae15b3ba68a5d2e644c",
"comment_count": 0,
"verification": {
"verified": false,
"reason": "unsigned",
"signature": null,
"payload": null
}
},
"url": "https://api.github.com/repos/appium/appium/commits/3c2752f9f9c56000705a4ae15b3ba68a5d2e644c",
"html_url": "https://github.com/appium/appium/commit/3c2752f9f9c56000705a4ae15b3ba68a5d2e644c",
"comments_url": "https://api.github.com/repos/appium/appium/commits/3c2752f9f9c56000705a4ae15b3ba68a5d2e644c/comments",
});
await verifyBuildInfoUpdate(false);
});
});
describe('showConfig', function () {
@@ -40,7 +118,7 @@ describe('Config', function () {
sinon.spy(console, "log");
});
it('should log the config to console', async function () {
let config = await getAppiumConfig();
const config = await getBuildInfo();
await showConfig();
console.log.calledOnce.should.be.true; // eslint-disable-line no-console
console.log.getCall(0).args[0].should.contain(JSON.stringify(config)); // eslint-disable-line no-console
+16
View File
@@ -37,6 +37,22 @@ describe('utils', function () {
processedJsonwpCapabilities.should.deep.equal({foo: 'bar', ...BASE_CAPS});
processedW3CCapabilities.alwaysMatch.should.deep.equal({'appium:foo': 'bar', ...insertAppiumPrefixes(BASE_CAPS)});
});
it('should rewrite default capabilities in results', function () {
const baseCapsWithDefault = Object.assign({}, BASE_CAPS, {
foo: 'baz',
'appium:foo2': 'baz2',
});
const w3cCapsWithDefault = _.cloneDeep(W3C_CAPS);
w3cCapsWithDefault.alwaysMatch.foo = 'baz';
w3cCapsWithDefault.alwaysMatch.foo2 = 'baz2';
let {desiredCaps, processedJsonwpCapabilities, processedW3CCapabilities} = parseCapsForInnerDriver(baseCapsWithDefault, w3cCapsWithDefault, {}, {
foo: 'bar',
'appium:foo2': 'bar2',
});
desiredCaps.should.deep.equal({foo: 'baz', foo2: 'baz2', ...BASE_CAPS});
processedJsonwpCapabilities.should.deep.equal({foo: 'baz', foo2: 'baz2', ...BASE_CAPS});
processedW3CCapabilities.alwaysMatch.should.deep.equal({'appium:foo': 'baz', 'appium:foo2': 'baz2', ...insertAppiumPrefixes(BASE_CAPS)});
});
it('should reject if W3C caps are not passing constraints', function () {
const err = parseCapsForInnerDriver(undefined, W3C_CAPS, {hello: {presence: true}}).error;
err.message.should.match(/'hello' can't be blank/);