#!/usr/bin/env python3 """ 简化版无头浏览器测试 使用requests和备用方法进行模块测试 """ import os import sys import time import json import requests import logging from datetime import datetime from pathlib import Path from typing import Dict, List, Tuple, Optional from urllib.parse import urljoin # 配置日志 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler('simple_headless_test.log'), logging.StreamHandler() ] ) logger = logging.getLogger(__name__) class SimpleHeadlessTester: """简化版无头测试器""" def __init__(self, base_url: str = "http://localhost:8000"): self.base_url = base_url self.session = requests.Session() self.session.headers.update({ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' }) self.test_results = [] self.test_data_dir = Path("test_data") self.test_data_dir.mkdir(exist_ok=True) def test_url(self, url: str, module: str, test_name: str, expected_status: int = 200, check_content: List[str] = None) -> Dict: """测试URL访问""" try: start_time = time.time() response = self.session.get(url, timeout=10) elapsed = (time.time() - start_time) * 1000 # 检查状态码 if response.status_code == expected_status: passed = True details = f"状态码: {response.status_code}, 响应时间: {elapsed:.1f}ms" # 检查内容 if check_content: content = response.text missing = [] for keyword in check_content: if keyword not in content: missing.append(keyword) passed = False if missing: details = f"缺少关键词: {', '.join(missing)}" else: details = f"包含所有关键词, 响应时间: {elapsed:.1f}ms" else: passed = False details = f"期望状态码 {expected_status}, 实际 {response.status_code}" # 记录结果 result = self.record_test_result(module, test_name, passed, details) # 保存响应内容 if not passed: self.save_test_data(f"{module}_{test_name}_failed.html", response.text) return result except Exception as e: details = f"请求失败: {e}" return self.record_test_result(module, test_name, False, details) def test_api_json(self, url: str, module: str, test_name: str, check_fields: List[str] = None, api_type: str = "wanandroid") -> Dict: """测试API JSON响应 Args: url: API地址 module: 模块名称 test_name: 测试名称 check_fields: 需要检查的字段 api_type: API类型,'wanandroid'或'health' """ try: response = self.session.get(url, timeout=10) if response.status_code == 200: try: data = response.json() # 检查必需字段 if check_fields: missing = [] for field in check_fields: if field not in data: missing.append(field) if missing: details = f"缺少字段: {', '.join(missing)}" return self.record_test_result(module, test_name, False, details) # 根据API类型检查格式 if api_type == "wanandroid": # 玩Android API格式 if 'errorCode' in data and data['errorCode'] == 0: details = f"API格式正确, errorCode: {data['errorCode']}" return self.record_test_result(module, test_name, True, details) else: details = f"API错误码: {data.get('errorCode', '未知')}" return self.record_test_result(module, test_name, False, details) elif api_type == "health": # 健康检查API格式 if 'status' in data and data['status'] == 'healthy': details = f"健康检查正常, 状态: {data['status']}" return self.record_test_result(module, test_name, True, details) else: details = f"健康状态异常: {data.get('status', '未知')}" return self.record_test_result(module, test_name, False, details) else: # 通用API检查 details = f"API响应正常, 状态码: {response.status_code}" return self.record_test_result(module, test_name, True, details) except json.JSONDecodeError as e: details = f"JSON解析失败: {e}" return self.record_test_result(module, test_name, False, details) else: details = f"HTTP状态码: {response.status_code}" return self.record_test_result(module, test_name, False, details) except Exception as e: details = f"API测试失败: {e}" return self.record_test_result(module, test_name, False, details) def record_test_result(self, module: str, test_name: str, passed: bool, details: str = "") -> Dict: """记录测试结果""" result = { "module": module, "test_name": test_name, "passed": passed, "timestamp": datetime.now().isoformat(), "details": details } self.test_results.append(result) status = "✅" if passed else "❌" logger.info(f"{status} {module} - {test_name}: {details}") return result def save_test_data(self, filename: str, content: str): """保存测试数据""" filepath = self.test_data_dir / filename with open(filepath, 'w', encoding='utf-8') as f: f.write(content) logger.debug(f"📄 测试数据保存: {filepath}") def test_module_homepage(self) -> Dict: """测试首页模块""" module = "首页模块" results = [] # 测试首页访问 result = self.test_url( urljoin(self.base_url, "/"), module, "首页访问", check_content=["玩Android", "Material Design", "文章列表"] ) results.append(result) # 测试Material Design元素 result = self.test_url( urljoin(self.base_url, "/"), module, "Material元素", check_content=["search-container", "banner", "tab-bar"] ) results.append(result) # 测试CSS样式 result = self.test_url( urljoin(self.base_url, "/"), module, "CSS样式", check_content=["6200EE", "03DAC6", "Roboto"] ) results.append(result) 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 } def test_module_material_preview(self) -> Dict: """测试Material预览模块""" module = "Material预览模块" results = [] # 测试Material预览页面 result = self.test_url( urljoin(self.base_url, "/material_preview.html"), module, "预览页面访问", check_content=["Material Design", "颜色系统", "图标系统", "组件系统"] ) results.append(result) # 测试颜色系统 result = self.test_url( urljoin(self.base_url, "/material_preview.html"), module, "颜色系统", check_content=["color-swatch", "primary-color", "secondary-color", "Color System"] ) results.append(result) # 测试响应式设计 result = self.test_url( urljoin(self.base_url, "/material_preview.html"), module, "响应式设计", check_content=["@media", "max-width"] ) results.append(result) 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 } def test_module_api(self) -> Dict: """测试API接口模块""" module = "API接口模块" results = [] # 测试健康检查API result = self.test_api_json( urljoin(self.base_url, "/health"), module, "健康检查API", check_fields=["status", "timestamp", "service"], api_type="health" ) results.append(result) # 测试Banner API result = self.test_api_json( urljoin(self.base_url, "/api/banner/json"), module, "Banner API", check_fields=["errorCode", "data"] ) results.append(result) # 测试文章列表API result = self.test_api_json( urljoin(self.base_url, "/api/article/list/0/json"), module, "文章列表API", check_fields=["errorCode", "data"] ) results.append(result) # 测试热词API result = self.test_api_json( urljoin(self.base_url, "/api/hotkey/json"), module, "热词API", check_fields=["errorCode", "data"] ) results.append(result) 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 } def test_module_quick_test(self) -> Dict: """测试快速测试页面模块""" module = "快速测试页面模块" results = [] # 测试快速测试页面 result = self.test_url( urljoin(self.base_url, "/quick_test.html"), module, "快速测试页面访问", check_content=["快速测试", "测试按钮", "服务器信息", "test-button", "按钮集合"] ) results.append(result) # 测试按钮功能 result = self.test_url( urljoin(self.base_url, "/quick_test.html"), module, "测试按钮", check_content=["test-button", "button-icon", "访问首页", "Material预览"] ) results.append(result) # 测试服务器信息显示 result = self.test_url( urljoin(self.base_url, "/quick_test.html"), module, "服务器信息", check_content=["服务器地址", "129.211.173.87", "运行时长", "测试状态"] ) results.append(result) 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 } def test_module_uniapp_structure(self) -> Dict: """测试Uniapp项目结构模块""" module = "Uniapp结构模块" results = [] # 检查项目文件结构 project_files = [ ("pages/", "目录存在", True), ("pages/index/index.vue", "首页文件", True), ("pages/project/project.vue", "项目页面", True), ("pages/official/official.vue", "公众号页面", True), ("pages/system/system.vue", "体系页面", True), ("pages/mine/mine.vue", "我的页面", True), ("static/images/icons/", "图标目录", True), ("manifest.json", "配置文件", True), ("pages.json", "路由配置", True), ("App.vue", "应用配置", True), ] for filepath, description, required in project_files: exists = Path(filepath).exists() passed = exists if required else True result = self.record_test_result( module, description, passed, f"{filepath} {'存在' if exists else '不存在'}" ) results.append(result) # 检查Material图标 icons_dir = Path("static/images/icons") if icons_dir.exists(): icon_count = len(list(icons_dir.glob("*"))) result = self.record_test_result( module, "Material图标", icon_count >= 5, f"找到{icon_count}个图标文件" ) results.append(result) 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 } def test_module_performance(self) -> Dict: """测试性能模块""" module = "性能测试模块" results = [] # 测试首页性能 start_time = time.time() try: response = self.session.get(urljoin(self.base_url, "/"), timeout=10) elapsed = (time.time() - start_time) * 1000 if response.status_code == 200: passed = elapsed < 1000 # 1秒内响应 result = self.record_test_result( module, "首页响应时间", passed, f"响应时间: {elapsed:.1f}ms" ) results.append(result) else: result = self.record_test_result( module, "首页响应时间", False, f"HTTP状态码: {response.status_code}" ) results.append(result) except Exception as e: result = self.record_test_result( module, "首页响应时间", False, f"请求失败: {e}" ) results.append(result) # 测试API性能 apis_to_test = [ ("/health", "健康检查API"), ("/api/banner/json", "Banner API"), ("/api/hotkey/json", "热词API"), ] for api_path, api_name in apis_to_test: start_time = time.time() try: response = self.session.get(urljoin(self.base_url, api_path), timeout=10) elapsed = (time.time() - start_time) * 1000 if response.status_code == 200: passed = elapsed < 500 # 500ms内响应 result = self.record_test_result( module, f"{api_name}响应时间", passed, f"响应时间: {elapsed:.1f}ms" ) results.append(result) else: result = self.record_test_result( module, f"{api_name}响应时间", False, f"HTTP状态码: {response.status_code}" ) results.append(result) except Exception as e: result = self.record_test_result( module, f"{api_name}响应时间", False, f"请求失败: {e}" ) results.append(result) 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 } def run_all_tests(self) -> Dict: """运行所有测试""" logger.info("🚀 开始简化版无头浏览器测试") logger.info(f"📡 测试地址: {self.base_url}") logger.info(f"⏰ 开始时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") all_results = [] # 运行各个模块测试 test_modules = [ ("首页模块", self.test_module_homepage), ("Material预览模块", self.test_module_material_preview), ("API接口模块", self.test_module_api), ("快速测试页面模块", self.test_module_quick_test), ("Uniapp结构模块", self.test_module_uniapp_structure), ("性能测试模块", self.test_module_performance), ] 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 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 = "simple_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报告: simple_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"""
{report['summary']}