mirror of
https://github.com/appium/appium.git
synced 2026-05-12 22:08:40 -05:00
@@ -1,11 +0,0 @@
|
||||
version: 1
|
||||
update_configs:
|
||||
- package_manager: "javascript"
|
||||
directory: "/"
|
||||
update_schedule: "live"
|
||||
- package_manager: "javascript"
|
||||
directory: "/sample-code/javascript-wd"
|
||||
update_schedule: "live"
|
||||
- package_manager: "javascript"
|
||||
directory: "/sample-code/javascript-webdriverio"
|
||||
update_schedule: "live"
|
||||
@@ -0,0 +1,20 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: npm
|
||||
directory: "/sample-code/javascript-webdriverio"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "11:00"
|
||||
open-pull-requests-limit: 10
|
||||
- package-ecosystem: npm
|
||||
directory: "/sample-code/javascript-wd"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "11:00"
|
||||
open-pull-requests-limit: 10
|
||||
- package-ecosystem: npm
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "11:00"
|
||||
open-pull-requests-limit: 10
|
||||
+80
-1
@@ -1,3 +1,82 @@
|
||||
CHANGES IN VERSION 1.19.0 (FROM 1.18.3)
|
||||
===================================
|
||||
|
||||
Appium 1.19.0 is a minor release
|
||||
|
||||
### Android General
|
||||
|
||||
* Add capabilities:
|
||||
* `allowDelayAdb` to prevent `-delay-adb` command line option to detect emulator startup [#14773](https://github.com/appium/appium/issues/14773)
|
||||
* Add `mobile:` functions:
|
||||
* `mobile:getContexts` to get WebView details [appium-android-driver#662](https://github.com/appium/appium-android-driver/pull/662)
|
||||
* Accept Web Authentication routes (they are part of W3C) [appium-base-driver#433](https://github.com/appium/appium-base-driver/pull/433)
|
||||
* Change to enable `ensureWebviewsHavePages` by default [appium-android-driver#652](https://github.com/appium/appium-android-driver/pull/652)
|
||||
* Change to close pending requests on the server side if the server is terminated [appium-base-driver#424](https://github.com/appium/appium-base-driver/pull/424)
|
||||
* Fix CPU performance data parsing [appium-android-driver#659](https://github.com/appium/appium-android-driver/pull/659)
|
||||
* Fix possible memory leak [appium-base-driver#430](https://github.com/appium/appium-base-driver/pull/430)
|
||||
* Fix responses to unexpected server errors [appium-base-driver#432](https://github.com/appium/appium-base-driver/pull/432)
|
||||
|
||||
### Android(UIAutomator2)
|
||||
|
||||
* Add `mobile:` functions:
|
||||
* `mobile:dragGesture`, `mobile:flingGesture`, `mobile:longClickGesture`, `mobile:pinchCloseGesture`, `mobile:pinchOpenGesture`, `mobile:swipeGesture` and `mobile:scrollGesture`: [Automating Mobile Gestures With UiAutomator2 Backend](https://appium.io/docs/en/writing-running-appium/android/android-mobile-gestures/)
|
||||
* Add css selector support [appium-uiautomator2-driver#410](https://github.com/appium/appium-uiautomator2-driver/pull/410)
|
||||
* `css selector` will be converted to `-android uiautomator` selector in UIAutomator2. Read [appium-uiautomator2-driver#410](https://github.com/appium/appium-uiautomator2-driver/pull/410) for more details.
|
||||
* Enhance XPath lookup performance [appium-uiautomator2-server#386](https://github.com/appium/appium-uiautomator2-server/pull/386)
|
||||
* Fix port guard logic to avoid port conflict [appium-uiautomator2-driver#409](https://github.com/appium/appium-uiautomator2-driver/pull/409)
|
||||
|
||||
### Android(Espresso)
|
||||
|
||||
* Raise `InvalidContextError` error when the app under test stops because of instrumentation process crashes (appium-espresso-driver#591)[https://github.com/appium/appium-espresso-driver/pull/591]
|
||||
* Add a possibility to allow JSON formatted string as `espressoBuildConfig` capability [appium-espresso-driver#609](https://github.com/appium/appium-espresso-driver/pull/609)
|
||||
* Add `mobile:` functions:
|
||||
* `mobile:registerIdlingResources`, `mobile:unregisterIdlingResources` and `mobile:listIdlingResources` to customize idling resources [appium-espresso-driver#597](https://github.com/appium/appium-espresso-driver/pull/597)
|
||||
* Add a custom exception description, which helps to debug issues when the application under test is not idling long enough [appium-espresso-driver#589](https://github.com/appium/appium-espresso-driver/pull/589)
|
||||
* The `com.google.android.material` dependency became optional [appium-espresso-driver#616](https://github.com/appium/appium-espresso-driver/pull/616)
|
||||
* `espressoBuildConfig` now allows to prevent custom dependencies for both categories: `additionalAppDependencies` and `additionalAndroidTestDependencies`. Please check [caps](https://appium.io/docs/en/writing-running-appium/caps/).
|
||||
* `mobile:navigateTo` requires `com.google.android.material`, so please add `"espressoBuildConfig": "{\"additionalAndroidTestDependencies\":[\"com.google.android.material:material:1.2.1\"]}"` as your capabilities if needed
|
||||
* Fix to exclude transitive dependencies of espresso-contrib[appium-espresso-driver#596](https://github.com/appium/appium-espresso-driver/pull/596)
|
||||
* Fix to be able to build with SDK 29 [appium-espresso-driver#604](https://github.com/appium/appium-espresso-driver/pull/604)
|
||||
|
||||
### iOS(general)
|
||||
|
||||
* Improve the performane of simulator state state checks [appium-ios-simulator#284](https://github.com/appium/appium-ios-simulator/pull/284)
|
||||
* Fix granting access to Calendar on Xcode 11.4 and later [appium-ios-simulator#288](https://github.com/appium/appium-ios-simulator/pull/288)
|
||||
* Adjust the decimal separator in the Simulator location setting script according to system locale settings [appium-ios-simulator#295](https://github.com/appium/appium-ios-simulator/pull/295)
|
||||
|
||||
### iOS(XCUITest)
|
||||
|
||||
* Add capabilities:
|
||||
* `simulatorDevicesSetPath` allows to set an alternative path to a Simulator devices set [appium-ios-simulator#290](https://github.com/appium/appium-ios-simulator/pull/290)
|
||||
* `allowProvisioningDeviceRegistration` adds `-allowProvisioningUpdates` and `-allowProvisioningDeviceRegistration` flag to the list of xcodebuild arguments [appium-xcuitest-driver#1241](https://github.com/appium/appium-xcuitest-driver/pull/1241)
|
||||
* Add mobile functions:
|
||||
* `mobile:resetPermission` to reset all previous allowed or denied permissions for the application under test. It requires Xcode 11.4 and later [https://github.com/appium/appium-xcuitest-driver#1239](https://github.com/appium/appium-xcuitest-driver/pull/1239), [WebDriverAgent#392](https://github.com/appium/WebDriverAgent/pull/392)
|
||||
* Add `velocity` argument for over Xcode 11.4 in `mobile:swipe` [appium#14793](https://github.com/appium/appium/pull/14793)
|
||||
* Enhance performance
|
||||
* e.g. improve taking snapshot to get elements [WebDriverAgent#393](https://github.com/appium/WebDriverAgent/pull/393)
|
||||
* Fix alert handling in some cases [WebDriverAgent#400](https://github.com/appium/WebDriverAgent/pull/400)
|
||||
|
||||
### Flutter
|
||||
|
||||
The version is `0.0.25`
|
||||
|
||||
### You.i Engine Driver
|
||||
|
||||
The version is `1.2.7`
|
||||
|
||||
|
||||
CHANGES IN VERSION 1.18.2 and 1.18.3 (FROM 1.18.1)
|
||||
===================================
|
||||
|
||||
Appium 1.18.2 and 1.18.3 are patch releases
|
||||
|
||||
* Supports Xcode 12 + iOS 14
|
||||
|
||||
### iOS(XCUITest)
|
||||
* Allow to input text by send keys API without focus check for newer Xcode versions [WebDriverAgent#379](https://github.com/appium/WebDriverAgent/pull/379)
|
||||
* Add supports `xcrun xctrace` instead of `instruments` command for Xcode 12+ [appium-xcuitest-driver#1223](https://github.com/appium/appium-xcuitest-driver/pull/1223)
|
||||
|
||||
|
||||
CHANGES IN VERSION 1.18.1 (FROM 1.18.0)
|
||||
===================================
|
||||
|
||||
@@ -8,7 +87,7 @@ Appium 1.18.1 is a patch release
|
||||
* Fix to avoid a redundant Chromedriver download operation if a matching driver is already present [appium-chromedriver#186](https://github.com/appium/appium-chromedriver/pull/186)
|
||||
|
||||
#### Android (UiAutomator2)
|
||||
* Fix to return a proper response for missing route [appium/appium-uiautomator2-server#373](https://github.com/appium/appium-uiautomator2-server/pull/373)
|
||||
* Fix to return a proper response for missing route [appium/appium-uiautomator2-server#373](https://github.com/appium/appium-uiautomator2-server/pull/373)
|
||||
* Fix to allow double values as touch action coordinates [ appium/appium-uiautomator2-server#372](https://github.com/appium/appium-uiautomator2-server/pull/372)
|
||||
* Fix [#14586](https://github.com/appium/appium/issues/14586) which might affect XPath locators executed on elemetns, retrieved from nested lookup requests [appium/appium-uiautomator2-server#372](https://github.com/appium/appium-uiautomator2-server/pull/371)
|
||||
* A known issue in 1.18.0
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
[](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fappium%2Fappium?ref=badge_shield)
|
||||
|
||||
Appium is an open source, cross-platform test automation tool for native,
|
||||
hybrid and mobile web and desktop apps. We support simulators (iOS), emulators
|
||||
Appium is an open-source, cross-platform test automation tool for native,
|
||||
hybrid, and mobile web and desktop apps. We support simulators (iOS), emulators
|
||||
(Android), and real devices (iOS, Android, Windows, Mac).
|
||||
|
||||
Want to skip straight to the action? Check out our [getting
|
||||
@@ -35,7 +35,7 @@ specific information about how that driver works and how to set it up:
|
||||
### Why Appium?
|
||||
|
||||
1. You don't have to recompile your app or modify it in any way, due
|
||||
to use of standard automation APIs on all platforms.
|
||||
to the use of standard automation APIs on all platforms.
|
||||
2. You can write tests with your favorite dev tools using any
|
||||
[WebDriver](https://w3c.github.io/webdriver/webdriver-spec.html)-compatible
|
||||
language such as Java, Objective-C, JavaScript (Node), PHP, Python, Ruby,
|
||||
@@ -43,22 +43,22 @@ specific information about how that driver works and how to set it up:
|
||||
client libraries](/docs/en/about-appium/appium-clients.md).
|
||||
3. You can use any testing framework.
|
||||
4. Appium has built-in mobile web and hybrid app support. Within the same
|
||||
script you can switch seamlessly between native app automation and webview
|
||||
script, you can switch seamlessly between native app automation and webview
|
||||
automation, all using the WebDriver model that's already the standard for
|
||||
web automation.
|
||||
|
||||
Investing in the
|
||||
[WebDriver](https://w3c.github.io/webdriver/webdriver-spec.html) protocol means
|
||||
you are betting on a single, free and open protocol for testing that has become
|
||||
you are betting on a single, free, and open protocol for testing that has become
|
||||
a web standard. Don't lock yourself into a proprietary stack.
|
||||
|
||||
For example, if you use Apple's XCUITest library without Appium you can only
|
||||
write tests using Obj-C/Swift, and you can only run tests through Xcode.
|
||||
Similarly, with Google's UiAutomator or Espresso you can only write tests in
|
||||
Similarly, with Google's UiAutomator or Espresso, you can only write tests in
|
||||
Java/Kotlin. Appium opens up the possibility of true cross-platform native app
|
||||
automation, for mobile and beyond. Finally!
|
||||
|
||||
If you're new to Appium, or want a more comprehensive description of what this is all
|
||||
If you're new to Appium or want a more comprehensive description of what this is all
|
||||
about, please read our [Introduction to Appium
|
||||
Concepts](/docs/en/about-appium/intro.md).
|
||||
|
||||
@@ -74,7 +74,7 @@ Node.js 10+.
|
||||
Check out our [Getting Started](/docs/en/about-appium/getting-started.md) guide
|
||||
to get going with Appium.
|
||||
|
||||
There is also sample code that contains [many examples of tests in a variety
|
||||
There is also a sample code that contains [many examples of tests in a variety
|
||||
of different languages](https://github.com/appium/appium/tree/master/sample-code)!
|
||||
|
||||
### Documentation
|
||||
@@ -92,7 +92,7 @@ Once the PR has been merged, the latest documentation will be in [appium.io](htt
|
||||
### Contributing
|
||||
|
||||
Please take a look at our [contribution documentation](CONTRIBUTING.md)
|
||||
for instructions on how to build, test and run Appium from source.
|
||||
for instructions on how to build, test, and run Appium from the source.
|
||||
|
||||
### Roadmap
|
||||
|
||||
|
||||
@@ -8,11 +8,10 @@ description:
|
||||
For information on the format of the context names, see the [get context
|
||||
documentation](/docs/en/commands/context/get-context.md).
|
||||
|
||||
On iOS, using the XCUITest driver, one can use the `mobile: getContexts`
|
||||
[mobile command](/docs/en/commands/mobile-command.md) as an alternative to
|
||||
the standard method in order to get the title and url associated with each
|
||||
context as additional metadata.
|
||||
|
||||
[mobile command](/docs/en/commands/mobile-command.md)
|
||||
`mobile: getContexts` is available on iOS (XCUITest) and Android
|
||||
(UIAutomator2 and Espresso) to get more detailed contexts.
|
||||
|
||||
For information on contexts, see Appium's [hybrid automation docs](/docs/en/writing-running-appium/web/hybrid.md).
|
||||
|
||||
example_usage:
|
||||
|
||||
@@ -9,7 +9,7 @@ description:
|
||||
There are 3 types of parameters which may be passed to this method:
|
||||
|
||||
|
||||
1. An object that looks like `{"timeout": secs}`, where `secs` is an
|
||||
1. An object that looks like `{"seconds": secs}`, where `secs` is an
|
||||
integer designating how long, in seconds, to background the app for. -1
|
||||
means to deactivate the app entirely.
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@
|
||||
| mobile:deleteFile | Delete a file on the device under test. The remote path value should be the full path or a file inside an application bundle. | `{remotePath}` | `{remotePath: '@io.appium.example/path/in/bundle'}`, `{remotePath: '/tmp/data/file'}` |
|
||||
| mobile:startService | Starts the given service by calling `am start-service` or `am start-foreground-service` under the hood since Appium 1.18.0. The `intent` argument is mandatory. `user` is the ID of the user the service should be started for (the current user ID is used by default). If `foreground` is set to `true` then the service will be started in foreground. | `{intent, user, foreground}` | `{intent: 'my.app/my.activity', foreground: true}` |
|
||||
| mobile:stopService | Stops the given service by calling `am stop-service` under the hood since Appium 1.18.0. The `intent` argument is mandatory. `user` is the ID of the user the service should be stopped for (the current user ID is used by default). | `{intent, user}` | `{intent: 'my.app/my.activity'}` |
|
||||
| mobile:getContexts | Retrieve available contexts, along with details associated with each webview (see [get contexts](/docs/en/commands/context/get-contexts.md)) in JSON structure. The details of each webview includes such as android package name and its window handles details (see [get handles](/docs/en/commands/web/window/get-handles.md)). The details of each window handle (which donated as `pages` in the JSON) contains such as url, title and id. The id is part of a window handle name. Therefore, by concatenating a string like "CDwindow-" + id, you can use the string for switching to window (see [set window](/docs/en/commands/web/window/set-window.md).) | | |
|
||||
|
||||
### Android (UiAutomator2 only)
|
||||
| Command | Description | Argument | Argument Example |
|
||||
@@ -97,6 +98,13 @@
|
||||
| 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:deviceInfo | Gets device information like manufacturer, model, timezone and locale. 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> |
|
||||
| mobile:type | Types the given Unicode string into focused field. The combination of `unicodeKeyboard` capability and the [send keys](https://appium.io/docs/en/commands/element/actions/send-keys/) works as _replacing_ the text field content. The send key in W3C action works only for ASCII. This command helps such cases to append unicode text content against the focused field. | `{text}` | `{text: 'happy testing'}` |
|
||||
| mobile:longClickGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-longclickgesture) since Appium 1.19.0 | | |
|
||||
| mobile:dragGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-draggesture) since Appium 1.19.0 | | |
|
||||
| mobile:flingGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-flinggesture) since Appium 1.19.0 | | |
|
||||
| mobile:pinchOpenGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-pinchopengesture) since Appium 1.19.0 | | |
|
||||
| mobile:pinchCloseGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-pinchclosegesture) since Appium 1.19.0 | | |
|
||||
| mobile:swipeGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-swipegesture) since Appium 1.19.0 | | |
|
||||
| mobile:scrollGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-scrollgesture) since Appium 1.19.0 | | |
|
||||
|
||||
### Android (Espresso only)
|
||||
| Command | Description | Argument | Argument Example |
|
||||
@@ -109,7 +117,7 @@
|
||||
| mobile:closeDrawer | Close drawer by [DrawerAction](https://developer.android.com/reference/android/support/test/espresso/contrib/DrawerActions) with [gravity](https://developer.android.com/reference/android/view/Gravity). `gravity` is option. The default is [GravityCompat.START](https://developer.android.com/reference/android/support/v4/view/GravityCompat.html#START). This method blocks until the drawer is fully closed. No operation if the drawer is already closed. | `{element, gravity}` | `{ element: element_id }`, `{ element: elementId, gravity: 3 }` |
|
||||
| mobile:setDate | Set date by [PickerActions#setDate](https://developer.android.com/reference/android/support/test/espresso/contrib/PickerActions.html#setDate(int,%20int,%20int)) for [DataPicker](https://developer.android.com/reference/android/widget/DatePicker). | `{element, year, monthOfYear, dayOfMonth}` | `{element: elementId, year: 2018, monthOfYear: 12, dayOfMonth: 1}` |
|
||||
| mobile:setTime | Set time by [PickerActions#setTime](https://developer.android.com/reference/android/support/test/espresso/contrib/PickerActions.html#setTime(int,%20int)) for [TimePicker](https://developer.android.com/reference/android/widget/TimePicker). | `{element, hours, minutes}` | `{element: elementId, hours: 12, minutes: 10}` |
|
||||
| mobile:navigateTo | Action to [NavigationView](https://developer.android.com/reference/android/support/design/widget/NavigationView) by [NavigationViewActions#navigateTo](https://developer.android.com/reference/android/support/test/espresso/contrib/NavigationViewActions.html#navigateTo(int)). The view must be a child of a DrawerLayout, of type NavigationView, visible on screen and displayed on screen. | `{element, menuItemId}` | `{element: elementId, menuItemId: 1}` |
|
||||
| mobile:navigateTo | Action to [NavigationView](https://developer.android.com/reference/android/support/design/widget/NavigationView) by [NavigationViewActions#navigateTo](https://developer.android.com/reference/android/support/test/espresso/contrib/NavigationViewActions.html#navigateTo(int)). The view must be a child of a DrawerLayout, of type NavigationView, visible on screen and displayed on screen. `com.google.android.material:material` as `additionalAndroidTestDependencies` capability is necessary. | `{element, menuItemId}` | `{element: elementId, menuItemId: 1}` |
|
||||
| mobile:scrollToPage | Action to [ViewPager](https://developer.android.com/reference/android/support/v4/view/ViewPager) by [ViewPagerActions](https://developer.android.com/reference/android/support/test/espresso/contrib/ViewPagerActions). `scrollTo` must be one of `first`, `last`, `left`, `right`. `scrollTo` is used by default if `scrollTo` and `scrollToPage` are provided. | `{scrollTo, scrollToPage, smoothScroll}` | `{element: elementId, scrollTo: 'left', smoothScroll: true}`, `{element: elementId, scrollToPage: 2}`|
|
||||
| mobile:backdoor | Invoke arbitrary methods defined in Android app. The methods must be `public` in Java and must be `open` in Kotlin. `target` is `activity`, `application` or `element`. `methods` are methods you would like to invoke. `element` is mandatory if `target` is `element`. Read docstring of _mobileBackdoor_ method [here](https://github.com/appium/appium-espresso-driver/blob/master/lib/commands/general.js) for more details | `{target, methods, element}` | `{target: 'activity', methods: [{name: 'method1'}, {name: 'method2', args: [{value: 'hello appium', type: 'java.lang.String'}] }] }`, `{target: 'element', element: elementId, [{name: 'getTypeface'}, {name: 'isItalic'}]}` |
|
||||
| mobile:flashElement | Flashes an element on the screen to visually confirm you are operating on the correct element. Can optionally set `durationMillis` (in ms) and `repeatCount` of animation| `{element, durationMillis, repeatCount}` | `{element: "1234-abcd-5678", durationMillis: 1000, repeatCount: 10}` |
|
||||
@@ -117,6 +125,9 @@
|
||||
| mobile:webAtoms | Runs a chain of [webatoms](https://developer.android.com/training/testing/espresso/web). `withElement` and `perform` are basic chain items. Please refer [issue](https://github.com/appium/appium-espresso-driver/pull/380) and [Sample test code](https://github.com/appium/ruby_lib_core/blob/c9062c4b744263d790c7de17263cbd4645cdefc6/test/functional/android/android/mobile_commands_test.rb#L196-L239) as references. | `{webviewElement, forceJavascriptEnabled, methodChain}` | (webdriverio) `{ webviewElement: webviewEl.value, forceJavascriptEnabled: true, methodChain: [...]}` |
|
||||
| mobile:dismissAutofill | Disable autofill dialog in Android O+ | `{element}` | `{element: elementId}` |
|
||||
| mobile:deviceInfo | Gets device information like manufacturer, model, timezone and locale. Read [GetDeviceInfo](https://github.com/appium/appium-espresso-driver/blob/master/espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/GetDeviceInfo.kt) for more details. | <none> | <none> |
|
||||
| mobile:registerIdlingResources | Registers one or more idling resources. Read [idling-resources.js](https://github.com/appium/appium-espresso-driver/blob/master/lib/commands/idling-resources.js) for more details. | `{classNames}` | `{classNames: 'com.package.MyIdlingResource1, com.package.MyIdlingResource2'}` |
|
||||
| mobile:unregisterIdlingResources | Unregisters one or more idling resources. Read [idling-resources.js](https://github.com/appium/appium-espresso-driver/blob/master/lib/commands/idling-resources.js) for more details. | `{classNames}` | `{classNames: 'com.package.MyIdlingResource1, com.package.MyIdlingResource2'}` |
|
||||
| mobile:listIdlingResources | Returns the list of canonical class names of currently registered idling resources. Read [idling-resources.js](https://github.com/appium/appium-espresso-driver/blob/master/lib/commands/idling-resources.js) for more details. | <none> | <none> |
|
||||
|
||||
|
||||
example_usage:
|
||||
|
||||
@@ -8,7 +8,7 @@ example_usage:
|
||||
Map<String, Object> caps = driver.getSessionDetails();
|
||||
python:
|
||||
|
|
||||
desired_caps = self.driver.desired_capabilities
|
||||
desired_caps = self.driver.session
|
||||
javascript_wd:
|
||||
|
|
||||
let caps = await driver.sessionCapabilities();
|
||||
@@ -30,7 +30,7 @@ example_usage:
|
||||
|
||||
client_docs:
|
||||
java: "https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/remote/server/ActiveSessions.html#get-org.openqa.selenium.remote.SessionId-"
|
||||
python: "http://selenium-python.readthedocs.io/api.html#selenium.webdriver.remote.webdriver.WebDriver.desired_capabilities"
|
||||
python: "https://appium.github.io/python-client-sphinx/webdriver.extensions.html#webdriver.extensions.session.Session.session"
|
||||
javascript_wdio: "https://webdriver.io/docs/api/jsonwp.html#getsession"
|
||||
javascript_wd: "https://github.com/admc/wd/blob/master/lib/commands.js#L227"
|
||||
ruby: "https://www.rubydoc.info/gems/selenium-webdriver/Selenium%2FWebDriver%2FRemote%2FOSS%2FBridge:session_capabilities"
|
||||
|
||||
@@ -8,7 +8,7 @@ example_usage:
|
||||
String windowHandle = driver.getWindowHandle();
|
||||
python:
|
||||
|
|
||||
window_handle = self.driver.current_window_handle()
|
||||
window_handle = self.driver.current_window_handle
|
||||
javascript_wd:
|
||||
|
|
||||
let windowHandle = await driver.windowHandle();
|
||||
|
||||
@@ -8,7 +8,7 @@ example_usage:
|
||||
Set<String> windowHandles = driver.getWindowHandles();
|
||||
python:
|
||||
|
|
||||
window_handles = self.driver.window_handles()
|
||||
window_handles = self.driver.window_handles
|
||||
javascript_wd:
|
||||
|
|
||||
let windowHandle = await driver.windowHandles();
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
The latest doc in Chinese, maintained by [TesterHome Appium 中文文档小组](https://testerhome.com/appium-doc-cn).
|
||||
The latest doc in Chinese, maintained by TesterHome Appium 中文文档小组.
|
||||
|
||||
@@ -36,4 +36,4 @@ Appium 支持多种平台以及各种测试方式(native,hybrid,web,真
|
||||
* [Windows App Testing](/docs/en/drivers/windows.md)(English)
|
||||
|
||||
|
||||
本文由 [thanksdanny](https://testerhome.com/thanksdanny) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
本文由 thanksdanny 翻译,由 lihuazhang 校验。
|
||||
|
||||
@@ -21,7 +21,7 @@ from appium import webdriver
|
||||
```center
|
||||
from selenium import webdriver
|
||||
```
|
||||
|
||||
|
||||
### 新的 desired capabilities
|
||||
|
||||
不再使用以下的 capabilities:
|
||||
@@ -165,4 +165,4 @@ set_context "WEBVIEW"
|
||||
|
||||
就是这样!快乐的迁移!
|
||||
|
||||
本文由 [高鹏](https://testerhome.com/026) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
本文由 高鹏 翻译,由 lihuazhang 校验。
|
||||
|
||||
@@ -122,5 +122,5 @@ Finally, a list of known issues with the initial 1.6 release (we'll strike throu
|
||||
|
||||
我们将尽可能添加缺失的功能,并在以后的 Appium 版本中修复其他已知问题。
|
||||
|
||||
|
||||
本文由 [校长](https://testerhome.com/xushizhao) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
|
||||
本文由 校长 翻译,由 lihuazhang 校验。
|
||||
|
||||
@@ -32,4 +32,4 @@ Appium 给用户提供了在一个机器上启动多个 Android sessions 的方
|
||||
如果你想运行 iOS 的并发测试,你需要使用 Sauce 上传你的 Appium 测试脚本,然后就可以运行多台 iOS 和 Android 的并发测试,只要你的账号允许。查看更多相关信息可以查看 [这里](https://docs.saucelabs.com/tutorials/appium/)。
|
||||
|
||||
|
||||
本文由 [thanksdanny](https://testerhome.com/thanksdanny) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
本文由 thanksdanny 翻译,由 lihuazhang 校验。
|
||||
|
||||
@@ -6,7 +6,7 @@ Appium 的 iOS 版本的后端用的是[Facebook's WebDriverAgent](https://githu
|
||||
* 如果使用了Appium的默认设置,则不需要如下的步骤。服务器将为您搞定一切,当然你也不能对WDA做太多控制。
|
||||
* 对连接的被测设备必须有SSH或物理访问权限。
|
||||
|
||||
### 安装WDA
|
||||
### 安装WDA
|
||||
|
||||
Appium 会自动下载 WebDriverAgent 源码。如果使用 npm 命令(`npm install -g appium`) 安装Appium的话,通常情况下会保存在/usr/local/lib/node_modules/appium/node_modules/appium-xcuitest-driver/WebDriverAgent 目录下。
|
||||
如果是首次安装的话,还需要下载一些第三方依赖("carthage"工具就是为此准备的: `brew install carthage`):
|
||||
@@ -33,7 +33,7 @@ npm install -g iproxy
|
||||
|
||||
Xcode 会成功构建项目并安装到真机/模拟器上,所以您将在苹果系统的桌面上看到 WebDriverAgentRunner 应用程序的图标。
|
||||
|
||||
### 启动WDA
|
||||
### 启动WDA
|
||||
|
||||
WebDriverAgent 应用程序扮演一个 REST 服务的角色,接收外部 API 请求,然后传递给被测应用的原生 XCTest 调用。如果在模拟器上运行你的测试,REST 服务的地址将是localhost,如果在有实际的 IP 地址的真实设备上运行,REST 服务的地址将是实际的 ip 地址。我们使用 iproxy 将网络请求路由到通过 USB 连接的真实设备上,这意味着可以使用这个工具将模拟器和真实设备上的 WDA 网络地址统一。
|
||||
|
||||
@@ -282,5 +282,5 @@ public class WDAServer {
|
||||
* 模拟器ID可以从 `xcrun simctl list` 输出中解析
|
||||
* UrlChecker 类是从 org.openqa.selenium.net 包导入的
|
||||
|
||||
|
||||
本文由 [simple](https://testerhome.com/simple) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
|
||||
本文由 simple 翻译,由 lihuazhang 校验。
|
||||
|
||||
@@ -146,4 +146,4 @@ npm run e2e-test
|
||||
|
||||
在提交代码之前,请运行 `npm run test` 一些基本测试,并根据代码质量标准检查您的更改。
|
||||
|
||||
本文由 [校长](https://testerhome.com/xushizhao) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
本文由 校长 翻译,由 lihuazhang 校验。
|
||||
|
||||
@@ -253,4 +253,4 @@
|
||||
- 一堆fs方法
|
||||
- plist帮助解析和更新plist文件
|
||||
|
||||
本文由 [校长](https://testerhome.com/xushizhao) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
本文由 校长 翻译,由 lihuazhang 校验。
|
||||
|
||||
@@ -42,7 +42,7 @@ Appium 使用 [Trunk Based Development](http://paulhammant.com/2013/04/05/what-i
|
||||
2. 在接下来的六个星期里,这个团队将他们的代码提交到 `master`.
|
||||
3. 7月15日,执行 RE 创建 20.1-branch。第一个节点被标记为 “20.1.0 Beta”。
|
||||
4. 一个团队成员开始修复测试版中的错误。这些修复会被提交到 `master`.
|
||||
5. 其他贡献者开始按计划提交代码到 `20.2` 中去。这些内容也会被提交到 `master`。
|
||||
5. 其他贡献者开始按计划提交代码到 `20.2` 中去。这些内容也会被提交到 `master`。
|
||||
6. RE把修复的内容cherry picks到 `20.1-branch`, 并保留 `master` 的其他变更。
|
||||
7. 该团队庆祝 8 月 1 日发布的所有测试版本都已修复。
|
||||
8. RE 标签的 HEAD 20.1-branch 为 20.1.0 发布版本。
|
||||
@@ -50,6 +50,4 @@ Appium 使用 [Trunk Based Development](http://paulhammant.com/2013/04/05/what-i
|
||||
10. 执行 RE 将主机的崩溃修复程序拉入 20.1-branch,将 HEAD 标记为 20.1.1 并发布修补程序。
|
||||
11. 一旦 20.2 发布完毕,循环就会重复。
|
||||
|
||||
本文由 [大东](https://testerhome.com/Anikikun) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
|
||||
|
||||
本文由 大东 翻译,由 lihuazhang 校验。
|
||||
|
||||
@@ -56,6 +56,6 @@ Appium 1.5 移除了大部分旧版本遗留的 CLI 标识;其余部分可转
|
||||
---
|
||||
EOF.
|
||||
|
||||
本文由 [thanksdanny](https://testerhome.com/thanksdanny) 翻译
|
||||
本文由 thanksdanny 翻译
|
||||
|
||||
Last english version: a4dd79b8144864cbc034eb97a8f0b5d744e3435c, Oct 24, 2017
|
||||
|
||||
@@ -48,7 +48,7 @@ Appium Desktop 有一个简洁的布局,由源代码树、屏幕截图、记
|
||||
---
|
||||
EOF.
|
||||
|
||||
本文由 [thanksdanny](https://testerhome.com/thanksdanny) 翻译。由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
本文由 thanksdanny 翻译。由 lihuazhang 校验。
|
||||
|
||||
翻译:@[Pandorym](https://github.com/Pandorym)
|
||||
Last english version: 438d6c3b38e785edc701354cf660aa9f76baceaf, Apr 11, 2019
|
||||
|
||||
@@ -94,7 +94,7 @@ driver.execute('mobile: doubleTap', {element: element.value.ELEMENT});
|
||||
* _duration_: 长按的持续时间(秒),浮点型。强制性参数
|
||||
* _x_: 屏幕x轴坐标点,浮点型. 仅当`element`未设置时才是强制参数
|
||||
* _y_: 屏幕y轴坐标点,浮点型. 仅当`element`未设置时才是强制参数
|
||||
|
||||
|
||||
#### 用法示例
|
||||
|
||||
```csharp
|
||||
@@ -217,4 +217,4 @@ driver.execute_script('mobile: alert', {'action': 'accept', 'buttonLabel': 'My C
|
||||
查看 [WDA Element Commands API](https://github.com/facebook/WebDriverAgent/blob/master/WebDriverAgentLib/Commands/FBElementCommands.m)
|
||||
以获取有关在Facebook WebDriverAgent中实现的手势的信息。
|
||||
|
||||
本文由 [大东](https://testerhome.com/Anikikun) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
本文由 大东 翻译,由 lihuazhang 校验。
|
||||
|
||||
@@ -91,4 +91,4 @@ git clone https://github.com/snevesbarros/SafariLauncher.git
|
||||
}
|
||||
```
|
||||
|
||||
本文由 [高鹏](https://testerhome.com/026) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
本文由 高鹏 翻译,由 lihuazhang 校验。
|
||||
|
||||
@@ -70,4 +70,4 @@ $ sudo make install
|
||||
**注意:** 为了允许建立连接,设备上需要打开 **"Web 检查器"**。在 **设置 >
|
||||
safari > 高级** 中打开。Web 检查器作为 iOS6 的一部分被添加,不在之前的版本中提供,请知悉。
|
||||
|
||||
本文由 [sanlengjingvv](https://testerhome.com/sanlengjingvv) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
本文由 sanlengjingvv 翻译,由 lihuazhang 校验。
|
||||
|
||||
@@ -190,7 +190,7 @@ const opts = {
|
||||
platformName: "Android",
|
||||
platformVersion: "8",
|
||||
deviceName: "Android Emulator",
|
||||
app: "/path/to/the/downloaded/ApiDemos.apk",
|
||||
app: "/path/to/the/downloaded/ApiDemos-debug.apk",
|
||||
appPackage: "io.appium.android.apis",
|
||||
appActivity: ".view.TextFields",
|
||||
automationName: "UiAutomator2"
|
||||
@@ -245,7 +245,7 @@ const opts = {
|
||||
platformName: "Android",
|
||||
platformVersion: "8",
|
||||
deviceName: "Android Emulator",
|
||||
app: "/path/to/the/downloaded/ApiDemos.apk",
|
||||
app: "/path/to/the/downloaded/ApiDemos-debug.apk",
|
||||
appPackage: "io.appium.android.apis",
|
||||
appActivity: ".view.TextFields",
|
||||
automationName: "UiAutomator2"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## Selenium Grid
|
||||
|
||||
You are able to register your appium server with a local [Selenium grid](https://github.com/SeleniumHQ/selenium/wiki/Grid2) ([setup docs](http://docs.seleniumhq.org/docs/07_selenium_grid.jsp)) by using the
|
||||
You are able to register your appium server with a local [Selenium grid](https://github.com/SeleniumHQ/selenium/wiki/Grid2) ([setup docs](https://www.selenium.dev/documentation/en/grid/grid_3/setting_up_your_own_grid/)) by using the
|
||||
`--nodeconfig` server parameter.
|
||||
|
||||
```center
|
||||
|
||||
@@ -22,6 +22,28 @@ Dump files are created in the same folder where the main Appium script has been
|
||||
They have the `.heapsnapshot` extension, and can be loaded into the Chrome Inspector for further investigation.
|
||||
|
||||
|
||||
#### Examples
|
||||
```shell
|
||||
# 1. Go to the directory where appium is installed via NPM using one of the two cd command below, depending on appium
|
||||
# is installed globally or locally
|
||||
## if your appium is globally installed via NPM with command "npm install -g appium":
|
||||
cd "$(npm -g root)/appium/"
|
||||
## else if your appium is locally installed via NPM:
|
||||
cd "$(npm root)/appium/"
|
||||
|
||||
# 2. Start appium server with heapsnapshot signal
|
||||
# "&" at the end puts the process at background, so we can continue working on the same terminal
|
||||
node --heapsnapshot-signal=SIGUSR2 . &
|
||||
|
||||
# 3. Get the PID of previous node process
|
||||
pid=$!
|
||||
|
||||
# 4. When it's time to dump the heap, issue a SIGUSR2 signal to the PID got above
|
||||
kill -SIGUSR2 $pid
|
||||
# Then the heap dump file is created in current directory
|
||||
```
|
||||
|
||||
|
||||
### Dump file analysis
|
||||
|
||||
Read the [Rising Stack article](https://blog.risingstack.com/finding-a-memory-leak-in-node-js/) for more details.
|
||||
|
||||
@@ -77,6 +77,7 @@ Settings are implemented via the following API endpoints:
|
||||
| `waitForIdleTimeout` | Same as: [setWaitForIdleTimeout](https://developer.android.com/reference/android/support/test/uiautomator/Configurator.html#setWaitForIdleTimeout(long)). If a negative value is given, it would set to default (10 * 1000 milliseconds). Handled by [UiAutomator Configurator](https://developer.android.com/reference/android/support/test/uiautomator/Configurator.html) in Android API 18 and above. | e.g. `10000` |
|
||||
| `waitForSelectorTimeout` | Same as: [setWaitForSelectorTimeout](https://developer.android.com/reference/android/support/test/uiautomator/Configurator.html#setWaitForSelectorTimeout(long)). If a negative value is given, it would set to default (10 * 1000 milliseconds). Handled by [UiAutomator Configurator](https://developer.android.com/reference/android/support/test/uiautomator/Configurator.html) in Android API 18 and above. | e.g. `10000` |
|
||||
| `wakeLockTimeout` | Controls the timeout of the acquired wake lock. The lock is acquired on server startup and is held until the UIAutomator2 server is killed or the timeout expires. Setting this value to zero or a negative number will release the lock immediately if it is held. Defaults to '24 * 60 * 60 * 1000' milliseconds. | e.g. `0`, `60000` (1 min) |
|
||||
|`useResourcesForOrientationDetection`| Whether to use application resource properties to figure out the current device orientation. By default the orientation value is based on the current rotation, which might be incorrect for some particular device types (usually tablets). `false` by default. Available since Appium 1.19.1 | e.g. `true` |
|
||||
|
||||
### iOS Only
|
||||
|
||||
@@ -91,7 +92,9 @@ Settings are implemented via the following API endpoints:
|
||||
|`mjpegScalingFactor`| Changes the scale of screenshots. Defaults to `100`, no scaling. Integer between `1` and `100` are available. | e.g. `1`, `50`, `100` |1.12.0+|
|
||||
|`keyboardAutocorrection`| Changes the 'Auto-Correction' preference in _Keyboards_ setting. Defaults to `false` when WDA starts as xctest. | `true`, `false` |1.14.0+|
|
||||
|`keyboardPrediction`| Changes the 'Predictive' preference in _Keyboards_ setting. Defaults to `false` when WDA starts as xctest. | `true`, `false` |1.14.0+|
|
||||
|`snapshotTimeout` | Changes the accessibility snapshots resolution timeout. _Snapshots_ are mainly used for page source generation, XML lookup and attributes retrieval. It might be necessary to increase this value if the actual page source is very large and contains hundreds of UI elements. Defaults to 15 seconds. | e.g. `10`, `100` (seconds) |1.15.0+|
|
||||
|`customSnapshotTimeout` (`snapshotTimeout` before 1.19.1) | Set how much time is allowed to resolve a single accessibility snapshot with custom attributes. _Snapshots_ are mainly used for page source generation, XML lookup and custom attributes retrieval (these are visibility and accessibility ones). It might be necessary to increase this value if the actual page source is very large and contains hundreds of UI elements. Defaults to 15 seconds. Since Appium 1.19.1 if this timeout expires and no custom snapshot could be made then WDA tries to calculate the missing attributes using its own algorithms, so setting this value to zero might speed up, for example, page source retrieval, but for the cost of some element attribute preciseness. | e.g. `10`, `100` (seconds) |1.15.0+|
|
||||
|`waitForIdleTimeout`|The amount of time in float seconds to wait until the application under test is idling. XCTest requires the app's main thread to be idling in order to execute any actions on it, so each action, like single click, might take a lot of time in case your app is constantly hogging the main thread. The default value is `10` (seconds). Setting it to zero disables idling checks completely (not recommended).|e.g. `2.5`|1.19.1+|
|
||||
|`animationCoolOffTimeout`|The amount of time in float seconds to wait until the application under test does not have any active animations. This check is usually applied after each automation action that is supposed to change the state of the application under test, like `click` one, and blocks XCTest until the transition of the tested application to a new state completes or the cool off timeout occurs. The default value is `2` (seconds). Setting it to zero disables animation checks completely.|e.g. `1.5`, `0`|1.19.1+|
|
||||
|`snapshotMaxDepth`| Changes the value of maximum depth for traversing elements source tree. It may help to prevent out of memory or timeout errors while getting the elements source tree, but it might restrict the depth of source tree. Please consider restricting this value if you observed an error like _Timed out snapshotting com.apple.testmanagerd..._ message or _Cannot get 'xml' source of the current application_ in your Appium log since they are possibly timeout related. A part of elements source tree might be lost if the value was too small. Defaults to `50` | e.g. `100` | 1.17.0+ |
|
||||
|`useFirstMatch` | Enabling this setting makes single element lookups faster, but there is the known [problem](https://github.com/appium/appium/issues/10101) related to nested elements lookup. Defaults to `false`. |`true`, `false` |1.15.0+|
|
||||
|`reduceMotion`| Changes the 'reduce motion' preference of accessibility feature. | `true`, `false` |1.15.0+|
|
||||
|
||||
@@ -57,7 +57,7 @@ List<string> AllContexts = new List<string>();
|
||||
## Description
|
||||
|
||||
Retrieve all the contexts available to be automated. This will include, at least, the native context. There can also be zero or more web view contexts. For information on the format of the context names, see the [get context documentation](/docs/en/commands/context/get-context.md).
|
||||
On iOS, using the XCUITest driver, one can use the `mobile: getContexts` [mobile command](/docs/en/commands/mobile-command.md) as an alternative to the standard method in order to get the title and url associated with each context as additional metadata.
|
||||
[mobile command](/docs/en/commands/mobile-command.md) `mobile: getContexts` is available on iOS (XCUITest) and Android (UIAutomator2 and Espresso) to get more detailed contexts.
|
||||
For information on contexts, see Appium's [hybrid automation docs](/docs/en/writing-running-appium/web/hybrid.md).
|
||||
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ driver.BackgroundApp(10)
|
||||
Send the currently active app to the background, and either return after a certain amount of time, or leave the app deactivated.
|
||||
There are 3 types of parameters which may be passed to this method:
|
||||
|
||||
1. An object that looks like `{"timeout": secs}`, where `secs` is an
|
||||
1. An object that looks like `{"seconds": secs}`, where `secs` is an
|
||||
integer designating how long, in seconds, to background the app for. -1
|
||||
means to deactivate the app entirely.
|
||||
|
||||
|
||||
@@ -131,6 +131,7 @@ List of available commands:
|
||||
| mobile:deleteFile | Delete a file on the device under test. The remote path value should be the full path or a file inside an application bundle. | `{remotePath}` | `{remotePath: '@io.appium.example/path/in/bundle'}`, `{remotePath: '/tmp/data/file'}` |
|
||||
| mobile:startService | Starts the given service by calling `am start-service` or `am start-foreground-service` under the hood since Appium 1.18.0. The `intent` argument is mandatory. `user` is the ID of the user the service should be started for (the current user ID is used by default). If `foreground` is set to `true` then the service will be started in foreground. | `{intent, user, foreground}` | `{intent: 'my.app/my.activity', foreground: true}` |
|
||||
| mobile:stopService | Stops the given service by calling `am stop-service` under the hood since Appium 1.18.0. The `intent` argument is mandatory. `user` is the ID of the user the service should be stopped for (the current user ID is used by default). | `{intent, user}` | `{intent: 'my.app/my.activity'}` |
|
||||
| mobile:getContexts | Retrieve available contexts, along with details associated with each webview (see [get contexts](/docs/en/commands/context/get-contexts.md)) in JSON structure. The details of each webview includes such as android package name and its window handles details (see [get handles](/docs/en/commands/web/window/get-handles.md)). The details of each window handle (which donated as `pages` in the JSON) contains such as url, title and id. The id is part of a window handle name. Therefore, by concatenating a string like "CDwindow-" + id, you can use the string for switching to window (see [set window](/docs/en/commands/web/window/set-window.md).) | | |
|
||||
|
||||
### Android (UiAutomator2 only)
|
||||
| Command | Description | Argument | Argument Example |
|
||||
@@ -145,6 +146,13 @@ List of available commands:
|
||||
| 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:deviceInfo | Gets device information like manufacturer, model, timezone and locale. 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> |
|
||||
| mobile:type | Types the given Unicode string into focused field. The combination of `unicodeKeyboard` capability and the [send keys](https://appium.io/docs/en/commands/element/actions/send-keys/) works as _replacing_ the text field content. The send key in W3C action works only for ASCII. This command helps such cases to append unicode text content against the focused field. | `{text}` | `{text: 'happy testing'}` |
|
||||
| mobile:longClickGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-longclickgesture) since Appium 1.19.0 | | |
|
||||
| mobile:dragGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-draggesture) since Appium 1.19.0 | | |
|
||||
| mobile:flingGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-flinggesture) since Appium 1.19.0 | | |
|
||||
| mobile:pinchOpenGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-pinchopengesture) since Appium 1.19.0 | | |
|
||||
| mobile:pinchCloseGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-pinchclosegesture) since Appium 1.19.0 | | |
|
||||
| mobile:swipeGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-swipegesture) since Appium 1.19.0 | | |
|
||||
| mobile:scrollGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-scrollgesture) since Appium 1.19.0 | | |
|
||||
|
||||
### Android (Espresso only)
|
||||
| Command | Description | Argument | Argument Example |
|
||||
@@ -157,7 +165,7 @@ List of available commands:
|
||||
| mobile:closeDrawer | Close drawer by [DrawerAction](https://developer.android.com/reference/android/support/test/espresso/contrib/DrawerActions) with [gravity](https://developer.android.com/reference/android/view/Gravity). `gravity` is option. The default is [GravityCompat.START](https://developer.android.com/reference/android/support/v4/view/GravityCompat.html#START). This method blocks until the drawer is fully closed. No operation if the drawer is already closed. | `{element, gravity}` | `{ element: element_id }`, `{ element: elementId, gravity: 3 }` |
|
||||
| mobile:setDate | Set date by [PickerActions#setDate](https://developer.android.com/reference/android/support/test/espresso/contrib/PickerActions.html#setDate(int,%20int,%20int)) for [DataPicker](https://developer.android.com/reference/android/widget/DatePicker). | `{element, year, monthOfYear, dayOfMonth}` | `{element: elementId, year: 2018, monthOfYear: 12, dayOfMonth: 1}` |
|
||||
| mobile:setTime | Set time by [PickerActions#setTime](https://developer.android.com/reference/android/support/test/espresso/contrib/PickerActions.html#setTime(int,%20int)) for [TimePicker](https://developer.android.com/reference/android/widget/TimePicker). | `{element, hours, minutes}` | `{element: elementId, hours: 12, minutes: 10}` |
|
||||
| mobile:navigateTo | Action to [NavigationView](https://developer.android.com/reference/android/support/design/widget/NavigationView) by [NavigationViewActions#navigateTo](https://developer.android.com/reference/android/support/test/espresso/contrib/NavigationViewActions.html#navigateTo(int)). The view must be a child of a DrawerLayout, of type NavigationView, visible on screen and displayed on screen. | `{element, menuItemId}` | `{element: elementId, menuItemId: 1}` |
|
||||
| mobile:navigateTo | Action to [NavigationView](https://developer.android.com/reference/android/support/design/widget/NavigationView) by [NavigationViewActions#navigateTo](https://developer.android.com/reference/android/support/test/espresso/contrib/NavigationViewActions.html#navigateTo(int)). The view must be a child of a DrawerLayout, of type NavigationView, visible on screen and displayed on screen. `com.google.android.material:material` as `additionalAndroidTestDependencies` capability is necessary. | `{element, menuItemId}` | `{element: elementId, menuItemId: 1}` |
|
||||
| mobile:scrollToPage | Action to [ViewPager](https://developer.android.com/reference/android/support/v4/view/ViewPager) by [ViewPagerActions](https://developer.android.com/reference/android/support/test/espresso/contrib/ViewPagerActions). `scrollTo` must be one of `first`, `last`, `left`, `right`. `scrollTo` is used by default if `scrollTo` and `scrollToPage` are provided. | `{scrollTo, scrollToPage, smoothScroll}` | `{element: elementId, scrollTo: 'left', smoothScroll: true}`, `{element: elementId, scrollToPage: 2}`|
|
||||
| mobile:backdoor | Invoke arbitrary methods defined in Android app. The methods must be `public` in Java and must be `open` in Kotlin. `target` is `activity`, `application` or `element`. `methods` are methods you would like to invoke. `element` is mandatory if `target` is `element`. Read docstring of _mobileBackdoor_ method [here](https://github.com/appium/appium-espresso-driver/blob/master/lib/commands/general.js) for more details | `{target, methods, element}` | `{target: 'activity', methods: [{name: 'method1'}, {name: 'method2', args: [{value: 'hello appium', type: 'java.lang.String'}] }] }`, `{target: 'element', element: elementId, [{name: 'getTypeface'}, {name: 'isItalic'}]}` |
|
||||
| mobile:flashElement | Flashes an element on the screen to visually confirm you are operating on the correct element. Can optionally set `durationMillis` (in ms) and `repeatCount` of animation| `{element, durationMillis, repeatCount}` | `{element: "1234-abcd-5678", durationMillis: 1000, repeatCount: 10}` |
|
||||
@@ -165,6 +173,9 @@ List of available commands:
|
||||
| mobile:webAtoms | Runs a chain of [webatoms](https://developer.android.com/training/testing/espresso/web). `withElement` and `perform` are basic chain items. Please refer [issue](https://github.com/appium/appium-espresso-driver/pull/380) and [Sample test code](https://github.com/appium/ruby_lib_core/blob/c9062c4b744263d790c7de17263cbd4645cdefc6/test/functional/android/android/mobile_commands_test.rb#L196-L239) as references. | `{webviewElement, forceJavascriptEnabled, methodChain}` | (webdriverio) `{ webviewElement: webviewEl.value, forceJavascriptEnabled: true, methodChain: [...]}` |
|
||||
| mobile:dismissAutofill | Disable autofill dialog in Android O+ | `{element}` | `{element: elementId}` |
|
||||
| mobile:deviceInfo | Gets device information like manufacturer, model, timezone and locale. Read [GetDeviceInfo](https://github.com/appium/appium-espresso-driver/blob/master/espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/GetDeviceInfo.kt) for more details. | <none> | <none> |
|
||||
| mobile:registerIdlingResources | Registers one or more idling resources. Read [idling-resources.js](https://github.com/appium/appium-espresso-driver/blob/master/lib/commands/idling-resources.js) for more details. | `{classNames}` | `{classNames: 'com.package.MyIdlingResource1, com.package.MyIdlingResource2'}` |
|
||||
| mobile:unregisterIdlingResources | Unregisters one or more idling resources. Read [idling-resources.js](https://github.com/appium/appium-espresso-driver/blob/master/lib/commands/idling-resources.js) for more details. | `{classNames}` | `{classNames: 'com.package.MyIdlingResource1, com.package.MyIdlingResource2'}` |
|
||||
| mobile:listIdlingResources | Returns the list of canonical class names of currently registered idling resources. Read [idling-resources.js](https://github.com/appium/appium-espresso-driver/blob/master/lib/commands/idling-resources.js) for more details. | <none> | <none> |
|
||||
|
||||
|
||||
[//]: # (DO NOT EDIT THIS FILE! This is an auto-generated file. Editing for this document happens in /commands-yml/commands/mobile-command.yml)
|
||||
|
||||
@@ -15,7 +15,12 @@ driver.executeDriverScript(script, new ScriptOptions().withTimeout(200));
|
||||
|
||||
```python
|
||||
# Python
|
||||
# TODO fill out once client code is written
|
||||
import textwrap
|
||||
script = """
|
||||
const el = await driver.$('~foo');
|
||||
await el.click();
|
||||
"""
|
||||
response = driver.execute_driver(script=textwrap.dedent(script))
|
||||
|
||||
```
|
||||
|
||||
@@ -40,10 +45,18 @@ await driver.executeDriver(script, {timeout: 200});
|
||||
```ruby
|
||||
# Ruby
|
||||
# ruby_lib example
|
||||
# TODO fill out once client code is written
|
||||
script = <<-SCRIPT
|
||||
const status = await driver.status();
|
||||
return status;
|
||||
SCRIPT
|
||||
driver.execute_driver script: script
|
||||
|
||||
# ruby_lib_core example
|
||||
# TODO fill out once client code is written
|
||||
script = <<-SCRIPT
|
||||
const status = await driver.status();
|
||||
return status;
|
||||
SCRIPT
|
||||
@driver.execute_driver script: script
|
||||
|
||||
```
|
||||
|
||||
@@ -118,10 +131,10 @@ The advantage of this approach of using WebdriverIO code is that you have access
|
||||
|Language|Support|Documentation|
|
||||
|--------|-------|-------------|
|
||||
|[Java](https://github.com/appium/java-client/releases/latest)| All | [javadoc.io](https://javadoc.io/page/io.appium/java-client/latest/io/appium/java_client/ExecutesDriverScript.html#executeDriverScript-java.lang.String-io.appium.java_client.driverscripts.ScriptOptions-) |
|
||||
|[Python](https://github.com/appium/python-client/releases/latest)| None | |
|
||||
|[Python](https://github.com/appium/python-client/releases/latest)| None | [appium.github.io](https://appium.github.io/python-client-sphinx/webdriver.extensions.html#module-webdriver.extensions.execute_driver) |
|
||||
|[Javascript (WebdriverIO)](http://webdriver.io/index.html)| None | |
|
||||
|[Javascript (WD)](https://github.com/admc/wd/releases/latest)| None | |
|
||||
|[Ruby](https://github.com/appium/ruby_lib/releases/latest)| None | |
|
||||
|[Ruby](https://github.com/appium/ruby_lib/releases/latest)| None | [www.rubydoc.info](https://www.rubydoc.info/github/appium/ruby_lib/master/Appium/Driver#execute_driver-instance_method) |
|
||||
|[PHP](https://github.com/appium/php-client/releases/latest)| None | |
|
||||
|[C#](https://github.com/appium/appium-dotnet-driver/releases/latest)| None | |
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ Map<String, Object> caps = driver.getSessionDetails();
|
||||
|
||||
```python
|
||||
# Python
|
||||
desired_caps = self.driver.desired_capabilities
|
||||
desired_caps = self.driver.session
|
||||
|
||||
```
|
||||
|
||||
@@ -73,7 +73,7 @@ ICapabilities caps = driver.Capabilities;
|
||||
|Language|Support|Documentation|
|
||||
|--------|-------|-------------|
|
||||
|[Java](https://github.com/appium/java-client/releases/latest)| All | [seleniumhq.github.io](https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/remote/server/ActiveSessions.html#get-org.openqa.selenium.remote.SessionId-) |
|
||||
|[Python](https://github.com/appium/python-client/releases/latest)| All | [selenium-python.readthedocs.io](http://selenium-python.readthedocs.io/api.html#selenium.webdriver.remote.webdriver.WebDriver.desired_capabilities) |
|
||||
|[Python](https://github.com/appium/python-client/releases/latest)| All | [appium.github.io](https://appium.github.io/python-client-sphinx/webdriver.extensions.html#webdriver.extensions.session.Session.session) |
|
||||
|[Javascript (WebdriverIO)](http://webdriver.io/index.html)| All | |
|
||||
|[Javascript (WD)](https://github.com/admc/wd/releases/latest)| All | [github.com](https://github.com/admc/wd/blob/master/lib/commands.js#L227) |
|
||||
|[Ruby](https://github.com/appium/ruby_lib/releases/latest)| All | [www.rubydoc.info](https://www.rubydoc.info/gems/selenium-webdriver/Selenium%2FWebDriver%2FRemote%2FOSS%2FBridge:session_capabilities) |
|
||||
|
||||
@@ -13,7 +13,7 @@ String windowHandle = driver.getWindowHandle();
|
||||
|
||||
```python
|
||||
# Python
|
||||
window_handle = self.driver.current_window_handle()
|
||||
window_handle = self.driver.current_window_handle
|
||||
|
||||
```
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ Set<String> windowHandles = driver.getWindowHandles();
|
||||
|
||||
```python
|
||||
# Python
|
||||
window_handles = self.driver.window_handles()
|
||||
window_handles = self.driver.window_handles
|
||||
|
||||
```
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ Appium also supports Android automation using the
|
||||
|
||||
In addition to Appium's general requirements:
|
||||
|
||||
* Java 7 installed and configured correctly for your platform
|
||||
* Java 8 installed and configured correctly for your platform
|
||||
* Mac, Windows, or Linux OS with the ability to run the Android SDK
|
||||
|
||||
### Usage
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
## The Gecko Driver
|
||||
|
||||
Gecko driver has been added to Appium since version 1.20. This driver
|
||||
is a wrapper over Mozilla's [geckodriver](https://firefox-source-docs.mozilla.org/testing/geckodriver/)
|
||||
binary, which implements communication with either
|
||||
desktop/mobile Gecko-based browsers like Firefox or Gecko-based web views (on mobile)
|
||||
via [W3C WebDriver protocol](https://www.w3.org/TR/webdriver/).
|
||||
|
||||
Development of the Gecko driver happens at the
|
||||
[appium-geckodriver](https://github.com/appium/appium-geckodriver)
|
||||
repo.
|
||||
|
||||
|
||||
### Requirements and Support
|
||||
|
||||
In addition to Appium's general requirements:
|
||||
|
||||
* A supported browser must be installed on the destination platform. The recent browser releases (both desktop and mobile) could be retrieved from the official [download page](https://www.mozilla.org/en-GB/firefox/all/).
|
||||
* The corresponding executable driver binary for the target platform must be available in `PATH` under `geckodriver` (`geckodriver.exe` in Windows) name. Geckodriver build for different supported platforms could be retrieved from the GitHub [Releases page](https://github.com/mozilla/geckodriver/releases).
|
||||
* Windows (32/64 bit), Linux (32/64 bit) or macOS are supported as the host platforms.
|
||||
* [Android SDK](https://developer.android.com/studio) must be installed if it is necessary to communicate with the browser or a web view on mobile devices. Also, the Emulator SDK is needed if automated tests are going to use Android emulators. For real Android devices it is necessary to make sure they have the `online` status to in the `adb devices -l` output. The device's screen must not be locked.
|
||||
|
||||
|
||||
### Usage
|
||||
|
||||
The way to start a session using the Gecko driver is to include the
|
||||
`automationName` capability in your new session request, with
|
||||
the value `Gecko`. Of course, you must also include appropriate
|
||||
`platformName` (`Mac`/`Android`/`Windows`/`Linux`). Read
|
||||
https://github.com/appium/appium-geckodriver/blob/master/README.md for
|
||||
more details.
|
||||
|
||||
### Capabilities
|
||||
|
||||
The list of available driver capabilities could be found at
|
||||
https://github.com/appium/appium-geckodriver/blob/master/README.md
|
||||
@@ -0,0 +1,47 @@
|
||||
## The Safari Driver
|
||||
|
||||
Safari driver has been added to Appium since version 1.20. This driver
|
||||
is a wrapper over Apple's [safaridriver](https://developer.apple.com/documentation/webkit/testing_with_webdriver_in_safari?language=objc)
|
||||
binary, which is included to the standard macOS distribution and implements communication with either
|
||||
desktop or mobile Safari browser via [W3C WebDriver protocol](https://www.w3.org/TR/webdriver/).
|
||||
|
||||
Development of the Safari driver happens at the
|
||||
[appium-safari-driver](https://github.com/appium/appium-safari-driver)
|
||||
repo.
|
||||
|
||||
Appium also supports mobile Safari automation and Safari web views automation using the
|
||||
[appium-remote-debugger](https://github.com/appium/appium-remote-debugger). This module is
|
||||
used to implement context switching feature with XCUITest automation name. It is more universal
|
||||
and flexible than the current Safari driver, however it is using the undocumented WebKit communication
|
||||
protocol under the hood. Which means the development of it and the effort to keep it in working state
|
||||
is comparably high while the list of supported features is smaller. On the other hand `safaridriver`
|
||||
is maintained by Apple, which means it is always in sync with the most recent browser
|
||||
and communication protocol requirements.
|
||||
|
||||
Long story short, it makes sense to prefer this driver if it is necessary to only automate
|
||||
a web application that only works in Safari browser (either mobile or desktop). In case it is necessary to interact with native context or switch between different applications/contexts while automating your scenario
|
||||
then the obvious choice would be either [XCUITest Driver](/docs/en/drivers/ios-xcuitest.md)
|
||||
(for the mobile platform) or [Mac driver](/docs/en/drivers/mac.md) (for the desktop platform).
|
||||
|
||||
### Requirements and Support
|
||||
|
||||
In addition to Appium's general requirements:
|
||||
|
||||
* Run the `safaridriver --enable` command from the macOS terminal and provide your administrator password before any automated session will be executed. This only should be done once.
|
||||
* In order to automate Safari on real devices it is necessary to enable Remote Automation switch in `Settings → Safari → Advanced → Remote Automation` for these particular devices and trust them on the target host. The device's screen must not be locked while starting tests.
|
||||
* Only macOS (High Sierra or newer) is supported as the host platform.
|
||||
* Only iOS 13 and newer is supported for mobile browser automation. As it is stated in the WebKit blog [article](https://webkit.org/blog/9395/webdriver-is-coming-to-safari-in-ios-13/), Safari for iOS does not allow WebDriver-initiated navigations to be handled outside of Safari. In other words, clicking a tel:// link will not offer to place a phone call, and clicking an app store link will not redirect the user to the App Store. Similarly, non-Safari content displayed by the system–such as update dialogs, app notifications, incoming calls, etc.–are suppressed while a WebDriver session is active.
|
||||
|
||||
### Usage
|
||||
|
||||
The way to start a session using the Safari driver is to include the
|
||||
`automationName` capability in your new session request, with
|
||||
the value `Safari`. Of course, you must also include appropriate
|
||||
`platformName` (`Mac` or `iOS`). Read
|
||||
https://github.com/appium/appium-safari-driver/blob/master/README.md for
|
||||
more details.
|
||||
|
||||
### Capabilities
|
||||
|
||||
The list of available driver capabilities could be found at
|
||||
https://github.com/appium/appium-safari-driver/blob/master/README.md
|
||||
@@ -12,33 +12,42 @@ We can get distributed apk files from the `.aab` file via the CLI. Using the gen
|
||||
2. Generate the `.apks` file from the `.aab` file
|
||||
- The `.aab` is available over Android Studio 3.2
|
||||
- You must sign correctly when you generate `.apks` from `.aab`. This step requires data signing.
|
||||
```bash
|
||||
$ java -jar apks/bundletool.jar build-apks \
|
||||
--bundle apks/release/release/app.aab \ # A generated aab file
|
||||
--output apks/AppBundleSample.apks \ # An apks file you'd like to out put to
|
||||
--ks apks/sign \ # Signing keystore
|
||||
--ks-key-alias key0 \ # Alias of the keytstore
|
||||
--ks-pass pass:kazucocoa \ # Password of the keystore
|
||||
--overwrite # Overwrite any existing apks files
|
||||
```
|
||||
3. Use the path to the `.apks` file as your `app` capability.
|
||||
```ruby
|
||||
desired_capability = caps: {
|
||||
platformName: :android,
|
||||
automationName: 'uiautomator2',
|
||||
platformVersion: '8.1',
|
||||
deviceName: 'Android Emulator',
|
||||
app: "path/to/your.apks", # This line is important
|
||||
fullReset: true,
|
||||
...
|
||||
}
|
||||
|
||||
core = ::Appium::Core.for(desired_capability)
|
||||
driver = core.start_driver
|
||||
```
|
||||
```bash
|
||||
$ java -jar apks/bundletool.jar build-apks \
|
||||
--bundle apks/release/release/app.aab \ # A generated aab file
|
||||
--output apks/AppBundleSample.apks \ # An apks file you'd like to out put to
|
||||
--ks apks/sign \ # Signing keystore
|
||||
--ks-key-alias key0 \ # Alias of the keytstore
|
||||
--ks-pass pass:kazucocoa \ # Password of the keystore
|
||||
--overwrite # Overwrite any existing apks files
|
||||
```
|
||||
|
||||
3. Use the path to the `.apks` file as your `app` capability.
|
||||
|
||||
```ruby
|
||||
desired_capability = caps: {
|
||||
platformName: :android,
|
||||
automationName: 'uiautomator2',
|
||||
platformVersion: '8.1',
|
||||
deviceName: 'Android Emulator',
|
||||
app: "path/to/your.apks", # This line is important
|
||||
fullReset: true,
|
||||
...
|
||||
}
|
||||
|
||||
core = ::Appium::Core.for(desired_capability)
|
||||
driver = core.start_driver
|
||||
```
|
||||
|
||||
You can find another way to get test APKs in https://developer.android.com/guide/app-bundle/
|
||||
|
||||
You could also install `.apks` bundles via [Install App](/docs/en/commands/device/app/install-app.md) command like below.
|
||||
|
||||
```ruby
|
||||
driver.install_app 'path/to/your.apks'
|
||||
```
|
||||
|
||||
## Tips
|
||||
### Make `bundletool.jar` executable
|
||||
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
## Automating Mobile Gestures With UiAutomator2 Backend
|
||||
|
||||
Touch actions are the most advanced and the most complicated way to
|
||||
implement any Android gesture. Although, there is a couple of basic
|
||||
gestures, like swipe, fling or pinch, which are commonly used in
|
||||
Android applications and for which it makes sense to have shortcuts,
|
||||
where only high-level options are configurable.
|
||||
|
||||
|
||||
### mobile: longClickGesture
|
||||
|
||||
This gesture performs long click action on the given element/coordinates.
|
||||
Available since Appium v1.19
|
||||
|
||||
#### Supported arguments
|
||||
|
||||
* _elementId_: The id of the element to be clicked.
|
||||
If the element is missing then both click offset coordinates must be provided.
|
||||
If both the element id and offset are provided then the coordinates
|
||||
are parsed as relative offsets from the top left corner of the element.
|
||||
* _x_: The x-offset coordinate
|
||||
* _y_: The y-offset coordinate
|
||||
* _duration_: Click duration in milliseconds. `500` by default. The value must not be negative
|
||||
|
||||
#### Usage examples
|
||||
|
||||
```java
|
||||
// Java
|
||||
((JavascriptExecutor) driver).executeScript("mobile: longClickGesture", ImmutableMap.of(
|
||||
"elementId", ((RemoteWebElement) element).getId()
|
||||
));
|
||||
```
|
||||
|
||||
```python
|
||||
# Python
|
||||
driver.execute_script('mobile: longClickGesture', {'x': 100, 'y': 100, 'duration': 1000})
|
||||
```
|
||||
|
||||
|
||||
### mobile: dragGesture
|
||||
|
||||
This gesture performs drag action from the given element/coordinates to the given point.
|
||||
Available since Appium v1.19
|
||||
|
||||
#### Supported arguments
|
||||
|
||||
* _elementId_: The id of the element to be dragged.
|
||||
If the element id is missing then both start coordinates must be provided.
|
||||
If both the element id and the start coordinates are provided then these
|
||||
coordinates are considered as offsets from the top left element corner.
|
||||
* _startX_: The x-start coordinate
|
||||
* _startY_: The y-start coordinate
|
||||
* _endX_: The x-end coordinate. Mandatory argument
|
||||
* _endY_: The y-end coordinate. Mandatory argument
|
||||
* _speed_: The speed at which to perform this gesture in pixels per second.
|
||||
The value must not be negative. The default value is `2500 * displayDensity`
|
||||
|
||||
#### Usage examples
|
||||
|
||||
```java
|
||||
// Java
|
||||
((JavascriptExecutor) driver).executeScript("mobile: dragGesture", ImmutableMap.of(
|
||||
"elementId", ((RemoteWebElement) element).getId(),
|
||||
"endX", 100,
|
||||
"endY", 100
|
||||
));
|
||||
```
|
||||
|
||||
|
||||
### mobile: flingGesture
|
||||
|
||||
This gesture performs fling gesture on the given element/area.
|
||||
Available since Appium v1.19
|
||||
|
||||
#### Supported arguments
|
||||
|
||||
* _elementId_: The id of the element to be flinged.
|
||||
If the element id is missing then fling bounding area must be provided.
|
||||
If both the element id and the fling bounding area are provided then this
|
||||
area is effectively ignored.
|
||||
* _left_: The left coordinate of the fling bounding area
|
||||
* _top_: The top coordinate of the fling bounding area
|
||||
* _width_: The width of the fling bounding area
|
||||
* _height_: The height of the fling bounding area
|
||||
* _direction_: Direction of the fling. Mandatory value.
|
||||
Acceptable values are: `up`, `down`, `left` and `right` (case insensitive)
|
||||
* _speed_: The speed at which to perform this
|
||||
gesture in pixels per second. The value must be greater than the minimum fling
|
||||
velocity for the given view (50 by default). The default value is `7500 * displayDensity`
|
||||
|
||||
#### Returned value
|
||||
|
||||
The returned value is a boolean one and equals to `true` if the object can still scroll in the given direction
|
||||
|
||||
#### Usage examples
|
||||
|
||||
```java
|
||||
// Java
|
||||
boolean canScrollMore = (Boolean) ((JavascriptExecutor) driver).executeScript("mobile: flingGesture", ImmutableMap.of(
|
||||
"elementId", ((RemoteWebElement) element).getId(),
|
||||
"direction", "down",
|
||||
"speed", 500
|
||||
));
|
||||
```
|
||||
|
||||
|
||||
### mobile: pinchOpenGesture
|
||||
|
||||
This gesture performs pinch-open gesture on the given element/area.
|
||||
Available since Appium v1.19
|
||||
|
||||
#### Supported arguments
|
||||
|
||||
* _elementId_: The id of the element to be pinched.
|
||||
If the element id is missing then pinch bounding area must be provided.
|
||||
If both the element id and the pinch bounding area are provided then the
|
||||
area is effectively ignored.
|
||||
* _left_: The left coordinate of the pinch bounding area
|
||||
* _top_: The top coordinate of the pinch bounding area
|
||||
* _width_: The width of the pinch bounding area
|
||||
* _height_: The height of the pinch bounding area
|
||||
* _percent_: The size of the pinch as a percentage of the pinch area size.
|
||||
Valid values must be float numbers in range 0..1, where 1.0 is 100%.
|
||||
Mandatory value.
|
||||
* _speed_: The speed at which to perform this gesture in pixels per second.
|
||||
The value must not be negative. The default value is `2500 * displayDensity`
|
||||
|
||||
#### Usage examples
|
||||
|
||||
```java
|
||||
// Java
|
||||
((JavascriptExecutor) driver).executeScript("mobile: pinchOpenGesture", ImmutableMap.of(
|
||||
"elementId", ((RemoteWebElement) element).getId(),
|
||||
"percent", 0.75
|
||||
));
|
||||
```
|
||||
|
||||
|
||||
### mobile: pinchCloseGesture
|
||||
|
||||
This gesture performs pinch-close gesture on the given element/area.
|
||||
Available since Appium v1.19
|
||||
|
||||
#### Supported arguments
|
||||
|
||||
* _elementId_: The id of the element to be pinched.
|
||||
If the element id is missing then pinch bounding area must be provided.
|
||||
If both the element id and the pinch bounding area are provided then the
|
||||
area is effectively ignored.
|
||||
* _left_: The left coordinate of the pinch bounding area
|
||||
* _top_: The top coordinate of the pinch bounding area
|
||||
* _width_: The width of the pinch bounding area
|
||||
* _height_: The height of the pinch bounding area
|
||||
* _percent_: The size of the pinch as a percentage of the pinch area size.
|
||||
Valid values must be float numbers in range 0..1, where 1.0 is 100%.
|
||||
Mandatory value.
|
||||
* _speed_: The speed at which to perform this gesture in pixels per second.
|
||||
The value must not be negative. The default value is `2500 * displayDensity`
|
||||
|
||||
#### Usage examples
|
||||
|
||||
```java
|
||||
// Java
|
||||
((JavascriptExecutor) driver).executeScript("mobile: pinchCloseGesture", ImmutableMap.of(
|
||||
"elementId", ((RemoteWebElement) element).getId(),
|
||||
"percent", 0.75
|
||||
));
|
||||
```
|
||||
|
||||
```python
|
||||
# Python
|
||||
can_scroll_more = driver.execute_script('mobile: pinchCloseGesture', {
|
||||
'elementId': element.id,
|
||||
'percent': 0.75
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
### mobile: swipeGesture
|
||||
|
||||
This gesture performs swipe gesture on the given element/area.
|
||||
Available since Appium v1.19
|
||||
|
||||
#### Supported arguments
|
||||
|
||||
* _elementId_: The id of the element to be swiped.
|
||||
If the element id is missing then swipe bounding area must be provided.
|
||||
If both the element id and the swipe bounding area are provided then the
|
||||
area is effectively ignored.
|
||||
* _left_: The left coordinate of the swipe bounding area
|
||||
* _top_: The top coordinate of the swipe bounding area
|
||||
* _width_: The width of the swipe bounding area
|
||||
* _height_: The height of the swipe bounding area
|
||||
* _direction_: Swipe direction. Mandatory value.
|
||||
Acceptable values are: `up`, `down`, `left` and `right` (case insensitive)
|
||||
* _percent_: The size of the swipe as a percentage of the swipe area size.
|
||||
Valid values must be float numbers in range 0..1, where 1.0 is 100%.
|
||||
Mandatory value.
|
||||
* _speed_: The speed at which to perform this gesture in pixels per second.
|
||||
The value must not be negative. The default value is `5000 * displayDensity`
|
||||
|
||||
#### Usage examples
|
||||
|
||||
```java
|
||||
// Java
|
||||
((JavascriptExecutor) driver).executeScript("mobile: swipeGesture", ImmutableMap.of(
|
||||
"left", 100, "top", 100, "width", 200, "height", 200,
|
||||
"direction", "left",
|
||||
"percent", 0.75
|
||||
));
|
||||
```
|
||||
|
||||
|
||||
### mobile: scrollGesture
|
||||
|
||||
This gesture performs scroll gesture on the given element/area.
|
||||
Available since Appium v1.19
|
||||
|
||||
#### Supported arguments
|
||||
|
||||
* _elementId_: The id of the element to be scrolled.
|
||||
If the element id is missing then scroll bounding area must be provided.
|
||||
If both the element id and the scroll bounding area are provided then this
|
||||
area is effectively ignored.
|
||||
* _left_: The left coordinate of the scroll bounding area
|
||||
* _top_: The top coordinate of the scroll bounding area
|
||||
* _width_: The width of the scroll bounding area
|
||||
* _height_: The height of the scroll bounding area
|
||||
* _direction_: Scrolling direction. Mandatory value.
|
||||
Acceptable values are: `up`, `down`, `left` and `right` (case insensitive)
|
||||
* _percent_: The size of the scroll as a percentage of the scrolling area size.
|
||||
Valid values must be float numbers greater than zero, where 1.0 is 100%.
|
||||
Mandatory value.
|
||||
* _speed_: The speed at which to perform this gesture in pixels per second.
|
||||
The value must not be negative. The default value is `5000 * displayDensity`
|
||||
|
||||
#### Returned value
|
||||
|
||||
The returned value is a boolean one and equals to `true` if the object can still scroll in the given direction
|
||||
|
||||
#### Usage examples
|
||||
|
||||
```java
|
||||
// Java
|
||||
boolean canScrollMore = (Boolean) ((JavascriptExecutor) driver).executeScript("mobile: scrollGesture", ImmutableMap.of(
|
||||
"left", 100, "top", 100, "width", 200, "height", 200,
|
||||
"direction", "down",
|
||||
"percent", 3.0
|
||||
));
|
||||
```
|
||||
|
||||
```python
|
||||
# Python
|
||||
can_scroll_more = driver.execute_script('mobile: scrollGesture', {
|
||||
'left': 100, 'top': 100, 'width': 200, 'height': 200,
|
||||
'direction': 'down',
|
||||
'percent': 3.0
|
||||
})
|
||||
```
|
||||
@@ -140,6 +140,7 @@ These Capabilities are available only on Android-based drivers (like
|
||||
| `mockLocationApp` | Sets the package identifier of the app, which is used as a system mock location provider since Appium 1.18.0+. This capability has no effect on emulators. If the value is set to `null` or an empty string, then Appium will skip the mocked location provider setup procedure. Defaults to Appium Setting package identifier (`io.appium.settings`). | e.g., `null`, `io.appium.settings`, `example.your.app` |
|
||||
| logcatFormat | Set the output format for logcat messages since Appium 1.18.0. Supported formats are listed in [here](https://github.com/appium/appium-adb/blob/master/lib/logcat.js). Please read [logcat#outputFormat](https://developer.android.com/studio/command-line/logcat#outputFormat) for more details about each format. Defaults to `threadtime`. | e.g., `process`|
|
||||
| logcatFilterSpecs | Set the output filter rule for logcat messages since Appium 1.18.0. Please read [logcat#filteringOutput](https://developer.android.com/studio/command-line/logcat#filteringOutput) for more details about the rule. [Write and View Logs with Logcat](https://developer.android.com/studio/debug/am-logcat) is also helpful. | e.g., `['*:W', 'MyActivity:D']` (`MyActivity` is a tag)|
|
||||
| allowDelayAdb | Whether enable `-delay-adb` on emulator startup. Defaults to `true` | `true`, `false` |
|
||||
|
||||
#### UIAutomator (1 & 2)
|
||||
|
||||
@@ -173,7 +174,7 @@ These Capabilities are available only on the [Espresso Driver](/docs/en/drivers/
|
||||
|Capability|Description|Values|
|
||||
|----|-----------|-------|
|
||||
|`espressoServerLaunchTimeout`|Timeout in milliseconds used to wait for the Espresso server to launch. Defaults to `30000` |e.g., `50000`|
|
||||
|`espressoBuildConfig`|Path to the Espresso server build configuration JSON (see below)|e.g., `/projects/myapp-tests/buildconfig.json`|
|
||||
|`espressoBuildConfig`|Path to the Espresso server build configuration JSON (see below) or a stringified JSON itself (only supported since server version 1.19)|e.g., `/projects/myapp-tests/buildconfig.json`|
|
||||
|`showGradleLog`|Whether to pipe Gradle build log for the Espresso server to the Appium log. Defaults to `false`|e.g., `true`|
|
||||
|`skipServerInstallation`|Skip Espresso server build and apk installation. This option could break proper Espresso server setup for the particular Appium version, but it can improve startup performance when the proper Espresso server and the proper app under test are already installed on the device. Please, make sure not to enable this option if the Espresso server or the application under test needs an update. Defaults to `false` | `true` or `false`|
|
||||
|`intentOptions`|Intent options which will be used to start the application under test. It can set intent options such as `action`, `categories` and `component` as JSON format. Please read [#538](https://github.com/appium/appium-espresso-driver/issues/538) as an example usage. | e.g. `{"action": "android.intent.action.MAIN", "categories": "android.intent.category.LAUNCHER", "component": "com.appium/.launcher.MainActivity"` |
|
||||
@@ -221,9 +222,10 @@ The module versions enumerated under `toolsVersion` are only used to build the s
|
||||
|
||||
***Application dependencies***
|
||||
|
||||
`additionalAppDependencies` array specifies additional dependencies of the application under test that build tools should know about when building the Espresso server. For example: `[ "com.google.android.material:material:1.0.0" ]`.
|
||||
`additionalAppDependencies` and `additionalAndroidTestDependencies` array specify additional dependencies of the application under test that build tools should know about when building the Espresso server.
|
||||
For example: `"additionalAndroidTestDependencies": [ "com.google.android.material:material:1.0.0" ]`.
|
||||
|
||||
Items belonging to this array are translated to `implementation` lines in Gradle build files of the Espresso server.
|
||||
Items belonging to `additionalAppDependencies` array are translated to `implementation` lines in Gradle build files of the Espresso server. `additionalAndroidTestDependencies` are translated to `androidTestImplementation`.
|
||||
|
||||
### iOS Only
|
||||
|
||||
@@ -281,6 +283,5 @@ Driver](/docs/en/drivers/ios-uiautomation.md).
|
||||
### WinAppDriver Only
|
||||
(For WinAppDriver specific capabilities, please refer to the documentation on the [Appium Windows Driver repo](https://github.com/appium/appium-windows-driver#windowsdriver-specific-capabilities) itself.)
|
||||
|
||||
### Flutter driver only
|
||||
### Flutter driver only
|
||||
(For FlutterDriver specific capabilities, examples please refer to the documentation on the [Flutter Driver repo](https://github.com/truongsinh/appium-flutter-driver#desired-capabilities-for-flutter-driver-only) itself.)
|
||||
|
||||
|
||||
@@ -21,9 +21,13 @@ coordinates and duration.
|
||||
|
||||
#### Supported arguments
|
||||
|
||||
* _direction_: Either 'up', 'down', 'left' or 'right'. The parameter is mandatory
|
||||
* _direction_: Either 'up', 'down', 'left' or 'right'. The argument is mandatory
|
||||
* _velocity_: This argument is optional and is only supported since Appium server version 1.19 and Xcode SDK version 11.4+.
|
||||
The value is measured in pixels per second and same values could behave differently on different devices depending on their display density. Higher values make
|
||||
swipe gesture faster (which usually scrolls larger areas if we apply it to a list) and lower
|
||||
values slow it down. Only values greater than zero have effect.
|
||||
* _element_: The internal element identifier (as hexadecimal hash string) to swipe on.
|
||||
Application element will be used instead if this parameter is not provided
|
||||
Application element will be used instead if this argument is not provided
|
||||
|
||||
#### Usage examples
|
||||
|
||||
@@ -32,6 +36,7 @@ coordinates and duration.
|
||||
JavascriptExecutor js = (JavascriptExecutor) driver;
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("direction", "down");
|
||||
params.put("velocity", 2500);
|
||||
params.put("element", ((RemoteWebElement) element).getId());
|
||||
js.executeScript("mobile: swipe", params);
|
||||
```
|
||||
|
||||
@@ -25,7 +25,7 @@ Each Appium driver is responsible for its own security, and can create its own f
|
||||
|`shutdown_other_sims`|Allow any session to use a capability to shutdown any running simulators on the host|XCUITest|
|
||||
|`perf_record`|Allow recording the system performance and other metrics of the simulator|XCUITest|
|
||||
|`record_audio`|Allow recording of host machine audio inputs|XCUITest|
|
||||
|`chromedriver_autodownload`|Allow to downalod ChromeDriver automatically if Appium does not have proper the version |Android, UiAutomator2, Espresso|
|
||||
|`chromedriver_autodownload`|Allow to download ChromeDriver automatically if Appium does not have proper the version |Android, UiAutomator2, Espresso|
|
||||
|`execute_driver_script`| Allows to send a request which has multiple Appium commands. Read [documentation](https://github.com/appium/appium/blob/master/docs/en/commands/session/execute-driver.md) for more details|All|
|
||||
|
||||
Please see also below links. They might have additional flags.
|
||||
@@ -33,6 +33,7 @@ Please see also below links. They might have additional flags.
|
||||
- [appium-android-driver](https://github.com/appium/appium-android-driver#opt-in-features-with-security-risk)
|
||||
- [appium-xcuitest-driver](https://github.com/appium/appium-xcuitest-driver#opt-in-features-with-security-risk)
|
||||
- [appium-mac-driver](https://github.com/appium/appium-mac-driver#opt-in-features-with-security-risk)
|
||||
- [appium-windows-driver](https://github.com/appium/appium-windows-driver#power-shell-commands-execution)
|
||||
|
||||
## For Driver Developers
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ module.exports = {
|
||||
['UIAutomator2 (Android)', 'android-uiautomator2.md'],
|
||||
['Espresso (Android)', 'android-espresso.md'],
|
||||
['Windows', 'windows.md'],
|
||||
['Safari (Mac/iOS)', 'safari.md'],
|
||||
['Gecko (Firefox)', 'gecko.md'],
|
||||
['Mac', 'mac.md'],
|
||||
]],
|
||||
['Commands', ['commands',
|
||||
@@ -259,6 +261,7 @@ module.exports = {
|
||||
['Activities Startup Troubleshooting Guide', 'activity-startup.md'],
|
||||
['How To Execute Shell Commands On The Remote Device', 'android-shell.md'],
|
||||
['Android Device Screen Streaming', 'android-screen-streaming.md'],
|
||||
['Automating Mobile Gestures With UiAutomator2 Backend', 'android-mobile-gestures.md'],
|
||||
['How To Emulate IME Actions Generation', 'android-ime.md'],
|
||||
['How To Test Android App Bundle', 'android-appbundle.md']
|
||||
]],
|
||||
|
||||
+5
-5
@@ -71,20 +71,20 @@ gulp.task('docs', gulp.series(['transpile']), function parseDocs () {
|
||||
const exampleArg = typeof arg[0][1] === 'undefined' ? arg[0][0] : arg[0][1];
|
||||
const argOpts = arg[1];
|
||||
|
||||
// --keystore-path defaultValue contains a user-specific path,
|
||||
// --keystore-path default contains a user-specific path,
|
||||
// let's replace it with <user>/...
|
||||
if (arg[0][0] === '--keystore-path') {
|
||||
const userPath = process.env.HOME || process.env.USERPROFILE;
|
||||
argOpts.defaultValue = argOpts.defaultValue.replace(userPath, '<user>');
|
||||
argOpts.default = argOpts.default.replace(userPath, '<user>');
|
||||
}
|
||||
|
||||
// handle empty objects
|
||||
if (JSON.stringify(argOpts.defaultValue) === '{}') {
|
||||
argOpts.defaultValue = '{}';
|
||||
if (JSON.stringify(argOpts.default) === '{}') {
|
||||
argOpts.default = '{}';
|
||||
}
|
||||
|
||||
md += '|`' + argNames.join('`, `') + '`';
|
||||
md += '|' + ((typeof argOpts.defaultValue === 'undefined') ? '' : argOpts.defaultValue);
|
||||
md += '|' + ((typeof argOpts.default === 'undefined') ? '' : argOpts.default);
|
||||
md += '|' + argOpts.help;
|
||||
md += '|' + ((typeof argOpts.example === 'undefined') ? '' : '`' + exampleArg + ' ' + argOpts.example + '`');
|
||||
md += '|\n';
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import { Action } from 'argparse';
|
||||
|
||||
|
||||
const DEFAULT_CAPS_ARG = '--default-capabilities';
|
||||
|
||||
|
||||
class StoreDeprecatedAction extends Action {
|
||||
constructor (options = {}) {
|
||||
const opts = Object.assign({}, options);
|
||||
let helpPrefix = '[DEPRECATED]';
|
||||
if (opts.deprecated_for) {
|
||||
helpPrefix = `[DEPRECATED, use ${opts.deprecated_for} instead]`;
|
||||
delete opts.deprecated_for;
|
||||
}
|
||||
if (opts.help) {
|
||||
opts.help = `${helpPrefix} - ${opts.help}`;
|
||||
} else {
|
||||
opts.help = helpPrefix;
|
||||
}
|
||||
super(opts);
|
||||
}
|
||||
|
||||
call (parser, namespace, values) {
|
||||
namespace[this.dest] = values;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class StoreDeprecatedTrueAction extends StoreDeprecatedAction {
|
||||
constructor (options = {}) {
|
||||
super(Object.assign({}, options, {const: true, nargs: 0}));
|
||||
}
|
||||
|
||||
call (parser, namespace) {
|
||||
namespace[this.dest] = this.const;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class StoreDeprecatedDefaultCapabilityAction extends StoreDeprecatedAction {
|
||||
constructor (options = {}) {
|
||||
super(Object.assign({}, options, {deprecated_for: DEFAULT_CAPS_ARG}));
|
||||
}
|
||||
|
||||
_writeDefaultCap (namespace, value) {
|
||||
namespace[this.dest] = value;
|
||||
if (value === this.default) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!namespace.defaultCapabilities) {
|
||||
namespace.defaultCapabilities = {};
|
||||
}
|
||||
namespace.defaultCapabilities[this.dest] = value;
|
||||
}
|
||||
|
||||
call (parser, namespace, values) {
|
||||
this._writeDefaultCap(namespace, values);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class StoreDeprecatedDefaultCapabilityTrueAction extends StoreDeprecatedDefaultCapabilityAction {
|
||||
constructor (options = {}) {
|
||||
super(Object.assign({}, options, {const: true, nargs: 0}));
|
||||
}
|
||||
|
||||
call (parser, namespace) {
|
||||
this._writeDefaultCap(namespace, this.const);
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
StoreDeprecatedAction, StoreDeprecatedTrueAction,
|
||||
StoreDeprecatedDefaultCapabilityAction, StoreDeprecatedDefaultCapabilityTrueAction,
|
||||
DEFAULT_CAPS_ARG,
|
||||
};
|
||||
+83
-105
@@ -1,6 +1,7 @@
|
||||
import { DEFAULT_BASE_PATH } from 'appium-base-driver';
|
||||
import { parseSecurityFeatures, parseDefaultCaps, parsePluginNames, parseInstallTypes } from './parser-helpers';
|
||||
import { INSTALL_TYPES, DEFAULT_APPIUM_HOME, DRIVER_TYPE, PLUGIN_TYPE } from '../extension-config';
|
||||
import { DEFAULT_CAPS_ARG, StoreDeprecatedDefaultCapabilityAction } from './argparse-actions';
|
||||
|
||||
const DRIVER_EXAMPLE = 'xcuitest';
|
||||
const PLUGIN_EXAMPLE = 'find_by_image';
|
||||
@@ -10,32 +11,32 @@ const USE_ALL_PLUGINS = 'all';
|
||||
const sharedArgs = [
|
||||
[['-ah', '--home', '--appium-home'], {
|
||||
required: false,
|
||||
defaultValue: process.env.APPIUM_HOME || DEFAULT_APPIUM_HOME,
|
||||
default: process.env.APPIUM_HOME || DEFAULT_APPIUM_HOME,
|
||||
help: 'The path to the directory where Appium will keep installed drivers, plugins, and any other metadata necessary for its operation',
|
||||
dest: 'appiumHome',
|
||||
}],
|
||||
|
||||
[['--log-filters'], {
|
||||
dest: 'logFilters',
|
||||
defaultValue: null,
|
||||
default: null,
|
||||
required: false,
|
||||
action: 'store_true',
|
||||
help: 'Set the full path to a JSON file containing one or more log filtering rules',
|
||||
example: '/home/rules.json',
|
||||
}],
|
||||
];
|
||||
|
||||
const serverArgs = [
|
||||
[['--shell'], {
|
||||
required: false,
|
||||
defaultValue: null,
|
||||
default: null,
|
||||
help: 'Enter REPL mode',
|
||||
nargs: 0,
|
||||
action: 'store_true',
|
||||
dest: 'shell',
|
||||
}],
|
||||
|
||||
[['--plugins'], {
|
||||
required: false,
|
||||
defaultValue: [],
|
||||
default: [],
|
||||
help: `A comma-separated list of installed plugin names that should be active for this ` +
|
||||
`server. To activate all plugins, you can use the single string "${USE_ALL_PLUGINS}" ` +
|
||||
`as the value (e.g. --plugins=${USE_ALL_PLUGINS})`,
|
||||
@@ -45,79 +46,70 @@ const serverArgs = [
|
||||
|
||||
[['--allow-cors'], {
|
||||
required: false,
|
||||
defaultValue: false,
|
||||
action: 'storeTrue',
|
||||
default: false,
|
||||
action: 'store_true',
|
||||
help: 'Whether the Appium server should allow web browser connections from any host',
|
||||
nargs: 0,
|
||||
dest: 'allowCors',
|
||||
}],
|
||||
|
||||
[['--reboot'], {
|
||||
defaultValue: false,
|
||||
default: false,
|
||||
dest: 'reboot',
|
||||
action: 'storeTrue',
|
||||
action: StoreDeprecatedDefaultCapabilityAction,
|
||||
required: false,
|
||||
help: '(Android-only) reboot emulator after each session and kill it at the end',
|
||||
nargs: 0,
|
||||
}],
|
||||
|
||||
[['-a', '--address'], {
|
||||
defaultValue: '0.0.0.0',
|
||||
default: '0.0.0.0',
|
||||
required: false,
|
||||
example: '0.0.0.0',
|
||||
help: 'IP Address to listen on',
|
||||
dest: 'address',
|
||||
}],
|
||||
|
||||
[['-p', '--port'], {
|
||||
defaultValue: 4723,
|
||||
default: 4723,
|
||||
required: false,
|
||||
type: 'int',
|
||||
example: '4723',
|
||||
help: 'port to listen on',
|
||||
dest: 'port',
|
||||
}],
|
||||
|
||||
[['-pa', '--base-path'], {
|
||||
required: false,
|
||||
defaultValue: DEFAULT_BASE_PATH,
|
||||
default: DEFAULT_BASE_PATH,
|
||||
dest: 'basePath',
|
||||
example: '/path/prefix',
|
||||
help: 'Base path to use as the prefix for all webdriver routes running' +
|
||||
`on this server (default: ${DEFAULT_BASE_PATH})`
|
||||
help: 'Base path to use as the prefix for all webdriver routes running ' +
|
||||
`on this server`
|
||||
}],
|
||||
|
||||
[['-ca', '--callback-address'], {
|
||||
required: false,
|
||||
dest: 'callbackAddress',
|
||||
defaultValue: null,
|
||||
example: '127.0.0.1',
|
||||
default: null,
|
||||
help: 'callback IP Address (default: same as --address)',
|
||||
}],
|
||||
|
||||
[['-cp', '--callback-port'], {
|
||||
required: false,
|
||||
dest: 'callbackPort',
|
||||
defaultValue: null,
|
||||
default: null,
|
||||
type: 'int',
|
||||
example: '4723',
|
||||
help: 'callback port (default: same as port)',
|
||||
}],
|
||||
|
||||
[['--session-override'], {
|
||||
defaultValue: false,
|
||||
default: false,
|
||||
dest: 'sessionOverride',
|
||||
action: 'storeTrue',
|
||||
action: 'store_true',
|
||||
required: false,
|
||||
help: 'Enables session override (clobbering)',
|
||||
nargs: 0,
|
||||
}],
|
||||
|
||||
[['-g', '--log'], {
|
||||
defaultValue: null,
|
||||
default: null,
|
||||
dest: 'logFile',
|
||||
required: false,
|
||||
example: '/path/to/appium.log',
|
||||
help: 'Also send log output to this file',
|
||||
}],
|
||||
|
||||
@@ -128,100 +120,94 @@ const serverArgs = [
|
||||
'error', 'error:debug', 'error:info', 'error:warn', 'error:error',
|
||||
'debug', 'debug:debug', 'debug:info', 'debug:warn', 'debug:error',
|
||||
],
|
||||
defaultValue: 'debug',
|
||||
default: 'debug',
|
||||
dest: 'loglevel',
|
||||
required: false,
|
||||
example: 'debug',
|
||||
help: 'log level; default (console[:file]): debug[:debug]',
|
||||
}],
|
||||
|
||||
[['--log-timestamp'], {
|
||||
defaultValue: false,
|
||||
default: false,
|
||||
required: false,
|
||||
help: 'Show timestamps in console output',
|
||||
nargs: 0,
|
||||
action: 'storeTrue',
|
||||
action: 'store_true',
|
||||
dest: 'logTimestamp',
|
||||
}],
|
||||
|
||||
[['--local-timezone'], {
|
||||
defaultValue: false,
|
||||
default: false,
|
||||
required: false,
|
||||
help: 'Use local timezone for timestamps',
|
||||
nargs: 0,
|
||||
action: 'storeTrue',
|
||||
action: 'store_true',
|
||||
dest: 'localTimezone',
|
||||
}],
|
||||
|
||||
[['--log-no-colors'], {
|
||||
defaultValue: false,
|
||||
default: false,
|
||||
required: false,
|
||||
help: 'Do not use colors in console output',
|
||||
nargs: 0,
|
||||
action: 'storeTrue',
|
||||
action: 'store_true',
|
||||
dest: 'logNoColors',
|
||||
}],
|
||||
|
||||
[['-G', '--webhook'], {
|
||||
defaultValue: null,
|
||||
default: null,
|
||||
required: false,
|
||||
example: 'localhost:9876',
|
||||
dest: 'webhook',
|
||||
help: 'Also send log output to this HTTP listener',
|
||||
help: 'Also send log output to this HTTP listener, for example localhost:9876',
|
||||
}],
|
||||
|
||||
[['--nodeconfig'], {
|
||||
required: false,
|
||||
defaultValue: null,
|
||||
default: null,
|
||||
dest: 'nodeconfig',
|
||||
help: 'Configuration JSON file to register appium with selenium grid',
|
||||
example: '/abs/path/to/nodeconfig.json',
|
||||
}],
|
||||
|
||||
[['--chromedriver-port'], {
|
||||
defaultValue: null,
|
||||
default: null,
|
||||
dest: 'chromeDriverPort',
|
||||
required: false,
|
||||
action: StoreDeprecatedDefaultCapabilityAction,
|
||||
type: 'int',
|
||||
example: '9515',
|
||||
help: 'Port upon which ChromeDriver will run. If not given, Android driver will pick a random available port.',
|
||||
}],
|
||||
|
||||
[['--chromedriver-executable'], {
|
||||
defaultValue: null,
|
||||
default: null,
|
||||
dest: 'chromedriverExecutable',
|
||||
action: StoreDeprecatedDefaultCapabilityAction,
|
||||
required: false,
|
||||
help: 'ChromeDriver executable full path',
|
||||
}],
|
||||
|
||||
[['--show-config'], {
|
||||
defaultValue: false,
|
||||
default: false,
|
||||
dest: 'showConfig',
|
||||
action: 'storeTrue',
|
||||
action: 'store_true',
|
||||
required: false,
|
||||
help: 'Show info about the appium server configuration and exit',
|
||||
}],
|
||||
|
||||
[['--no-perms-check'], {
|
||||
defaultValue: false,
|
||||
default: false,
|
||||
dest: 'noPermsCheck',
|
||||
action: 'storeTrue',
|
||||
action: 'store_true',
|
||||
required: false,
|
||||
help: 'Bypass Appium\'s checks to ensure we can read/write necessary files',
|
||||
}],
|
||||
|
||||
[['--strict-caps'], {
|
||||
defaultValue: false,
|
||||
default: false,
|
||||
dest: 'enforceStrictCaps',
|
||||
action: 'storeTrue',
|
||||
action: 'store_true',
|
||||
required: false,
|
||||
help: 'Cause sessions to fail if desired caps are sent in that Appium ' +
|
||||
'does not recognize as valid for the selected device',
|
||||
nargs: 0,
|
||||
}],
|
||||
|
||||
[['--tmp'], {
|
||||
defaultValue: null,
|
||||
default: null,
|
||||
dest: 'tmpDir',
|
||||
required: false,
|
||||
help: 'Absolute path to directory Appium can use to manage temporary ' +
|
||||
@@ -230,7 +216,7 @@ const serverArgs = [
|
||||
}],
|
||||
|
||||
[['--trace-dir'], {
|
||||
defaultValue: null,
|
||||
default: null,
|
||||
dest: 'traceDir',
|
||||
required: false,
|
||||
help: 'Absolute path to directory Appium use to save ios instruments ' +
|
||||
@@ -239,77 +225,74 @@ const serverArgs = [
|
||||
|
||||
[['--debug-log-spacing'], {
|
||||
dest: 'debugLogSpacing',
|
||||
defaultValue: false,
|
||||
action: 'storeTrue',
|
||||
default: false,
|
||||
action: 'store_true',
|
||||
required: false,
|
||||
help: 'Add exaggerated spacing in logs to help with visual inspection',
|
||||
}],
|
||||
|
||||
[['--suppress-adb-kill-server'], {
|
||||
dest: 'suppressKillServer',
|
||||
defaultValue: false,
|
||||
action: 'storeTrue',
|
||||
default: false,
|
||||
action: StoreDeprecatedDefaultCapabilityAction,
|
||||
required: false,
|
||||
help: '(Android-only) If set, prevents Appium from killing the adb server instance',
|
||||
nargs: 0,
|
||||
}],
|
||||
|
||||
[['--long-stacktrace'], {
|
||||
dest: 'longStacktrace',
|
||||
defaultValue: false,
|
||||
default: false,
|
||||
required: false,
|
||||
action: 'storeTrue',
|
||||
action: 'store_true',
|
||||
help: 'Add long stack traces to log entries. Recommended for debugging only.',
|
||||
}],
|
||||
|
||||
[['--webkit-debug-proxy-port'], {
|
||||
defaultValue: 27753,
|
||||
default: 27753,
|
||||
dest: 'webkitDebugProxyPort',
|
||||
action: StoreDeprecatedDefaultCapabilityAction,
|
||||
required: false,
|
||||
type: 'int',
|
||||
example: '27753',
|
||||
help: '(IOS-only) Local port used for communication with ios-webkit-debug-proxy'
|
||||
}],
|
||||
|
||||
[['--webdriveragent-port'], {
|
||||
defaultValue: 8100,
|
||||
default: 8100,
|
||||
dest: 'wdaLocalPort',
|
||||
action: StoreDeprecatedDefaultCapabilityAction,
|
||||
required: false,
|
||||
type: 'int',
|
||||
example: '8100',
|
||||
help: '(IOS-only, XCUITest-only) Local port used for communication with WebDriverAgent'
|
||||
}],
|
||||
|
||||
[['-dc', '--default-capabilities'], {
|
||||
[['-dc', DEFAULT_CAPS_ARG], {
|
||||
dest: 'defaultCapabilities',
|
||||
defaultValue: {},
|
||||
default: {},
|
||||
type: parseDefaultCaps,
|
||||
required: false,
|
||||
example: '[ \'{"app": "myapp.app", "deviceName": "iPhone Simulator"}\' ' +
|
||||
'| /path/to/caps.json ]',
|
||||
help: 'Set the default desired capabilities, which will be set on each ' +
|
||||
'session unless overridden by received capabilities.'
|
||||
'session unless overridden by received capabilities. For example: ' +
|
||||
'[ \'{"app": "myapp.app", "deviceName": "iPhone Simulator"}\' ' +
|
||||
'| /path/to/caps.json ]'
|
||||
}],
|
||||
|
||||
[['--relaxed-security'], {
|
||||
defaultValue: false,
|
||||
default: false,
|
||||
dest: 'relaxedSecurityEnabled',
|
||||
action: 'storeTrue',
|
||||
action: 'store_true',
|
||||
required: false,
|
||||
help: 'Disable additional security checks, so it is possible to use some advanced features, provided ' +
|
||||
'by drivers supporting this option. Only enable it if all the ' +
|
||||
'clients are in the trusted network and it\'s not the case if a client could potentially ' +
|
||||
'break out of the session sandbox. Specific features can be overridden by ' +
|
||||
'using the --deny-insecure flag',
|
||||
nargs: 0
|
||||
}],
|
||||
|
||||
[['--allow-insecure'], {
|
||||
dest: 'allowInsecure',
|
||||
defaultValue: [],
|
||||
default: [],
|
||||
type: parseSecurityFeatures,
|
||||
required: false,
|
||||
example: 'execute_driver_script,adb_shell',
|
||||
help: 'Set which insecure features are allowed to run in this server\'s sessions. ' +
|
||||
'Features are defined on a driver level; see documentation for more details. ' +
|
||||
'This should be either a comma-separated list of feature names, or a path to ' +
|
||||
@@ -319,16 +302,15 @@ const serverArgs = [
|
||||
|
||||
[['--deny-insecure'], {
|
||||
dest: 'denyInsecure',
|
||||
defaultValue: [],
|
||||
default: [],
|
||||
type: parseSecurityFeatures,
|
||||
required: false,
|
||||
example: 'execute_driver_script,adb_shell',
|
||||
help: 'Set which insecure features are not allowed to run in this server\'s sessions. ' +
|
||||
'Features are defined on a driver level; see documentation for more details. ' +
|
||||
'This should be either a comma-separated list of feature names, or a path to ' +
|
||||
'a file where each feature name is on a line. Features listed here will not be ' +
|
||||
'enabled even if also listed in --allow-insecure, and even if --relaxed-security ' +
|
||||
'is turned on.',
|
||||
'is turned on. For example: execute_driver_script,adb_shell',
|
||||
}],
|
||||
];
|
||||
|
||||
@@ -336,10 +318,9 @@ const serverArgs = [
|
||||
const globalExtensionArgs = [
|
||||
[['--json'], {
|
||||
required: false,
|
||||
defaultValue: false,
|
||||
action: 'storeTrue',
|
||||
default: false,
|
||||
action: 'store_true',
|
||||
help: 'Use JSON for output format',
|
||||
nargs: 0,
|
||||
dest: 'json'
|
||||
}]
|
||||
];
|
||||
@@ -351,18 +332,16 @@ function makeListArgs (type) {
|
||||
...globalExtensionArgs,
|
||||
[['--installed'], {
|
||||
required: false,
|
||||
defaultValue: false,
|
||||
action: 'storeTrue',
|
||||
default: false,
|
||||
action: 'store_true',
|
||||
help: `List only installed ${type}s`,
|
||||
nargs: 0,
|
||||
dest: 'showInstalled'
|
||||
}],
|
||||
[['--updates'], {
|
||||
required: false,
|
||||
defaultValue: false,
|
||||
action: 'storeTrue',
|
||||
default: false,
|
||||
action: 'store_true',
|
||||
help: 'Show information about newer versions',
|
||||
nargs: 0,
|
||||
dest: 'showUpdates'
|
||||
}]
|
||||
];
|
||||
@@ -373,13 +352,13 @@ function makeInstallArgs (type) {
|
||||
return [
|
||||
...globalExtensionArgs,
|
||||
[[type], {
|
||||
type: 'string',
|
||||
example: type === DRIVER_TYPE ? DRIVER_EXAMPLE : PLUGIN_EXAMPLE,
|
||||
help: `Name of the ${type} to install`,
|
||||
type: 'str',
|
||||
help: `Name of the ${type} to install, for example: ` +
|
||||
type === DRIVER_TYPE ? DRIVER_EXAMPLE : PLUGIN_EXAMPLE,
|
||||
}],
|
||||
[['--source'], {
|
||||
required: false,
|
||||
defaultValue: null,
|
||||
default: null,
|
||||
type: parseInstallTypes,
|
||||
help: `Where to look for the ${type} if it is not one of Appium's verified ` +
|
||||
`${type}s. Possible values: ${JSON.stringify(INSTALL_TYPES)}`,
|
||||
@@ -387,8 +366,8 @@ function makeInstallArgs (type) {
|
||||
}],
|
||||
[['--package'], {
|
||||
required: false,
|
||||
defaultValue: null,
|
||||
type: 'string',
|
||||
default: null,
|
||||
type: 'str',
|
||||
help: `If installing from Git or GitHub, the package name, as defined in the plugin's ` +
|
||||
`package.json file in the "name" field, cannot be determined automatically, and ` +
|
||||
`should be reported here, otherwise the install will probably fail.`,
|
||||
@@ -401,9 +380,9 @@ function makeUninstallArgs (type) {
|
||||
return [
|
||||
...globalExtensionArgs,
|
||||
[[type], {
|
||||
type: 'string',
|
||||
example: type === DRIVER_TYPE ? DRIVER_EXAMPLE : PLUGIN_EXAMPLE,
|
||||
help: 'Name of the driver to uninstall',
|
||||
type: 'str',
|
||||
help: 'Name of the driver to uninstall, for example: ' +
|
||||
type === DRIVER_TYPE ? DRIVER_EXAMPLE : PLUGIN_EXAMPLE
|
||||
}],
|
||||
];
|
||||
}
|
||||
@@ -412,16 +391,15 @@ function makeUpdateArgs (type) {
|
||||
return [
|
||||
...globalExtensionArgs,
|
||||
[[type], {
|
||||
type: 'string',
|
||||
example: type === DRIVER_TYPE ? DRIVER_EXAMPLE : PLUGIN_EXAMPLE,
|
||||
type: 'str',
|
||||
help: `Name of the ${type} to update, or the word "installed" to update all installed ` +
|
||||
`${type}s. To see available updates, run "appium ${type} list --installed --updates"`,
|
||||
`${type}s. To see available updates, run "appium ${type} list --installed --updates". ` +
|
||||
'For example: ' + type === DRIVER_TYPE ? DRIVER_EXAMPLE : PLUGIN_EXAMPLE,
|
||||
}],
|
||||
[['--unsafe'], {
|
||||
required: false,
|
||||
defaultValue: false,
|
||||
action: 'storeTrue',
|
||||
nargs: 0,
|
||||
default: false,
|
||||
action: 'store_true',
|
||||
help: `Include updates that might have a new major revision, and potentially include ` +
|
||||
`breaking changes`,
|
||||
}],
|
||||
|
||||
@@ -18,6 +18,10 @@ async function runExtensionCommand (args, type) {
|
||||
// TODO driver config file should be locked while any of these commands are
|
||||
// running to prevent weird situations
|
||||
let jsonResult = null;
|
||||
const extCmd = args[`${type}Command`];
|
||||
if (!extCmd) {
|
||||
throw new TypeError(`Cannot call ${type} command without a subcommand like 'install'`);
|
||||
}
|
||||
const {json, appiumHome} = args;
|
||||
const logFn = (msg) => log(json, msg);
|
||||
const ConfigClass = type === DRIVER_TYPE ? DriverConfig : PluginConfig;
|
||||
|
||||
+27
-15
@@ -4,14 +4,16 @@ import { INSTALL_TYPES } from '../extension-config';
|
||||
|
||||
// serverArgs will be added to the `server` (default) subcommand
|
||||
function parseSecurityFeatures (features) {
|
||||
const splitter = (splitOn, str) => `${str}`.split(splitOn).map((s) => s.trim()).filter(Boolean);
|
||||
const splitter = (splitOn, str) => `${str}`.split(splitOn)
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
let parsedFeatures;
|
||||
try {
|
||||
parsedFeatures = splitter(',', features);
|
||||
} catch (err) {
|
||||
throw new Error('Could not parse value of --allow/deny-insecure. Should be ' +
|
||||
'a list of strings separated by commas, or a path to a file ' +
|
||||
'listing one feature name per line.');
|
||||
throw new TypeError('Could not parse value of --allow/deny-insecure. Should be ' +
|
||||
'a list of strings separated by commas, or a path to a file ' +
|
||||
'listing one feature name per line.');
|
||||
}
|
||||
|
||||
if (parsedFeatures.length === 1 && fs.existsSync(parsedFeatures[0])) {
|
||||
@@ -20,8 +22,8 @@ function parseSecurityFeatures (features) {
|
||||
const fileFeatures = fs.readFileSync(parsedFeatures[0], 'utf8');
|
||||
parsedFeatures = splitter('\n', fileFeatures);
|
||||
} catch (err) {
|
||||
throw new Error(`Attempted to read --allow/deny-insecure feature names ` +
|
||||
`from file ${parsedFeatures[0]} but got error: ${err.message}`);
|
||||
throw new TypeError(`Attempted to read --allow/deny-insecure feature names ` +
|
||||
`from file ${parsedFeatures[0]} but got error: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,36 +32,46 @@ function parseSecurityFeatures (features) {
|
||||
|
||||
function parsePluginNames (names) {
|
||||
if (!_.isString(names)) {
|
||||
throw new Error('To parse plugin names, names must be a CSV string');
|
||||
throw new TypeError('To parse plugin names, names must be a CSV string');
|
||||
}
|
||||
|
||||
try {
|
||||
return names.split(',').map((s) => s.trim()).filter(Boolean);
|
||||
} catch (err) {
|
||||
throw new Error('Could not parse value of --plugins. Should be a list of plugin names ' +
|
||||
throw new TypeError('Could not parse value of --plugins. Should be a list of plugin names ' +
|
||||
'separated by commas. Plugin names are those found when running `appium ' +
|
||||
'plugin list`');
|
||||
}
|
||||
}
|
||||
|
||||
function parseDefaultCaps (caps) {
|
||||
function parseDefaultCaps (capsOrPath) {
|
||||
let caps = capsOrPath;
|
||||
let loadedFromFile = false;
|
||||
try {
|
||||
// use synchronous file access, as `argparse` provides no way of either
|
||||
// awaiting or using callbacks. This step happens in startup, in what is
|
||||
// effectively command-line code, so nothing is blocked in terms of
|
||||
// sessions, so holding up the event loop does not incur the usual
|
||||
// drawbacks.
|
||||
if (fs.statSync(caps).isFile()) {
|
||||
caps = fs.readFileSync(caps, 'utf8');
|
||||
if (_.isString(capsOrPath) && fs.statSync(capsOrPath).isFile()) {
|
||||
caps = fs.readFileSync(capsOrPath, 'utf8');
|
||||
loadedFromFile = true;
|
||||
}
|
||||
} catch (err) {
|
||||
// not a file, or not readable
|
||||
}
|
||||
caps = JSON.parse(caps);
|
||||
if (!_.isPlainObject(caps)) {
|
||||
throw 'Invalid format for default capabilities';
|
||||
try {
|
||||
const result = JSON.parse(caps);
|
||||
if (!_.isPlainObject(result)) {
|
||||
throw new Error(`'${_.truncate(result, {length: 100})}' is not an object`);
|
||||
}
|
||||
return result;
|
||||
} catch (e) {
|
||||
const msg = loadedFromFile
|
||||
? `Default capabilities in '${capsOrPath}' must be a valid JSON`
|
||||
: `Default capabilities must be a valid JSON`;
|
||||
throw new TypeError(`${msg}. Original error: ${e.message}`);
|
||||
}
|
||||
return caps;
|
||||
}
|
||||
|
||||
function parseInstallTypes (source) {
|
||||
|
||||
+45
-26
@@ -5,15 +5,27 @@ import { sharedArgs, serverArgs, extensionArgs } from './args';
|
||||
import { DRIVER_TYPE, PLUGIN_TYPE } from '../extension-config';
|
||||
import { rootDir } from '../utils';
|
||||
|
||||
|
||||
function makeDebugParser (parser) {
|
||||
parser.exit = (status, message = undefined) => {
|
||||
throw new Error(message);
|
||||
};
|
||||
}
|
||||
|
||||
function getParser (debug = false) {
|
||||
const parser = new ArgumentParser({
|
||||
version: require(path.resolve(rootDir, 'package.json')).version,
|
||||
addHelp: true,
|
||||
add_help: true,
|
||||
description: 'A webdriver-compatible server for use with native and hybrid iOS and Android applications.',
|
||||
prog: process.argv[1] ? path.basename(process.argv[1]) : 'appium',
|
||||
debug
|
||||
});
|
||||
const subParsers = parser.addSubparsers({dest: 'subcommand', debug});
|
||||
if (debug) {
|
||||
makeDebugParser(parser);
|
||||
}
|
||||
parser.add_argument('-v', '--version', {
|
||||
action: 'version',
|
||||
version: require(path.resolve(rootDir, 'package.json')).version
|
||||
});
|
||||
const subParsers = parser.add_subparsers({dest: 'subcommand'});
|
||||
|
||||
// add the 'server' subcommand, and store the raw arguments on the parser
|
||||
// object as a way for other parts of the code to work with the arguments
|
||||
@@ -24,31 +36,34 @@ function getParser (debug = false) {
|
||||
// add the 'driver' and 'plugin' subcommands
|
||||
addExtensionsToParser(sharedArgs, subParsers, debug);
|
||||
|
||||
// modify the parseArgs function to insert the 'server' subcommand if the
|
||||
// modify the parse_args function to insert the 'server' subcommand if the
|
||||
// user hasn't specified a subcommand or the global help command
|
||||
parser._parseArgs = parser.parseArgs;
|
||||
parser.parseArgs = function (args, namespace) {
|
||||
parser._parse_args = parser.parse_args.bind(parser);
|
||||
parser.parse_args = function (args, namespace) {
|
||||
if (_.isUndefined(args)) {
|
||||
args = [...process.argv.slice(2)];
|
||||
}
|
||||
if (!_.includes([DRIVER_TYPE, PLUGIN_TYPE, 'server', '-h'], args[0])) {
|
||||
args.splice(0, 0, 'server');
|
||||
}
|
||||
return this._parseArgs(args, namespace);
|
||||
return this._parse_args(args, namespace);
|
||||
}.bind(parser);
|
||||
return parser;
|
||||
}
|
||||
|
||||
function addServerToParser (sharedArgs, subParsers, debug) {
|
||||
const serverParser = subParsers.addParser('server', {
|
||||
addHelp: true,
|
||||
function addServerToParser (sharedArgs, subParsers, debug = false) {
|
||||
const serverParser = subParsers.add_parser('server', {
|
||||
add_help: true,
|
||||
help: 'Run an Appium server',
|
||||
debug
|
||||
});
|
||||
|
||||
for (const [flags, opts] of [...sharedArgs, ...serverArgs]) {
|
||||
// addArgument mutates arguments so make copies
|
||||
serverParser.addArgument([...flags], {...opts});
|
||||
if (debug) {
|
||||
makeDebugParser(serverParser);
|
||||
}
|
||||
|
||||
for (const [flagsOrNames, opts] of [...sharedArgs, ...serverArgs]) {
|
||||
// add_argument mutates arguments so make copies
|
||||
serverParser.add_argument(...flagsOrNames, {...opts});
|
||||
}
|
||||
|
||||
return serverArgs;
|
||||
@@ -57,21 +72,22 @@ function addServerToParser (sharedArgs, subParsers, debug) {
|
||||
function getDefaultServerArgs () {
|
||||
let defaults = {};
|
||||
for (let [, arg] of serverArgs) {
|
||||
defaults[arg.dest] = arg.defaultValue;
|
||||
defaults[arg.dest] = arg.default;
|
||||
}
|
||||
return defaults;
|
||||
}
|
||||
|
||||
function addExtensionsToParser (sharedArgs, subParsers, debug) {
|
||||
function addExtensionsToParser (sharedArgs, subParsers, debug = false) {
|
||||
for (const type of [DRIVER_TYPE, PLUGIN_TYPE]) {
|
||||
const extParser = subParsers.addParser(type, {
|
||||
addHelp: true,
|
||||
const extParser = subParsers.add_parser(type, {
|
||||
add_help: true,
|
||||
help: `Access the ${type} management CLI commands`,
|
||||
debug
|
||||
});
|
||||
const extSubParsers = extParser.addSubparsers({
|
||||
if (debug) {
|
||||
makeDebugParser(extParser);
|
||||
}
|
||||
const extSubParsers = extParser.add_subparsers({
|
||||
dest: `${type}Command`,
|
||||
debug
|
||||
});
|
||||
const parserSpecs = [
|
||||
{command: 'list', args: extensionArgs[type].list,
|
||||
@@ -85,10 +101,13 @@ function addExtensionsToParser (sharedArgs, subParsers, debug) {
|
||||
];
|
||||
|
||||
for (const {command, args, help} of parserSpecs) {
|
||||
const parser = extSubParsers.addParser(command, {help, debug});
|
||||
for (const [flags, opts] of [...sharedArgs, ...args]) {
|
||||
// addArgument mutates params so make sure to send in copies instead
|
||||
parser.addArgument([...flags], {...opts});
|
||||
const parser = extSubParsers.add_parser(command, {help});
|
||||
if (debug) {
|
||||
makeDebugParser(parser);
|
||||
}
|
||||
for (const [flagsOrNames, opts] of [...sharedArgs, ...args]) {
|
||||
// add_argument mutates params so make sure to send in copies instead
|
||||
parser.add_argument(...flagsOrNames, {...opts});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+32
-8
@@ -6,6 +6,9 @@ import { exec } from 'teen_process';
|
||||
import { rootDir } from './utils';
|
||||
import logger from './logger';
|
||||
import semver from 'semver';
|
||||
import {
|
||||
StoreDeprecatedDefaultCapabilityAction, DEFAULT_CAPS_ARG,
|
||||
} from './cli/argparse-actions';
|
||||
|
||||
|
||||
const npmPackage = require(path.resolve(rootDir, 'package.json'));
|
||||
@@ -24,6 +27,11 @@ function getNodeVersion () {
|
||||
return semver.coerce(process.version);
|
||||
}
|
||||
|
||||
function isSubClass (candidateClass, superClass) {
|
||||
return _.isFunction(superClass) && _.isFunction(candidateClass)
|
||||
&& (candidateClass.prototype instanceof superClass || candidateClass === superClass);
|
||||
}
|
||||
|
||||
async function updateBuildInfo (useGithubApiFallback = false) {
|
||||
const sha = await getGitRev(useGithubApiFallback);
|
||||
if (!sha) {
|
||||
@@ -136,14 +144,29 @@ async function showConfig () {
|
||||
}
|
||||
|
||||
function getNonDefaultArgs (parser, args) {
|
||||
let nonDefaults = {};
|
||||
for (let rawArg of parser.rawArgs) {
|
||||
let arg = rawArg[1].dest;
|
||||
if (args[arg] && args[arg] !== rawArg[1].defaultValue) {
|
||||
nonDefaults[arg] = args[arg];
|
||||
return parser.rawArgs.reduce((acc, [, {dest, default: defaultValue}]) => {
|
||||
if (args[dest] && args[dest] !== defaultValue) {
|
||||
acc[dest] = args[dest];
|
||||
}
|
||||
}
|
||||
return nonDefaults;
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function getDeprecatedArgs (parser, args) {
|
||||
// go through the server command line arguments and figure
|
||||
// out which of the ones used are deprecated
|
||||
return parser.rawArgs.reduce((acc, [[name], {dest, default: defaultValue, action}]) => {
|
||||
if (!args[dest] || args[dest] === defaultValue) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
if (action?.deprecated_for) {
|
||||
acc[name] = action.deprecated_for;
|
||||
} else if (isSubClass(action, StoreDeprecatedDefaultCapabilityAction)) {
|
||||
acc[name] = DEFAULT_CAPS_ARG;
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function checkValidPort (port, portName) {
|
||||
@@ -182,7 +205,7 @@ function validateServerArgs (parser, args) {
|
||||
bootstrapPort: checkValidPort,
|
||||
chromedriverPort: checkValidPort,
|
||||
robotPort: checkValidPort,
|
||||
backendRetries: (r) => r >= 0
|
||||
backendRetries: (r) => r >= 0,
|
||||
};
|
||||
|
||||
const nonDefaultArgs = getNonDefaultArgs(parser, args);
|
||||
@@ -209,4 +232,5 @@ export {
|
||||
getBuildInfo, validateServerArgs, checkNodeOk, showConfig,
|
||||
warnNodeDeprecations, validateTmpDir, getNonDefaultArgs,
|
||||
getGitRev, checkValidPort, APPIUM_VER, updateBuildInfo,
|
||||
getDeprecatedArgs,
|
||||
};
|
||||
|
||||
@@ -15,6 +15,8 @@ const KNOWN_DRIVERS = {
|
||||
espresso: 'appium-espresso-driver',
|
||||
tizen: 'appium-tizen-driver',
|
||||
flutter: 'appium-flutter-driver',
|
||||
safari: 'appium-safari-driver',
|
||||
gecko: 'appium-geckodriver',
|
||||
};
|
||||
|
||||
function getDriverBySupport (drivers, matchAutomationName, matchPlatformName) {
|
||||
|
||||
+9
-6
@@ -1,7 +1,6 @@
|
||||
import npmlog from 'npmlog';
|
||||
import { createLogger, format, transports } from 'winston';
|
||||
import { fs, logger } from 'appium-support';
|
||||
import dateformat from 'dateformat';
|
||||
import _ from 'lodash';
|
||||
|
||||
|
||||
@@ -36,16 +35,20 @@ const npmToWinstonLevels = {
|
||||
};
|
||||
|
||||
let log = null;
|
||||
let timeZone = null;
|
||||
let useLocalTimeZone = false;
|
||||
|
||||
// add the timestamp in the correct format to the log info object
|
||||
const timestampFormat = format.timestamp({
|
||||
format () {
|
||||
let date = new Date();
|
||||
if (!timeZone) {
|
||||
date = new Date(date.valueOf() + date.getTimezoneOffset() * 60000);
|
||||
if (useLocalTimeZone) {
|
||||
date = new Date(date.valueOf() - date.getTimezoneOffset() * 60000);
|
||||
}
|
||||
return dateformat(date, 'yyyy-mm-dd HH:MM:ss:l');
|
||||
// '2012-11-04T14:51:06.157Z' -> '2012-11-04 14:51:06:157'
|
||||
return date.toISOString()
|
||||
.replace(/[TZ]/g, ' ')
|
||||
.replace(/\./g, ':')
|
||||
.trim();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -182,7 +185,7 @@ async function createTransports (args) {
|
||||
|
||||
async function init (args) {
|
||||
// set de facto param passed to timestamp function
|
||||
timeZone = args.localTimezone;
|
||||
useLocalTimeZone = args.localTimezone;
|
||||
|
||||
// clean up in case we have initiated before since npmlog is a global object
|
||||
clear();
|
||||
|
||||
+1
-1
@@ -107,7 +107,7 @@ async function main (args = null) {
|
||||
}
|
||||
} else {
|
||||
// otherwise parse from CLI
|
||||
args = parser.parseArgs();
|
||||
args = parser.parse_args();
|
||||
}
|
||||
await logsinkInit(args);
|
||||
|
||||
|
||||
+2
-3
@@ -44,13 +44,12 @@
|
||||
"@babel/runtime": "^7.6.0",
|
||||
"appium-base-driver": "8.0.0-beta.0",
|
||||
"appium-support": "2.x",
|
||||
"argparse": "^1.0.10",
|
||||
"argparse": "^2.0.1",
|
||||
"async-lock": "^1.0.0",
|
||||
"asyncbox": "2.x",
|
||||
"axios": "^0.20.0",
|
||||
"axios": "^0.21.0",
|
||||
"bluebird": "3.x",
|
||||
"continuation-local-storage": "3.x",
|
||||
"dateformat": "^3.0.3",
|
||||
"find-root": "^1.1.0",
|
||||
"lodash": "^4.17.11",
|
||||
"longjohn": "^0.2.12",
|
||||
|
||||
@@ -26,14 +26,65 @@ public class AndroidBasicInteractionsTest extends BaseTest {
|
||||
File appDir = new File(classpathRoot, "../apps");
|
||||
File app = new File(appDir.getCanonicalPath(), "ApiDemos-debug.apk");
|
||||
DesiredCapabilities capabilities = new DesiredCapabilities();
|
||||
capabilities.setCapability("deviceName", "Android Emulator");
|
||||
/*
|
||||
'deviceName' capability only affects device selection if you run the test in a cloud
|
||||
environment. It has no effect if the test is executed on a local machine.
|
||||
*/
|
||||
// capabilities.setCapability("deviceName", "Android Emulator");
|
||||
|
||||
/*
|
||||
It makes sense to set device udid if there are multiple devices/emulators
|
||||
connected to the local machine. Run `adb devices -l` to check which devices
|
||||
are online and what are their identifiers.
|
||||
If only one device is connected then this capability might be omitted
|
||||
*/
|
||||
// capabilities.setCapability("udid", "ABCD123456789");
|
||||
|
||||
/*
|
||||
It is recommended to set a full path to the app being tested.
|
||||
Appium for Android supports application .apk and .apks bundles.
|
||||
If this capability is not set then your test starts on Dashboard view.
|
||||
It is also possible to provide an URL where the app is located.
|
||||
*/
|
||||
capabilities.setCapability("app", app.getAbsolutePath());
|
||||
|
||||
/*
|
||||
By default Appium tries to autodetect the main application activity,
|
||||
but if you app's very first activity is not the main one then
|
||||
it is necessary to provide its name explicitly. Check
|
||||
https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/android/activity-startup.md
|
||||
for more details on activities selection topic.
|
||||
*/
|
||||
// capabilities.setCapability("appActivity", "com.myapp.SplashActivity"));
|
||||
// capabilities.setCapability("appPackage", "com.myapp"));
|
||||
// capabilities.setCapability("appWaitActivity", "com.myapp.MainActivity"));
|
||||
|
||||
/*
|
||||
Appium for Android supports multiple automation backends, where
|
||||
each of them has its own pros and cons. The default one is UIAutomator2.
|
||||
*/
|
||||
// capabilities.setCapability("automationName", "UIAutomator2");
|
||||
// capabilities.setCapability("automationName", "Espresso");
|
||||
|
||||
/*
|
||||
There are much more capabilities and driver settings, that allow
|
||||
you to customize and tune your test to achieve the best automation
|
||||
experience. Read http://appium.io/docs/en/writing-running-appium/caps/
|
||||
and http://appium.io/docs/en/advanced-concepts/settings/
|
||||
for more details.
|
||||
|
||||
Feel free to visit our forum at https://discuss.appium.io/
|
||||
if you have more questions.
|
||||
*/
|
||||
|
||||
driver = new AndroidDriver<WebElement>(getServiceUrl(), capabilities);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public void tearDown() {
|
||||
driver.quit();
|
||||
if (driver != null) {
|
||||
driver.quit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,9 @@ public abstract class BaseTest {
|
||||
|
||||
@AfterSuite
|
||||
public void globalTearDown () {
|
||||
service.stop();
|
||||
if (service != null) {
|
||||
service.stop();
|
||||
}
|
||||
}
|
||||
|
||||
public URL getServiceUrl () {
|
||||
|
||||
@@ -23,16 +23,74 @@ public class IOSBasicInteractionsTest extends BaseTest {
|
||||
String deviceName = System.getenv("IOS_DEVICE_NAME");
|
||||
String platformVersion = System.getenv("IOS_PLATFORM_VERSION");
|
||||
DesiredCapabilities capabilities = new DesiredCapabilities();
|
||||
/*
|
||||
'deviceName' capability only affects device selection if you run the test in a cloud
|
||||
environment or your run your test on a Simulator device. This combination of this value
|
||||
plus `platformVersion` capability value
|
||||
is used to select a proper Simulator if it already exists. Use `xcrun simctl list` command
|
||||
to list available Simulator devices.
|
||||
*/
|
||||
capabilities.setCapability("deviceName", deviceName == null ? "iPhone 6s" : deviceName);
|
||||
|
||||
/*
|
||||
udid value must be set if you run your test on a real iOS device.
|
||||
The udid of your real device could be retrieved from Xcode->Windows->Devices and Simulators
|
||||
dialog.
|
||||
Usually, it is not enough to simply provide udid itself in order to automate apps
|
||||
on real iOS devices. You must also verify the target device is included into
|
||||
your Apple developer profile and the WebDriverAgent is signed with a proper signature.
|
||||
Refer https://github.com/appium/appium-xcuitest-driver/blob/master/docs/real-device-config.md
|
||||
for more details.
|
||||
*/
|
||||
// capabilities.setCapability("udid", "ABCD123456789");
|
||||
|
||||
/*
|
||||
Platform version is required to be set. Only the major and minor version numbers have effect.
|
||||
Check `xcrun simctl list` to see which platform versions are available if the test is going
|
||||
to run on a Simulator.
|
||||
*/
|
||||
capabilities.setCapability("platformVersion", platformVersion == null ? "11.1" : platformVersion);
|
||||
|
||||
/*
|
||||
It is recommended to set a full path to the app being tested.
|
||||
Appium for iOS supports .app and .ipa application bundles.
|
||||
It is also possible to pass zipped .app packages (they will be extracted automatically).
|
||||
|
||||
Make sure the application is built for correct architecture (either
|
||||
real device or Simulator) before running your tests, as there are not interchangeable.
|
||||
If the test is going to run on a real device then make sure your app
|
||||
is signed with correct development signature (as described in the above
|
||||
Real Device Config document)
|
||||
|
||||
If this capability is not set then your test starts on Springboard view.
|
||||
It is also possible to provide an URL where the app is located.
|
||||
*/
|
||||
capabilities.setCapability("app", app.getAbsolutePath());
|
||||
|
||||
/*
|
||||
This is the only supported automation backend for iOS
|
||||
*/
|
||||
capabilities.setCapability("automationName", "XCUITest");
|
||||
|
||||
/*
|
||||
There are much more capabilities and driver settings, that allow
|
||||
you to customize and tune your test to achieve the best automation
|
||||
experience. Read http://appium.io/docs/en/writing-running-appium/caps/
|
||||
and http://appium.io/docs/en/advanced-concepts/settings/
|
||||
for more details.
|
||||
|
||||
Feel free to visit our forum at https://discuss.appium.io/
|
||||
if you have more questions.
|
||||
*/
|
||||
|
||||
driver = new IOSDriver<WebElement>(getServiceUrl(), capabilities);
|
||||
}
|
||||
|
||||
@AfterTest
|
||||
public void tearDown() {
|
||||
driver.quit();
|
||||
if (driver != null) {
|
||||
driver.quit();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -203,7 +203,7 @@ describe('Config', function () {
|
||||
beforeEach(function () {
|
||||
// give all the defaults
|
||||
for (let rawArg of parser.rawArgs) {
|
||||
args[rawArg[1].dest] = rawArg[1].defaultValue;
|
||||
args[rawArg[1].dest] = rawArg[1].default;
|
||||
}
|
||||
});
|
||||
describe('getNonDefaultArgs', function () {
|
||||
@@ -278,7 +278,7 @@ describe('Config', function () {
|
||||
const defaultArgs = {};
|
||||
// give all the defaults
|
||||
for (let rawArg of parser.rawArgs) {
|
||||
defaultArgs[rawArg[1].dest] = rawArg[1].defaultValue;
|
||||
defaultArgs[rawArg[1].dest] = rawArg[1].default;
|
||||
}
|
||||
let args = {};
|
||||
beforeEach(function () {
|
||||
|
||||
+52
-50
@@ -12,23 +12,23 @@ const DENY_FIXTURE = 'test/fixtures/deny-feat.txt';
|
||||
describe('Main Parser', function () {
|
||||
let p = getParser(true);
|
||||
it('should accept only server and driver subcommands', function () {
|
||||
p.parseArgs([]);
|
||||
p.parseArgs(['server']);
|
||||
p.parseArgs(['driver', 'list']);
|
||||
(() => p.parseArgs(['foo'])).should.throw();
|
||||
(() => p.parseArgs(['foo --bar'])).should.throw();
|
||||
p.parse_args([]);
|
||||
p.parse_args(['server']);
|
||||
p.parse_args(['driver', 'list']);
|
||||
(() => p.parse_args(['foo'])).should.throw();
|
||||
(() => p.parse_args(['foo --bar'])).should.throw();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Server Parser', function () {
|
||||
let p = getParser(true);
|
||||
it('should return an arg parser', function () {
|
||||
should.exist(p.parseArgs);
|
||||
p.parseArgs([]).should.have.property('port');
|
||||
should.exist(p.parse_args);
|
||||
p.parse_args([]).should.have.property('port');
|
||||
});
|
||||
it('should default to the server subcommand', function () {
|
||||
p.parseArgs([]).subcommand.should.eql('server');
|
||||
p.parseArgs([]).should.eql(p.parseArgs(['server']));
|
||||
p.parse_args([]).subcommand.should.eql('server');
|
||||
p.parse_args([]).should.eql(p.parse_args(['server']));
|
||||
});
|
||||
it('should keep the raw server flags array', function () {
|
||||
should.exist(p.rawArgs);
|
||||
@@ -39,40 +39,45 @@ describe('Server Parser', function () {
|
||||
}
|
||||
});
|
||||
it('should throw an error with unknown argument', function () {
|
||||
(() => {p.parseArgs(['--apple']);}).should.throw();
|
||||
(() => {p.parse_args(['--apple']);}).should.throw();
|
||||
});
|
||||
it('should parse default capabilities correctly from a string', function () {
|
||||
let defaultCapabilities = {a: 'b'};
|
||||
let args = p.parseArgs(['--default-capabilities', JSON.stringify(defaultCapabilities)]);
|
||||
let args = p.parse_args(['--default-capabilities', JSON.stringify(defaultCapabilities)]);
|
||||
args.defaultCapabilities.should.eql(defaultCapabilities);
|
||||
});
|
||||
it('should parse default capabilities correctly from a file', function () {
|
||||
let defaultCapabilities = {a: 'b'};
|
||||
let args = p.parseArgs(['--default-capabilities', 'test/fixtures/caps.json']);
|
||||
let args = p.parse_args(['--default-capabilities', 'test/fixtures/caps.json']);
|
||||
args.defaultCapabilities.should.eql(defaultCapabilities);
|
||||
});
|
||||
it('should throw an error with invalid arg to default capabilities', function () {
|
||||
(() => {p.parseArgs(['-dc', '42']);}).should.throw();
|
||||
(() => {p.parseArgs(['-dc', 'false']);}).should.throw();
|
||||
(() => {p.parseArgs(['-dc', 'null']);}).should.throw();
|
||||
(() => {p.parseArgs(['-dc', 'does/not/exist.json']);}).should.throw();
|
||||
(() => {p.parse_args(['-dc', '42']);}).should.throw();
|
||||
(() => {p.parse_args(['-dc', 'false']);}).should.throw();
|
||||
(() => {p.parse_args(['-dc', 'null']);}).should.throw();
|
||||
(() => {p.parse_args(['-dc', 'does/not/exist.json']);}).should.throw();
|
||||
});
|
||||
it('should parse args that are caps into default capabilities', function () {
|
||||
let defaultCapabilities = {chromedriverExecutable: '/my/dir'};
|
||||
let args = p.parse_args(['--chromedriver-executable', '/my/dir']);
|
||||
args.defaultCapabilities.should.eql(defaultCapabilities);
|
||||
});
|
||||
it('should parse --allow-insecure correctly', function () {
|
||||
p.parseArgs([]).allowInsecure.should.eql([]);
|
||||
p.parseArgs(['--allow-insecure', '']).allowInsecure.should.eql([]);
|
||||
p.parseArgs(['--allow-insecure', 'foo']).allowInsecure.should.eql(['foo']);
|
||||
p.parseArgs(['--allow-insecure', 'foo,bar']).allowInsecure.should.eql(['foo', 'bar']);
|
||||
p.parseArgs(['--allow-insecure', 'foo ,bar']).allowInsecure.should.eql(['foo', 'bar']);
|
||||
p.parse_args([]).allowInsecure.should.eql([]);
|
||||
p.parse_args(['--allow-insecure', '']).allowInsecure.should.eql([]);
|
||||
p.parse_args(['--allow-insecure', 'foo']).allowInsecure.should.eql(['foo']);
|
||||
p.parse_args(['--allow-insecure', 'foo,bar']).allowInsecure.should.eql(['foo', 'bar']);
|
||||
p.parse_args(['--allow-insecure', 'foo ,bar']).allowInsecure.should.eql(['foo', 'bar']);
|
||||
});
|
||||
it('should parse --deny-insecure correctly', function () {
|
||||
p.parseArgs([]).denyInsecure.should.eql([]);
|
||||
p.parseArgs(['--deny-insecure', '']).denyInsecure.should.eql([]);
|
||||
p.parseArgs(['--deny-insecure', 'foo']).denyInsecure.should.eql(['foo']);
|
||||
p.parseArgs(['--deny-insecure', 'foo,bar']).denyInsecure.should.eql(['foo', 'bar']);
|
||||
p.parseArgs(['--deny-insecure', 'foo ,bar']).denyInsecure.should.eql(['foo', 'bar']);
|
||||
p.parse_args([]).denyInsecure.should.eql([]);
|
||||
p.parse_args(['--deny-insecure', '']).denyInsecure.should.eql([]);
|
||||
p.parse_args(['--deny-insecure', 'foo']).denyInsecure.should.eql(['foo']);
|
||||
p.parse_args(['--deny-insecure', 'foo,bar']).denyInsecure.should.eql(['foo', 'bar']);
|
||||
p.parse_args(['--deny-insecure', 'foo ,bar']).denyInsecure.should.eql(['foo', 'bar']);
|
||||
});
|
||||
it('should parse --allow and --deny insecure from files', function () {
|
||||
const parsed = p.parseArgs([
|
||||
const parsed = p.parse_args([
|
||||
'--allow-insecure', ALLOW_FIXTURE, '--deny-insecure', DENY_FIXTURE
|
||||
]);
|
||||
parsed.allowInsecure.should.eql(['feature1', 'feature2', 'feature3']);
|
||||
@@ -82,15 +87,12 @@ describe('Server Parser', function () {
|
||||
|
||||
describe('Driver Parser', function () {
|
||||
let p = getParser(true);
|
||||
it('should require a sub-subcommand', function () {
|
||||
(() => p.parseArgs(['driver'])).should.throw();
|
||||
});
|
||||
it('should not allow random sub-subcommands', function () {
|
||||
(() => p.parseArgs(['driver', 'foo'])).should.throw();
|
||||
(() => p.parse_args(['driver', 'foo'])).should.throw();
|
||||
});
|
||||
describe('list', function () {
|
||||
it('should allow an empty argument list', function () {
|
||||
const args = p.parseArgs(['driver', 'list']);
|
||||
const args = p.parse_args(['driver', 'list']);
|
||||
args.subcommand.should.eql('driver');
|
||||
args.driverCommand.should.eql('list');
|
||||
args.showInstalled.should.eql(false);
|
||||
@@ -99,28 +101,28 @@ describe('Driver Parser', function () {
|
||||
args.appiumHome.should.eql(DEFAULT_APPIUM_HOME);
|
||||
});
|
||||
it('should allow json format', function () {
|
||||
const args = p.parseArgs(['driver', 'list', '--json']);
|
||||
const args = p.parse_args(['driver', 'list', '--json']);
|
||||
args.json.should.eql(true);
|
||||
});
|
||||
it('should allow custom appium home', function () {
|
||||
const args = p.parseArgs(['driver', 'list', '--home', '/foo/bar']);
|
||||
const args = p.parse_args(['driver', 'list', '--home', '/foo/bar']);
|
||||
args.appiumHome.should.eql('/foo/bar');
|
||||
});
|
||||
it('should allow --installed', function () {
|
||||
const args = p.parseArgs(['driver', 'list', '--installed']);
|
||||
const args = p.parse_args(['driver', 'list', '--installed']);
|
||||
args.showInstalled.should.eql(true);
|
||||
});
|
||||
it('should allow --updates', function () {
|
||||
const args = p.parseArgs(['driver', 'list', '--updates']);
|
||||
const args = p.parse_args(['driver', 'list', '--updates']);
|
||||
args.showUpdates.should.eql(true);
|
||||
});
|
||||
});
|
||||
describe('install', function () {
|
||||
it('should not allow an empty argument list', function () {
|
||||
(() => p.parseArgs(['driver', 'install'])).should.throw();
|
||||
(() => p.parse_args(['driver', 'install'])).should.throw();
|
||||
});
|
||||
it('should take a driver name to install', function () {
|
||||
const args = p.parseArgs(['driver', 'install', 'foobar']);
|
||||
const args = p.parse_args(['driver', 'install', 'foobar']);
|
||||
args.subcommand.should.eql('driver');
|
||||
args.driverCommand.should.eql('install');
|
||||
args.driver.should.eql('foobar');
|
||||
@@ -129,29 +131,29 @@ describe('Driver Parser', function () {
|
||||
args.json.should.eql(false);
|
||||
});
|
||||
it('should allow json format', function () {
|
||||
const args = p.parseArgs(['driver', 'install', 'foobar', '--json']);
|
||||
const args = p.parse_args(['driver', 'install', 'foobar', '--json']);
|
||||
args.json.should.eql(true);
|
||||
});
|
||||
it('should allow custom appium home', function () {
|
||||
const args = p.parseArgs(['driver', 'install', 'foobar', '--home', '/foo/bar']);
|
||||
const args = p.parse_args(['driver', 'install', 'foobar', '--home', '/foo/bar']);
|
||||
args.appiumHome.should.eql('/foo/bar');
|
||||
});
|
||||
it('should allow --source', function () {
|
||||
for (const source of INSTALL_TYPES) {
|
||||
const args = p.parseArgs(['driver', 'install', 'foobar', '--source', source]);
|
||||
const args = p.parse_args(['driver', 'install', 'foobar', '--source', source]);
|
||||
args.installType.should.eql(source);
|
||||
}
|
||||
});
|
||||
it('should not allow unknown --source', function () {
|
||||
(() => p.parseArgs(['driver', 'install', 'fobar', '--source', 'blah'])).should.throw();
|
||||
(() => p.parse_args(['driver', 'install', 'fobar', '--source', 'blah'])).should.throw();
|
||||
});
|
||||
});
|
||||
describe('uninstall', function () {
|
||||
it('should not allow an empty argument list', function () {
|
||||
(() => p.parseArgs(['driver', 'uninstall'])).should.throw();
|
||||
(() => p.parse_args(['driver', 'uninstall'])).should.throw();
|
||||
});
|
||||
it('should take a driver name to uninstall', function () {
|
||||
const args = p.parseArgs(['driver', 'uninstall', 'foobar']);
|
||||
const args = p.parse_args(['driver', 'uninstall', 'foobar']);
|
||||
args.subcommand.should.eql('driver');
|
||||
args.driverCommand.should.eql('uninstall');
|
||||
args.driver.should.eql('foobar');
|
||||
@@ -159,20 +161,20 @@ describe('Driver Parser', function () {
|
||||
args.json.should.eql(false);
|
||||
});
|
||||
it('should allow json format', function () {
|
||||
const args = p.parseArgs(['driver', 'uninstall', 'foobar', '--json']);
|
||||
const args = p.parse_args(['driver', 'uninstall', 'foobar', '--json']);
|
||||
args.json.should.eql(true);
|
||||
});
|
||||
it('should allow custom appium home', function () {
|
||||
const args = p.parseArgs(['driver', 'uninstall', 'foobar', '--home', '/foo/bar']);
|
||||
const args = p.parse_args(['driver', 'uninstall', 'foobar', '--home', '/foo/bar']);
|
||||
args.appiumHome.should.eql('/foo/bar');
|
||||
});
|
||||
});
|
||||
describe('update', function () {
|
||||
it('should not allow an empty argument list', function () {
|
||||
(() => p.parseArgs(['driver', 'update'])).should.throw();
|
||||
(() => p.parse_args(['driver', 'update'])).should.throw();
|
||||
});
|
||||
it('should take a driver name to update', function () {
|
||||
const args = p.parseArgs(['driver', 'update', 'foobar']);
|
||||
const args = p.parse_args(['driver', 'update', 'foobar']);
|
||||
args.subcommand.should.eql('driver');
|
||||
args.driverCommand.should.eql('update');
|
||||
args.driver.should.eql('foobar');
|
||||
@@ -180,11 +182,11 @@ describe('Driver Parser', function () {
|
||||
args.json.should.eql(false);
|
||||
});
|
||||
it('should allow json format', function () {
|
||||
const args = p.parseArgs(['driver', 'update', 'foobar', '--json']);
|
||||
const args = p.parse_args(['driver', 'update', 'foobar', '--json']);
|
||||
args.json.should.eql(true);
|
||||
});
|
||||
it('should allow custom appium home', function () {
|
||||
const args = p.parseArgs(['driver', 'update', 'foobar', '--home', '/foo/bar']);
|
||||
const args = p.parse_args(['driver', 'update', 'foobar', '--home', '/foo/bar']);
|
||||
args.appiumHome.should.eql('/foo/bar');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user