play.wenpai.net/Justfile
elementary-qa 8f258e2bcc play.wenpai.net 入口页 + pw-test 集成到验收流程
- play/index.html: 插件体验展示页,插件注册表驱动,新增插件只需加一条配置
- play/blueprints/: WPMind + 空白中文环境两个预设
- Justfile: pw-test 集成到 test-plugin 完整验收流程

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-20 15:50:54 +08:00

363 lines
14 KiB
Makefile
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# WordPress 插件自动化验收测试
# 用法: just <任务名> [参数]
# 查看所有任务: just --list
set dotenv-load
set shell := ["bash", "-cu"]
# 变量
site := env("WP_SITE", "http://localhost:9400")
plugin := env("WP_PLUGIN", "")
ssh_target := env("SSH_TARGET", "")
debug_log := env("WP_DEBUG_LOG_PATH", "/var/www/html/wp-content/debug.log")
pg_ver := env("PLAYGROUND_VERSION", "3.0.52")
pg_wp := env("PLAYGROUND_WP", "6.8")
pg_php := env("PLAYGROUND_PHP", "8.4")
chrome := env("CHROME_PATH", home_directory() / ".cache/ms-playwright/chromium-1208/chrome-linux/chrome")
date := `date +%Y-%m-%d`
results := home_directory() / "test-results" / date / plugin
blueprints := home_directory() / "blueprints"
# ─── Playground 环境管理 ───────────────────────────
# 启动干净 WordPress Playground
playground:
npx @wp-playground/cli@{{pg_ver}} server --port=9400 --login
# 启动 Playground + 加载 Blueprint
playground-bp name:
npx @wp-playground/cli@{{pg_ver}} server --port=9400 --login \
--blueprint={{blueprints}}/{{name}}.json
# 启动 Playground + 挂载本地插件目录
playground-dev path:
npx @wp-playground/cli@{{pg_ver}} server --port=9400 --login \
--mount={{path}}:/wordpress/wp-content/plugins/
# 中文基础环境
playground-zh:
npx @wp-playground/cli@{{pg_ver}} server --port=9400 --login \
--blueprint={{blueprints}}/zh-cn-base.json
# 执行 Blueprint不启动 serverCI 用)
run-bp name:
npx @wp-playground/cli@{{pg_ver}} run-blueprint \
--blueprint={{blueprints}}/{{name}}.json
# 打包快照
snapshot name:
mkdir -p {{results}}
npx @wp-playground/cli@{{pg_ver}} build-snapshot \
--blueprint={{blueprints}}/{{name}}.json \
--outfile={{results}}/{{name}}-snapshot.zip
# ─── 完整验收流程 ──────────────────────────────────
# 一键拉取 + 验收(从 Forgejo 拉最新 release → 启动 Playground → 跑验收 → 导入趋势)
fetch-and-test repo name="":
#!/usr/bin/env bash
set -euo pipefail
PLUGIN_NAME="${2:-$(basename "{{repo}}")}"
echo "=== 拉取 {{repo}} 最新 release ==="
bash scripts/fetch-release.sh "{{repo}}"
ZIP=$(ls -t ~/下载/${PLUGIN_NAME}-*.zip 2>/dev/null | head -1)
[ -z "$ZIP" ] && { echo "未找到 zip 文件"; exit 1; }
echo "=== 准备 Playground ==="
# 停掉旧的 Playground 和 HTTP server
pkill -f "wp-playground" 2>/dev/null || true
pkill -f "python3 -m http.server 8888" 2>/dev/null || true
sleep 2
# 启动 HTTP server 服务 zip
cp "$ZIP" /tmp/${PLUGIN_NAME}.zip
cd /tmp && nohup python3 -m http.server 8888 > /dev/null 2>&1 &
HTTP_PID=$!
sleep 1
# 生成临时 Blueprint
cat > /tmp/${PLUGIN_NAME}-blueprint.json << BPEOF
{
"\$schema": "https://playground.wordpress.net/blueprint-schema.json",
"landingPage": "/wp-admin/plugins.php",
"preferredVersions": { "wp": "{{pg_wp}}", "php": "{{pg_php}}" },
"steps": [
{ "step": "setSiteOptions", "options": { "blogname": "验收测试站", "WPLANG": "zh_CN", "timezone_string": "Asia/Shanghai", "date_format": "Y-m-d", "time_format": "H:i" } },
{ "step": "login", "username": "admin", "password": "password" },
{ "step": "installPlugin", "pluginData": { "resource": "url", "url": "http://127.0.0.1:8888/${PLUGIN_NAME}.zip" } }
]
}
BPEOF
# 启动 Playground
nohup npx @wp-playground/cli@{{pg_ver}} server --port=9400 --login --blueprint=/tmp/${PLUGIN_NAME}-blueprint.json > /tmp/playground.log 2>&1 &
PG_PID=$!
echo "等待 Playground 启动..."
for i in $(seq 1 30); do
curl -s http://localhost:9400/ -o /dev/null && break
sleep 1
done
echo "=== 开始验收 ==="
just test-plugin "$PLUGIN_NAME"
# 导入趋势数据库
RESULTS="$HOME/test-results/$(date +%Y-%m-%d)/${PLUGIN_NAME}"
if [ -f "$RESULTS/verdict.json" ]; then
node scripts/trend-tracker.js import "$RESULTS/verdict.json" 2>/dev/null && echo "[trend] 已导入趋势数据库" || true
fi
# 清理
kill $HTTP_PID 2>/dev/null || true
echo "=== 全流程完成 ==="
# 完整验收CLI + Playwright + 视觉回归 + 报告)
test-plugin name:
#!/usr/bin/env bash
set -euo pipefail
export WP_PLUGIN="{{name}}"
RESULTS="$HOME/test-results/{{date}}/{{name}}"
echo "=== 验收插件: {{name}} ==="
echo "目标: {{site}}"
echo "结果: $RESULTS"
just setup-dirs "{{name}}"
# CLI 工具扫描
just a11y-scan "{{name}}" || true
just lighthouse-scan "{{name}}" || true
just link-check "{{name}}" || true
just api-scan "{{name}}" || true
just html-validate-page "{{name}}" "{{site}}" || true
if [ -n "{{ssh_target}}" ]; then
just server-errors "{{name}}" || true
fi
# Playwright 自动化
node scripts/playwright/screenshots.js "{{name}}" "{{site}}" "$RESULTS/screenshots" || true
node scripts/playwright/security-scan.js "{{site}}" "$RESULTS/security" || true
# 等待 Chromium 进程释放,避免与 BackstopJS 资源竞争
sleep 2
pkill -f "chromium.*--headless" 2>/dev/null || true
sleep 1
# 视觉回归(自动选择基线)
bash scripts/backstop-baseline.sh auto "{{name}}" "unknown" 2>/dev/null || true
just visual-test || true
# i18n 验收
just i18n-test "{{name}}" || true
# PHPCS 静态分析(如果有源码)
PLUGIN_SRC="/tmp/wpmind-review/wpmind-src/{{name}}"
if [ -d "$PLUGIN_SRC" ]; then
bash scripts/phpcs-scan.sh "$PLUGIN_SRC" "$RESULTS/phpcs" || true
fi
# Playwright 功能回归测试
export TEST_OUTPUT="$RESULTS/playwright"
mkdir -p "$TEST_OUTPUT"
npx playwright test --config=playwright.config.js 2>&1 | tee "$TEST_OUTPUT/output.log" || true
# 生成汇总报告
node scripts/generate-report.js "{{name}}" "$RESULTS"
echo "=== 验收完成: $RESULTS/report.md ==="
# ─── 测试任务 ──────────────────────────────────────
# 创建结果目录
setup-dirs name:
mkdir -p ~/test-results/{{date}}/{{name}}/{screenshots,a11y,performance,security,api,i18n,diff}
# 无障碍扫描pa11y + axe 双引擎)
a11y-scan name:
#!/usr/bin/env bash
set -uo pipefail
DIR="$HOME/test-results/{{date}}/{{name}}/a11y"
mkdir -p "$DIR"
echo "[pa11y+axe] 扫描 {{site}} ..."
pa11y "{{site}}" --config ~/pa11y.json --reporter json > "$DIR/pa11y-htmlcs.json" 2>&1 || true
PA11Y_ISSUES=$(jq 'length' "$DIR/pa11y-htmlcs.json" 2>/dev/null || echo "?")
echo "[pa11y/htmlcs] 发现 $PA11Y_ISSUES 个问题"
pa11y "{{site}}" --config ~/pa11y.json --runner axe --reporter json > "$DIR/pa11y-axe.json" 2>&1 || true
AXE_ISSUES=$(jq 'length' "$DIR/pa11y-axe.json" 2>/dev/null || echo "?")
echo "[pa11y/axe] 发现 $AXE_ISSUES 个问题"
echo "[a11y] 结果: $DIR/"
# Lighthouse 性能/SEO 扫描
lighthouse-scan name:
#!/usr/bin/env bash
set -uo pipefail
DIR="$HOME/test-results/{{date}}/{{name}}/performance"
mkdir -p "$DIR"
echo "[lighthouse] 扫描 {{site}} ..."
CHROME_PATH="{{chrome}}" lighthouse "{{site}}" \
--output=json --output=html \
--output-path="$DIR/lighthouse" \
--chrome-flags="--headless --no-sandbox --disable-setuid-sandbox" \
--quiet 2>&1 || true
if [ -f "$DIR/lighthouse.report.json" ]; then
jq -r '.categories | to_entries[] | " \(.value.title): \(.value.score * 100 | floor)"' "$DIR/lighthouse.report.json" 2>/dev/null || true
fi
# 全站死链检测
link-check name:
#!/usr/bin/env bash
set -uo pipefail
DIR="$HOME/test-results/{{date}}/{{name}}"
mkdir -p "$DIR"
echo "[linkchecker] 检测 {{site}} ..."
linkchecker "{{site}}" --output=csv > "$DIR/link-check.csv" 2>&1 || true
BROKEN=$(grep -c "^[^#].*error" "$DIR/link-check.csv" 2>/dev/null || echo "0")
echo "[linkchecker] 发现 $BROKEN 个断链"
# REST API 端点扫描
api-scan name:
#!/usr/bin/env bash
set -uo pipefail
DIR="$HOME/test-results/{{date}}/{{name}}/api"
mkdir -p "$DIR"
echo "[api] 扫描 {{site}}/wp-json/ ..."
curl -sL -c /tmp/wp-just-cookies.txt -b /tmp/wp-just-cookies.txt "{{site}}/wp-json/" > "$DIR/routes.json" 2>&1 || true
ROUTES=$(jq '.routes | length' "$DIR/routes.json" 2>/dev/null || echo "?")
echo "[api] 发现 $ROUTES 个路由"
# HTML 验证(单页)
html-validate-page name url:
#!/usr/bin/env bash
set -uo pipefail
DIR="$HOME/test-results/{{date}}/{{name}}"
mkdir -p "$DIR"
echo "[html-validate] 验证 {{url}} ..."
curl -sL -c /tmp/wp-just-cookies.txt -b /tmp/wp-just-cookies.txt "{{url}}" > /tmp/validate-page.html 2>/dev/null
html-validate /tmp/validate-page.html > "$DIR/html-validate.txt" 2>&1 || true
ERRORS=$(grep -c "error" "$DIR/html-validate.txt" 2>/dev/null || echo "0")
echo "[html-validate] 发现 $ERRORS 个问题"
# 服务端错误检查(需要 SSH 访问)
server-errors name:
#!/usr/bin/env bash
set -uo pipefail
DIR="$HOME/test-results/{{date}}/{{name}}"
mkdir -p "$DIR"
if [ -z "{{ssh_target}}" ]; then
echo "[server] 跳过: 未配置 SSH_TARGETPlayground 模式无需此步)"
exit 0
fi
echo "[server] 拉取 debug.log ..."
ssh "{{ssh_target}}" "cat {{debug_log}}" > "$DIR/debug.log" 2>&1 || true
FATALS=$(grep -c "Fatal" "$DIR/debug.log" 2>/dev/null || echo "0")
WARNINGS=$(grep -c "Warning" "$DIR/debug.log" 2>/dev/null || echo "0")
echo "[server] Fatal: $FATALS, Warning: $WARNINGS"
# ─── 视觉回归 ─────────────────────────────────────
# 建立视觉基线
visual-baseline:
backstop reference --config=backstop.json
# 视觉回归对比
visual-test:
backstop test --config=backstop.json
# 基线版本管理
baseline-save plugin version:
bash scripts/backstop-baseline.sh save {{plugin}} {{version}}
baseline-use plugin version:
bash scripts/backstop-baseline.sh use {{plugin}} {{version}}
baseline-auto plugin version:
bash scripts/backstop-baseline.sh auto {{plugin}} {{version}}
baseline-list plugin="":
bash scripts/backstop-baseline.sh list {{plugin}}
# PHPCS 静态分析
phpcs-scan name src-dir:
bash scripts/phpcs-scan.sh {{src-dir}} "$HOME/test-results/{{date}}/{{name}}/phpcs"
# Playwright 功能回归测试
pw-test name:
#!/usr/bin/env bash
export TEST_OUTPUT="$HOME/test-results/{{date}}/{{name}}/playwright"
mkdir -p "$TEST_OUTPUT"
npx playwright test --config=playwright.config.js 2>&1 | tee "$TEST_OUTPUT/output.log"
echo "[pw-test] 结果: $TEST_OUTPUT"
# 截图对比(两个目录)
screenshot-diff name actual expected:
#!/usr/bin/env bash
DIR="$HOME/test-results/{{date}}/{{name}}/diff"
mkdir -p "$DIR"
reg-cli "{{actual}}" "{{expected}}" "$DIR" --report "$DIR/report.html"
echo "[diff] 报告: $DIR/report.html"
# ─── Playwright 交互流程 ──────────────────────────
# 多分辨率截图4 视口 × 4 页面)
screenshots name:
node scripts/playwright/screenshots.js "{{name}}" "{{site}}"
# 插件安装验收(上传→激活→截图→检查菜单)
plugin-install zip:
node scripts/playwright/plugin-install.js "{{zip}}" "{{site}}"
# 设置页面验收(字段收集→保存→刷新验证)
settings-test path="/wp-admin/options-general.php":
node scripts/playwright/settings-test.js "{{path}}" "{{site}}"
# 基础安全扫描XSS/CSRF/SQLi/信息泄露)
security-scan name="default":
node scripts/playwright/security-scan.js "{{site}}"
# i18n 验收(溢出 + 中英截图 + 覆盖率 + 日期格式)
i18n-test name zip="":
#!/usr/bin/env bash
set -uo pipefail
STARTED_EN=false
RESULTS="$HOME/test-results/{{date}}/{{name}}/i18n"
mkdir -p "$RESULTS"
# Auto-start en_US Playground if not running
if ! ss -tlnp | grep -q ':9401'; then
echo "[i18n] 启动 en_US Playground..."
npx @wp-playground/cli@{{pg_ver}} server --port=9401 --login \
--blueprint={{blueprints}}/en-us-base.json &>/dev/null &
STARTED_EN=true
sleep 8
fi
cleanup() { if [ "$STARTED_EN" = "true" ]; then pkill -f 'port=9401' 2>/dev/null || true; fi; }
trap cleanup EXIT
EN_URL="http://localhost:9401"
if ! ss -tlnp | grep -q ':9401'; then
echo "[i18n] en_US 启动失败,仅测试 zh_CN"
EN_URL=""
fi
ZIP_ARG=""
if [ -n "{{zip}}" ] && [ -f "{{zip}}" ]; then ZIP_ARG="{{zip}}"; fi
node scripts/playwright/i18n-test.js "{{name}}" "{{site}}" "$EN_URL" "$RESULTS" "$ZIP_ARG"
# 启动英文 Playgroundi18n 对比用)
playground-en:
npx @wp-playground/cli@{{pg_ver}} server --port=9401 --login \
--blueprint={{blueprints}}/en-us-base.json
# ─── 数据库管理(远程站点) ─────────────────────────
# 数据库快照
db-snapshot:
#!/usr/bin/env bash
if [ -z "{{ssh_target}}" ]; then echo "需要 SSH_TARGET"; exit 1; fi
ssh "{{ssh_target}}" "wp db export ~/backups/pre-test-$(date +%Y%m%d-%H%M).sql"
echo "[db] 快照已保存"
# 数据库回滚
db-rollback file:
#!/usr/bin/env bash
if [ -z "{{ssh_target}}" ]; then echo "需要 SSH_TARGET"; exit 1; fi
ssh "{{ssh_target}}" "wp db import {{file}}"
echo "[db] 已回滚到 {{file}}"
# ─── 工具 ─────────────────────────────────────────
# 监控 NAS staging 目录,自动跑验收
watch-staging:
scripts/staging-watcher.sh
# 检查一次 staging适合 cron
check-staging:
scripts/staging-watcher.sh --once
# 列出所有测试结果
results:
@ls -la ~/test-results/{{date}}/ 2>/dev/null || echo "今天没有测试结果"
# 清理旧结果(保留最近 7 天)
clean-results:
find ~/test-results/ -maxdepth 1 -type d -mtime +7 -exec rm -rf {} \;
echo "已清理 7 天前的测试结果"