mirror of
https://github.com/appium/appium.git
synced 2026-11-11 21:06:51 +08:00
Compare commits
48
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27504d1926 | ||
|
|
ccb2e28ffd | ||
|
|
0325dcf0ad | ||
|
|
fa7af8e09b | ||
|
|
6ed7b17f2b | ||
|
|
e09cb2fcad | ||
|
|
ad6941b194 | ||
|
|
0da28a25d5 | ||
|
|
456ee2aa68 | ||
|
|
8b6f6997cb | ||
|
|
d7abd776ee | ||
|
|
8557189e60 | ||
|
|
22fb0048ef | ||
|
|
5b7a5d9404 | ||
|
|
e3c3fb2ce7 | ||
|
|
a5d1aa2a1d | ||
|
|
b47bd6ce85 | ||
|
|
e588f9c294 | ||
|
|
efc9e9ce49 | ||
|
|
8102f84ea8 | ||
|
|
4b4856111d | ||
|
|
3b6815f847 | ||
|
|
0b7efd1f8c | ||
|
|
8150b4f3a6 | ||
|
|
691c2f66c0 | ||
|
|
b590d9b9d5 | ||
|
|
0da1891ac9 | ||
|
|
2cadaa284e | ||
|
|
c78847855b | ||
|
|
4970643d96 | ||
|
|
e05f93eb28 | ||
|
|
66184e132d | ||
|
|
02b4abe465 | ||
|
|
4034d16d2d | ||
|
|
19cb2d6f99 | ||
|
|
001327b894 | ||
|
|
51bfe25c51 | ||
|
|
d7027c09ea | ||
|
|
ed98432390 | ||
|
|
0d9b4a812a | ||
|
|
aa8d0e70aa | ||
|
|
dc4901a87a | ||
|
|
8313c6026b | ||
|
|
547d308382 | ||
|
|
3e931207b7 | ||
|
|
563670ecde | ||
|
|
bf0458ba9b | ||
|
|
74fa4504f8 |
@@ -19,3 +19,12 @@ Describe in more detail the problem you have been experiencing, if necessary.
|
||||
## Link to Appium logs
|
||||
|
||||
Create a [gist](https://gist.github.com) which is a paste of your _full_ Appium logs, and link them here. Do _not_ paste your full Appium logs here, as it will make this issue very long and hard to read! If you are reporting a bug, _always_ include Appium logs!
|
||||
|
||||
|
||||
## Code To Reproduce Issue [ Good To Have ]
|
||||
|
||||
Please remember that, with sample code; it's easier to reproduce bug and much faster to fix it.
|
||||
|
||||
Please git clone https://github.com/appium/sample-code and use one of your favourite languages and sample apps to reproduce issue.
|
||||
|
||||
In case a similar scenario is missing in sample-code, please submit a PR with one of the sample apps provided.
|
||||
@@ -30,3 +30,4 @@ build/
|
||||
*DerivedData
|
||||
__pycache__
|
||||
old
|
||||
.tern-project
|
||||
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
[submodule "submodules/instruments-without-delay"]
|
||||
path = submodules/instruments-without-delay
|
||||
url = https://github.com/facebook/instruments-without-delay.git
|
||||
[submodule "submodules/ApiDemos"]
|
||||
path = submodules/ApiDemos
|
||||
url = https://github.com/appium/android-apidemos.git
|
||||
[submodule "submodules/appium.io"]
|
||||
path = submodules/appium.io
|
||||
url = https://github.com/appium/appium.io.git
|
||||
[submodule "submodules/selendroid"]
|
||||
path = submodules/selendroid
|
||||
url = https://github.com/selendroid/selendroid.git
|
||||
[submodule "submodules/io.appium.gappium.sampleapp"]
|
||||
path = submodules/io.appium.gappium.sampleapp
|
||||
url = https://github.com/appium/io.appium.gappium.sampleapp.git
|
||||
[submodule "submodules/unlock_apk"]
|
||||
path = submodules/unlock_apk
|
||||
url = https://github.com/appium/unlock_apk.git
|
||||
[submodule "submodules/libimobiledevice-macosx"]
|
||||
path = submodules/libimobiledevice-macosx
|
||||
url = https://github.com/benvium/libimobiledevice-macosx.git
|
||||
[submodule "submodules/ForceQuitUnresponsiveApps"]
|
||||
path = submodules/ForceQuitUnresponsiveApps
|
||||
url = https://github.com/appium/ForceQuitUnresponsiveApps.git
|
||||
[submodule "submodules/appium-atoms"]
|
||||
path = submodules/appium-atoms
|
||||
url = https://github.com/appium/appium-atoms.git
|
||||
branch = published
|
||||
[submodule "submodules/appium-instruments"]
|
||||
path = submodules/appium-instruments
|
||||
url = https://github.com/appium/appium-instruments.git
|
||||
branch = published
|
||||
[submodule "submodules/appium-uiauto"]
|
||||
path = submodules/appium-uiauto
|
||||
url = https://github.com/appium/appium-uiauto.git
|
||||
branch = published
|
||||
[submodule "submodules/appium-adb"]
|
||||
path = submodules/appium-adb
|
||||
url = https://github.com/appium/appium-adb.git
|
||||
[submodule "submodules/io.appium.android.ime"]
|
||||
path = submodules/io.appium.android.ime
|
||||
url = https://github.com/appium/io.appium.android.ime.git
|
||||
[submodule "submodules/io.appium.settings"]
|
||||
path = submodules/io.appium.settings
|
||||
url = https://github.com/appium/io.appium.settings.git
|
||||
[submodule "submodules/sample-code"]
|
||||
path = submodules/sample-code
|
||||
url = https://github.com/appium/sample-code
|
||||
[submodule "submodules/deviceconsole"]
|
||||
path = submodules/deviceconsole
|
||||
url = https://github.com/rpetrich/deviceconsole.git
|
||||
[submodule "submodules/appium-ci-gulper"]
|
||||
path = submodules/appium-ci-gulper
|
||||
url = https://github.com/appium/appium-ci-gulper.git
|
||||
branch = master
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- "5"
|
||||
- "stable"
|
||||
- "4"
|
||||
- "0.12"
|
||||
after_success:
|
||||
|
||||
+104
@@ -1,3 +1,105 @@
|
||||
CHANGES IN VERSION 1.5.3 (from 1.5.2)
|
||||
===================================
|
||||
|
||||
*NOTE*: Appium 1.5.3 is primarily a bug-fix release.
|
||||
|
||||
It also adds functionality to support for iOS [WKWebViews](https://developer.apple.com/library/ios/documentation/WebKit/Reference/WKWebView_Ref/) and for automating the iOS Calendar app, as well as adding a capability to use `abd` for screenshots in Android web/hybrid sessions, selecting the correct version of Android emulator, and providing a list of Android activities to wait for on session startup.
|
||||
|
||||
Further, Android session reporting is increased, so that automation clients can get the device UDID for the automation session, as well as the currently running `appPackage` and `appActivity`.
|
||||
|
||||
#### General
|
||||
- Allow `--default-capabilities` server argument to specify a file containing the default capabilities.
|
||||
- Fix handling of statuses from proxied drivers (Chrome, Selendroid, etc.).
|
||||
- Fix handling of `browserName` capability for Selenium Grid usage.
|
||||
- Fix intermittent bug where creating a directory might crash the server.
|
||||
- Fix handling of `--session-override` server argument.
|
||||
|
||||
#### iOS
|
||||
- Add support for `WKWebView`-based webviews.
|
||||
- Add support for automating the built-in Calandar app.
|
||||
- Fix `ios-debug-proxy-launcher`.
|
||||
- Fix bug where scrolling in Safari would crash.
|
||||
- Fix handling of Safari in "new window" state.
|
||||
- Fix handling of arguments for `keys` method so that clients that send an array do not fail.
|
||||
- Clean up handling of `ipa` files for real device tests.
|
||||
- Make sure `processArguments` parsing works.
|
||||
- Fix iwd script to allow re-running.
|
||||
|
||||
#### Android
|
||||
- Add `nativeWebScreenshot` capability, to use `adb` screenshots instead of ChromeDriver if necessary.
|
||||
- Add support to auto-select device based on `platformVersion` rather than using first available device.
|
||||
- Allow `appWaitActivity` to be a list of activities
|
||||
- Fix setting wifi and data state
|
||||
- Implement `initAutoWebView` capability.
|
||||
- Report `appPackage`, `appActivity`, and `deviceUDID` when requesting session details.
|
||||
- Make sure unlock app is closed at the end of a session so that the device works again.
|
||||
- Fix handling of unlock to make less flakey.
|
||||
- Make sure unicode encoding works correctly when strings are long.
|
||||
- Fix handling of `disableAndroidWatchers` capability.
|
||||
- Fix killing ChromeDriver on Ubuntu 16.04.
|
||||
- Fix bug where early ChromeDriver failure crashed Appium.
|
||||
|
||||
|
||||
|
||||
CHANGES IN VERSION 1.5.2 (from 1.5.1)
|
||||
===================================
|
||||
|
||||
*NOTE*: Appium 1.5.2 is a bug-fix release.
|
||||
|
||||
#### General
|
||||
- deprecated `--command-timeout`. Use `newCommandTimeout` desired capability instead
|
||||
- ensure implicit wait can be set through `timeout` method
|
||||
- add better logging for `EPIPE` errors
|
||||
|
||||
#### iOS
|
||||
- make sure `ipa` files are handled correctly for installing on real devices
|
||||
- ensure that existing SafariLauncher on device is used instead of rebuilding and reinstalling
|
||||
- fix issues with getting webview contexts on real devices
|
||||
- add full timeout support through `timeout` method
|
||||
- make sure Xpath searches respect implicit wait timeout
|
||||
- make sure bare Instruments process arguments are accepted
|
||||
|
||||
#### Android
|
||||
- fix failure when `apk` file is too large
|
||||
- re-implement setting geolocation so it does not use Telnet.
|
||||
- add support for Chromium browser
|
||||
- fix issues with `flick`
|
||||
- fix bug where touch action `release` would throw an error
|
||||
- fix bug in later Android SDK version where noticing a newly started avd would fail
|
||||
- implement `autoWebviewTimeout`
|
||||
|
||||
|
||||
|
||||
CHANGES IN VERSION 1.5.1 (from 1.5)
|
||||
===================================
|
||||
|
||||
*NOTE*: Appium 1.5.1 is a bug-fix release.
|
||||
|
||||
#### General
|
||||
- allow `platformName` to be any case
|
||||
- Windows process handling is cleaned up
|
||||
- Desired capabilities `language` and `locale` added
|
||||
|
||||
#### iOS
|
||||
- iOS 9.3 (Xcode 7.3) support
|
||||
- Fix handling of return values from `executeScript` in Safari
|
||||
- Don't stop if Instruments doesn't shut down in a timely manner
|
||||
- Escape single quotes in all methods that set the value on an element
|
||||
- Allow custom device names
|
||||
- Make full use of process arguments to Instruments
|
||||
- Pass `launchTimeout` to Instruments when checking devices
|
||||
|
||||
#### Android
|
||||
- Make use of `--bootstrap-port` server argument
|
||||
- Fix `keystorePassword` capability to allow a string
|
||||
- Fix handling of localization in Android 6
|
||||
- Use Appium's unlock logic for Chrome sessions
|
||||
- Make sure reset works
|
||||
- Make unlock more reliable for later versions of Android
|
||||
- Allow Xpath searching from the context of another element
|
||||
- Make full use of process arguments to adb
|
||||
- Better error messages when ChromeDriver fails to start
|
||||
|
||||
CHANGES IN VERSION 1.5 (from 1.4.16)
|
||||
======================================
|
||||
|
||||
@@ -22,9 +124,11 @@ tests.
|
||||
amount of complexity to the startup flow and Appium shouldn't be responsible
|
||||
for this kind of use case
|
||||
- Remove long-deprecated `name` locator strategy
|
||||
- Remove long-deprecated `mobile: xxx` gesture commands, except scroll
|
||||
|
||||
#### iOS
|
||||
- add Tapster support for some more touch methods
|
||||
- fix moveTo: treat coordinates as relative, instead of absolute
|
||||
- iPhone 6 + 6S support
|
||||
- iOS 9.3 support
|
||||
|
||||
|
||||
@@ -25,14 +25,14 @@ See the [platform support doc](/docs/en/appium-setup/platform-support.md) for mo
|
||||
|
||||
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.
|
||||
2. You can write tests with your favorite dev tools using any [WebDriver](https://code.google.com/p/selenium/wiki/JsonWireProtocol)-compatible
|
||||
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](https://github.com/appium/selenium-objective-c),
|
||||
JavaScript with Node.js (in [promise, callback](https://github.com/admc/wd) or [generator](https://github.com/jlipps/yiewd) flavors),
|
||||
PHP, Python, [Ruby](https://github.com/appium/ruby_lib), C#, Clojure, or Perl
|
||||
with the Selenium WebDriver API and language-specific client libraries.
|
||||
3. You can use any testing framework.
|
||||
|
||||
Investing in the [WebDriver](https://code.google.com/p/selenium/wiki/JsonWireProtocol) protocol means you are betting on a single, free and open protocol for testing that has become a defacto standard. Don't lock yourself into a proprietary stack.
|
||||
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 a defacto standard. Don't lock yourself into a proprietary stack.
|
||||
|
||||
If you use Apple's UIAutomation library without Appium you can only write tests
|
||||
using JavaScript and you can only run tests through the Instruments application.
|
||||
@@ -83,7 +83,7 @@ You also need to download the Appium client for your language so you can write t
|
||||
|
||||
### Quick Start
|
||||
|
||||
Kick up an Appium server, and then run a test written in your favorite [WebDriver](https://code.google.com/p/selenium/wiki/JsonWireProtocol)-compatible language!
|
||||
Kick up an Appium server, and then run a test written in your favorite [WebDriver](https://w3c.github.io/webdriver/webdriver-spec.html)-compatible language!
|
||||
You can run an Appium server using node.js or using the application, see below.
|
||||
|
||||
#### Using Node.js
|
||||
@@ -102,7 +102,7 @@ $ appium
|
||||
|
||||
The main guide for getting started writing and running tests is [the running tests](/docs/en/writing-running-appium/running-tests.md) doc, which includes explanations for iOS, Android, and Android older devices. If you're interested in testing on physical hardware, you might be interested in our [real devices guide](/docs/en/appium-setup/real-devices.md).
|
||||
|
||||
Essentially, we support a subset of the [Selenium WebDriver JSON Wire Protocol](https://code.google.com/p/selenium/wiki/JsonWireProtocol), and extend it so that you can specify mobile-targeted [desired capabilities](/docs/en/writing-running-appium/caps.md) to run your test through Appium.
|
||||
Essentially, we support a subset of the [Selenium WebDriver JSON Wire Protocol](https://w3c.github.io/webdriver/webdriver-spec.html), and extend it so that you can specify mobile-targeted [desired capabilities](/docs/en/writing-running-appium/caps.md) to run your test through Appium.
|
||||
|
||||
You find elements by using a subset of WebDriver's element-finding strategies.
|
||||
See [finding elements](/docs/en/writing-running-appium/finding-elements.md) for detailed information. We also have several extensions to the JSON Wire Protocol for [automating mobile gestures](/docs/en/writing-running-appium/touch-actions.md) like tap, flick, and swipe.
|
||||
@@ -116,7 +116,7 @@ For the full list of Appium doc pages, visit [this directory](/docs/en/).
|
||||
### How It Works
|
||||
|
||||
Appium drives various native automation frameworks and provides an API based on
|
||||
Selenium's [WebDriver JSON wire protocol](https://code.google.com/p/selenium/wiki/JsonWireProtocol).
|
||||
Selenium's [WebDriver JSON wire protocol](https://w3c.github.io/webdriver/webdriver-spec.html).
|
||||
|
||||
Appium drives Apple's UIAutomation library for iOS support, which is based on
|
||||
[Dan Cuellar's](http://github.com/penguinho) work on iOS Auto.
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"use strict";
|
||||
|
||||
var spawn = require('child_process').spawn,
|
||||
_ = require('underscore');
|
||||
_ = require('lodash');
|
||||
|
||||
var args = process.argv.slice(2);
|
||||
|
||||
|
||||
+5
-5
@@ -23,14 +23,14 @@ Appium 是一个开源的,跨平台的自动化测试工具。
|
||||
|
||||
1. 因为 Appium 在所有平台上都使用了标准的自动化接口,
|
||||
所以你不需要以重新编译或者修改你的 app。
|
||||
2. 你可以用你喜欢的开发工具,使用任何 [WebDriver](https://code.google.com/p/selenium/wiki/JsonWireProtocol) 兼容的语言来编写测试用例。比如
|
||||
2. 你可以用你喜欢的开发工具,使用任何 [WebDriver](https://w3c.github.io/webdriver/webdriver-spec.html) 兼容的语言来编写测试用例。比如
|
||||
Java, [Objective-C](https://github.com/appium/selenium-objective-c),
|
||||
JavaScript with Node.js (in both [callback](https://github.com/admc/wd) and [yield-based](https://github.com/jlipps/yiewd) flavours),
|
||||
PHP, Python, [Ruby](https://github.com/appium/ruby_lib), C#, Clojure, 或者 Perl。
|
||||
可以使用标准的Selenium WebDriver API和特定语言的客户端库。
|
||||
3. 你可以使用任何测试框架。
|
||||
|
||||
投资 [WebDriver](https://code.google.com/p/selenium/wiki/JsonWireProtocol) 意味着你可以押宝在一个已经成为标准的独立,自由和开放的协议。你不会被任何专利限制。
|
||||
投资 [WebDriver](https://w3c.github.io/webdriver/webdriver-spec.html) 意味着你可以押宝在一个已经成为标准的独立,自由和开放的协议。你不会被任何专利限制。
|
||||
|
||||
|
||||
如果没有 Appium,你就只能用 Javascript 来写 Apple 的 UIAutomation 脚本,并且只能通过Instruments 应用来运行你的测试。同样的,在 Google 的UiAutomator体系下,你只能用 Java 写你的测试案例。Appium 最终开启了跨平台原生移动自动化的可能。
|
||||
@@ -69,7 +69,7 @@ Java, [Objective-C](https://github.com/appium/selenium-objective-c),
|
||||
* [Firefox OS Simulator](https://developer.mozilla.org/en/docs/Tools/Firefox_OS_Simulator)
|
||||
|
||||
### 快速开始
|
||||
启动 Appium server,并运行用你喜欢的 [WebDriver](https://code.google.com/p/selenium/wiki/JsonWireProtocol) 兼容的语言编写的测试用例。
|
||||
启动 Appium server,并运行用你喜欢的 [WebDriver](https://w3c.github.io/webdriver/webdriver-spec.html) 兼容的语言编写的测试用例。
|
||||
你可以用 node.js 或者下面的应用程序来运行 Appium
|
||||
|
||||
#### 使用 Node.js
|
||||
@@ -92,7 +92,7 @@ Java, [Objective-C](https://github.com/appium/selenium-objective-c),
|
||||
我们支持 [Selenium WebDriver JSON Wire Protocol](https://github.com/appium/appium/wiki/JSON-Wire-Protocol:-Supported-Methods) 的一个子集,而且在此之上扩展了移动平台相关的特性。所以你可以
|
||||
指定特定移动平台[desired capabilities](writing-running-appium/caps.cn.md) 来运行你的测试。
|
||||
|
||||
你可以通过 [WebDriver](https://code.google.com/p/selenium/wiki/JsonWireProtocol) 元素定位策略的子集来定位元素。更多信息请参考 [finding elements](writing-running-appium/finding-elements.cn.md)
|
||||
你可以通过 [WebDriver](https://w3c.github.io/webdriver/webdriver-spec.html) 元素定位策略的子集来定位元素。更多信息请参考 [finding elements](writing-running-appium/finding-elements.cn.md)
|
||||
为了支持移动手势[automating mobile gestures](writing-running-appium/touch-actions.cn.md),比如
|
||||
tap,flick 和 swipe,我们扩展了JSON Wire Protocolor。
|
||||
|
||||
@@ -102,7 +102,7 @@ Java, [Objective-C](https://github.com/appium/selenium-objective-c),
|
||||
|
||||
### 工作原理
|
||||
|
||||
Appium 可以驱动多种原生自动化框架,同时也提供基于 Selenium 的 [WebDriver JSON wire protocol](https://code.google.com/p/selenium/wiki/JsonWireProtocol) api。
|
||||
Appium 可以驱动多种原生自动化框架,同时也提供基于 Selenium 的 [WebDriver JSON wire protocol](https://w3c.github.io/webdriver/webdriver-spec.html) api。
|
||||
|
||||
基于[Dan Cuellar's](http://github.com/penguinho) 在 iOS 自动化上的成果,Appium 通过驱动 Apple 的 UIAutomation 库来提供 iOS 支持。
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
## 客户端类库列表及Appium服务端支持
|
||||
|
||||
这些类库封装了标准Selenium客户端类库,为用户提供所有常见的[JSON](https://code.google.com/p/selenium/wiki/JsonWireProtocol) 格式selenium命令以及额外的移动设备控制相关的命令,如多点**触控手势**和**屏幕朝向**。
|
||||
这些类库封装了标准Selenium客户端类库,为用户提供所有常见的[JSON](https://w3c.github.io/webdriver/webdriver-spec.html) 格式selenium命令以及额外的移动设备控制相关的命令,如多点**触控手势**和**屏幕朝向**。
|
||||
|
||||
Appium客户端类库实现了[Mobile JSON Wire Protocol](https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile)(一个标准协议的官方扩展草稿)和[W3C Webdriver spec](https://dvcs.w3.org/hg/webdriver/raw-file/default/webdriver-spec.html)(一个传输不可预知的自动化协议,该协议定义了MultiAction 接口)的元素。
|
||||
Appium客户端类库实现了[Mobile JSON Wire Protocol](https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md)(一个标准协议的官方扩展草稿)和[W3C Webdriver spec](https://dvcs.w3.org/hg/webdriver/raw-file/default/webdriver-spec.html)(一个传输不可预知的自动化协议,该协议定义了MultiAction 接口)的元素。
|
||||
|
||||
Appium 服务端定义了官方协议的扩展,为Appium 用户提供了方便的接口来执行各种设备动作,例如在测试过程中安装/卸载app。这就是为什么我们需要Appium 特定的客户端,而不是通用的Selenium 客户端。当然,Appium 客户端类库只是增加了一些功能,而实际上这些功能就是简单的扩展了Selenium 客户端,所以他们仍然可以用来运行通用的selenium会话。
|
||||
|
||||
|
||||
@@ -26,10 +26,10 @@ Appium 是一个自动化测试开源工具,支持 iOS 平台和 Android 平
|
||||
* Android 2.3+: Google's [Instrumentation](http://developer.android.com/reference/android/app/Instrumentation.html). (Instrumentation由单独的项目[Selendroid](http://selendroid.io)提供支持 )
|
||||
|
||||
为了满足第二点,我们把这些第三方框架封装成一套 API,[WebDriver](http://docs.seleniumhq.org/projects/webdriver/) API.WebDriver(也就是 "Selenium WebDriver") 指定了客户端到服务端的协议。
|
||||
(参见 [JSON Wire Protocol](https://code.google.com/p/selenium/wiki/JsonWireProtocol))。使用这种客户端-服务端的架构,我们可以使用任何语言来编写客户端,向服务端发送恰当的 HTTP 请求。
|
||||
(参见 [JSON Wire Protocol](https://w3c.github.io/webdriver/webdriver-spec.html))。使用这种客户端-服务端的架构,我们可以使用任何语言来编写客户端,向服务端发送恰当的 HTTP 请求。
|
||||
目前已经实现了大多数流行语言版本的客户端,这意味着你可以使用任何测试套件或者测试框架。客户端库就是简单的HTTP 客户,可以以任何你喜欢的方式潜入你的代码。换句话说,Appium 和 WebDriver 客户端不是技术意义上的“测试框架”,而是“自动化库”。你可以在你的测试环境中随意使用这些自动化库!
|
||||
|
||||
事实上 WebDriver 已经成为 web 浏览器自动化的标准,也成了 W3C 的标准 —— [W3C Working Draft](https://dvcs.w3.org/hg/webdriver/raw-file/tip/webdriver-spec.html)。我们又何必为移动做一个完全不同的呢?所以我们扩充了[WebDriver 的协议](https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile),在原有的基础上添加移动自动化相关的 API 方法,这也满足了第三条理念。
|
||||
事实上 WebDriver 已经成为 web 浏览器自动化的标准,也成了 W3C 的标准 —— [W3C Working Draft](https://dvcs.w3.org/hg/webdriver/raw-file/tip/webdriver-spec.html)。我们又何必为移动做一个完全不同的呢?所以我们扩充了[WebDriver 的协议](https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md),在原有的基础上添加移动自动化相关的 API 方法,这也满足了第三条理念。
|
||||
|
||||
第四条就不用说了,[Appium 是开源的](https://github.com/appium/appium)。
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Appium支持webdriver定位策略的子集
|
||||
* 根据"class"定位(例如, UI组件类型)
|
||||
* 根据"xpath"定位 (例如,具有一定约束的路径抽象标示, 基于XPath方式)
|
||||
|
||||
另外, Appium 还支持部分 [Mobile JSON 连接协议](https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile) 的定位策略
|
||||
另外, Appium 还支持部分 [Mobile JSON 连接协议](https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md) 的定位策略
|
||||
|
||||
* `ios uiautomation`: 一个递归地、对应使用 [UIAutomation library](ios_predicate.cn.md) 搜索元素的字符串(iOS-only)
|
||||
* `android uiautomator`: 一个递归地、对应使用 [UiAutomator Api](uiautomator_uiselector.cn.md)搜索元素的字符串 (Android-only)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## 调整网络设置
|
||||
|
||||
Selenium 的 [Mobile JSON Wire Protocol Specification](https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile) 支持一个获取和设置设备网络连接的 [API](https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile#104) 。这个 API 通过位掩码(bitmask)工作,把所有可能的状态用一个整型数据表示:
|
||||
Selenium 的 [Mobile JSON Wire Protocol Specification](https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md) 支持一个获取和设置设备网络连接的 [API](https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md#104) 。这个 API 通过位掩码(bitmask)工作,把所有可能的状态用一个整型数据表示:
|
||||
|
||||
| 值 (别名) | 数据连接 | Wifi 连接 | 飞行模式 |
|
||||
| ------------------ | ---- | ---- | ------------- |
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
## List of client libraries with Appium server support
|
||||
|
||||
These libraries wrap standard Selenium client libraries to provide all the regular selenium commands dictated by the [JSON Wire protocol](https://code.google.com/p/selenium/wiki/JsonWireProtocol), and add extra commands related to controlling mobile devices, such as **multi-touch gestures** and **screen orientation**.
|
||||
These libraries wrap standard Selenium client libraries to provide all the regular selenium commands dictated by the [JSON Wire protocol](https://w3c.github.io/webdriver/webdriver-spec.html), and add extra commands related to controlling mobile devices, such as **multi-touch gestures** and **screen orientation**.
|
||||
|
||||
Appium client libraries implement the [Mobile JSON Wire Protocol](https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile) (an official draft extension to the standard protocol), and elements of the [W3C Webdriver spec](https://dvcs.w3.org/hg/webdriver/raw-file/default/webdriver-spec.html) (a transport-agnostic automation spec; this is where the MultiAction API is defined).
|
||||
Appium client libraries implement the [Mobile JSON Wire Protocol](https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md) (an official draft extension to the standard protocol), and elements of the [W3C Webdriver spec](https://dvcs.w3.org/hg/webdriver/raw-file/default/webdriver-spec.html) (a transport-agnostic automation spec; this is where the MultiAction API is defined).
|
||||
|
||||
The Appium server itself defines custom extensions to the official protocols, giving Appium users helpful access to various device behaviors (such as installing/uninstalling apps during the course of a test session). This is why we need Appium-specific clients, not just the 'vanilla' Selenium clients. Of course, Appium client libraries only **add** functionality (in fact, they simply extend the standard Selenium clients), so they can still be used to run regular Selenium sessions.
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ third-party code or frameworks to your app. This means **you're testing the same
|
||||
We meet requirement #2 by wrapping the vendor-provided frameworks in one API,
|
||||
the [WebDriver](http://docs.seleniumhq.org/projects/webdriver/) API.
|
||||
WebDriver (aka "Selenium WebDriver") specifies a client-server protocol
|
||||
(known as the [JSON Wire Protocol](https://code.google.com/p/selenium/wiki/JsonWireProtocol)).
|
||||
(known as the [JSON Wire Protocol](https://w3c.github.io/webdriver/webdriver-spec.html)).
|
||||
Given this client-server architecture, a client written in any language can
|
||||
be used to send the appropriate HTTP requests to the server. There are
|
||||
already [clients written in every popular programming language](http://appium.io/downloads). This also
|
||||
@@ -50,7 +50,7 @@ manage your test environment any way you like!
|
||||
|
||||
We meet requirement #3 in the same way: WebDriver has become the de facto
|
||||
standard for automating web browsers, and is a [W3C Working Draft](https://dvcs.w3.org/hg/webdriver/raw-file/tip/webdriver-spec.html).
|
||||
Why do something totally different for mobile? Instead we have [extended the protocol](https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile)
|
||||
Why do something totally different for mobile? Instead we have [extended the protocol](https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md)
|
||||
with extra API methods useful for mobile automation.
|
||||
|
||||
It should be obvious that requirement #4 is a given -- you're reading this
|
||||
@@ -89,7 +89,7 @@ during a Safari automation session, we're allowed to use JavaScript to open
|
||||
up new windows. See the [capabilities doc](/docs/en/writing-running-appium/caps.md) for the complete list of capabilities available for Appium.
|
||||
|
||||
**Appium Server**<br/>
|
||||
Appium is a server written in Node.js. It can be built and installed [from source](github.com/appium/appium) or installed directly from NPM:
|
||||
Appium is a server written in Node.js. It can be built and installed [from source](https://github.com/appium/appium/blob/master/docs/en/contributing-to-appium/appium-from-source.md) or installed directly from NPM:
|
||||
```
|
||||
$ npm install -g appium
|
||||
$ appium
|
||||
|
||||
@@ -12,9 +12,9 @@ WebDriver protocol for everything.
|
||||
Here are the steps required to talk to a web view in your Appium test:
|
||||
|
||||
1. Navigate to a portion of your app where a web view is active
|
||||
1. Call [GET session/:sessionId/contexts](https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile)
|
||||
1. Call [GET session/:sessionId/contexts](https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md)
|
||||
1. This returns a list of contexts we can access, like 'NATIVE_APP' or 'WEBVIEW_1'
|
||||
1. Call [POST session/:sessionId/context](https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile)
|
||||
1. Call [POST session/:sessionId/context](https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md)
|
||||
with the id of the context you want to access
|
||||
1. (This puts your Appium session into a mode where all commands are
|
||||
interpreted as being intended for automating the web view,
|
||||
|
||||
@@ -5,11 +5,11 @@ the command line (appium does this under the hood for xcode < 7). See [iwd](http
|
||||
|
||||
For enabling iwd for xcode >= 7,
|
||||
- Checkout [appium-instruments](https://github.com/appium/appium-instruments)
|
||||
- Run `xcode_iwd.sh` present in `<appium-instruments>/bin/`
|
||||
- Run `xcode-iwd.sh` present in `<appium-instruments>/bin/`
|
||||
|
||||
```
|
||||
sh <appium-instruments>/bin/xcode_iwd.sh <path to xcode> <path to appium-instruments>
|
||||
sh <appium-instruments>/bin/xcode-iwd.sh <path to xcode> <path to appium-instruments>
|
||||
```
|
||||
eg. `sh ./bin/xcode_iwd.sh /Applications/Xcode.app /Users/xyz/appium-instruments/`
|
||||
eg. `sh ./bin/xcode-iwd.sh /Applications/Xcode.app /Users/xyz/appium-instruments/`
|
||||
|
||||
Note: iwd with xcode7 will only work for iOS >= 9.0, you can switch to older xcode for iOS < 9.0
|
||||
|
||||
@@ -38,7 +38,7 @@ instructions.
|
||||
* Versions: 2.3 and up
|
||||
* Versions 2.3 through 4.2 are supported via Appium's bundled version of
|
||||
[Selendroid](http://selendroid.io), which utilizes [Instrumentation](http://developer.android.com/reference/android/app/Instrumentation.html). Selendroid has a different set of commands than the default Appium (though this is rapidly being minimized) and a different support profile. To access this automation backend, use the `automationName` capability with the value `Selendroid`.
|
||||
* Versions 4.2 and up are supported via Appium's own [UiAutomator](http://developer.android.com/tools/help/uiautomator/index.html)
|
||||
* Versions 4.2 and up are supported via Appium's own [UiAutomator](http://developer.android.com/tools/testing-support-library/index.html#UIAutomator)
|
||||
libraries. This is the default automation backend.
|
||||
* Devices: Android emulators and real Android devices
|
||||
* Native app support: Yes
|
||||
|
||||
@@ -65,3 +65,67 @@ If you're using multiple Xcode versions, you can switch between them using:
|
||||
|
||||
Instructions for setting up Android and running tests on Mac OS X are the same as
|
||||
those on Linux. See the [Android setup docs](/docs/en/appium-setup/android-setup.md).
|
||||
|
||||
### Running iOS tests on OS X using Jenkins
|
||||
|
||||
First download the jenkins-cli.jar and verify the Mac successfully connects to Jenkins master. Ensure you've run the `authorize_ios` command mentioned above.
|
||||
|
||||
`wget https://jenkins.ci.cloudbees.com/jnlpJars/jenkins-cli.jar`
|
||||
|
||||
```
|
||||
java -jar jenkins-cli.jar \
|
||||
-s https://team-appium.ci.cloudbees.com \
|
||||
-i ~/.ssh/id_rsa \
|
||||
on-premise-executor \
|
||||
-fsroot ~/jenkins \
|
||||
-labels osx \
|
||||
-name mac_appium
|
||||
```
|
||||
|
||||
Next define a LaunchAgent for Jenkins to launch automatically on login. A LaunchDaemon will not work because daemons don't have GUI access. Make sure the plist doesn't contain the `SessionCreate` or `User` key as that may prevent tests from running. You'll see a `Failed to authorize rights` error if misconfigured.
|
||||
|
||||
```
|
||||
$ sudo nano /Library/LaunchAgents/com.jenkins.ci.plist
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.jenkins.ci</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>java</string>
|
||||
<string>-Djava.awt.headless=true</string>
|
||||
<string>-jar</string>
|
||||
<string>/Users/appium/jenkins/jenkins-cli.jar</string>
|
||||
<string>-s</string>
|
||||
<string>https://instructure.ci.cloudbees.com</string>
|
||||
<string>on-premise-executor</string>
|
||||
<string>-fsroot</string>
|
||||
<string>/Users/appium/jenkins</string>
|
||||
<string>-executors</string>
|
||||
<string>1</string>
|
||||
<string>-labels</string>
|
||||
<string>mac</string>
|
||||
<string>-name</string>
|
||||
<string>mac_appium</string>
|
||||
</array>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>StandardOutPath</key>
|
||||
<string>/Users/appium/jenkins/stdout.log</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/Users/appium/jenkins/error.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
Finally set the owner, permissions, and then start the agent.
|
||||
|
||||
```
|
||||
sudo chown root:wheel /Library/LaunchAgents/com.jenkins.ci.plist
|
||||
sudo chmod 644 /Library/LaunchAgents/com.jenkins.ci.plist
|
||||
|
||||
launchctl load /Library/LaunchAgents/com.jenkins.ci.plist
|
||||
launchctl start com.jenkins.ci
|
||||
```
|
||||
|
||||
@@ -15,12 +15,14 @@ To get started:
|
||||
|
||||
1. Install [node.js](http://nodejs.org/download/) (v.0.12 or greater). Use the
|
||||
installer from nodejs.org.
|
||||
1. Install `appium-doctor` in order to check your system: `npm install appium-doctor -g`.
|
||||
Then run by typing `appium-doctor`.
|
||||
1. Install the [Android SDK](http://developer.android.com/sdk/index.html).
|
||||
You will need to run the 'android' tool (included in the SDK, in the `tools` folder) and make sure
|
||||
you have an API Level 17 or greater API installed. Set `ANDROID_HOME` to be
|
||||
your Android SDK path and add the `tools` and `platform-tools` folders to your
|
||||
PATH variable.
|
||||
1. Install the Java JDK and set `JAVA_HOME` to your JDK folder.
|
||||
1. Install the Java JDK and set `JAVA_HOME` to your JDK's `bin` folder.
|
||||
1. Install [Apache Ant](http://ant.apache.org/bindownload.cgi) or use the one
|
||||
that comes with the Android Windows SDK in the eclipse\plugins folder. Be
|
||||
sure to add the folder containing Ant to your PATH variable.
|
||||
|
||||
@@ -22,7 +22,9 @@ git clone https://github.com/appium/appium.git
|
||||
cd appium
|
||||
npm install
|
||||
gulp transpile # requires gulp, see below
|
||||
sudo node_modules/.bin/authorize-ios.js # for ios only
|
||||
sudo node_modules/.bin/authorize-ios # for ios only
|
||||
# alternatively
|
||||
sudo $(npm bin)/authorize-ios
|
||||
node .
|
||||
```
|
||||
|
||||
@@ -81,7 +83,10 @@ have to modify your `/etc/authorization` file in one of two ways:
|
||||
|
||||
```center
|
||||
sudo node_modules/.bin/authorize-ios.js
|
||||
```
|
||||
|
||||
# alternatively
|
||||
sudo $(npm bin)/authorize-ios
|
||||
```
|
||||
|
||||
At this point, run:
|
||||
|
||||
|
||||
@@ -16,14 +16,14 @@
|
||||
- starts baseServer (appium-express)
|
||||
- passes routes given by driver
|
||||
|
||||
## appium-express
|
||||
## appium-express (part of appium-base-driver)
|
||||
- starts express server (allows x-domain-origin)
|
||||
- initialises routes from AppiumDriver
|
||||
- timeout handling
|
||||
- serves a static page for test purposes
|
||||
- connects req/res events to logger
|
||||
|
||||
## mobile-json-wire-protocol
|
||||
## mobile-json-wire-protocol (part of appium-base-driver)
|
||||
- provides list of Appium commands
|
||||
- subclassed by drivers that will use the protocol
|
||||
- kind of middleware between client and driver
|
||||
@@ -146,7 +146,7 @@
|
||||
- launches, restarts and stops (or kills all) chrome instances
|
||||
- uses appium-jsonwp-proxy to send json wire protocol commands to the driver
|
||||
|
||||
## jsonwp-proxy
|
||||
## jsonwp-proxy (part of appium-base-driver)
|
||||
- allows to send json wire protocol commands to a server that understands it (browser drivers)
|
||||
- parses response into json
|
||||
- allows to proxy requests to a proxied server
|
||||
|
||||
@@ -825,7 +825,7 @@ swipe start_x: 75, start_y: 500, end_x: 75, end_y: 0, duration: 0.8
|
||||
|
||||
```python
|
||||
# python
|
||||
driver.swipe(start=75, starty=500, endx=75, endy=0, duration=800)
|
||||
driver.swipe(start_x=75, start_y=500, end_x=75, end_y=0, duration=800)
|
||||
```
|
||||
|
||||
```java
|
||||
@@ -987,27 +987,28 @@ Scroll to an element.
|
||||
|
||||
```ruby
|
||||
# ruby
|
||||
element = find_element :name, 'Element Name'
|
||||
execute_script "mobile: scrollTo", :element => element.ref
|
||||
element = find_element :name, "Element Name"
|
||||
execute_script "mobile: scroll", direction: "down", element: element.ref
|
||||
```
|
||||
|
||||
```python
|
||||
# python
|
||||
driver.execute_script("mobile: scrollTo", {"element": element.id})
|
||||
driver.execute_script("mobile: scroll", {"direction": "down", element: element.id})
|
||||
```
|
||||
|
||||
```java
|
||||
// java
|
||||
WebElement element = driver.findElement(By.name("Element Name"));
|
||||
HashMap<String, String> arguments = new HashMap<String, String>();
|
||||
arguments.put("element", element.getId());
|
||||
(JavascriptExecutor)driver.executeScript("mobile: scrollTo", arguments);
|
||||
JavascriptExecutor js = (JavascriptExecutor) driver;
|
||||
HashMap<String, String> scrollObject = new HashMap<String, String>();
|
||||
scrollObject.put("direction", "down");
|
||||
scrollObject.put("element", ((RemoteWebElement) element).getId());
|
||||
js.executeScript("mobile: scroll", scrollObject);
|
||||
```
|
||||
|
||||
```javascript
|
||||
// javascript
|
||||
return driver.elementByName().then(function (el) {
|
||||
return driver.execute('mobile: scrollTo', {element: el.value});
|
||||
driver.execute("mobile: scroll", [{direction: "down", element: el.value}]);
|
||||
});
|
||||
```
|
||||
|
||||
@@ -1019,7 +1020,10 @@ $this->scroll($els[count($els) - 1], $els[0]);
|
||||
|
||||
```csharp
|
||||
// c#
|
||||
todo: csharp
|
||||
Dictionary<string, string> scrollObject = new Dictionary<string, string>();
|
||||
scrollObject.Add("direction", "down");
|
||||
scrollObject.Add("element", <element_id>);
|
||||
((IJavaScriptExecutor)driver).ExecuteScript("mobile: scroll", scrollObject));
|
||||
```
|
||||
|
||||
### Pull file
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
|`intentAction`| Intent action which will be used to start activity (default `android.intent.action.MAIN`)| e.g.`android.intent.action.MAIN`, `android.intent.action.VIEW`|
|
||||
|`intentCategory`| Intent category which will be used to start activity (default `android.intent.category.LAUNCHER`)| e.g. `android.intent.category.LAUNCHER`, `android.intent.category.APP_CONTACTS`
|
||||
|`intentFlags`| Flags that will be used to start activity (default `0x10200000`)| e.g. `0x10200000`
|
||||
|`optionalIntentArguments`| Additional intent arguments that will be used to start activity. See [Intent arguments](http://developer.android.com/tools/help/adb.html#IntentSpec) | e.g. `--esn <EXTRA_KEY>`, `--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE>`, etc.
|
||||
|`optionalIntentArguments`| Additional intent arguments that will be used to start activity. See [Intent arguments](http://developer.android.com/reference/android/content/Intent.html) | e.g. `--esn <EXTRA_KEY>`, `--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE>`, etc.
|
||||
|`dontStopAppOnReset`| Doesn't stop the process of the app under test, before starting the app using adb. If the app under test is created by another anchor app, setting this false, allows the process of the anchor app to be still alive, during the start of the test app using adb. In other words, with `dontStopAppOnReset` set to `true`, we will not include the `-S` flag in the `adb shell am start` call. With this capability omitted or set to `false`, we include the `-S` flag. Default `false`| `true` or `false`|
|
||||
|`unicodeKeyboard`| Enable Unicode input, default `false`| `true` or `false`|
|
||||
|`resetKeyboard`| Reset keyboard to its original state, after running Unicode tests with `unicodeKeyboard` capability. Ignored if used alone. Default `false`| `true` or `false`|
|
||||
@@ -58,6 +58,7 @@
|
||||
|`disableAndroidWatchers`| Disables android watchers that watch for application not responding and application crash, this will reduce cpu usage on android device/emulator. This capability will work only with UiAutomator and not with selendroid, default `false`| `true` or `false`|
|
||||
|`chromeOptions`| Allows passing chromeOptions capability for ChromeDriver. For more information see [chromeOptions](https://sites.google.com/a/chromium.org/chromedriver/capabilities) | `chromeOptions: {args: ['--disable-popup-blocking']}` |
|
||||
|`recreateChromeDriverSessions`| Kill ChromeDriver session when moving to a non-ChromeDriver webview. Defaults to `false` | `true` or `false`|
|
||||
|`nativeWebScreenshot`| In a web context, use native (adb) method for taking a screenshot, rather than proxying to ChromeDriver. Defaults to `false` | `true` or `false`|
|
||||
|
||||
### iOS Only
|
||||
|
||||
|
||||
@@ -3,10 +3,13 @@
|
||||
Appium 1.5 does away with most CLI flags; the remainder can be converted into JSON and made part of the `--default-capabilities` flag. For example:
|
||||
|
||||
```
|
||||
# raw JSON as an argument
|
||||
--default-capabilities '{"app": "myapp.app", "deviceName": "iPhone Simulator"}'
|
||||
# or the name of a JSON file
|
||||
--default-capabilities /path/to/file.json
|
||||
```
|
||||
|
||||
**Windows users** will need to escape the quotes: `--default-capabilities "{\"app\": \"myapp.app\"}"`
|
||||
**Windows users** will need to escape the quotes in JSON passed on the command line: `--default-capabilities "{\"app\": \"myapp.app\"}"`
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Appium supports a subset of the WebDriver locator strategies:
|
||||
* find by "xpath" (i.e., an abstract representation of a path to an element,
|
||||
with certain constraints)
|
||||
|
||||
Appium additionally supports some of the [Mobile JSON Wire Protocol](https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile) locator strategies
|
||||
Appium additionally supports some of the [Mobile JSON Wire Protocol](https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md) locator strategies
|
||||
|
||||
* `-ios uiautomation`: a string corresponding to a recursive element search
|
||||
using the [UIAutomation library](ios_predicate.md) (iOS-only)
|
||||
|
||||
@@ -9,7 +9,7 @@ It is worth looking at *'-ios uiautomation'* search strategy with **Predicates**
|
||||
```
|
||||
|
||||
Native JS search strategy (powered by Apple) provides much more flexibility and is like Xpath.
|
||||
**[Predicates](https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Predicates/predicates.html)** can be used to restrict an elements set to select only those ones for which some condition is true.
|
||||
**[Predicates](https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Predicates/AdditionalChapters/Introduction.html)** can be used to restrict an elements set to select only those ones for which some condition is true.
|
||||
|
||||
For example:
|
||||
|
||||
@@ -20,7 +20,7 @@ appiumDriver.findElementsByIosUIAutomation("collectionViews()[0].cells().withPre
|
||||
|
||||
\- will select only those ```UIACollectionCell``` elements that have visible ```UIAStaticText``` child elements, and themselves are childs of 1st ```UIACollectionView``` element that should be located under the main app window. Here ```staticTexts()``` and ```isVisible()``` are methods available in ```UIAElementArray``` and ```UIAElement``` classes respectively. **Note that ```UIAElementArray``` numbering begins with ```0``` unlike Xpath where indexes counting starts from ```1```**
|
||||
|
||||
Here's a list of available Predicates (mostly taken from [Predicates Programming Guide](https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Predicates/predicates.html))
|
||||
Here's a list of available Predicates (mostly taken from [Predicates Programming Guide](https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Predicates/AdditionalChapters/Introduction.html))
|
||||
|
||||
### Basic Comparisons
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ class ContextTests extends PHPUnit_Extensions_AppiumTestCase
|
||||
Pre-requisites:
|
||||
|
||||
* Make sure Chrome (an app with the package `com.android.chrome`) is installed on your device or emulator. Getting Chrome for the x86 version of the emulator is not currently possible without building Chromium, so you may want to run an ARM emulator and then copy a Chrome APK from a real device to get Chrome on an emulator.
|
||||
* If downloaded from [NPM](https://www.npmjs.org/package/appium), or running from the [.app](https://github.com/appium/appium-dot-app), nothing needs to be done. If running from source, `npm install` will download ChromeDriver and put it in `node_modules/appium-android-driver/node_modules/appium-chromedriver/chromedriver`. A particular version can be specified by passing the `--chromedriver_version` config property (e.g., `npm install appium --chromedriver_version="2.16"`), otherwise the most recent one will be retrieved.
|
||||
* If downloaded from [NPM](https://www.npmjs.org/package/appium), or running from the [.app](https://github.com/appium/appium-dot-app), nothing needs to be done. If running from source, `npm install` will download ChromeDriver and put it in `node_modules/appium-chromedriver/chromedriver/<OS name>/` for users having npm v3+ and for npm v2 it will be in `node_modules/appium-android-driver/node_modules/appium-chromedriver/chromedriver/<OS name>/`. A particular version can be specified by passing the `--chromedriver_version` config property (e.g., `npm install appium --chromedriver_version="2.16"`), otherwise the most recent one will be retrieved.
|
||||
|
||||
Then, use desired capabilities like these to run your test in Chrome:
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## Adjusting Network Connection
|
||||
|
||||
The Selenium [Mobile JSON Wire Protocol Specification](https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile) supports an [API](https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile#104) for getting and setting the network connection for a device. The API works through a bitmask, assigning an integer to each possible state:
|
||||
The Selenium [Mobile JSON Wire Protocol Specification](https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md) supports an [API](https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md#104) for getting and setting the network connection for a device. The API works through a bitmask, assigning an integer to each possible state:
|
||||
|
||||
| Value (Alias) | Data | Wifi | Airplane Mode |
|
||||
| ------------------ | ---- | ---- | ------------- |
|
||||
|
||||
@@ -91,9 +91,9 @@ First, make sure you have one and only one Android emulator or device
|
||||
connected. If you run `adb devices`, for example, you should see one device
|
||||
connected. This is the device Appium will use for tests. Of course, to have
|
||||
a device connected, you'll need to have made an Android AVD (see system
|
||||
setup ([Windows](running-on-windows.md),
|
||||
[Mac](running-on-osx.md),
|
||||
or [Linux](running-on-linux.md))
|
||||
setup ([Windows](https://github.com/appium/appium/blob/master/docs/en/appium-setup/running-on-windows.md),
|
||||
[Mac](https://github.com/appium/appium/blob/master/docs/en/appium-setup/running-on-osx.md),
|
||||
or [Linux](https://github.com/appium/appium/blob/master/docs/en/appium-setup/running-on-linux.md))
|
||||
for more information). If the Android SDK tools are on your path, you can
|
||||
simply run:
|
||||
|
||||
@@ -196,7 +196,7 @@ all set now!
|
||||
### Running your test app with Appium (Android devices < 4.2, and hybrid tests)
|
||||
|
||||
Android devices before version 4.2 (API Level 17) do not have Google's
|
||||
[UiAutomator framework](http://developer.android.com/tools/help/uiautomator/index.html)
|
||||
[UiAutomator framework](http://developer.android.com/tools/testing-support-library/index.html#UIAutomator)
|
||||
installed. This is what Appium uses to perform the automation behaviors on
|
||||
the device. For earlier devices or tests of hybrid (webview-based) apps,
|
||||
Appium comes bundled with another automation backend called [Selendroid]
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
## Appium server arguments
|
||||
# Appium server arguments
|
||||
|
||||
Many Appium 1.5 server arguments have been deprecated in favor of the [--default-capabilities flag](/docs/en/writing-running-appium/default-capabilities-arg.md).
|
||||
|
||||
Usage: `node . [flags]`
|
||||
|
||||
### Server flags
|
||||
## Server flags
|
||||
All flags are optional, but some are required in conjunction with certain others.
|
||||
|
||||
|
||||
@@ -43,7 +43,6 @@ All flags are optional, but some are required in conjunction with certain others
|
||||
|`--chromedriver-executable`|null|ChromeDriver executable full path||
|
||||
|`--show-config`|false|Show info about the appium server configuration and exit||
|
||||
|`--no-perms-check`|false|Bypass Appium's checks to ensure we can read/write necessary files||
|
||||
|`--command-timeout`|60|The default command timeout for the server to use for all sessions (in seconds and should be less than 2147483). Will still be overridden by newCommandTimeout cap||
|
||||
|`--strict-caps`|false|Cause sessions to fail if desired caps are sent in that Appium does not recognize as valid for the selected device||
|
||||
|`--isolate-sim-device`|false|Xcode 6 has a bug on some platforms where a certain simulator can only be launched without error if all other simulator devices are first deleted. This option causes Appium to delete all devices other than the one being used by Appium. Note that this is a permanent deletion, and you are responsible for using simctl or xcode to manage the categories of devices used with Appium.||
|
||||
|`--tmp`|null|Absolute path to directory Appium can use to manage temporary files, like built-in iOS apps it needs to move around. On *nix/Mac defaults to /tmp, on Windows defaults to C:\Windows\Temp||
|
||||
@@ -52,7 +51,8 @@ All flags are optional, but some are required in conjunction with certain others
|
||||
|`--suppress-adb-kill-server`|false|(Android-only) If set, prevents Appium from killing the adb server instance||
|
||||
|`--async-trace`|false|Add long stack traces to log entries. Recommended for debugging only.||
|
||||
|`--webkit-debug-proxy-port`|27753|(IOS-only) Local port used for communication with ios-webkit-debug-proxy|`--webkit-debug-proxy-port 27753`|
|
||||
|`--default-capabilities`|{}|Set the default desired capabilities, which will be set on each session unless overridden by received capabilities. [More infomation](/docs/en/writing-running-appium/default-capabilities-arg.md). |`--default-capabilities '{"app": "myapp.app", "deviceName": "iPhone Simulator"}'`|
|
||||
|`-dc`, `--default-capabilities`|{}|Set the default desired capabilities, which will be set on each session unless overridden by received capabilities.|`--default-capabilities [ '{"app": "myapp.app", "deviceName": "iPhone Simulator"}' | /path/to/caps.json ]`|
|
||||
|`--command-timeout`|60|[DEPRECATED] No effect. This used to be the default command timeout for the server to use for all sessions (in seconds and should be less than 2147483). Use newCommandTimeout cap instead||
|
||||
|`-k`, `--keep-artifacts`|false|[DEPRECATED] - no effect, trace is now in tmp dir by default and is cleared before each run. Please also refer to the --trace-dir flag.||
|
||||
|`--platform-name`|null|[DEPRECATED] - Name of the mobile platform: iOS, Android, or FirefoxOS|`--platform-name iOS`|
|
||||
|`--platform-version`|null|[DEPRECATED] - Version of the mobile platform|`--platform-version 7.1`|
|
||||
|
||||
@@ -44,10 +44,18 @@ to another position, and removing their finger from the screen.
|
||||
Appium performs the events in sequence. You can add a `wait` event to control
|
||||
the timing of the gesture.
|
||||
|
||||
`moveTo` coordinates are *relative* to the current position. For example, dragging from
|
||||
100,100 to 200,200 can be achieved by:
|
||||
```
|
||||
.press(100,100) // Start at 100,00
|
||||
.moveTo(100,100) // Increase X & Y by 100 each, ending up at 200,200
|
||||
|
||||
```
|
||||
|
||||
The appium client libraries have different ways of implementing this, for example:
|
||||
you can pass in coordinates or an element to a `moveTo` event. Passing both
|
||||
coordinates _and_ an element will treat the coordinates as relative to the
|
||||
elements position, rather than absolute.
|
||||
element's position, rather than relative to the current position.
|
||||
|
||||
Calling the `perform` event sends the entire sequence of events to appium,
|
||||
and the touch gesture is run on your device.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
## uiautomator UiSelector
|
||||
|
||||
Appium enables searching using [UiSelectors](http://developer.android.com/tools/help/uiautomator/UiSelector.html).
|
||||
[UiScrollable](http://developer.android.com/tools/help/uiautomator/UiScrollable.html)
|
||||
Appium enables searching using [UiSelectors](http://developer.android.com/reference/android/support/test/uiautomator/UiSelector.html).
|
||||
[UiScrollable](http://developer.android.com/reference/android/support/test/uiautomator/UiScrollable.html)
|
||||
is also supported.
|
||||
|
||||
Note that the index selector is unreliable so prefer instance instead. The
|
||||
|
||||
@@ -13,14 +13,14 @@
|
||||
## 为什么选择appium ?
|
||||
|
||||
1. 你不需要以任何方式重新编译或者修改你的app,就可以在所有的平台上使用标准的自动化APIs
|
||||
2. 你可以用你喜欢的开发工具使用任何 [WebDriver](https://code.google.com/p/selenium/wiki/JsonWireProtocol) 兼容的语言来编写测试用例.比如
|
||||
2. 你可以用你喜欢的开发工具使用任何 [WebDriver](https://w3c.github.io/webdriver/webdriver-spec.html) 兼容的语言来编写测试用例.比如
|
||||
Java, [Objective-C](https://github.com/appium/selenium-objective-c),
|
||||
JavaScript with Node.js (in both [callback](https://github.com/admc/wd) and [yield-based](https://github.com/jlipps/yiewd) flavours),
|
||||
PHP, Python, [Ruby](https://github.com/appium/ruby_lib), C#, Clojure, 或者 Perl
|
||||
可以使用标准的Selenium WebDriver API和特定语言的客户端库.
|
||||
3. 你可以使用任何测试框架.
|
||||
|
||||
依托 [WebDriver](https://code.google.com/p/selenium/wiki/JsonWireProtocol) 意味着你可以押宝在一个已经成为事实上标准的独立,自由和开放的协议.而不会被限制在任何的专利中
|
||||
依托 [WebDriver](https://w3c.github.io/webdriver/webdriver-spec.html) 意味着你可以押宝在一个已经成为事实上标准的独立,自由和开放的协议.而不会被限制在任何的专利中
|
||||
|
||||
|
||||
如果在没有使用Appium的情况,你使用了Apple的UIAutomation库就只能通过Javascript,并且只能通过Instruments application插桩应用来运行你的测试.
|
||||
@@ -54,7 +54,7 @@ Appium最终开启了跨平台原生移动自动化的可能.
|
||||
* [Firefox OS Simulator](https://developer.mozilla.org/en/docs/Tools/Firefox_OS_Simulator)
|
||||
|
||||
## 快速开始
|
||||
启动Appium server,并运行用你喜欢的 [WebDriver](https://code.google.com/p/selenium/wiki/JsonWireProtocol) 兼容的语言编写的测试用例.
|
||||
启动Appium server,并运行用你喜欢的 [WebDriver](https://w3c.github.io/webdriver/webdriver-spec.html) 兼容的语言编写的测试用例.
|
||||
你可以用node.js或者下面的应用程序来运行Appium
|
||||
|
||||
### 使用Node.js
|
||||
@@ -72,7 +72,7 @@ Appium最终开启了跨平台原生移动自动化的可能.
|
||||
我们支持 [Selenium WebDriver JSON Wire Protocol](https://github.com/appium/appium/wiki/JSON-Wire-Protocol:-Supported-Methods) 的一个子集
|
||||
首先还需要指定特定移动平台相关的 [desired capabilities](caps.cn.md) 来通过appium运行你的测试
|
||||
|
||||
你可以通过 [WebDriver](https://code.google.com/p/selenium/wiki/JsonWireProtocol) 的元素定位策略的一个子集来定位元素
|
||||
你可以通过 [WebDriver](https://w3c.github.io/webdriver/webdriver-spec.html) 的元素定位策略的一个子集来定位元素
|
||||
更多信息请参考 [finding elements](finding-elements.cn.md)
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ Appium最终开启了跨平台原生移动自动化的可能.
|
||||
|
||||
## 工作原理
|
||||
|
||||
Appium通过多种原生自动化框架来提供基于Selenium的 [WebDriver JSON wire protocol](https://code.google.com/p/selenium/wiki/JsonWireProtocol) api
|
||||
Appium通过多种原生自动化框架来提供基于Selenium的 [WebDriver JSON wire protocol](https://w3c.github.io/webdriver/webdriver-spec.html) api
|
||||
|
||||
Appium驱动Apple的UIAutomation库提供IOS支持. UIAutomation基于[Dan Cuellar's](http://github.com/penguinho)
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ Appium 是一个自动化测试开源工具,支持 iOS 平台和 Android 平
|
||||
|
||||
为了满足第二点,我们把这些第三方框架封装成一套 API, [WebDriver](http://docs.seleniumhq.org/projects/webdriver/) API.
|
||||
WebDriver (也就是 "Selenium WebDriver") 指定了客户端到服务端的协议。
|
||||
(参见 [JSON Wire Protocol](https://code.google.com/p/selenium/wiki/JsonWireProtocol))。
|
||||
(参见 [JSON Wire Protocol](https://w3c.github.io/webdriver/webdriver-spec.html))。
|
||||
使用这种客户端-服务端的架构,我们可以使用任何语言来编写客户端,向服务端发送恰当的 HTTP 请求。
|
||||
而且目前已经有大多数流行语言版本的客户端实现了。这也意味着你可以使用任何测试套件或者测试框架。客户端库就是简单的
|
||||
HTTP 客户,可以以任何你喜欢的方式潜入你的代码。换句话说,Appium 和 WebDriver 客户端不是技术意义上的“测试框架”,
|
||||
@@ -41,7 +41,7 @@ HTTP 客户,可以以任何你喜欢的方式潜入你的代码。换句话说
|
||||
|
||||
事实上 WebDriver 已经成为 web 浏览器自动化的标准,也成了 W3C 的标准 —— [W3C Working Draft](https://dvcs.w3.org/hg/webdriver/raw-file/tip/webdriver-spec.html)。
|
||||
我们又何必为移动做一个完全不同的呢?
|
||||
所以我们扩充了[WebDriver 的协议](https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile),
|
||||
所以我们扩充了[WebDriver 的协议](https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md),
|
||||
在原有的基础上添加移动自动化相关的 API 方法,这也也满足了第三条理念。
|
||||
|
||||
第四条就不用说了,[Appium 是开源的](https://github.com/appium/appium)。
|
||||
|
||||
+23
-8
@@ -1,14 +1,12 @@
|
||||
import _ from 'lodash';
|
||||
import log from './logger';
|
||||
import { getAppiumConfig } from './config';
|
||||
import { fixCaps } from './helpers';
|
||||
import { BaseDriver } from 'appium-base-driver';
|
||||
import { BaseDriver, routeConfiguringFunction, errors,
|
||||
isSessionCommand } from 'appium-base-driver';
|
||||
import { FakeDriver } from 'appium-fake-driver';
|
||||
import { AndroidDriver } from 'appium-android-driver';
|
||||
import { IosDriver } from 'appium-ios-driver';
|
||||
import { SelendroidDriver } from 'appium-selendroid-driver';
|
||||
import { routeConfiguringFunction, errors,
|
||||
isSessionCommand } from 'mobile-json-wire-protocol';
|
||||
import B from 'bluebird';
|
||||
import util from 'util';
|
||||
|
||||
@@ -26,7 +24,7 @@ class AppiumDriver extends BaseDriver {
|
||||
}
|
||||
|
||||
sessionExists (sessionId) {
|
||||
return _.contains(_.keys(this.sessions), sessionId) &&
|
||||
return _.includes(_.keys(this.sessions), sessionId) &&
|
||||
this.sessions[sessionId].sessionId !== null;
|
||||
}
|
||||
|
||||
@@ -81,7 +79,7 @@ class AppiumDriver extends BaseDriver {
|
||||
|
||||
async getSessions () {
|
||||
let sessions = [];
|
||||
for (let [id, driver] of _.pairs(this.sessions)) {
|
||||
for (let [id, driver] of _.toPairs(this.sessions)) {
|
||||
sessions.push({id: id, capabilities: driver.caps});
|
||||
}
|
||||
return sessions;
|
||||
@@ -94,16 +92,33 @@ class AppiumDriver extends BaseDriver {
|
||||
log.info(`Creating new ${InnerDriver.name} session`);
|
||||
log.info('Capabilities:');
|
||||
util.inspect(caps);
|
||||
for (let [cap, value] of _.pairs(caps)) {
|
||||
for (let [cap, value] of _.toPairs(caps)) {
|
||||
log.info(` ${cap}: ${util.inspect(value)}`);
|
||||
}
|
||||
|
||||
// sessionOverride server flag check
|
||||
// this will need to be re-thought when we go to multiple session support
|
||||
if (this.args.sessionOverride && !!this.sessions && _.keys(this.sessions).length > 0) {
|
||||
log.info('Session override is on. Deleting other sessions.');
|
||||
for (let id of _.keys(this.sessions)) {
|
||||
log.info(` Deleting session '${id}'`);
|
||||
try {
|
||||
await this.deleteSession(id);
|
||||
} catch (ign) {
|
||||
// the error has already been logged in AppiumDriver.deleteSession
|
||||
// continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
curSessions = this.curSessionDataForDriver(InnerDriver);
|
||||
} catch (e) {
|
||||
throw new errors.SessionNotCreatedError(e.message);
|
||||
}
|
||||
|
||||
let d = new InnerDriver(this.args);
|
||||
let [innerSessionId, dCaps] = await d.createSession(fixCaps(caps), reqCaps, curSessions);
|
||||
let [innerSessionId, dCaps] = await d.createSession(caps, reqCaps, curSessions);
|
||||
this.sessions[innerSessionId] = d;
|
||||
|
||||
// Remove the session on unexpected shutdown, so that we are in a position
|
||||
|
||||
+1
-1
@@ -126,7 +126,7 @@ function validateServerArgs (parser, args) {
|
||||
|
||||
const nonDefaultArgs = getNonDefaultArgs(parser, args);
|
||||
|
||||
for (let [arg, validator] of _.pairs(validations)) {
|
||||
for (let [arg, validator] of _.toPairs(validations)) {
|
||||
if (_.has(nonDefaultArgs, arg)) {
|
||||
if (!validator(args[arg], arg)) {
|
||||
throw new Error(`Invalid argument for param ${arg}: ${args[arg]}`);
|
||||
|
||||
@@ -109,9 +109,13 @@ async function isAlreadyRegistered (jsonObject) {
|
||||
throw new Error(`Request failed`);
|
||||
}
|
||||
let responseData = JSON.parse(response.body);
|
||||
if (responseData.success !== true) {
|
||||
// if register fail, print the debug msg
|
||||
logger.debug(`Grid registration error: ${responseData.msg}`);
|
||||
}
|
||||
return responseData.success;
|
||||
} catch (err) {
|
||||
logger.debug("Hub down or not responding: ${err.message}");
|
||||
logger.debug(`Hub down or not responding: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import _ from 'lodash';
|
||||
import log from './logger';
|
||||
|
||||
|
||||
function fixCaps (originalCaps) {
|
||||
let caps = _.clone(originalCaps);
|
||||
// boolean capabilities can be passed in as strings 'false' and 'true'
|
||||
// which we want to translate into boolean values
|
||||
for (let [cap, value] of _.pairs(caps)) {
|
||||
if (_.isString(value)) {
|
||||
value = value.toLowerCase();
|
||||
if (value === 'true' || value === 'false') {
|
||||
log.debug(`Capability '${cap}' changed from string to boolean. This may cause unexpected behavior`);
|
||||
caps[cap] = (value === 'true');
|
||||
}
|
||||
}
|
||||
}
|
||||
return caps;
|
||||
}
|
||||
|
||||
|
||||
export { fixCaps };
|
||||
+20
-10
@@ -3,6 +3,7 @@ import { patchLogger } from 'appium-logger';
|
||||
import winston from 'winston';
|
||||
import { fs } from 'appium-support';
|
||||
import 'date-utils';
|
||||
import _ from 'lodash';
|
||||
|
||||
|
||||
// set up distributed logging before everything else
|
||||
@@ -12,10 +13,10 @@ global._global_npmlog = npmlog;
|
||||
// npmlog is used only for emitting, we use winston for output
|
||||
npmlog.level = "silent";
|
||||
const levels = {
|
||||
debug: 1,
|
||||
info: 2,
|
||||
warn: 3,
|
||||
error: 4,
|
||||
debug: 4,
|
||||
info: 3,
|
||||
warn: 2,
|
||||
error: 1,
|
||||
};
|
||||
|
||||
const colors = {
|
||||
@@ -99,7 +100,7 @@ function _createFileTransport (args, logLvl) {
|
||||
return transport;
|
||||
}
|
||||
|
||||
function _createWebhookTransport (args, logLvl) {
|
||||
function _createHttpTransport (args, logLvl) {
|
||||
let host = null,
|
||||
port = null;
|
||||
|
||||
@@ -109,8 +110,8 @@ function _createWebhookTransport (args, logLvl) {
|
||||
port = parseInt(hostAndPort[1], 10);
|
||||
}
|
||||
|
||||
let transport = new (winston.transports.Webhook)({
|
||||
name: "webhook",
|
||||
let transport = new (winston.transports.Http)({
|
||||
name: "http",
|
||||
host: host || '127.0.0.1',
|
||||
port: port || 9003,
|
||||
path: '/',
|
||||
@@ -157,9 +158,9 @@ async function _createTransports (args) {
|
||||
|
||||
if (args.webhook) {
|
||||
try {
|
||||
transports.push(_createWebhookTransport(args, fileLogLevel));
|
||||
transports.push(_createHttpTransport(args, fileLogLevel));
|
||||
} catch (e) {
|
||||
console.log(`Tried to attach logging to webhook at ${args.webhook} but ` +
|
||||
console.log(`Tried to attach logging to Http at ${args.webhook} but ` +
|
||||
`an error occurred: ${e.message}`);
|
||||
}
|
||||
}
|
||||
@@ -204,6 +205,15 @@ async function init (args) {
|
||||
}
|
||||
}
|
||||
|
||||
function clear () {
|
||||
if (logger) {
|
||||
for (let transport of _.keys(logger.transports)) {
|
||||
logger.remove(transport);
|
||||
}
|
||||
}
|
||||
npmlog.removeAllListeners('log');
|
||||
}
|
||||
|
||||
export { init };
|
||||
|
||||
export { init, clear };
|
||||
export default init;
|
||||
|
||||
+4
-4
@@ -4,7 +4,7 @@
|
||||
import { init as logsinkInit } from './logsink';
|
||||
import logger from './logger'; // logger needs to remain first of imports
|
||||
import _ from 'lodash';
|
||||
import { default as baseServer } from 'appium-express';
|
||||
import { server as baseServer } from 'appium-base-driver';
|
||||
import { asyncify } from 'asyncbox';
|
||||
import getParser from './parser';
|
||||
import { showConfig, checkNodeOk, validateServerArgs,
|
||||
@@ -38,14 +38,14 @@ async function preflightChecks (parser, args) {
|
||||
|
||||
function logDeprecationWarning (deprecatedArgs) {
|
||||
logger.warn('Deprecated server args:');
|
||||
for (let [arg, realArg] of _.pairs(deprecatedArgs)) {
|
||||
for (let [arg, realArg] of _.toPairs(deprecatedArgs)) {
|
||||
logger.warn(` ${arg.red} => ${realArg}`);
|
||||
}
|
||||
}
|
||||
|
||||
function logNonDefaultArgsWarning (args) {
|
||||
logger.info('Non-default server args:');
|
||||
for (let [arg, value] of _.pairs(args)) {
|
||||
for (let [arg, value] of _.toPairs(args)) {
|
||||
logger.info(` ${arg}: ${util.inspect(value)}`);
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ function logDefaultCapabilitiesWarning (caps) {
|
||||
logger.info('Default capabilities, which will be added to each request ' +
|
||||
'unless overridden by desired capabilities:');
|
||||
util.inspect(caps);
|
||||
for (let [cap, value] of _.pairs(caps)) {
|
||||
for (let [cap, value] of _.toPairs(caps)) {
|
||||
logger.info(` ${cap}: ${util.inspect(value)}`);
|
||||
}
|
||||
}
|
||||
|
||||
+35
-13
@@ -1,3 +1,4 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import _ from 'lodash';
|
||||
import { ArgumentParser } from 'argparse';
|
||||
@@ -259,16 +260,6 @@ const args = [
|
||||
help: 'Bypass Appium\'s checks to ensure we can read/write necessary files',
|
||||
}],
|
||||
|
||||
[['--command-timeout'], {
|
||||
defaultValue: 60,
|
||||
dest: 'defaultCommandTimeout',
|
||||
type: 'int',
|
||||
required: false,
|
||||
help: 'The default command timeout for the server to use for all ' +
|
||||
'sessions (in seconds and should be less than 2147483). ' +
|
||||
'Will still be overridden by newCommandTimeout cap',
|
||||
}],
|
||||
|
||||
[['--strict-caps'], {
|
||||
defaultValue: false,
|
||||
dest: 'enforceStrictCaps',
|
||||
@@ -344,18 +335,29 @@ const args = [
|
||||
help: '(IOS-only) Local port used for communication with ios-webkit-debug-proxy'
|
||||
}],
|
||||
|
||||
[['--default-capabilities'], {
|
||||
[['-dc', '--default-capabilities'], {
|
||||
dest: 'defaultCapabilities',
|
||||
defaultValue: {},
|
||||
type: JSON.parse,
|
||||
type: parseDefaultCaps,
|
||||
required: false,
|
||||
example: '{"app": "myapp.app", "deviceName": "iPhone Simulator"}',
|
||||
example: '[ \'{"app": "myapp.app", "deviceName": "iPhone Simulator"}\' ' +
|
||||
'| /path/to/caps.json ]',
|
||||
help: 'Set the default desired capabilities, which will be set on each ' +
|
||||
'session unless overridden by received capabilities.'
|
||||
}],
|
||||
];
|
||||
|
||||
const deprecatedArgs = [
|
||||
[['--command-timeout'], {
|
||||
defaultValue: 60,
|
||||
dest: 'defaultCommandTimeout',
|
||||
type: 'int',
|
||||
required: false,
|
||||
help: '[DEPRECATED] No effect. This used to be the default command ' +
|
||||
'timeout for the server to use for all sessions (in seconds and ' +
|
||||
'should be less than 2147483). Use newCommandTimeout cap instead'
|
||||
}],
|
||||
|
||||
[['-k', '--keep-artifacts'], {
|
||||
defaultValue: false,
|
||||
dest: 'keepArtifacts',
|
||||
@@ -731,6 +733,26 @@ function updateParseArgsForDefaultCapabilities (parser) {
|
||||
};
|
||||
}
|
||||
|
||||
function parseDefaultCaps (caps) {
|
||||
try {
|
||||
// use synchronous file access, as `argparse` provides no way of either
|
||||
// awaiting or using callbacks. This step happens in startup, in what is
|
||||
// effectively command-line code, so nothing is blocked in terms of
|
||||
// sessions, so holding up the event loop does not incur the usual
|
||||
// drawbacks.
|
||||
if (fs.statSync(caps).isFile()) {
|
||||
caps = fs.readFileSync(caps, 'utf8');
|
||||
}
|
||||
} catch (err) {
|
||||
// not a file, or not readable
|
||||
}
|
||||
caps = JSON.parse(caps);
|
||||
if (!_.isPlainObject(caps)) {
|
||||
throw 'Invalid format for default capabilities';
|
||||
}
|
||||
return caps;
|
||||
}
|
||||
|
||||
function getParser () {
|
||||
let parser = new ArgumentParser({
|
||||
version: pkgObj.version,
|
||||
|
||||
Generated
+5350
-1899
File diff suppressed because it is too large
Load Diff
+28
-28
@@ -11,7 +11,7 @@
|
||||
"firefoxos",
|
||||
"testing"
|
||||
],
|
||||
"version": "1.5.0",
|
||||
"version": "1.5.3",
|
||||
"author": "https://github.com/appium",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
@@ -21,9 +21,10 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/appium/appium/issues"
|
||||
},
|
||||
"engines": [
|
||||
"node"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=0.12",
|
||||
"iojs": ">=1.0.0"
|
||||
},
|
||||
"main": "./build/lib/main.js",
|
||||
"bin": {
|
||||
"appium": "./build/lib/main.js",
|
||||
@@ -34,27 +35,26 @@
|
||||
"doc": "./docs"
|
||||
},
|
||||
"dependencies": {
|
||||
"appium-android-driver": "^1.6.11",
|
||||
"appium-base-driver": "^1.3.0",
|
||||
"appium-express": "^1.2.0",
|
||||
"appium-fake-driver": "^0.1.9",
|
||||
"appium-ios-driver": "^1.8.7",
|
||||
"appium-android-driver": "^1.10.6",
|
||||
"appium-base-driver": "^2.0.3",
|
||||
"appium-fake-driver": "^0.1.10",
|
||||
"appium-ios-driver": "^1.12.2",
|
||||
"appium-logger": "^2.1.0",
|
||||
"appium-selendroid-driver": "^1.2.2",
|
||||
"appium-support": "^2.0.9",
|
||||
"argparse": "^1.0.2",
|
||||
"appium-selendroid-driver": "^1.3.4",
|
||||
"appium-support": "^2.3.0",
|
||||
"argparse": "^1.0.7",
|
||||
"asyncbox": "^2.3.1",
|
||||
"authorize-ios": "^1.0.4",
|
||||
"babel-runtime": "=5.8.24",
|
||||
"bluebird": "^2.9.25",
|
||||
"date-utils": "^1.2.18",
|
||||
"lodash": "^3.9.1",
|
||||
"mobile-json-wire-protocol": "^1.3.2",
|
||||
"npmlog": "^2.0.1",
|
||||
"bluebird": "^2.10.2",
|
||||
"continuation-local-storage": "^3.1.7",
|
||||
"date-utils": "^1.2.21",
|
||||
"lodash": "^4.13.1",
|
||||
"npmlog": "^2.0.4",
|
||||
"request-promise": "^1.0.2",
|
||||
"source-map-support": "^0.3.2",
|
||||
"teen_process": "^1.5.1",
|
||||
"winston": "^1.0.0"
|
||||
"source-map-support": "^0.4.0",
|
||||
"teen_process": "^1.6.0",
|
||||
"winston": "^2.2.0"
|
||||
},
|
||||
"scripts": {
|
||||
"prepublish": "gulp prepublish",
|
||||
@@ -62,15 +62,15 @@
|
||||
"watch": "gulp"
|
||||
},
|
||||
"devDependencies": {
|
||||
"appium-gulp-plugins": "^1.3.12",
|
||||
"chai": "^3.2.0",
|
||||
"chai-as-promised": "^5.0.0",
|
||||
"gulp": "^3.8.11",
|
||||
"mocha": "^2.2.5",
|
||||
"sinon": "^1.14.1",
|
||||
"wd": "~0.3.11"
|
||||
"appium-gulp-plugins": "^1.4.5",
|
||||
"chai": "^3.5.0",
|
||||
"chai-as-promised": "^5.3.0",
|
||||
"gulp": "^3.9.1",
|
||||
"mocha": "^2.5.3",
|
||||
"sinon": "^1.17.4",
|
||||
"wd": "~0.4.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "^1.0.6"
|
||||
"fsevents": "^1.0.12"
|
||||
}
|
||||
}
|
||||
|
||||
+70
-38
@@ -55,7 +55,7 @@ describe('Config', () => {
|
||||
// but also to have access to process methods
|
||||
// so copy them over to a writable object
|
||||
let tempProcess = {};
|
||||
for (let [prop, value] of _.pairs(process)) {
|
||||
for (let [prop, value] of _.toPairs(process)) {
|
||||
tempProcess[prop] = value;
|
||||
}
|
||||
process = tempProcess;
|
||||
@@ -64,17 +64,21 @@ describe('Config', () => {
|
||||
process = _process;
|
||||
});
|
||||
describe('checkNodeOk', () => {
|
||||
it('should fail if node is below 0.10', () => {
|
||||
it('should fail if node is below 0.12', () => {
|
||||
process.version = 'v0.9.12';
|
||||
checkNodeOk.should.throw;
|
||||
checkNodeOk.should.throw();
|
||||
process.version = 'v0.1';
|
||||
checkNodeOk.should.throw();
|
||||
process.version = 'v0.10.36';
|
||||
checkNodeOk.should.throw();
|
||||
});
|
||||
it('should succeed if node is 0.10+', () => {
|
||||
process.version = 'v0.10.0';
|
||||
checkNodeOk.should.not.throw;
|
||||
it('should succeed if node is 0.12+', () => {
|
||||
process.version = 'v0.12.0';
|
||||
checkNodeOk.should.not.throw();
|
||||
});
|
||||
it('should succeed if node is 1.x', () => {
|
||||
process.version = 'v1.0.0';
|
||||
checkNodeOk.should.not.throw;
|
||||
checkNodeOk.should.not.throw();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -106,6 +110,7 @@ describe('Config', () => {
|
||||
|
||||
describe('server arguments', () => {
|
||||
let parser = getParser();
|
||||
parser.debug = true; // throw instead of exit on error; pass as option instead?
|
||||
let args = {};
|
||||
beforeEach(() => {
|
||||
// give all the defaults
|
||||
@@ -175,85 +180,109 @@ describe('Config', () => {
|
||||
|
||||
describe('validateServerArgs', () => {
|
||||
let parser = getParser();
|
||||
parser.debug = true; // throw instead of exit on error; pass as option instead?
|
||||
const defaultArgs = {};
|
||||
// give all the defaults
|
||||
for (let rawArg of parser.rawArgs) {
|
||||
defaultArgs[rawArg[1].dest] = rawArg[1].defaultValue;
|
||||
}
|
||||
let args = {};
|
||||
beforeEach(() => {
|
||||
args = _.clone(defaultArgs);
|
||||
});
|
||||
describe('mutually exclusive server arguments', () => {
|
||||
describe('noReset and fullReset', () => {
|
||||
it('should not allow both', () => {
|
||||
(() => {
|
||||
validateServerArgs(parser, {noReset: true, fullReset: true});
|
||||
}).should.throw;
|
||||
args.noReset = args.fullReset = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.throw();
|
||||
});
|
||||
it('should allow noReset', () => {
|
||||
(() => {
|
||||
validateServerArgs(parser, {noReset: true});
|
||||
}).should.not.throw;
|
||||
args.noReset = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.not.throw();
|
||||
});
|
||||
it('should allow fullReset', () => {
|
||||
(() => {
|
||||
validateServerArgs(parser, {fullReset: true});
|
||||
}).should.not.throw;
|
||||
args.fullReset = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.not.throw();
|
||||
});
|
||||
});
|
||||
describe('ipa and safari', () => {
|
||||
it('should not allow both', () => {
|
||||
(() => {
|
||||
validateServerArgs(parser, {ipa: true, safari: true});
|
||||
}).should.throw;
|
||||
args.ipa = args.safari = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.throw();
|
||||
});
|
||||
it('should allow ipa', () => {
|
||||
(() => {
|
||||
validateServerArgs(parser, {ipa: true});
|
||||
}).should.not.throw;
|
||||
args.ipa = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.not.throw();
|
||||
});
|
||||
it('should allow safari', () => {
|
||||
(() => {
|
||||
validateServerArgs(parser, {safari: true});
|
||||
}).should.not.throw;
|
||||
args.safari = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.not.throw();
|
||||
});
|
||||
});
|
||||
describe('app and safari', () => {
|
||||
it('should not allow both', () => {
|
||||
(() => {
|
||||
validateServerArgs(parser, {app: true, safari: true});
|
||||
}).should.throw;
|
||||
args.app = args.safari = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.throw();
|
||||
});
|
||||
it('should allow app', () => {
|
||||
(() => {
|
||||
validateServerArgs(parser, {app: true});
|
||||
}).should.not.throw;
|
||||
args.app = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.not.throw();
|
||||
});
|
||||
});
|
||||
describe('forceIphone and forceIpad', () => {
|
||||
it('should not allow both', () => {
|
||||
(() => {
|
||||
validateServerArgs(parser, {forceIphone: true, forceIpad: true});
|
||||
}).should.throw;
|
||||
args.forceIphone = args.forceIpad = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.throw();
|
||||
});
|
||||
it('should allow forceIphone', () => {
|
||||
(() => {
|
||||
validateServerArgs(parser, {forceIphone: true});
|
||||
}).should.not.throw;
|
||||
args.forceIphone = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.not.throw();
|
||||
});
|
||||
it('should allow forceIpad', () => {
|
||||
(() => {
|
||||
validateServerArgs(parser, {forceIpad: true});
|
||||
}).should.not.throw;
|
||||
args.forceIpad = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.not.throw();
|
||||
});
|
||||
});
|
||||
describe('deviceName and defaultDevice', () => {
|
||||
it('should not allow both', () => {
|
||||
(() => {
|
||||
validateServerArgs(parser, {deviceName: true, defaultDevice: true});
|
||||
}).should.throw;
|
||||
args.deviceName = args.defaultDevice = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.throw();
|
||||
});
|
||||
it('should allow deviceName', () => {
|
||||
(() => {
|
||||
validateServerArgs(parser, {deviceName: true});
|
||||
}).should.not.throw;
|
||||
args.deviceName = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.not.throw();
|
||||
});
|
||||
it('should allow defaultDevice', () => {
|
||||
(() => {
|
||||
validateServerArgs(parser, {defaultDevice: true});
|
||||
}).should.not.throw;
|
||||
args.defaultDevice = true;
|
||||
validateServerArgs(parser, args);
|
||||
}).should.not.throw();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -262,13 +291,16 @@ describe('Config', () => {
|
||||
// the only argument left is `backendRetries`
|
||||
describe('backendRetries', () => {
|
||||
it('should fail with value less than 0', () => {
|
||||
(() => {validateServerArgs(parser, {backendRetries: -1});}).should.throw;
|
||||
args.backendRetries = -1;
|
||||
(() => {validateServerArgs(parser, args);}).should.throw();
|
||||
});
|
||||
it('should succeed with value of 0', () => {
|
||||
(() => {validateServerArgs(parser, {backendRetries: 0});}).should.not.throw;
|
||||
args.backendRetries = 0;
|
||||
(() => {validateServerArgs(parser, args);}).should.not.throw();
|
||||
});
|
||||
it('should succeed with value above 0', () => {
|
||||
(() => {validateServerArgs(parser, {backendRetries: 100});}).should.not.throw;
|
||||
args.backendRetries = 100;
|
||||
(() => {validateServerArgs(parser, args);}).should.not.throw();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+32
-6
@@ -71,13 +71,39 @@ describe('AppiumDriver', () => {
|
||||
await appium.createSession(BASE_CAPS);
|
||||
mockFakeDriver.verify();
|
||||
});
|
||||
it('should call inner driver\'s createSession with desired capabilities with string booleans transformed to booleans', async () => {
|
||||
let sentCaps = _.extend({autoAcceptAlerts: 'false', autoDismissAlerts: 'true'}, BASE_CAPS);
|
||||
let expectedCaps = _.extend({autoAcceptAlerts: false, autoDismissAlerts: true}, BASE_CAPS);
|
||||
it('should kill all other sessions if sessionOverride is on', async () => {
|
||||
appium.args.sessionOverride = true;
|
||||
|
||||
// mock three sessions that should be removed when the new one is created
|
||||
let fakeDrivers = [new FakeDriver(),
|
||||
new FakeDriver(),
|
||||
new FakeDriver()];
|
||||
let mockFakeDrivers = _.map(fakeDrivers, (fd) => {return sinon.mock(fd);});
|
||||
mockFakeDrivers[0].expects('deleteSession')
|
||||
.once();
|
||||
mockFakeDrivers[1].expects('deleteSession')
|
||||
.once()
|
||||
.throws('Cannot shut down Android driver; it has already shut down');
|
||||
mockFakeDrivers[2].expects('deleteSession')
|
||||
.once();
|
||||
appium.sessions['abc-123-xyz'] = fakeDrivers[0];
|
||||
appium.sessions['xyz-321-abc'] = fakeDrivers[1];
|
||||
appium.sessions['123-abc-xyz'] = fakeDrivers[2];
|
||||
|
||||
let sessions = await appium.getSessions();
|
||||
sessions.should.have.length(3);
|
||||
|
||||
mockFakeDriver.expects("createSession")
|
||||
.once().withExactArgs(expectedCaps, undefined, [])
|
||||
.returns([1, expectedCaps]);
|
||||
await appium.createSession(sentCaps);
|
||||
.once().withExactArgs(BASE_CAPS, undefined, [])
|
||||
.returns([1, BASE_CAPS]);
|
||||
await appium.createSession(BASE_CAPS);
|
||||
|
||||
sessions = await appium.getSessions();
|
||||
sessions.should.have.length(1);
|
||||
|
||||
for (let mfd of mockFakeDrivers) {
|
||||
mfd.verify();
|
||||
}
|
||||
mockFakeDriver.verify();
|
||||
});
|
||||
});
|
||||
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"a": "b"
|
||||
}
|
||||
+65
-7
@@ -1,14 +1,72 @@
|
||||
// transpile:mocha
|
||||
|
||||
import { init as logsinkInit } from '../lib/logsink';
|
||||
import logger from '../lib/logger';
|
||||
import { init as logsinkInit, clear as logsinkClear } from '../lib/logsink';
|
||||
import sinon from 'sinon';
|
||||
import { getLogger } from 'appium-logger';
|
||||
|
||||
|
||||
// temporarily turn on logging to stdio, so we can catch and query
|
||||
let forceLogs = process.env._FORCE_LOGS;
|
||||
process.env._FORCE_LOGS = 1;
|
||||
let logger = getLogger('Appium');
|
||||
|
||||
describe('Logger', () => {
|
||||
before(() => {
|
||||
logsinkInit({});
|
||||
let stderrSpy;
|
||||
let stdoutSpy;
|
||||
beforeEach(() => {
|
||||
stderrSpy = sinon.spy(process.stderr, 'write');
|
||||
stdoutSpy = sinon.spy(process.stdout, 'write');
|
||||
logsinkClear();
|
||||
});
|
||||
it('should work', () => {
|
||||
logger.warn('something');
|
||||
logger.debug('something');
|
||||
afterEach(() => {
|
||||
stderrSpy.restore();
|
||||
stdoutSpy.restore();
|
||||
});
|
||||
after(() => {
|
||||
process.env._FORCE_LOGS = forceLogs;
|
||||
});
|
||||
|
||||
const errorMsg = 'some error';
|
||||
const warnMsg = 'some warning';
|
||||
const debugMsg = 'some debug';
|
||||
|
||||
function doLogging () {
|
||||
logger.error(errorMsg);
|
||||
logger.warn(warnMsg);
|
||||
logger.debug(debugMsg);
|
||||
}
|
||||
|
||||
it('should send error, info and debug when loglevel is debug', async () => {
|
||||
await logsinkInit({loglevel: 'debug'});
|
||||
|
||||
doLogging();
|
||||
|
||||
stderrSpy.callCount.should.equal(1);
|
||||
stderrSpy.args[0][0].should.include(errorMsg);
|
||||
|
||||
stdoutSpy.callCount.should.equal(2);
|
||||
stdoutSpy.args[0][0].should.include(warnMsg);
|
||||
stdoutSpy.args[1][0].should.include(debugMsg);
|
||||
});
|
||||
it('should send error and info when loglevel is info', async () => {
|
||||
await logsinkInit({loglevel: 'info'});
|
||||
|
||||
doLogging();
|
||||
|
||||
stderrSpy.callCount.should.equal(1);
|
||||
stderrSpy.args[0][0].should.include(errorMsg);
|
||||
|
||||
stdoutSpy.callCount.should.equal(1);
|
||||
stdoutSpy.args[0][0].should.include(warnMsg);
|
||||
});
|
||||
it('should send error when loglevel is error', async () => {
|
||||
await logsinkInit({loglevel: 'error'});
|
||||
|
||||
doLogging();
|
||||
|
||||
stderrSpy.callCount.should.equal(1);
|
||||
stderrSpy.args[0][0].should.include(errorMsg);
|
||||
|
||||
stdoutSpy.callCount.should.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
+15
-2
@@ -7,6 +7,7 @@ const should = chai.should();
|
||||
|
||||
describe('Parser', () => {
|
||||
let p = getParser();
|
||||
p.debug = true; // throw instead of exit on error; pass as option instead?
|
||||
it('should return an arg parser', () => {
|
||||
should.exist(p.parseArgs);
|
||||
p.parseArgs([]).should.have.property('port');
|
||||
@@ -20,14 +21,26 @@ describe('Parser', () => {
|
||||
}
|
||||
});
|
||||
it('should throw an error with unknown argument', () => {
|
||||
(() => {p.parseArgs(['--apple']);}).should.throw;
|
||||
(() => {p.parseArgs(['--apple']);}).should.throw();
|
||||
});
|
||||
it('should parse default capabilities correctly', () => {
|
||||
it('should parse default capabilities correctly from a string', () => {
|
||||
let defaultCapabilities = {a: 'b'};
|
||||
let args = p.parseArgs(['--default-capabilities',
|
||||
JSON.stringify(defaultCapabilities)]);
|
||||
args.defaultCapabilities.should.eql(defaultCapabilities);
|
||||
});
|
||||
it('should parse default capabilities correctly from a file', () => {
|
||||
let defaultCapabilities = {a: 'b'};
|
||||
let args = p.parseArgs(['--default-capabilities',
|
||||
'test/fixtures/caps.json']);
|
||||
args.defaultCapabilities.should.eql(defaultCapabilities);
|
||||
});
|
||||
it('should throw an error with invalid arg to default capabilities', () => {
|
||||
(() => {p.parseArgs(['-dc', '42']);}).should.throw();
|
||||
(() => {p.parseArgs(['-dc', 'false']);}).should.throw();
|
||||
(() => {p.parseArgs(['-dc', 'null']);}).should.throw();
|
||||
(() => {p.parseArgs(['-dc', 'does/not/exist.json']);}).should.throw();
|
||||
});
|
||||
it('should parse args that are caps into default capabilities', () => {
|
||||
let defaultCapabilities = {localizableStringsDir: '/my/dir'};
|
||||
let args = p.parseArgs(['--localizable-strings-dir', '/my/dir']);
|
||||
|
||||
Reference in New Issue
Block a user