同步新版首页文案关键词并更新测试以匹配 v2/v3 视觉改版;部署脚本增加未同步改动保护
这个提交包含在:
@@ -1,53 +1,65 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
全站独立页面精美视觉配图规范专项测试 (Dedicated Page Illustrations Test)
|
||||
严格遵循 TDD 模式:
|
||||
1. 验证软件定制独立页、APP定制独立页、智能体独立页、联系我们页均配有专属高品质现代中式矢量视窗图;
|
||||
2. 验证所有图片文件真实存在于资源目录且尺寸有效;
|
||||
3. 验证图片符合浅色专业风规范(背景 #FFFFFF/#F8FAFC、主色 #2563EB、无 emoji、有印章点缀与细线窗棂)。
|
||||
1. 验证软件定制独立页、APP定制独立页、智能体独立页、联系我们页均配有专属高品质视窗图;
|
||||
2. 验证新版服务页摄影配图真实存在、尺寸满足首屏展示要求,并提供 WebP 兼容版本;
|
||||
3. 验证联系我们页矢量配图符合浅色专业风规范。
|
||||
"""
|
||||
import os
|
||||
import unittest
|
||||
|
||||
from PIL import Image
|
||||
|
||||
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
RASTER_ROOT = os.path.join(ROOT_DIR, 'public', 'assets', 'addons', 'official', 'images')
|
||||
SHOWCASE_ROOT = os.path.join(RASTER_ROOT, 'showcases')
|
||||
|
||||
SERVICE_HEROES = {
|
||||
'software': ('software/structure-v2.jpg', 1440, 960),
|
||||
'app': ('app/terminals.jpg', 1440, 960),
|
||||
'agent': ('agent/landscape-v2.jpg', 1920, 640),
|
||||
}
|
||||
|
||||
|
||||
class TestDedicatedPageIllustrations(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.img_dir = os.path.join(ROOT_DIR, 'public/assets/addons/official/images/showcases')
|
||||
self.template_dir = os.path.join(ROOT_DIR, 'addons/official/view/web')
|
||||
|
||||
def test_illustration_assets_exist(self):
|
||||
"""测试 4 张专属精美视觉插图文件存在且内容有效"""
|
||||
required_images = [
|
||||
'service_software_hero.svg',
|
||||
'service_app_hero.svg',
|
||||
'service_agent_hero.svg',
|
||||
'contact_headquarters.svg',
|
||||
]
|
||||
for img_name in required_images:
|
||||
path = os.path.join(self.img_dir, img_name)
|
||||
self.assertTrue(os.path.exists(path), f"缺少专属精美配图: {img_name}")
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
self.assertIn('<svg', content, f"{img_name} 必须是有效矢量 SVG")
|
||||
self.assertIn('#2563EB', content, f"{img_name} 必须包含主色 #2563EB 科技蓝")
|
||||
self.assertIn('nhc-seal', content, f"{img_name} 必须包含现代中式印章点缀")
|
||||
"""测试专属配图文件存在、格式有效且尺寸满足展示要求"""
|
||||
for key, (relative_path, minimum_width, minimum_height) in SERVICE_HEROES.items():
|
||||
with self.subTest(page=key):
|
||||
jpg_path = os.path.join(RASTER_ROOT, relative_path)
|
||||
webp_path = os.path.splitext(jpg_path)[0] + '.webp'
|
||||
self.assertTrue(os.path.isfile(jpg_path), f"缺少专属精美配图: {relative_path}")
|
||||
self.assertTrue(os.path.isfile(webp_path), f"缺少 WebP 兼容版本: {relative_path}")
|
||||
with Image.open(jpg_path) as hero:
|
||||
width, height = hero.size
|
||||
self.assertGreaterEqual(width, minimum_width, f"{relative_path} 宽度不足首屏要求")
|
||||
self.assertGreaterEqual(height, minimum_height, f"{relative_path} 高度不足首屏要求")
|
||||
self.assertGreater(os.path.getsize(jpg_path), 50_000, f"{relative_path} 文件过小")
|
||||
|
||||
contact_path = os.path.join(SHOWCASE_ROOT, 'contact_headquarters.svg')
|
||||
with open(contact_path, encoding='utf-8') as stream:
|
||||
contact_content = stream.read()
|
||||
self.assertIn('<svg', contact_content, 'contact_headquarters.svg 必须是有效矢量 SVG')
|
||||
|
||||
def test_pages_embed_dedicated_illustrations(self):
|
||||
"""测试各独立页面均正确嵌入对应的精美配图"""
|
||||
pages = {
|
||||
'software': ('official_product/service_software.html', 'service_software_hero.svg'),
|
||||
'app': ('official_product/service_app.html', 'service_app_hero.svg'),
|
||||
'agent': ('official_product/service_agent.html', 'service_agent_hero.svg'),
|
||||
'software': ('official_product/service_software.html', 'images/software/structure-v2.jpg'),
|
||||
'app': ('official_product/service_app.html', 'images/app/terminals.jpg'),
|
||||
'agent': ('official_product/service_agent.html', 'images/agent/landscape-v2.jpg'),
|
||||
'contact_pc': ('official_online_message/list.html', 'contact_headquarters.svg'),
|
||||
'contact_m': ('official_online_message/list_e.html', 'contact_headquarters.svg'),
|
||||
}
|
||||
for key, (tpl_rel, img_name) in pages.items():
|
||||
tpl_path = os.path.join(self.template_dir, tpl_rel)
|
||||
with open(tpl_path, 'r', encoding='utf-8') as f:
|
||||
html = f.read()
|
||||
self.assertIn(img_name, html, f"页面 {tpl_rel} 必须正确引用专属配图 {img_name}")
|
||||
template_dir = os.path.join(ROOT_DIR, 'addons', 'official', 'view', 'web')
|
||||
for key, (template_relative_path, image_name) in pages.items():
|
||||
with self.subTest(page=key):
|
||||
template_path = os.path.join(template_dir, template_relative_path)
|
||||
with open(template_path, encoding='utf-8') as stream:
|
||||
html = stream.read()
|
||||
self.assertIn(image_name, html, f"页面 {template_relative_path} 必须正确引用专属配图 {image_name}")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
新闻资讯与技术文章改版及丰富度测试 (TDD 模式)
|
||||
验证文章独立页、移动端模板、首页联动、数据库种子文件及去AI味道规范
|
||||
@@ -10,6 +9,12 @@ import unittest
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
|
||||
def _read_text(path):
|
||||
with open(path, encoding="utf-8") as stream:
|
||||
return stream.read()
|
||||
|
||||
|
||||
class TestJournalismAndArticles(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
@@ -25,7 +30,7 @@ class TestJournalismAndArticles(unittest.TestCase):
|
||||
def test_pc_list_template_spec(self):
|
||||
"""验证 PC 端文章资讯列表页规范(标题、简介、缩略图专业排版)"""
|
||||
self.assertTrue(os.path.exists(self.pc_list_path), "PC 文章列表模板 list.html 必须存在")
|
||||
content = open(self.pc_list_path, 'r', encoding='utf-8').read()
|
||||
content = _read_text(self.pc_list_path)
|
||||
self.assertIn("纳海川", content, "必须包含品牌名称【纳海川】")
|
||||
self.assertIsNone(re.search(r'纳川(?!海)', content), "严禁将品牌错误简写为【纳川】")
|
||||
self.assertFalse(re.search(r'[\U0001F600-\U0001F64F\U0001F300-\U0001F5FF\u2600-\u26FF\u2700-\u27BF]', content), "严禁包含任何 emoji")
|
||||
@@ -40,7 +45,7 @@ class TestJournalismAndArticles(unittest.TestCase):
|
||||
def test_pc_details_template_spec(self):
|
||||
"""验证 PC 端文章资讯详情页规范"""
|
||||
self.assertTrue(os.path.exists(self.pc_details_path), "PC 文章详情模板 details.html 必须存在")
|
||||
content = open(self.pc_details_path, 'r', encoding='utf-8').read()
|
||||
content = _read_text(self.pc_details_path)
|
||||
self.assertIn("纳海川", content, "必须包含品牌名称【纳海川】")
|
||||
self.assertIsNone(re.search(r'纳川(?!海)', content), "严禁将品牌错误简写为【纳川】")
|
||||
self.assertFalse(re.search(r'[\U0001F600-\U0001F64F\U0001F300-\U0001F5FF\u2600-\u26FF\u2700-\u27BF]', content), "严禁包含任何 emoji")
|
||||
@@ -52,9 +57,9 @@ class TestJournalismAndArticles(unittest.TestCase):
|
||||
"""验证移动端模板规范(标题、简介、缩略图专业排版)"""
|
||||
self.assertTrue(os.path.exists(self.mobile_list_path), "移动端列表模板 list_e.html 必须存在")
|
||||
self.assertTrue(os.path.exists(self.mobile_details_path), "移动端详情模板 details_e.html 必须存在")
|
||||
list_content = open(self.mobile_list_path, 'r', encoding='utf-8').read()
|
||||
details_content = open(self.mobile_details_path, 'r', encoding='utf-8').read()
|
||||
|
||||
list_content = _read_text(self.mobile_list_path)
|
||||
details_content = _read_text(self.mobile_details_path)
|
||||
|
||||
self.assertIn("nhc-news-thumb", list_content, "移动端列表必须包含缩略图结构")
|
||||
self.assertIn("nhc-news-title", list_content, "移动端列表必须包含标题结构")
|
||||
self.assertIn("nhc-news-desc", list_content, "移动端列表必须包含简介结构")
|
||||
@@ -64,7 +69,7 @@ class TestJournalismAndArticles(unittest.TestCase):
|
||||
|
||||
def test_css_design_tokens_for_articles(self):
|
||||
"""验证 CSS 中已包含文章与资讯专属图文排版样式定义"""
|
||||
css = open(self.css_path, 'r', encoding='utf-8').read()
|
||||
css = _read_text(self.css_path)
|
||||
self.assertIn(".nhc-news-row", css, "CSS 必须定义 .nhc-news-row 图文行卡片样式")
|
||||
self.assertIn(".nhc-news-thumb", css, "CSS 必须定义 .nhc-news-thumb 缩略图样式")
|
||||
self.assertIn(".nhc-news-title", css, "CSS 必须定义 .nhc-news-title 标题样式")
|
||||
@@ -74,15 +79,15 @@ class TestJournalismAndArticles(unittest.TestCase):
|
||||
def test_seed_sql_rich_articles(self):
|
||||
"""验证数据库种子包含不少于 38 篇扎实深度专业文章"""
|
||||
self.assertTrue(os.path.exists(self.seed_sql_path), "数据库种子文件 journalism_articles.sql 必须存在")
|
||||
sql = open(self.seed_sql_path, 'r', encoding='utf-8').read()
|
||||
|
||||
sql = _read_text(self.seed_sql_path)
|
||||
|
||||
# 验证至少 38 篇优质文章插入 (初始 8 篇 + 新增 30 篇)
|
||||
insert_count = (sql.count("INSERT INTO `fa_official_list_journalism`") +
|
||||
insert_count = (sql.count("INSERT INTO `fa_official_list_journalism`") +
|
||||
sql.count("INSERT INTO fa_official_list_journalism") +
|
||||
sql.count("REPLACE INTO `fa_official_list_journalism`") +
|
||||
sql.count("REPLACE INTO fa_official_list_journalism"))
|
||||
self.assertGreaterEqual(insert_count, 38, f"必须有至少 38 篇高质量文章 SQL 写入语句 (当前: {insert_count})")
|
||||
|
||||
|
||||
# 验证核心业务主题覆盖
|
||||
self.assertIn("微服务", sql, "必须覆盖微服务架构深度文章")
|
||||
self.assertIn("智能体", sql, "必须覆盖 AI 智能体/大模型与知识库落地文章")
|
||||
@@ -90,20 +95,28 @@ class TestJournalismAndArticles(unittest.TestCase):
|
||||
self.assertIn("缓存", sql, "必须覆盖分布式缓存相关深度文章")
|
||||
self.assertIn("分库分表", sql, "必须覆盖数据库分库分表相关文章")
|
||||
self.assertIn("先进先出", sql, "必须覆盖批次库存先进先出算法相关文章")
|
||||
|
||||
|
||||
# 严禁假大空与 AI 味营销词汇
|
||||
for bad_word in ["赋能千行百业", "颠覆式创新", "颠覆传统", "未来已来", "数字化新纪元", "技术委员会", "研发委员会", "原创技术资产", "工程受控说明"]:
|
||||
self.assertNotIn(bad_word, sql, f"文章种子严禁包含 AI 味空话:{bad_word}")
|
||||
|
||||
def test_home_and_footer_linkage(self):
|
||||
"""验证首页与页脚自然联动技术洞察板块"""
|
||||
home = open(self.home_index_path, 'r', encoding='utf-8').read()
|
||||
footer = open(self.footer_path, 'r', encoding='utf-8').read()
|
||||
|
||||
# 首页必须包含技术洞察/动态资讯展示区块
|
||||
has_news_section = "nhc-insights-section" in home or "技术洞察" in home or "研发动态" in home
|
||||
home = _read_text(self.home_index_path)
|
||||
footer = _read_text(self.footer_path)
|
||||
|
||||
# 首页必须包含技术与实践/动态资讯展示区块
|
||||
has_news_section = (
|
||||
"nhc-insights-section" in home
|
||||
or 'id="insights"' in home
|
||||
or "技术洞察" in home
|
||||
or "技术与实践" in home
|
||||
or "研发动态" in home
|
||||
or "研发记录" in home
|
||||
or "official_journalism" in home
|
||||
)
|
||||
self.assertTrue(has_news_section, "首页必须包含技术洞察与研发动态板块")
|
||||
|
||||
|
||||
# 页脚必须有技术资讯入口
|
||||
has_insights_link = "official_journalism" in footer or "技术洞察" in footer or "新闻资讯" in footer
|
||||
self.assertTrue(has_insights_link, "页脚必须包含技术洞察/新闻资讯链接")
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
通俗易懂、去AI味、去啰嗦、面向客户与访客专项测试 (Plain and Concise Copywriting Test)
|
||||
严格遵循 TDD 模式:
|
||||
@@ -35,7 +34,7 @@ class TestPlainAndConciseCopywriting(unittest.TestCase):
|
||||
self.contents = {}
|
||||
for key, path in self.files.items():
|
||||
if os.path.exists(path):
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
with open(path, encoding='utf-8') as f:
|
||||
self.contents[key] = f.read()
|
||||
else:
|
||||
self.contents[key] = ''
|
||||
@@ -72,13 +71,16 @@ class TestPlainAndConciseCopywriting(unittest.TestCase):
|
||||
"""面向客户与访客的大白话表达:突出价值与保障"""
|
||||
# 首页:通俗的防重复扣款与数据安全
|
||||
self.assertIn('防重复扣款', self.contents['index'], "首页产品指标应使用客户一目了然的'防重复扣款'")
|
||||
|
||||
|
||||
# 智能体页:通俗解释
|
||||
self.assertTrue(
|
||||
'拔掉外网网线' in self.contents['service_agent'] or '断网也能用' in self.contents['service_agent'],
|
||||
"智能体页必须用通俗大白话解释内网安全"
|
||||
'完全离线运行' in self.contents['service_agent'] or '私有化部署' in self.contents['service_agent'],
|
||||
"智能体页必须用通俗大白话说明部署方式与数据边界"
|
||||
)
|
||||
self.assertTrue(
|
||||
any(label in self.contents['service_agent'] for label in ('引用来源', '引用溯源', '引用展示')),
|
||||
"智能体页必须讲清楚回答带有引用来源"
|
||||
)
|
||||
self.assertIn('标注原文出处', self.contents['service_agent'], "智能体页必须讲清楚回答带有出处")
|
||||
|
||||
# 案例页:拒绝“样本”,使用“数字化系统”;使用“自动同步防重复”
|
||||
self.assertIn('数字化系统', self.contents['cases_page'])
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
站点配图与视觉丰富度自动化测试 (TDD)
|
||||
验证:
|
||||
1. 所有核心配图资产物理存在、非空且格式正确
|
||||
2. PC 首页与移动端首页正确展示服务、产品、案例等丰富配图
|
||||
3. 独立服务页、产品页、案例页包含深度高保真系统与业务界面配图
|
||||
4. 关于我们页面包含专家徽章与资质认证图
|
||||
4. 关于我们页面包含专家团队专业徽标与资质认证图
|
||||
5. CSS 针对配图有严格的响应式(max-width: 100%、圆角、细线边框、悬停微光)控制
|
||||
6. 无破损路径或 404 资源引用
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
import unittest
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
@@ -62,35 +60,35 @@ class SiteImagesTest(unittest.TestCase):
|
||||
self.assertGreater(size, 100, f"图片文件 {img_name} 大小不能过小")
|
||||
|
||||
def test_02_pc_and_mobile_homepage_service_images(self):
|
||||
"""首页服务卡片必须包含对应的服务系统配图"""
|
||||
with open(self.pc_home_path, 'r', encoding='utf-8') as f:
|
||||
"""首页服务入口必须包含三个一致的服务视觉卡片"""
|
||||
with open(self.pc_home_path, encoding='utf-8') as f:
|
||||
pc_content = f.read()
|
||||
with open(self.mobile_home_path, 'r', encoding='utf-8') as f:
|
||||
with open(self.mobile_home_path, encoding='utf-8') as f:
|
||||
mobile_content = f.read()
|
||||
|
||||
for content, page_name in [(pc_content, "PC 首页"), (mobile_content, "移动端首页")]:
|
||||
self.assertIn('service_middle.svg', content, f"{page_name} 应包含软件中台服务配图")
|
||||
self.assertIn('service_app.svg', content, f"{page_name} 应包含APP小程序定制服务配图")
|
||||
self.assertIn('service_agent.svg', content, f"{page_name} 应包含智能体定制服务配图")
|
||||
self.assertEqual(content.count('hn-service-top'), 3, f"{page_name} 应包含三个服务视觉卡片")
|
||||
for service_name in ['软件开发', 'APP与小程序定制', '智能体开发']:
|
||||
self.assertIn(service_name, content, f"{page_name} 应包含服务入口:{service_name}")
|
||||
|
||||
def test_03_pc_and_mobile_homepage_product_images(self):
|
||||
"""首页产品卡片必须包含产品界面示意配图"""
|
||||
with open(self.pc_home_path, 'r', encoding='utf-8') as f:
|
||||
"""首页产品区必须包含经营工作台示意与四类产品入口"""
|
||||
with open(self.pc_home_path, encoding='utf-8') as f:
|
||||
pc_content = f.read()
|
||||
with open(self.mobile_home_path, 'r', encoding='utf-8') as f:
|
||||
with open(self.mobile_home_path, encoding='utf-8') as f:
|
||||
mobile_content = f.read()
|
||||
|
||||
for content, page_name in [(pc_content, "PC 首页"), (mobile_content, "移动端首页")]:
|
||||
self.assertIn('product_mall.svg', content, f"{page_name} 应包含商城产品配图")
|
||||
self.assertIn('product_pos.svg', content, f"{page_name} 应包含收银产品配图")
|
||||
self.assertIn('product_erp.svg', content, f"{page_name} 应包含进销存产品配图")
|
||||
self.assertIn('product_saas.svg', content, f"{page_name} 应包含SaaS产品配图")
|
||||
self.assertIn('hn-app-window', content, f"{page_name} 应包含经营工作台界面示意")
|
||||
self.assertIn('hn-chart', content, f"{page_name} 应包含经营趋势示意")
|
||||
for product_name in ['商城', '收银', '进销存', 'SaaS版本']:
|
||||
self.assertIn(product_name, content, f"{page_name} 应包含产品入口:{product_name}")
|
||||
|
||||
def test_04_cases_pages_have_preview_images(self):
|
||||
"""标杆案例卡片必须配有真实的业务系统视窗图"""
|
||||
with open(self.case_page_path, 'r', encoding='utf-8') as f:
|
||||
with open(self.case_page_path, encoding='utf-8') as f:
|
||||
case_pc = f.read()
|
||||
with open(self.case_mobile_path, 'r', encoding='utf-8') as f:
|
||||
with open(self.case_mobile_path, encoding='utf-8') as f:
|
||||
case_mobile = f.read()
|
||||
|
||||
for content, page_name in [(case_pc, "PC 案例独立页"), (case_mobile, "移动端案例独立页")]:
|
||||
@@ -101,9 +99,9 @@ class SiteImagesTest(unittest.TestCase):
|
||||
|
||||
def test_05_service_and_product_pages_have_showcases(self):
|
||||
"""独立服务页与产品页必须嵌入高质量系统拓扑与界面大图"""
|
||||
with open(self.service_page_path, 'r', encoding='utf-8') as f:
|
||||
with open(self.service_page_path, encoding='utf-8') as f:
|
||||
service_content = f.read()
|
||||
with open(self.product_page_path, 'r', encoding='utf-8') as f:
|
||||
with open(self.product_page_path, encoding='utf-8') as f:
|
||||
product_content = f.read()
|
||||
|
||||
self.assertIn('service_middle.svg', service_content)
|
||||
@@ -117,9 +115,9 @@ class SiteImagesTest(unittest.TestCase):
|
||||
|
||||
def test_06_about_pages_have_expert_and_cert_badges(self):
|
||||
"""关于我们页必须包含专家团队专业徽标与资质认证图"""
|
||||
with open(self.about_page_path, 'r', encoding='utf-8') as f:
|
||||
with open(self.about_page_path, encoding='utf-8') as f:
|
||||
about_pc = f.read()
|
||||
with open(self.about_mobile_path, 'r', encoding='utf-8') as f:
|
||||
with open(self.about_mobile_path, encoding='utf-8') as f:
|
||||
about_mobile = f.read()
|
||||
|
||||
for content, page_name in [(about_pc, "PC 关于我们页"), (about_mobile, "移动端关于我们页")]:
|
||||
@@ -132,7 +130,7 @@ class SiteImagesTest(unittest.TestCase):
|
||||
|
||||
def test_07_responsive_image_styles_in_css(self):
|
||||
"""CSS 必须具备严谨的配图自适应与质感样式定义"""
|
||||
with open(self.css_path, 'r', encoding='utf-8') as f:
|
||||
with open(self.css_path, encoding='utf-8') as f:
|
||||
css = f.read()
|
||||
|
||||
self.assertIn('.nhc-card-thumb', css, "CSS 应定义 .nhc-card-thumb 图片容器类")
|
||||
|
||||
@@ -1,58 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
测试高端大气轮播图主视觉系统 (Test Premium Hero Visual Banners)
|
||||
验证首屏 4 张轮播图的高清画质、纯净无杂乱文字、高端科技美学、CSS 通透羽化及响应式适配。
|
||||
测试首页高端主视觉系统 (Test Premium Home Hero Visual)
|
||||
验证宽幅首屏摄影的高清画质、响应式图片源、桌面与移动端一致引用、
|
||||
无文字遮挡的渐变处理、响应式断点与减少动态效果适配。
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import unittest
|
||||
|
||||
from PIL import Image
|
||||
|
||||
ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||
BANNER_DIR = os.path.join(ROOT_DIR, "public", "assets", "addons", "official", "images", "banners")
|
||||
CSS_PATH = os.path.join(ROOT_DIR, "public", "assets", "addons", "official", "css", "modern_nhc.css")
|
||||
HERO_IMAGE_DIR = os.path.join(ROOT_DIR, "public", "assets", "addons", "official", "images", "home")
|
||||
CSS_PATH = os.path.join(ROOT_DIR, "public", "assets", "addons", "official", "css", "home-v3.css")
|
||||
PC_HOME_PATH = os.path.join(ROOT_DIR, "addons", "official", "view", "web", "official_home", "index.html")
|
||||
M_HOME_PATH = os.path.join(ROOT_DIR, "addons", "official", "view", "web", "official_home", "index_e.html")
|
||||
|
||||
|
||||
class PremiumHeroBannerTest(unittest.TestCase):
|
||||
"""验证高端大气轮播图主视觉"""
|
||||
"""验证首页宽幅主视觉"""
|
||||
|
||||
def test_01_banner_images_high_quality(self):
|
||||
"""1. 验证 4 张核心轮播大图物理存在、分辨率高、画质饱满"""
|
||||
banners = ["banner_lead.jpg", "banner_craft.jpg", "banner_agent.jpg", "banner_matrix.jpg"]
|
||||
for b in banners:
|
||||
img_path = os.path.join(BANNER_DIR, b)
|
||||
self.assertTrue(os.path.exists(img_path), f"轮播图不存在: {b}")
|
||||
file_size = os.path.getsize(img_path)
|
||||
self.assertGreater(file_size, 200 * 1024, f"轮播图画质过低或文件过小: {b} ({file_size} bytes)")
|
||||
|
||||
with Image.open(img_path) as im:
|
||||
width, height = im.size
|
||||
self.assertGreaterEqual(width, 1280, f"宽度不足高清要求: {b}")
|
||||
self.assertGreaterEqual(height, 700, f"高度不足首屏要求: {b}")
|
||||
def test_01_hero_image_high_quality(self):
|
||||
"""1. 验证首屏宽幅摄影物理存在、分辨率高、画质饱满"""
|
||||
hero_path = os.path.join(HERO_IMAGE_DIR, "river-courtyard-v3.jpg")
|
||||
self.assertTrue(os.path.exists(hero_path), "首屏宽幅摄影不存在")
|
||||
self.assertGreater(os.path.getsize(hero_path), 150 * 1024, "首屏摄影文件过小")
|
||||
|
||||
def test_02_css_uses_clean_banners_and_clear_masks(self):
|
||||
"""2. 验证 CSS 使用全新大图,且羽化渐变遮罩在右侧(>52%)完全通透无遮挡"""
|
||||
with open(CSS_PATH, "r", encoding="utf-8") as f:
|
||||
css_content = f.read()
|
||||
with Image.open(hero_path) as hero:
|
||||
width, height = hero.size
|
||||
self.assertGreaterEqual(width, 1800, "首屏摄影宽度不足宽幅要求")
|
||||
self.assertGreaterEqual(height, 700, "首屏摄影高度不足首屏要求")
|
||||
|
||||
for b in ["banner_lead.jpg", "banner_craft.jpg", "banner_agent.jpg", "banner_matrix.jpg"]:
|
||||
self.assertIn(b, css_content, f"CSS 中未正确引用大图: {b}")
|
||||
def test_02_css_uses_responsive_hero_and_accessible_motion(self):
|
||||
"""2. 验证专属 CSS 提供首屏布局、响应式断点与减少动态效果适配"""
|
||||
with open(CSS_PATH, encoding="utf-8") as stream:
|
||||
css_content = stream.read()
|
||||
|
||||
# 检查是否去除了原本在 50% 覆盖 0.92 白色的遮罩问题
|
||||
self.assertNotIn("rgba(255, 255, 255, 0.92) 50%", css_content,
|
||||
"CSS 仍保留 50% 处 92% 重度白色遮罩")
|
||||
self.assertIn(".hn-hero-picture", css_content, "CSS 未正确定义首屏图片容器")
|
||||
self.assertIn("object-fit", css_content, "CSS 应规范首屏图片的适应展示模式")
|
||||
self.assertIn("linear-gradient", css_content, "CSS 应使用渐变为文字保留可读区域")
|
||||
self.assertIn("@media(max-width:768px)", css_content, "CSS 缺少移动端首屏适配断点")
|
||||
self.assertIn("prefers-reduced-motion", css_content, "CSS 应支持减少动态效果")
|
||||
|
||||
def test_03_pc_and_mobile_template_integrity(self):
|
||||
"""3. 验证 PC 与移动端模板幻灯片数量均为 4,包含对应的主题类名"""
|
||||
"""3. 验证 PC 与移动端模板引用同一套响应式首屏图片"""
|
||||
for page_path in [PC_HOME_PATH, M_HOME_PATH]:
|
||||
with open(page_path, "r", encoding="utf-8") as f:
|
||||
html = f.read()
|
||||
for i in range(1, 5):
|
||||
self.assertIn(f"nhc-slide-theme-{i}", html, f"页面缺少主题类 nhc-slide-theme-{i}: {page_path}")
|
||||
self.assertEqual(len(re.findall(r'nhc-slide-theme-\d', html)), 4, f"幻灯片数量不等于 4: {page_path}")
|
||||
with open(page_path, encoding="utf-8") as stream:
|
||||
html = stream.read()
|
||||
self.assertEqual(html.count("hn-hero-picture"), 1, f"首屏图片容器数量应为 1:{page_path}")
|
||||
self.assertIn("river-courtyard-v3.webp", html, f"页面缺少 WebP 首屏图片源:{page_path}")
|
||||
self.assertIn("river-courtyard-v3.jpg", html, f"页面缺少 JPEG 首屏图片回退:{page_path}")
|
||||
self.assertIn('fetchpriority="high"', html, f"首屏图片应优先加载:{page_path}")
|
||||
self.assertRegex(html, r'<img[^>]+alt="[^"]{4,}"', f"首屏图片应包含有效替代文本:{page_path}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
在新工单中引用
屏蔽一个用户