#!/usr/bin/env python3 """ 本地测试驱动运行器 (Local TDD Test Runner) 与各 PHP 单元测试断言 100% 对齐,提供极速本地验证反馈 """ import os import re import sys import unittest BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) if BASE_DIR not in sys.path: sys.path.insert(0, BASE_DIR) def _read(path): """读取文本文件;文件不存在时返回空字符串。""" if not os.path.exists(path): return "" with open(path, encoding="utf-8") as f: return f.read() class TestResult: def __init__(self, name): self.name = name self.passed = 0 self.failed = 0 self.messages = [] def assert_true(self, condition, message): if condition: self.passed += 1 self.messages.append(f" [PASS] {message}") else: self.failed += 1 self.messages.append(f" [FAIL] {message}") def summary(self): print(f"\n=== {self.name} 测试结果: {self.passed} 通过, {self.failed} 失败 ===") for msg in self.messages: print(msg) return self.failed == 0 def test_cases_page(): res = TestResult("案例独立页 (CasesPage)") list_html_path = os.path.join( BASE_DIR, "addons", "official", "view", "web", "official_case_show", "list.html" ) list_e_html_path = os.path.join( BASE_DIR, "addons", "official", "view", "web", "official_case_show", "list_e.html", ) css_path = os.path.join( BASE_DIR, "public", "assets", "addons", "official", "css", "modern_nhc.css" ) html = _read(list_html_path) if os.path.exists(list_html_path) else "" mobile_html = _read(list_e_html_path) if os.path.exists(list_e_html_path) else "" css = _read(css_path) if os.path.exists(css_path) else "" res.assert_true("纳海川" in html, "PC 案例页必须明确包含品牌名称【纳海川】") res.assert_true( not re.search(r"纳川(?!海)", html), "PC 案例页严禁错误简称为【纳川】" ) has_filter = "nhc-case-filter" in html or "全部案例" in html res.assert_true(has_filter, "PC 案例页必须包含【筛选】板块") has_chal = "遇到的问题" in html or "业务挑战" in html or "CHALLENGE" in html has_sol = any(label in html for label in ("我们的做法", "技术方案", "实施方案", "SOLUTION")) has_res = any(label in html for label in ("交付结果", "成果验证", "交付内容", "RESULT")) res.assert_true( has_chal and has_sol and has_res, "PC 案例页必须包含【挑战、方案、结果】档案" ) res.assert_true( "nhc-case-metrics" in html or "数据验证" in html, "PC 案例页必须包含【数据】板块", ) has_metrics = "nhc-case-metrics" in html and ( "自动分单" in html or "断网断电仍可收银" in html or "动态 BOM" in html or "动态BOM" in html ) res.assert_true(has_metrics, "去AI味道:具备真实可信的落地业务与架构机制") has_emoji = bool( re.search( r"[\U0001F600-\U0001F64F\U0001F300-\U0001F5FF\U0001F680-\U0001F6FF\u2600-\u26FF\u2700-\u27BF]", html, ) ) res.assert_true(not has_emoji, "案例页严禁使用任何 emoji 表情符号") res.assert_true("#2563EB" in css or "#2563eb" in css, "沿用科技蓝 #2563EB 主色规范") res.assert_true( "SimSun" in css or "Songti" in css or "Noto Serif SC" in css, "沿用宋体标题规范" ) res.assert_true(bool(mobile_html), "移动端案例模板 list_e.html 必须存在") if mobile_html: res.assert_true("纳海川" in mobile_html, "移动端模板必须包含品牌名【纳海川】") res.assert_true( "nhc-case-filter" in mobile_html, "移动端模板必须包含多维案例筛选器" ) res.assert_true( "遇到的问题" in mobile_html or "CHALLENGE" in mobile_html or "业务挑战" in mobile_html, "移动端模板必须包含标杆案例挑战/方案/结果", ) res.assert_true( "nhc-case-metrics" in mobile_html, "移动端模板必须包含标杆案例量化指标" ) return res.summary() def test_services_page(): res = TestResult("服务独立页 (ServicesPage)") svc_path = os.path.join( BASE_DIR, "addons", "official", "view", "web", "official_product", "service_page.html", ) list_path = os.path.join( BASE_DIR, "addons", "official", "view", "web", "official_product", "list.html" ) html = "" if os.path.exists(svc_path): html += _read(svc_path) + "\n" if os.path.exists(list_path): html += _read(list_path) + "\n" res.assert_true("纳海川" in html, "服务页必须明确包含品牌名称【纳海川】") res.assert_true(not re.search(r"纳川(?!海)", html), "服务页严禁错误简称为【纳川】") res.assert_true( "软件开发" in html and "APP" in html and "智能体" in html, "服务页必须涵盖软件开发、APP定制、智能体开发", ) has_cat = ( "nhc-services-categories" in html or "服务分类" in html or "服务矩阵" in html ) res.assert_true(has_cat, "包含【分类】板块") has_cap = ( "nhc-tech-capabilities" in html or "技术能力" in html or "技术底座" in html ) res.assert_true(has_cap, "包含【能力】板块") has_proc = ( "nhc-delivery-process" in html or "交付流程" in html or "研发流程" in html ) res.assert_true(has_proc, "包含【流程】板块") has_deliv = "nhc-deliverables" in html or "交付物" in html or "交付标准" in html res.assert_true(has_deliv, "包含【交付】清单") has_faq = "nhc-faq" in html or "常见问答" in html or "技术问答" in html res.assert_true(has_faq, "包含【FAQ】板块") return res.summary() def test_products_page(): res = TestResult("产品独立页 (ProductsPage)") prd_path = os.path.join( BASE_DIR, "addons", "official", "view", "web", "official_product", "product_page.html", ) list_path = os.path.join( BASE_DIR, "addons", "official", "view", "web", "official_product", "list.html" ) html = "" if os.path.exists(prd_path): html += _read(prd_path) + "\n" if os.path.exists(list_path): html += _read(list_path) + "\n" res.assert_true("纳海川" in html, "产品页必须明确包含品牌名称【纳海川】") res.assert_true(not re.search(r"纳川(?!海)", html), "产品页严禁错误简称为【纳川】") res.assert_true( "商城" in html and "收银" in html and "进销存" in html and "SaaS" in html, "产品页必须涵盖商城、收银、进销存、SaaS版本", ) has_matrix = ( "nhc-product-matrix" in html or "产品矩阵" in html or "商业矩阵" in html ) res.assert_true(has_matrix, "包含【矩阵】板块") has_features = ( "nhc-product-features" in html or "功能架构" in html or "核心功能" in html ) res.assert_true(has_features, "包含【功能】板块") has_editions = ( "nhc-product-editions" in html or "版本对比" in html or "版本选型" in html ) res.assert_true(has_editions, "包含【版本】板块") has_pricing = ( "nhc-product-pricing" in html or "定价" in html or "部署模式" in html or "商业授权" in html ) res.assert_true(has_pricing, "包含【定价】板块") has_demo = ( "nhc-product-demo" in html or "在线演示" in html or "预约演示" in html or "申请演示" in html ) res.assert_true(has_demo, "包含【演示】板块") return res.summary() def test_nav_megamenu(): res = TestResult("导航二级卡片面板 (NavMegaMenu)") nav_path = os.path.join( BASE_DIR, "addons", "official", "view", "web", "official_common", "pc", "nav.html", ) css_path = os.path.join( BASE_DIR, "public", "assets", "addons", "official", "css", "modern_nhc.css" ) html = _read(nav_path) if os.path.exists(nav_path) else "" css = _read(css_path) if os.path.exists(css_path) else "" res.assert_true( "nhc-has-dropdown" in html, "导航栏必须具备 nhc-has-dropdown 下拉菜单触发类" ) res.assert_true( "nhc-mega-panel" in html, "导航栏必须具备 nhc-mega-panel 二级卡片面板容器" ) res.assert_true( "软件开发" in html and "智能体开发" in html, "服务二级面板包含核心服务" ) res.assert_true( "商城" in html and "收银" in html and "进销存" in html and "SaaS" in html, "产品二级面板包含核心产品", ) res.assert_true(".nhc-mega-panel" in css, "CSS 包含 .nhc-mega-panel 卡片面板样式") res.assert_true(".nhc-has-dropdown:hover" in css, "CSS 包含鼠标悬停触发规则") return res.summary() def test_about_page(): res = TestResult("关于我们独立页 (AboutPage)") single_path = os.path.join( BASE_DIR, "addons", "official", "view", "web", "official_home", "single.html" ) single_e_path = os.path.join( BASE_DIR, "addons", "official", "view", "web", "official_home", "single_e.html" ) html = _read(single_path) if os.path.exists(single_path) else "" mobile_html = _read(single_e_path) if os.path.exists(single_e_path) else "" res.assert_true("纳海川" in html, "PC 关于我们页必须明确包含品牌名称【纳海川】") res.assert_true( not re.search(r"纳川(?!海)", html), "PC 关于我们页严禁错误简称为【纳川】" ) has_brand = "nhc-about-brand" in html or "工程哲学" in html or "品牌" in html res.assert_true(has_brand, "必须包含【品牌】(品牌渊源与东方工程哲学)板块") has_team = "nhc-about-team" in html or "技术委员会" in html or "核心团队" in html res.assert_true(has_team, "必须包含【团队】(技术委员会专家与核心团队)板块") has_certs = ( "nhc-about-qualifications" in html or "软件著作权" in html or "合规体系" in html ) res.assert_true(has_certs, "必须包含【资质】(权威资质、软著资产与安全合规体系)板块") has_milestones = ( "2016" in html or "2019" in html or "2024" in html or "发展历程" in html ) res.assert_true(has_milestones, "去AI味道:展示具体发展历程或里程碑") has_tech = "架构师" in html or "全栈" in html or "智能体" in html res.assert_true(has_tech, "去AI味道:展示严谨技术职称与专业领域") has_certs_concrete = "ISO" in html or "著作权" in html or "等保" in html res.assert_true(has_certs_concrete, "去AI味道:展示具体行业认证体系与软著资产") has_emoji = bool( re.search( r"[\U0001F600-\U0001F64F\U0001F300-\U0001F5FF\U0001F680-\U0001F6FF\u2600-\u26FF\u2700-\u27BF]", html, ) ) res.assert_true(not has_emoji, "严禁使用任何 emoji 表情符号") res.assert_true(bool(mobile_html), "移动端关于我们模板 single_e.html 必须存在") if mobile_html: res.assert_true("纳海川" in mobile_html, "移动端模板包含品牌名【纳海川】") res.assert_true( "团队" in mobile_html or "专家" in mobile_html, "移动端模板包含核心团队介绍" ) res.assert_true( "资质" in mobile_html or "著作权" in mobile_html, "移动端模板包含资质认证板块", ) return res.summary() def test_contact_page(): res = TestResult("联系我们独立页 (ContactPage)") contact_path = os.path.join( BASE_DIR, "addons", "official", "view", "web", "official_online_message", "list.html", ) contact_e_path = os.path.join( BASE_DIR, "addons", "official", "view", "web", "official_online_message", "list_e.html", ) html = _read(contact_path) if os.path.exists(contact_path) else "" mobile_html = _read(contact_e_path) if os.path.exists(contact_e_path) else "" res.assert_true("纳海川" in html, "PC 联系我们页必须明确包含品牌名称【纳海川】") res.assert_true( not re.search(r"纳川(?!海)", html), "PC 联系我们页严禁错误简称为【纳川】" ) has_form = "