From fdc91dbc8bdee4bb2d438ecb52578759039ba501 Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Fri, 15 May 2026 18:55:59 +0700 Subject: [PATCH] test: expand smoke suite to 32 checks + Hugo CI matrix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit scripts/smoke-tests.sh: - Per-kind CSS budget (home/post/list/archives/search), each ≤ 4200 B gz - theme-color light/dark meta tags - aria-pressed SSR on theme-toggle - breadcrumbs nav + BreadcrumbList JSON-LD presence on post, absence on home - prev/next nav + rel=prev attribute - linkToSection aria-label on heading anchors - Speculation Rules absent by default - llm.txt artifact present - Pagefind UI preload-swap pattern on /search/ - Giscus preconnect gating (absent when comments disabled) - Per-kind CSS bundle routing (home loads home.css but not single.css; post loads single.css but not home.css) - code-copy.js gating (post loads it, home does not) .github/workflows/pages.yml: - Build matrix on Hugo 0.146.0 (theme.toml floor) + 0.154.0 (current). Smoke + htmltest run on both. Artifact upload + deploy gated on the current version only. - Drop the redundant standalone CSS-budget step; smoke-tests.sh now enforces per-kind budgets. --- .github/workflows/pages.yml | 27 ++++-------- scripts/smoke-tests.sh | 84 ++++++++++++++++++++++++++++++++----- 2 files changed, 82 insertions(+), 29 deletions(-) diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 6f74071..96c606c 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -17,8 +17,11 @@ concurrency: jobs: build: runs-on: ubuntu-latest - env: - HUGO_VERSION: 0.154.0 + strategy: + fail-fast: false + matrix: + # Floor (theme.toml min_version) + current pinned. Deploy artifact is uploaded only from the current version. + hugo: ["0.146.0", "0.154.0"] steps: - uses: actions/checkout@v6 with: @@ -28,7 +31,7 @@ jobs: - name: Setup Hugo (extended) uses: peaceiris/actions-hugo@v3 with: - hugo-version: ${{ env.HUGO_VERSION }} + hugo-version: ${{ matrix.hugo }} extended: true - name: Setup Node @@ -47,21 +50,7 @@ jobs: - name: Build Pagefind index run: npx pagefind --site exampleSite/public - - name: Assert CSS bundle budget (≤ 4200 B gz) - run: | - css=$(find exampleSite/public -name 'tsuki.bundle.*.css' -print -quit) - if [ -z "$css" ]; then - echo "::error::Could not locate tsuki.bundle.*.css under exampleSite/public" - exit 1 - fi - sz=$(gzip -9 -c "$css" | wc -c) - echo "tsuki bundle gz: ${sz} B (limit 4200)" - if [ "$sz" -gt 4200 ]; then - echo "::error::CSS bundle exceeds 4200 B gz budget (was ${sz} B)" - exit 1 - fi - - - name: Smoke tests (SEO + a11y + features regression guard) + - name: Smoke tests (SEO + a11y + per-kind CSS budget + features regression guard) run: ./scripts/smoke-tests.sh exampleSite/public - name: htmltest (broken internal links + HTML5 validation) @@ -71,6 +60,8 @@ jobs: config: .htmltest.yml - name: Upload artifact + # Only upload from the current Hugo version; the floor-version run is for compatibility check. + if: matrix.hugo == '0.154.0' uses: actions/upload-pages-artifact@v5 with: path: exampleSite/public diff --git a/scripts/smoke-tests.sh b/scripts/smoke-tests.sh index bb6049a..49a616e 100755 --- a/scripts/smoke-tests.sh +++ b/scripts/smoke-tests.sh @@ -61,21 +61,83 @@ assert "reading time byline" 'reading-time' "$po # Discovery (Phase 5) assert "related-posts aside" 'class=related-posts' "$post" -# CSS budget (Phase 1) — duplicates the workflow assert; included so local runs catch it too. -css=$(find "$public_dir" -name 'tsuki.bundle.*.css' -print -quit || true) -if [ -n "$css" ]; then - sz=$(gzip -9 -c "$css" | wc -c) - if [ "$sz" -gt 4200 ]; then - echo "::error::CSS bundle gz ${sz} B exceeds 4200 B budget" - fail=1 - else - echo " ok [CSS budget] ${sz} B / 4200 B" - fi +# v0.3.0 feature surface +assert "theme-color meta (light)" 'name=theme-color content="#fbfaf7"' "$home" +assert "theme-color meta (dark)" 'name=theme-color content="#14151a"' "$home" +assert "aria-pressed SSR" 'data-theme-toggle [^>]*aria-pressed=false|aria-pressed=false [^>]*data-theme-toggle' "$home" +assert "breadcrumbs nav on post" 'class=breadcrumbs' "$post" +assert "BreadcrumbList JSON-LD" '"@type":"BreadcrumbList"' "$post" +assert "no breadcrumbs on home" 'class=breadcrumbs' "$home" 0 +assert "prev/next nav" 'class=prev-next' "$post" +assert "rel=prev on post" 'rel=prev' "$post" +assert "linkToSection aria-label" 'class=heading-anchor [^>]*aria-label' "$post" +assert "no speculationrules by default" 'speculationrules' "$home" 0 + +# llm.txt artifact +if [ -f "$public_dir/llm.txt" ]; then + echo " ok [llm.txt artifact present]" else - echo "::error::Could not locate tsuki.bundle.*.css" + echo "::error::FAIL [llm.txt artifact present] expected $public_dir/llm.txt" fail=1 fi +# Pagefind preload-swap on search (rel=preload as=style) +if [ -f "$public_dir/search/index.html" ]; then + assert "Pagefind preload swap" 'rel=preload as=style href=[^ >]*pagefind' "$public_dir/search/index.html" +fi + +# Giscus preconnect: comments disabled in demo → must NOT emit +assert "no giscus preconnect (disabled)" 'preconnect[^>]*giscus' "$post" 0 + +# Per-page CSS bundle gating: home gets home.css, post does NOT +assert "home loads home.css" 'home\.min\.[0-9a-f]+\.css' "$home" +assert "post does not load home.css" 'home\.min\.[0-9a-f]+\.css' "$post" 0 +assert "post loads single.css" 'tsuki\.single\.min\.[0-9a-f]+\.css' "$post" +assert "home does not load single.css" 'tsuki\.single\.min\.[0-9a-f]+\.css' "$home" 0 + +# code-copy.js gate: only post pages should load it +assert "post loads code-copy.js" 'code-copy\.[0-9a-f]+\.js' "$post" +assert "home does not load code-copy.js" 'code-copy\.[0-9a-f]+\.js' "$home" 0 + +# Per-kind CSS budget — every page kind's total stylesheet payload ≤ 4200 B gz. +# Iterate over each kind's representative HTML, extract its CSS, +# sum gzipped sizes (excluding third-party like /pagefind/), assert each kind under budget. +check_kind_css_budget() { + local label="$1" html="$2" + [ -f "$html" ] || { echo " skip [$label CSS budget] no HTML at $html"; return; } + local total=0 missing=0 + while IFS= read -r href; do + # strip query string + leading slash + baseURL prefix + local rel="${href#/tsuki/}" + rel="${rel#/}" + local fpath="$public_dir/$rel" + if [ -f "$fpath" ]; then + local sz + sz=$(gzip -9 -c "$fpath" | wc -c) + total=$((total + sz)) + else + missing=$((missing + 1)) + fi + done < <(grep -oE 'rel=stylesheet href=[^ >]+\.css' "$html" | grep -v '/pagefind/' | sed -E 's|rel=stylesheet href=||') + if [ "$total" -gt 4200 ]; then + echo "::error::[$label CSS budget] ${total} B gz exceeds 4200 B" + fail=1 + else + echo " ok [$label CSS budget] ${total} B / 4200 B (${missing} third-party skipped)" + fi +} + +# Pick representative HTML per kind from the build. +list_html="$public_dir/post/index.html" +search_html="$public_dir/search/index.html" +archives_html="$public_dir/archives/index.html" + +check_kind_css_budget "home" "$home" +check_kind_css_budget "post" "$post" +[ -f "$list_html" ] && check_kind_css_budget "list" "$list_html" +[ -f "$archives_html" ] && check_kind_css_budget "archives" "$archives_html" +[ -f "$search_html" ] && check_kind_css_budget "search" "$search_html" + echo [ "$fail" -eq 0 ] && echo "All smoke tests passed." || echo "Smoke tests failed." exit "$fail"