f14002559e
refactor(components): 调整头部和页脚布局样式 style(hero-section): 更新徽章动画效果 docs: 添加测试框架README文档 test: 实现首页、导航和联系表单的测试用例 ci: 添加CI测试脚本和配置
271 lines
8.1 KiB
Python
271 lines
8.1 KiB
Python
"""
|
|
浏览器配置模块
|
|
提供跨浏览器测试的配置和工具函数
|
|
"""
|
|
|
|
from typing import Dict, List, Optional, Tuple
|
|
from dataclasses import dataclass
|
|
from enum import Enum
|
|
|
|
from playwright.sync_api import Browser, BrowserType, BrowserContext, Page
|
|
from playwright.sync_api import Error as PlaywrightError
|
|
from playwright.sync_api import sync_playwright
|
|
|
|
from config.settings import get_settings
|
|
|
|
|
|
class BrowserTypeEnum(Enum):
|
|
"""支持的浏览器类型"""
|
|
CHROMIUM = "chromium"
|
|
FIREFOX = "firefox"
|
|
WEBKIT = "webkit"
|
|
|
|
|
|
@dataclass
|
|
class BrowserCapabilities:
|
|
"""浏览器能力描述"""
|
|
name: str
|
|
display_name: str
|
|
channel: Optional[str]
|
|
is_headless_supported: bool
|
|
default_viewport: tuple
|
|
user_agent: str
|
|
description: str
|
|
|
|
|
|
class BrowserConfigManager:
|
|
"""浏览器配置管理器"""
|
|
|
|
# 浏览器能力定义
|
|
BROWSER_CAPABILITIES: Dict[str, BrowserCapabilities] = {
|
|
"chromium": BrowserCapabilities(
|
|
name="chromium",
|
|
display_name="Chrome/Chromium",
|
|
channel="chrome",
|
|
is_headless_supported=True,
|
|
default_viewport=(1920, 1080),
|
|
user_agent=(
|
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
|
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
|
),
|
|
description="Google Chrome / Chromium浏览器"
|
|
),
|
|
"firefox": BrowserCapabilities(
|
|
name="firefox",
|
|
display_name="Firefox",
|
|
channel=None,
|
|
is_headless_supported=True,
|
|
default_viewport=(1920, 1080),
|
|
user_agent=(
|
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) "
|
|
"Gecko/20100101 Firefox/121.0"
|
|
),
|
|
description="Mozilla Firefox浏览器"
|
|
),
|
|
"webkit": BrowserCapabilities(
|
|
name="webkit",
|
|
display_name="WebKit (Safari)",
|
|
channel=None,
|
|
is_headless_supported=True,
|
|
default_viewport=(1920, 1080),
|
|
user_agent=(
|
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 14_2) "
|
|
"AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15"
|
|
),
|
|
description="Apple WebKit (Safari)浏览器"
|
|
)
|
|
}
|
|
|
|
def __init__(self):
|
|
self.settings = get_settings()
|
|
self._playwright: Optional[sync_playwright] = None
|
|
self._browser: Optional[Browser] = None
|
|
self._context: Optional[BrowserContext] = None
|
|
|
|
def _ensure_playwright(self) -> sync_playwright:
|
|
"""确保Playwright实例已启动"""
|
|
if self._playwright is None:
|
|
self._playwright = sync_playwright().start()
|
|
return self._playwright
|
|
|
|
def get_available_browsers(self) -> List[str]:
|
|
"""获取可用的浏览器列表"""
|
|
available = []
|
|
p = self._ensure_playwright()
|
|
|
|
browser_map = {
|
|
"chromium": p.chromium,
|
|
"firefox": p.firefox,
|
|
"webkit": p.webkit
|
|
}
|
|
|
|
for name in browser_map:
|
|
try:
|
|
available.append(name)
|
|
except Exception:
|
|
continue
|
|
|
|
return available
|
|
|
|
def _get_browser_type(self, browser_name: str):
|
|
"""获取浏览器类型"""
|
|
p = self._ensure_playwright()
|
|
browser_map = {
|
|
"chromium": p.chromium,
|
|
"firefox": p.firefox,
|
|
"webkit": p.webkit
|
|
}
|
|
return browser_map.get(browser_name)
|
|
|
|
def launch_browser(
|
|
self,
|
|
browser_name: str = "chromium",
|
|
headless: bool = False,
|
|
viewport: Optional[Tuple[int, int]] = None,
|
|
**kwargs
|
|
) -> Browser:
|
|
"""启动浏览器"""
|
|
capabilities = self.BROWSER_CAPABILITIES.get(browser_name)
|
|
if not capabilities:
|
|
raise ValueError(f"不支持的浏览器类型: {browser_name}")
|
|
|
|
viewport = viewport or (self.settings.viewport_width, self.settings.viewport_height)
|
|
|
|
launch_args = self._get_launch_arguments(browser_name, headless)
|
|
|
|
browser_type = self._get_browser_type(browser_name)
|
|
if not browser_type:
|
|
raise ValueError(f"不支持的浏览器类型: {browser_name}")
|
|
|
|
self._browser = browser_type.launch(
|
|
headless=headless,
|
|
args=launch_args,
|
|
**kwargs
|
|
)
|
|
|
|
return self._browser
|
|
|
|
def _get_launch_arguments(self, browser_name: str, headless: bool) -> List[str]:
|
|
"""获取浏览器启动参数"""
|
|
args = []
|
|
|
|
if browser_name == "chromium":
|
|
args.extend([
|
|
"--disable-extensions",
|
|
"--disable-background-networking",
|
|
"--disable-sync",
|
|
"--disable-translate",
|
|
"--metrics-recording-only",
|
|
"--mute-audio",
|
|
"--no-first-run",
|
|
"--safebrowsing-disable-auto-update",
|
|
"--ignore-certificate-errors",
|
|
"--ignore-ssl-errors",
|
|
"--disable-dev-shm-usage",
|
|
])
|
|
|
|
if headless:
|
|
args.extend([
|
|
"--headless=new",
|
|
"--disable-gpu",
|
|
"--no-sandbox",
|
|
])
|
|
|
|
elif browser_name == "firefox":
|
|
if headless:
|
|
args.extend(["-headless"])
|
|
|
|
args.extend([
|
|
"-profile",
|
|
"/tmp/firefox-profile",
|
|
])
|
|
|
|
elif browser_name == "webkit":
|
|
if headless:
|
|
args.append("--headless")
|
|
|
|
args.extend([
|
|
"--no-sandbox",
|
|
"--disable-setuid-sandbox",
|
|
])
|
|
|
|
return args
|
|
|
|
def create_context(
|
|
self,
|
|
browser: Browser,
|
|
viewport: Optional[Tuple[int, int]] = None,
|
|
**context_kwargs
|
|
) -> BrowserContext:
|
|
"""创建浏览器上下文"""
|
|
viewport = viewport or (self.settings.viewport_width, self.settings.viewport_height)
|
|
|
|
capabilities = self.BROWSER_CAPABILITIES.get(
|
|
browser.browser_type.name,
|
|
self.BROWSER_CAPABILITIES["chromium"]
|
|
)
|
|
|
|
context_options = {
|
|
"viewport": {
|
|
"width": viewport[0],
|
|
"height": viewport[1]
|
|
},
|
|
"user_agent": capabilities.user_agent,
|
|
"locale": "zh-CN",
|
|
"timezone_id": "Asia/Shanghai",
|
|
**context_kwargs
|
|
}
|
|
|
|
self._context = browser.new_context(**context_options)
|
|
|
|
return self._context
|
|
|
|
def create_browser_session(
|
|
self,
|
|
browser_name: str = "chromium",
|
|
headless: bool = False,
|
|
viewport: Optional[Tuple[int, int]] = None
|
|
) -> Tuple[Browser, BrowserContext, Page]:
|
|
"""创建完整的浏览器会话"""
|
|
browser = self.launch_browser(browser_name, headless, viewport)
|
|
context = self.create_context(browser, viewport)
|
|
page = context.new_page()
|
|
|
|
return browser, context, page
|
|
|
|
def close_browser(self) -> None:
|
|
"""关闭浏览器和上下文"""
|
|
if self._context:
|
|
try:
|
|
self._context.close()
|
|
except Exception:
|
|
pass
|
|
self._context = None
|
|
|
|
if self._browser:
|
|
try:
|
|
self._browser.close()
|
|
except Exception:
|
|
pass
|
|
self._browser = None
|
|
|
|
if self._playwright:
|
|
try:
|
|
self._playwright.stop()
|
|
except Exception:
|
|
pass
|
|
self._playwright = None
|
|
|
|
def __enter__(self) -> 'BrowserConfigManager':
|
|
"""上下文管理器入口"""
|
|
return self
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
|
"""上下文管理器退出"""
|
|
self.close_browser()
|
|
|
|
|
|
def get_browser_factory() -> BrowserConfigManager:
|
|
"""获取浏览器工厂实例"""
|
|
return BrowserConfigManager()
|