Skip to main content

UI Automation Framework for Harmony Next

Project description

hmdriver2

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

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

Feature

  • 支持鸿蒙Next系统的自动化
  • 无侵入式,无需在手机安装基于ArkTS的testRunner APP
  • 稳定高效,直接和鸿蒙底层uitest服务交互
  • 轻量,上手简单,即插即用
  • 支持应用管理
    • 应用启动,停止
    • 应用安装,卸载
    • 应用数据清理
    • 获取应用列表,应用详情等
  • 支持设备操作
    • 获取设备信息,分辨率,旋转状态等
    • 屏幕解锁,亮屏,息屏
    • Key Events
    • 文件操作
    • 屏幕截图
    • 屏幕录屏
    • 手势操作(点击,滑动,输入,复杂手势)
  • 支持控件操作
    • 控件查找(联合查找,模糊查找,相对查找)
    • 控件信息获取
    • 控件点击,长按,拖拽,缩放
    • 文本输入,清除
    • 获取控件树
  • 支持Toast获取
  • [TODO] 全场景弹窗处理
  • [TODO] 操作标记

QUICK START

  1. 配置鸿蒙HDC环境
    1. 下载 Command Line Tools 并解压
    2. hdc文件在command-line-tools/sdk/HarmonyOS-NEXT-DB2/openharmony/toolchains目录下
    3. 配置环境变量,macOS为例,在~/.bash_profile 或者 ~/.zshrc文件中添加
export HM_SDK_HOME="/Users/develop/command-line-tools/sdk/HarmonyOS-NEXT-DB2"  //请以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 hmdirver2

# 或者这样
python3 -m pip install -U hmdirver2

也可以通过源码安装

git clone git@github.com:codematrixer/hmdriver2.git
cd hmdriver2
pip3 install -U -e .
  1. 接下来就可以愉快的进行脚本开发了 😊😊
from hmdriver2.driver import Driver

d = Driver("FMR0223C13000649")

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>)


API Documents

初始化Driver

from hmdriver2.driver import Driver

d = Driver("FMR0223C13000649")

初始化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", "EntryAbility")

传入的两个参数分别是package_name, page_name,可以通过hdc命令获取hdc shell aa dump -l

停止App

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

清除App数据

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

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

获取App详情

d.get_app_info("com.samples.test.uitest")

输出的数据结构是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"
}

设备操作

获取设备信息

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度

备注:目前旋转状态只能查看,不支持设置

Home

d.go_home()

返回

d.go_back()

亮屏

d.screen_on()

息屏

d.screen_off()

屏幕解锁

d.unlock()

Key Events

from hmdriver2.proto import KeyCode

d.press_key(KeyCode.POWER)

详细的Key code请参考 harmony key code

执行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表示截图保存在本地电脑的文件路径

屏幕录屏

方式一

# 开启录屏
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 清理资源

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, 不在范围内设为默认值为600, 单位: 像素点/秒

输入

d.input_text(x, y, text)

# eg.
d.input_text(0.3, 0.5, "adbcdfg")

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

复杂手势

复杂手势就是手指按下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(x, y, interval=.5).move(x, y).pause(interval=1).move(x, y).action()

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

这是一个复杂手势的效果展示 Watch the gif

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

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

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

控件操作

控件选择器

控件查找支持这些by属性

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

普通定位

d(text="tab_recrod")

d(id="drag")

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

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

模糊定位TODO

组合定位 指定多个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)

控件查找

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

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

控件信息

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()

获取控件树

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

Refer to

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

hmdriver2-1.2.0.tar.gz (88.5 kB view details)

Uploaded Source

Built Distribution

hmdriver2-1.2.0-py3-none-any.whl (86.1 kB view details)

Uploaded Python 3

File details

Details for the file hmdriver2-1.2.0.tar.gz.

File metadata

  • Download URL: hmdriver2-1.2.0.tar.gz
  • Upload date:
  • Size: 88.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.1 CPython/3.12.6

File hashes

Hashes for hmdriver2-1.2.0.tar.gz
Algorithm Hash digest
SHA256 0dff22ccefec1b744c9ac02fcbff56356e4c24538decbd766f9b39c39a920fb3
MD5 8553200d9d24ac822847c17d997dde09
BLAKE2b-256 e38f138fa4570423223710c6b906a33acea7a57e0f7340a2dc21af37c46ced0f

See more details on using hashes here.

File details

Details for the file hmdriver2-1.2.0-py3-none-any.whl.

File metadata

  • Download URL: hmdriver2-1.2.0-py3-none-any.whl
  • Upload date:
  • Size: 86.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.1 CPython/3.12.6

File hashes

Hashes for hmdriver2-1.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9a8c86ff4a22950f16fd2e379e5eeb243d19c6da30d7b2c9efce8a0d84ffdda6
MD5 0a70326c4667472ae77b15416855901b
BLAKE2b-256 fa8e73d575c4b09e5bdeb54d2235a4bc1aec8f130c203116cb04c672403b41bc

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page