# simlpe_automation_framework
### 介绍
simple_automation_framework(简称:SAF)
使用最简单的模式就可以实现需要功能和测试效果,也是xiaobaiauto2的简化版
SAF继承了selenium、requests/httpx、appium、loguru、xiaobaiauto2、飞书机器人、钉钉机器人、企业微信机器人(暂时不支持)、禅道提单API
### 软件架构
xiaobaiauto2的简化版
### 版本注意
建议使用Python 3.9.* 版本
建议selenium >=4.16.0 支持代码自动执行无需关注浏览器驱动问题,可以自行下载
防止某些库出现不兼容问题,导致功能不可使用
### 安装教程
```commandline
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple 注:将pip源修改为国内源
pip install xiaobaisaf
```
### 使用说明
- 优先修改saf/data/config.py中飞书/钉钉的webhook
```python
# filename=config.py
class feishu(object):
@staticmethod
def webhook():
return 'https://open.feishu.cn/open-apis/bot/v2/hook/xxxx'
class dingding(object):
@staticmethod
def webhook():
return 'https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxx'
```
- conftest.py(保持此文件与用例文件在同目录下)
```python
# filename = conftest.py
from saf.utils.SendMsgUtils import robotSendMessage
import pytest
@pytest.mark.hookwrapper
def pytest_runtest_makereport(item):
"""
:param item:
"""
outcome = yield
report = outcome.get_result()
if report.outcome == 'failed':
# 调用机器人发送执行结果
robotSendMessage(robot_name='feishu', msg=f'测试脚本:{report.nodeid.split("::")[0]}\n测试用例:{report.nodeid.split("::")[1]}\n测试结果:{report.outcome}')
# robotSendMessage(robot_name='dingding', msg=f'测试脚本:{report.nodeid.split("::")[0]}\n测试用例:{report.nodeid.split("::")[1]}\n测试结果:{report.outcome}')
# robotSendMessage(robot_name='feishu,dingding', msg=f'测试脚本:{report.nodeid.split("::")[0]}\n测试用例:{report.nodeid.split("::")[1]}\n测试结果:{report.outcome}')
```
- 用例文件
```python
# fielname = test_xiaobai_testcase.py
def setup_module():
''' 用例脚本执行之前需要准备的信息 '''
...
def teardown_module():
''' 用例脚本执行之后需要清除的信息 '''
def setup_function():
''' 初始化测试用例执行之前状态信息 '''
...
def teardown_function():
''' 清除测试用例执行之后所产生的信息 '''
...
def test_yewu_name_a():
''' 用例函数
需要针对业务场景的测试步骤的实现
1、UI测试就是定位需要操作的界面节点然后执行操作
2、API测试就是执行相关接口实现接口的功能
需要针对每次的结果添加断言进行判断处理
'''
def test_yewu_name_b():
''' 用例函数
需要针对业务场景的测试步骤的实现
1、UI测试就是定位需要操作的界面节点然后执行操作
2、API测试就是执行相关接口实现接口的功能
需要针对每次的结果添加断言进行判断处理
'''
```
```python
# filename = test_xiaobai_allure.py
# JDK与Allure已安装且配置好环境变量(若不知道可以查看公众号:小白科技之窗)
import pytest
import allure
@allure.feature('下单')
class Test_order():
@allure.story('登录')
def test_login(self):
''' 登录 '''
with allure.step('输入账户'):
assert True
with allure.step('输入密码'):
assert True
with allure.step('点击登录'):
assert True
@allure.story('搜索商品')
def test_search(self):
''' 搜索商品 '''
with allure.step('搜索框输入:苹果'):
assert True
with allure.step('点击搜索按钮'):
assert False
'''
# 执行脚本
pytest test_xiaobai_allure.py --alluredir=../data
# 打开报告
allure serve ../data
或者
allure generate -c -o ../report ../data
allure open ../report
'''
```
### saf>1.1 使用禅道API,测试失败自动提单
- 需要在禅道后台>>二次开发>>应用>>添加应用>>创建开启免密的应用
- 需要将上一步所生成数据【代号】与【密钥】写入到`saf/data/config.py`中zenTao相关的参数位置
```python
# filename = saf/data/config.py
import hashlib
import time
class zenTao(object):
'''
参考禅道接口文档:
https://www.zentao.net/book/zentaopmshelp/integration-287.html
'''
@staticmethod
def baseURL():
''' 禅道的根路径 '''
return 'http://192.168.0.240/zentao'
@staticmethod
def account():
''' 后台-》二次开发-》应用-》免密登录的账户名 '''
return '开启密钥的账户名称,例如管理员:admin'
@staticmethod
def getCode():
''' 后台-》二次开发-》应用-》创建-》代号 '''
return '复制生成应用的代号字符串'
@staticmethod
def getKey():
''' 后台-》二次开发-》应用-》创建-》密钥 '''
return '复制生成应用的密钥字符串'
@staticmethod
def getTime():
''' 获取时间戳 ,默认即可,无需修改'''
return int(time.time())
@staticmethod
def getToken():
''' 获取token: md5($code + $key + $time) ,默认即可,无需修改'''
_md5 = hashlib.md5(f'{zenTao.getCode()}{zenTao.getKey()}{zenTao.getTime()}'.encode('utf-8'))
return _md5.hexdigest()
```
- 用例同目录下创建`conftest.py`pytest的配置文件
```python
# filename = conftest.py
from saf.utils.BugUtils import addZenTaoBUG
import pytest
@pytest.mark.hookwrapper
def pytest_runtest_makereport(item, call):
"""
:param item:
"""
outcome = yield
report = outcome.get_result()
if report.outcome == 'failed':
doc = item.function.__doc__
doc = str(doc).replace('\n', '<br>')
addZenTaoBUG(title=item.function.__name__,
steps=f'{doc}预期结果:passed<br>测试结果:{report.outcome}')
```
- 用例文件正常编写,正常运行即可
### saf>1.0 拷贝web自动化模板到D:\autoProject目录下
```bat
xiaobaicmd -t web -d D:\autoProject
xiaobaicmd --template web --dirname D:\autoProject
xiaobaicmd -t api -d D:\autoProject
xiaobaicmd --template api --dirname D:\autoProject
xiaobaicmd -t app -d D:\autoProject[暂时不支持]
```
### saf>1.3 新增pytest参数多种样例,`web`中包含
```python
# filename = test_xiaobai_case_v2.py
import pytest
from saf import full_load
''' 参数化 '''
data2 = {
'test_login': {
'keys': 'username, password, _assert',
'values': [('xiaobai', '12345', 200), ('xiaohui', '1234567', 200)]
}
}
data3 = full_load(open('..\\data\\testCase.yaml', 'r').read())
# 内部数据
@pytest.mark.parametrize('username, password, _assert', [('xiaobai', '12345', 200), ('xiaohui', '1234567', 200)])
def test_xiaobai_login1(username, password, _assert):
# 业务实现代码
assert _assert == 200
# 外部数据
@pytest.mark.parametrize(data2['test_login']['keys'], data2['test_login']['values'])
def test_xiaobai_login2(username, password, _assert):
# 业务实现代码
assert _assert == 200
# 外部文件数据
@pytest.mark.parametrize(data3['test_login']['keys'], [eval(v) for v in data3['test_login']['values']])
def test_xiaobai_login3(username, password, _assert):
# 业务实现代码
assert _assert == 200
```
```yaml
# filename = ..\\data\\testCase.yaml
---
test_login:
keys: username,password,_assert
values:
- ('xiaobai', '12346', 200)
- ('xiaohui', '123456', 200)
```
### saf>1.8 工具会自动在当前目录下生成target文件夹,target目录内容与web模板保持一致,页面对象代码在PageObjects目录下
```cmd
xiaobaicmd -u https://www.baidu.com
xiaobaicmd --url https://www.baidu.com
```
### saf>1.9 基于adb实现监控Android设备中APP操作时实时生成XPath表达式及坐标等数据
```cmd
xiaobaicmd -m gui # 基于界面实时获取APP数据
xiaobaicmd --monitor gui # 基于界面实时获取APP数据
xiaobaicmd -m cli # 基于命令实时获取APP数据
xiaobaicmd --monitor cli # 基于命令实时获取APP数据
```
### saf>2.0 基础adb实现Android设备的界面监控功能
```cmd
xiaobaicmd -e [1] # 默认值为1,可省略;表示打开界面监控第一个设备的实时界面
xiaobaicmd --device 1
```
### saf>2.2 基础adb实现Android设备的电量监控功能
```cmd
xiaobaicmd -m power # 界面监控设备的电量与内存使用率的实时界面
xiaobaicmd -m memory # 界面监控设备的电量与内存使用率的实时界面
```
### saf>2.3.5 新增实时监控Android当前APP的CPU使用率及FPS数据
```cmd
xiaobaicmd -m gui
```
### saf>2.3.7 新增识别滑块验证码破解
```python
from saf.utils.CaptchaUtils import checkSlider
from saf import selenium_webdriver, By
driver = selenium_webdriver.Chrome()
driver.get("https://www.xiaobaisoftware.com")
# 其它操作...
# 定位目标图(小图)
target_element = driver.find_element(By.XPATH, value='')
# 定位背景图(大图)
background_element = driver.find_element(By.XPATH, value='')
# 定位滑块按钮
button_element = driver.find_element(By.XPATH, value='')
# 参数:浏览器驱动、目标元素、背景元素、滑块元素、失败重试(非必须,默认:False)、重试次数(非必须,默认:3)
checkSlider(driver, target_element, background_element, button_element, True, 5)
```
### saf>2.3.8 新增解析DNS并刷新DNS缓存,数据保存HOSTS
```cmd
# 执行脚本之前请修改系统hosts文件在当前用户下有可读可写的权限!
# windows的hosts文件路径:C:\Windows\System32\drivers\etc\hosts
# Mac OS的hosts文件路径 :/private/etc/hosts
# Linux的hosts文件路径 :/etc/hosts
# --domains 后面的域名使用逗号分离即可
xiaobaicmd --domains github.com,raw.githubusercontent.com,github.global.ssl.fastly.net,assets-cdn.github.com
```
### saf>=2.5.0 新增小白软件管理工具,目前支持(安装、卸载、替换不同版本的)JMeter
```cmd
# 命令行运行
xiaobaimanager
```
### saf>=2.5.2 新增文件查找工具
```cmd
# 命令行运行
xiaobaifinder
```
### saf>=3.0.0 全面升级安卓设备实时监控、生成代码、启动服务、运行脚本等(Python版)
```cmd
# 命令行运行
xiaobaidevice
```
### saf>=3.0.0 全面升级安卓设备实时监控、生成代码、启动服务、运行脚本等(二进制版)
```cmd
# 命令行运行
xiaobaidevice2
```
### saf>=3.0.0 监控粘贴板将浏览器 >> F12 >> copy >> fetch(Nodejs)转为requests代码
```cmd
# 命令行运行
xiaobaicmd -m cp
```
### saf>=3.0.2 替换api自动化项目模板(template)
```cmd
xiaobaicmd -t api -d D:/
```
### saf>=3.0.3 替换api自动化项目模板(template)
```cmd
# 导出模板
xiaobaicmd -t api -d D:/
# 打开UI界面,操作转化
convert_ui.bat
或者
convert_ui.sh
或者
python convert_ui.py
```
细节请查阅导出的模板项目中`README.md`文档内容
### 操作介绍
可以观看官方抖音(抖音号:xiaobaiTser)
### 环境检测[未实现]
```bat
xiaobaicmd --init
检测内容:
1、python版本及第三方库
2、第三方工具及环境
```
### 参与贡献
[selenium官网文档](https://www.selenium.dev/documentation/, "selenium官网文档")
[requests官网文档](https://requests.readthedocs.io/en/latest/, "requests官网文档")
[appium官网](http://appium.io/, "appium官网")
[loguru官方文档](https://loguru.readthedocs.io/en/stable/overview.html, "loguru官方文档")
[xiaobaiauto2帮助文档](https://pypi.org/project/xiaobaiauto2/, "xiaobaiauto2帮助文档")
[Allure帮助文档](https://docs.qameta.io/allure, "Allure帮助文档")
[飞书机器人获取WebHook](https://open.feishu.cn/document/ukTMukTMukTM/ucTM5YjL3ETO24yNxkjN?lang=zh-CN, "飞书机器人获取WebHook")
[钉钉机器人获取WebHook](https://open.dingtalk.com/document/group/custom-robot-access, "钉钉机器人获取WebHook")
[163邮箱配置](http://help.163.com/09/1223/14/5R7P3QI100753VB8.html, "163邮箱配置")
[QQ邮箱配置](https://service.mail.qq.com/cgi-bin/help?subtype=1&id=28&no=369, "QQ邮箱配置")
### 更新日志
| version | info |
|---------|-------------------------------------------------|
| 1.0 | 基本实现web自动化模板功能 |
| 1.1 | 修复已知BUG |
| 1.2 | 新增allure报告库及封装禅道提单接口 |
| 1.3 | 新增jira提单接口 |
| 1.4 | 新增pytest参数化样例 |
| 1.5 | 优化pytest样例内容 |
| 1.6 | 修复已知BUG |
| 1.7 | 新增基础环境检测功能 |
| 1.8 | 新增API自动化模板 |
| 1.9 | 新增xiaobaicmd -u命令 |
| 2.0 | 新增xiaobaicmd -m命令 |
| 2.1 | 新增xiaobaicmd --device命令 |
| 2.2 | 修复已知BUG |
| 2.3 | 新增实时监控Android设备耗电量 |
| 2.3.1 | 修复已知BUG |
| 2.3.2 | 修复已知BUG |
| 2.3.3 | 新增实时监控Android当前APP的内存使用率 |
| 2.3.4 | 新增xiaobaicmd -m gui效果展示 |
| 2.3.5 | 新增xiaobaicmd -u 转PO代码时xpath的表达式 |
| 2.3.6 | 新增实时监控Android当前APP的CPU使用率及FPS数据 |
| 2.3.7 | 新增识别滑块验证码破解 |
| 2.3.8 | 优化识别滑块验证码破解 |
| 2.3.9 | 新增解析DNS并刷新DNS缓存,数据保存HOSTS |
| 2.4 | 修复已知BUG |
| 2.4.1 | 优化DNS解析效果 |
| 2.4.2 | 优化自动生成代码 |
| 2.4.3 | 优化 |
| 2.4.3.1 | 优化 |
| 2.4.3.2 | 优化 |
| 2.5.0 | 添加xiaobaimanager命令 |
| 2.5.1 | 优化xiaobai |
| 2.5.1 | 新增xiaobaifinder |
| 3.0.0 | 新增xiaobaidevice、xiaobaidevice2、xiaobaicmd -m cp |
| 3.0.1 | xiaobaicmd -m cp 新增【Ctrl + S】 |
| 3.0.2 | xiaobaicmd -t api 替换模板 |
| 3.0.3 | xiaobaicmd -t api 新增界面操作替换模板 |
| 3.0.4 | 优化生成接口自动化模板 |
| 3.1.0 | 优化 |
| 3.1.1 | Fix |
| 3.1.2 | Fix |
| 3.1.6 | Fix |
| 3.1.7 | Fix |
| 3.1.8 | 修复`xiaobaidevice2`功能不可用 |
| 3.1.9 | 精简依赖库 |
Raw data
{
"_id": null,
"home_page": "https://gitee.com/xiaobaikeji/simlpe_automation_framework",
"name": "xiaobaisaf",
"maintainer": null,
"docs_url": null,
"requires_python": ">3.8",
"maintainer_email": null,
"keywords": "saf automation xiaobai xiaobaiauto2 test framework",
"author": "xiaobaiTser",
"author_email": "807447312@qq.com",
"download_url": null,
"platform": null,
"description": "# simlpe_automation_framework\r\n\r\n### \u4ecb\u7ecd\r\n simple_automation_framework(\u7b80\u79f0\uff1aSAF)\r\n \u4f7f\u7528\u6700\u7b80\u5355\u7684\u6a21\u5f0f\u5c31\u53ef\u4ee5\u5b9e\u73b0\u9700\u8981\u529f\u80fd\u548c\u6d4b\u8bd5\u6548\u679c\uff0c\u4e5f\u662fxiaobaiauto2\u7684\u7b80\u5316\u7248\r\n SAF\u7ee7\u627f\u4e86selenium\u3001requests/httpx\u3001appium\u3001loguru\u3001xiaobaiauto2\u3001\u98de\u4e66\u673a\u5668\u4eba\u3001\u9489\u9489\u673a\u5668\u4eba\u3001\u4f01\u4e1a\u5fae\u4fe1\u673a\u5668\u4eba\uff08\u6682\u65f6\u4e0d\u652f\u6301\uff09\u3001\u7985\u9053\u63d0\u5355API\r\n \r\n\r\n### \u8f6f\u4ef6\u67b6\u6784\r\n xiaobaiauto2\u7684\u7b80\u5316\u7248\r\n\r\n### \u7248\u672c\u6ce8\u610f\r\n \u5efa\u8bae\u4f7f\u7528Python 3.9.* \u7248\u672c\r\n \u5efa\u8baeselenium >=4.16.0 \u652f\u6301\u4ee3\u7801\u81ea\u52a8\u6267\u884c\u65e0\u9700\u5173\u6ce8\u6d4f\u89c8\u5668\u9a71\u52a8\u95ee\u9898\uff0c\u53ef\u4ee5\u81ea\u884c\u4e0b\u8f7d\r\n \u9632\u6b62\u67d0\u4e9b\u5e93\u51fa\u73b0\u4e0d\u517c\u5bb9\u95ee\u9898\uff0c\u5bfc\u81f4\u529f\u80fd\u4e0d\u53ef\u4f7f\u7528\r\n\r\n### \u5b89\u88c5\u6559\u7a0b\r\n```commandline\r\npip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple \u6ce8\uff1a\u5c06pip\u6e90\u4fee\u6539\u4e3a\u56fd\u5185\u6e90\r\npip install xiaobaisaf\r\n```\r\n\r\n### \u4f7f\u7528\u8bf4\u660e\r\n- \u4f18\u5148\u4fee\u6539saf/data/config.py\u4e2d\u98de\u4e66/\u9489\u9489\u7684webhook\r\n```python\r\n# filename=config.py\r\n\r\nclass feishu(object):\r\n @staticmethod\r\n def webhook():\r\n return 'https://open.feishu.cn/open-apis/bot/v2/hook/xxxx'\r\n\r\nclass dingding(object):\r\n @staticmethod\r\n def webhook():\r\n return 'https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxx'\r\n```\r\n\r\n- conftest.py\uff08\u4fdd\u6301\u6b64\u6587\u4ef6\u4e0e\u7528\u4f8b\u6587\u4ef6\u5728\u540c\u76ee\u5f55\u4e0b\uff09\r\n```python\r\n# filename = conftest.py\r\nfrom saf.utils.SendMsgUtils import robotSendMessage\r\nimport pytest\r\n\r\n@pytest.mark.hookwrapper\r\ndef pytest_runtest_makereport(item):\r\n \"\"\"\r\n :param item:\r\n \"\"\"\r\n outcome = yield\r\n report = outcome.get_result()\r\n if report.outcome == 'failed':\r\n # \u8c03\u7528\u673a\u5668\u4eba\u53d1\u9001\u6267\u884c\u7ed3\u679c\r\n robotSendMessage(robot_name='feishu', msg=f'\u6d4b\u8bd5\u811a\u672c\uff1a{report.nodeid.split(\"::\")[0]}\\n\u6d4b\u8bd5\u7528\u4f8b\uff1a{report.nodeid.split(\"::\")[1]}\\n\u6d4b\u8bd5\u7ed3\u679c\uff1a{report.outcome}')\r\n # robotSendMessage(robot_name='dingding', msg=f'\u6d4b\u8bd5\u811a\u672c\uff1a{report.nodeid.split(\"::\")[0]}\\n\u6d4b\u8bd5\u7528\u4f8b\uff1a{report.nodeid.split(\"::\")[1]}\\n\u6d4b\u8bd5\u7ed3\u679c\uff1a{report.outcome}')\r\n # robotSendMessage(robot_name='feishu,dingding', msg=f'\u6d4b\u8bd5\u811a\u672c\uff1a{report.nodeid.split(\"::\")[0]}\\n\u6d4b\u8bd5\u7528\u4f8b\uff1a{report.nodeid.split(\"::\")[1]}\\n\u6d4b\u8bd5\u7ed3\u679c\uff1a{report.outcome}')\r\n```\r\n\r\n- \u7528\u4f8b\u6587\u4ef6\r\n```python\r\n# fielname = test_xiaobai_testcase.py\r\n\r\ndef setup_module():\r\n ''' \u7528\u4f8b\u811a\u672c\u6267\u884c\u4e4b\u524d\u9700\u8981\u51c6\u5907\u7684\u4fe1\u606f '''\r\n ...\r\n\r\ndef teardown_module():\r\n ''' \u7528\u4f8b\u811a\u672c\u6267\u884c\u4e4b\u540e\u9700\u8981\u6e05\u9664\u7684\u4fe1\u606f '''\r\n\r\ndef setup_function():\r\n ''' \u521d\u59cb\u5316\u6d4b\u8bd5\u7528\u4f8b\u6267\u884c\u4e4b\u524d\u72b6\u6001\u4fe1\u606f '''\r\n ...\r\n\r\ndef teardown_function():\r\n ''' \u6e05\u9664\u6d4b\u8bd5\u7528\u4f8b\u6267\u884c\u4e4b\u540e\u6240\u4ea7\u751f\u7684\u4fe1\u606f '''\r\n ...\r\n\r\ndef test_yewu_name_a():\r\n ''' \u7528\u4f8b\u51fd\u6570\r\n \u9700\u8981\u9488\u5bf9\u4e1a\u52a1\u573a\u666f\u7684\u6d4b\u8bd5\u6b65\u9aa4\u7684\u5b9e\u73b0\r\n 1\u3001UI\u6d4b\u8bd5\u5c31\u662f\u5b9a\u4f4d\u9700\u8981\u64cd\u4f5c\u7684\u754c\u9762\u8282\u70b9\u7136\u540e\u6267\u884c\u64cd\u4f5c\r\n 2\u3001API\u6d4b\u8bd5\u5c31\u662f\u6267\u884c\u76f8\u5173\u63a5\u53e3\u5b9e\u73b0\u63a5\u53e3\u7684\u529f\u80fd\r\n \u9700\u8981\u9488\u5bf9\u6bcf\u6b21\u7684\u7ed3\u679c\u6dfb\u52a0\u65ad\u8a00\u8fdb\u884c\u5224\u65ad\u5904\u7406\r\n '''\r\n\r\ndef test_yewu_name_b():\r\n ''' \u7528\u4f8b\u51fd\u6570\r\n \u9700\u8981\u9488\u5bf9\u4e1a\u52a1\u573a\u666f\u7684\u6d4b\u8bd5\u6b65\u9aa4\u7684\u5b9e\u73b0\r\n 1\u3001UI\u6d4b\u8bd5\u5c31\u662f\u5b9a\u4f4d\u9700\u8981\u64cd\u4f5c\u7684\u754c\u9762\u8282\u70b9\u7136\u540e\u6267\u884c\u64cd\u4f5c\r\n 2\u3001API\u6d4b\u8bd5\u5c31\u662f\u6267\u884c\u76f8\u5173\u63a5\u53e3\u5b9e\u73b0\u63a5\u53e3\u7684\u529f\u80fd\r\n \u9700\u8981\u9488\u5bf9\u6bcf\u6b21\u7684\u7ed3\u679c\u6dfb\u52a0\u65ad\u8a00\u8fdb\u884c\u5224\u65ad\u5904\u7406\r\n '''\r\n```\r\n\r\n```python\r\n# filename = test_xiaobai_allure.py\r\n# JDK\u4e0eAllure\u5df2\u5b89\u88c5\u4e14\u914d\u7f6e\u597d\u73af\u5883\u53d8\u91cf\uff08\u82e5\u4e0d\u77e5\u9053\u53ef\u4ee5\u67e5\u770b\u516c\u4f17\u53f7\uff1a\u5c0f\u767d\u79d1\u6280\u4e4b\u7a97\uff09\r\nimport pytest\r\nimport allure\r\n\r\n@allure.feature('\u4e0b\u5355')\r\nclass Test_order():\r\n @allure.story('\u767b\u5f55')\r\n def test_login(self):\r\n ''' \u767b\u5f55 '''\r\n with allure.step('\u8f93\u5165\u8d26\u6237'):\r\n assert True\r\n with allure.step('\u8f93\u5165\u5bc6\u7801'):\r\n assert True\r\n with allure.step('\u70b9\u51fb\u767b\u5f55'):\r\n assert True\r\n\r\n @allure.story('\u641c\u7d22\u5546\u54c1')\r\n def test_search(self):\r\n ''' \u641c\u7d22\u5546\u54c1 '''\r\n with allure.step('\u641c\u7d22\u6846\u8f93\u5165\uff1a\u82f9\u679c'):\r\n assert True\r\n with allure.step('\u70b9\u51fb\u641c\u7d22\u6309\u94ae'):\r\n assert False\r\n'''\r\n# \u6267\u884c\u811a\u672c\r\npytest test_xiaobai_allure.py --alluredir=../data\r\n\r\n# \u6253\u5f00\u62a5\u544a\r\nallure serve ../data\r\n\u6216\u8005\r\nallure generate -c -o ../report ../data\r\nallure open ../report\r\n'''\r\n```\r\n\r\n### saf>1.1 \u4f7f\u7528\u7985\u9053API\uff0c\u6d4b\u8bd5\u5931\u8d25\u81ea\u52a8\u63d0\u5355\r\n- \u9700\u8981\u5728\u7985\u9053\u540e\u53f0>>\u4e8c\u6b21\u5f00\u53d1>>\u5e94\u7528>>\u6dfb\u52a0\u5e94\u7528>>\u521b\u5efa\u5f00\u542f\u514d\u5bc6\u7684\u5e94\u7528 \r\n- \u9700\u8981\u5c06\u4e0a\u4e00\u6b65\u6240\u751f\u6210\u6570\u636e\u3010\u4ee3\u53f7\u3011\u4e0e\u3010\u5bc6\u94a5\u3011\u5199\u5165\u5230`saf/data/config.py`\u4e2dzenTao\u76f8\u5173\u7684\u53c2\u6570\u4f4d\u7f6e\r\n\r\n```python\r\n# filename = saf/data/config.py\r\n\r\nimport hashlib\r\nimport time\r\nclass zenTao(object):\r\n '''\r\n \u53c2\u8003\u7985\u9053\u63a5\u53e3\u6587\u6863\uff1a\r\n https://www.zentao.net/book/zentaopmshelp/integration-287.html\r\n '''\r\n @staticmethod\r\n def baseURL():\r\n ''' \u7985\u9053\u7684\u6839\u8def\u5f84 '''\r\n return 'http://192.168.0.240/zentao'\r\n \r\n @staticmethod\r\n def account():\r\n ''' \u540e\u53f0-\u300b\u4e8c\u6b21\u5f00\u53d1-\u300b\u5e94\u7528-\u300b\u514d\u5bc6\u767b\u5f55\u7684\u8d26\u6237\u540d '''\r\n return '\u5f00\u542f\u5bc6\u94a5\u7684\u8d26\u6237\u540d\u79f0\uff0c\u4f8b\u5982\u7ba1\u7406\u5458\uff1aadmin'\r\n \r\n @staticmethod\r\n def getCode():\r\n ''' \u540e\u53f0-\u300b\u4e8c\u6b21\u5f00\u53d1-\u300b\u5e94\u7528-\u300b\u521b\u5efa-\u300b\u4ee3\u53f7 '''\r\n return '\u590d\u5236\u751f\u6210\u5e94\u7528\u7684\u4ee3\u53f7\u5b57\u7b26\u4e32'\r\n \r\n @staticmethod\r\n def getKey():\r\n ''' \u540e\u53f0-\u300b\u4e8c\u6b21\u5f00\u53d1-\u300b\u5e94\u7528-\u300b\u521b\u5efa-\u300b\u5bc6\u94a5 '''\r\n return '\u590d\u5236\u751f\u6210\u5e94\u7528\u7684\u5bc6\u94a5\u5b57\u7b26\u4e32'\r\n \r\n @staticmethod\r\n def getTime():\r\n ''' \u83b7\u53d6\u65f6\u95f4\u6233 \uff0c\u9ed8\u8ba4\u5373\u53ef\uff0c\u65e0\u9700\u4fee\u6539'''\r\n return int(time.time())\r\n \r\n @staticmethod\r\n def getToken():\r\n ''' \u83b7\u53d6token\uff1a md5($code + $key + $time) \uff0c\u9ed8\u8ba4\u5373\u53ef\uff0c\u65e0\u9700\u4fee\u6539'''\r\n _md5 = hashlib.md5(f'{zenTao.getCode()}{zenTao.getKey()}{zenTao.getTime()}'.encode('utf-8'))\r\n return _md5.hexdigest()\r\n```\r\n\r\n- \u7528\u4f8b\u540c\u76ee\u5f55\u4e0b\u521b\u5efa`conftest.py`pytest\u7684\u914d\u7f6e\u6587\u4ef6\r\n```python\r\n# filename = conftest.py\r\n\r\nfrom saf.utils.BugUtils import addZenTaoBUG\r\nimport pytest\r\n \r\n@pytest.mark.hookwrapper\r\ndef pytest_runtest_makereport(item, call):\r\n \"\"\"\r\n :param item:\r\n \"\"\"\r\n outcome = yield\r\n report = outcome.get_result()\r\n if report.outcome == 'failed':\r\n doc = item.function.__doc__\r\n doc = str(doc).replace('\\n', '<br>')\r\n addZenTaoBUG(title=item.function.__name__,\r\n steps=f'{doc}\u9884\u671f\u7ed3\u679c\uff1apassed<br>\u6d4b\u8bd5\u7ed3\u679c\uff1a{report.outcome}')\r\n \r\n ```\r\n- \u7528\u4f8b\u6587\u4ef6\u6b63\u5e38\u7f16\u5199\uff0c\u6b63\u5e38\u8fd0\u884c\u5373\u53ef\r\n\r\n### saf>1.0 \u62f7\u8d1dweb\u81ea\u52a8\u5316\u6a21\u677f\u5230D:\\autoProject\u76ee\u5f55\u4e0b\r\n```bat\r\nxiaobaicmd -t web -d D:\\autoProject\r\nxiaobaicmd --template web --dirname D:\\autoProject\r\nxiaobaicmd -t api -d D:\\autoProject\r\nxiaobaicmd --template api --dirname D:\\autoProject\r\nxiaobaicmd -t app -d D:\\autoProject[\u6682\u65f6\u4e0d\u652f\u6301]\r\n```\r\n\r\n### saf>1.3 \u65b0\u589epytest\u53c2\u6570\u591a\u79cd\u6837\u4f8b\uff0c`web`\u4e2d\u5305\u542b\r\n```python\r\n# filename = test_xiaobai_case_v2.py\r\nimport pytest\r\nfrom saf import full_load\r\n\r\n''' \u53c2\u6570\u5316 '''\r\n\r\ndata2 = {\r\n 'test_login': {\r\n 'keys': 'username, password, _assert',\r\n 'values': [('xiaobai', '12345', 200), ('xiaohui', '1234567', 200)]\r\n }\r\n}\r\n\r\ndata3 = full_load(open('..\\\\data\\\\testCase.yaml', 'r').read())\r\n\r\n# \u5185\u90e8\u6570\u636e\r\n@pytest.mark.parametrize('username, password, _assert', [('xiaobai', '12345', 200), ('xiaohui', '1234567', 200)])\r\ndef test_xiaobai_login1(username, password, _assert):\r\n # \u4e1a\u52a1\u5b9e\u73b0\u4ee3\u7801\r\n assert _assert == 200\r\n\r\n# \u5916\u90e8\u6570\u636e\r\n@pytest.mark.parametrize(data2['test_login']['keys'], data2['test_login']['values'])\r\ndef test_xiaobai_login2(username, password, _assert):\r\n # \u4e1a\u52a1\u5b9e\u73b0\u4ee3\u7801\r\n assert _assert == 200\r\n\r\n\r\n# \u5916\u90e8\u6587\u4ef6\u6570\u636e\r\n@pytest.mark.parametrize(data3['test_login']['keys'], [eval(v) for v in data3['test_login']['values']])\r\ndef test_xiaobai_login3(username, password, _assert):\r\n # \u4e1a\u52a1\u5b9e\u73b0\u4ee3\u7801\r\n assert _assert == 200\r\n```\r\n```yaml\r\n# filename = ..\\\\data\\\\testCase.yaml\r\n---\r\ntest_login:\r\n keys: username,password,_assert\r\n values:\r\n - ('xiaobai', '12346', 200)\r\n - ('xiaohui', '123456', 200)\r\n```\r\n\r\n### saf>1.8 \u5de5\u5177\u4f1a\u81ea\u52a8\u5728\u5f53\u524d\u76ee\u5f55\u4e0b\u751f\u6210target\u6587\u4ef6\u5939\uff0ctarget\u76ee\u5f55\u5185\u5bb9\u4e0eweb\u6a21\u677f\u4fdd\u6301\u4e00\u81f4\uff0c\u9875\u9762\u5bf9\u8c61\u4ee3\u7801\u5728PageObjects\u76ee\u5f55\u4e0b\r\n```cmd\r\nxiaobaicmd -u https://www.baidu.com \r\nxiaobaicmd --url https://www.baidu.com \r\n```\r\n\r\n### saf>1.9 \u57fa\u4e8eadb\u5b9e\u73b0\u76d1\u63a7Android\u8bbe\u5907\u4e2dAPP\u64cd\u4f5c\u65f6\u5b9e\u65f6\u751f\u6210XPath\u8868\u8fbe\u5f0f\u53ca\u5750\u6807\u7b49\u6570\u636e\r\n```cmd\r\nxiaobaicmd -m gui # \u57fa\u4e8e\u754c\u9762\u5b9e\u65f6\u83b7\u53d6APP\u6570\u636e\r\nxiaobaicmd --monitor gui # \u57fa\u4e8e\u754c\u9762\u5b9e\u65f6\u83b7\u53d6APP\u6570\u636e\r\nxiaobaicmd -m cli # \u57fa\u4e8e\u547d\u4ee4\u5b9e\u65f6\u83b7\u53d6APP\u6570\u636e\r\nxiaobaicmd --monitor cli # \u57fa\u4e8e\u547d\u4ee4\u5b9e\u65f6\u83b7\u53d6APP\u6570\u636e\r\n```\r\n\r\n### saf>2.0 \u57fa\u7840adb\u5b9e\u73b0Android\u8bbe\u5907\u7684\u754c\u9762\u76d1\u63a7\u529f\u80fd\r\n```cmd\r\nxiaobaicmd -e [1] # \u9ed8\u8ba4\u503c\u4e3a1\uff0c\u53ef\u7701\u7565\uff1b\u8868\u793a\u6253\u5f00\u754c\u9762\u76d1\u63a7\u7b2c\u4e00\u4e2a\u8bbe\u5907\u7684\u5b9e\u65f6\u754c\u9762\r\nxiaobaicmd --device 1\r\n```\r\n\r\n### saf>2.2 \u57fa\u7840adb\u5b9e\u73b0Android\u8bbe\u5907\u7684\u7535\u91cf\u76d1\u63a7\u529f\u80fd\r\n```cmd\r\nxiaobaicmd -m power # \u754c\u9762\u76d1\u63a7\u8bbe\u5907\u7684\u7535\u91cf\u4e0e\u5185\u5b58\u4f7f\u7528\u7387\u7684\u5b9e\u65f6\u754c\u9762\r\nxiaobaicmd -m memory # \u754c\u9762\u76d1\u63a7\u8bbe\u5907\u7684\u7535\u91cf\u4e0e\u5185\u5b58\u4f7f\u7528\u7387\u7684\u5b9e\u65f6\u754c\u9762\r\n```\r\n\r\n### saf>2.3.5 \u65b0\u589e\u5b9e\u65f6\u76d1\u63a7Android\u5f53\u524dAPP\u7684CPU\u4f7f\u7528\u7387\u53caFPS\u6570\u636e\r\n```cmd\r\nxiaobaicmd -m gui\r\n```\r\n\r\n### saf>2.3.7 \u65b0\u589e\u8bc6\u522b\u6ed1\u5757\u9a8c\u8bc1\u7801\u7834\u89e3\r\n```python\r\nfrom saf.utils.CaptchaUtils import checkSlider\r\nfrom saf import selenium_webdriver, By\r\n\r\ndriver = selenium_webdriver.Chrome()\r\n\r\ndriver.get(\"https://www.xiaobaisoftware.com\")\r\n\r\n# \u5176\u5b83\u64cd\u4f5c...\r\n\r\n# \u5b9a\u4f4d\u76ee\u6807\u56fe\uff08\u5c0f\u56fe\uff09\r\ntarget_element = driver.find_element(By.XPATH, value='')\r\n\r\n# \u5b9a\u4f4d\u80cc\u666f\u56fe\uff08\u5927\u56fe\uff09\r\nbackground_element = driver.find_element(By.XPATH, value='')\r\n\r\n# \u5b9a\u4f4d\u6ed1\u5757\u6309\u94ae\r\nbutton_element = driver.find_element(By.XPATH, value='')\r\n\r\n# \u53c2\u6570\uff1a\u6d4f\u89c8\u5668\u9a71\u52a8\u3001\u76ee\u6807\u5143\u7d20\u3001\u80cc\u666f\u5143\u7d20\u3001\u6ed1\u5757\u5143\u7d20\u3001\u5931\u8d25\u91cd\u8bd5\uff08\u975e\u5fc5\u987b\uff0c\u9ed8\u8ba4\uff1aFalse\uff09\u3001\u91cd\u8bd5\u6b21\u6570\uff08\u975e\u5fc5\u987b\uff0c\u9ed8\u8ba4\uff1a3\uff09\r\ncheckSlider(driver, target_element, background_element, button_element, True, 5)\r\n```\r\n\r\n### saf>2.3.8 \u65b0\u589e\u89e3\u6790DNS\u5e76\u5237\u65b0DNS\u7f13\u5b58\uff0c\u6570\u636e\u4fdd\u5b58HOSTS\r\n```cmd\r\n# \u6267\u884c\u811a\u672c\u4e4b\u524d\u8bf7\u4fee\u6539\u7cfb\u7edfhosts\u6587\u4ef6\u5728\u5f53\u524d\u7528\u6237\u4e0b\u6709\u53ef\u8bfb\u53ef\u5199\u7684\u6743\u9650\uff01\r\n# windows\u7684hosts\u6587\u4ef6\u8def\u5f84\uff1aC:\\Windows\\System32\\drivers\\etc\\hosts\r\n# Mac OS\u7684hosts\u6587\u4ef6\u8def\u5f84 \uff1a/private/etc/hosts\r\n# Linux\u7684hosts\u6587\u4ef6\u8def\u5f84 \uff1a/etc/hosts\r\n\r\n# --domains \u540e\u9762\u7684\u57df\u540d\u4f7f\u7528\u9017\u53f7\u5206\u79bb\u5373\u53ef\r\nxiaobaicmd --domains github.com,raw.githubusercontent.com,github.global.ssl.fastly.net,assets-cdn.github.com\r\n```\r\n\r\n### saf>=2.5.0 \u65b0\u589e\u5c0f\u767d\u8f6f\u4ef6\u7ba1\u7406\u5de5\u5177\uff0c\u76ee\u524d\u652f\u6301\uff08\u5b89\u88c5\u3001\u5378\u8f7d\u3001\u66ff\u6362\u4e0d\u540c\u7248\u672c\u7684\uff09JMeter\r\n```cmd\r\n# \u547d\u4ee4\u884c\u8fd0\u884c\r\nxiaobaimanager\r\n```\r\n\r\n### saf>=2.5.2 \u65b0\u589e\u6587\u4ef6\u67e5\u627e\u5de5\u5177\r\n```cmd\r\n# \u547d\u4ee4\u884c\u8fd0\u884c\r\nxiaobaifinder\r\n```\r\n### saf>=3.0.0 \u5168\u9762\u5347\u7ea7\u5b89\u5353\u8bbe\u5907\u5b9e\u65f6\u76d1\u63a7\u3001\u751f\u6210\u4ee3\u7801\u3001\u542f\u52a8\u670d\u52a1\u3001\u8fd0\u884c\u811a\u672c\u7b49\uff08Python\u7248\uff09\r\n```cmd\r\n# \u547d\u4ee4\u884c\u8fd0\u884c\r\nxiaobaidevice\r\n```\r\n### saf>=3.0.0 \u5168\u9762\u5347\u7ea7\u5b89\u5353\u8bbe\u5907\u5b9e\u65f6\u76d1\u63a7\u3001\u751f\u6210\u4ee3\u7801\u3001\u542f\u52a8\u670d\u52a1\u3001\u8fd0\u884c\u811a\u672c\u7b49\uff08\u4e8c\u8fdb\u5236\u7248\uff09\r\n```cmd\r\n# \u547d\u4ee4\u884c\u8fd0\u884c\r\nxiaobaidevice2\r\n```\r\n\r\n### saf>=3.0.0 \u76d1\u63a7\u7c98\u8d34\u677f\u5c06\u6d4f\u89c8\u5668 >> F12 >> copy >> fetch(Nodejs)\u8f6c\u4e3arequests\u4ee3\u7801\r\n```cmd\r\n# \u547d\u4ee4\u884c\u8fd0\u884c\r\nxiaobaicmd -m cp\r\n```\r\n\r\n### saf>=3.0.2 \u66ff\u6362api\u81ea\u52a8\u5316\u9879\u76ee\u6a21\u677f(template)\r\n```cmd\r\nxiaobaicmd -t api -d D:/\r\n```\r\n\r\n### saf>=3.0.3 \u66ff\u6362api\u81ea\u52a8\u5316\u9879\u76ee\u6a21\u677f(template)\r\n```cmd\r\n# \u5bfc\u51fa\u6a21\u677f\r\nxiaobaicmd -t api -d D:/\r\n\r\n# \u6253\u5f00UI\u754c\u9762\uff0c\u64cd\u4f5c\u8f6c\u5316\r\nconvert_ui.bat\r\n\u6216\u8005\r\nconvert_ui.sh\r\n\u6216\u8005\r\npython convert_ui.py\r\n```\r\n\u7ec6\u8282\u8bf7\u67e5\u9605\u5bfc\u51fa\u7684\u6a21\u677f\u9879\u76ee\u4e2d`README.md`\u6587\u6863\u5185\u5bb9\r\n\r\n### \u64cd\u4f5c\u4ecb\u7ecd\r\n\u53ef\u4ee5\u89c2\u770b\u5b98\u65b9\u6296\u97f3\uff08\u6296\u97f3\u53f7\uff1axiaobaiTser\uff09\r\n\r\n### \u73af\u5883\u68c0\u6d4b[\u672a\u5b9e\u73b0]\r\n```bat\r\nxiaobaicmd --init\r\n\r\n\u68c0\u6d4b\u5185\u5bb9: \r\n1\u3001python\u7248\u672c\u53ca\u7b2c\u4e09\u65b9\u5e93\r\n2\u3001\u7b2c\u4e09\u65b9\u5de5\u5177\u53ca\u73af\u5883\r\n```\r\n\r\n### \u53c2\u4e0e\u8d21\u732e\r\n[selenium\u5b98\u7f51\u6587\u6863](https://www.selenium.dev/documentation/, \"selenium\u5b98\u7f51\u6587\u6863\")\r\n\r\n[requests\u5b98\u7f51\u6587\u6863](https://requests.readthedocs.io/en/latest/, \"requests\u5b98\u7f51\u6587\u6863\")\r\n\r\n[appium\u5b98\u7f51](http://appium.io/, \"appium\u5b98\u7f51\")\r\n\r\n[loguru\u5b98\u65b9\u6587\u6863](https://loguru.readthedocs.io/en/stable/overview.html, \"loguru\u5b98\u65b9\u6587\u6863\")\r\n\r\n[xiaobaiauto2\u5e2e\u52a9\u6587\u6863](https://pypi.org/project/xiaobaiauto2/, \"xiaobaiauto2\u5e2e\u52a9\u6587\u6863\")\r\n\r\n[Allure\u5e2e\u52a9\u6587\u6863](https://docs.qameta.io/allure, \"Allure\u5e2e\u52a9\u6587\u6863\")\r\n\r\n[\u98de\u4e66\u673a\u5668\u4eba\u83b7\u53d6WebHook](https://open.feishu.cn/document/ukTMukTMukTM/ucTM5YjL3ETO24yNxkjN?lang=zh-CN, \"\u98de\u4e66\u673a\u5668\u4eba\u83b7\u53d6WebHook\")\r\n\r\n[\u9489\u9489\u673a\u5668\u4eba\u83b7\u53d6WebHook](https://open.dingtalk.com/document/group/custom-robot-access, \"\u9489\u9489\u673a\u5668\u4eba\u83b7\u53d6WebHook\")\r\n\r\n[163\u90ae\u7bb1\u914d\u7f6e](http://help.163.com/09/1223/14/5R7P3QI100753VB8.html, \"163\u90ae\u7bb1\u914d\u7f6e\")\r\n\r\n[QQ\u90ae\u7bb1\u914d\u7f6e](https://service.mail.qq.com/cgi-bin/help?subtype=1&id=28&no=369, \"QQ\u90ae\u7bb1\u914d\u7f6e\")\r\n\r\n### \u66f4\u65b0\u65e5\u5fd7\r\n\r\n| version | info |\r\n|---------|-------------------------------------------------|\r\n| 1.0 | \u57fa\u672c\u5b9e\u73b0web\u81ea\u52a8\u5316\u6a21\u677f\u529f\u80fd |\r\n| 1.1 | \u4fee\u590d\u5df2\u77e5BUG |\r\n| 1.2 | \u65b0\u589eallure\u62a5\u544a\u5e93\u53ca\u5c01\u88c5\u7985\u9053\u63d0\u5355\u63a5\u53e3 |\r\n| 1.3 | \u65b0\u589ejira\u63d0\u5355\u63a5\u53e3 |\r\n| 1.4 | \u65b0\u589epytest\u53c2\u6570\u5316\u6837\u4f8b |\r\n| 1.5 | \u4f18\u5316pytest\u6837\u4f8b\u5185\u5bb9 |\r\n| 1.6 | \u4fee\u590d\u5df2\u77e5BUG |\r\n| 1.7 | \u65b0\u589e\u57fa\u7840\u73af\u5883\u68c0\u6d4b\u529f\u80fd |\r\n| 1.8 | \u65b0\u589eAPI\u81ea\u52a8\u5316\u6a21\u677f |\r\n| 1.9 | \u65b0\u589exiaobaicmd -u\u547d\u4ee4 |\r\n| 2.0 | \u65b0\u589exiaobaicmd -m\u547d\u4ee4 |\r\n| 2.1 | \u65b0\u589exiaobaicmd --device\u547d\u4ee4 |\r\n| 2.2 | \u4fee\u590d\u5df2\u77e5BUG |\r\n| 2.3 | \u65b0\u589e\u5b9e\u65f6\u76d1\u63a7Android\u8bbe\u5907\u8017\u7535\u91cf |\r\n| 2.3.1 | \u4fee\u590d\u5df2\u77e5BUG |\r\n| 2.3.2 | \u4fee\u590d\u5df2\u77e5BUG |\r\n| 2.3.3 | \u65b0\u589e\u5b9e\u65f6\u76d1\u63a7Android\u5f53\u524dAPP\u7684\u5185\u5b58\u4f7f\u7528\u7387 |\r\n| 2.3.4 | \u65b0\u589exiaobaicmd -m gui\u6548\u679c\u5c55\u793a |\r\n| 2.3.5 | \u65b0\u589exiaobaicmd -u \u8f6cPO\u4ee3\u7801\u65f6xpath\u7684\u8868\u8fbe\u5f0f |\r\n| 2.3.6 | \u65b0\u589e\u5b9e\u65f6\u76d1\u63a7Android\u5f53\u524dAPP\u7684CPU\u4f7f\u7528\u7387\u53caFPS\u6570\u636e |\r\n| 2.3.7 | \u65b0\u589e\u8bc6\u522b\u6ed1\u5757\u9a8c\u8bc1\u7801\u7834\u89e3 |\r\n| 2.3.8 | \u4f18\u5316\u8bc6\u522b\u6ed1\u5757\u9a8c\u8bc1\u7801\u7834\u89e3 |\r\n| 2.3.9 | \u65b0\u589e\u89e3\u6790DNS\u5e76\u5237\u65b0DNS\u7f13\u5b58\uff0c\u6570\u636e\u4fdd\u5b58HOSTS |\r\n| 2.4 | \u4fee\u590d\u5df2\u77e5BUG |\r\n| 2.4.1 | \u4f18\u5316DNS\u89e3\u6790\u6548\u679c |\r\n| 2.4.2 | \u4f18\u5316\u81ea\u52a8\u751f\u6210\u4ee3\u7801 |\r\n| 2.4.3 | \u4f18\u5316 |\r\n| 2.4.3.1 | \u4f18\u5316 |\r\n| 2.4.3.2 | \u4f18\u5316 |\r\n| 2.5.0 | \u6dfb\u52a0xiaobaimanager\u547d\u4ee4 |\r\n| 2.5.1 | \u4f18\u5316xiaobai |\r\n| 2.5.1 | \u65b0\u589exiaobaifinder |\r\n| 3.0.0 | \u65b0\u589exiaobaidevice\u3001xiaobaidevice2\u3001xiaobaicmd -m cp |\r\n| 3.0.1 | xiaobaicmd -m cp \u65b0\u589e\u3010Ctrl + S\u3011 |\r\n| 3.0.2 | xiaobaicmd -t api \u66ff\u6362\u6a21\u677f | \r\n| 3.0.3 | xiaobaicmd -t api \u65b0\u589e\u754c\u9762\u64cd\u4f5c\u66ff\u6362\u6a21\u677f | \r\n| 3.0.4 | \u4f18\u5316\u751f\u6210\u63a5\u53e3\u81ea\u52a8\u5316\u6a21\u677f | \r\n| 3.1.0 | \u4f18\u5316 |\r\n| 3.1.1 | Fix |\r\n| 3.1.2 | Fix |\r\n| 3.1.6 | Fix |\r\n| 3.1.7 | Fix |\r\n| 3.1.8 | \u4fee\u590d`xiaobaidevice2`\u529f\u80fd\u4e0d\u53ef\u7528 |\r\n| 3.1.9 | \u7cbe\u7b80\u4f9d\u8d56\u5e93 |\r\n",
"bugtrack_url": null,
"license": null,
"summary": "simple_automation_framework(\u7b80\u79f0\uff1aSAF)\u4f7f\u7528\u6700\u7b80\u5355\u7684\u6a21\u5f0f\u5c31\u53ef\u4ee5\u5b9e\u73b0\u9700\u8981\u529f\u80fd\u548c\u6d4b\u8bd5\u6548\u679c\uff0c\u4e5f\u662fxiaobaiauto2\u7684\u7b80\u5316\u7248SAF\u7ee7\u627f\u4e86selenium\u3001requests/httpx\u3001appium\u3001loguru\u3001xiaobaiauto2\u3001\u98de\u4e66\u673a\u5668\u4eba\u3001\u9489\u9489\u673a\u5668\u4eba\u3001\u4f01\u4e1a\u5fae\u4fe1\u673a\u5668\u4eba\uff08\u6682\u65f6\u4e0d\u652f\u6301\uff09\u3001\u7985\u9053\u63d0\u5355API",
"version": "3.1.9",
"project_urls": {
"Homepage": "https://gitee.com/xiaobaikeji/simlpe_automation_framework"
},
"split_keywords": [
"saf",
"automation",
"xiaobai",
"xiaobaiauto2",
"test",
"framework"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "7e3fe233b6603ab33d0897c30ed9c0325d4351d12f3590875737c817f9754a28",
"md5": "4d86abce25bb86128737b14ad53343fa",
"sha256": "a16c1432f5ca19cba14da343e3ac0e72515087353357616d2bd66dba3cea66f3"
},
"downloads": -1,
"filename": "xiaobaisaf-3.1.9-py3-none-any.whl",
"has_sig": false,
"md5_digest": "4d86abce25bb86128737b14ad53343fa",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">3.8",
"size": 13143139,
"upload_time": "2024-12-13T01:41:53",
"upload_time_iso_8601": "2024-12-13T01:41:53.209350Z",
"url": "https://files.pythonhosted.org/packages/7e/3f/e233b6603ab33d0897c30ed9c0325d4351d12f3590875737c817f9754a28/xiaobaisaf-3.1.9-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-12-13 01:41:53",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "xiaobaisaf"
}