加载中...

加载中...

本文是 matery 主题自动化测试体系的完整教程,与其余 4 篇功能测试文章(内容 tag / 交互视觉 / 布局页面 / Markdown 语法)配套——那 4 篇是"测试靶场",本文是"测试引擎"。权威依据:tools/tests/release-test.sh(L1-L14,13 层 156 行纯编排)、docs/superpowers/specs/2026-08-06-layout-test-cases.md


1. 测试金字塔总览

┌──────────────┐
│  L2 生产 URL  │  发版后:20 页面 HTTP 200 + 功能标记(curl)
│  L9/L10 SW   │  发版前:Vercel 测试域 SW 冒烟(playwright)
├──────────────┤
│  L11 Playwright 集成 │  真实浏览器:特效/词云/懒加载/阅读模式等 15 项
├──────────────┤
│  L14 视觉断言 │  visual-assert 精确数值:7 套件(生产阻塞,本地默认跳过)
├──────────────┤
│  L13 单测门禁 │  node 直跑:13 个 .test.js + 4 个 shell 单测
├──────────────┤
│  L1 构建验证  │  hexo g Success + 产物完整(index.html > 1000B)
└──────────────┘
名称运行方式依赖
L1构建验证hexo generate + 产物检查
L13全量单测门禁node tools/tests/*.test.js + shell 单测无 chromium
L11Playwright 集成真实浏览器 + 本地 serverchromium + server
L2/L2b/L2x/L2y/L2i生产 URL 断言curl + grep生产/本地 server
L3-L8功能标记检查curl + 静态断言生产/本地 server
L9静态资源 + SW 冒烟curl + playwright生产域名
L13a覆盖率统计node --experimental-test-coverageunit/**
L14视觉断言node tools/visual-assert.js run(7 套件)chromium + server
L10SW 冒烟(测试域)playwrightVercel 测试域 blog2.17lai.site

2. release-test.sh 用法

# 完整跑(构建 + 生产检查 + SW 冒烟 + playwright 集成)——发版门禁
tools/tests/release-test.sh

# 跳过构建(public/ 已就绪)
tools/tests/release-test.sh --skip-build

# 本地迭代(端口自适应探测,跳过远程 SW 冒烟 L10 + playwright 集成 L11)
tools/tests/release-test.sh --local

# 仅跳过远程 SW 冒烟(保留 playwright 集成)
tools/tests/release-test.sh --skip-l10
参数作用
--skip-build跳过 L1 构建 + 产物验证(已构建时用)
--local目标改本地基址(端口自适应探测,见下方基址段),跳过 L10(远程 SW 冒烟 5min+)与 L11(playwright 串行 >800s)
--skip-l10仅跳过 L10,保留 L11 playwright 集成
--prepare自动就绪环境:容器未跑则启动、产物无效则重建、最后复检
RUN_L14=1本地模式下启用 L14 视觉断言(默认本地跳过)
SKIP_L14=1强制跳过 L14(即使 RUN_L14=1

基址:生产固定 https://blog.17lai.site443,绝无端口);本地 LOCAL_BASE 走端口自适应——探测顺序 BLOG_PORT 环境变量 → TEST_BASE 环境变量 → 根 _config.ymlserver.port(docker 4000)→ 4100,取第一个 HTTP 200;成功后导出 TEST_BASE/TEST_URL,36 个测试文件自动跟随。

执行顺序(156 行纯编排,非文档顺序):L0 preflight → L10 → L01 → L02 → L03_05 → L07 → L08 → L09 → L11 → L12 → L2i → L13 → L13a → L14 → TC 索引校验 → 汇总。注意 L10 最先跑(在 L01 之前)——旧文档常写反。

发版铁律tools/cicd.sh -r 发布后必须跑 tools/tests/release-test.sh 验证生产环境。


3. L1-L14 各层覆盖内容

覆盖内容断言示例
L1构建验证hexo g Success + public/index.html > 1000B
L220 个 layout 页面 URL 可达/posts/fc764afd//archives//categories//tags//about//friends//galleries//bb//musics//movies//msg/ + 4 篇测试文章 → HTTP 200
L2b多语言验证en 文章真实翻译 / 回落提示条 / 评论 path 归一化 / 默认语言无前缀
L2x统一语言回落MATERY_LANG 注入 / 回落页 / 独立页多语言(含 gallery 子页)/ 聚合页多语言
L2y加密解密刷新wiki 侧栏无 encrypt_ 垃圾标题 / 加密容器完整(hbeForm + storage-key)
L2iSEO 结构化数据og:image / JSON-LD / fetchpriority
L3评论区检查msg/friends/bb 页含 #comments
L4href=/ 样式错误页面 href=/ 引用数应为 0
L5版本号一致性config vs sw.tmp.js 3 处一致
L6关键资源检查404 页面 / 关键 JS 版本
L7核心功能覆盖相册灯箱 / 加密相册 / 搜索 / JS 模块合并 / TOC 折叠 / 暗色 / 评论 / infinite-scroll / wiki 代码块
L8增强功能覆盖tag 插件渲染(note/timeline/tabs/label/githubCard/mermaid)/ 加密文章 / Feed / 静态资源 / 分析
L9静态资源 + SW 冒烟main.css > 50KB / utils.js > 5KB / 生产域名 7 页无 SW 错误
L10SW 冒烟(测试域)blog2.17lai.site 7 页无 SW 错误 + 交互验证(搜索/打赏/灯箱/TOC/暗色)
L11Playwright 集成(15 项)见下表
L12本地化资源检查mermaid / aplayer 资源存在
L13全量单测门禁13 个 *.test.js + 4 个 *.test.sh = 17 文件
L13a覆盖率统计unit/** 覆盖率扫描(附带执行 unit/** 全部单测)
L14视觉断言(7 套件)见 §9

L11 Playwright 集成清单(真实浏览器)

测试文件覆盖
effects-integration.test.js页面特效(sakura/snowflake/fireworks 加载)
videobg-integration.test.js视频背景(门槛/亮暗池/webm 契约/禁用态)
wordcloud.test.js词云 / 去 jQuery / TOC 防闪烁 / 明暗切换
i18n.test.js多语言补全(MATERY_I18N 注入 + 按钮文案 + en 翻译)
musics-integration.test.js在线音乐 APlayer(多域名 failover)
lazyload-integration.test.js懒加载 handleCard 三分发(51 断言)
floating-panel-integration.test.js悬浮面板(开关/字体缩放/持久化)
content-width.test.js内容宽度统一(4K/FHD/2K/单栏封顶)
readmode-integration.test.js阅读模式 / 代码全屏(PASS=14)
core-events-integration.test.js事件总线 / 进度条联动
wiki-prism-integration.test.jsWiki 代码块 prism + line-numbers
misc-interactions.test.js打赏弹窗 / 繁简转换(G14/G15)
lang-client-strategy.test.js客户端语言策略(全站跟随/爬虫跳过/面板回显,7 断言)
rich-content-integration.test.js富内容渲染(公式 MathJax+KaTeX/mermaid/markmap/打字机/分享/Live2D,11 断言)
lightbox.test.js灯箱(相册/画廊图片放大交互)

4. 单测清单(tools/tests/)

L13 门禁单测(release-test.sh 直跑)

# node 单测(纯单测/静态,无浏览器依赖)
unit-toc.test.js          # TOC 折叠配置(collapseDepth 0||6 falsy 陷阱)
unit-search.test.js       # 搜索三引擎
unit-multilang.test.js    # 多语言配置
unit-encrypt2.test.js     # 加密扩展(片段 + wiki)
unit-wiki-helper.test.js  # wiki helper
lang-fallback.test.js     # 回落链 4 级(13 断言)
echarts-charts.test.js    # ECharts 图表
banner-bg.test.js         # banner 与背景偏好联动
homepage.test.js          # 首页结构
imgsize-unit.test.js      # 图片尺寸注入脚本的单元测试(normalizeImgUrl 协议相对 URL 补全 + 本地/网络图片尺寸读取逻辑)
search-integration.test.js
infinite-scroll-aos.test.js
unit-standalone.test.js   # 独立页排除法判定 + 语言前缀豁免(22 断言)

# shell 单测
unit-css-architecture.test.sh   # CSS 架构(编译产物关键选择器)
unit-version.test.sh            # 版本递增进位(防 12.99→12.100)
unit-retry.test.sh              # 退避重试库
unit-deploy-target.test.sh      # 部署目标切换(hexo/hexoback 双激活校验)

其他单测(原 run-tests.sh L1 / L8 段,已并入 release-test.sh

unit-encrypt.test.js      # 自研加密体系(AES-GCM + PBKDF2 闭环,9 用例)
unit-fontawesome.test.py  # FontAwesome 精简脚本

5. 外部依赖重试与降级(lib/retry.sh)

第三方网络抖动不可避免(Algolia API / CDN 视频 / 音乐 API),且外部服务不可用不是产品缺陷,不应拦发版tools/tests/lib/retry.sh 统一处理:

source tools/tests/lib/retry.sh

# exit-code 判定:退避重试 3 次(1s/4s/9s + 抖动)
retry_run "L2 搜索" "node tools/tests/search-integration.test.js"

# 输出模式判定(release-test L11 用)
retry_match "L2d 视频" "node .../videobg-integration.test.js" "PASS=.*FAIL=0"

失败分类(全失败时按日志签名区分):

签名判定结果
ERR_NAME_NOT_RESOLVED / ECONNREFUSED / ETIMEDOUT / 429 / 50x外部不可用⚠️ 降级 warn(返回 0,不拦发版)
无外部签名业务断言失败❌ fail(返回 1,真 bug)

6. 第三方拦截 + ERR_ABORTED 排除(lib/browser-env.js)

playwright 测试的共享环境辅助,统一"页面自身错误"归属:

const { interceptThirdParty, collectPageErrors } = require('./lib/browser-env');

const env = collectPageErrors(page, BASE);   // 持续累积 JS 异常 + 同源资源失败
await interceptThirdParty(page);             // 拦截已知污染源
// 断言:env.jsErrors.length === 0 && env.sameOriginFailures.length === 0
机制说明
interceptThirdParty拦截已知污染源(如 webpushr 返回非 JSON → 页面 unhandled rejection),返回合法 JSON 而非 abort(abort 会产生 net::ERR_FAILED 反成新噪声)
collectPageErrors仅收集 JS 异常 + 同源(localhost/生产域)资源失败;第三方 CDN/图床失败为环境噪声不计入
ERR_ABORTED 排除导航中断(多次 goto/离开页面取消在途请求)非资源错误,直接跳过

7. 压缩开关验证(cicd.sh

tools/cicd.sh 按模式注入压缩开关(sed 替换根 _config.yml# minify-switch 标记行):

模式minify 值说明
-d / --debugfalse不压缩(本地调试,产物可读)
-r / -c / -ttrue压缩(hexo-minify 在 generate 时恒压缩,故需改其 enable)

验证方法

# 1. debug 构建(未压缩)
tools/cicd.sh -d
head -c 300 public/css/main.css   # 多行、带缩进、有注释

# 2. 压缩构建(-t 本地编译测试,压缩无 CDN)
tools/cicd.sh -t
head -c 300 public/css/main.css   # 单行、无注释、体积显著变小

构建后 cicd.sh 会还原 _config.yml(仅当改动只涉及 minify-switch 标记行时),避免工作区脏。


8. 环境就绪、单实例锁与基址自适应

门禁前先过 preflight(环境预检,step 0),未就绪则秒级失败并打印修复命令,避免"跑一半才发现 server 挂了":

检查项不通过表现
本地 server 可达(HTTP 200)打印修复命令后退出
构建产物有效(public/index.html > 1000B)打印重建命令
playwright 可用跳过依赖浏览器的层
themes/matery/_config.yml 未被构建污染自动 git checkout 精确恢复
网络基址可达(仅发版模式)提示基址不可达
# 未就绪时打印的 3 条修复命令(任选其一)
docker compose -f docker-compose.test.yml exec -T hexo bash -c 'tools/cicd.sh -d'
tools/test.sh server
docker compose -f docker-compose.test.yml exec -T hexo bash -c 'pm2 restart all'

--prepare 自动就绪:容器未跑则启动、产物无效则重建、最后复检。

单实例锁:门禁启动即在 ${TMPDIR:-/tmp}/opencode/gate.lock 取锁,并发运行被直接拒绝(打印持锁 PID);陈旧锁自动接管,trap EXIT 释放。禁止并发门禁——并发会互相 kill 本地 server。

基址与端口自适应:见 §2 的基址段(生产 443 固定 / 本地端口探测)——36 个测试文件靠导出的 TEST_BASE/TEST_URL 自动跟随。


9. L14 视觉断言层

tools/visual-assert/*.json 共 16 套件,门禁 L14 只跑其中 7 套layout-checksdark-modesearch-modalreward-modalhomepageaboutfooter

属性
语义生产阻塞(失败即 FAIL);本地默认跳过
本地启用RUN_L14=1 tools/tests/release-test.sh
强制跳过SKIP_L14=1
失败处理某套件失败自动重试一次(负载抖动),仍失败才 FAIL,并打印细节
耗时约 299s(重)

⚠️ make visual-assert 陷阱:该 make 目标依赖 build,build 会原地改写 themes/matery/_config.yml 占位符 → 触发 L5 版本一致性 4 项失败。正确用法:node tools/visual-assert.js run --local [tools/visual-assert/<suite>.json],或交给 L14 层跑。


10. TC 索引与 git hooks

测试的权威来源是 tools/tests/**;生成的 TC 表位于 docs/superpowers/specs/2026-08-06-layout-test-cases.md<!-- GENERATED:START --><!-- GENERATED:END --> 之间。

机制说明
node tools/tests/gen-tc-index.js / make tc-index重新生成 TC 清单
make tc-index-check校验清单是否落后
.githooks/pre-commit提交涉及 tools/tests/自动重生成并 stage 该文档 → 提交陈旧索引在结构上不可能
门禁 --check汇总前跑 gen-tc-index.js --check(覆盖 --no-verify 等旁路)

首次克隆需启用一次git config core.hooksPath .githooks。门禁报"TC 清单不一致"时,跑 make tc-index 再提交(旁路 git commit --no-verify)。

当前索引:13 层 / 33 测试文件 / 3 lib 文件 / 251 断言 / 84 用例名


11. flake 可见化与基线参考值

flake 可见化:被重试后通过的项目记入 FLAKY_LIST,门禁末尾打印 ⚠️ 本次门禁 flaky: N 项(…);它不改变 PASS/FAIL 计数,只让抖动可见。

基线命令
本地PASS=181 FAIL=0tools/tests/release-test.sh --local
本地 + L14PASS=188 FAIL=0RUN_L14=1 tools/tests/release-test.sh
生产PASS=207 FAIL=0tools/tests/release-test.sh(发版后)

3 个 libassert.sh(82 行)、retry.sh(114 行,负责 flake 追踪)、preflight.sh(230 行)。browser-env.js 为 playwright 共享辅助(见 §6)。


12. 如何新增一个测试用例

步骤

  1. 确定测试类型
    • 纯单测 / 静态断言(无浏览器)→ 归 L13(node 直跑)
    • playwright 集成(需 chromium + server)→ 归 L11
  2. 创建测试文件 tools/tests/xxx.test.js(node:test 或 playwright)
  3. 外部依赖测试:用 retry_run / retry_match 包装(lib/retry.sh),全失败自动分类降级
  4. playwright 测试:用 lib/browser-env.jsinterceptThirdParty + collectPageErrors 统一错误归属
  5. 注册到 release-test.sh 对应层(L11 或 L13 段),输出格式对齐(PASS=N FAIL=0N 通过 / 0 失败
  6. 本地验证tools/tests/release-test.sh --local(跑 L13 + curl 断言);发版门禁跑全量
  7. 同步 TC 索引:新增/改动后必须让 TC 索引同步(.githooks/pre-commit 自动完成;或手动 make tc-index)——否则门禁 --check 报"TC 清单不一致"

已知覆盖缺口:以下 4 个测试文件存在但未挂入任何层——unit/helpers.test.jsunit/utils.test.js(仅被 L13a 的 unit/** 覆盖率 glob 附带执行)、wiki-integration.test.jsinfinite-scroll-integration.test.js(纯 playwright,从不运行)。新增用例时注意别重蹈覆辙。

断言输出约定

release-test.sh 按输出模式判定通过/失败,测试文件末尾必须输出可匹配的汇总行:

console.log(`\n结果: PASS=${pass} FAIL=${fail}`);
process.exit(fail > 0 ? 1 : 0);

测试用例权威清单

docs/superpowers/specs/2026-08-06-layout-test-cases.md(全量 layout 测试用例)+ docs/testing/TESTING-PYRAMID-GUIDE.md(测试方法论)——新增功能后同步更新用例清单与 release-test.sh 覆盖。


⚠️ 已知坑

坑 5:改配置后必须 clean 构建

现象:跑门禁时 L1 构建验证通过但功能断言失败。

原因:本地预览使用旧缓存产物,测试断言的是旧版行为。

正确做法:跑门禁前必须 npm run clean && npm run build,或使用 tools/tests/release-test.sh --prepare 自动就绪。

坑 6:source/_data/* 增量缓存误判

现象:修改友链/相册数据后,L2 URL 断言页面空白或数据不更新。

原因:Hexo 增量缓存误判「已缓存未变化」。

正确做法:修改数据文件后先 rm -f db.json && npm run build 再跑门禁。容器环境用 tools/cicd.sh -d(内置 clean)。


附:测试体系速查表

命令用途
tools/tests/release-test.sh发版全量门禁(L1-L14)
tools/tests/release-test.sh --local本地迭代(端口自适应,跳 L10/L11/L14)
tools/tests/release-test.sh --prepare自动就绪环境(容器 + 构建 + 复检)
RUN_L14=1 tools/tests/release-test.sh --local本地启用 L14 视觉断言(基线 PASS=188
make tc-index / make tc-index-check生成 / 校验 TC 清单(.githooks/pre-commit 自动同步)
tools/tests/run-tests.sh已于 2026-09-13 删除(与 L11 功能重叠);等价能力见 tools/tests/release-test.sh
node tools/tests/gen-tc-index.js生成/校验 TC 清单(make tc-index
make test-pwaVercel 测试域 SW 冒烟(7 页)+ 交互验证
make visual-compare / visual-assert视觉回归(playwright 数值断言);⚠️ make visual-assert 含 build 依赖会污染 _config.yml,优先 node tools/visual-assert.js run --local
tools/visual-regression.sh report视觉对比报告(20 页面 × 4 断点)

Hexo主题功能测试-自动化测试篇
发布于
2026年9月11日
许可协议。转载请注明来源
评论
数据加载中 ...
 本篇

阅读全文

Hexo主题功能测试-自动化测试篇
Hexo主题功能测试-自动化测试篇 Hexo主题功能测试-自动化测试篇
主题自动化测试体系完整教程:测试金字塔(L1 构建 / L13 单测门禁 / L11 Playwright 集成 / 生产 release-test)、release-test.sh 用法、L1-L14 各层覆盖、单测清单、外部依赖重试与降
2026-09-11
下一篇 

阅读全文

Hexo主题功能测试-Markdown语法篇
Hexo主题功能测试-Markdown语法篇 Hexo主题功能测试-Markdown语法篇
主题 Markdown 语法完整教程与测试:16 个 markdown-it 插件全覆盖(emoji/缩写/脚注/插入/上下标/高亮/任务列表/表格增强/图片尺寸/容器/定义列表/数学公式/中文排版)。每项含用途、语法、示例、实现效果,供自
2026-08-08