ha4t package

Subpackages

Submodules

Module contents

HA4T —— 跨平台 UI 自动化测试框架。

核心抽象: - connect(platform=...) 返回 Device,所有平台同一套 API:

dev.click(...) / dev.swipe(...) / dev.wait(...) / dev.assert_element(...) ...

  • Selector 类承载跨平台元素定位器::

    from ha4t import connect, Selector
    LOGIN = Selector(android={"text": "登录"}, ios={"label": "Login"})
    dev = connect(platform="android")
    dev.click(LOGIN)                       # 平台分桶自动选 android 分支
    
  • 临时定位也接受 canonical kwargs,自动按当前平台翻译::

    dev.click(text="登录")                 # iOS 上自动用 label='登录'
    

模块结构: - ha4t.selector —— Selector / canonical→native 映射 - ha4t.device —— Device 类(用 mixin 装配出来) - ha4t.matchers —— OCR / 图像找点(与 Device 解耦的找点策略) - ha4t.drivers —— Android / iOS / Harmony 平台 driver

POM 工程化用法见工作区根目录 ``AGENTS.md``(编辑器初始化时自动生成)。

class Device(driver=None, config: DeviceConfig | None = None)[源代码]

基类:InteractionMixin, QueryMixin, AppMixin, FileMixin, CaptureMixin, DeviceBase

跨平台 UI 自动化设备入口。

持有 config + driver,把所有 UI 操作统一暴露在 self 上。 平台差异由 driver 隔离,selector 跨平台语义由 self._resolve_selector + Selector 类承担,Device 自身只负责 动作分发

connect(platform: str = 'android', device_serial: str | None = None, android_package_name: str | None = None, android_activity_name: str | None = None, **driver_kwargs) Device[源代码]

连接设备,返回 Device

参数:
  • platform -- 平台标识 android / ios / harmony

  • device_serial -- 设备序列号;为 None 时取系统第一个可用设备

  • android_package_name -- Android 包名(写进 DeviceConfigstart_app 默认值)

  • android_activity_name -- Android 启动 Activity(同上)

  • driver_kwargs -- 透传给底层 driver.connect() 的额外关键字

include(path: str) None[源代码]

在当前进程内执行另一个用例 .py 文件,复用其步骤。

被引用文件中的 import os / os.environ / from ha4t import ... / from time import sleep / dev = connect(...) 这些样板代码会被剥离, 其余源码在调用者的全局命名空间中 exec——这样被引用文件可以直接 使用调用者已经建立的 dev 连接 / sleep 等符号,无需重新连接设备。

路径解析顺序:调用者所在目录 → 调用者所在目录下的 testcases/(编辑器工作区模式)→ 当前工作目录下的 testcases/ → 当前工作目录 → 字面路径。 通过 _INCLUDE_STACK 防止循环引用。

class Selector(*, image: str | None = None, **kwargs: Any)[源代码]

基类:object

跨平台元素定位器(不可变值对象)。

示例:

# 平台 native selector 分别采集 LOGIN_BTN = Selector(

android={"resourceId": "com.x:id/login", "text": "登录"}, ios={"label": "Login"},

)

# 跨平台共享图像 LOGIN_ICON = Selector(image="login_icon.png")

# 带 meta(_parent 父子关系、_doc 给 AI 看的注释) BACK_BTN = Selector(

_parent="顶部导航", _doc="点击退回上一页", android={"text": "返回"}, ios={"label": "Back"},

)

解析顺序(for_platform(p)):
  1. 该平台分桶里有 native selector → 返回之

  2. 平台分桶为空 → 落回 image`(如有),返回 `{"image": ...}

  3. 都没 → raise SelectorNotAvailableError

property parent: str | None

父元素名;无父返回 None。

property doc: str | None

元素自由文本说明;无返回 None。

property image: str | None

图像模板文件名;非图像元素返回 None。

platforms() FrozenSet[str][源代码]

有 native selector 的平台名集合(不含 image 元素)。

for_platform(platform: str) Dict[str, Any][源代码]

返回该平台的 native kwargs,可以 ** 解包传给 driver.find。

优先级:平台分桶 > image fallback > raise。 meta 字段(_parent/_doc)从来不在输出里。

supports(platform: str) bool[源代码]

该平台是否有可用定位(含 image fallback)。

with_platform(platform: str, **kwargs: Any) Selector[源代码]

返回新 Selector,覆盖某平台分桶的字段(不动其它平台 / meta / image)。

without_platform(platform: str) Selector[源代码]

返回新 Selector,移除某平台分桶(清除当前平台 selector 用)。

format(**kwargs: Any) Selector[源代码]

按平台分桶逐字段 str.format(**kwargs),返回新 Selector。

用于参数化元素(列表项第 N 个、模板文本含变量等):

"商品项": Selector(

android={"xpath": "//*[@resource-id='list']/View[{index}]"}, ios={"xpath": "//XCUIElementTypeCell[{index}]"},

)

dev.click(ELEMENTS["商品项"].format(index=2))

非字符串字段(index、image)原样保留。

classmethod everywhere(**kwargs: Any) Selector[源代码]

所有平台用同一份 native kwargs 的工厂方法。

"取消": Selector.everywhere(text="取消") # 等价 Selector(android={"text":"取消"}, ios={"text":"取消"}, harmony={"text":"取消"})

注意:本工厂走的是 canonical 同名 假设——text="取消" 在所有平台都用 text 字段。这适用于真正所有平台都同样写法的场景;如果各平台字段不同,仍要写完整的 分桶 Selector(android=..., ios=...)。

exception SelectorNotAvailableError[源代码]

基类:LookupError

当前 Selector 在指定平台没有可用定位信息(既没采集 native selector,也没 image)。