Skip to content

ziguiway/hmdriver2

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

96 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

hmdriver2

github actions pypi version python downloads

写这个项目前github上已有个叫hmdriver的项目,但它是侵入式(需要提前在手机端安装一个testRunner app);另外鸿蒙官方提供的hypium自动化框架,使用较为复杂,依赖繁杂。于是决定重写一套。

hmdriver2 是一款支持HarmonyOS NEXT系统的UI自动化框架,无侵入式,提供应用管理,UI操作,元素定位等功能,轻量高效,上手简单,快速实现鸿蒙应用自动化测试需求。

arch

微信交流群(永久生效)

wechat

Key idea

  • 无侵入式
    • 无需提前在手机端安装testRunner APP(类似atx app)
  • 易上手
    • 在PC端编写Python脚本实现自动化
    • 对齐Android端 uiautomator2 的脚本编写姿势
  • 轻量高效
    • 摒弃复杂依赖(几乎0依赖),即插即用
    • 操作响应快,低延时

Feature

  • 支持应用管理
    • 应用启动,停止
    • 应用安装,卸载
    • 应用数据清理
    • 获取应用列表,应用详情等
  • 支持设备操作
    • 获取设备信息,分辨率,旋转状态等
    • 屏幕解锁,亮屏,息屏
    • Key Events
    • 文件操作
    • 屏幕截图
    • 屏幕录屏
    • 手势操作(点击,滑动,输入,复杂手势)
  • 支持控件操作
    • 控件查找(联合查找,子串/正则/模糊 matching uiautomator2,相对查找,xpath)
    • 等待出现 / 等待消失(wait / wait_gone,与 u2 习惯接近;d(...)d.xpath(...) 均支持)
    • 列表/滚动容器内 scroll(纵横向滚动、scroll.to 等,Hypium 优先、bounds 内滑动手势兜底)
    • 控件信息获取
    • 控件点击,长按,拖拽,缩放
    • 文本输入,清除
    • 获取控件树
  • 支持Toast获取
  • 纯 Python 后台 Watcherd.watcher:多规则轮询、自动点击 / 返回等,不依赖新协议)
  • UI Inspector
  • [TODO] 操作标记

QUICK START

  1. 配置鸿蒙HDC环境
    1. 下载 Command Line Tools 并解压
    2. hdc文件在command-line-tools/sdk/default/openharmony/toolchains目录下
    3. 配置环境变量,macOS为例,在~/.bash_profile 或者 ~/.zshrc文件中添加
export HM_SDK_HOME="/Users/develop/command-line-tools/sdk/default"  //请以sdk实际安装目录为准
export PATH=$PATH:$HM_SDK_HOME/hms/toolchains:$HM_SDK_HOME/openharmony/toolchains
export HDC_SERVER_PORT=7035
  1. 电脑插上手机,开启USB调试,确保执行hdc list targets 可以看到设备序列号

  2. 安装hmdirver2 基础库

pip3 install -U hmdriver2
  1. 接下来就可以愉快的进行脚本开发了 😊😊
from hmdriver2.driver import Driver

d = Driver()

print(d.device_info)
# ouput: DeviceInfo(productName='HUAWEI Mate 60 Pro', model='ALN-AL00', sdkVersion='12', sysVersion='ALN-AL00 5.0.0.60(SP12DEVC00E61R4P9log)', cpuAbi='arm64-v8a', wlanIp='172.31.125.111', displaySize=(1260, 2720), displayRotation=<DisplayRotation.ROTATION_0: 0>)

d.start_app("com.kuaishou.hmapp")
d(text="精选").click()
d(textContains="同意").click_if_exists()  # 子串匹配,见「模糊子串与正则选择器」
d.watcher("ad").when_xpath('//*[@text="跳过"]').click()
d.watcher.start(interval=0.3)
d.swipe(0.5, 0.8, 0.5, 0.4)
# d.watcher.stop()

UI inspector

UI 控件树可视化工具,查看控件树层级,获取控件详情。

ui-viewer

详细介绍请看 ui-viewer

Environment

如何需要连接远端的HDC Server来实现操作远端设备执行自动化,运行脚本前需要设置环境变量

export HDC_SERVER_HOST=127.0.0.1  # Replace with the remote host
export HDC_SERVER_PORT=8710

PS 如果需要移除环境变量,执行以下命令

unset HDC_SERVER_HOST
unset HDC_SERVER_PORT

API Documents

初始化Driver

from hmdriver2.driver import Driver

d = Driver()
# d = Driver("FMR0223C13000649")

参数serial 通过hdc list targets 命令获取;如果不传serial,则默认读取hdc list targets的第一个设备

初始化driver后,下面所有的操作都是调用dirver实现

App管理

安装App

d.install_app("/Users/develop/harmony_prj/demo.hap")

卸载App

d.uninstall_app("com.kuaishou.hmapp")

传入的参数是package_name,可通过hdc命令获取hdc shell bm dump -a

启动App

d.start_app("com.kuaishou.hmapp")

d.start_app("com.kuaishou.hmapp", "EntryAbility")

package_name, page_name分别为包名和ability name,可以通过hdc命令获取:hdc shell aa dump -l

不传page_name时,默认会使用main ability作为page_name

用软件名 / 显示名启动

当你只知道应用名称(如「微信」「系统设置」),可以用包元数据里解析出的显示名 / vendor 等来匹配,再启动:

# 按子串匹配显示名;匹配不到会抛 AppNameNotFoundError;多个命中会抛 AppNameAmbiguousError
d.start_app_by_name("系统设置", match="contains", include_system_apps=True)

# 与 force_start_app 一样:先回桌面、强停、再按名启动
d.force_start_app_by_name("微信")

# 先只解析包名(例如你想打日志 / 重试逻辑)
pkg = d.find_package_by_display_name("微信", on_ambiguous="first")
d.start_app(pkg)

说明

  • 会遍历本机已安装包列表,并可能对每个包执行 bm dump -n 解析元数据,可能较慢;同一次 Driver 生命周期内会缓存已解析的显示名。
  • match 支持:contains(默认)、exactstartswithendswithregex
  • include_bundle_name=True(默认)时,也允许用包名子串匹配到 com.xxx(例如只填 kuaishou)。

停止App

d.stop_app("com.kuaishou.hmapp")

清除App数据

d.clear_app("com.kuaishou.hmapp")

该方法表示清除App数据和缓存

获取App详情

d.get_app_info("com.kuaishou.hmapp")

输出的数据结构是Dict, 内容如下

{
    "appId": "com.kuaishou.hmapp_BIS88rItfUAk+V9Y4WZp2HgIZ/JeOgvEBkwgB/YyrKiwrWhje9Xn2F6Q7WKFVM22RdIR4vFsG14A7ombgQmIIxU=",
    "appIdentifier": "5765880207853819885",
    "applicationInfo": {
        ...
        "bundleName": "com.kuaishou.hmapp",
        "codePath": "/data/app/el1/bundle/public/com.kuaishou.hmapp",
        "compileSdkType": "HarmonyOS",
        "compileSdkVersion": "4.1.0.73",
        "cpuAbi": "arm64-v8a",
        "deviceId": "PHONE-001",
				...
        "vendor": "快手",
        "versionCode": 999999,
        "versionName": "12.2.40"
    },
    "compatibleVersion": 40100011,
    "cpuAbi": "",
    "hapModuleInfos": [
        ...
    ],
    "reqPermissions": [
        "ohos.permission.ACCELEROMETER",
        "ohos.permission.GET_NETWORK_INFO",
        "ohos.permission.GET_WIFI_INFO",
        "ohos.permission.INTERNET",
        ...
    ],
		...
    "vendor": "快手",
    "versionCode": 999999,
    "versionName": "12.2.40"
}

获取App main ability

d.get_app_main_ability("com.kuaishou.hmapp")

输出的数据结构是Dict, 内容如下

{
    "name": "EntryAbility",
    "moduleName": "kwai",
    "moduleMainAbility": "EntryAbility",
    "mainModule": "kwai",
    "isLauncherAbility": true,
    "score": 2
}

设备操作

获取设备信息

from hmdriver2.proto import DeviceInfo

info: DeviceInfo = d.device_info

输入内容如下

DeviceInfo(productName='HUAWEI Mate 60 Pro', model='ALN-AL00', sdkVersion='12', sysVersion='ALN-AL00 5.0.0.60(SP12DEVC00E61R4P9log)', cpuAbi='arm64-v8a', wlanIp='172.31.125.111', displaySize=(1260, 2720), displayRotation=<DisplayRotation.ROTATION_0: 0>)

然后就可以获取你想要的值, 比如

info.productName
info.model
info.wlanIp
info.sdkVersion
info.sysVersion
info.cpuAbi
info.displaySize
info.displayRotation

获取设备分辨率

w, h = d.display_size

# outout: (1260, 2720)

获取设备旋转状态

from hmdriver2.proto import DisplayRotation

rotation = d.display_rotation
# ouput: DisplayRotation.ROTATION_0

设备旋转状态包括:

ROTATION_0 = 0    # 未旋转
ROTATION_90 = 1  # 顺时针旋转90度
ROTATION_180 = 2  # 顺时针旋转180度
ROTATION_270 = 3  # 顺时针旋转270度

设置设备旋转

from hmdriver2.proto import DisplayRotation

# 旋转180度
d.set_display_rotation(DisplayRotation.ROTATION_180)

Home

d.go_home()
# 或(与 uiautomator2 式命名一致)
d.press_home()

返回

d.go_back()
# 或
d.press_back()

亮屏

d.screen_on()

息屏

d.screen_off()

屏幕解锁

d.unlock()

Key Events

常用键:无需手写枚举时可直接调 Driver 的封装方法(实际均为向设备发送 KeyCode 的快捷方式,仍走 HDC uitest uiInput keyEvent 与现有一致):

方法 说明
d.press_back() / d.press_home() go_back() / go_home()
d.press_power() 电源键
d.press_menu() 菜单键
d.press_enter() 回车
d.press_backspace() 退格(DEL
d.press_delete() 向前删除(FORWARD_DEL
d.volume_up() / d.volume_down() / d.volume_mute() 音量 + / - / 静音
d.press_tab() / d.press_space() / d.press_escape() Tab、空格、Esc
d.page_up() / d.page_down() 翻页
d.press_dpad_up()d.press_dpad_center() 方向键与中心确认
d.press_multitask() 多任务/最近任务(VIRTUAL_MULTITASK
d.press_search() 查找(FIND
d.press_brightness_up() / d.press_brightness_down() 亮度调节键

任意码:其他按键仍用 press_key

from hmdriver2.proto import KeyCode

d.press_key(KeyCode.POWER)
d.press_key(2017)  # 需要时也可传整数码

完整键值表见 proto.py 中 KeyCode 枚举

执行 HDC 命令

data = d.shell("ls -l /data/local/tmp")

print(data.output)

这个方法等价于执行 hdc shell ls -l /data/local/tmp

Notes: HDC详细的命令解释参考:awesome-hdc

打开URL (schema)

d.open_url("http://www.baidu.com")

d.open_url("kwai://myprofile")

文件操作

# 将手机端文件下载到本地电脑
d.pull_file(rpath, lpath)

# 将本地电脑文件推送到手机端
d.push_file(lpath, rpath)

参数rpath表示手机端文件路径,lpath表示本地电脑文件路径

屏幕截图

d.screenshot(path)

参数path表示截图保存在本地电脑的文件路径

视觉定位(找图/找色)并点击

需要安装 OpenCV 依赖(可选项):

pip3 install -U "hmdriver2[opencv-python]"

找图点击(模板匹配)

# template_path 是你保存的“小图标/按钮”模板文件(png/jpg)
ok = d.click_image(template_path="res/btn_ok.png", threshold=0.88)
if not ok:
    print("not found")

找色点击(RGB 容差)

# 找到第一个接近该颜色的像素并点击(rgb)
d.click_color((255, 0, 0), tolerance=12)

# 可限制在截图上的一个区域 (x1, y1, x2, y2)(截图像素坐标)
d.click_color((0, 160, 255), tolerance=10, region=(100, 400, 600, 1200))

以上方法内部流程:先截图到本地临时文件 → 在截图像素坐标系里定位 → 映射到设备 display_size 坐标并点击。

屏幕录屏

方式一

# 开启录屏
d.screenrecord.start("test.mp4")

# do somethings
time.sleep(5)

# 结束录屏
d.screenrecord.stop()

上述方式如果录屏过程中,脚本出现异常时,stop无法被调用,导致资源泄漏,需要加上try catch

【推荐】方式二 ⭐️⭐️⭐️⭐️⭐️

with d.screenrecord.start("test2.mp4"):
    # do somethings
    time.sleep(5)

通过上下文语法,在录屏结束时框架会自动调用stop 清理资源

Notes: 使用屏幕录屏需要依赖opencv-python

pip3 install -U "hmdriver[opencv-python]"

Device Touch

单击

d.click(x, y)

# eg.
d.click(200, 300)
d.click(0.4, 0.6)

参数x, y表示点击的坐标,可以为绝对坐标值,也可以为相当坐标(屏幕百分比)

双击

d.double_click(x, y)

# eg.
d.double_click(500, 1000)
d.double_click(0.5, 0.4)

长按

d.long_click(x, y)

# eg.
d.long_click(500, 1000)
d.long_click(0.5, 0.4)

滑动

d.swipe(x1, y1, x2, y2, spped)

# eg.
d.swipe(600, 2600, 600, 1200, speed=2000)  # 上滑
d.swipe(0.5, 0.8, 0.5, 0.4, speed=2000)
  • x1, y1表示滑动的起始点,x2, y2表示滑动的终点
  • speed为滑动速率, 范围:200~40000, 不在范围内设为默认值为2000, 单位: 像素点/秒

滑动 ext

d.swipe_ext("up")  # 向上滑动,"left", "right", "up", "down"
d.swipe_ext("right", scale=0.8)  # 向右滑动,滑动距离为屏幕宽度的80%
d.swipe_ext("up", box=(0.2, 0.2, 0.8, 0.8))  # 在屏幕 (0.2, 0.2) -> (0.8, 0.8) 这个区域上滑

# 使用枚举作为参数
from hmdriver2.proto import SwipeDirection
d.swipe_ext(SwipeDirection.DOWN)  # 向下滑动
  • direction表示滑动方向,可以为up, down, left, right, 也可以为SwipeDirection的枚举值
  • scale表示滑动距离百分比,范围:0.1~1.0, 默认值为0.8
  • box表示滑动区域,格式为(x1, y1, x2, y2), 表示滑动区域的左上角和右下角的坐标,可以为绝对坐标值,也可以为相当坐标(屏幕百分比)

Notes: swipe_extswipe的区别在于swipe_ext可以指定滑动区域,并且可以指定滑动方向,更简洁灵活

复杂手势

复杂手势就是手指按下start,移动move,暂停pause的集合,最后运行action

g = d.gesture

g.start(x1, y1, interval=0.5)
g.move(x2, y2)
g.pause(interval=1)
g.move(x3, y3)
g.action()

也支持链式调用(推荐)

d.gesture.start(x1, y1, interval=.5).move(x2, y2).pause(interval=1).move(x3, y3).action()

参数x, y表示坐标位置,可以为绝对坐标值,也可以为相当坐标(屏幕百分比),interval表示手势持续的时间,单位秒。

如果只有start手势,则等价于点击:

d.gesture.start(x, y).action() # click

# 等价于
d.click(x, y)

如下是一个复杂手势的效果展示

Watch the gif

输入

d.input_text(text)

# eg.
d.input_text("adbcdfg")

参数x, y表示输入的位置,text表示输入的文本

控件操作

常规选择器

控件查找支持这些by属性

  • id
  • key
  • text
  • type
  • description
  • clickable
  • longClickable
  • scrollable
  • enabled
  • focused
  • selected
  • checked
  • checkable
  • isBefore
  • isAfter

Notes: 获取控件属性值可以配合 UI inspector 工具查看。子串、正则、类名/资源 id 的模糊匹配见下节 模糊子串与正则选择器

普通定位

d(text="tab_recrod")

d(id="drag")

# 定位所有`type`为Button的元素,选中第0个
d(type="Button", index=0)

Notes:当同一界面有多个属性相同的元素时,index属性非常实用

组合定位

指定多个by属性进行元素定位

# 定位`type`为Button且`text`为tab_recrod的元素
d(type="Button", text="tab_recrod")

相对定位

# 定位`text`为showToast的元素的前面一个元素
d(text="showToast", isAfter=True) 

# 定位`id`为drag的元素的后面一个元素
d(id="drag", isBefore=True)

模糊子串与正则选择器

在精确 text / type / description / id 等之外,可单独使用与 uiautomator2 相近的 Contains / Matches / 别名 关键字。底层会优先调用 On.xxx(value, MatchPattern) 风格的两参数接口;若当前设备 uitest 版本不支持,会依次尝试字符串型 pattern、以及备用的单参 On 方法名(如 On.textContains 等,见源码 hmdriver2/match.pyFALLBACK_ON_ALTERNATE_NAME)。

能力 示例参数名
文本子串、前缀、后缀、正则 textContainstextStartsWithtextEndsWithtextMatches
描述 descriptionContainsdescriptionMatches
类名/组件类型 className(同 type 精确), classNameContains, classNameMatchestypeContains, typeMatches
资源 id / key resourceId(同 id 精确), resourceIdContains, resourceIdMatchesidContains/idMatcheskeyContains/keyMatches

textMatches正则参数传入字符串(如 d(textMatches="^ok\\d+$"))。不要同时传冲突组合(如 idresourceId 同现、typeclassName 同现)——会 ReferenceError

示例

from hmdriver2 import MatchPattern  # 需要引用枚举时(一般不必手写)

d(textContains="登").click()
d(textMatches=r"^\d+条$").click()
d(resourceIdContains="entry", type="Button").click()
d(classNameMatches="Text|Button", textContains="提").click()

MatchPattern 枚举

定义见 hmdriver2.match.MatchPattern(同包可 from hmdriver2 import MatchPattern)。常见取值:EQUALSCONTAINSSTARTS_WITHENDS_WITHREGEX 等。若你设备上 Hypium 实际枚举与文档不一致,可在本仓库 match.py 中按真机调一次 invoke 后对齐,或只依赖上述关键字而不用显式 MatchPattern

控件查找

结合上面讲的控件选择器,就可以进行元素的查找

d(text="tab_recrod").exists()
d(type="Button", text="tab_recrod").exists()
d(text="tab_recrod", isAfter=True).exists()

# 返回 True or False

d(text="tab_recrod").find_component()
# 当没找到返回None

等待出现与等待消失

与 uiautomator2 的 wait / wait_gone 类似:超时时间单位为秒,返回 bool。未传 timeout 时默认 20 秒UiObject.DEFAULT_WAIT_TIMEOUT)。

  • wait(timeout=20.0):在超时前若当前选择器能匹配到控件则返回 True(并缓存 Component 供后续点击等),否则 False
  • wait_gone(timeout=20.0):在超时前若不再匹配到(含从未出现)则 True;若超时仍存在则 False
if d(text="加载完成").wait(timeout=10.0):
    d(text="进入首页").click()

# 等蒙层/弹窗消失(请用能唯一描述该层的选择条件)
d(type="Dialog").wait_gone(timeout=15.0)

控件信息

d(text="tab_recrod").info

# output:
{
    "id": "",
    "key": "",
    "type": "Button",
    "text": "tab_recrod",
    "description": "",
    "isSelected": False,
    "isChecked": False,
    "isEnabled": True,
    "isFocused": False,
    "isCheckable": False,
    "isClickable": True,
    "isLongClickable": False,
    "isScrollable": False,
    "bounds": {
        "left": 539,
        "top": 1282,
        "right": 832,
        "bottom": 1412
    },
    "boundsCenter": {
        "x": 685,
        "y": 1347
    }
}

也可以单独调用对应的属性

d(text="tab_recrod").id
d(text="tab_recrod").key
d(text="tab_recrod").type
d(text="tab_recrod").text
d(text="tab_recrod").description
d(text="tab_recrod").isSelected
d(text="tab_recrod").isChecked
d(text="tab_recrod").isEnabled
d(text="tab_recrod").isFocused
d(text="tab_recrod").isCheckable
d(text="tab_recrod").isClickable
d(text="tab_recrod").isLongClickable
d(text="tab_recrod").isScrollable
d(text="tab_recrod").bounds
d(text="tab_recrod").boundsCenter

控件数量

d(type="Button").count   # 输出当前页面`type`为Button的元素数量

# 也可以这样写
len(d(type="Button"))

控件点击

d(text="tab_recrod").click()
d(type="Button", text="tab_recrod").click()

d(text="tab_recrod").click_if_exists() 

以上两个方法有一定的区别

  • click 如果元素没找到,会报错ElementNotFoundError
  • click_if_exists 即使元素没有找到,也不会报错,相当于跳过

控件双击

d(text="tab_recrod").double_click()
d(type="Button", text="tab_recrod").double_click()

控件长按

d(text="tab_recrod").long_click()
d(type="Button", text="tab_recrod").long_click()

控件拖拽

from hmdriver2.proto import ComponentData

componentB: ComponentData = d(type="ListItem", index=1).find_component()

# 将元素拖动到元素B上
d(type="ListItem").drag_to(componentB)

drag_to的参数componentComponentData类型

控件缩放

# 将元素按指定的比例进行捏合缩小1倍
d(text="tab_recrod").pinch_in(scale=0.5)

# 将元素按指定的比例进行捏合放大2倍
d(text="tab_recrod").pinch_out(scale=2)

其中scale参数为放大和缩小比例

控件输入

d(text="tab_recrod").input_text("abc")

文本清除

d(text="tab_recrod").clear_text()

可滚动列表 scroll

可滚动容器UiObject 上通过 .scroll 使用,风格接近 uiautomator2 的 scroll

  • 轴向:scroll.vert / scroll.horizforward / backwardsteps, speed, extent 可调)、fling(快滑)。
  • 导航:toBeginning / toEnd 滚到顶/底;to(max_swipes=20, **选择器) 在容器内向下查找直到子项可匹配或次数用尽,返回 bool
  • 实现:优先设备侧 Component.scroll* 等;若无对应 API,则在控件 boundsDriver.swipe 模拟滑动。
lst = d(type="List", scrollable=True)   # 先能唯一定位到该列表容器
if lst.scroll.to(text="某一行文案", type="ListItem", max_swipes=25):
    d(text="某一行文案", type="ListItem").click()

lst.scroll.vert.forward(steps=2, speed=2200)
lst.scroll.toBeginning()
lst.scroll.horiz.fling(speed=5000)

后台 Watcher

不增加设备协议,在 PC 端用守护线程轮询已注册规则:匹配则执行 click / press_back / 自定义回调。与主流程共享同一 Driver 连接;底层已通过 HmClientI/O 锁串行化,避免多线程与 Hypium socket 交错。规则按注册顺序、每轮先命中先执行一条。典型用途:主脚本跑用例时顺带关掉权限弹窗、营销页「跳过」等。

d.watcher("ok").when(text="确定").click()
d.watcher("skip").when_xpath('//Button[@text="跳过"]').click()
d.watcher("b").when(text="暂无").press_back()
d.watcher("c").when(type="Text").do(lambda dr: dr.go_back())

d.watcher.start(interval=0.3)   # 轮询间隔,秒
# ... 主流程 d.start_app / d(text=...).click() ...
d.watcher.stop()
d.watcher.remove("ok")
d.watcher.clear()               # 移除全部规则

同名 d.watcher("x") 后注册会覆盖同名字规则。when(...) 与 u2 一样,参数与 d(...) 一致;when_xpath 使用 d.xpath 的 XML 与 XPath选择器 同套逻辑。

说明:这是轮询式与 u2 在 Android 上基于 UiAutomator 的 watcher 有延迟和负载差异,适合弹窗/提示类;与设备事件型的 d.toast_watcher 用途不同。

XPath选择器

xpath选择器基于标准的xpath规范,也可以使用//*[@属性="属性值"]的样式(xpath lite)

d.xpath('//root[1]/Row[1]/Column[1]/Row[1]/Button[3]')
d.xpath('//*[@text="showDialog"]')

控件xpath路径获取可以配合 UI inspector 工具查看

xpath控件是否存在

d.xpath('//*[@text="showDialog"]').exists()   # 返回True/False
d.xpath('//root[1]/Row[1]/Column[1]/Row[1]/Button[3]').exists()

xpath控件点击

d.xpath('//*[@text="showDialog"]').click()
d.xpath('//root[1]/Row[1]/Column[1]/Row[1]/Button[3]').click_if_exists()

以上两个方法有一定的区别

  • click 如果元素没找到,会报错XmlElementNotFoundError
  • click_if_exists 即使元素没有找到,也不会报错,相当于跳过

xpath控件双击

d.xpath('//*[@text="showDialog"]').double_click()

xpath控件长按

d.xpath('//*[@text="showDialog"]').long_click()

xpath控件输入

d.xpath('//*[@text="showDialog"]').input_text("adb")

获取控件树

d.dump_hierarchy()

输出控件树格式参考 hierarchy.json

获取Toast

# 启动toast监控
d.toast_watcher.start()

# do something 比如触发toast的操作
d(text="xx").click()  

# 获取toast
toast = d.toast_watcher.get_toast()

# output: 'testMessage'

鸿蒙Uitest协议

See DEVELOP.md

拓展阅读

hmdriver2 发布:开启鸿蒙 NEXT 自动化新时代

Contributors

Contributors

Reference

About

UI Automation Framework for HarmonyOS NEXT | 鸿蒙NEXT自动化框架

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • Python 99.9%
  • Shell 0.1%