feat: 优化定位转换器调整框架结构

- 新增 finder.py 重构定位转换器
- 优化 .gitignore 文件
- 其他优化
This commit is contained in:
2026-01-25 17:24:04 +08:00
parent 92d06dd9cf
commit 5a50eb8289
15 changed files with 342 additions and 126 deletions

139
utils/finder.py Normal file
View File

@@ -0,0 +1,139 @@
#!/usr/bin/env python
# coding=utf-8
"""
@author: CNWei
@Software: PyCharm
@contact: t6i888@163.com
@file: finder
@date: 2026/1/25 16:56
@desc:
"""
from typing import Literal, Final
from selenium.webdriver.common.by import By
ByType = Literal[
# By(selenium)
"id", "xpath", "link text", "partial link text", "name", "tag name", "class name", "css selector",
# 自定义常用简写 (Shortcuts)
"lt","plt", "class", "css",
]
class FinderConverter:
"""
定位查找转换工具类
提供策略的归一化处理、简写映射及动态自定义注册
"""
# 预设的常用简写
_BUILTIN_SHORTCUTS: Final = {
"lt": By.LINK_TEXT,
"plt": By.PARTIAL_LINK_TEXT,
"class": By.CLASS_NAME,
"css": By.CSS_SELECTOR,
}
def __init__(self):
self._finder_map: dict[str, str] = {}
self._map_cache: dict[str, str] = {}
self._initialize()
@staticmethod
def _normalize(text: str) -> str:
"""
统一清洗逻辑:转小写、去除空格、下划线、横杠
"""
if not isinstance(text, str):
raise TypeError(f"Locator strategy must be a string, got {type(text).__name__} instead.")
return text.lower().strip().replace('_', '').replace(' ', '').replace('-', '')
def _initialize(self) -> None:
"""初始化基础映射表"""
# 1. 动态加载 AppiumBy 常量值
for attr_name in dir(By):
if attr_name.startswith("_"):
continue
attr_value = getattr(By, attr_name)
if isinstance(attr_value, str):
# "class name" -> classname,"class_name" -> classname
self._finder_map[self._normalize(attr_value)] = attr_value
# 2. 加载内置简写(会覆盖同名的策略)
self._finder_map.update(self._BUILTIN_SHORTCUTS)
# 3. 备份初始状态
self._map_cache = self._finder_map.copy()
def convert(self, by_value: ByType | str) -> str:
"""
将模糊或简写的定位方式转换为 Appium 标准定位字符串
:raises ValueError: 当定位方式不支持时抛出
"""
if not by_value or not isinstance(by_value, str):
raise ValueError(f"Invalid selector type: {type(by_value)}. Expected a string.")
clean_key = self._normalize(by_value)
target = self._finder_map.get(clean_key)
if target is None:
raise ValueError(f"Unsupported locator strategy: '{by_value}'.")
return target
def register_custom_finder(self, alias: str, target: str) -> None:
"""注册自定义定位策略"""
self._finder_map[self._normalize(alias)] = target
def clear_custom_finders(self) -> None:
"""重置回初始官方/内置状态"""
self._finder_map = self._map_cache.copy()
def get_all_finders(self) -> list[str]:
"""返回当前所有支持的策略 key用于调试"""
return list(self._finder_map.keys())
# 导出单例,方便直接使用
converter = FinderConverter()
by_converter = converter.convert
register_custom_finder = converter.register_custom_finder
__all__ = ["by_converter", "register_custom_finder"]
if __name__ == '__main__':
# 1. 测试标准转换与内置简写
print(f"ID 转换: {by_converter('id')}") # 输出: id
print(f"Class 简写转换: {by_converter('class')}") # 输出: accessibility id
print(f"CSS 简写转换: {by_converter('css')}") # 输出: css selector
# 2. 测试强大的归一化容错 (空格、下划线、横杠、大小写)
print(f"类链容错: {by_converter(' link_text ')}") # 输出: link text
print(f"PARTIAL_LINK_TEXT 容错: {by_converter('PARTIAL_LINK_TEXT')}") # 输出: partial link text
# 3. 测试自定义注册
register_custom_finder("my_text", "-my uiautomator")
print(f"自定义注册测试: {by_converter('my_text')}") # 输出: -my uiautomator
# 4. 测试重置功能
converter.clear_custom_finders()
print("已重置自定义查找器")
try:
by_converter("my_text")
except ValueError as e:
print(f"验证重置成功 (捕获异常): {e}")
# 5. 查看当前全量支持的归一化后的 Key
print(f"当前支持的策略总数: {len(converter.get_all_finders())}")
print(f"前 5 个策略示例: {converter.get_all_finders()[:5]}")
# 6. 增加类型非法测试
print("\n--- 异常类型测试 ---")
try:
by_converter(123) # 传入数字
except TypeError as e:
print(f"验证类型拦截成功: {e}")
try:
by_converter(None) # 传入 None
except TypeError as e:
print(f"验证空值拦截成功: {e}")