Appium, Android, iOS

YKYL

appium2.0

  • Running Appium from Source
  • Appium in a Nutshell,架构关系图
  • 2.0依赖node 14+ 和 npm 8+
  • 至今(2023-01-16)由于对应的文档还没准备好,尚未正式发布
  • 关键修改是将不同的包进行独立管理,启用了默认变量$APPIUM_HOME = ~/.appium,将会在这里管理各种插件和驱动
    • appium driver list
    • appium plugin list

使用nvm安装到话,会自动替换appium的软连接。

➜  ~ npm install -g appium@next
➜  ~ appium -v
2.0.0-beta.52
➜  ~ which appium
/Users/geb/.nvm/versions/node/v16.17.1/bin/appium
➜  ~ appium -g /tmp/appium2.log
[Appium] Welcome to Appium v2.0.0-beta.52 (REV 9600617c52d0d2e48493424c529ac6c945d2775b)
[Appium] Appium REST http interface listener started on 0.0.0.0:4723
[Appium] No drivers have been installed in /Users/geb/.appium. Use the "appium driver" command to install the one(s) you want to use.
[Appium] No plugins have been installed. Use the "appium plugin" command to install the one(s) you want to use.

^C[Appium] Received SIGINT - shutting down
[debug] [AppiumDriver@9511] There are no active sessions for cleanup
[HTTP] Waiting until the server is closed
[HTTP] Received server close event

nvm管理不同的文件夹以支持不同版本的node;安装的应用连接到对应的bin目录下。

➜  ~ ls -l /Users/geb/.nvm/versions/node/v16.17.1/bin/
total 152400
lrwxr-xr-x@ 1 geb  staff        35 Jan 16 15:10 appium -> ../lib/node_modules/appium/index.js
lrwxr-xr-x  1 geb  staff        45 Sep 23 10:44 corepack -> ../lib/node_modules/corepack/dist/corepack.js
-rwxr-xr-x  1 geb  staff  78027072 Sep 23 10:44 node
lrwxr-xr-x  1 geb  staff        38 Sep 23 10:44 npm -> ../lib/node_modules/npm/bin/npm-cli.js
lrwxr-xr-x  1 geb  staff        38 Sep 23 10:44 npx -> ../lib/node_modules/npm/bin/npx-cli.js

独立安装driver和plgin,检查对应依赖是否下载完整(首次安装手动结束后,只有文件夹好像也被认为安装成功,但启动appium后会报错。卸载重新安装对应的driver即可)

➜  ~ appium driver install xcuitest
✔ Installing 'xcuitest' using NPM install spec 'appium-xcuitest-driver'
ℹ Driver xcuitest@4.16.9 successfully installed
- automationName: XCUITest
- platformNames: ["iOS","tvOS"]
➜  ~ appium driver install uiautomator2
✔ Installing 'uiautomator2' using NPM install spec 'appium-uiautomator2-driver'
ℹ Driver uiautomator2@2.12.2 successfully installed
- automationName: UiAutomator2
- platformNames: ["Android"]

已经安装了 2.0的beta版本,可以直接升级 npm i --location=global appium –> 2.0.0-beta.52 --> 2.1.3(目前的最新版本230926)

从appium 1.x 迁移到 appium 2.x

升级须知——

  • 有哪些不兼容的设置,参考Breaking Changes
  • 针对这些不兼容的点,目前项目中是否涉及到,如果改造? one by one
  • 除了不兼容的部分,项目中哪些功能依赖到appium到哪些功能?
  • 升级后这些功能点是否依然正常? one by one

Breaking Changes

  • 默认的server base path,之前为/wd/hub,现在不需要了,可以使用 appium --base-path=/wd/hub进行设置兼容,不需要修改项目中的设置
  • driver的安装和目录

1.0 所有的driver和主程序是一起安装的,默认的安装目录在 /path/to/appium/node_modules,例如 /path/to/appium/node_modules/appium-xcuitest-driver/node_modules/appium-webdriveragent
2.0 主程序与driver分离,可以单独指定安装所需的driver, 引入新的变量 $APPIUM_HOME,wda的目录在$APPIUM_HOME/node_modules/appium-xcuitest-driver/node_modules/appium-webdriveragent

  • driver的命令行参数——可忽略,未使用
  • driver的自动化参数——可忽略,只是返回code不一致
  • driver升级:之前只能随appium一起升级;现在可以独立升级,例如 appium driver update xcuitest
  • 协议变更: 2.0依然支持旧的client端。appium api基于 W3C WebDriver Protocol,包括"JSONWP" (JSON Wire Protocol) and “MJSONWP” (Mobile JSON Wire Protocol)
  • Capabilities 变更较大,但依然支持旧版本的格式(因为官方维护的client已经默认采用2.0的方式)

标准的capabilities,即官方W3C WebDriver Protocol中指定的capabilities,依然保持现有格式;其他的capabilities必须知道前缀appium:,例如 appium:app。所有不支持W3C协议的WebDriver客户端将不能创建appium session,可以以类似如下的方式做兼容

{
  "platformName": "iOS",
  "browserName": "Safari",
  "appium:options": {
    "platformVersion": "14.4",
    "deviceName": "iPhone 11",
    "automationName": "XCUITest"
  }
}
  • 不再支持的命令(待更新)包括——所有变更到driver层实现的;属于老版本但不属于 W3C Protocol 的。 ——可忽略,未使用
  • 图片分析功能提取到plugin中实现 —— 确认没有使用 appium的’以图定位元素’api的话,可忽略
  • 执行driver 脚本 —— 未使用,可忽略
  • 不支持的参数列表?——针对配置文件的,可忽略
  • 删除老的driver,本身也不再使用 ——可忽略
  • 内部包重命名,例如 appium-base-driver –> @appium/base-driver
  • “WD” javascrip 客户端不再支持,推荐使用 WebdriverIO
  • Appium Inspector 变更为独立的app,不再依赖 GUI Appium Desktop。需要注意,Desktop版本低于1.21的依赖WD 客户端,所以不兼容2.0

新Feature

升级前需要确保1.x版本被彻底删除。目前还是需要先指定next方式 npm install --location global appium@next,目前可以按照2.0.1版本,然后再升级 npm install --location=global appium@latest 到最新版本

安装driver时注意npm到registry源地址,似乎只有官方的源才能获取到对应的driver。

临时使用npm --registry https://registry.npmjs.org install --location global appium@next 持久使用 npm config set registry https://registry.npmjs.org

appium/build/lib下是依据编译后的js代码;appium/lib下是真正的源码。

覆盖旧版本

如果旧版本已经在 /usr/local/bin/appium 存在,而新版本安装在了用户目录下。

新版本升级完成之后——

  • 先删除对应的/usr/local/bin/appium 和指向的appiumm目录
  • 再将用户目录下的 bin/appium 和指向的软连接文件 ../lib/node_modules/appium/build/lib/main.js 中的appium目录都复制
  • 如果提示 Permission Denied 问题,执行 sudo chmod +x /usr/local/bin/appium

检查版本。

appium 是否需要每次都编译wda项目

Could not reuse existing WDA on real iPhone device.

低版本xcode编译wda提示

[Xcode 12.3] WebDriverAgent.xcodeproj Building for iOS, but the embedded framework ‘YYCache.framework’ was built for iOS + iOS Simulator.

本地复现环境:xcode 12.5.1 + appium 1.15.0, 升级到1.22.0之后问题不再复现。

fix:将编译选项的 Validate Workplace从 No 变更为 YES

appium for android 14

权限问题:SecurityException: Permission denial: writing to settings requires:android.permission.WRITE_SECURE_SETTINGS

issue: 13802, 以及appium论坛讨论

不同设备有对应的设置:

  • Oppo/一加:开发者权限–禁止权限监控 开启
  • 如果有:开启 “USB调试(安全设置)”选型
  • 如果有,关闭 “USB验证App”( Verify apps over USB.) 以及 “验证可调试应用的字节码”(Verify bytecode of debuggable apps) 上述操作设置之后,需要重启手机方可使用

目前的问题:

  1. 只能第一次初始化成功:可以获取截图+页面信息。
  2. 无法点击,总是提示超时
  3. 无法刷新,提示截图失败

从日志看——

  • appium端:手机端端socket被挂起,截图请求出现异常
[AndroidUiautomator2Driver@8b7b (38654284)] socket hang up
[debug] [AndroidUiautomator2Driver@8b7b (38654284)] Encountered internal error running command: UnknownError: An unknown server-side error occurred while processing the command. Original error: Could not proxy command to the remote server. Original error: socket hang up
[debug] [AndroidUiautomator2Driver@8b7b (38654284)]     at UIA2Proxy.command (/Users/zhiwei/.nvm/versions/node/v18.13.0/lib/node_modules/appium/node_modules/@appium/base-driver/lib/jsonwp-proxy/proxy.js:342:13)
[debug] [AndroidUiautomator2Driver@8b7b (38654284)]     at processTicksAndRejections (node:internal/process/task_queues:95:5)
[debug] [AndroidUiautomator2Driver@8b7b (38654284)]     at AndroidUiautomator2Driver.commands.click (/Users/zhiwei/.appium/node_modules/appium-uiautomator2-driver/lib/commands/element.js:81:10)
[AndroidUiautomator2Driver@de2f (7254fcfd)] socket hang up
[debug] [AndroidUiautomator2Driver@8b7b (38654284)] Responding to client with driver.getTimeouts() result: {"command":3600000,"implicit":0}
[debug] [AndroidUiautomator2Driver@8b7b (38654284)] Matched '/screenshot' to command name 'getScreenshot'
[debug] [AndroidUiautomator2Driver@8b7b (38654284)] Proxying [GET /screenshot] to [GET http://127.0.0.1:8200/session/182ec5cb-2b58-4647-98f1-f535accd800f/screenshot] with no body
[debug] [AndroidUiautomator2Driver@8b7b (38654284)] Responding to client with driver.getTimeouts() result: {"command":3600000,"implicit":0}
[AndroidUiautomator2Driver@8b7b (38654284)] socket hang up
  • device端:刚启动之后,uia2的server被强制停止了
06-15 11:36:11.535 D/OplusSmartBrightnessController( 3334): handleMessage MSG_LUX_CHANGED, mLux:162.66734 luxQueue=[162.0, 163.0, 163.0, 162.0, 163.0, 163.0]
06-15 11:36:11.539 D/AndroidRuntime(13475): Calling main entry com.android.commands.am.Am
06-15 11:36:11.544 D/UiAutomationConnection(13475): Created on user UserHandle{0}
06-15 11:36:11.545 I/ActivityManager( 3334): Force stopping io.appium.uiautomator2.server appid=10321 user=0: start instr
06-15 11:36:11.567 E/ActivityManager( 3334): Unable to freeze binder for 12200: -11
06-15 11:36:11.569 D/OplusUIFirst_FB( 3334): io.appium.uiautomator2.server remove pid: 12200
06-15 11:36:11.572 I/ActivityManager( 3334): Killing 12200:io.appium.uiautomator2.server/u0a321 (adj 0): stop io.appium.uiautomator2.server due to start instr
06-15 11:36:11.574 E/IHansComunication( 9664): HANS printMessageBody: RCV message: type = 3,port = 86870,caller_pid = 3334,caller_uid = 1000,target_pid = 12200,target_uid = 10321,pkg_cmd = -1,rpc = signal/9
06-15 11:36:11.578 I/OplusHansManager ( 3334): onUidGone(), 10321 io.appium.uiautomator2.server exit SM
06-15 11:36:11.579 I/OplusHansManager ( 3334): uid=10321, pkg=io.appium.uiautomator2.server F exit SM
06-15 11:36:11.579 I/OplusHansManager ( 3334): uid=10321, pkg=io.appium.uiautomator2.server F exit(), F stay=196, reason=UidGone
06-15 11:36:11.580 I/OplusHansManager ( 3334): unfreeze uid: 10321 io.appium.uiautomator2.server pids: [12200] reason: UidGone scene: LcdOn
06-15 11:36:11.580 I/OplusHansManager ( 3334): uid=10321, pkg=io.appium.uiautomator2.server M exit(), M stay=0
06-15 11:36:11.580 I/OplusHansManager ( 3334): uid=10321, pkg=io.appium.uiautomator2.server R exit(), R stay=0
06-15 11:36:11.582 D/SensorService( 3334): onUidStateChanged uid = 10321, state = UID_STATE_IDLE
06-15 11:36:11.583 D/DisplayManagerService( 3334): Drop pending events for gone uid 10321

curl 调用api

Controlling Appium via raw HTTP requests with curl

# 0. Check the Appium server is online
> curl http://localhost:4723/wd/hub/status

# response:
{"value":{"build":{"version":"1.17.0"}},"sessionId":null,"status":0}

# 1. Create a new session
> curl -H 'Content-type: application/json' \
       -X POST \
       http://localhost:4723/wd/hub/session \
       -d '{"capabilities": {"alwaysMatch": {"platformName": "iOS", "platformVersion": "13.3", "browserName": "Safari", "deviceName": "iPhone 11"}}}'

# response:
{"value":{"capabilities":{"webStorageEnabled":false,"locationContextEnabled":false,"browserName":"Safari","platform":"MAC","javascriptEnabled":true,"databaseEnabled":false,"takesScreenshot":true,"networkConnectionEnabled":false,"platformName":"iOS","platformVersion":"13.3","deviceName":"iPhone 11","udid":"140472E9-8733-44FD-B8A1-CDCFF51BD071"},"sessionId":"ac3dbaf9-3b4e-43a2-9416-1a207cdf52da"}}

# save session id
> export sid="ac3dbaf9-3b4e-43a2-9416-1a207cdf52da"

WDA

How To Set Up And Customize WebDriverAgent Server 入门文档。

  • appium在ios端使用WDA作为后端server; wda基于XCTest framework

设置——

  • 使用Xcode打开WDA工程
  • 选择 WebDriverAgentRunner 项目
  • 选择 设备 作为 测试目标
  • 选择 Product --> Test

启动——可以使用多种启动方式

  • iproxy, 使用xcodebuild命令行启动,然后使用iproxy转发端口请求
  • go-ios
  • tidevice 进行启动,WDA已经安装的情况下 tidevice xctest -B com.facebook.wda.WebDriverAgent.Runner

前提是针对iOS项目已经有通用的签名证书并设置了对应的bundleId等信息。

#349 tidevice issue

  • Looks like iOS 17 is using a new device connectivity stack (named CoreDevice), changes involves:

  • A libimobiledevice issue that records some investigation. Looks like this comment has fixed a part of controlling features in pymobiledevice3

  • go-ios issue

  • appium issue

5.10版本使用xcode 15编译成功,无法启动

提示内容类似——手动点击icon启动,会弹框提示“需要联网进行认证",联网后会自动激活。

The request to open "com.geb.test.WebDriverAgentRunner.xctrunner" failed.
Domain: IDELaunchCoreDevice
Code: 0
Recovery Suggestion: Verify that the Developer App certificate for your account is trusted on your device. Open Settings on the device and navigate to General -> VPN & Device Management, then select your Developer App certificate to trust it.
User Info: {
    DVTErrorCreationDateKey = "2023-09-26 04:06:58 +0000";
    IDERunOperationFailingWorker = IDELaunchCoreDeviceWorker;
}

WDA提示 端口占用——

The port #xxxx is occupied by an other process.xxxx

跟踪了半天,是由于最初是由root用户启动的,执行的iproxy转发也是root用户下的。切换用户之后,lsof -I:xxx是看不见root用户启动的进程,但系统端口被占用是可以检测到

tidevice启动wda时也会报错 sock.bind() Address already in use

端口被占用是个基础问题

证书信任

企业级证书需要先添加到信任列表之后,才能通过WDA启动起来,否则将会超时报错。 NoHttpResponseException: xxx failed to respond

  • 低版本:“设置”>“通用”>“关于本机”>“证书信任设置”。在“针对根证书启用完全信任”下,开启对这个证书的信任。
  • 高版本:“设置”>“通用”>“VPN与设备管理”。在“企业级App”下,开启对这个证书的信任。(不同的应用可能使用了不同的企业级证书)

使用缓存build的wda

待验证。。。

libimobiledevice 系列

github libimobiledevice A cross-platform FOSS library written in C to communicate with iOS devices natively. official site: libimobiledevice.org

包含多个类库——

  • usbmuxd 基础库 A socket daemon to multiplex connections from and to iOS devices.
    • 上层库 libimobiledevice A library to communicate with services on iOS devices using native protocols. 包括多种工具与iOS设备交互,例如 idevice_id,ideviceinfo, idevicescreenshot, ideviceprovision 等
    • 底层库 libusbmuxd A client library for applications to handle usbmux protocol connections with iOS devices. 包括 iproxy,inetcat工具

Understanding usbmux and the iOS lockdown service 介绍底层机制

Automate iOS devices — The (almost) Mac free way

ideviceinstaller安装

$brew uninstall ideviceinstaller
$brew uninstall libimobiledevice
$brew install --HEAD libimobiledevice
$brew link --overwrite libimobiledevice
$brew install ideviceinstaller
$brew link --overwrite ideviceinstaller

appium-webdriveragent 项目构建

当前使用的版本v3.16.0,下载zip源码后,nodejs项目,执行node ./Scripts/build-webdriveragent.js——需要确保依赖的npm包已经安装,可先执行npm install,即使整体任务失败也没关系,打包wda的依赖安装成功即可。

业务逻辑:

  • 获取xcode版本
  • 删除用户目录下的DerivedData中wda相关的(缓存WDA)编译结果 ~/Library/Developer/Xcode/DerivedData/WebDriverAgent-*
  • 执行wda编译脚本,默认针对的模拟器构建 /bin/bash ./Scripts/build.sh,传递的参数 runner,sim
  • 创建 uncompressedbundles目录,
  • 将xcode项目文件复制到此目录下,
  • 并将编译生成的 WebDriverAgent-* 文件保存当前目录的DerivedData/WebDriverAgent
  • 将此目录整体打包为 webdriveragent-xcode_xcodeversion.zip 文件,并保存中 bundles 目录下
  • 将编译打包的DerivedData/WebDriverAgent/Build/Products/Debug-iphonesimulator/WebDriverAgentRunner-Runner.app压缩为zip文件 WebDriverAgentRunner-Runner.app.zip保存在工程主目录
  • 删除uncopressed目录

环境搭建

使用现有的环境,还没有按照上面的文档进行搭建操作。

简单理解:appium作为web server,实现了JSONWP协议。这样客户端可以语言无关,调用符合规范的http接口即可。后端对接不同的实现,支持不同的平台,例如android、iOS、windows、Web等等。

多版本支持

How to install multiple appium versions on my Mac

本质上,appium是一个nodejs版本的web server,安装好依赖环境之后,只需要将appium安装到不同的目录即可——

#Install version 1.22.2
npm install --prefix /opt/appium1.22.2 appium@1.22.2
#Install version 1.22.3
npm install --prefix /opt/appium1.22.3 appium@1.22.3

#Start the version 1.22.2
cd /opt/appium1.20.1
./node_modules/appium/build/lib/main.js --version
1.22.2
% ./node_modules/appium/build/lib/main.js
[Appium] Welcome to Appium v1.22.2
[Appium] Appium REST http interface listener started on 0.0.0.0:4723


#Start the version 1.22.3
cd /opt/appium1.22.3
./node_modules/appium/build/lib/main.js --version
1.22.3
./node_modules/appium/build/lib/main.js -p 4724
[Appium] Welcome to Appium v1.22.3
[Appium] Non-default server args:
[Appium] port: 4724
[Appium] Appium REST http interface listener started on 0.0.0.0:4724

指定appium使用的Xcode版本

Running Appium with multiple Xcode versions installed

如果有多个Xcode版本——

  • 使用 xcode-select 工具
# show current default Xcode
xcode-select --print-path
# Set default Xcode.
sudo xcode-select -s /Applications/Xcode13.app/Contents/Developer
# Run Appium (from command line or with GUI).
appium
  • 使用环境变量
#Set DEVELOPER_DIR environment variable.
export DEVELOPER_DIR=/Applications/Xcode12.app/Contents/Developer
#Run Appium from the same shell.
appium 

node环境

Mac: 下载pkg文件,命令行安装 sudo installer -pkg /path/to/you_pkg_file.pkg -target /

install dmg

# 命令行下安装 dmg
# 1. 挂在 dmg
sudo hdiutil attach <image>.dmg
# 2. 安装 pkg -target 参数指就是 / 
sudo installer -pkg /Volumes/<image>/<image>.pkg -target /
# 3. 卸载 dmg
sudo hdituil detach <image>.dmg

Permission denial

各个手机设置不同——

  • 小米:在开发者选项里,把“USB调试(安全设置)“打开即可,允许USB调试修改权限或模拟点击
  • oppo/一加:在开发者选项里,把"禁止权限监控"打开(在“应用”部分,最下面)
[UiAutomator2] Relaxing hidden api policy
[debug] [ADB] Running '/Users/geb/Library/Android/sdk/platform-tools/adb -P 5037 -s 18a281e9 shell 'settings put global hidden_api_policy_pre_p_apps 1;settings put global hidden_api_policy_p_apps 1;settings put global hidden_api_policy 1''
[debug] [UiAutomator2] Deleting UiAutomator2 session
[debug] [ADB] Running '/Users/geb/Library/Android/sdk/platform-tools/adb -P 5037 -s 18a281e9 shell am force-stop com.coloros.calculator'
[UiAutomator2] Restoring hidden api policy to the device default configuration
[debug] [ADB] Running '/Users/geb/Library/Android/sdk/platform-tools/adb -P 5037 -s 18a281e9 shell 'settings delete global hidden_api_policy_pre_p_apps;settings delete global hidden_api_policy_p_apps;settings delete global hidden_api_policy''
[debug] [BaseDriver] Event 'newSessionStarted' logged at 1665558963920 (15:16:03 GMT+0800 (China Standard Time))
[debug] [W3C] Encountered internal error running command: Error executing adbExec. Original error: 'Command '/Users/geb/Library/Android/sdk/platform-tools/adb -P 5037 -s 18a281e9 shell 'settings delete global hidden_api_policy_pre_p_apps;settings delete global hidden_api_policy_p_apps;settings delete global hidden_api_policy'' exited with code 255'; Command output: 
[debug] [W3C] Exception occurred while executing 'delete':
[debug] [W3C] java.lang.SecurityException: Permission denial: writing to settings requires:android.permission.WRITE_SECURE_SETTINGS
[debug] [W3C] 	at com.android.providers.settings.SettingsProvider.enforceWritePermission(SettingsProvider.java:2640)
[debug] [W3C] 	at com.android.providers.settings.SettingsProvider.mutateGlobalSetting(SettingsProvider.java:1632)
[debug] [W3C] 	at com.android.providers.settings.SettingsProvider.mutateGlobalSetting(SettingsProvider.java:1624)
[debug] [W3C] 	at com.android.providers.settings.SettingsProvider.deleteGlobalSetting(SettingsProvider.java:1595)
[debug] [W3C] 	at com.android.providers.settings.SettingsProvider.call(SettingsProvider.java:661)
[debug] [W3C] 	at android.content.ContentProvider.call(ContentProvider.java:2473)
[debug] [W3C] 	at android.content.ContentProvider$Transport.call(ContentProvider.java:521)
[debug] [W3C] 	at com.android.providers.settings.SettingsService$MyShellCommand.deleteForUser(SettingsService.java:408)
[debug] [W3C] 	at com.android.providers.settings.SettingsService$MyShellCommand.onCommand(SettingsService.java:282)
[debug] [W3C] 	at com.android.modules.utils.BasicShellCommandHandler.exec(BasicShellCommandHandler.java:97)
[debug] [W3C] 	at android.os.ShellCommand.exec(ShellCommand.java:38)
[debug] [W3C] 	at com.android.providers.settings.SettingsService.onShellCommand(SettingsService.java:50)
[debug] [W3C] 	at android.os.Binder.shellCommand(Binder.java:970)
[debug] [W3C] 	at android.os.Binder.onTransact(Binder.java:854)
[debug] [W3C] 	at android.os.Binder.execTransactInternal(Binder.java:1226)
[debug] [W3C] 	at android.os.Binder.execTransact(Binder.java:1163)

安装libimobiledevice 最新版本

目前brew上的最新版本为1.3.0,但不支持iOS 16.0。使用 brew install --HEAD libimobiledevice时,提示缺少libimobiledevice-glue-1.0

参考issue 1217下的comment,手动创建blue的formula

  • brew手动安装libimobiledevice-glue
#手动创建formula
brew create "https://github.com/libimobiledevice/libimobiledevice-glue.git"
#自动进入编辑模式,或手动进入,按照下面的格式编辑
brew edit libimobiledevice-glue

# 执行安装
brew install --HEAD libimobiledevice-glue
 class LibimobiledeviceGlue < Formula
   desc ""
   homepage ""
   license ""
   head "https://github.com/libimobiledevice/libimobiledevice-glue.git"

   depends_on "autoconf" => :build
   depends_on "automake" => :build
   depends_on "libtool" => :build
   depends_on "pkg-config" => :build
   depends_on "libplist"

   def install
     system "./autogen.sh", "--prefix=#{prefix}"
     system "make", "install"
   end

 end
  • 修改libimobiledevice的formula之后安装HEAD

brew edit libimobiledevice修改formula,增加 depends_on "libimobiledevice-glue",然后执行 brew install --HEAD libimobiledevice

手动搬运Mac环境下的appium

目标机器网络环境不佳,另外一台机器上有appium 1.22.3版本的环境。

# 打包appium, 目录 /usr/local/lib/node_modules
tar -czvf appium.tar.gz appium

# 解压到目标机器对应相同目录 /usr/local/lib/node_modules
tar -zxvf appium.tar.gz -C ./

# jar 命令有类似的功能,但不能指定解压目录, 将以下war包解压到当前目录
jar -xvf test.war 

# 创建软连接启动项目 目录   /usr/local/bin 
ln -s ../lib/node_modules/appium/build/lib/main.js appium 
# 这里的坑是:记得前面是源地址,后面的是目标地址。 将后面的软连接连接到源地址上。一开始整错了,没修改;又重新连接了一回,结果就循环了 
# 检查方式:https://www.baeldung.com/linux/too-many-levels-of-symlinks
find -L appium  # Mac 下提示 find: Symlink loop resolving appium
file appium 

# 检查appium版本
appium -v 

脚本编写

  • Find Element
  • 安装对应的client端,例如 pip install Appium-Python-Client
  • 安装pytest框架 pip install pytest
  • 使用appium inspector进行录制,转换为对应的脚本,再做对应的修改
  • 执行pytest,详情参考
# This sample code uses the Appium python client
# pip install Appium-Python-Client
# Then you can paste this into a file and simply run with Python

import os
from time import sleep

from appium import webdriver
from selenium.webdriver.common.by import By


class TestIOS:

    def setup(self):
        # https://appium.io/docs/en/drivers/ios-xcuitest/index.html 
        caps = {}
        caps["platformName"] = "ios"
        caps["platformVersion"] = "16.1"
        caps["automationName"] = "xcuitest"
        caps["bundleId"] = "com.ecpmobile.ecp"
        caps["deviceName"] = "xx的 iPhone"
        caps["udid"] = "d6afe671ffcf3efdadb686f296a29cb4a7452aea"
        caps["xcodeOrgId"] = "2Z7MNS5L34"
        caps["xcodeSigningId"] = "iPhone Developer"
        self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", caps)

    def test_real_buttons(self):
        el3 = self.driver.find_element(By.ID, "login aboutus")
        el3.click()

        el2 = self.driver.find_element(By.ID, "更多信息")
        el2.click()
        self.driver.back()
        self.driver.back()
        el5 = self.driver.find_element(By.XPATH, "//XCUIElementTypeStaticText[@name=\"忘记密码?\"]")
        el5.click()

        directory = '%s/' % os.getcwd()
        fn = 'screenshot.png'
        self.driver.get_screenshot_as_file(directory+fn)

    def teardown(self):
        sleep(3)
        self.driver.quit()

脚本保存到 testappium文件夹下,命名为test.py

命令行方式执行

独立命令行启动appium appium -g /tmp/appium.log 确保使用 node版本为v16.17.1的长期版本,否则appium可能提示failed to receive to receive any data within the timeout 5000 ,参考官方的issue 16399

# 创建虚拟环境
python3 -m venv testappium
# 激活当前环境
source testappium/bin/activate 
# 安装依赖
pip3 install pytest
pip3 install Appium-Python-Client
# 执行测试,确保已经有appium server启动
pytest test.py

打包WDA

  • 下载项目,使用xcode打开
  • WDA签名使用导入的方式,不勾选 “Automatically manage signing”,使用import方式
    • Targets中的 WebDriverAgentRunner必须导入签名, WebDriverAgentLib可以不添加证书
  • 根据provisiioning profile文件中的AppId修改项目的bundle identifier
  • 选择Product菜单栏对应的Destination和Scheme,分别设置为连接的iPhone设备和 WebDriverAgentRunner
  • 执行 Product菜单栏的 Test

将自动打包安装WDA到对应的手机。详细的操作可以参考,或者这里

也可以采用 xcodebuild 命令行模式执行——

# 解锁keychain,以便可以正常的签名应用,PASSWORD是你自己mac电脑的开机密码
PASSWORD="你自己的开机密码" 
security unlock-keychain -p $PASSWORD ~/Library/Keychains/login.keychain

# 获取设备的UDID
UDID=$(idevice_id -l | head -n1)
xcodebuild -project WebDriverAgent.xcodeproj -scheme WebDriverAgentRunner -destination "id=$UDID" USE_PORT=8100 test 

# with 

Xcode提示“codesign 想要访问您的钥匙串中的密钥”

在 Keychain Access中选择 login.keychain中的登录选项中导入的证书,双击私钥,修改“访问控制 access controll”,选择 所有应用都可以访问。更精细的可以将 codesign应用添加到列表中(没找到如何选中 /usr/bin/codesign的方法)。 图文参考

安装WDA时报错 “The code signature version is no longer supported”

在iOS 15以上版本安装WDA时提示报错。因为“Apple has changed the codesign signature to include DER (Distinguished Encoding Rules) encoded entitlements in addition to the plist encoded entitlements. ” 采用了新的编码规范,低版本Xcode默认不支持,需要进行设置。参考

  • Xcode: Build Setting -> Other Code Signing Flags 添加 --generate-entitlement-der
  • xcodebuild: xcodebuild OTHER_CODE_SIGN_FLAGS='--generate-entitlement-der' -project WebDriverAgent.xcodeproj -scheme WebDriverAgentRunner -destination "id=$UDID" USE_PORT=8100 test

更直接的办法是升级Xcode版本:)

Xcode 14.0打包WDA报错

遇到链接问题: ld: cannot link directly with dylib/framework, your binary is not an allowed client of /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/XCTAutomationSupport.framework/XCTAutomationSupport for architecture arm64

官方论坛相关问题,appium的github描述了原因:苹果官方更严格的限制,导致XCTAutomationSupport问题,在appium2中修复了这个问题。Fastbot也出现类似的问题,建议使用低版本的进行代替——实操可行。

替换之后,遇到的问题时之前截图的命令现在提示——

 Encountered internal error running command: UnableToCaptureScreen: Error Domain=XCTDaemonErrorDomain Code=39 "Legacy screen requests are no longer supported" UserInfo={NSLocalizedDescription=Legacy screen requests are no longer supported}
[debug] [W3C (4754dddc)]     at errorFromW3CJsonCode (/usr/local/lib/node_modules/appium/node_modules/appium-base-driver/lib/protocol/errors.js:780:25)
[debug] [W3C (4754dddc)]     at ProxyRequestError.getActualError (/usr/local/lib/node_modules/appium/node_modules/appium-base-driver/lib/protocol/errors.js:663:14)
[debug] [W3C (4754dddc)]     at JWProxy.command (/usr/local/lib/node_modules/appium/node_modules/appium-base-driver/lib/jsonwp-proxy/proxy.js:272:19)
[debug] [W3C (4754dddc)]     at runMicrotasks (<anonymous>)
[debug] [W3C (4754dddc)]     at processTicksAndRejections (node:internal/process/task_queues:96:5)
[debug] [W3C (4754dddc)]     at XCUITestDriver.proxyCommand (/usr/local/lib/node_modules/appium/node_modules/appium-xcuitest-driver/lib/commands/proxy-helper.js:96:12)
[debug] [W3C (4754dddc)]     at getScreenshotFromWDA (/usr/local/lib/node_modules/appium/node_modules/appium-xcuitest-driver/lib/commands/screenshots.js:11:18)
[debug] [W3C (4754dddc)]     at wrapped (/usr/local/lib/node_modules/appium/node_modules/asyncbox/lib/asyncbox.js:60:13)
[debug] [W3C (4754dddc)]     at retry (/usr/local/lib/node_modules/appium/node_modules/asyncbox/lib/asyncbox.js:43:13)
[debug] [W3C (4754dddc)]     at retryInterval (/usr/local/lib/node_modules/appium/node_modules/asyncbox/lib/asyncbox.js:70:10)
[debug] [W3C (4754dddc)]     at XCUITestDriver.getScreenshot (/usr/local/lib/node_modules/appium/node_modules/appium-xcuitest-driver/lib/commands/screenshots.js:42:10)

现状总结

使用appium 1.22.3版本,打包自带的WDA工程(位于 /usr/local/lib/node_modules/appium/node_modules/appium-webdriveragent),然后写自动化脚本进行验证

  • Mac 12.6 + Xcode 13.4.1 + Appium 1.22.3 可以正常支持 iOS 16.0 版本,操作、截图正常
  • Mac 13.0 + Xcode 14.0 + Appium 1.22.3 由于Xcode中的XCTAutomationSupport问题导致无法正常编译;使用Xcode 13.4版本的XCTAutomationSupport替换之后,正常点击操作OK,但截图提示错误

重新离线下载Xcode 13.4.1,但无法安装到Mac13.0 Ventura系统,官方描述,即使使用命令行启动,依然会报错——

utfdeMini-148:~  /Applications/Xcode13.4.1.app/Contents/MacOS/Xcode 
Killed: 9
utfdeMini-148:~ utf$ The application cannot be opened for an unexpected reason, error=Error Domain=NSOSStatusErrorDomain Code=-10664 "kLSIncompatibleApplicationVersionErr: The app is incompatible with the current OS" UserInfo={_LSLine=4047, _LSFunction=_LSOpenStuffCallLocal}

Utilize a Different Xcode Version for Build Process 这里的方式……好像多尝试几次之后居然成功了?

需要先设置变量

export DEVELOPER_DIR=/Applications/Xcode13.4.1.app/Contents/Developer # make sure set this value first, or else the Xcode cann't open. 
/Applications/Xcode13.4.1.app/Contents/MacOS/Xcode

效果验证

# example
utfdeMini-148:~ utf$ export DEVELOPER_DIR=/Applications/Xcode13.4.1.app/Contents/Developer
utfdeMini-148:~ utf$ /Applications/Xcode13.4.1.app/Contents/MacOS/Xcode 
objc[38398]: Class ASVError is implemented in both /Applications/Xcode13.4.1.app/Contents/SharedFrameworks/GPUToolsCore.framework/Versions/A/GPUToolsCore (0x12c851050) and /Applications/Xcode13.4.1.app/Contents/PlugIns/GPUDebugger.ideplugin/Contents/Frameworks/GPUToolsASVC.framework/Versions/A/GPUToolsASVC (0x125c30338). One of the two will be used. Which one is undefined.
objc[38398]: Class DYShaderAnalyzerNextGPU is implemented in both /Applications/Xcode13.4.1.app/Contents/SharedFrameworks/MTLToolsShaderProfiler.framework/Versions/A/MTLToolsShaderProfiler (0x12cc7ac60) and /Applications/Xcode13.4.1.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/GPUTools/PlugIns/GLToolsShaderProfilerMobileSupport.gtplugin/Contents/MacOS/GLToolsShaderProfilerMobileSupport (0x12ce2a9c0). One of the two will be used. Which one is undefined.
2022-09-29 17:36:29.376 Xcode[38398:1616211] Looking for Simulator.app in (
    "<DVTFilePath:0x60000294bf00:'/Applications/Xcode13.4.1.app/Contents/Developer/Applications'>"
)
2022-09-29 17:36:33.295 Xcode[38398:1617460]  DVTPortal: Service '<DVTPortalViewDeveloperService: 0x600001a018c0; action='viewDeveloper'>' encountered an unexpected result code from the portal ('1100')
2022-09-29 17:36:33.295 Xcode[38398:1617460]  DVTPortal: Error:
...

执行sudo xcode-select --switch /Applications/Xcode13.4.1.app强制切换后(图标已经显示为禁止操作)。重新执行成功了(尽管命令行启动时会有报错信息)

 
comments powered by Disqus