""" Pytest fixtures for UI testing """ import os import subprocess import time from pathlib import Path import pytest from playwright.sync_api import Browser, Page # 测试服务器配置 TEST_SERVER_HOST = os.getenv("TEST_SERVER_HOST", "localhost") TEST_SERVER_PORT = int(os.getenv("TEST_SERVER_PORT", "8000")) BASE_URL = f"http://{TEST_SERVER_HOST}:{TEST_SERVER_PORT}" @pytest.fixture(scope="session") def test_server(): """ 启动测试服务器(如果未运行) 如果服务器已经在运行,直接返回 URL。 否则,启动一个测试服务器进程。 """ # 检查服务器是否已经在运行 try: import requests response = requests.get(BASE_URL, timeout=2) if response.status_code < 500: print(f"✓ 服务器已在运行: {BASE_URL}") yield BASE_URL return except: pass # 启动服务器 print(f"启动测试服务器: {BASE_URL}") project_root = Path(__file__).parent.parent process = subprocess.Popen( [ "uvicorn", "app.main:app", "--host", TEST_SERVER_HOST, "--port", str(TEST_SERVER_PORT), ], cwd=project_root, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) # 等待服务器启动 max_retries = 30 for i in range(max_retries): try: import requests response = requests.get(BASE_URL, timeout=2) if response.status_code < 500: print(f"✓ 服务器启动成功 (尝试 {i+1}/{max_retries})") break except: pass time.sleep(1) else: process.kill() raise RuntimeError(f"无法启动测试服务器: {BASE_URL}") yield BASE_URL # 清理 print("停止测试服务器") process.terminate() try: process.wait(timeout=5) except subprocess.TimeoutExpired: process.kill() @pytest.fixture def browser_context_args(browser_context_args): """配置浏览器上下文""" return { **browser_context_args, "viewport": {"width": 1920, "height": 1080}, "locale": "zh-CN", } @pytest.fixture def page(page: Page, test_server): """ 配置页面并导航到首页 自动导航到测试服务器的首页,并等待页面加载完成。 """ page.goto(test_server) page.wait_for_load_state("networkidle") # 设置默认超时 page.set_default_timeout(10000) # 10 秒 yield page # 测试失败时截图 if page.context.browser.is_connected(): try: screenshots_dir = Path(__file__).parent / "screenshots" screenshots_dir.mkdir(exist_ok=True) test_name = os.environ.get("PYTEST_CURRENT_TEST", "unknown").split(":")[-1].split(" ")[0] screenshot_path = screenshots_dir / f"{test_name}.png" page.screenshot(path=str(screenshot_path)) print(f"截图保存至: {screenshot_path}") except Exception as e: print(f"截图失败: {e}") @pytest.fixture def clean_rules(page: Page): """ 清除所有规则的 fixture 在测试前清除所有现有的正则规则,确保测试从干净状态开始。 """ # 清除所有规则 while page.locator(".rule-row").count() > 0: try: remove_btn = page.locator(".rule-row button[title='删除此规则']").first remove_btn.click() page.wait_for_timeout(50) except: break # 如果没有更多规则可删除 yield # 测试后不清理,让下一个测试自己清理 # 这样可以在浏览器中查看测试结果 @pytest.fixture def sample_rules(): """提供示例规则数据""" return [ {"pattern": r"/old/path/", "replacement": r"/new/path/"}, {"pattern": r"C:\\Music", "replacement": r"D:\\Audio"}, {"pattern": r"\\\\nas\\share", "replacement": r"Z:"}, ] @pytest.fixture def nas_conversion_rules(): """提供 NAS 路径转换规则""" return [ {"pattern": r"\\\\koha9-nas\\koha9-nas\\Music", "replacement": r"N:\\Music"}, {"pattern": r"/music/cache/", "replacement": r"/data/music/"}, {"pattern": r"\\", "replacement": r"/"}, ]