kuto
Advanced tools
@@ -10,3 +10,3 @@ """ | ||
| """ | ||
| 图像识别可以配合安卓应用或者IOS应用使用 | ||
| 图像识别可以配合安卓应用或者IOS应用使用,ios需要加上scale | ||
| """ | ||
@@ -13,0 +13,0 @@ |
@@ -10,3 +10,3 @@ """ | ||
| """ | ||
| ocr识别可以配合安卓应用或者IOS应用进行使用 | ||
| ocr识别可以配合安卓应用或者IOS应用进行使用,ios需要加上scale | ||
| """ | ||
@@ -19,3 +19,3 @@ | ||
| searchResult = Elem(xpath="//Table/Cell[2]", desc='搜索结果') | ||
| schoolEntry = OCRElem(text="校园场馆", pos=12) | ||
| schoolEntry = OCRElem(text="校园场馆", pos=12, scale=3) | ||
| import kuto | ||
| from kuto import logger | ||
| from kuto.utils.reader import file_data | ||
@@ -21,21 +20,13 @@ | ||
| @file_data(file='../static/data.json') | ||
| @kuto.file_data(file='../static/data.json') | ||
| def test_json(self, param): | ||
| logger.info(param) | ||
| @file_data(file='../static/data.yml', key='names') | ||
| @kuto.file_data(file='../static/data.yml', key='names') | ||
| def test_yaml(self, param): | ||
| print(param) | ||
| @file_data(file='../static/data.csv') | ||
| def test_csv(self, param): | ||
| logger.info(param) | ||
| @file_data(file='../static/data.xlsx', row=1) | ||
| def test_excel(self, param): | ||
| logger.info(param) | ||
| if __name__ == '__main__': | ||
| """仅执行本模块""" | ||
| kuto.main() |
| Metadata-Version: 2.1 | ||
| Name: kuto | ||
| Version: 0.0.65 | ||
| Version: 0.0.66 | ||
| Summary: 全平台自动化测试框架 | ||
@@ -16,3 +16,3 @@ Home-page: https://gitee.com/bluepang2021/kuto | ||
| Requires-Python: >=3.9 | ||
| Provides-Extra: reader | ||
| Provides-Extra: excel | ||
| Provides-Extra: encrypt | ||
@@ -19,0 +19,0 @@ Provides-Extra: android |
@@ -22,2 +22,7 @@ requests-toolbelt==0.10.1 | ||
| [excel] | ||
| pandas==1.3.4 | ||
| openpyxl==3.0.9 | ||
| XlsxWriter==3.0.2 | ||
| [ios] | ||
@@ -30,3 +35,3 @@ tidevice==0.6.1 | ||
| easyocr==1.7.0 | ||
| opencv-python==4.6.0.66 | ||
| Pillow==9.5.0 | ||
@@ -37,9 +42,4 @@ [pc] | ||
| [reader] | ||
| pandas==1.3.4 | ||
| openpyxl==3.0.9 | ||
| XlsxWriter==3.0.2 | ||
| [web] | ||
| playwright==1.33.0 | ||
| opencv-python==4.6.0.66 |
@@ -81,3 +81,2 @@ README.md | ||
| kuto/utils/pytest_util.py | ||
| kuto/utils/reader.py | ||
| kuto/utils/swagger_util.py | ||
@@ -84,0 +83,0 @@ kuto/web/__init__.py |
+2
-2
@@ -10,6 +10,6 @@ from allure import * | ||
| from kuto.utils.pytest_util import \ | ||
| depend, order, data | ||
| depend, order, data, file_data | ||
| __version__ = "0.0.65" | ||
| __version__ = "0.0.66" | ||
| __description__ = "全平台自动化测试框架" |
@@ -167,2 +167,6 @@ import os | ||
| def enter(self): | ||
| logger.info("点击回车") | ||
| self.d.press("enter") | ||
| def click(self, x, y): | ||
@@ -188,19 +192,3 @@ logger.info(f"点击坐标: {x}, {y}") | ||
| if __name__ == '__main__': | ||
| driver1 = AndroidDriver( | ||
| device_id="UJK0220521066836", | ||
| pkg_name="com.qizhidao.clientapp" | ||
| ) | ||
| # helper = [ | ||
| # {"method": "assert", "loc": "未发现风险"}, | ||
| # {"method": "click", "loc": "继续安装"}, | ||
| # {"method": "click", "loc": "完成"} | ||
| # ] | ||
| helper1 = [ | ||
| ["assert", {"text": "未发现风险"}], | ||
| ["click", {"text": "继续安装"}], | ||
| ["click", {"text": "完成"}], | ||
| ] | ||
| driver1.install_app( | ||
| apk_path="http://maven.qizhidao.net:8081/repository/packages-host/qzd/qizhidao_app_android/dev-work/jenkins_771/app_qzd_v4.4.2_80403001_2023-07-26_arm64-v8a_pre_release.apk") | ||
| driver1.start_app() | ||
| pass | ||
@@ -220,1 +208,2 @@ | ||
@@ -116,3 +116,3 @@ import typing | ||
| self._driver.screenshot(self._desc + "_查找失败") | ||
| raise KError(f"控件 {self._kwargs} 查找失败") | ||
| raise KError(f"控件 {self._desc} 查找失败") | ||
@@ -187,12 +187,14 @@ if watch: | ||
| def input(self, text, watch=None): | ||
| def input(self, text, watch=None, enter=False): | ||
| logger.info(f"输入文本: {text}") | ||
| self.find(watch=watch).set_text(text) | ||
| if enter is True: | ||
| self._driver.enter() | ||
| def input_exists(self, text: str, timeout=3, watch=None): | ||
| def input_exists(self, text: str, timeout=3, watch=None, enter=False): | ||
| logger.info(f"{self._desc} 存在才输入: {text}") | ||
| if self.exists(timeout=timeout): | ||
| self.input(text, watch=watch) | ||
| self.input(text, watch=watch, enter=enter) | ||
| def input_pwd(self, text, watch=None): | ||
| def input_pwd(self, text, watch=None, enter=False): | ||
| """密码输入框输入有时候用input输入不了""" | ||
@@ -202,2 +204,4 @@ logger.info(f"输入密码: {text}") | ||
| self._driver.d(focused=True).set_text(text) | ||
| if enter is True: | ||
| self._driver.enter() | ||
@@ -204,0 +208,0 @@ def clear(self, watch=None): |
| import time | ||
| from kuto.ios.driver import Driver | ||
| # from kuto.ios.driver import Driver | ||
| from kuto.utils.exceptions import KError | ||
@@ -17,2 +17,3 @@ from kuto.utils.log import logger | ||
| image: str = None, | ||
| scale: int = None, | ||
| debug: bool = False): | ||
@@ -26,2 +27,3 @@ self.driver = driver | ||
| self._debug = debug | ||
| self._scale = scale | ||
@@ -40,3 +42,3 @@ def __get__(self, instance, owner): | ||
| logger.info(f'第{i + 1}次查找:') | ||
| source_image = self.driver.screenshot(self._desc) | ||
| source_image = self.driver.screenshot(self._desc + f"_第{i+1}次识别") | ||
| res = ImageDiscern(self.target_image, source_image, grade, gauss_num).get_coordinate() | ||
@@ -61,4 +63,5 @@ logger.debug(res) | ||
| x, y = res[0], res[1] | ||
| if isinstance(self.driver, Driver): | ||
| x, y = int(x/self.driver.d.scale), int(y/self.driver.d.scale) | ||
| if self._scale is not None: | ||
| """iphone的scale是3""" | ||
| x, y = int(x/self._scale), int(y/self._scale) | ||
| if self._debug is True: | ||
@@ -65,0 +68,0 @@ file_path = self.driver.screenshot(f'图像识别定位成功-{self._desc}') |
@@ -118,2 +118,6 @@ import shutil | ||
| def enter(self): | ||
| logger.info("点击回车") | ||
| self.d.send_keys("\n") | ||
| def click(self, x, y): | ||
@@ -120,0 +124,0 @@ logger.info(f"点击坐标: {x}, {y}") |
@@ -152,3 +152,3 @@ import time | ||
| self._driver.screenshot(self._desc + "_查找失败") | ||
| raise KError(f"控件 {self._kwargs} 查找失败") | ||
| raise KError(f"控件 {self._desc} 查找失败") | ||
| except ConnectionError: | ||
@@ -167,3 +167,3 @@ logger.info('wda连接失败, 进行重连!!!') | ||
| self._driver.screenshot(self._desc + "_查找失败") | ||
| raise KError(f"控件 {self._kwargs} 查找失败") | ||
| raise KError(f"控件 {self._desc} 查找失败") | ||
@@ -235,11 +235,13 @@ if watch: | ||
| def input(self, text): | ||
| def input(self, text, enter=False): | ||
| """输入内容""" | ||
| logger.info(f"输入文本:{text}") | ||
| self.find().set_text(text) | ||
| if enter is True: | ||
| self._driver.enter() | ||
| def input_exists(self, text: str, timeout=3): | ||
| def input_exists(self, text: str, timeout=3, enter=False): | ||
| logger.info(f"{self._desc} 存在才输入: {text}") | ||
| if self.exists(timeout=timeout): | ||
| self.input(text) | ||
| self.input(text, enter=enter) | ||
| logger.info("输入成功") | ||
@@ -246,0 +248,0 @@ else: |
+12
-15
| import time | ||
| from kuto.ios.driver import Driver | ||
| # from kuto.ios.driver import Driver | ||
| from kuto.utils.log import logger | ||
@@ -33,2 +33,3 @@ from kuto.ocr.driver import OCRDiscern | ||
| grade=0.8, | ||
| scale: int = None, | ||
| debug: bool = False): | ||
@@ -49,2 +50,3 @@ """ | ||
| self._debug = debug | ||
| self._scale = scale | ||
@@ -61,5 +63,4 @@ def __get__(self, instance, owner): | ||
| for i in range(retry): | ||
| if i > 0: | ||
| logger.info(f"第{i}次重试") | ||
| info = self.driver.screenshot(self.text, | ||
| logger.info(f"第{i+1}次识别") | ||
| info = self.driver.screenshot(self.text + f"_第{i+1}次识别", | ||
| position=self._position) | ||
@@ -95,4 +96,6 @@ if self._position is not None: | ||
| y = y + cut_height | ||
| if isinstance(self.driver, Driver): | ||
| x, y = int(x/self.driver.d.scale), int(y/self.driver.d.scale) | ||
| if self._scale is not None: | ||
| """iphone的scale是3""" | ||
| x, y = int(x/self._scale), int(y/self._scale) | ||
| logger.info(f'识别坐标为: ({x}, {y})') | ||
@@ -103,2 +106,3 @@ if self._debug is True: | ||
| return x, y | ||
| time.sleep(timeout) | ||
@@ -109,12 +113,5 @@ else: | ||
| def exists(self, retry=3, timeout=1): | ||
| def exists(self, timeout=1): | ||
| logger.info(f'ocr识别文本: {self.text} 是否存在') | ||
| try: | ||
| self.find_element(retry=retry, timeout=timeout) | ||
| except: | ||
| return False | ||
| else: | ||
| return True | ||
| finally: | ||
| logger.info("识别成功") | ||
| return self.find_element(timeout=timeout) | ||
@@ -121,0 +118,0 @@ def click(self, retry=3, timeout=1): |
@@ -5,2 +5,4 @@ """ | ||
| import pytest | ||
| import json | ||
| import yaml | ||
@@ -32,1 +34,61 @@ | ||
| return pytest.mark.parametrize('param', list_data) | ||
| # 从json文件获取数据进行参数化 | ||
| def file_data(file=None, key=None): | ||
| # logger.debug(config.get_env()) | ||
| """ | ||
| 感觉excel和csv不常用,去掉 | ||
| @param file: 文件名 | ||
| @param key: 针对json和yaml文件 | ||
| """ | ||
| file_path = file # 去掉文件查找机制,不好理解,用处也不大 | ||
| # logger.debug(file_path) | ||
| if file_path.endswith(".json"): | ||
| content = read_json(file_path, key) | ||
| elif file_path.endswith(".yml"): | ||
| content = read_yaml(file_path, key) | ||
| # elif file_path.endswith(".csv"): | ||
| # content = read_csv(file_path, row) | ||
| # elif file_path.endswith(".xlsx"): | ||
| # content = read_excel(file_path, row) | ||
| else: | ||
| raise TypeError("不支持的文件类型,仅支持json、yml") | ||
| if content: | ||
| return data(content) | ||
| else: | ||
| raise ValueError('数据不能为空') | ||
| def read_json(file_path, key=None): | ||
| """ | ||
| 读取json文件中的指定key | ||
| @return | ||
| """ | ||
| with open(file_path, 'r', encoding='utf-8') as f: | ||
| json_data = json.load(f) | ||
| if isinstance(json_data, list): | ||
| return json_data | ||
| else: | ||
| if key: | ||
| return json_data[key] | ||
| raise ValueError('key不能为空') | ||
| def read_yaml(file_path, key=None): | ||
| """ | ||
| 读取yaml文件中的指定key | ||
| @param file_path: | ||
| @param key: | ||
| """ | ||
| with open(file_path, 'r', encoding='utf-8') as f: | ||
| yaml_data = yaml.load(f, Loader=yaml.FullLoader) | ||
| if isinstance(yaml_data, list): | ||
| return yaml_data | ||
| else: | ||
| if key: | ||
| return yaml_data[key] | ||
| raise ValueError('key不能为空') |
+2
-2
| Metadata-Version: 2.1 | ||
| Name: kuto | ||
| Version: 0.0.65 | ||
| Version: 0.0.66 | ||
| Summary: 全平台自动化测试框架 | ||
@@ -16,3 +16,3 @@ Home-page: https://gitee.com/bluepang2021/kuto | ||
| Requires-Python: >=3.9 | ||
| Provides-Extra: reader | ||
| Provides-Extra: excel | ||
| Provides-Extra: encrypt | ||
@@ -19,0 +19,0 @@ Provides-Extra: android |
+2
-2
@@ -45,3 +45,3 @@ # -*- coding: utf-8 -*- | ||
| extras_require={ | ||
| "reader": ['pandas==1.3.4', 'openpyxl==3.0.9', 'XlsxWriter==3.0.2'], | ||
| "excel": ['pandas==1.3.4', 'openpyxl==3.0.9', 'XlsxWriter==3.0.2'], | ||
| "encrypt": ["pycryptodome==3.14.1"], | ||
@@ -52,3 +52,3 @@ "android": ['uiautomator2==2.16.23', 'opencv-python==4.6.0.66'], | ||
| "pc": ['PyAutoGUI==0.9.54', 'opencv-python==4.6.0.66'], | ||
| "ocr": ['easyocr==1.7.0', 'opencv-python==4.6.0.66'] | ||
| "ocr": ['easyocr==1.7.0', 'Pillow==9.5.0'] | ||
| }, | ||
@@ -55,0 +55,0 @@ |
| """ | ||
| pip install -i https://pypi.tuna.tsinghua.edu.cn/simple kuto[reader] | ||
| @Author: kang.yang | ||
| @Date: 2023/9/20 13:51 | ||
| """ | ||
| import json | ||
| import yaml | ||
| from kuto.utils.excel import Excel, CSV | ||
| from kuto.utils.pytest_util import data | ||
| # 从json文件获取数据进行参数化 | ||
| def file_data(file=None, key=None, row=None): | ||
| # logger.debug(config.get_env()) | ||
| """ | ||
| @param row: 第几行,针对csv和excel文件 | ||
| @param file: 文件名 | ||
| @param key: 针对json和yaml文件 | ||
| """ | ||
| file_path = file # 去掉文件查找机制,不好理解,用处也不大 | ||
| # logger.debug(file_path) | ||
| if file_path.endswith(".json"): | ||
| content = read_json(file_path, key) | ||
| elif file_path.endswith(".yml"): | ||
| content = read_yaml(file_path, key) | ||
| elif file_path.endswith(".csv"): | ||
| content = read_csv(file_path, row) | ||
| elif file_path.endswith(".xlsx"): | ||
| content = read_excel(file_path, row) | ||
| else: | ||
| raise TypeError("不支持的文件类型,仅支持json、yml、csv、xlsx") | ||
| if content: | ||
| return data(content) | ||
| else: | ||
| raise ValueError('数据不能为空') | ||
| def read_json(file_path, key=None): | ||
| """ | ||
| 读取json文件中的指定key | ||
| @return | ||
| """ | ||
| with open(file_path, 'r', encoding='utf-8') as f: | ||
| json_data = json.load(f) | ||
| if isinstance(json_data, list): | ||
| return json_data | ||
| else: | ||
| if key: | ||
| return json_data[key] | ||
| raise ValueError('key不能为空') | ||
| def read_yaml(file_path, key=None): | ||
| """ | ||
| 读取yaml文件中的指定key | ||
| @param file_path: | ||
| @param key: | ||
| """ | ||
| with open(file_path, 'r', encoding='utf-8') as f: | ||
| yaml_data = yaml.load(f, Loader=yaml.FullLoader) | ||
| if isinstance(yaml_data, list): | ||
| return yaml_data | ||
| else: | ||
| if key: | ||
| return yaml_data[key] | ||
| raise ValueError('key不能为空') | ||
| def read_csv(file_path, row=None): | ||
| """ | ||
| 读取csv文件中的指定行 | ||
| @param file_path: 文件名 | ||
| @param row: 行数,从1开始 | ||
| """ | ||
| csv_file = CSV(file_path) | ||
| if row: | ||
| csv_data = [csv_file.read_row_index(row)] | ||
| else: | ||
| csv_data = csv_file.read_all() | ||
| return csv_data | ||
| def read_excel(file_path, row=None): | ||
| """ | ||
| 读取excel文件中的指定行,暂时只支持读取第一个sheet | ||
| @param file_path: 文件名 | ||
| @param row: 行数,从1开始 | ||
| """ | ||
| excel_file = Excel(file_path) | ||
| if row: | ||
| excel_data = [excel_file.read_row_index(row)] | ||
| else: | ||
| excel_data = excel_file.read_all() | ||
| return excel_data |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
221019
-0.6%93
-1.06%5320
-0.82%