Files
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

851 lines
35 KiB
Python

#!/usr/bin/env python3
"""
玩Android Material Design无头浏览器测试框架
使用Selenium进行自动化测试
"""
import os
import sys
import time
import json
import logging
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Tuple, Optional
# 尝试导入Selenium
try:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.options import Options
from selenium.common.exceptions import TimeoutException, WebDriverException
SELENIUM_AVAILABLE = True
except ImportError:
SELENIUM_AVAILABLE = False
print("⚠️ Selenium未安装,将使用备用测试方法")
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('headless_test.log'),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
class HeadlessBrowserTester:
"""无头浏览器测试器"""
def __init__(self, base_url: str = "http://localhost:8000"):
self.base_url = base_url
self.driver = None
self.test_results = []
self.screenshots_dir = Path("test_screenshots")
self.screenshots_dir.mkdir(exist_ok=True)
def setup_driver(self):
"""设置无头浏览器驱动"""
if not SELENIUM_AVAILABLE:
logger.error("Selenium不可用,无法进行无头浏览器测试")
return False
try:
chrome_options = Options()
chrome_options.add_argument("--headless") # 无头模式
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--disable-dev-shm-usage")
chrome_options.add_argument("--disable-gpu")
chrome_options.add_argument("--window-size=1920,1080")
chrome_options.add_argument("--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
# 尝试使用系统Chrome
self.driver = webdriver.Chrome(options=chrome_options)
logger.info("✅ Chrome无头浏览器启动成功")
return True
except Exception as e:
logger.error(f"❌ 启动Chrome失败: {e}")
# 尝试使用Firefox
try:
from selenium.webdriver.firefox.options import Options as FirefoxOptions
firefox_options = FirefoxOptions()
firefox_options.add_argument("--headless")
self.driver = webdriver.Firefox(options=firefox_options)
logger.info("✅ Firefox无头浏览器启动成功")
return True
except Exception as e2:
logger.error(f"❌ 启动Firefox也失败: {e2}")
return False
def take_screenshot(self, name: str):
"""截取屏幕截图"""
if self.driver:
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = self.screenshots_dir / f"{name}_{timestamp}.png"
self.driver.save_screenshot(str(filename))
logger.info(f"📸 截图保存: {filename}")
return str(filename)
return None
def record_test_result(self, module: str, test_name: str, passed: bool,
details: str = "", screenshot: str = None):
"""记录测试结果"""
result = {
"module": module,
"test_name": test_name,
"passed": passed,
"timestamp": datetime.now().isoformat(),
"details": details,
"screenshot": screenshot
}
self.test_results.append(result)
status = "✅" if passed else "❌"
logger.info(f"{status} {module} - {test_name}: {details}")
return result
def test_homepage(self) -> Dict:
"""测试首页模块"""
module = "首页模块"
results = []
try:
# 访问首页
self.driver.get(f"{self.base_url}/")
time.sleep(2)
# 截图
screenshot = self.take_screenshot("homepage")
# 检查页面标题
title = self.driver.title
if "玩Android" in title or "Material Design" in title:
results.append(self.record_test_result(
module, "页面标题", True, f"标题: {title}", screenshot
))
else:
results.append(self.record_test_result(
module, "页面标题", False, f"标题不符合预期: {title}", screenshot
))
# 检查Material Design元素
material_elements = [
("搜索栏", "search-container", By.CLASS_NAME),
("Banner区域", "banner", By.CLASS_NAME),
("文章列表", "article-list", By.CLASS_NAME),
("底部导航", "tab-bar", By.CLASS_NAME),
]
for element_name, selector, by_type in material_elements:
try:
element = self.driver.find_element(by_type, selector)
results.append(self.record_test_result(
module, f"{element_name}存在", True, f"找到{selector}"
))
# 检查样式
bg_color = element.value_of_css_property("background-color")
if bg_color:
results.append(self.record_test_result(
module, f"{element_name}样式", True, f"背景色: {bg_color}"
))
except Exception as e:
results.append(self.record_test_result(
module, f"{element_name}存在", False, f"未找到{selector}: {e}"
))
# 测试交互
try:
# 点击搜索栏
search_box = self.driver.find_element(By.CLASS_NAME, "search-container")
search_box.click()
time.sleep(1)
results.append(self.record_test_result(
module, "搜索栏点击", True, "搜索栏可点击"
))
except Exception as e:
results.append(self.record_test_result(
module, "搜索栏点击", False, f"搜索栏点击失败: {e}"
))
# 检查响应式
original_size = self.driver.get_window_size()
self.driver.set_window_size(375, 667) # 手机尺寸
time.sleep(1)
mobile_screenshot = self.take_screenshot("homepage_mobile")
results.append(self.record_test_result(
module, "响应式布局", True, "移动端适配正常", mobile_screenshot
))
# 恢复窗口大小
self.driver.set_window_size(original_size['width'], original_size['height'])
return {
"module": module,
"total_tests": len(results),
"passed": sum(1 for r in results if r["passed"]),
"failed": sum(1 for r in results if not r["passed"]),
"results": results
}
except Exception as e:
logger.error(f"❌ 首页测试失败: {e}")
return {
"module": module,
"total_tests": 0,
"passed": 0,
"failed": 1,
"error": str(e)
}
def test_material_preview(self) -> Dict:
"""测试Material Design预览模块"""
module = "Material预览模块"
results = []
try:
# 访问Material预览页面
self.driver.get(f"{self.base_url}/material_preview.html")
time.sleep(2)
# 截图
screenshot = self.take_screenshot("material_preview")
# 检查页面内容
page_text = self.driver.find_element(By.TAG_NAME, "body").text
material_keywords = ["Material Design", "颜色系统", "图标系统", "组件系统"]
for keyword in material_keywords:
if keyword in page_text:
results.append(self.record_test_result(
module, f"包含{keyword}", True, f"找到关键词: {keyword}"
))
else:
results.append(self.record_test_result(
module, f"包含{keyword}", False, f"未找到关键词: {keyword}"
))
# 检查颜色系统
try:
color_elements = self.driver.find_elements(By.CLASS_NAME, "color-swatch")
if len(color_elements) >= 3:
results.append(self.record_test_result(
module, "颜色系统", True, f"找到{len(color_elements)}个颜色样本"
))
else:
results.append(self.record_test_result(
module, "颜色系统", False, f"颜色样本不足: {len(color_elements)}个"
))
except Exception as e:
results.append(self.record_test_result(
module, "颜色系统", False, f"颜色系统检查失败: {e}"
))
# 检查图标系统
try:
icon_elements = self.driver.find_elements(By.CLASS_NAME, "material-icon")
if len(icon_elements) >= 5:
results.append(self.record_test_result(
module, "图标系统", True, f"找到{len(icon_elements)}个Material图标"
))
else:
results.append(self.record_test_result(
module, "图标系统", False, f"图标数量不足: {len(icon_elements)}个"
))
except Exception as e:
results.append(self.record_test_result(
module, "图标系统", False, f"图标系统检查失败: {e}"
))
return {
"module": module,
"total_tests": len(results),
"passed": sum(1 for r in results if r["passed"]),
"failed": sum(1 for r in results if not r["passed"]),
"results": results
}
except Exception as e:
logger.error(f"❌ Material预览测试失败: {e}")
return {
"module": module,
"total_tests": 0,
"passed": 0,
"failed": 1,
"error": str(e)
}
def test_api_endpoints(self) -> Dict:
"""测试API接口模块"""
module = "API接口模块"
results = []
try:
# 测试健康检查API
self.driver.get(f"{self.base_url}/health")
time.sleep(1)
# 检查JSON响应
page_text = self.driver.find_element(By.TAG_NAME, "pre").text
try:
health_data = json.loads(page_text)
if health_data.get("status") == "healthy":
results.append(self.record_test_result(
module, "健康检查API", True, "API返回健康状态"
))
else:
results.append(self.record_test_result(
module, "健康检查API", False, f"状态异常: {health_data.get('status')}"
))
except json.JSONDecodeError as e:
results.append(self.record_test_result(
module, "健康检查API", False, f"JSON解析失败: {e}"
))
# 测试Banner API
self.driver.get(f"{self.base_url}/api/banner/json")
time.sleep(1)
page_text = self.driver.find_element(By.TAG_NAME, "pre").text
try:
banner_data = json.loads(page_text)
if banner_data.get("errorCode") == 0 and "data" in banner_data:
results.append(self.record_test_result(
module, "Banner API", True, f"返回{len(banner_data['data'])}个Banner"
))
else:
results.append(self.record_test_result(
module, "Banner API", False, f"API格式错误: {banner_data}"
))
except Exception as e:
results.append(self.record_test_result(
module, "Banner API", False, f"Banner API测试失败: {e}"
))
# 测试文章列表API
self.driver.get(f"{self.base_url}/api/article/list/0/json")
time.sleep(1)
page_text = self.driver.find_element(By.TAG_NAME, "pre").text
try:
article_data = json.loads(page_text)
if article_data.get("errorCode") == 0 and "data" in article_data:
article_count = len(article_data["data"].get("datas", []))
results.append(self.record_test_result(
module, "文章列表API", True, f"返回{article_count}篇文章"
))
else:
results.append(self.record_test_result(
module, "文章列表API", False, f"API格式错误"
))
except Exception as e:
results.append(self.record_test_result(
module, "文章列表API", False, f"文章API测试失败: {e}"
))
return {
"module": module,
"total_tests": len(results),
"passed": sum(1 for r in results if r["passed"]),
"failed": sum(1 for r in results if not r["passed"]),
"results": results
}
except Exception as e:
logger.error(f"❌ API接口测试失败: {e}")
return {
"module": module,
"total_tests": 0,
"passed": 0,
"failed": 1,
"error": str(e)
}
def test_quick_test_page(self) -> Dict:
"""测试快速测试页面模块"""
module = "快速测试页面模块"
results = []
try:
# 访问快速测试页面
self.driver.get(f"{self.base_url}/quick_test.html")
time.sleep(2)
# 截图
screenshot = self.take_screenshot("quick_test_page")
# 检查页面结构
test_buttons = self.driver.find_elements(By.CLASS_NAME, "test-button")
if len(test_buttons) >= 4:
results.append(self.record_test_result(
module, "测试按钮", True, f"找到{len(test_buttons)}个测试按钮"
))
else:
results.append(self.record_test_result(
module, "测试按钮", False, f"测试按钮不足: {len(test_buttons)}个"
))
# 检查服务器信息显示
server_info_elements = self.driver.find_elements(By.CLASS_NAME, "info-item")
if len(server_info_elements) >= 4:
results.append(self.record_test_result(
module, "服务器信息", True, f"显示{len(server_info_elements)}项服务器信息"
))
else:
results.append(self.record_test_result(
module, "服务器信息", False, f"服务器信息不足: {len(server_info_elements)}项"
))
# 测试按钮点击(新标签页打开)
for i, button in enumerate(test_buttons[:2]): # 只测试前两个按钮
try:
button_text = button.text
button.click()
time.sleep(1)
# 切换到新标签页
if len(self.driver.window_handles) > 1:
self.driver.switch_to.window(self.driver.window_handles[1])
time.sleep(1)
# 检查新页面
new_page_title = self.driver.title
results.append(self.record_test_result(
module, f"按钮点击测试{i+1}", True,
f"按钮'{button_text}'打开页面: {new_page_title}"
))
# 关闭新标签页,返回原页面
self.driver.close()
self.driver.switch_to.window(self.driver.window_handles[0])
else:
results.append(self.record_test_result(
module, f"按钮点击测试{i+1}", False,
f"按钮'{button_text}'未打开新标签页"
))
except Exception as e:
results.append(self.record_test_result(
module, f"按钮点击测试{i+1}", False,
f"按钮点击测试失败: {e}"
))
return {
"module": module,
"total_tests": len(results),
"passed": sum(1 for r in results if r["passed"]),
"failed": sum(1 for r in results if not r["passed"]),
"results": results
}
except Exception as e:
logger.error(f"❌ 快速测试页面测试失败: {e}")
return {
"module": module,
"total_tests": 0,
"passed": 0,
"failed": 1,
"error": str(e)
}
def test_uniapp_pages(self) -> Dict:
"""测试Uniapp页面模块"""
module = "Uniapp页面模块"
results = []
try:
# 检查pages目录是否存在
pages_dir = Path("pages")
if pages_dir.exists() and pages_dir.is_dir():
results.append(self.record_test_result(
module, "pages目录", True, "Uniapp pages目录存在"
))
# 统计页面数量
page_folders = [d for d in pages_dir.iterdir() if d.is_dir()]
results.append(self.record_test_result(
module, "页面数量", True, f"找到{len(page_folders)}个页面目录"
))
# 检查关键页面文件
required_pages = ["index", "project", "official", "system", "mine"]
for page in required_pages:
page_file = pages_dir / page / f"{page}.vue"
if page_file.exists():
results.append(self.record_test_result(
module, f"{page}页面文件", True, f"{page}.vue文件存在"
))
else:
results.append(self.record_test_result(
module, f"{page}页面文件", False, f"{page}.vue文件不存在"
))
else:
results.append(self.record_test_result(
module, "pages目录", False, "Uniapp pages目录不存在"
))
# 检查静态资源
static_dir = Path("static")
if static_dir.exists():
# 检查图标
icons_dir = static_dir / "images" / "icons"
if icons_dir.exists():
icon_files = list(icons_dir.glob("*.png")) + list(icons_dir.glob("*.svg"))
results.append(self.record_test_result(
module, "图标资源", True, f"找到{len(icon_files)}个图标文件"
))
else:
results.append(self.record_test_result(
module, "图标资源", False, "图标目录不存在"
))
# 检查配置文件
config_files = ["manifest.json", "pages.json", "App.vue"]
for config_file in config_files:
if Path(config_file).exists():
results.append(self.record_test_result(
module, f"{config_file}文件", True, f"{config_file}存在"
))
else:
results.append(self.record_test_result(
module, f"{config_file}文件", False, f"{config_file}不存在"
))
return {
"module": module,
"total_tests": len(results),
"passed": sum(1 for r in results if r["passed"]),
"failed": sum(1 for r in results if not r["passed"]),
"results": results
}
except Exception as e:
logger.error(f"❌ Uniapp页面测试失败: {e}")
return {
"module": module,
"total_tests": 0,
"passed": 0,
"failed": 1,
"error": str(e)
}
def run_all_tests(self) -> Dict:
"""运行所有测试"""
logger.info("🚀 开始无头浏览器自动化测试")
logger.info(f"📡 测试地址: {self.base_url}")
if not self.setup_driver():
logger.error("❌ 无法启动无头浏览器,测试终止")
return {"error": "无法启动无头浏览器"}
all_results = []
try:
# 运行各个模块测试
test_modules = [
("首页模块", self.test_homepage),
("Material预览模块", self.test_material_preview),
("API接口模块", self.test_api_endpoints),
("快速测试页面模块", self.test_quick_test_page),
("Uniapp页面模块", self.test_uniapp_pages),
]
for module_name, test_func in test_modules:
logger.info(f"\n🔍 开始测试: {module_name}")
result = test_func()
all_results.append(result)
logger.info(f"📊 {module_name}测试完成: {result['passed']}/{result['total_tests']} 通过")
# 生成测试报告
report = self.generate_test_report(all_results)
return report
finally:
if self.driver:
self.driver.quit()
logger.info("🛑 无头浏览器已关闭")
def generate_test_report(self, all_results: List[Dict]) -> Dict:
"""生成测试报告"""
total_tests = sum(r.get("total_tests", 0) for r in all_results)
total_passed = sum(r.get("passed", 0) for r in all_results)
total_failed = sum(r.get("failed", 0) for r in all_results)
report = {
"timestamp": datetime.now().isoformat(),
"base_url": self.base_url,
"total_modules": len(all_results),
"total_tests": total_tests,
"total_passed": total_passed,
"total_failed": total_failed,
"success_rate": (total_passed / total_tests * 100) if total_tests > 0 else 0,
"modules": all_results,
"summary": self._generate_summary(all_results)
}
# 保存报告到文件
report_file = "headless_test_report.json"
with open(report_file, "w", encoding="utf-8") as f:
json.dump(report, f, ensure_ascii=False, indent=2)
# 生成HTML报告
self._generate_html_report(report)
logger.info(f"\n🎯 测试完成!")
logger.info(f"📊 总体结果: {total_passed}/{total_tests} 通过 ({report['success_rate']:.1f}%)")
logger.info(f"📄 详细报告: {report_file}")
logger.info(f"📄 HTML报告: headless_test_report.html")
return report
def _generate_summary(self, all_results: List[Dict]) -> str:
"""生成测试摘要"""
summary_lines = []
for module_result in all_results:
module_name = module_result.get("module", "未知模块")
passed = module_result.get("passed", 0)
total = module_result.get("total_tests", 0)
if total > 0:
percentage = (passed / total) * 100
status = "✅" if percentage >= 80 else "⚠️" if percentage >= 50 else "❌"
summary_lines.append(f"{status} {module_name}: {passed}/{total} ({percentage:.1f}%)")
else:
summary_lines.append(f"❌ {module_name}: 0/0 (无测试)")
return "\n".join(summary_lines)
def _generate_html_report(self, report: Dict):
"""生成HTML测试报告"""
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<title>无头浏览器测试报告</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
body {{ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: #f5f5f5; color: #333; line-height: 1.6; padding: 20px; }}
.container {{ max-width: 1200px; margin: 0 auto; }}
.header {{ background: linear-gradient(135deg, #6200EE 0%, #3700B3 100%); color: white; padding: 40px; border-radius: 15px; margin-bottom: 30px; text-align: center; }}
.header h1 {{ font-size: 36px; margin-bottom: 10px; }}
.header .subtitle {{ font-size: 18px; opacity: 0.9; }}
.summary-card {{ background: white; border-radius: 15px; padding: 30px; margin-bottom: 30px; box-shadow: 0 5px 15px rgba(0,0,0,0.1); }}
.summary-grid {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin-top: 20px; }}
.summary-item {{ text-align: center; padding: 20px; border-radius: 10px; }}
.summary-item.total {{ background: #e3f2fd; }}
.summary-item.passed {{ background: #e8f5e9; }}
.summary-item.failed {{ background: #ffebee; }}
.summary-item.rate {{ background: #fff3e0; }}
.summary-value {{ font-size: 32px; font-weight: bold; margin-bottom: 5px; }}
.summary-label {{ font-size: 14px; color: #666; }}
.module-section {{ margin-bottom: 30px; }}
.module-header {{ background: #6200EE; color: white; padding: 15px 20px; border-radius: 10px 10px 0 0; display: flex; justify-content: space-between; align-items: center; }}
.module-title {{ font-size: 20px; font-weight: 500; }}
.module-stats {{ font-size: 16px; }}
.test-results {{ background: white; border-radius: 0 0 10px 10px; padding: 20px; box-shadow: 0 3px 10px rgba(0,0,0,0.1); }}
.test-item {{ padding: 15px; border-bottom: 1px solid #eee; display: flex; align-items: center; }}
.test-item:last-child {{ border-bottom: none; }}
.test-status {{ font-size: 24px; margin-right: 15px; width: 40px; text-align: center; }}
.test-details {{ flex: 1; }}
.test-name {{ font-weight: 500; margin-bottom: 5px; }}
.test-info {{ font-size: 14px; color: #666; }}
.screenshot-grid {{ display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px; margin-top: 20px; }}
.screenshot-item {{ border: 1px solid #ddd; border-radius: 8px; overflow: hidden; }}
.screenshot-item img {{ width: 100%; height: 200px; object-fit: cover; }}
.screenshot-caption {{ padding: 10px; background: #f9f9f9; font-size: 14px; }}
.footer {{ text-align: center; margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd; color: #666; font-size: 14px; }}
@media (max-width: 768px) {{
.summary-grid {{ grid-template-columns: 1fr; }}
.screenshot-grid {{ grid-template-columns: 1fr; }}
}}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🎯 无头浏览器测试报告</h1>
<div class="subtitle">玩Android Material Design开发服务器</div>
<div class="subtitle">测试时间: {report['timestamp']}</div>
</div>
<div class="summary-card">
<h2>📊 测试概览</h2>
<div class="summary-grid">
<div class="summary-item total">
<div class="summary-value">{report['total_modules']}</div>
<div class="summary-label">测试模块</div>
</div>
<div class="summary-item total">
<div class="summary-value">{report['total_tests']}</div>
<div class="summary-label">总测试数</div>
</div>
<div class="summary-item passed">
<div class="summary-value">{report['total_passed']}</div>
<div class="summary-label">通过测试</div>
</div>
<div class="summary-item failed">
<div class="summary-value">{report['total_failed']}</div>
<div class="summary-label">失败测试</div>
</div>
<div class="summary-item rate">
<div class="summary-value">{report['success_rate']:.1f}%</div>
<div class="summary-label">通过率</div>
</div>
</div>
</div>
<div class="summary-card">
<h2>🔍 测试服务器信息</h2>
<div style="margin-top: 15px;">
<p><strong>测试地址:</strong> {report['base_url']}</p>
<p><strong>测试时间:</strong> {report['timestamp']}</p>
<p><strong>测试摘要:</strong></p>
<pre style="background: #f5f5f5; padding: 15px; border-radius: 8px; margin-top: 10px; white-space: pre-wrap;">{report['summary']}</pre>
</div>
</div>
<h2 style="margin: 30px 0 20px 0;">📋 详细测试结果</h2>
{self._generate_module_html(report['modules'])}
{self._generate_screenshots_html()}
<div class="footer">
<p>© 2026 玩Android Material Design测试报告 | 生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
<p>测试框架: Selenium无头浏览器自动化测试</p>
</div>
</div>
</body>
</html>
"""
with open("headless_test_report.html", "w", encoding="utf-8") as f:
f.write(html_content)
def _generate_module_html(self, modules: List[Dict]) -> str:
"""生成模块测试结果的HTML"""
html_parts = []
for module in modules:
module_name = module.get("module", "未知模块")
passed = module.get("passed", 0)
total = module.get("total_tests", 0)
results = module.get("results", [])
# 计算通过率
percentage = (passed / total * 100) if total > 0 else 0
# 模块头部
html_parts.append(f"""
<div class="module-section">
<div class="module-header">
<div class="module-title">{module_name}</div>
<div class="module-stats">{passed}/{total} 通过 ({percentage:.1f}%)</div>
</div>
<div class="test-results">
""")
# 测试结果
for result in results:
status = "✅" if result.get("passed", False) else "❌"
test_name = result.get("test_name", "未知测试")
details = result.get("details", "")
html_parts.append(f"""
<div class="test-item">
<div class="test-status">{status}</div>
<div class="test-details">
<div class="test-name">{test_name}</div>
<div class="test-info">{details}</div>
</div>
</div>
""")
html_parts.append("""
</div>
</div>
""")
return "\n".join(html_parts)
def _generate_screenshots_html(self) -> str:
"""生成截图展示的HTML"""
screenshot_files = list(self.screenshots_dir.glob("*.png"))
if not screenshot_files:
return ""
html_parts = ['<h2 style="margin: 30px 0 20px 0;">📸 测试截图</h2><div class="screenshot-grid">']
for screenshot_file in screenshot_files[:6]: # 最多显示6张截图
filename = screenshot_file.name
html_parts.append(f"""
<div class="screenshot-item">
<img src="{screenshot_file}" alt="{filename}">
<div class="screenshot-caption">{filename}</div>
</div>
""")
html_parts.append('</div>')
return "\n".join(html_parts)
def main():
"""主函数"""
print("🚀 玩Android Material Design无头浏览器测试框架")
print("="*60)
# 检查Selenium是否可用
if not SELENIUM_AVAILABLE:
print("❌ Selenium未安装,无法进行无头浏览器测试")
print("💡 请安装: pip install selenium")
return
# 创建测试器
tester = HeadlessBrowserTester(base_url="http://localhost:8000")
# 运行测试
report = tester.run_all_tests()
# 输出结果
if "error" in report:
print(f"❌ 测试失败: {report['error']}")
sys.exit(1)
else:
print(f"\n🎯 测试完成!")
print(f"📊 总体结果: {report['total_passed']}/{report['total_tests']} 通过 ({report['success_rate']:.1f}%)")
print(f"📄 JSON报告: headless_test_report.json")
print(f"📄 HTML报告: headless_test_report.html")
print(f"📸 截图目录: test_screenshots/")
if report['success_rate'] >= 80:
print("✅ 测试通过!")
sys.exit(0)
elif report['success_rate'] >= 50:
print("⚠️ 测试部分通过,需要改进")
sys.exit(1)
else:
print("❌ 测试失败!")
sys.exit(1)
if __name__ == "__main__":
main()