devhelmkit 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- devhelmkit/__init__.py +44 -0
- devhelmkit/android/__init__.py +7 -0
- devhelmkit/android/driver.py +18 -0
- devhelmkit/assets/so/arm64-v8a/agent_v1.so +0 -0
- devhelmkit/assets/so/arm64-v8a/agent_v2.so +0 -0
- devhelmkit/assets/so/x86_64/agent.so +0 -0
- devhelmkit/core/__init__.py +3 -0
- devhelmkit/core/base_component.py +214 -0
- devhelmkit/core/base_driver.py +414 -0
- devhelmkit/core/base_window.py +25 -0
- devhelmkit/core/selector_spec.py +102 -0
- devhelmkit/entry.py +90 -0
- devhelmkit/exceptions.py +56 -0
- devhelmkit/harmony/__init__.py +3 -0
- devhelmkit/harmony/agent/__init__.py +7 -0
- devhelmkit/harmony/agent/so_manager.py +300 -0
- devhelmkit/harmony/config.py +124 -0
- devhelmkit/harmony/device/__init__.py +6 -0
- devhelmkit/harmony/device/hdc.py +430 -0
- devhelmkit/harmony/driver.py +1416 -0
- devhelmkit/harmony/finder/__init__.py +12 -0
- devhelmkit/harmony/finder/component_finder.py +336 -0
- devhelmkit/harmony/finder/popup_handler.py +81 -0
- devhelmkit/harmony/finder/selector_adapter.py +101 -0
- devhelmkit/harmony/finder/xpath_query.py +110 -0
- devhelmkit/harmony/rpc/__init__.py +12 -0
- devhelmkit/harmony/rpc/client.py +126 -0
- devhelmkit/harmony/rpc/proxy_v2.py +106 -0
- devhelmkit/harmony/rpc/remote_object.py +48 -0
- devhelmkit/harmony/uiobject.py +246 -0
- devhelmkit/harmony/uiwindow.py +43 -0
- devhelmkit/harmony/webview/__init__.py +17 -0
- devhelmkit/harmony/webview/chromedriver_manager.py +251 -0
- devhelmkit/harmony/webview/devtools_finder.py +131 -0
- devhelmkit/harmony/webview/webview_driver.py +326 -0
- devhelmkit/model/__init__.py +3 -0
- devhelmkit/model/action.py +147 -0
- devhelmkit/model/app_state.py +50 -0
- devhelmkit/model/constants.py +22 -0
- devhelmkit/model/display.py +32 -0
- devhelmkit/model/format_string.py +24 -0
- devhelmkit/model/input.py +50 -0
- devhelmkit/model/json_base.py +42 -0
- devhelmkit/model/keys.py +375 -0
- devhelmkit/model/match_pattern.py +15 -0
- devhelmkit/model/page.py +13 -0
- devhelmkit/model/params.py +42 -0
- devhelmkit/model/rect.py +58 -0
- devhelmkit/model/runnable.py +18 -0
- devhelmkit/utils/__init__.py +3 -0
- devhelmkit/utils/logger.py +72 -0
- devhelmkit/utils/retry.py +46 -0
- devhelmkit/utils/timeout.py +64 -0
- devhelmkit-0.1.0.dist-info/METADATA +411 -0
- devhelmkit-0.1.0.dist-info/RECORD +58 -0
- devhelmkit-0.1.0.dist-info/WHEEL +5 -0
- devhelmkit-0.1.0.dist-info/licenses/LICENSE +201 -0
- devhelmkit-0.1.0.dist-info/top_level.txt +1 -0
devhelmkit/__init__.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# !/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""devhelmkit:跨平台 UI 自动化框架。
|
|
4
|
+
|
|
5
|
+
包根只导出稳定入口、公共契约与统一异常,平台实现延迟导入。
|
|
6
|
+
"""
|
|
7
|
+
from devhelmkit.entry import connect
|
|
8
|
+
from devhelmkit.core.base_driver import BaseDriver
|
|
9
|
+
from devhelmkit.core.base_component import BaseComponent
|
|
10
|
+
from devhelmkit.core.base_window import BaseWindow
|
|
11
|
+
from devhelmkit.core.selector_spec import SelectorSpec, build_selector
|
|
12
|
+
from devhelmkit.exceptions import (
|
|
13
|
+
DevhelmError,
|
|
14
|
+
DeviceNotFoundError,
|
|
15
|
+
DeviceConnectError,
|
|
16
|
+
PlatformNotSupportedError,
|
|
17
|
+
DevhelmTimeoutError,
|
|
18
|
+
RpcError,
|
|
19
|
+
BackendObjectDroppedError,
|
|
20
|
+
ComponentNotFoundError,
|
|
21
|
+
ComponentDisappearedError,
|
|
22
|
+
AgentError,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"connect",
|
|
27
|
+
"BaseDriver",
|
|
28
|
+
"BaseComponent",
|
|
29
|
+
"BaseWindow",
|
|
30
|
+
"SelectorSpec",
|
|
31
|
+
"build_selector",
|
|
32
|
+
"DevhelmError",
|
|
33
|
+
"DeviceNotFoundError",
|
|
34
|
+
"DeviceConnectError",
|
|
35
|
+
"PlatformNotSupportedError",
|
|
36
|
+
"DevhelmTimeoutError",
|
|
37
|
+
"RpcError",
|
|
38
|
+
"BackendObjectDroppedError",
|
|
39
|
+
"ComponentNotFoundError",
|
|
40
|
+
"ComponentDisappearedError",
|
|
41
|
+
"AgentError",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# !/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""AndroidDriver:Android 平台占位实现。
|
|
4
|
+
|
|
5
|
+
首阶段不引入 AndroidUiObject、AdbDevice、uiautomator2 适配层。
|
|
6
|
+
connect(platform="android") 由 entry.py 统一抛 PlatformNotSupportedError,
|
|
7
|
+
不会到达本类。
|
|
8
|
+
|
|
9
|
+
后续接入 Android 时,在此实现 BaseDriver 契约,并解除 entry.py 中的
|
|
10
|
+
平台拦截。
|
|
11
|
+
"""
|
|
12
|
+
from devhelmkit.core.base_driver import BaseDriver
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AndroidDriver(BaseDriver):
|
|
16
|
+
"""Android 平台占位驱动,未实现任何契约方法。"""
|
|
17
|
+
|
|
18
|
+
pass
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# !/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""BaseComponent:跨平台控件对象契约。
|
|
4
|
+
|
|
5
|
+
对齐 U2 单对象模型:所有操作和关系方法统一在此定义。
|
|
6
|
+
内部持有 SelectorSpec(纯数据),不依赖任何可操作选择器抽象。
|
|
7
|
+
命名原则:U2 风格 snake_case 为基准,语义化方法为补充。
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from abc import ABC, abstractmethod
|
|
12
|
+
from typing import Any, Dict, Optional, Tuple, Union, TYPE_CHECKING
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from PIL.Image import Image
|
|
16
|
+
from devhelmkit.core.selector_spec import SelectorSpec
|
|
17
|
+
from devhelmkit.core.base_driver import BaseDriver
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class BaseComponent(ABC):
|
|
21
|
+
"""控件契约(查找到的控件对象)。"""
|
|
22
|
+
|
|
23
|
+
def __init__(self, driver: 'BaseDriver', selector: 'SelectorSpec'):
|
|
24
|
+
"""
|
|
25
|
+
Args:
|
|
26
|
+
driver: 驱动实例
|
|
27
|
+
selector: 定位条件(纯数据)
|
|
28
|
+
"""
|
|
29
|
+
self._driver = driver
|
|
30
|
+
self._selector = selector
|
|
31
|
+
|
|
32
|
+
# ============================================================
|
|
33
|
+
# 点击类
|
|
34
|
+
# ============================================================
|
|
35
|
+
|
|
36
|
+
@abstractmethod
|
|
37
|
+
def click(self) -> None:
|
|
38
|
+
"""点击。"""
|
|
39
|
+
|
|
40
|
+
@abstractmethod
|
|
41
|
+
def long_click(self, duration: float = 0.5) -> None:
|
|
42
|
+
"""长按。"""
|
|
43
|
+
|
|
44
|
+
@abstractmethod
|
|
45
|
+
def double_click(self) -> None:
|
|
46
|
+
"""双击。"""
|
|
47
|
+
|
|
48
|
+
@abstractmethod
|
|
49
|
+
def click_exists(self, timeout: float = 0) -> bool:
|
|
50
|
+
"""存在则点击,返回是否成功。"""
|
|
51
|
+
|
|
52
|
+
# ============================================================
|
|
53
|
+
# 文本类
|
|
54
|
+
# ============================================================
|
|
55
|
+
|
|
56
|
+
@abstractmethod
|
|
57
|
+
def set_text(self, text: str) -> None:
|
|
58
|
+
"""输入文本。"""
|
|
59
|
+
|
|
60
|
+
@abstractmethod
|
|
61
|
+
def get_text(self) -> str:
|
|
62
|
+
"""获取文本。"""
|
|
63
|
+
|
|
64
|
+
@abstractmethod
|
|
65
|
+
def clear_text(self) -> None:
|
|
66
|
+
"""清空文本。"""
|
|
67
|
+
|
|
68
|
+
@abstractmethod
|
|
69
|
+
def input_text(self, text: str) -> None:
|
|
70
|
+
"""输入文本(set_text 别名)。"""
|
|
71
|
+
|
|
72
|
+
# ============================================================
|
|
73
|
+
# 状态类
|
|
74
|
+
# ============================================================
|
|
75
|
+
|
|
76
|
+
@abstractmethod
|
|
77
|
+
def exists(self) -> bool:
|
|
78
|
+
"""是否存在。"""
|
|
79
|
+
|
|
80
|
+
@abstractmethod
|
|
81
|
+
def wait(self, timeout: float) -> bool:
|
|
82
|
+
"""等待出现。"""
|
|
83
|
+
|
|
84
|
+
@abstractmethod
|
|
85
|
+
def wait_gone(self, timeout: float) -> bool:
|
|
86
|
+
"""等待消失。"""
|
|
87
|
+
|
|
88
|
+
# ============================================================
|
|
89
|
+
# 信息类
|
|
90
|
+
# ============================================================
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
@abstractmethod
|
|
94
|
+
def info(self) -> Dict[str, Any]:
|
|
95
|
+
"""控件信息(text/id/class/bounds/clickable/enabled/visible)。"""
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
@abstractmethod
|
|
99
|
+
def bounds(self) -> Any:
|
|
100
|
+
"""控件坐标 Rect(left, top, right, bottom)。"""
|
|
101
|
+
|
|
102
|
+
@abstractmethod
|
|
103
|
+
def center(self) -> Tuple[int, int]:
|
|
104
|
+
"""控件中心点 (x, y)。"""
|
|
105
|
+
|
|
106
|
+
@abstractmethod
|
|
107
|
+
def get_attribute(self, name: str) -> Any:
|
|
108
|
+
"""获取控件属性。"""
|
|
109
|
+
|
|
110
|
+
@abstractmethod
|
|
111
|
+
def screenshot(self, filename: Optional[str] = None) -> Union['Image', str, None]:
|
|
112
|
+
"""控件截图。"""
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
@abstractmethod
|
|
116
|
+
def description(self) -> str:
|
|
117
|
+
"""description 属性。"""
|
|
118
|
+
|
|
119
|
+
@abstractmethod
|
|
120
|
+
def get_hint(self) -> str:
|
|
121
|
+
"""获取 hint 属性。"""
|
|
122
|
+
|
|
123
|
+
@abstractmethod
|
|
124
|
+
def get_all_properties(self) -> dict:
|
|
125
|
+
"""获取所有属性。"""
|
|
126
|
+
|
|
127
|
+
@abstractmethod
|
|
128
|
+
def get_original_text(self) -> str:
|
|
129
|
+
"""获取原始文本。"""
|
|
130
|
+
|
|
131
|
+
# ============================================================
|
|
132
|
+
# 布尔属性
|
|
133
|
+
# ============================================================
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
@abstractmethod
|
|
137
|
+
def is_long_clickable(self) -> bool:
|
|
138
|
+
"""是否可长按。"""
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
@abstractmethod
|
|
142
|
+
def is_checked(self) -> bool:
|
|
143
|
+
"""是否已选中。"""
|
|
144
|
+
|
|
145
|
+
@property
|
|
146
|
+
@abstractmethod
|
|
147
|
+
def is_checkable(self) -> bool:
|
|
148
|
+
"""是否可选中。"""
|
|
149
|
+
|
|
150
|
+
@property
|
|
151
|
+
@abstractmethod
|
|
152
|
+
def is_selected(self) -> bool:
|
|
153
|
+
"""是否处于选中态。"""
|
|
154
|
+
|
|
155
|
+
# ============================================================
|
|
156
|
+
# 拖拽类
|
|
157
|
+
# ============================================================
|
|
158
|
+
|
|
159
|
+
@abstractmethod
|
|
160
|
+
def drag_to(self, x: int, y: int) -> None:
|
|
161
|
+
"""拖拽到坐标。"""
|
|
162
|
+
|
|
163
|
+
@abstractmethod
|
|
164
|
+
def drag_to_component(self, other: 'BaseComponent') -> None:
|
|
165
|
+
"""拖拽到另一控件。"""
|
|
166
|
+
|
|
167
|
+
# ============================================================
|
|
168
|
+
# 缩放类
|
|
169
|
+
# ============================================================
|
|
170
|
+
|
|
171
|
+
@abstractmethod
|
|
172
|
+
def pinch_in(self, scale: float = 0.5) -> None:
|
|
173
|
+
"""控件上捏合缩小。"""
|
|
174
|
+
|
|
175
|
+
@abstractmethod
|
|
176
|
+
def pinch_out(self, scale: float = 1.5) -> None:
|
|
177
|
+
"""控件上捏合放大。"""
|
|
178
|
+
|
|
179
|
+
# ============================================================
|
|
180
|
+
# 滚动类
|
|
181
|
+
# ============================================================
|
|
182
|
+
|
|
183
|
+
@abstractmethod
|
|
184
|
+
def scroll_search(self, target, vertical: bool = True,
|
|
185
|
+
offset: Optional[int] = None) -> Optional['BaseComponent']:
|
|
186
|
+
"""滚动查找子控件。"""
|
|
187
|
+
|
|
188
|
+
@abstractmethod
|
|
189
|
+
def scroll_to_top(self, speed: int = 600) -> None:
|
|
190
|
+
"""滚动到顶部。"""
|
|
191
|
+
|
|
192
|
+
@abstractmethod
|
|
193
|
+
def scroll_to_bottom(self, speed: int = 600) -> None:
|
|
194
|
+
"""滚动到底部。"""
|
|
195
|
+
|
|
196
|
+
# ============================================================
|
|
197
|
+
# 关系选择器(返回新的 BaseComponent,持有衍生 SelectorSpec)
|
|
198
|
+
# ============================================================
|
|
199
|
+
|
|
200
|
+
@abstractmethod
|
|
201
|
+
def child(self, **kwargs) -> 'BaseComponent':
|
|
202
|
+
"""子控件。"""
|
|
203
|
+
|
|
204
|
+
@abstractmethod
|
|
205
|
+
def sibling(self, **kwargs) -> 'BaseComponent':
|
|
206
|
+
"""兄弟控件。"""
|
|
207
|
+
|
|
208
|
+
@abstractmethod
|
|
209
|
+
def after(self, **kwargs) -> 'BaseComponent':
|
|
210
|
+
"""之后控件。"""
|
|
211
|
+
|
|
212
|
+
@abstractmethod
|
|
213
|
+
def before(self, **kwargs) -> 'BaseComponent':
|
|
214
|
+
"""之前控件。"""
|