applib-ui 0.0.1__tar.gz
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.
- applib_ui-0.0.1/PKG-INFO +170 -0
- applib_ui-0.0.1/README.md +144 -0
- applib_ui-0.0.1/applib_ui/__init__.py +13 -0
- applib_ui-0.0.1/applib_ui/case.py +79 -0
- applib_ui-0.0.1/applib_ui/cli.py +75 -0
- applib_ui-0.0.1/applib_ui/core/__init__.py +0 -0
- applib_ui-0.0.1/applib_ui/core/android/__init__.py +16 -0
- applib_ui-0.0.1/applib_ui/core/android/case.py +8 -0
- applib_ui-0.0.1/applib_ui/core/android/driver.py +191 -0
- applib_ui-0.0.1/applib_ui/core/android/element.py +291 -0
- applib_ui-0.0.1/applib_ui/core/android/page.py +6 -0
- applib_ui-0.0.1/applib_ui/core/android/recorder.py +171 -0
- applib_ui-0.0.1/applib_ui/core/android/remote_adb.py +69 -0
- applib_ui-0.0.1/applib_ui/core/android/remote_driver.py +176 -0
- applib_ui-0.0.1/applib_ui/core/img/__init__.py +4 -0
- applib_ui-0.0.1/applib_ui/core/img/driver.py +43 -0
- applib_ui-0.0.1/applib_ui/core/img/element.py +73 -0
- applib_ui-0.0.1/applib_ui/core/ios/__init__.py +16 -0
- applib_ui-0.0.1/applib_ui/core/ios/case.py +8 -0
- applib_ui-0.0.1/applib_ui/core/ios/driver.py +290 -0
- applib_ui-0.0.1/applib_ui/core/ios/elem.py +222 -0
- applib_ui-0.0.1/applib_ui/core/ios/local_wda.py +576 -0
- applib_ui-0.0.1/applib_ui/core/ios/page.py +6 -0
- applib_ui-0.0.1/applib_ui/core/ios/recorder.py +202 -0
- applib_ui-0.0.1/applib_ui/core/ios/remote_driver.py +220 -0
- applib_ui-0.0.1/applib_ui/core/ios/remote_wda.py +54 -0
- applib_ui-0.0.1/applib_ui/core/ocr/__init__.py +5 -0
- applib_ui-0.0.1/applib_ui/core/ocr/driver.py +185 -0
- applib_ui-0.0.1/applib_ui/core/ocr/element.py +65 -0
- applib_ui-0.0.1/applib_ui/device.py +53 -0
- applib_ui-0.0.1/applib_ui/element.py +210 -0
- applib_ui-0.0.1/applib_ui/page.py +6 -0
- applib_ui-0.0.1/applib_ui/pytest_plugin.py +57 -0
- applib_ui-0.0.1/applib_ui/running/__init__.py +0 -0
- applib_ui-0.0.1/applib_ui/running/conf.py +14 -0
- applib_ui-0.0.1/applib_ui/running/conf.yml +19 -0
- applib_ui-0.0.1/applib_ui/running/runner.py +33 -0
- applib_ui-0.0.1/applib_ui/utils/__init__.py +1 -0
- applib_ui-0.0.1/applib_ui/utils/aes_util.py +47 -0
- applib_ui-0.0.1/applib_ui/utils/allure_util.py +213 -0
- applib_ui-0.0.1/applib_ui/utils/base64_util.py +21 -0
- applib_ui-0.0.1/applib_ui/utils/common.py +84 -0
- applib_ui-0.0.1/applib_ui/utils/config.py +135 -0
- applib_ui-0.0.1/applib_ui/utils/cv_util.py +110 -0
- applib_ui-0.0.1/applib_ui/utils/dingtalk.py +56 -0
- applib_ui-0.0.1/applib_ui/utils/excel_util.py +101 -0
- applib_ui-0.0.1/applib_ui/utils/log.py +12 -0
- applib_ui-0.0.1/applib_ui/utils/mail.py +47 -0
- applib_ui-0.0.1/applib_ui/utils/mysql_util.py +74 -0
- applib_ui-0.0.1/applib_ui/utils/nacos_util.py +52 -0
- applib_ui-0.0.1/applib_ui/utils/pytest_util.py +89 -0
- applib_ui-0.0.1/applib_ui/utils/swagger_util.py +195 -0
- applib_ui-0.0.1/applib_ui.egg-info/PKG-INFO +170 -0
- applib_ui-0.0.1/applib_ui.egg-info/SOURCES.txt +61 -0
- applib_ui-0.0.1/applib_ui.egg-info/dependency_links.txt +1 -0
- applib_ui-0.0.1/applib_ui.egg-info/entry_points.txt +5 -0
- applib_ui-0.0.1/applib_ui.egg-info/requires.txt +20 -0
- applib_ui-0.0.1/applib_ui.egg-info/top_level.txt +1 -0
- applib_ui-0.0.1/pyproject.toml +50 -0
- applib_ui-0.0.1/setup.cfg +4 -0
- applib_ui-0.0.1/tests/test_device.py +50 -0
- applib_ui-0.0.1/tests/test_pytest_integration.py +150 -0
- applib_ui-0.0.1/tests/test_runner.py +46 -0
applib_ui-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: applib-ui
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Android / iOS 通用 UI 自动化测试框架
|
|
5
|
+
Project-URL: Repository, https://github.com/purewhite-88/applib
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: Framework :: Pytest
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: click==8.1.7
|
|
11
|
+
Requires-Dist: loguru==0.7.0
|
|
12
|
+
Requires-Dist: PyYAML<7,>=6.0.2
|
|
13
|
+
Requires-Dist: allure-pytest<3,>=2.13.5
|
|
14
|
+
Requires-Dist: pytest<10,>=8.2
|
|
15
|
+
Provides-Extra: android
|
|
16
|
+
Requires-Dist: uiautomator2==3.2.4; extra == "android"
|
|
17
|
+
Provides-Extra: ios
|
|
18
|
+
Requires-Dist: tidevice==0.12.10; extra == "ios"
|
|
19
|
+
Requires-Dist: facebook-wda==1.5.0; extra == "ios"
|
|
20
|
+
Provides-Extra: image
|
|
21
|
+
Requires-Dist: numpy<3,>=1.26; extra == "image"
|
|
22
|
+
Requires-Dist: opencv-python<5,>=4.8; extra == "image"
|
|
23
|
+
Provides-Extra: ocr
|
|
24
|
+
Requires-Dist: requests<3,>=2.28; extra == "ocr"
|
|
25
|
+
Requires-Dist: natocr==2.3.0; extra == "ocr"
|
|
26
|
+
|
|
27
|
+
# applib-ui
|
|
28
|
+
|
|
29
|
+
基于 pytest 的 Android / iOS 自动化测试库,提供设备驱动、页面对象、双平台元素定位、截图、录屏和 Allure 报告支持。Python 要求为 3.10 及以上。
|
|
30
|
+
|
|
31
|
+
## 安装
|
|
32
|
+
|
|
33
|
+
在业务测试项目的虚拟环境中安装对应平台的依赖:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
python -m pip install 'applib-ui[android]'
|
|
37
|
+
# iOS
|
|
38
|
+
python -m pip install 'applib-ui[ios]'
|
|
39
|
+
# 双平台、图像识别及 OCR
|
|
40
|
+
python -m pip install 'applib-ui[android,ios,image,ocr]'
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
基础包安装命令为 `python -m pip install applib-ui`,Python 导入名为 `applib_ui`。
|
|
44
|
+
|
|
45
|
+
如需从源码开发安装:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
python -m pip install -e '/path/to/applib-ui[android]'
|
|
49
|
+
# iOS
|
|
50
|
+
python -m pip install -e '/path/to/applib-ui[ios]'
|
|
51
|
+
# 双平台、图像识别及 OCR
|
|
52
|
+
python -m pip install -e '/path/to/applib-ui[android,ios,image,ocr]'
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
仅安装基础包 `python -m pip install -e /path/to/applib-ui` 即可使用 pytest 插件与通用工具。设备驱动和识别库按实际使用的功能安装。
|
|
56
|
+
|
|
57
|
+
Android 需要可用的 adb 及已授权设备;iOS 需要 macOS、已签名且可运行的 WDA,以及现有 WDA 启动流程所需的 tidevice / go-ios 工具。录屏还需要对应平台使用的 scrcpy / ffmpeg。
|
|
58
|
+
|
|
59
|
+
## 使用 pytest
|
|
60
|
+
|
|
61
|
+
安装后插件自动加载,无需在业务项目中编写 `pytest_plugins`。插件通过标准的 [pytest 插件入口](https://docs.pytest.org/en/stable/how-to/writing_plugins.html#making-your-plugin-installable-by-others)注册。
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
# tests/test_app.py
|
|
65
|
+
|
|
66
|
+
def test_launch(app_driver):
|
|
67
|
+
app_driver.start_app()
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pytest tests/test_app.py --app-platform android --app-device SERIAL --app-package com.example.app
|
|
72
|
+
pytest tests/test_app.py --app-platform ios --app-device UDID --app-package com.example.app
|
|
73
|
+
# 筛选、参数化和报告使用 pytest 自身的参数
|
|
74
|
+
pytest tests -k launch --app-platform android --app-device SERIAL --alluredir report
|
|
75
|
+
# 仅收集,不连接设备
|
|
76
|
+
pytest tests --collect-only --app-platform android
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`app_driver` 是函数级 fixture,每条用例建立一个设备驱动,用例结束后停止未结束的录屏并释放连接。普通测试无需配置设备。`app_config` fixture 返回当前的 `App` 配置。
|
|
80
|
+
|
|
81
|
+
也可以在业务项目的 `pytest.ini` 中设置默认参数:
|
|
82
|
+
|
|
83
|
+
```ini
|
|
84
|
+
[pytest]
|
|
85
|
+
app_platform = android
|
|
86
|
+
app_device = SERIAL
|
|
87
|
+
app_package = com.example.app
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
三个命令行参数分别覆盖对应的 INI 配置。平台配置在用例收集前生效,供页面元素选择对应定位方式。会话结束后恢复运行前的配置,同一进程连续运行不会留下本次参数。一次 pytest 会话对应一个平台和一台设备;多设备请分别启动进程。
|
|
91
|
+
|
|
92
|
+
如果主动设置了 `PYTEST_DISABLE_PLUGIN_AUTOLOAD=1`,需要显式加 `-p applib_ui.pytest_plugin`;使用报告参数时还需加载 Allure 插件。
|
|
93
|
+
|
|
94
|
+
## 页面对象与测试类
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from applib_ui import Elem, Page, TestCase
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class HomePage(Page):
|
|
101
|
+
login = Elem(
|
|
102
|
+
'登录',
|
|
103
|
+
android=('text', '登录'),
|
|
104
|
+
ios=('name', '登录'),
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class TestLogin(TestCase):
|
|
109
|
+
def start(self):
|
|
110
|
+
self.start_app()
|
|
111
|
+
|
|
112
|
+
def test_login(self):
|
|
113
|
+
HomePage(self.dr).login.click()
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
保留 `start_class` / `end_class`、`start` / `end` 扩展钩子,异常会交由 pytest 报告。驱动释放由 fixture 负责,`start` / `end` 失败时仍执行清理。新用例可以直接使用普通测试函数和 fixture,无需继承基类。
|
|
117
|
+
|
|
118
|
+
平台专用接口分别从 `applib_ui.core.android`、`applib_ui.core.ios` 导入。专用 `TestCase` 要求配置匹配的平台,并会在每条用例开始前解锁设备。Sonic 远程连接可通过业务 `conftest.py` 设置 `App.sonic_host`、`App.sonic_user`、`App.sonic_pwd`;iOS 远程连接还需要设置 `App.sib_path`。
|
|
119
|
+
|
|
120
|
+
## 其他入口
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
applib-ui --version
|
|
124
|
+
applib-ui run tests --app-platform android --app-device SERIAL --app-package com.example.app
|
|
125
|
+
applib-ui start-wda -u UDID
|
|
126
|
+
applib-ui stop-wda -u UDID
|
|
127
|
+
applib-ui restart-wda -u UDID
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`applib-ui run` 透传 pytest 参数并返回 pytest 退出码。程序调用方式:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from applib_ui import main
|
|
134
|
+
|
|
135
|
+
exit_code = main(
|
|
136
|
+
case_path='tests',
|
|
137
|
+
report_path='report', # 传 None 关闭此入口默认添加的 Allure 输出
|
|
138
|
+
did='SERIAL',
|
|
139
|
+
pkg='com.example.app',
|
|
140
|
+
platform='android',
|
|
141
|
+
pytest_args=['-k', 'login'],
|
|
142
|
+
)
|
|
143
|
+
raise SystemExit(exit_code)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## 配置与目录
|
|
147
|
+
|
|
148
|
+
- `applib_ui/core/android/`:Android 本地 / 远程驱动、元素及录屏。
|
|
149
|
+
- `applib_ui/core/ios/`:iOS 驱动、WDA、元素及录屏。
|
|
150
|
+
- `applib_ui/core/img/`、`applib_ui/core/ocr/`:图像及 OCR。
|
|
151
|
+
- `applib_ui/pytest_plugin.py`:pytest 参数、配置和 fixture。
|
|
152
|
+
- `applib_ui/running/`:程序入口及默认 YAML 配置。
|
|
153
|
+
- `applib_ui/utils/`:通用工具。
|
|
154
|
+
|
|
155
|
+
发布名和命令为 `applib-ui`,Python 导入名为 `applib_ui`,Android 目录和元素关键字统一为 `android`。升级业务用例时需同步修改导入和元素定义,不提供旧名称别名。
|
|
156
|
+
|
|
157
|
+
运行环境变量统一使用 `APPLIB_UI_` 前缀:`APPLIB_UI_WDA_TIMEOUT`、`APPLIB_UI_TUNNEL_PORT_BASE`、`APPLIB_UI_OCR_LANG`、`APPLIB_UI_OCR_FALLBACK_LANG`。iOS 端口注册、配对文件和日志使用 `~/.applib-ui/` 目录。
|
|
158
|
+
|
|
159
|
+
`order()`、`depend()` 仍分别依赖额外安装的 pytest-ordering、pytest-dependency;基础包不会自动安装这两个插件。数据参数化可直接用 `pytest.mark.parametrize` 或 `data()`。
|
|
160
|
+
|
|
161
|
+
## 开发验证
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
python -m pip install -e .
|
|
165
|
+
python -m pytest
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
仓库测试使用模拟驱动验证 pytest 接入、异常清理和退出码,不会连接设备。Android / iOS 真机行为需在具备上述设备环境的业务项目中验证。
|
|
169
|
+
|
|
170
|
+
本仓库未额外声明许可证。
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# applib-ui
|
|
2
|
+
|
|
3
|
+
基于 pytest 的 Android / iOS 自动化测试库,提供设备驱动、页面对象、双平台元素定位、截图、录屏和 Allure 报告支持。Python 要求为 3.10 及以上。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
在业务测试项目的虚拟环境中安装对应平台的依赖:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
python -m pip install 'applib-ui[android]'
|
|
11
|
+
# iOS
|
|
12
|
+
python -m pip install 'applib-ui[ios]'
|
|
13
|
+
# 双平台、图像识别及 OCR
|
|
14
|
+
python -m pip install 'applib-ui[android,ios,image,ocr]'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
基础包安装命令为 `python -m pip install applib-ui`,Python 导入名为 `applib_ui`。
|
|
18
|
+
|
|
19
|
+
如需从源码开发安装:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
python -m pip install -e '/path/to/applib-ui[android]'
|
|
23
|
+
# iOS
|
|
24
|
+
python -m pip install -e '/path/to/applib-ui[ios]'
|
|
25
|
+
# 双平台、图像识别及 OCR
|
|
26
|
+
python -m pip install -e '/path/to/applib-ui[android,ios,image,ocr]'
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
仅安装基础包 `python -m pip install -e /path/to/applib-ui` 即可使用 pytest 插件与通用工具。设备驱动和识别库按实际使用的功能安装。
|
|
30
|
+
|
|
31
|
+
Android 需要可用的 adb 及已授权设备;iOS 需要 macOS、已签名且可运行的 WDA,以及现有 WDA 启动流程所需的 tidevice / go-ios 工具。录屏还需要对应平台使用的 scrcpy / ffmpeg。
|
|
32
|
+
|
|
33
|
+
## 使用 pytest
|
|
34
|
+
|
|
35
|
+
安装后插件自动加载,无需在业务项目中编写 `pytest_plugins`。插件通过标准的 [pytest 插件入口](https://docs.pytest.org/en/stable/how-to/writing_plugins.html#making-your-plugin-installable-by-others)注册。
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
# tests/test_app.py
|
|
39
|
+
|
|
40
|
+
def test_launch(app_driver):
|
|
41
|
+
app_driver.start_app()
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pytest tests/test_app.py --app-platform android --app-device SERIAL --app-package com.example.app
|
|
46
|
+
pytest tests/test_app.py --app-platform ios --app-device UDID --app-package com.example.app
|
|
47
|
+
# 筛选、参数化和报告使用 pytest 自身的参数
|
|
48
|
+
pytest tests -k launch --app-platform android --app-device SERIAL --alluredir report
|
|
49
|
+
# 仅收集,不连接设备
|
|
50
|
+
pytest tests --collect-only --app-platform android
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`app_driver` 是函数级 fixture,每条用例建立一个设备驱动,用例结束后停止未结束的录屏并释放连接。普通测试无需配置设备。`app_config` fixture 返回当前的 `App` 配置。
|
|
54
|
+
|
|
55
|
+
也可以在业务项目的 `pytest.ini` 中设置默认参数:
|
|
56
|
+
|
|
57
|
+
```ini
|
|
58
|
+
[pytest]
|
|
59
|
+
app_platform = android
|
|
60
|
+
app_device = SERIAL
|
|
61
|
+
app_package = com.example.app
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
三个命令行参数分别覆盖对应的 INI 配置。平台配置在用例收集前生效,供页面元素选择对应定位方式。会话结束后恢复运行前的配置,同一进程连续运行不会留下本次参数。一次 pytest 会话对应一个平台和一台设备;多设备请分别启动进程。
|
|
65
|
+
|
|
66
|
+
如果主动设置了 `PYTEST_DISABLE_PLUGIN_AUTOLOAD=1`,需要显式加 `-p applib_ui.pytest_plugin`;使用报告参数时还需加载 Allure 插件。
|
|
67
|
+
|
|
68
|
+
## 页面对象与测试类
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
from applib_ui import Elem, Page, TestCase
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class HomePage(Page):
|
|
75
|
+
login = Elem(
|
|
76
|
+
'登录',
|
|
77
|
+
android=('text', '登录'),
|
|
78
|
+
ios=('name', '登录'),
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class TestLogin(TestCase):
|
|
83
|
+
def start(self):
|
|
84
|
+
self.start_app()
|
|
85
|
+
|
|
86
|
+
def test_login(self):
|
|
87
|
+
HomePage(self.dr).login.click()
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
保留 `start_class` / `end_class`、`start` / `end` 扩展钩子,异常会交由 pytest 报告。驱动释放由 fixture 负责,`start` / `end` 失败时仍执行清理。新用例可以直接使用普通测试函数和 fixture,无需继承基类。
|
|
91
|
+
|
|
92
|
+
平台专用接口分别从 `applib_ui.core.android`、`applib_ui.core.ios` 导入。专用 `TestCase` 要求配置匹配的平台,并会在每条用例开始前解锁设备。Sonic 远程连接可通过业务 `conftest.py` 设置 `App.sonic_host`、`App.sonic_user`、`App.sonic_pwd`;iOS 远程连接还需要设置 `App.sib_path`。
|
|
93
|
+
|
|
94
|
+
## 其他入口
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
applib-ui --version
|
|
98
|
+
applib-ui run tests --app-platform android --app-device SERIAL --app-package com.example.app
|
|
99
|
+
applib-ui start-wda -u UDID
|
|
100
|
+
applib-ui stop-wda -u UDID
|
|
101
|
+
applib-ui restart-wda -u UDID
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`applib-ui run` 透传 pytest 参数并返回 pytest 退出码。程序调用方式:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from applib_ui import main
|
|
108
|
+
|
|
109
|
+
exit_code = main(
|
|
110
|
+
case_path='tests',
|
|
111
|
+
report_path='report', # 传 None 关闭此入口默认添加的 Allure 输出
|
|
112
|
+
did='SERIAL',
|
|
113
|
+
pkg='com.example.app',
|
|
114
|
+
platform='android',
|
|
115
|
+
pytest_args=['-k', 'login'],
|
|
116
|
+
)
|
|
117
|
+
raise SystemExit(exit_code)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## 配置与目录
|
|
121
|
+
|
|
122
|
+
- `applib_ui/core/android/`:Android 本地 / 远程驱动、元素及录屏。
|
|
123
|
+
- `applib_ui/core/ios/`:iOS 驱动、WDA、元素及录屏。
|
|
124
|
+
- `applib_ui/core/img/`、`applib_ui/core/ocr/`:图像及 OCR。
|
|
125
|
+
- `applib_ui/pytest_plugin.py`:pytest 参数、配置和 fixture。
|
|
126
|
+
- `applib_ui/running/`:程序入口及默认 YAML 配置。
|
|
127
|
+
- `applib_ui/utils/`:通用工具。
|
|
128
|
+
|
|
129
|
+
发布名和命令为 `applib-ui`,Python 导入名为 `applib_ui`,Android 目录和元素关键字统一为 `android`。升级业务用例时需同步修改导入和元素定义,不提供旧名称别名。
|
|
130
|
+
|
|
131
|
+
运行环境变量统一使用 `APPLIB_UI_` 前缀:`APPLIB_UI_WDA_TIMEOUT`、`APPLIB_UI_TUNNEL_PORT_BASE`、`APPLIB_UI_OCR_LANG`、`APPLIB_UI_OCR_FALLBACK_LANG`。iOS 端口注册、配对文件和日志使用 `~/.applib-ui/` 目录。
|
|
132
|
+
|
|
133
|
+
`order()`、`depend()` 仍分别依赖额外安装的 pytest-ordering、pytest-dependency;基础包不会自动安装这两个插件。数据参数化可直接用 `pytest.mark.parametrize` 或 `data()`。
|
|
134
|
+
|
|
135
|
+
## 开发验证
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
python -m pip install -e .
|
|
139
|
+
python -m pytest
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
仓库测试使用模拟驱动验证 pytest 接入、异常清理和退出码,不会连接设备。Android / iOS 真机行为需在具备上述设备环境的业务项目中验证。
|
|
143
|
+
|
|
144
|
+
本仓库未额外声明许可证。
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .running.runner import main
|
|
2
|
+
from .running.conf import App
|
|
3
|
+
from .utils.config import FileConfig
|
|
4
|
+
from .utils.pytest_util import *
|
|
5
|
+
from .utils.allure_util import *
|
|
6
|
+
from .utils.log import logger
|
|
7
|
+
from .page import Page
|
|
8
|
+
from .element import Elem
|
|
9
|
+
from .case import TestCase
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
__version__ = "0.0.1"
|
|
13
|
+
__description__ = "Android / iOS 通用 UI 自动化测试框架"
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from .running.conf import App
|
|
6
|
+
from .utils.log import logger
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestCase:
|
|
10
|
+
"""pytest 测试基类;也可以直接在测试函数中使用 app_driver fixture。"""
|
|
11
|
+
|
|
12
|
+
dr = None
|
|
13
|
+
|
|
14
|
+
def start_class(self):
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
def end_class(self):
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
@classmethod
|
|
21
|
+
def setup_class(cls):
|
|
22
|
+
cls().start_class()
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def teardown_class(cls):
|
|
26
|
+
cls().end_class()
|
|
27
|
+
|
|
28
|
+
def start(self):
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
def end(self):
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
@pytest.fixture(autouse=True)
|
|
35
|
+
def _app_lifecycle(self, app_driver):
|
|
36
|
+
self.dr = app_driver
|
|
37
|
+
try:
|
|
38
|
+
if getattr(self, '_platform', None) is not None:
|
|
39
|
+
self.dr.unlock()
|
|
40
|
+
self.start()
|
|
41
|
+
yield
|
|
42
|
+
finally:
|
|
43
|
+
self.end()
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
def sleep(n: float):
|
|
47
|
+
logger.info(f"暂停: {n}s")
|
|
48
|
+
time.sleep(n)
|
|
49
|
+
|
|
50
|
+
def shot(self, name: str, delay=0):
|
|
51
|
+
if delay:
|
|
52
|
+
self.sleep(delay)
|
|
53
|
+
return self.dr.shot(name)
|
|
54
|
+
|
|
55
|
+
def start_record(self, video_name='record'):
|
|
56
|
+
"""开始录屏"""
|
|
57
|
+
return self.dr.start_record(video_name)
|
|
58
|
+
|
|
59
|
+
def stop_record(self):
|
|
60
|
+
"""结束录屏并上传 allure"""
|
|
61
|
+
return self.dr.stop_record()
|
|
62
|
+
|
|
63
|
+
def start_app(self, force=True, activity=None, use_monkey=False):
|
|
64
|
+
if force:
|
|
65
|
+
self.stop_app()
|
|
66
|
+
if App.platform == 'ios':
|
|
67
|
+
self.dr.start_app()
|
|
68
|
+
elif activity is not None:
|
|
69
|
+
self.dr.start_app(activity=activity)
|
|
70
|
+
elif use_monkey:
|
|
71
|
+
self.dr.start_app(use_monkey=True)
|
|
72
|
+
else:
|
|
73
|
+
self.dr.start_app()
|
|
74
|
+
|
|
75
|
+
def stop_app(self):
|
|
76
|
+
self.dr.stop_app()
|
|
77
|
+
|
|
78
|
+
def back(self):
|
|
79
|
+
self.dr.back()
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from . import __version__
|
|
3
|
+
|
|
4
|
+
@click.group()
|
|
5
|
+
@click.version_option(version=__version__, help="Show version.")
|
|
6
|
+
def main():
|
|
7
|
+
"""applib-ui 移动端自动化工具。"""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@main.command(context_settings={'ignore_unknown_options': True})
|
|
11
|
+
@click.argument('pytest_args', nargs=-1, type=click.UNPROCESSED)
|
|
12
|
+
def run(pytest_args):
|
|
13
|
+
"""运行 pytest,透传路径、筛选条件和报告等参数。"""
|
|
14
|
+
import pytest
|
|
15
|
+
|
|
16
|
+
raise SystemExit(pytest.main(list(pytest_args)))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@main.command()
|
|
20
|
+
@click.option('-u', '--udid', help="Specify the udid.")
|
|
21
|
+
def restart_wda(udid):
|
|
22
|
+
"""
|
|
23
|
+
重启wda
|
|
24
|
+
@param udid: IOS设备udid
|
|
25
|
+
@return:
|
|
26
|
+
"""
|
|
27
|
+
from .core.ios.local_wda import LocalWdaInit
|
|
28
|
+
|
|
29
|
+
LocalWdaInit(udid=udid).restart_wda()
|
|
30
|
+
|
|
31
|
+
@main.command()
|
|
32
|
+
@click.option('-u', '--udid', help="Specify the udid.")
|
|
33
|
+
def start_wda(udid):
|
|
34
|
+
"""
|
|
35
|
+
启动wda
|
|
36
|
+
@param udid: IOS设备udid
|
|
37
|
+
@return:
|
|
38
|
+
"""
|
|
39
|
+
from .core.ios.local_wda import LocalWdaInit
|
|
40
|
+
|
|
41
|
+
LocalWdaInit(udid=udid).run()
|
|
42
|
+
|
|
43
|
+
@main.command()
|
|
44
|
+
@click.option('-u', '--udid', help="Specify the udid.")
|
|
45
|
+
def stop_wda(udid):
|
|
46
|
+
"""
|
|
47
|
+
杀掉wda
|
|
48
|
+
@param udid: IOS设备udid
|
|
49
|
+
@return:
|
|
50
|
+
"""
|
|
51
|
+
from .core.ios.local_wda import LocalWdaInit
|
|
52
|
+
|
|
53
|
+
LocalWdaInit(udid=udid).kill_wda_server()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# @main.command()
|
|
57
|
+
# @click.option('-p', '--platform', help="Specify the platform.")
|
|
58
|
+
# @click.option('-u', '--url', help="Specify the url.")
|
|
59
|
+
# def inspector(platform, url):
|
|
60
|
+
# """
|
|
61
|
+
# 获取元素定位信息
|
|
62
|
+
# @param platform: 平台,android、ios、web
|
|
63
|
+
# @param url:针对web,需要录制的页面的url
|
|
64
|
+
# @return:
|
|
65
|
+
# """
|
|
66
|
+
# import os
|
|
67
|
+
# if platform in ['android', 'ios', 'web']:
|
|
68
|
+
# os.system('weditor')
|
|
69
|
+
# elif platform == 'web':
|
|
70
|
+
# if not url:
|
|
71
|
+
# raise KeyError('url不能为空')
|
|
72
|
+
# os.system(f'playwright codegen {url}')
|
|
73
|
+
# else:
|
|
74
|
+
# raise KeyError('只支持android、ios、web')
|
|
75
|
+
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""android 平台接口,按需加载驱动依赖。"""
|
|
2
|
+
|
|
3
|
+
from importlib import import_module
|
|
4
|
+
|
|
5
|
+
__all__ = ['Elem', 'Page', 'Driver', 'RemoteDriver', 'TestCase']
|
|
6
|
+
|
|
7
|
+
_MODULES = {'Elem': 'element', 'Page': 'page', 'Driver': 'driver',
|
|
8
|
+
'RemoteDriver': 'remote_driver', 'TestCase': 'case'}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def __getattr__(name):
|
|
12
|
+
if name not in _MODULES:
|
|
13
|
+
raise AttributeError(f'module {__name__!r} has no attribute {name!r}')
|
|
14
|
+
value = getattr(import_module(f'.{_MODULES[name]}', __name__), name)
|
|
15
|
+
globals()[name] = value
|
|
16
|
+
return value
|