Loading...

Loading...

This article is a complete tutorial for the matery theme’s automated testing system, paired with the other 4 feature test articles (Content Tags / Interaction & Visual / Layout & Pages / Markdown Syntax) — those 4 are the “test arena”, this one is the “test engine”.
Authoritative sources: tools/tests/release-test.sh (L1-L14), docs/superpowers/specs/2026-08-06-layout-test-cases.md.

When to Read This Article?

You are…Read this sectionWhat you get
User (wondering if a feature works)Appendix “Test ↔ Feature Map”Find the test file by feature name, confirm “this feature has automated verification”
Contributor (adding/modifying a test)§12 “How to Add a Test Case”Complete zero-to-gate steps with code templates
Maintainer (running the gate before release)§2 “release-test.sh Usage”Commands for local iteration / release gate / visual assertions
Debugging (gate shows red)§5 “External Dependency Retry” + §6 “Third-Party Interception” + Known PitfallsDistinguish “environment noise” from “real bug”

1. Testing Pyramid Overview

┌──────────────┐
│  L2 Prod URL  │  After release: 20 pages HTTP 200 + feature markers (curl)
│  L9/L10 SW   │  Before release: Vercel test domain SW smoke (playwright)
├──────────────┤
│  L11 Playwright Integration │  Real browser: effects/wordcloud/lazyload/read mode, 18 items
├──────────────┤
│  L14 Visual Assertions │  visual-assert precise values: 7 suites (production-blocking, skipped locally by default)
├──────────────┤
│  L13 Unit Gate │  node direct: 16 .test.js + 4 shell unit tests + 1 tutorial-structure gate
├──────────────┤
│  L1 Build Verify │  hexo g Success + artifact complete (index.html > 1000B)
└──────────────┘
LayerNameHow It RunsDependencies
L1Build verificationhexo generate + artifact checknone
L13Full unit test gatenode tools/tests/*.test.js + shell unit testsno chromium
L11Playwright integrationreal browser + local serverchromium + server
L2/L2b/L2x/L2y/L2iProduction URL assertionscurl + grepproduction/local server
L3-L8Feature marker checkscurl + static assertionsproduction/local server
L9Static assets + SW smokecurl + playwrightproduction domain
L10SW smoke (test domain)playwrightVercel test domain blog2.17lai.site
L13aCoverage statisticsnode --experimental-test-coverage scans unit/**none
L14Visual assertionsnode tools/visual-assert.js run (7 suites)chromium + server

2. release-test.sh Usage

# Full run (build + production checks + SW smoke + playwright integration) — release gate
tools/tests/release-test.sh

# Skip build (public/ already ready)
tools/tests/release-test.sh --skip-build

# Local iteration (tests localhost:4000, skips remote SW smoke L10 + playwright integration L11)
tools/tests/release-test.sh --local

# Skip only remote SW smoke (keep playwright integration)
tools/tests/release-test.sh --skip-l10
FlagEffect
--skip-buildSkips L1 build + artifact verification (use when already built)
--localTarget switches to adaptive local base (port detection, see base address section below), skips L10 (remote SW smoke 5min+) and L11 (playwright serial >800s)
--skip-l10Skips only L10, keeps L11 playwright integration
--prepareAuto-prepares environment: starts container if not running, rebuilds if artifacts invalid, rechecks at the end
RUN_L14=1Enables L14 visual assertions in local mode (skipped locally by default)
SKIP_L14=1Forces L14 skip (even if RUN_L14=1 is set)

Base address: production is fixed at https://blog.17lai.site (port 443, never a port number); local uses LOCAL_BASE with adaptive port detection — probe order: BLOG_PORT env var → TEST_BASE env var → root _config.yml server.port (docker 4000) → 4100, take the first HTTP 200; on success exports TEST_BASE/TEST_URL, and all 36 test files follow automatically.

Execution order (156 lines of pure orchestration, not documentation order): L0 preflight → L10 → L01 → L02 → L03_05 → L07 → L08 → L09 → L11 → L12 → L2i → L13 → L13a → L14 → TC index check → summary. Note L10 runs first (before L01) — old documentation often lists it incorrectly.

Release rule: after tools/cicd.sh -r publishes, you MUST run tools/tests/release-test.sh to verify the production environment.


3. L1-L14 Layer Coverage

LayerCoverageAssertion Example
L1Build verificationhexo g Success + public/index.html > 1000B
L220 layout page URLs reachable/posts/fc764afd/, /archives/, /categories/, /tags/, /about/, /friends/, /galleries/, /bb/, /musics/, /movies/, /msg/ + 4 test articles → HTTP 200
L2bMultilingual verificationen article real translation / fallback notice banner / comment path normalization / default language no prefix
L2xUnified language fallbackMATERY_LANG injection / fallback pages / standalone page multilingual (incl. gallery sub-pages) / aggregation page multilingual
L2yEncrypt/decrypt refreshwiki sidebar no encrypt_ junk titles / encrypted container complete (hbeForm + storage-key)
L2iSEO structured dataog:image / JSON-LD / fetchpriority
L3Comment section checkmsg/friends/bb pages contain #comments
L4href=/ style errorspage href=/ references should be 0
L5Version consistencyconfig vs sw.tmp.js consistent in 3 places
L6Key resource check404 page / key JS versions
L7Core feature coveragegallery lightbox / encrypted gallery / search / JS module merge / TOC collapse / dark mode / comments / infinite-scroll / wiki code blocks / wiki component styles (main has post-container / .wiki-content word-wrap / admonition .note rule precisely scoped)
L8Enhanced feature coveragetag plugin rendering (note/timeline/tabs/label/githubCard/mermaid) / encrypted posts / Feed / static assets / analytics
L9Static assets + SW smokemain.css > 50KB / utils.js > 5KB / production domain 7 pages no SW errors
L10SW smoke (test domain)blog2.17lai.site 7 pages no SW errors + interaction verification (search/reward/lightbox/TOC/dark mode)
L11Playwright integration (18 items)see table below
L12Localized asset checkmermaid / aplayer assets exist
L13Full unit test gate16 *.test.js + 4 *.test.sh = 20 files (plus 1 tutorial-structure gate)
L13aCoverage statisticsunit/** coverage scan (incidentally runs all unit/** unit tests)
L14Visual assertions (7 suites)see §9

L11 Playwright Integration List (real browser)

Test FileCoverage
effects-integration.test.jsPage effects (sakura/snowflake/fireworks loading)
videobg-integration.test.jsVideo background (threshold/light-dark pool/webm contract/disabled state)
wordcloud.test.jsWord cloud / no jQuery / TOC anti-flicker / dark-light switching
i18n.test.jsMultilingual completion (MATERY_I18N injection + button copy + en translations)
musics-integration.test.jsOnline music APlayer (multi-domain failover)
lazyload-integration.test.jsLazyload handleCard three-way dispatch (51 assertions)
floating-panel-integration.test.jsFloating panel (toggle/font scaling/persistence)
content-width.test.jsContent width unification (4K/FHD/2K/single-column cap)
readmode-integration.test.jsRead mode / code fullscreen / scroll-down-bar
wiki-styles.test.jsWiki tag component styles (note / ::: container colors + centering + width consistency)
code-widget.test.jscodeWidget delegation idempotency (second invocation still works with single click)
core-events-integration.test.jsEvent bus / progress bar linkage
wiki-prism-integration.test.jsWiki code blocks prism + line-numbers
misc-interactions.test.jsReward popup / trad-simp conversion (G14/G15)
lang-client-strategy.test.jsClient language strategy (site-wide follow/crawler skip/panel echo, 7 assertions)
rich-content-integration.test.jsRich content rendering (math MathJax+KaTeX/mermaid/markmap/typewriter/share/Live2D, 11 assertions)
wiki-integration.test.jsMultilingual wiki reachability / sidebar sections / language switcher (18 assertions)
lightbox.test.jsLightbox (gallery/image zoom interaction)

4. Unit Test List (tools/tests/)

L13 Gate Unit Tests (run directly by release-test.sh)

# node unit tests (pure unit/static, no browser dependency)
unit-toc.test.js          # TOC collapse config (collapseDepth 0||6 falsy trap)
unit-search.test.js       # search three engines
unit-multilang.test.js    # multilingual config
unit-encrypt2.test.js     # encryption extensions (fragment + wiki)
unit-wiki-helper.test.js  # wiki helper
helpers.test.js           # word-count helpers (wordcount/min2read/totalcount; encrypted origin / wiki de-dup / fallback / cache reset, 52 assertions)
lang-fallback.test.js     # fallback chain 4 levels (13 assertions)
unit-i18n-core.test.js   # i18n core fallback semantics (resolveI18n 4-level chain + cfg defense, 10 cases)
unit-i18n-helper.test.js  # t() helper (fallback_lang + case tolerance, 5 cases)
echarts-charts.test.js    # ECharts charts
banner-bg.test.js         # banner & background preference linkage
homepage.test.js          # homepage structure
imgsize-unit.test.js      # image size
search-integration.test.js
infinite-scroll-aos.test.js
unit-standalone.test.js   # standalone page exclusion logic + language prefix exemption (22 assertions)
unit-markmap-prune.test.js # markmap resource pruning on pages without mindmaps (6 cases; covered by L13a_cov via unit/** glob)
unit-updated.test.js      # "Updated on" takes the git last-commit time (4 cases: CJK path / missing / overwrite as moment / author value preserved)

# shell unit tests
unit-css-architecture.test.sh   # CSS architecture (key selectors in compiled output)
unit-version.test.sh            # version increment carry (prevents 12.99→12.100)
unit-retry.test.sh              # backoff retry library
unit-deploy-target.test.sh      # deploy target switching (hexo/hexoback dual-activation verification)

Other Unit Tests (former run-tests.sh L1 / L8 sections, now merged into release-test.sh)

unit-encrypt.test.js      # self-built encryption system (AES-GCM + PBKDF2 closed loop, 9 cases)
unit-fontawesome.test.py  # FontAwesome minify script

5. External Dependency Retry & Degradation (lib/retry.sh)

Third-party network jitter is unavoidable (Algolia API / CDN videos / music APIs), and an external service being unavailable is not a product defect and should not block a release. tools/tests/lib/retry.sh handles this uniformly:

source tools/tests/lib/retry.sh

# exit-code based: backoff retry 3 times (1s/4s/9s + jitter)
retry_run "L2 search" "node tools/tests/search-integration.test.js"

# output-pattern based (used by release-test L11)
retry_match "L2d video" "node .../videobg-integration.test.js" "PASS=.*FAIL=0"

Failure classification (when all retries fail, distinguish by log signature):

SignatureVerdictResult
ERR_NAME_NOT_RESOLVED / ECONNREFUSED / ETIMEDOUT / 429 / 50x etc.external unavailable⚠️ degraded warn (returns 0, does not block release)
no external signaturebusiness assertion failure❌ fail (returns 1, real bug)

6. Third-Party Interception + ERR_ABORTED Exclusion (lib/browser-env.js)

Shared environment helper for playwright tests, unifying “page’s own errors” attribution:

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

const env = collectPageErrors(page, BASE);   // continuously accumulates JS exceptions + same-origin resource failures
await interceptThirdParty(page);             // intercepts known pollution sources
// assert: env.jsErrors.length === 0 && env.sameOriginFailures.length === 0
MechanismDescription
interceptThirdPartyIntercepts known pollution sources (e.g. webpushr returning non-JSON → page unhandled rejection), returns valid JSON instead of aborting (abort produces net::ERR_FAILED, becoming new noise)
collectPageErrorsCollects only JS exceptions + same-origin (localhost/production domain) resource failures; third-party CDN/image-host failures are environment noise and not counted
ERR_ABORTED exclusionNavigation interruption (multiple goto/leaving the page cancels in-flight requests) is not a resource error, skipped directly

7. Minify Switch Verification (cicd.sh)

tools/cicd.sh injects the minify switch per mode (sed replaces the # minify-switch marker lines in the root _config.yml):

Modeminify valueDescription
-d / --debugfalseno minification (local debugging, readable output)
-r / -c / -ttrueminified (hexo-minify always minifies at generate time, so its enable must be toggled)

Verification method:

# 1. debug build (unminified)
tools/cicd.sh -d
head -c 300 public/css/main.css   # multi-line, indented, with comments

# 2. minified build (-t local compile test, minified without CDN)
tools/cicd.sh -t
head -c 300 public/css/main.css   # single line, no comments, significantly smaller

After building, cicd.sh restores _config.yml (only when the changes involve just the minify-switch marker lines), keeping the working tree clean.


8. Environment Preflight, Single-Instance Lock & Base Address Adaptation

Before the gate runs, preflight (step 0) checks environment readiness; if not ready it fails immediately and prints fix commands, preventing “running halfway才发现 server is down”:

CheckFailure Behavior
Local server reachable (HTTP 200)Prints fix commands then exits
Build artifacts valid (public/index.html > 1000B)Prints rebuild commands
playwright availableSkips browser-dependent layers
themes/matery/_config.yml not polluted by buildAuto git checkout to restore exactly
Network base address reachable (release mode only)Reports base address unreachable
# Three fix commands printed when not ready (pick any one)
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 auto-prepares: starts container if not running, rebuilds if artifacts invalid, rechecks at the end.

Single-instance lock: the gate acquires a lock at ${TMPDIR:-/tmp}/opencode/gate.lock on startup; concurrent runs are rejected outright (prints the lock-holding PID); stale locks are automatically taken over, trap EXIT releases. Concurrent gates are forbidden — they kill each other’s local server.

Base address & port adaptation: see §2’s base address section (production fixed at 443 / local port detection) — 36 test files follow automatically via exported TEST_BASE/TEST_URL.


9. L14 Visual Assertion Layer

tools/visual-assert/*.json has 16 suites total; the gate L14 only runs 7 of them: layout-checks, dark-mode, search-modal, reward-modal, homepage, about, footer.

PropertyValue
SemanticsProduction-blocking (failure = FAIL); skipped locally by default
Enable locallyRUN_L14=1 tools/tests/release-test.sh
Force skipSKIP_L14=1
Failure handlingA suite that fails is automatically retried once (load jitter); only fails if still failing after retry, with details printed
Duration~299s (heavy)

⚠️ make visual-assert trap: that make target depends on build, which overwrites themes/matery/_config.yml placeholders in-place → triggers L5 version consistency 4-item failures. Correct usage: node tools/visual-assert.js run --local [tools/visual-assert/<suite>.json], or let the L14 layer run it.


10. TC Index & Git Hooks

The authoritative source for tests is tools/tests/**; the generated TC table lives between <!-- GENERATED:START --> and <!-- GENERATED:END --> in docs/superpowers/specs/2026-08-06-layout-test-cases.md.

MechanismDescription
node tools/tests/gen-tc-index.js / make tc-indexRegenerate the TC index
make tc-index-checkCheck if the index is stale
.githooks/pre-commitWhen a commit touches tools/tests/, auto-regenerates and stages the document → submitting a stale index is structurally impossible
Gate --checkRuns gen-tc-index.js --check before summary (covers --no-verify and other bypasses)

First clone requires one-time setup: git config core.hooksPath .githooks. When the gate reports “TC index inconsistent”, run make tc-index then commit (bypass: git commit --no-verify).

Current index: 13 layers / 42 test files / 3 lib files / 284 assertions / 406 test names.


11. Flake Visibility & Baseline Reference Values

Flake visibility: items that pass after retry are recorded in FLAKY_LIST; the gate prints ⚠️ This gate had N flaky items (…) at the end; it does not change PASS/FAIL counts, it only makes jitter visible.

BaselineExpectedCommand
LocalAll pass (0 failures)tools/tests/release-test.sh --local
Local + L14All pass (0 failures, including visual assertions)RUN_L14=1 tools/tests/release-test.sh
ProductionAll pass (0 failures)tools/tests/release-test.sh (after release)

The exact PASS count grows with each version (every added assertion changes it); rely on the actual output instead of a hard-coded number.

3 libs: assert.sh (82 lines), retry.sh (114 lines, responsible for flake tracking), preflight.sh (230 lines). browser-env.js is a playwright shared helper (see §6).


12. How to Add a Test Case

Steps

  1. Determine the test type:
    • Pure unit / static assertions (no browser) → L13 (node direct)
    • playwright integration (needs chromium + server) → L11
  2. Create the test file tools/tests/xxx.test.js (node:test or playwright)
  3. External dependency tests: wrap with retry_run / retry_match (lib/retry.sh), auto-classify and degrade on total failure
  4. playwright tests: use lib/browser-env.js’s interceptThirdParty + collectPageErrors for unified error attribution
  5. Register in release-test.sh at the corresponding layer (L11 or L13 section), align the output format (PASS=N FAIL=0 or N passed / 0 failed)
  6. Local verification: tools/tests/release-test.sh --local (runs L13 + curl assertions); the release gate runs the full suite
  7. Sync TC index: after adding/modifying, the TC index must stay in sync (.githooks/pre-commit does this automatically; or manually run make tc-index) — otherwise the gate --check reports “TC index inconsistent”

Known coverage gaps: wiki-integration.test.js and infinite-scroll-integration.test.js exist but are not hooked into any layer (pure playwright, always fails without a browser; covered by L11 or a manual pre-release playwright run). Be careful not to repeat this pattern when adding new tests.

Mocks Must Model Real Hexo Objects (Query, Not Plain Arrays)

When unit-testing with a mocked site / locals, model Hexo’s real objects — otherwise a fully green suite still lets a build-breaking bug through:

  • site.posts / site.pages are Hexo Query objects (array-like): .filter() returns another Query, not an array, and they cannot be for...of-iterated — call .toArray() first.
  • helpers.test.js (word count) first mocked them as plain arrays: unit tests passed, but the real build threw wikiPages is not iterable → every HTML output was 0 bytes. The mock only exercised the real path after toArray() was added.

Lesson: the closer a mock is to the real object, the better it catches build-level failures; substituting a plain array for a Query hides a missing toArray().

Code Templates

L13 unit test template (node:test, no browser):

// tools/tests/my-feature.test.js
const { describe, it } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');

const PUBLIC = path.resolve(__dirname, '../../public');

describe('my-feature', () => {
  it('should render expected element', () => {
    const html = fs.readFileSync(path.join(PUBLIC, 'index.html'), 'utf8');
    assert.ok(html.includes('expected-marker'), 'missing expected marker');
  });
});

// Output at end (release-test.sh uses this line to judge pass/fail)
const pass = 1, fail = 0;
console.log(`\nResult: PASS=${pass} FAIL=${fail}`);
process.exit(fail > 0 ? 1 : 0);

L11 playwright template (needs chromium + server):

// tools/tests/my-interaction.test.js
const { chromium } = require('playwright');
const { interceptThirdParty, collectPageErrors } = require('./lib/browser-env');

const BASE = process.env.TEST_BASE || 'http://localhost:4000';
let browser, page;
let pass = 0, fail = 0;

async function setup() {
  browser = await chromium.launch();
  page = await browser.newPage();
  await interceptThirdParty(page);
}

async function testFeature() {
  const env = collectPageErrors(page, BASE);
  await page.goto(`${BASE}/posts/xxxxx/`, { waitUntil: 'networkidle' });
  const el = await page.$('.my-feature');
  if (el) { pass++; } else { fail++; console.error('FAIL: .my-feature not found'); }
  // Check no JS errors
  if (env.jsErrors.length > 0) { fail++; console.error('FAIL: JS errors', env.jsErrors); }
  else { pass++; }
}

(async () => {
  await setup();
  await testFeature();
  await browser.close();
  console.log(`\nResult: PASS=${pass} FAIL=${fail}`);
  process.exit(fail > 0 ? 1 : 0);
})();

Hooking Into the Gate: Where to Add a Line

L13 (node unit test): Append one line in the ## L13 section of release-test.sh:

run_test "my-feature" "node tools/tests/my-feature.test.js"

L11 (playwright integration): Append in the ## L11 section:

retry_match "my-interaction" "node tools/tests/my-interaction.test.js" "PASS=.*FAIL=0"

retry_match wraps external-dependency tests — auto-retries 3 times on network jitter; total failure degrades to warn instead of fail (see §5).

Worked Example: Adding a “Sidebar Collapse” Test

  1. Requirement: Verify the post sidebar auto-collapses on narrow screens
  2. Type: Needs browser → L11 playwright
  3. Create tools/tests/sidebar-collapse.test.js (using the L11 template above)
  4. Hook in: Add a retry_match line in the L11 section of release-test.sh
  5. Verify: tools/tests/release-test.sh --local confirms PASS
  6. TC sync: make tc-index (or let the pre-commit hook do it automatically)

Assertion Output Convention

release-test.sh judges pass/fail by output pattern, so the test file must print a matchable summary line at the end:

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

Authoritative Test Case List

docs/superpowers/specs/2026-08-06-layout-test-cases.md (full layout test cases) + docs/testing/TESTING-PYRAMID-GUIDE.md (testing methodology) — after adding a feature, update the case list and release-test.sh coverage in sync.


13. Performance Verification (LCP Baseline, Manual)

Performance metrics (LCP) are not part of the automated gate — they require Lighthouse sampling on real devices with controlled network conditions, which assertion-style tests cannot express stably. Verification is by baseline document + reproduction commands:

  • Baseline: docs/analysis/LCP-V2-BASELINE-2026-09-18.md (resource counts / Lighthouse desktop+mobile / LCP element identity / §5 reproduction commands / §7 optimization work)
  • Four optimizations (2026-09-18~20): critical CSS preload · highlight conditional loading by layout · markmap localized + async · post hero-image preload
  • Threshold: mobile LCP ≤ 2.5s (Search Console “40 URLs over threshold” is the observation basis)
  • Pitfall: not stripping noscript inflates the resource count by 4 per page (baseline §1.3) — always strip before re-measuring
  • Trigger: re-measure whenever head-level resource loading order/conditions change (CSS/JS/images)

⚠️ Known Pitfalls

Pitfall 5: Config Changes Require Clean Build

Symptom: L1 build verification passes but feature assertions fail when running the gate.

Cause: Local preview uses stale cached artifacts; test assertions are checking old behavior.

Fix: Before running the gate, you must npm run clean && npm run build, or use tools/tests/release-test.sh --prepare for auto-preparation.

Pitfall 6: source/_data/* Incremental Cache Misjudgment

Symptom: After modifying friends/gallery data, L2 URL assertions show blank pages or data doesn’t update.

Cause: Hexo’s incremental cache incorrectly judges “cached, unchanged”.

Fix: After modifying data files, run rm -f db.json && npm run build before the gate. In container environments, use tools/cicd.sh -d (includes clean).


Appendix: Test ↔ Feature Map

Purpose: Want to know “is feature X covered by automated tests?” — look up by feature name below.

FeatureTest FileLayerWhat It Verifies
Page effects (sakura/snowflakes/fireworks)effects-integration.test.jsL11Effect scripts load per gate, no errors
Video backgroundvideobg-integration.test.jsL11Threshold/light-dark pool/webm contract/disabled state
Word cloudwordcloud.test.jsL11Rendering / no jQuery / TOC anti-flicker / dark-light switch
Multilinguali18n.test.jsL11MATERY_I18N injection + button copy + en translations
Music APlayermusics-integration.test.jsL11Multi-domain failover
Lazyloadlazyload-integration.test.jsL11handleCard three-way dispatch (51 assertions)
Floating panelfloating-panel-integration.test.jsL11Toggle / font scaling / persistence
Content widthcontent-width.test.jsL114K/FHD/2K/single-column cap
Read modereadmode-integration.test.jsL11Fullscreen enter/exit / floating ball / code fullscreen / expand
Wiki styleswiki-styles.test.jsL11Note / ::: container colors + centering + width consistency
codeWidget idempotencycode-widget.test.jsL11Second invocation still works with single click
Event buscore-events-integration.test.jsL11Event bus / progress bar linkage
Wiki prismwiki-prism-integration.test.jsL11Code blocks prism + line-numbers
Reward / trad-simpmisc-interactions.test.jsL11Reward popup / simplified-traditional conversion
Client language strategylang-client-strategy.test.jsL11Site-wide follow / crawler skip / panel echo
Rich contentrich-content-integration.test.jsL11Math / mermaid / markmap / typewriter / share / Live2D
Wiki multilingualwiki-integration.test.jsL11Multilingual wiki reachability / sidebar / language switcher
Lightboxlightbox.test.jsL11Gallery/image zoom interaction
Searchunit-search.test.jsL13Three-engine config
Multilingual configunit-multilang.test.jsL13Language config parsing
Encryption extensionsunit-encrypt2.test.jsL13Fragment + wiki encryption
Wiki helperunit-wiki-helper.test.jsL13Language parsing / sidebar rendering / canonical fallback
Sidebar i18nunit-sidebar-i18n.test.jsL13Lookup + fallback chain
Sidebar keysunit-sidebar-keys.test.jsL13Gate validator 4 wikis × 9 languages
Fallback chainlang-fallback.test.jsL134-level fallback (13 assertions)
i18n coreunit-i18n-core.test.jsL13resolveI18n 4-level chain + cfg defense
i18n helperunit-i18n-helper.test.jsL13t() helper fallback
EChartsecharts-charts.test.jsL13Chart rendering
Banner backgroundbanner-bg.test.jsL13Banner & background preference linkage
Homepage structurehomepage.test.jsL13Homepage DOM structure
Image sizeimgsize-unit.test.jsL13normalizeImgUrl + dimension reading
Standalone pagesunit-standalone.test.jsL13Exclusion logic + language prefix exemption
CSS architectureunit-css-architecture.test.shL13Key selectors in compiled output
Version numbersunit-version.test.shL13Increment carry
Retry libraryunit-retry.test.shL13Backoff retry
Deploy targetsunit-deploy-target.test.shL13hexo/hexoback dual-activation

Appendix: Triple-Role Self-Check List

When adding or modifying an article in the “hexo theme feature test” series, check these three items:

#CheckPass Criteria
①VerifiableEvery feature has a clear input → expected output (automated assertion marker or manual verification standard)
②DemonstratedEvery feature describes its rendered effect (text description, rendered example, or screenshot)
③ActionableEvery feature has syntax / config steps + caveats (parameter order, prerequisites, common pitfalls)

Quick check: If a feature “only lists its name without saying how to verify” → missing ①; “only lists syntax without showing effect” → missing ②; “doesn’t explain how to use it or is ambiguous” → missing ③.

Appendix: Testing System Quick Reference

CommandPurpose
tools/tests/release-test.shFull release gate (L1-L14)
tools/tests/release-test.sh --localLocal iteration (adaptive port, skips L10/L11/L14)
tools/tests/release-test.sh --prepareAuto-prepare environment (container + build + recheck)
RUN_L14=1 tools/tests/release-test.sh --localEnable L14 visual assertions locally
make tc-index / make tc-index-checkGenerate / verify TC index (.githooks/pre-commit auto-syncs)
tools/tests/run-tests.shRemoved 2026-09-13 (overlapped with L11); see tools/tests/release-test.sh
node tools/tests/gen-tc-index.jsGenerate/verify TC index (make tc-index)
make test-pwaVercel test domain SW smoke (7 pages) + interaction verification
make visual-compare / visual-assertVisual regression (playwright numeric assertions); ⚠️ make visual-assert includes a build dependency that pollutes _config.yml, prefer node tools/visual-assert.js run --local
tools/visual-regression.sh reportVisual comparison report (20 pages × 4 breakpoints)

References

系列教程

全部文章RSS订阅

Hexo 主题功能测试系列


Hexo Theme Feature Test - Automated Testing
Posted on
September 11, 2026
reprint policy. If reproduced, please indicate source
Support
Comments
Data Loading...
 Current

Read More

Hexo Theme Feature Test - Automated Testing
Hexo Theme Feature Test - Automated Testing Hexo Theme Feature Test - Automated Testing
Complete tutorial for the theme's automated testing system: testing pyramid (L1 build / L13 unit gate / L11 Playwright i
2026-09-11
Next 

Read More

Hexo Theme Feature Test - Markdown Syntax
Hexo Theme Feature Test - Markdown Syntax Hexo Theme Feature Test - Markdown Syntax
A complete Markdown syntax tutorial and test for the theme: full coverage of 16 markdown-it plugins (emoji/abbr/footnote
2026-08-08