fix(conftest,config_loader): 修复 get_caps 的 Capabilities 加载逻辑
- 新增 pytest_addoption 增加 "--caps_name" 获取配置文件中的设备/平台名称 - 修复 get_caps 的 Capabilities 加载逻辑 - 优化 其他优化 enums.py - 删除 移除部分文档,代码,第三方包(loguru)
This commit is contained in:
@@ -9,59 +9,54 @@
|
||||
@date: 2026/1/16 10:52
|
||||
@desc: Pytest 核心配置与 Fixture 管理
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
import logging
|
||||
from typing import Any, Optional
|
||||
from utils.data_loader import load_yaml
|
||||
from core.settings import CAPS_CONFIG_PATH, ENV_CONFIG, CURRENT_ENV
|
||||
from core.modules import AppPlatform
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_env_config() -> dict[str, str]:
|
||||
def get_env_config(env_name: Optional[str] = None) -> dict[str, str]:
|
||||
"""
|
||||
根据当前环境 (CURRENT_ENV) 获取对应的业务配置
|
||||
获取当前运行环境的业务配置信息。
|
||||
|
||||
逻辑:
|
||||
1. 优先使用传入的 `env_name`。
|
||||
2. 若未传入,使用全局设置 `CURRENT_ENV`。
|
||||
3. 若都为空,默认为 "test"。
|
||||
4. 如果目标环境在配置中不存在,强制回退到 "test" 环境并记录警告。
|
||||
|
||||
:param env_name: 指定的环境名称 (e.g., "dev", "prod"),可选。
|
||||
:return: 对应环境的配置字典。
|
||||
"""
|
||||
return ENV_CONFIG.get(CURRENT_ENV, ENV_CONFIG["test"])
|
||||
target_env = env_name or CURRENT_ENV or "test"
|
||||
|
||||
if target_env not in ENV_CONFIG:
|
||||
logger.warning(f"环境 '{target_env}' 未在配置中定义,将回退到 'test' 环境。")
|
||||
return ENV_CONFIG.get("test", {})
|
||||
|
||||
return ENV_CONFIG[target_env]
|
||||
|
||||
|
||||
def get_caps(platform: str) -> dict[str, Any]:
|
||||
def get_caps(caps_name: str) -> dict[str, Any]:
|
||||
"""
|
||||
从 YAML 文件加载 Capabilities 配置
|
||||
从 YAML 配置文件加载指定的 Appium Capabilities。
|
||||
|
||||
:param caps_name: 配置文件中的设备/平台名称 (不区分大小写),例如 "android_pixel"。
|
||||
:return: 该设备对应的 Capabilities 字典。
|
||||
:raises ValueError: 当指定的 caps_name 在配置文件中不存在时。
|
||||
:raises RuntimeError: 当配置文件加载失败或格式错误时。
|
||||
"""
|
||||
try:
|
||||
all_caps = load_yaml(CAPS_CONFIG_PATH)
|
||||
all_caps = {k.lower(): v for k, v in all_caps.items()}
|
||||
platform_key = platform.lower()
|
||||
caps_key = caps_name.lower()
|
||||
|
||||
if platform_key not in all_caps:
|
||||
if caps_key not in all_caps:
|
||||
raise ValueError(f"在 {CAPS_CONFIG_PATH} 中找不到平台 '{caps_key}' 的配置")
|
||||
|
||||
base_caps: dict[str, Any] = {
|
||||
"noReset": False,
|
||||
"newCommandTimeout": 60,
|
||||
}
|
||||
|
||||
match platform_key:
|
||||
case AppPlatform.ANDROID:
|
||||
android_caps = {
|
||||
"platformName": "Android",
|
||||
"automationName": "uiautomator2",
|
||||
"deviceName": "Android",
|
||||
"appPackage": "com.manu.wanandroid",
|
||||
"appActivity": "com.manu.wanandroid.ui.main.activity.MainActivity",
|
||||
}
|
||||
return base_caps | android_caps
|
||||
case AppPlatform.IOS:
|
||||
|
||||
ios_caps = {
|
||||
"platformName": "iOS",
|
||||
"automationName": "XCUITest",
|
||||
"deviceName": "iPhone 14",
|
||||
"bundleId": "com.example.app",
|
||||
"autoAcceptAlerts": True,
|
||||
"waitForQuiescence": False,
|
||||
}
|
||||
return base_caps | ios_caps
|
||||
|
||||
return all_caps[platform_key]
|
||||
return all_caps[caps_key]
|
||||
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"无法加载“caps”内容 {CAPS_CONFIG_PATH}: {e}")
|
||||
raise RuntimeError(f"加载 Capabilities 失败 ({CAPS_CONFIG_PATH}): {e}")
|
||||
|
||||
@@ -30,10 +30,10 @@ from selenium.webdriver.common.actions import interaction
|
||||
from selenium.webdriver.common.actions.action_builder import ActionBuilder
|
||||
from selenium.webdriver.common.actions.pointer_input import PointerInput
|
||||
|
||||
from core.enums import AppPlatform
|
||||
from core.settings import IMPLICIT_WAIT_TIMEOUT, EXPLICIT_WAIT_TIMEOUT, APPIUM_HOST, APPIUM_PORT, SCREENSHOT_DIR
|
||||
from utils.finder import by_converter
|
||||
from utils.decorators import resolve_wait_method
|
||||
from core.modules import AppPlatform
|
||||
from core.settings import IMPLICIT_WAIT_TIMEOUT, EXPLICIT_WAIT_TIMEOUT, APPIUM_HOST, APPIUM_PORT, SCREENSHOT_DIR
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -623,6 +623,7 @@ class CoreDriver:
|
||||
:return: self
|
||||
"""
|
||||
return self.switch_to_context('NATIVE_APP')
|
||||
|
||||
def full_screen_screenshot(self, name: str | None = None) -> str:
|
||||
"""
|
||||
截取当前完整屏幕内容 (自愈逻辑、异常报错首选)
|
||||
|
||||
58
core/enums.py
Normal file
58
core/enums.py
Normal file
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
@author: CNWei,ChenWei
|
||||
@Software: PyCharm
|
||||
@contact: t6g888@163.com
|
||||
@file: enums
|
||||
@date: 2026/2/27 17:05
|
||||
@desc:
|
||||
"""
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class AppiumStatus(Enum):
|
||||
"""Appium 服务状态枚举"""
|
||||
READY = "服务已启动" # 服务和驱动都加载完成 (HTTP 200 + ready: true)
|
||||
INITIALIZING = "驱动正在加载" # 服务已响应但驱动仍在加载 (HTTP 200 + ready: false)
|
||||
CONFLICT = "端口被其他程序占用" # 端口被其他非 Appium 程序占用
|
||||
OFFLINE = "服务未启动" # 服务未启动
|
||||
ERROR = "内部错误"
|
||||
UNKNOWN = "未知状态"
|
||||
|
||||
|
||||
class ServiceRole(Enum):
|
||||
"""服务角色枚举:定义服务的所有权和生命周期"""
|
||||
MANAGED = "托管模式" # 由本脚本启动,负责清理
|
||||
EXTERNAL = "共享模式" # 复用现有服务,不负责清理
|
||||
NULL = "空模式" # 无效或未初始化的服务
|
||||
|
||||
|
||||
class AppPlatform(Enum):
|
||||
"""
|
||||
定义支持的移动应用平台枚举。
|
||||
"""
|
||||
ANDROID = "android"
|
||||
IOS = "ios"
|
||||
|
||||
|
||||
class Locator(str, Enum):
|
||||
"""
|
||||
定义元素定位策略枚举。
|
||||
继承 str 以便直接作为参数传递给 Selenium/Appium 方法。
|
||||
"""
|
||||
# --- 原有 Selenium 支持 ---
|
||||
ID = "id"
|
||||
NAME = "name"
|
||||
CLASS = "class"
|
||||
TAG = "tag"
|
||||
LINK_TEXT = "link_text"
|
||||
PARTIAL_LINK_TEXT = "partial_link_text"
|
||||
CSS = "css"
|
||||
XPATH = "xpath"
|
||||
# --- Appium 特有支持 ---
|
||||
ACCESSIBILITY_ID = "accessibility_id"
|
||||
AID = "aid" # 简写
|
||||
ANDROID_UIAUTOMATOR = "android_uiautomator"
|
||||
IOS_PREDICATE = "ios_predicate"
|
||||
@@ -9,33 +9,3 @@
|
||||
@date: 2026/1/20 11:54
|
||||
@desc:
|
||||
"""
|
||||
|
||||
from enum import Enum
|
||||
|
||||
class AppPlatform(Enum):
|
||||
"""
|
||||
定义支持的移动应用平台枚举。
|
||||
"""
|
||||
ANDROID = "android"
|
||||
IOS = "ios"
|
||||
|
||||
|
||||
class Locator(str, Enum):
|
||||
"""
|
||||
定义元素定位策略枚举。
|
||||
继承 str 以便直接作为参数传递给 Selenium/Appium 方法。
|
||||
"""
|
||||
# --- 原有 Selenium 支持 ---
|
||||
ID = "id"
|
||||
NAME = "name"
|
||||
CLASS = "class"
|
||||
TAG = "tag"
|
||||
LINK_TEXT = "link_text"
|
||||
PARTIAL_LINK_TEXT = "partial_link_text"
|
||||
CSS = "css"
|
||||
XPATH = "xpath"
|
||||
# --- Appium 特有支持 ---
|
||||
ACCESSIBILITY_ID = "accessibility_id"
|
||||
AID = "aid" # 简写
|
||||
ANDROID_UIAUTOMATOR = "android_uiautomator"
|
||||
IOS_PREDICATE = "ios_predicate"
|
||||
@@ -5,7 +5,7 @@
|
||||
@author: CNWei,ChenWei
|
||||
@Software: PyCharm
|
||||
@contact: t6g888@163.com
|
||||
@file: test
|
||||
@file: run_appium
|
||||
@date: 2026/1/12 10:21
|
||||
@desc:
|
||||
"""
|
||||
@@ -19,10 +19,11 @@ import sys
|
||||
import http.client
|
||||
import socket
|
||||
import json
|
||||
from enum import Enum
|
||||
|
||||
from typing import List
|
||||
|
||||
from core.settings import BASE_DIR, APPIUM_HOST, APPIUM_PORT, MAX_RETRIES
|
||||
from core.enums import AppiumStatus, ServiceRole
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -55,23 +56,6 @@ class AppiumInternalError(AppiumStartupError):
|
||||
pass
|
||||
|
||||
|
||||
class AppiumStatus(Enum):
|
||||
"""Appium 服务状态枚举"""
|
||||
READY = "服务已启动" # 服务和驱动都加载完成 (HTTP 200 + ready: true)
|
||||
INITIALIZING = "驱动正在加载" # 服务已响应但驱动仍在加载 (HTTP 200 + ready: false)
|
||||
CONFLICT = "端口被其他程序占用" # 端口被其他非 Appium 程序占用
|
||||
OFFLINE = "服务未启动" # 服务未启动
|
||||
ERROR = "内部错误"
|
||||
UNKNOWN = "未知状态"
|
||||
|
||||
|
||||
class ServiceRole(Enum):
|
||||
"""服务角色枚举:定义服务的所有权和生命周期"""
|
||||
MANAGED = "托管模式" # 由本脚本启动,负责清理
|
||||
EXTERNAL = "共享模式" # 复用现有服务,不负责清理
|
||||
NULL = "空模式" # 无效或未初始化的服务
|
||||
|
||||
|
||||
def resolve_appium_command(host: str, port: int | str) -> List[str]:
|
||||
"""
|
||||
解析 Appium 可执行文件的绝对路径。
|
||||
|
||||
Reference in New Issue
Block a user